diff --git a/Package.swift b/Package.swift index ffcd1e200..cd47ae1ba 100644 --- a/Package.swift +++ b/Package.swift @@ -248,7 +248,7 @@ packageTargets.append(contentsOf: [ cSettings: cSettings ), .target( - name: "AwsChecksums", + name: "AwsCChecksums", dependencies: ["AwsCCommon"], path: "aws-common-runtime/aws-checksums", exclude: awsCChecksumsExcludes, @@ -256,7 +256,7 @@ packageTargets.append(contentsOf: [ ), .target( name: "AwsCEventStream", - dependencies: ["AwsChecksums", "AwsCCommon", "AwsCIo", "AwsCCal"], + dependencies: ["AwsCChecksums", "AwsCCommon", "AwsCIo", "AwsCCal"], path: "aws-common-runtime/aws-c-event-stream", exclude: awsCEventStreamExcludes, cSettings: cSettings @@ -269,7 +269,7 @@ packageTargets.append(contentsOf: [ "AwsCCompression", "AwsCIo", "AwsCCommon", - "AwsChecksums", + "AwsCChecksums", "AwsCEventStream", .product(name: "Collections", package: "swift-collections")], path: "Source/AwsCommonRuntimeKit" diff --git a/Source/AwsCommonRuntimeKit/crt/Checksums.swift b/Source/AwsCommonRuntimeKit/crt/Checksums.swift new file mode 100644 index 000000000..a533de84c --- /dev/null +++ b/Source/AwsCommonRuntimeKit/crt/Checksums.swift @@ -0,0 +1,29 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0. + +import struct Foundation.Data +import AwsCChecksums + +extension Data { + + /// Computes the CRC32 over data. + /// - Parameter previousCrc32: Pass 0 in the previousCrc32 parameter as an initial value unless continuing to update a running crc in a subsequent call. + public func crc32(previousCrc32: UInt32 = 0) -> UInt32 { + self.withUnsafeBytes { bufferPointer in + return aws_checksums_crc32(bufferPointer.baseAddress?.assumingMemoryBound(to: UInt8.self), + Int32(count), + previousCrc32) + } + } + + /// Computes the crc32c over data. + /// - Parameter previousCrc32c: Pass 0 in the previousCrc32c parameter as an initial value unless continuing to update a running crc in a subsequent call. + public func crc32c(previousCrc32c: UInt32 = 0) -> UInt32 { + self.withUnsafeBytes { bufferPointer in + return aws_checksums_crc32c(bufferPointer.baseAddress?.assumingMemoryBound(to: UInt8.self), + Int32(count), + previousCrc32c) + } + } + +} diff --git a/Source/AwsCommonRuntimeKit/crt/Hash.swift b/Source/AwsCommonRuntimeKit/crt/Hash.swift new file mode 100644 index 000000000..c52d83437 --- /dev/null +++ b/Source/AwsCommonRuntimeKit/crt/Hash.swift @@ -0,0 +1,65 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0. +import struct Foundation.Date +import struct Foundation.Data +import struct Foundation.TimeInterval +import AwsCCal + +extension String { + + /// Computes the md5 hash over input and writes the digest output to 'output'. Use this if you don't need to stream the data you're hashing and you can load + /// the entire input to hash into memory. + /// - Parameter truncate: If you specify truncate something other than 0, the output will be truncated to that number of bytes. + public func base64EncodedMD5(truncate: Int = 0) throws -> String { + let bufferSize = 16 + var bufferData = Data(count: bufferSize) + try bufferData.withUnsafeMutableBytes { bufferPointer in + var buffer = aws_byte_buf_from_empty_array(bufferPointer.baseAddress, bufferSize) + guard self.withByteCursorPointer({ strCursorPointer in + aws_md5_compute(allocator.rawValue, strCursorPointer, &buffer, truncate) + }) == AWS_OP_SUCCESS else { + throw CommonRunTimeError.crtError(.makeFromLastError()) + } + } + return bufferData.base64EncodedString() + } +} + +extension Data { + + /// Computes the sha256 hash over data. + /// - Parameter truncate: If you specify truncate something other than 0, the output will be truncated to that number of bytes. For + /// example, if you want a SHA256 digest as the first 16 bytes, set truncate to 16. + public func sha256(truncate: Int = 0) throws -> Data { + try self.withUnsafeBytes { bufferPointer in + var byteCursor = aws_byte_cursor_from_array(bufferPointer.baseAddress, count) + let bufferSize = Int(AWS_SHA256_LEN) + var bufferData = Data(count: bufferSize) + try bufferData.withUnsafeMutableBytes { bufferDataPointer in + var buffer = aws_byte_buf_from_empty_array(bufferDataPointer.baseAddress, bufferSize) + guard aws_sha256_compute(allocator.rawValue, &byteCursor, &buffer, truncate) == AWS_OP_SUCCESS else { + throw CommonRunTimeError.crtError(.makeFromLastError()) + } + } + return bufferData + } + } + + /// Computes the sha1 hash over data. + /// - Parameter truncate: If you specify truncate something other than 0, the output will be truncated to that number of bytes. For + /// example, if you want a SHA1 digest as the first 10 bytes, set truncate to 10. + public func sha1(truncate: Int = 0) throws -> Data { + try self.withUnsafeBytes { bufferPointer in + var byteCursor = aws_byte_cursor_from_array(bufferPointer.baseAddress, count) + let bufferSize = Int(AWS_SHA1_LEN) + var bufferData = Data(count: bufferSize) + try bufferData.withUnsafeMutableBytes { bufferDataPointer in + var buffer = aws_byte_buf_from_empty_array(bufferDataPointer.baseAddress, bufferSize) + guard aws_sha1_compute(allocator.rawValue, &byteCursor, &buffer, truncate) == AWS_OP_SUCCESS else { + throw CommonRunTimeError.crtError(.makeFromLastError()) + } + } + return bufferData + } + } +} diff --git a/Source/AwsCommonRuntimeKit/crt/Utilities.swift b/Source/AwsCommonRuntimeKit/crt/Utilities.swift index bb927cf95..5a7900abd 100644 --- a/Source/AwsCommonRuntimeKit/crt/Utilities.swift +++ b/Source/AwsCommonRuntimeKit/crt/Utilities.swift @@ -29,20 +29,6 @@ class Box { extension String { - public func base64EncodedMD5(truncate: Int = 0) throws -> String { - let bufferSize = 16 - var bufferData = Data(count: bufferSize) - try bufferData.withUnsafeMutableBytes { bufferPointer in - var buffer = aws_byte_buf_from_empty_array(bufferPointer.baseAddress, bufferSize) - guard self.withByteCursorPointer({ strCursorPointer in - aws_md5_compute(allocator.rawValue, strCursorPointer, &buffer, truncate) - }) == AWS_OP_SUCCESS else { - throw CommonRunTimeError.crtError(.makeFromLastError()) - } - } - return bufferData.base64EncodedString() - } - func withByteCursor(_ body: (aws_byte_cursor) -> Result ) -> Result { return self.withCString { arg1C in @@ -62,22 +48,6 @@ extension String { extension Data { - /// Computes the sha256 hash over data. - public func sha256(truncate: Int = 0) throws -> Data { - try self.withUnsafeBytes { bufferPointer in - var byteCursor = aws_byte_cursor_from_array(bufferPointer.baseAddress, count) - let bufferSize = Int(AWS_SHA256_LEN) - var bufferData = Data(count: bufferSize) - try bufferData.withUnsafeMutableBytes { bufferDataPointer in - var buffer = aws_byte_buf_from_empty_array(bufferDataPointer.baseAddress, bufferSize) - guard aws_sha256_compute(allocator.rawValue, &byteCursor, &buffer, truncate) == AWS_OP_SUCCESS else { - throw CommonRunTimeError.crtError(.makeFromLastError()) - } - } - return bufferData - } - } - func withAWSByteBufPointer(_ body: (UnsafeMutablePointer) -> Result) -> Result { let count = self.count return self.withUnsafeBytes { rawBufferPointer -> Result in diff --git a/Test/AwsCommonRuntimeKitTests/crt/ChecksumsTests.swift b/Test/AwsCommonRuntimeKitTests/crt/ChecksumsTests.swift new file mode 100644 index 000000000..b4b6c58e9 --- /dev/null +++ b/Test/AwsCommonRuntimeKitTests/crt/ChecksumsTests.swift @@ -0,0 +1,21 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0. +import XCTest +import AwsCCommon +@testable import AwsCommonRuntimeKit + +class ChecksumsTests: XCBaseTestCase { + + func testCRC32() throws { + XCTAssertEqual("".data(using: .utf8)!.crc32(), 0) + XCTAssertEqual("Hello".data(using: .utf8)!.crc32(), 4157704578) + XCTAssertEqual("{\"foo\":\"base64 encoded sha1 checksum\"}".data(using: .utf8)!.crc32(), 1195144130) + } + + func testCRC32C() throws { + XCTAssertEqual("".data(using: .utf8)!.crc32c(), 0) + XCTAssertEqual("Hello".data(using: .utf8)!.crc32c(), 2178485787) + XCTAssertEqual("{\"foo\":\"base64 encoded sha1 checksum\"}".data(using: .utf8)!.crc32c(), 3565301023) + } + +} diff --git a/Test/AwsCommonRuntimeKitTests/crt/UtilityTests.swift b/Test/AwsCommonRuntimeKitTests/crt/HashTests.swift similarity index 77% rename from Test/AwsCommonRuntimeKitTests/crt/UtilityTests.swift rename to Test/AwsCommonRuntimeKitTests/crt/HashTests.swift index 7773842e3..310e7fb69 100644 --- a/Test/AwsCommonRuntimeKitTests/crt/UtilityTests.swift +++ b/Test/AwsCommonRuntimeKitTests/crt/HashTests.swift @@ -4,7 +4,7 @@ import XCTest import AwsCCommon @testable import AwsCommonRuntimeKit -class UtilityTests: XCBaseTestCase { +class HashTests: XCBaseTestCase { func testMd5() throws { let hello = "Hello" @@ -41,6 +41,26 @@ class UtilityTests: XCBaseTestCase { XCTAssertEqual(sha256Data.base64EncodedString(), "lBSnDP4sj/yN8eIVOJlv+vC56hw+7JtN0132GiMQXRg=") } + + func testSha1() throws { + let hello = "Hello".data(using: .utf8)! + XCTAssertEqual(try! hello.sha1().encodeToHexString(), "f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0") + } + + func testSha1EmptyString() throws { + let empty = "".data(using: .utf8)! + XCTAssertEqual(try! empty.sha1().encodeToHexString(), "da39a3ee5e6b4b0d3255bfef95601890afd80709") + } + + func testSha1PayloadOutOfScope() throws { + var sha1Data: Data! = nil + do { + let payload = "{\"foo\":\"base64 encoded sha1 checksum\"}".data(using: .utf8)! + sha1Data = try! payload.sha1() + } + XCTAssertEqual(sha1Data.encodeToHexString(), "bc3c579755c719da780d4429d6e9cf32f0c29c7e") + } + func testByteCursorListToStringArray() throws { let list: UnsafeMutablePointer = allocator.allocate(capacity: 1) defer {