From 1be11c8a3e317a2053aa32b7724d320824aa3cf2 Mon Sep 17 00:00:00 2001 From: David Yaffe Date: Thu, 11 Jan 2024 17:13:56 -0500 Subject: [PATCH 1/7] add wrapper for checksums / hash functions + unit tests --- .../Networking/HashFunction.swift | 100 ++++++++++++ .../NetworkingTests/HashFunctionTests.swift | 144 ++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 Sources/ClientRuntime/Networking/HashFunction.swift create mode 100644 Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift diff --git a/Sources/ClientRuntime/Networking/HashFunction.swift b/Sources/ClientRuntime/Networking/HashFunction.swift new file mode 100644 index 000000000..164254543 --- /dev/null +++ b/Sources/ClientRuntime/Networking/HashFunction.swift @@ -0,0 +1,100 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +import AwsCommonRuntimeKit + +enum HashFunction { + case crc32, crc32c, sha1, sha256, md5 + + static func from(string: String) -> HashFunction? { + switch string.lowercased() { + case "crc32": return .crc32 + case "crc32c": return .crc32c + case "sha1": return .sha1 + case "sha256": return .sha256 + case "md5": return .md5 // md5 is not a valid flexible checksum algorithm + default: return nil + } + } + + var isSupported: Bool { + switch self { + case .crc32, .crc32c, .sha256, .sha1: + return true + default: + return false + } + } + + func computeHash(of data: Data) throws -> Data? { + switch self { + case .crc32: + // Should turn this result back into a UInt32 using .toUInt32() + return Data(fromUInt32: data.computeCRC32()) + case .crc32c: + // Should turn this result back into a UInt32 using .toUInt32() + return Data(fromUInt32: data.computeCRC32C()) + case .sha1: + do { + let hashed = try data.computeSHA1() + return Data(hashed) + } catch { + throw ClientRuntime.ClientError.unknownError("Error computing SHA1: \(error)") + } + case .sha256: + do { + let hashed = try data.computeSHA256() + return Data(hashed) + } catch { + throw ClientRuntime.ClientError.unknownError("Error computing SHA256: \(error)") + } + case .md5: + do { + let hashed = try data.computeMD5() + return Data(hashed) + } catch { + throw ClientRuntime.ClientError.unknownError("Error computing MD5: \(error)") + } + } + } +} + +protocol HexStringable { + func toHexString() -> String +} + +extension Data { + + // Create a Data type from a UInt32 value + init(fromUInt32 value: UInt32) { + var val = value // Create a mutable UInt32 + self = Swift.withUnsafeBytes(of: &val) { Data($0) } + } + + // Convert a Data type to UInt32 + func toUInt32() -> UInt32? { + guard self.count == MemoryLayout.size else { + return nil // Ensures the data is of the correct size + } + + return self.withUnsafeBytes { $0.load(as: UInt32.self) } + } +} + +extension Data: HexStringable { + + // Convert a Data type to a hexademical String + func toHexString() -> String { + return map { String(format: "%02x", $0) }.joined() + } +} + +extension UInt32: HexStringable { + + // Convert a UInt32 type to a hexademical String + func toHexString() -> String { + return String(format: "%08x", self) + } +} diff --git a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift new file mode 100644 index 000000000..5d86233da --- /dev/null +++ b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift @@ -0,0 +1,144 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@testable import ClientRuntime + +class HashFunctionTests: XCTestCase { + + func testCRC32() { + guard let hashFunction = HashFunction.from(string: "crc32") else { + XCTFail("CRC32 not found") + return + } + + // Create test data + let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 + let testData = Data(testBytes) + + let result = try? hashFunction.computeHash(of: testData)?.toUInt32() + let expected = testData.computeCRC32() + XCTAssertEqual(result, expected, "CRC32 hash does not match expected value") + } + + func testCRC32C() { + guard let hashFunction = HashFunction.from(string: "crc32c") else { + XCTFail("CRC32C not found") + return + } + + // Create test data + let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 + let testData = Data(testBytes) + + let result = try? hashFunction.computeHash(of: testData)?.toUInt32() + let expected = testData.computeCRC32C() + XCTAssertEqual(result, expected, "CRC32C hash does not match expected value") + } + + func testSHA1() { + guard let hashFunction = HashFunction.from(string: "sha1") else { + XCTFail("SHA1 not found") + return + } + + // Create test data + let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 + let testData = Data(testBytes) + + let result = try? hashFunction.computeHash(of: testData) + let expected = try? testData.computeSHA1() + XCTAssertEqual(result, expected, "SHA1 hash does not match expected value") + } + + func testSHA256() { + guard let hashFunction = HashFunction.from(string: "sha256") else { + XCTFail("SHA256 not found") + return + } + + // Create test data + let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 + let testData = Data(testBytes) + + let result = try? hashFunction.computeHash(of: testData) + let expected = try? testData.computeSHA256() + XCTAssertEqual(result, expected, "SHA256 hash does not match expected value") + } + + func testMD5() { + guard let hashFunction = HashFunction.from(string: "md5") else { + XCTFail("MD5 not found") + return + } + + // Create test data + let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 + let testData = Data(testBytes) + + let result = try? hashFunction.computeHash(of: testData) + let expected = try? testData.computeMD5() + XCTAssertEqual(result, expected, "MD5 hash does not match expected value") + } + + func testInvalidHashFunction() { + let invalidHashFunction = HashFunction.from(string: "invalid") + XCTAssertNil(invalidHashFunction, "Invalid hash function should return nil") + } + + func testDataConversionUInt32() { + let originalValue: UInt32 = 1234567890 + let dataRepresentation = Data(fromUInt32: originalValue) + let convertedValue = dataRepresentation.toUInt32() + XCTAssertEqual(originalValue, convertedValue, "Conversion to and from UInt32 failed") + } + + func testHashFunctionToHexString() { + let testData = Data("Hello, world!".utf8) + + // CRC32 + if let crc32Function = HashFunction.from(string: "crc32"), + let crc32Result = try? crc32Function.computeHash(of: testData)?.toUInt32() { + XCTAssertEqual(crc32Result.toHexString(), "ebe6c6e6", "CRC32 hexadecimal representation does not match expected value") + } else { + XCTFail("CRC32 hash function not found or computation failed") + } + + // CRC32C + if let crc32cFunction = HashFunction.from(string: "crc32c"), + let crc32cResult = try? crc32cFunction.computeHash(of: testData)?.toUInt32() { + XCTAssertEqual(crc32cResult.toHexString(), "c8a106e5", "CRC32C hexadecimal representation does not match expected value") + } else { + XCTFail("CRC32C hash function not found or computation failed") + } + + // SHA1 + if let sha1Function = HashFunction.from(string: "sha1"), + let sha1Result = try? sha1Function.computeHash(of: testData) { + XCTAssertEqual(sha1Result.toHexString(), "943a702d06f34599aee1f8da8ef9f7296031d699", "SHA1 hexadecimal representation does not match expected value") + } else { + XCTFail("SHA1 hash function not found or computation failed") + } + + // SHA256 + if let sha256Function = HashFunction.from(string: "sha256"), + let sha256Result = try? sha256Function.computeHash(of: testData) { + XCTAssertEqual(sha256Result.toHexString(), "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3", "SHA256 hexadecimal representation does not match expected value") + } else { + XCTFail("SHA256 hash function not found or computation failed") + } + + // MD5 + if let md5Function = HashFunction.from(string: "md5"), + let md5Result = try? md5Function.computeHash(of: testData) { + XCTAssertEqual(md5Result.toHexString(), "6cd3556deb0da54bca060b4c39479839", "MD5 hexadecimal representation does not match expected value") + } else { + XCTFail("MD5 hash function not found or computation failed") + } + } + +} From 90c0d1e46eaffe135e8ddc1284ab3c3c0833f0a5 Mon Sep 17 00:00:00 2001 From: David Yaffe Date: Thu, 11 Jan 2024 17:14:52 -0500 Subject: [PATCH 2/7] move protocol lower --- Sources/ClientRuntime/Networking/HashFunction.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/ClientRuntime/Networking/HashFunction.swift b/Sources/ClientRuntime/Networking/HashFunction.swift index 164254543..deef7c60c 100644 --- a/Sources/ClientRuntime/Networking/HashFunction.swift +++ b/Sources/ClientRuntime/Networking/HashFunction.swift @@ -61,10 +61,6 @@ enum HashFunction { } } -protocol HexStringable { - func toHexString() -> String -} - extension Data { // Create a Data type from a UInt32 value @@ -83,6 +79,10 @@ extension Data { } } +protocol HexStringable { + func toHexString() -> String +} + extension Data: HexStringable { // Convert a Data type to a hexademical String From dcf28461cde143eb043a066721043cd2c0847e69 Mon Sep 17 00:00:00 2001 From: David Yaffe Date: Thu, 11 Jan 2024 18:00:33 -0500 Subject: [PATCH 3/7] add initialize to try to fix failing linux CI --- .../NetworkingTests/HashFunctionTests.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift index 5d86233da..ced0b908d 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift @@ -7,6 +7,7 @@ import XCTest @testable import ClientRuntime +import AwsCommonRuntimeKit class HashFunctionTests: XCTestCase { @@ -98,6 +99,9 @@ class HashFunctionTests: XCTestCase { } func testHashFunctionToHexString() { + // Must be called to work on linux + CommonRuntimeKit.initialize() + let testData = Data("Hello, world!".utf8) // CRC32 From 3b9e3d6b8c8e3c343b8155e88a61c091119c4693 Mon Sep 17 00:00:00 2001 From: David Yaffe Date: Thu, 11 Jan 2024 18:11:06 -0500 Subject: [PATCH 4/7] move initialize to setUp --- .../NetworkingTests/HashFunctionTests.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift index ced0b908d..38912b2cf 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift @@ -11,6 +11,11 @@ import AwsCommonRuntimeKit class HashFunctionTests: XCTestCase { + override func setUp() { + // Initialize function needs to be called before interacting with CRT + CommonRuntimeKit.initialize() + } + func testCRC32() { guard let hashFunction = HashFunction.from(string: "crc32") else { XCTFail("CRC32 not found") @@ -99,9 +104,6 @@ class HashFunctionTests: XCTestCase { } func testHashFunctionToHexString() { - // Must be called to work on linux - CommonRuntimeKit.initialize() - let testData = Data("Hello, world!".utf8) // CRC32 From df1ad9038d2d418bf8bb15e2bcfdb5bcbdead864 Mon Sep 17 00:00:00 2001 From: David Yaffe Date: Fri, 12 Jan 2024 13:38:51 -0500 Subject: [PATCH 5/7] add HashResult enum with HexStringable + hardcoded test expectations --- .../Networking/HashFunction.swift | 48 +++++------ .../NetworkingTests/HashFunctionTests.swift | 83 +++++++++++-------- 2 files changed, 72 insertions(+), 59 deletions(-) diff --git a/Sources/ClientRuntime/Networking/HashFunction.swift b/Sources/ClientRuntime/Networking/HashFunction.swift index deef7c60c..1c878c10d 100644 --- a/Sources/ClientRuntime/Networking/HashFunction.swift +++ b/Sources/ClientRuntime/Networking/HashFunction.swift @@ -5,6 +5,11 @@ import AwsCommonRuntimeKit +enum HashResult { + case data(Data) + case integer(UInt32) +} + enum HashFunction { case crc32, crc32c, sha1, sha256, md5 @@ -28,32 +33,32 @@ enum HashFunction { } } - func computeHash(of data: Data) throws -> Data? { + func computeHash(of data: Data) throws -> HashResult? { switch self { case .crc32: // Should turn this result back into a UInt32 using .toUInt32() - return Data(fromUInt32: data.computeCRC32()) + return .integer(data.computeCRC32()) case .crc32c: // Should turn this result back into a UInt32 using .toUInt32() - return Data(fromUInt32: data.computeCRC32C()) + return .integer(data.computeCRC32C()) case .sha1: do { let hashed = try data.computeSHA1() - return Data(hashed) + return .data(hashed) } catch { throw ClientRuntime.ClientError.unknownError("Error computing SHA1: \(error)") } case .sha256: do { let hashed = try data.computeSHA256() - return Data(hashed) + return .data(hashed) } catch { throw ClientRuntime.ClientError.unknownError("Error computing SHA256: \(error)") } case .md5: do { let hashed = try data.computeMD5() - return Data(hashed) + return .data(hashed) } catch { throw ClientRuntime.ClientError.unknownError("Error computing MD5: \(error)") } @@ -61,24 +66,6 @@ enum HashFunction { } } -extension Data { - - // Create a Data type from a UInt32 value - init(fromUInt32 value: UInt32) { - var val = value // Create a mutable UInt32 - self = Swift.withUnsafeBytes(of: &val) { Data($0) } - } - - // Convert a Data type to UInt32 - func toUInt32() -> UInt32? { - guard self.count == MemoryLayout.size else { - return nil // Ensures the data is of the correct size - } - - return self.withUnsafeBytes { $0.load(as: UInt32.self) } - } -} - protocol HexStringable { func toHexString() -> String } @@ -98,3 +85,16 @@ extension UInt32: HexStringable { return String(format: "%08x", self) } } + +extension HashResult: HexStringable { + + // Convert a HashResult to a hexadecimal String + func toHexString() -> String { + switch self { + case .data(let data): + return data.toHexString() + case .integer(let integer): + return integer.toHexString() + } + } +} diff --git a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift index 38912b2cf..912b85c74 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift @@ -16,7 +16,7 @@ class HashFunctionTests: XCTestCase { CommonRuntimeKit.initialize() } - func testCRC32() { + func testCRC32NonUTF8Bytes() { guard let hashFunction = HashFunction.from(string: "crc32") else { XCTFail("CRC32 not found") return @@ -26,12 +26,16 @@ class HashFunctionTests: XCTestCase { let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 let testData = Data(testBytes) - let result = try? hashFunction.computeHash(of: testData)?.toUInt32() - let expected = testData.computeCRC32() + let computedHash = try? hashFunction.computeHash(of: testData) + guard case let .integer(result)? = computedHash else { + XCTFail("CRC32 computed hash is not an integer or is nil") + return + } + let expected = UInt32(1426237168) XCTAssertEqual(result, expected, "CRC32 hash does not match expected value") } - func testCRC32C() { + func testCRC32CNonUTF8Bytes() { guard let hashFunction = HashFunction.from(string: "crc32c") else { XCTFail("CRC32C not found") return @@ -41,12 +45,16 @@ class HashFunctionTests: XCTestCase { let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 let testData = Data(testBytes) - let result = try? hashFunction.computeHash(of: testData)?.toUInt32() - let expected = testData.computeCRC32C() + let computedHash = try? hashFunction.computeHash(of: testData) + guard case let .integer(result)? = computedHash else { + XCTFail("CRC32C computed hash is not an integer or is nil") + return + } + let expected = UInt32(1856745115) XCTAssertEqual(result, expected, "CRC32C hash does not match expected value") } - func testSHA1() { + func testSHA1NonUTF8Bytes() { guard let hashFunction = HashFunction.from(string: "sha1") else { XCTFail("SHA1 not found") return @@ -56,12 +64,16 @@ class HashFunctionTests: XCTestCase { let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 let testData = Data(testBytes) - let result = try? hashFunction.computeHash(of: testData) - let expected = try? testData.computeSHA1() - XCTAssertEqual(result, expected, "SHA1 hash does not match expected value") + let computedHash = try? hashFunction.computeHash(of: testData) + guard case let .data(result)? = computedHash else { + XCTFail("SHA1 computed hash is not a data type or is nil") + return + } + let expected = "ADfJtWg8Do2MpnFNsvFRmyMuEOI=" + XCTAssertEqual(result.base64EncodedString(), expected, "SHA1 hash does not match expected value") } - func testSHA256() { + func testSHA256NonUTF8Bytes() { guard let hashFunction = HashFunction.from(string: "sha256") else { XCTFail("SHA256 not found") return @@ -71,12 +83,16 @@ class HashFunctionTests: XCTestCase { let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 let testData = Data(testBytes) - let result = try? hashFunction.computeHash(of: testData) - let expected = try? testData.computeSHA256() - XCTAssertEqual(result, expected, "SHA256 hash does not match expected value") + let computedHash = try? hashFunction.computeHash(of: testData) + guard case let .data(result)? = computedHash else { + XCTFail("SHA256 computed hash is not a data type or is nil") + return + } + let expected = "jCosV0rEcc6HWQwT8O/bQr0ssZuxhJM3nUW/zJBgtlc=" + XCTAssertEqual(result.base64EncodedString(), expected, "SHA256 hash does not match expected value") } - func testMD5() { + func testMD5NonUTF8Bytes() { guard let hashFunction = HashFunction.from(string: "md5") else { XCTFail("MD5 not found") return @@ -86,9 +102,13 @@ class HashFunctionTests: XCTestCase { let testBytes: [UInt8] = [0xFF, 0xFE, 0xFD, 0xFC] // Bytes not valid in UTF-8 let testData = Data(testBytes) - let result = try? hashFunction.computeHash(of: testData) - let expected = try? testData.computeMD5() - XCTAssertEqual(result, expected, "MD5 hash does not match expected value") + let computedHash = try? hashFunction.computeHash(of: testData) + guard case let .data(result)? = computedHash else { + XCTFail("MD5 computed hash is not a data type or is nil") + return + } + let expected = "ilWq/WLcPzYHQ8fAzwCCLg==" + XCTAssertEqual(result.base64EncodedString(), expected, "MD5 hash does not match expected value") } func testInvalidHashFunction() { @@ -96,52 +116,45 @@ class HashFunctionTests: XCTestCase { XCTAssertNil(invalidHashFunction, "Invalid hash function should return nil") } - func testDataConversionUInt32() { - let originalValue: UInt32 = 1234567890 - let dataRepresentation = Data(fromUInt32: originalValue) - let convertedValue = dataRepresentation.toUInt32() - XCTAssertEqual(originalValue, convertedValue, "Conversion to and from UInt32 failed") - } - func testHashFunctionToHexString() { let testData = Data("Hello, world!".utf8) // CRC32 if let crc32Function = HashFunction.from(string: "crc32"), - let crc32Result = try? crc32Function.computeHash(of: testData)?.toUInt32() { - XCTAssertEqual(crc32Result.toHexString(), "ebe6c6e6", "CRC32 hexadecimal representation does not match expected value") + let crc32Result = try? crc32Function.computeHash(of: testData)?.toHexString() { + XCTAssertEqual(crc32Result, "ebe6c6e6", "CRC32 hexadecimal representation does not match expected value") } else { XCTFail("CRC32 hash function not found or computation failed") } // CRC32C if let crc32cFunction = HashFunction.from(string: "crc32c"), - let crc32cResult = try? crc32cFunction.computeHash(of: testData)?.toUInt32() { - XCTAssertEqual(crc32cResult.toHexString(), "c8a106e5", "CRC32C hexadecimal representation does not match expected value") + let crc32cResult = try? crc32cFunction.computeHash(of: testData)?.toHexString() { + XCTAssertEqual(crc32cResult, "c8a106e5", "CRC32C hexadecimal representation does not match expected value") } else { XCTFail("CRC32C hash function not found or computation failed") } // SHA1 if let sha1Function = HashFunction.from(string: "sha1"), - let sha1Result = try? sha1Function.computeHash(of: testData) { - XCTAssertEqual(sha1Result.toHexString(), "943a702d06f34599aee1f8da8ef9f7296031d699", "SHA1 hexadecimal representation does not match expected value") + let sha1Result = try? sha1Function.computeHash(of: testData)?.toHexString() { + XCTAssertEqual(sha1Result, "943a702d06f34599aee1f8da8ef9f7296031d699", "SHA1 hexadecimal representation does not match expected value") } else { XCTFail("SHA1 hash function not found or computation failed") } // SHA256 if let sha256Function = HashFunction.from(string: "sha256"), - let sha256Result = try? sha256Function.computeHash(of: testData) { - XCTAssertEqual(sha256Result.toHexString(), "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3", "SHA256 hexadecimal representation does not match expected value") + let sha256Result = try? sha256Function.computeHash(of: testData)?.toHexString() { + XCTAssertEqual(sha256Result, "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3", "SHA256 hexadecimal representation does not match expected value") } else { XCTFail("SHA256 hash function not found or computation failed") } // MD5 if let md5Function = HashFunction.from(string: "md5"), - let md5Result = try? md5Function.computeHash(of: testData) { - XCTAssertEqual(md5Result.toHexString(), "6cd3556deb0da54bca060b4c39479839", "MD5 hexadecimal representation does not match expected value") + let md5Result = try? md5Function.computeHash(of: testData)?.toHexString() { + XCTAssertEqual(md5Result, "6cd3556deb0da54bca060b4c39479839", "MD5 hexadecimal representation does not match expected value") } else { XCTFail("MD5 hash function not found or computation failed") } From cc134ade5c0c54e74870ad45a26ffc2d91a3aea7 Mon Sep 17 00:00:00 2001 From: David Yaffe Date: Fri, 12 Jan 2024 13:44:28 -0500 Subject: [PATCH 6/7] add better error throwing --- Sources/ClientRuntime/Networking/HashFunction.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Sources/ClientRuntime/Networking/HashFunction.swift b/Sources/ClientRuntime/Networking/HashFunction.swift index 1c878c10d..41c7d6ba1 100644 --- a/Sources/ClientRuntime/Networking/HashFunction.swift +++ b/Sources/ClientRuntime/Networking/HashFunction.swift @@ -10,6 +10,11 @@ enum HashResult { case integer(UInt32) } +enum HashError: Error { + case invalidInput + case hashingFailed(reason: String) +} + enum HashFunction { case crc32, crc32c, sha1, sha256, md5 @@ -46,21 +51,21 @@ enum HashFunction { let hashed = try data.computeSHA1() return .data(hashed) } catch { - throw ClientRuntime.ClientError.unknownError("Error computing SHA1: \(error)") + throw HashError.hashingFailed(reason: "Error computing SHA1: \(error)") } case .sha256: do { let hashed = try data.computeSHA256() return .data(hashed) } catch { - throw ClientRuntime.ClientError.unknownError("Error computing SHA256: \(error)") + throw HashError.hashingFailed(reason: "Error computing SHA256: \(error)") } case .md5: do { let hashed = try data.computeMD5() return .data(hashed) } catch { - throw ClientRuntime.ClientError.unknownError("Error computing MD5: \(error)") + throw HashError.hashingFailed(reason: "Error computing MD5: \(error)") } } } From c892662c320d2450720ff5e0e374ed2932cf561a Mon Sep 17 00:00:00 2001 From: David Yaffe Date: Fri, 12 Jan 2024 14:06:32 -0500 Subject: [PATCH 7/7] make computeHash return a non-nullable result + clean up --- .../Networking/HashFunction.swift | 30 +++---------------- .../NetworkingTests/HashFunctionTests.swift | 10 +++---- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/Sources/ClientRuntime/Networking/HashFunction.swift b/Sources/ClientRuntime/Networking/HashFunction.swift index 41c7d6ba1..73129da48 100644 --- a/Sources/ClientRuntime/Networking/HashFunction.swift +++ b/Sources/ClientRuntime/Networking/HashFunction.swift @@ -38,13 +38,11 @@ enum HashFunction { } } - func computeHash(of data: Data) throws -> HashResult? { + func computeHash(of data: Data) throws -> HashResult { switch self { case .crc32: - // Should turn this result back into a UInt32 using .toUInt32() return .integer(data.computeCRC32()) case .crc32c: - // Should turn this result back into a UInt32 using .toUInt32() return .integer(data.computeCRC32C()) case .sha1: do { @@ -71,35 +69,15 @@ enum HashFunction { } } -protocol HexStringable { - func toHexString() -> String -} - -extension Data: HexStringable { - - // Convert a Data type to a hexademical String - func toHexString() -> String { - return map { String(format: "%02x", $0) }.joined() - } -} - -extension UInt32: HexStringable { - - // Convert a UInt32 type to a hexademical String - func toHexString() -> String { - return String(format: "%08x", self) - } -} - -extension HashResult: HexStringable { +extension HashResult { // Convert a HashResult to a hexadecimal String func toHexString() -> String { switch self { case .data(let data): - return data.toHexString() + return data.map { String(format: "%02x", $0) }.joined() case .integer(let integer): - return integer.toHexString() + return String(format: "%08x", integer) } } } diff --git a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift index 912b85c74..d8e394e48 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/HashFunctionTests.swift @@ -121,7 +121,7 @@ class HashFunctionTests: XCTestCase { // CRC32 if let crc32Function = HashFunction.from(string: "crc32"), - let crc32Result = try? crc32Function.computeHash(of: testData)?.toHexString() { + let crc32Result = try? crc32Function.computeHash(of: testData).toHexString() { XCTAssertEqual(crc32Result, "ebe6c6e6", "CRC32 hexadecimal representation does not match expected value") } else { XCTFail("CRC32 hash function not found or computation failed") @@ -129,7 +129,7 @@ class HashFunctionTests: XCTestCase { // CRC32C if let crc32cFunction = HashFunction.from(string: "crc32c"), - let crc32cResult = try? crc32cFunction.computeHash(of: testData)?.toHexString() { + let crc32cResult = try? crc32cFunction.computeHash(of: testData).toHexString() { XCTAssertEqual(crc32cResult, "c8a106e5", "CRC32C hexadecimal representation does not match expected value") } else { XCTFail("CRC32C hash function not found or computation failed") @@ -137,7 +137,7 @@ class HashFunctionTests: XCTestCase { // SHA1 if let sha1Function = HashFunction.from(string: "sha1"), - let sha1Result = try? sha1Function.computeHash(of: testData)?.toHexString() { + let sha1Result = try? sha1Function.computeHash(of: testData).toHexString() { XCTAssertEqual(sha1Result, "943a702d06f34599aee1f8da8ef9f7296031d699", "SHA1 hexadecimal representation does not match expected value") } else { XCTFail("SHA1 hash function not found or computation failed") @@ -145,7 +145,7 @@ class HashFunctionTests: XCTestCase { // SHA256 if let sha256Function = HashFunction.from(string: "sha256"), - let sha256Result = try? sha256Function.computeHash(of: testData)?.toHexString() { + let sha256Result = try? sha256Function.computeHash(of: testData).toHexString() { XCTAssertEqual(sha256Result, "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3", "SHA256 hexadecimal representation does not match expected value") } else { XCTFail("SHA256 hash function not found or computation failed") @@ -153,7 +153,7 @@ class HashFunctionTests: XCTestCase { // MD5 if let md5Function = HashFunction.from(string: "md5"), - let md5Result = try? md5Function.computeHash(of: testData)?.toHexString() { + let md5Result = try? md5Function.computeHash(of: testData).toHexString() { XCTAssertEqual(md5Result, "6cd3556deb0da54bca060b4c39479839", "MD5 hexadecimal representation does not match expected value") } else { XCTFail("MD5 hash function not found or computation failed")