From 3b3fe444371c453d8bd181f735e34d195721c545 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Mon, 18 Nov 2024 21:38:33 +0000 Subject: [PATCH 1/8] add UUID to swift-hash --- Package.swift | 22 ++++--- Sources/UUID/UUID.swift | 137 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 Sources/UUID/UUID.swift diff --git a/Package.swift b/Package.swift index a9f7326..18c047f 100644 --- a/Package.swift +++ b/Package.swift @@ -1,18 +1,19 @@ -// swift-tools-version:5.8 +// swift-tools-version:5.9 import PackageDescription let package:Package = .init( name: "swift-hash", platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6)], products: [ - .library(name: "Base16", targets: ["Base16"]), - .library(name: "Base64", targets: ["Base64"]), - .library(name: "CRC", targets: ["CRC"]), - .library(name: "InlineBuffer", targets: ["InlineBuffer"]), - .library(name: "MD5", targets: ["MD5"]), + .library(name: "Base16", targets: ["Base16"]), + .library(name: "Base64", targets: ["Base64"]), + .library(name: "CRC", targets: ["CRC"]), + .library(name: "InlineBuffer", targets: ["InlineBuffer"]), + .library(name: "MD5", targets: ["MD5"]), .library(name: "MessageAuthentication", targets: ["MessageAuthentication"]), - .library(name: "SHA1", targets: ["SHA1"]), - .library(name: "SHA2", targets: ["SHA2"]), + .library(name: "SHA1", targets: ["SHA1"]), + .library(name: "SHA2", targets: ["SHA2"]), + .library(name: "UUID", targets: ["UUID"]), ], dependencies: [ .package(url: "https://github.com/tayloraswift/swift-grammar", .upToNextMinor( @@ -56,6 +57,11 @@ let package:Package = .init( .target(name: "MessageAuthentication"), ]), + .target(name: "UUID", + dependencies: [ + .target(name: "Base16"), + ]), + .executableTarget(name: "Base64Tests", dependencies: [ .product(name: "Testing_", package: "swift-grammar"), diff --git a/Sources/UUID/UUID.swift b/Sources/UUID/UUID.swift new file mode 100644 index 0000000..dfe4978 --- /dev/null +++ b/Sources/UUID/UUID.swift @@ -0,0 +1,137 @@ +import Base16 + +@frozen public +struct UUID:Sendable +{ + /// The raw bit pattern of this UUID. + /// + /// UUIDs are always big-endian, regardless of platform endianness, so the first + /// tuple element contains the high 64 bits of the UUID, and the second tuple + /// element contains the low 64 bits. + /// + /// The constituent ``UInt64`` components are also big-endian, and so their + /// numeric value, as interpreted by the current host, may vary among host + /// machines. In particular, do *not* assume that the *n*-th bit in the *i*th + /// component corresponds to a fixed bit in the UUID. + public + let bitPattern:(UInt64, UInt64) + + /// Creates a UUID with the given bit pattern. Do not use this initializer + /// to create a UUID from integer literals; use ``init(_:_:)``, which accounts + /// for platform endianness, instead. + @inlinable public + init(bitPattern:(UInt64, UInt64)) + { + self.bitPattern = bitPattern + } + + /// Creates a UUID with the given high- and low-components. The components + /// are interpreted by platform endianness; therefore the values stored + /// into ``bitPattern`` may be different if the current host is not big-endian. + @inlinable public + init(_ high:UInt64, _ low:UInt64) + { + self.init(bitPattern: (high.bigEndian, low.bigEndian)) + } +} +extension UUID +{ + /// Generates an [RFC 4122](https://www.rfc-editor.org/rfc/rfc4122)-compliant + /// random UUID (version 4). + @inlinable public static + func random() -> Self + { + var bitPattern:(UInt64, UInt64) = + ( + .random(in: .min ... .max), + .random(in: .min ... .max) + ) + withUnsafeMutableBytes(of: &bitPattern) + { + $0[6] = 0b0100_0000 | + 0b0000_1111 & $0[6] + $0[8] = 0b1000_0000 | + 0b0011_1111 & $0[8] + } + return .init(bitPattern: bitPattern) + } + /// Creates a UUID by initializing its raw memory from a collection of bytes. + /// If the collection does not contain at least 16 bytes, the uninitalized + /// portion of the UUID is filled with zero bytes. + @inlinable public + init(_ bytes:Bytes) where Bytes:Collection, Bytes.Element == UInt8 + { + self.init(bitPattern: (0, 0)) + withUnsafeMutableBytes(of: &self) + { + assert($0.count == 16) + $0.copyBytes(from: bytes) + } + } +} +extension UUID:Equatable +{ + @inlinable public + static func == (a:Self, b:Self) -> Bool { a.bitPattern == b.bitPattern } +} +extension UUID:Hashable +{ + @inlinable public + func hash(into hasher:inout Hasher) + { + // do not use the ``UInt64`` components directly, their numeric + // values may differ by endianness + for byte:UInt8 in self + { + byte.hash(into: &hasher) + } + } +} +extension UUID:RandomAccessCollection +{ + @inlinable public + var startIndex:Int + { + 0 + } + @inlinable public + var endIndex:Int + { + 16 + } + @inlinable public + subscript(index:Int) -> UInt8 + { + precondition(self.indices ~= index) + return withUnsafeBytes(of: self) { $0[index] } + } +} +extension UUID:LosslessStringConvertible +{ + @inlinable public + init?(_ string:String) + { + // do this instead of decoding directly into raw memory so we can check + // that the byte count is exactly 16 + let bytes:[UInt8] = Base16.decode(string) + if bytes.count == 16 + { + self.init(bytes) + } + else + { + return nil + } + } + public + var description:String + { + """ + \(Base16.encode(self[ 0 ..< 4], with: Base16.LowercaseDigits.self))-\ + \(Base16.encode(self[ 4 ..< 6], with: Base16.LowercaseDigits.self))-\ + \(Base16.encode(self[ 6 ..< 8], with: Base16.LowercaseDigits.self))-\ + \(Base16.encode(self[ 8 ..< 10], with: Base16.LowercaseDigits.self))-\ + \(Base16.encode(self[10 ..< 16], with: Base16.LowercaseDigits.self)) + """ + } +} From 4f57ad3455b70a986c15ad39761b22264d2a333f Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Mon, 18 Nov 2024 21:40:33 +0000 Subject: [PATCH 2/8] require Swift 6 --- Package.swift | 7 +------ README.md | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Package.swift b/Package.swift index 18c047f..d40261c 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 import PackageDescription let package:Package = .init( @@ -93,12 +93,7 @@ for target:PackageDescription.Target in package.targets { var settings:[PackageDescription.SwiftSetting] = $0 ?? [] - settings.append(.enableUpcomingFeature("BareSlashRegexLiterals")) - settings.append(.enableUpcomingFeature("ConciseMagicFile")) - settings.append(.enableUpcomingFeature("DeprecateApplicationMain")) settings.append(.enableUpcomingFeature("ExistentialAny")) - settings.append(.enableUpcomingFeature("GlobalConcurrency")) - settings.append(.enableUpcomingFeature("IsolatedDefaultValues")) settings.append(.enableExperimentalFeature("StrictConcurrency")) settings.append(.define("DEBUG", .when(configuration: .debug))) diff --git a/README.md b/README.md index e4c18b7..284404b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ## Requirements -The swift-hash library requires Swift 5.9 or later. +The swift-hash library requires Swift 6.0 or later. | Platform | Status | From 0ecc2bcbd614e6efc0755dc582815b8eb3dcad1e Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Mon, 18 Nov 2024 21:58:37 +0000 Subject: [PATCH 3/8] migrate Base64Tests and CRCTests to Swift Testing --- Package.swift | 6 +- ...se64Test.swift => Encoding.TestCase.swift} | 33 ++-- Sources/Base64Tests/Encoding.swift | 136 +++++++++++++++ Sources/Base64Tests/Main.swift | 158 ------------------ Sources/CRCTests/CRC32Test.swift | 17 -- .../ChecksumComputation.TestCase.swift | 20 +++ .../{Main.swift => ChecksumComputation.swift} | 45 ++--- 7 files changed, 194 insertions(+), 221 deletions(-) rename Sources/Base64Tests/{Base64Test.swift => Encoding.TestCase.swift} (56%) create mode 100644 Sources/Base64Tests/Encoding.swift delete mode 100644 Sources/Base64Tests/Main.swift delete mode 100644 Sources/CRCTests/CRC32Test.swift create mode 100644 Sources/CRCTests/ChecksumComputation.TestCase.swift rename Sources/CRCTests/{Main.swift => ChecksumComputation.swift} (95%) diff --git a/Package.swift b/Package.swift index d40261c..c47f259 100644 --- a/Package.swift +++ b/Package.swift @@ -62,15 +62,13 @@ let package:Package = .init( .target(name: "Base16"), ]), - .executableTarget(name: "Base64Tests", + .testTarget(name: "Base64Tests", dependencies: [ - .product(name: "Testing_", package: "swift-grammar"), .target(name: "Base64"), ]), - .executableTarget(name: "CRCTests", + .testTarget(name: "CRCTests", dependencies: [ - .product(name: "Testing_", package: "swift-grammar"), .target(name: "CRC"), ]), diff --git a/Sources/Base64Tests/Base64Test.swift b/Sources/Base64Tests/Encoding.TestCase.swift similarity index 56% rename from Sources/Base64Tests/Base64Test.swift rename to Sources/Base64Tests/Encoding.TestCase.swift index 60dad7c..1cfe37a 100644 --- a/Sources/Base64Tests/Base64Test.swift +++ b/Sources/Base64Tests/Encoding.TestCase.swift @@ -1,24 +1,27 @@ import Base64 -struct Base64Test +extension Encoding { - let name:String - let degenerate:String? - let canonical:String - let expected:[UInt8] - - init(name:String, - degenerate:String? = nil, - canonical:String, - expected:[UInt8]) + struct TestCase { - self.name = name - self.degenerate = degenerate - self.canonical = canonical - self.expected = expected + let name:String + let degenerate:String? + let canonical:String + let expected:[UInt8] + + init(name:String, + degenerate:String? = nil, + canonical:String, + expected:[UInt8]) + { + self.name = name + self.degenerate = degenerate + self.canonical = canonical + self.expected = expected + } } } -extension Base64Test +extension Encoding.TestCase { init(name:String, degenerate:String? = nil, diff --git a/Sources/Base64Tests/Encoding.swift b/Sources/Base64Tests/Encoding.swift new file mode 100644 index 0000000..ce21008 --- /dev/null +++ b/Sources/Base64Tests/Encoding.swift @@ -0,0 +1,136 @@ +import Base64 +import Testing + +@Suite +struct Encoding +{ + private + static let binary:[TestCase] = [ + .init(name: "all", + degenerate: + """ + AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\ + MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\ + YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\ + kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\ + wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\ + 8PHy8/T19vf4+fr7/P3+/w + """, + canonical: + """ + AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\ + MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\ + YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\ + kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\ + wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\ + 8PHy8/T19vf4+fr7/P3+/w== + """, + expected: 0x00 ... 0xff), + + .init(name: "reversed", + degenerate: + """ + //79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQ\ + z87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGg\ + n56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFw\ + b25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFA\ + Pz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQ\ + Dw4NDAsKCQgHBgUEAwIBAA + """, + canonical: + """ + //79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQ\ + z87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGg\ + n56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFw\ + b25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFA\ + Pz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQ\ + Dw4NDAsKCQgHBgUEAwIBAA== + """, + expected: (0x00 ... 0xff).reversed()), + ] + + private + static let string:[TestCase] = [ + .init(name: "empty", + canonical: "", + expected: ""), + + .init(name: "single", + degenerate: "YQ", + canonical: "YQ==", + expected: "a"), + + .init(name: "double", + degenerate: "YWI", + canonical: "YWI=", + expected: "ab"), + + .init(name: "triple", + canonical: "YWJj", + expected: "abc"), + + .init(name: "basic", + canonical: "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", + expected: "Many hands make light work."), + + .init(name: "whitespace", + degenerate: + """ + T\u{0C}WFueSBoY W5kc\ryBtYWt\tlIGxpZ2 + h0IHd + + vcmsu + """, + canonical: "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", + expected: "Many hands make light work."), + + .init(name: "padding-11-16", + degenerate: "bGlnaHQgd29yay4", + canonical: "bGlnaHQgd29yay4=", + expected: "light work."), + + .init(name: "padding-10-16", + degenerate: "bGlnaHQgd29yaw", + canonical: "bGlnaHQgd29yaw==", + expected: "light work"), + + .init(name: "padding-9-12", + canonical: "bGlnaHQgd29y", + expected: "light wor"), + + .init(name: "padding-8-12", + degenerate: "bGlnaHQgd28", + canonical: "bGlnaHQgd28=", + expected: "light wo"), + + .init(name: "padding-7-12", + degenerate: "bGlnaHQgdw", + canonical: "bGlnaHQgdw==", + expected: "light w"), + ] + + @Test(arguments: binary + string) + static func defaultDigits(_ test:TestCase) throws + { + #expect(test.expected == Base64.decode(test.canonical.utf8, to: [UInt8].self)) + #expect(test.canonical == Base64.encode(test.expected)) + + if let degenerate:String = test.degenerate + { + let decoded:[UInt8] = Base64.decode(degenerate, to: [UInt8].self) + let encoded:String = Base64.encode(decoded) + #expect(decoded == test.expected) + #expect(encoded == test.canonical) + } + } + + @Test + static func urlSafeDigits() + { + let encoded:String = Base64.encode("<>".utf8, + padding: false, + with: Base64.SafeDigits.self) + + #expect(encoded == "PDw_Pz8-Pg") + } +} diff --git a/Sources/Base64Tests/Main.swift b/Sources/Base64Tests/Main.swift deleted file mode 100644 index e5ce7e4..0000000 --- a/Sources/Base64Tests/Main.swift +++ /dev/null @@ -1,158 +0,0 @@ -import Base64 -import Testing_ - -@main -enum Main:TestMain, TestBattery -{ - static - func run(tests:TestGroup) - { - let binary:[Base64Test] = - [ - .init(name: "all", - degenerate: - """ - AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\ - MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\ - YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\ - kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\ - wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\ - 8PHy8/T19vf4+fr7/P3+/w - """, - canonical: - """ - AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v\ - MDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f\ - YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6P\ - kJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/\ - wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v\ - 8PHy8/T19vf4+fr7/P3+/w== - """, - expected: 0x00 ... 0xff), - - .init(name: "reversed", - degenerate: - """ - //79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQ\ - z87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGg\ - n56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFw\ - b25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFA\ - Pz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQ\ - Dw4NDAsKCQgHBgUEAwIBAA - """, - canonical: - """ - //79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQ\ - z87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGg\ - n56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFw\ - b25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFA\ - Pz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQ\ - Dw4NDAsKCQgHBgUEAwIBAA== - """, - expected: (0x00 ... 0xff).reversed()), - ] - let string:[Base64Test] = - [ - .init(name: "empty", - canonical: "", - expected: ""), - - .init(name: "single", - degenerate: "YQ", - canonical: "YQ==", - expected: "a"), - - .init(name: "double", - degenerate: "YWI", - canonical: "YWI=", - expected: "ab"), - - .init(name: "triple", - canonical: "YWJj", - expected: "abc"), - - .init(name: "basic", - canonical: "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", - expected: "Many hands make light work."), - - .init(name: "whitespace", - degenerate: - """ - T\u{0C}WFueSBoY W5kc\ryBtYWt\tlIGxpZ2 - h0IHd - - vcmsu - """, - canonical: "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", - expected: "Many hands make light work."), - - .init(name: "padding-11-16", - degenerate: "bGlnaHQgd29yay4", - canonical: "bGlnaHQgd29yay4=", - expected: "light work."), - - .init(name: "padding-10-16", - degenerate: "bGlnaHQgd29yaw", - canonical: "bGlnaHQgd29yaw==", - expected: "light work"), - - .init(name: "padding-9-12", - canonical: "bGlnaHQgd29y", - expected: "light wor"), - - .init(name: "padding-8-12", - degenerate: "bGlnaHQgd28", - canonical: "bGlnaHQgd28=", - expected: "light wo"), - - .init(name: "padding-7-12", - degenerate: "bGlnaHQgdw", - canonical: "bGlnaHQgdw==", - expected: "light w"), - ] - - for (tests, cases):(TestGroup?, [Base64Test]) in - [ - (tests / "binary", binary), - (tests / "string", string), - ] - { - guard let tests:TestGroup - else - { - continue - } - - for test:Base64Test in cases - { - guard let tests:TestGroup = tests / test.name - else - { - continue - } - - tests.expect(Base64.decode(test.canonical.utf8, to: [UInt8].self) ..? - test.expected) - tests.expect(Base64.encode(test.expected) ..? - test.canonical) - - if let degenerate:String = test.degenerate - { - let decoded:[UInt8] = Base64.decode(degenerate, to: [UInt8].self) - let encoded:String = Base64.encode(decoded) - tests.expect(decoded ..? test.expected) - tests.expect(encoded ..? test.canonical) - } - } - } - - if let tests:TestGroup = tests / "URL" - { - let encoded:String = Base64.encode("<>".utf8, - padding: false, - with: Base64.SafeDigits.self) - - tests.expect(encoded ..? "PDw_Pz8-Pg") - } - } -} diff --git a/Sources/CRCTests/CRC32Test.swift b/Sources/CRCTests/CRC32Test.swift deleted file mode 100644 index e309f7e..0000000 --- a/Sources/CRCTests/CRC32Test.swift +++ /dev/null @@ -1,17 +0,0 @@ -import CRC - -struct CRC32Test -{ - let name:String - let message:String - let expected:CRC32 - - init(name:String, - message:String, - expected:CRC32) - { - self.name = name - self.message = message - self.expected = expected - } -} diff --git a/Sources/CRCTests/ChecksumComputation.TestCase.swift b/Sources/CRCTests/ChecksumComputation.TestCase.swift new file mode 100644 index 0000000..6bfb990 --- /dev/null +++ b/Sources/CRCTests/ChecksumComputation.TestCase.swift @@ -0,0 +1,20 @@ +import CRC + +extension ChecksumComputation +{ + struct TestCase + { + let name:String + let message:String + let expected:CRC32 + + init(name:String, + message:String, + expected:CRC32) + { + self.name = name + self.message = message + self.expected = expected + } + } +} diff --git a/Sources/CRCTests/Main.swift b/Sources/CRCTests/ChecksumComputation.swift similarity index 95% rename from Sources/CRCTests/Main.swift rename to Sources/CRCTests/ChecksumComputation.swift index 5535e14..b2d620f 100644 --- a/Sources/CRCTests/Main.swift +++ b/Sources/CRCTests/ChecksumComputation.swift @@ -1,20 +1,17 @@ import CRC -import Testing_ +import Testing -@main -enum Main:TestMain, TestBattery +@Suite +struct ChecksumComputation { - static - func run(tests:TestGroup) - { - // https://www.rfc-editor.org/rfc/rfc3720#appendix-B.4 - let cases:[CRC32Test] = - [ - .init(name: "basic", message: "123456789", + private + static let cases:[TestCase] = [ + // https://www.rfc-editor.org/rfc/rfc3720#appendix-B.4 + .init(name: "basic", message: "123456789", expected: 0xcb_f4_39_26), - .init(name: "apache-license", - message: + .init(name: "apache-license", + message: """ Apache License @@ -220,19 +217,13 @@ enum Main:TestMain, TestBattery limitations under the License. """, - expected: 0xaf_fb_88_44), - ] - - for test:CRC32Test in cases - { - guard let tests:TestGroup = tests / test.name - else - { - continue - } - - let computed:CRC32 = .init(hashing: test.message.utf8) - tests.expect(computed ==? test.expected) - } - } + expected: 0xaf_fb_88_44), + ] + + @Test(arguments: cases) + static func checksum(_ test:TestCase) throws + { + let computed:CRC32 = .init(hashing: test.message.utf8) + #expect(computed == test.expected) + } } From fa813a23cbe25f6243f89c71e1467afc7b6d61dc Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Mon, 18 Nov 2024 21:59:20 +0000 Subject: [PATCH 4/8] update golden output --- Sources/CRCTests/ChecksumComputation.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/CRCTests/ChecksumComputation.swift b/Sources/CRCTests/ChecksumComputation.swift index b2d620f..1e08547 100644 --- a/Sources/CRCTests/ChecksumComputation.swift +++ b/Sources/CRCTests/ChecksumComputation.swift @@ -202,7 +202,7 @@ struct ChecksumComputation same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2022 Kelvin Ma (@taylorswift) + Copyright 2022 Dianna Ma (@taylorswift) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -217,7 +217,7 @@ struct ChecksumComputation limitations under the License. """, - expected: 0xaf_fb_88_44), + expected: 0x71_0c_55_9e), ] @Test(arguments: cases) From 985e1f39b41e22963b02820a6e3770b85d2df512 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Mon, 18 Nov 2024 22:18:44 +0000 Subject: [PATCH 5/8] format --- Sources/UUID/UUID.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/UUID/UUID.swift b/Sources/UUID/UUID.swift index dfe4978..6a1b2d4 100644 --- a/Sources/UUID/UUID.swift +++ b/Sources/UUID/UUID.swift @@ -38,8 +38,8 @@ extension UUID { /// Generates an [RFC 4122](https://www.rfc-editor.org/rfc/rfc4122)-compliant /// random UUID (version 4). - @inlinable public static - func random() -> Self + @inlinable public + static func random() -> Self { var bitPattern:(UInt64, UInt64) = ( From c18c368c270f981afa1fd8a95fc9db55fda42fc8 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Mon, 18 Nov 2024 22:19:41 +0000 Subject: [PATCH 6/8] port remaining tests to Swift Testing, and finally drop the swift-grammar dependency --- Package.resolved | 23 -- Package.swift | 12 +- Sources/Base64Tests/Encoding.swift | 2 +- Sources/CRCTests/ChecksumComputation.swift | 2 +- Sources/MD5Tests/Hashing.swift | 46 ++++ Sources/MD5Tests/Main.swift | 80 ------ Sources/MD5Tests/ParsingAndFormatting.swift | 17 ++ Sources/SHA2Tests/HMAC256.TestCase.swift | 21 ++ Sources/SHA2Tests/HMAC256.swift | 167 ++++++++++++ Sources/SHA2Tests/HMAC256Test.swift | 18 -- .../SHA2Tests/KeyDerivation.TestCase.swift | 20 ++ Sources/SHA2Tests/KeyDerivation.swift | 86 ++++++ Sources/SHA2Tests/KeyDerivationTest.swift | 17 -- Sources/SHA2Tests/Main.swift | 252 ------------------ 14 files changed, 362 insertions(+), 401 deletions(-) delete mode 100644 Package.resolved create mode 100644 Sources/MD5Tests/Hashing.swift delete mode 100644 Sources/MD5Tests/Main.swift create mode 100644 Sources/MD5Tests/ParsingAndFormatting.swift create mode 100644 Sources/SHA2Tests/HMAC256.TestCase.swift create mode 100644 Sources/SHA2Tests/HMAC256.swift delete mode 100644 Sources/SHA2Tests/HMAC256Test.swift create mode 100644 Sources/SHA2Tests/KeyDerivation.TestCase.swift create mode 100644 Sources/SHA2Tests/KeyDerivation.swift delete mode 100644 Sources/SHA2Tests/KeyDerivationTest.swift delete mode 100644 Sources/SHA2Tests/Main.swift diff --git a/Package.resolved b/Package.resolved deleted file mode 100644 index c248d0c..0000000 --- a/Package.resolved +++ /dev/null @@ -1,23 +0,0 @@ -{ - "pins" : [ - { - "identity" : "swift-atomics", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-atomics.git", - "state" : { - "revision" : "cd142fd2f64be2100422d658e7411e39489da985", - "version" : "1.2.0" - } - }, - { - "identity" : "swift-grammar", - "kind" : "remoteSourceControl", - "location" : "https://github.com/tayloraswift/swift-grammar", - "state" : { - "revision" : "642d5957896f06b03e35c48fc439488367d3fd21", - "version" : "0.4.0" - } - } - ], - "version" : 2 -} diff --git a/Package.swift b/Package.swift index c47f259..c4a2c00 100644 --- a/Package.swift +++ b/Package.swift @@ -3,7 +3,7 @@ import PackageDescription let package:Package = .init( name: "swift-hash", - platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6)], + platforms: [.macOS(.v14), .iOS(.v17), .tvOS(.v17), .visionOS(.v1), .watchOS(.v10)], products: [ .library(name: "Base16", targets: ["Base16"]), .library(name: "Base64", targets: ["Base64"]), @@ -15,10 +15,6 @@ let package:Package = .init( .library(name: "SHA2", targets: ["SHA2"]), .library(name: "UUID", targets: ["UUID"]), ], - dependencies: [ - .package(url: "https://github.com/tayloraswift/swift-grammar", .upToNextMinor( - from: "0.4.0")), - ], targets: [ .target(name: "BaseDigits"), @@ -72,15 +68,13 @@ let package:Package = .init( .target(name: "CRC"), ]), - .executableTarget(name: "MD5Tests", + .testTarget(name: "MD5Tests", dependencies: [ .target(name: "MD5"), - .product(name: "Testing_", package: "swift-grammar"), ]), - .executableTarget(name: "SHA2Tests", + .testTarget(name: "SHA2Tests", dependencies: [ - .product(name: "Testing_", package: "swift-grammar"), .target(name: "SHA2"), ]), ] diff --git a/Sources/Base64Tests/Encoding.swift b/Sources/Base64Tests/Encoding.swift index ce21008..9ed086b 100644 --- a/Sources/Base64Tests/Encoding.swift +++ b/Sources/Base64Tests/Encoding.swift @@ -109,7 +109,7 @@ struct Encoding expected: "light w"), ] - @Test(arguments: binary + string) + @Test(arguments: Self.binary + Self.string) static func defaultDigits(_ test:TestCase) throws { #expect(test.expected == Base64.decode(test.canonical.utf8, to: [UInt8].self)) diff --git a/Sources/CRCTests/ChecksumComputation.swift b/Sources/CRCTests/ChecksumComputation.swift index 1e08547..0fc9f5e 100644 --- a/Sources/CRCTests/ChecksumComputation.swift +++ b/Sources/CRCTests/ChecksumComputation.swift @@ -220,7 +220,7 @@ struct ChecksumComputation expected: 0x71_0c_55_9e), ] - @Test(arguments: cases) + @Test(arguments: Self.cases) static func checksum(_ test:TestCase) throws { let computed:CRC32 = .init(hashing: test.message.utf8) diff --git a/Sources/MD5Tests/Hashing.swift b/Sources/MD5Tests/Hashing.swift new file mode 100644 index 0000000..02db3fd --- /dev/null +++ b/Sources/MD5Tests/Hashing.swift @@ -0,0 +1,46 @@ +import MD5 +import Testing + +// Test vectors from: https://datatracker.ietf.org/doc/html/rfc1321 +@Suite +struct Hashing +{ + @Test(arguments: [ + (0xd41d8cd98f00b204e9800998ecf8427e, []), + (0x0cc175b9c0f1b6a831c399e269772661, [0x61]), + (0x900150983cd24fb0d6963f7d28e17f72, [0x61, 0x62, 0x63]), + ] as [(MD5, [UInt8])]) + static func binary(_ test:(expected:MD5, input:[UInt8])) + { + let md5:MD5 = .init(hashing: test.input) + #expect(md5 == test.expected) + } + + @Test(arguments: [ + ( + 0xf96b697d7cb7938d525a2f31aaf161d0, + "message digest" + ), + ( + 0xc3fcd3d76192e4007dfb496cca67e13b, + "abcdefghijklmnopqrstuvwxyz" + ), + ( + 0xd174ab98d277d9f5a5611c2c9f419d9f, + """ + ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 + """ + ), + ( + 0x57edf4a22be3c955ac49da2e2107b67a, + """ + 12345678901234567890123456789012345678901234567890123456789012345678901234567890 + """ + ), + ] as [(MD5, String)]) + static func string(_ test:(expected:MD5, input:String)) + { + let md5:MD5 = .init(hashing: test.input.utf8) + #expect(md5 == test.expected) + } +} diff --git a/Sources/MD5Tests/Main.swift b/Sources/MD5Tests/Main.swift deleted file mode 100644 index 9282d5f..0000000 --- a/Sources/MD5Tests/Main.swift +++ /dev/null @@ -1,80 +0,0 @@ -import MD5 -import Testing_ - -@main -enum Main:TestMain, TestBattery -{ - static - func run(tests:TestGroup) - { - guard #available( - macOS 13.3, - iOS 16.4, - macCatalyst 16.4, - tvOS 16.4, - visionOS 1.0, - watchOS 9.4, - * - ) - else - { - fatalError("MD5Tests requires macOS 13.3+, iOS 16.4+, tvOS 16.4+, visionOS 1.0+, or watchOS 9.4+") - } - - if let tests:TestGroup = tests / "Strings" - { - let string:String = "d41d8cd98f00b204e9800998ecf8427e" - let hash:MD5 = 0xd41d8cd98f00b204e9800998ecf8427e - - if let tests:TestGroup = tests / "Parsing", - let parsed:MD5 = tests.expect(value: .init(string)) - { - tests.expect(parsed ==? hash) - } - if let tests:TestGroup = tests / "Formatting" - { - tests.expect("\(hash)" ==? string) - } - } - // Test vectors from: https://datatracker.ietf.org/doc/html/rfc1321 - if let tests:TestGroup = tests / "Empty" - { - let md5:MD5 = .init(hashing: []) - tests.expect(md5 ==? 0xd41d8cd98f00b204e9800998ecf8427e) - } - if let tests:TestGroup = tests / "I" - { - let md5:MD5 = .init(hashing: [0x61]) - tests.expect(md5 ==? 0x0cc175b9c0f1b6a831c399e269772661) - } - if let tests:TestGroup = tests / "II" - { - let md5:MD5 = .init(hashing: [0x61, 0x62, 0x63]) - tests.expect(md5 ==? 0x900150983cd24fb0d6963f7d28e17f72) - } - if let tests:TestGroup = tests / "III" - { - let md5:MD5 = .init(hashing: "message digest".utf8) - tests.expect(md5 ==? 0xf96b697d7cb7938d525a2f31aaf161d0) - } - if let tests:TestGroup = tests / "IV" - { - let md5:MD5 = .init(hashing: "abcdefghijklmnopqrstuvwxyz".utf8) - tests.expect(md5 ==? 0xc3fcd3d76192e4007dfb496cca67e13b) - } - if let tests:TestGroup = tests / "V" - { - let md5:MD5 = .init(hashing: """ - ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 - """.utf8) - tests.expect(md5 ==? 0xd174ab98d277d9f5a5611c2c9f419d9f) - } - if let tests:TestGroup = tests / "VI" - { - let md5:MD5 = .init(hashing: """ - 12345678901234567890123456789012345678901234567890123456789012345678901234567890 - """.utf8) - tests.expect(md5 ==? 0x57edf4a22be3c955ac49da2e2107b67a) - } - } -} diff --git a/Sources/MD5Tests/ParsingAndFormatting.swift b/Sources/MD5Tests/ParsingAndFormatting.swift new file mode 100644 index 0000000..5e5030c --- /dev/null +++ b/Sources/MD5Tests/ParsingAndFormatting.swift @@ -0,0 +1,17 @@ +import MD5 +import Testing + +@Suite +struct ParsingAndFormatting +{ + @Test + static func strings() throws + { + let string:String = "d41d8cd98f00b204e9800998ecf8427e" + let hash:MD5 = 0xd41d8cd98f00b204e9800998ecf8427e + + let parsed:MD5 = try #require(.init(string)) + #expect(parsed == hash) + #expect(string == "\(hash)") + } +} diff --git a/Sources/SHA2Tests/HMAC256.TestCase.swift b/Sources/SHA2Tests/HMAC256.TestCase.swift new file mode 100644 index 0000000..27885fb --- /dev/null +++ b/Sources/SHA2Tests/HMAC256.TestCase.swift @@ -0,0 +1,21 @@ +import Base16 +import SHA2 + +extension HMAC256 +{ + struct TestCase + { + let name:String + let key:String, + message:String, + expected:SHA256 + + init(name:String, key:String, message:String, hmac256 expected:SHA256) + { + self.name = name + self.key = key + self.message = message + self.expected = expected + } + } +} diff --git a/Sources/SHA2Tests/HMAC256.swift b/Sources/SHA2Tests/HMAC256.swift new file mode 100644 index 0000000..b060f74 --- /dev/null +++ b/Sources/SHA2Tests/HMAC256.swift @@ -0,0 +1,167 @@ +import Base16 +import SHA2 +import Testing + +@Suite +struct HMAC256 +{ + // https://datatracker.ietf.org/doc/html/rfc4231 + static let cases:[TestCase] = [ + .init(name: "1", + key: + """ + 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b + 0b0b0b0b + """, + message: "4869205468657265", + hmac256: + """ + b0344c61d8db38535ca8afceaf0bf12b + 881dc200c9833da726e9376c2e32cff7 + """ + ), + + .init(name: "2", + key: "4a656665", + message: + """ + 7768617420646f2079612077616e7420 + 666f72206e6f7468696e673f + """, + hmac256: + """ + 5bdcc146bf60754e6a042426089575c7 + 5a003f089d2739839dec58b964ec3843 + """ + ), + + .init(name: "3", + key: + """ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaa + """, + message: + """ + dddddddddddddddddddddddddddddddd + dddddddddddddddddddddddddddddddd + dddddddddddddddddddddddddddddddd + dddd + """, + hmac256: + """ + 773ea91e36800e46854db8ebd09181a7 + 2959098b3ef8c122d9635514ced565fe + """ + ), + + .init(name: "4", + key: + """ + 0102030405060708090a0b0c0d0e0f10 + 111213141516171819 + """, + message: + """ + cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd + cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd + cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd + cdcd + """, + hmac256: + """ + 82558a389a443c0ea4cc819899f2083a + 85f0faa3e578f8077a2e3ff46729665b + """ + ), + + .init(name: "5", + key: + """ + 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c + 0c0c0c0c + """, + message: + """ + 546573742057697468205472756e6361 + 74696f6e + """, + hmac256: + """ + a3b6167473100ee06e0c796c2955552b + fa6f7c0a6a8aef8b93f860aab0cd20c5 + """ + ), + + .init(name: "6", + key: + """ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa + """, + message: + """ + 54657374205573696e67204c61726765 + 72205468616e20426c6f636b2d53697a + 65204b6579202d2048617368204b6579 + 204669727374 + """, + hmac256: + """ + 60e431591ee0b67f0d8a26aacbf5b77f + 8e0bc6213728c5140546040f0ee37f54 + """ + ), + + .init(name: "7", + key: + """ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + aaaaaa + """, + message: + """ + 54686973206973206120746573742075 + 73696e672061206c6172676572207468 + 616e20626c6f636b2d73697a65206b65 + 7920616e642061206c61726765722074 + 68616e20626c6f636b2d73697a652064 + 6174612e20546865206b6579206e6565 + 647320746f2062652068617368656420 + 6265666f7265206265696e6720757365 + 642062792074686520484d414320616c + 676f726974686d2e + """, + hmac256: + """ + 9b09ffa71b942fcb27635fbcd5b0e944 + bfdc63644f0713938a7f51535c3a35e2 + """ + ), + ] + + @Test(arguments: Self.cases) + static func binary(_ test:TestCase) + { + let key:[UInt8] = Base16.decode(test.key.utf8) + let message:[UInt8] = Base16.decode(test.message.utf8) + + let computed:SHA256 = .init(authenticating: message, key: key) + + #expect(computed == test.expected) + } +} diff --git a/Sources/SHA2Tests/HMAC256Test.swift b/Sources/SHA2Tests/HMAC256Test.swift deleted file mode 100644 index f4af964..0000000 --- a/Sources/SHA2Tests/HMAC256Test.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Base16 -import SHA2 - -struct MessageAuthenticationTest -{ - let name:String - let key:String, - message:String, - expected:SHA256 - - init(name:String, key:String, message:String, hmac256 expected:SHA256) - { - self.name = name - self.key = key - self.message = message - self.expected = expected - } -} diff --git a/Sources/SHA2Tests/KeyDerivation.TestCase.swift b/Sources/SHA2Tests/KeyDerivation.TestCase.swift new file mode 100644 index 0000000..f276954 --- /dev/null +++ b/Sources/SHA2Tests/KeyDerivation.TestCase.swift @@ -0,0 +1,20 @@ +extension KeyDerivation +{ + struct TestCase + { + let name:String + let password:String, + salt:String, + iterations:Int, + derived:[UInt8] + + init(name:String, password:String, salt:String, iterations:Int, derived:[UInt8]) + { + self.name = name + self.password = password + self.salt = salt + self.iterations = iterations + self.derived = derived + } + } +} diff --git a/Sources/SHA2Tests/KeyDerivation.swift b/Sources/SHA2Tests/KeyDerivation.swift new file mode 100644 index 0000000..6299af0 --- /dev/null +++ b/Sources/SHA2Tests/KeyDerivation.swift @@ -0,0 +1,86 @@ +import SHA2 +import Testing + +@Suite +struct KeyDerivation +{ + // https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors + static let cases:[TestCase] = [ + .init(name: "single-iteration", + password: "password", salt: "salt", iterations: 1, + derived: + [ + 0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, + 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, + 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, + 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b, + ]), + + .init(name: "multiple-iterations", + password: "password", salt: "salt", iterations: 2, + derived: + [ + 0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3, + 0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0, + 0x2a, 0x30, 0x3f, 0x8e, 0xf3, 0xc2, 0x51, 0xdf, + 0xd6, 0xe2, 0xd8, 0x5a, 0x95, 0x47, 0x4c, 0x43, + ]), + + .init(name: "many-iterations", + password: "password", salt: "salt", iterations: 4096, + derived: + [ + 0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, + 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, + 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, + 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a, + ]), + + // disabled to keep the CI flowing + + // $0.test(case: KeyDerivationTest.init(name: "absurd-iterations", + // password: "password", salt: "salt", iterations: 16777216, + // derived: + // [ + // 0xcf, 0x81, 0xc6, 0x6f, 0xe8, 0xcf, 0xc0, 0x4d, + // 0x1f, 0x31, 0xec, 0xb6, 0x5d, 0xab, 0x40, 0x89, + // 0xf7, 0xf1, 0x79, 0xe8, 0x9b, 0x3b, 0x0b, 0xcb, + // 0x17, 0xad, 0x10, 0xe3, 0xac, 0x6e, 0xba, 0x46, + // ])) + + .init(name: "multiple-blocks", + password: "passwordPASSWORDpassword", + salt: "saltSALTsaltSALTsaltSALTsaltSALTsalt", + iterations: 4096, + derived: + [ + 0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f, + 0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf, + 0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18, + 0x1c, 0x4e, 0x2a, 0x1f, 0xb8, 0xdd, 0x53, 0xe1, + 0xc6, 0x35, 0x51, 0x8c, 0x7d, 0xac, 0x47, 0xe9, + ]), + + .init(name: "null-bytes", + password: "pass\u{0}word", salt: "sa\u{0}lt", + iterations: 4096, + derived: + [ + 0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89, + 0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87, + ]), + ] + + @Test(arguments: Self.cases) + static func binary(_ test:TestCase) + { + let (quotient, remainder):(Int, Int) = test.derived.count.quotientAndRemainder( + dividingBy: SHA256.count) + let key:[UInt8] = SHA256.pbkdf2(password: test.password.utf8, + salt: test.salt.utf8, + iterations: test.iterations, + blocks: quotient + max(remainder, 1)) + + #expect(key.prefix(test.derived.count) == test.derived[...]) + } +} diff --git a/Sources/SHA2Tests/KeyDerivationTest.swift b/Sources/SHA2Tests/KeyDerivationTest.swift deleted file mode 100644 index 421601d..0000000 --- a/Sources/SHA2Tests/KeyDerivationTest.swift +++ /dev/null @@ -1,17 +0,0 @@ -struct KeyDerivationTest -{ - let name:String - let password:String, - salt:String, - iterations:Int, - derived:[UInt8] - - init(name:String, password:String, salt:String, iterations:Int, derived:[UInt8]) - { - self.name = name - self.password = password - self.salt = salt - self.iterations = iterations - self.derived = derived - } -} diff --git a/Sources/SHA2Tests/Main.swift b/Sources/SHA2Tests/Main.swift deleted file mode 100644 index a1a8996..0000000 --- a/Sources/SHA2Tests/Main.swift +++ /dev/null @@ -1,252 +0,0 @@ -import Base16 -import SHA2 -import Testing_ - -@main -enum Main:TestMain, TestBattery -{ - static - func run(tests:TestGroup) - { - // https://datatracker.ietf.org/doc/html/rfc4231 - for test:MessageAuthenticationTest in - [ - .init(name: "1", - key: - """ - 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b - 0b0b0b0b - """, - message: "4869205468657265", - hmac256: - """ - b0344c61d8db38535ca8afceaf0bf12b - 881dc200c9833da726e9376c2e32cff7 - """ - ), - - .init(name: "2", - key: "4a656665", - message: - """ - 7768617420646f2079612077616e7420 - 666f72206e6f7468696e673f - """, - hmac256: - """ - 5bdcc146bf60754e6a042426089575c7 - 5a003f089d2739839dec58b964ec3843 - """ - ), - - .init(name: "3", - key: - """ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaa - """, - message: - """ - dddddddddddddddddddddddddddddddd - dddddddddddddddddddddddddddddddd - dddddddddddddddddddddddddddddddd - dddd - """, - hmac256: - """ - 773ea91e36800e46854db8ebd09181a7 - 2959098b3ef8c122d9635514ced565fe - """ - ), - - .init(name: "4", - key: - """ - 0102030405060708090a0b0c0d0e0f10 - 111213141516171819 - """, - message: - """ - cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd - cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd - cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd - cdcd - """, - hmac256: - """ - 82558a389a443c0ea4cc819899f2083a - 85f0faa3e578f8077a2e3ff46729665b - """ - ), - - .init(name: "5", - key: - """ - 0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c - 0c0c0c0c - """, - message: - """ - 546573742057697468205472756e6361 - 74696f6e - """, - hmac256: - """ - a3b6167473100ee06e0c796c2955552b - fa6f7c0a6a8aef8b93f860aab0cd20c5 - """ - ), - - .init(name: "6", - key: - """ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaa - """, - message: - """ - 54657374205573696e67204c61726765 - 72205468616e20426c6f636b2d53697a - 65204b6579202d2048617368204b6579 - 204669727374 - """, - hmac256: - """ - 60e431591ee0b67f0d8a26aacbf5b77f - 8e0bc6213728c5140546040f0ee37f54 - """ - ), - - .init(name: "7", - key: - """ - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaa - """, - message: - """ - 54686973206973206120746573742075 - 73696e672061206c6172676572207468 - 616e20626c6f636b2d73697a65206b65 - 7920616e642061206c61726765722074 - 68616e20626c6f636b2d73697a652064 - 6174612e20546865206b6579206e6565 - 647320746f2062652068617368656420 - 6265666f7265206265696e6720757365 - 642062792074686520484d414320616c - 676f726974686d2e - """, - hmac256: - """ - 9b09ffa71b942fcb27635fbcd5b0e944 - bfdc63644f0713938a7f51535c3a35e2 - """ - ), - ] - { - if let tests:TestGroup = tests / "hmac-sha-256" / test.name - { - let key:[UInt8] = Base16.decode(test.key.utf8) - let message:[UInt8] = Base16.decode(test.message.utf8) - - let computed:SHA256 = .init(authenticating: message, key: key) - tests.expect(computed ==? test.expected) - } - } - - // https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors - for test:KeyDerivationTest in - [ - .init(name: "single-iteration", - password: "password", salt: "salt", iterations: 1, - derived: - [ - 0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, - 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, - 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, - 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b, - ]), - - .init(name: "multiple-iterations", - password: "password", salt: "salt", iterations: 2, - derived: - [ - 0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3, - 0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0, - 0x2a, 0x30, 0x3f, 0x8e, 0xf3, 0xc2, 0x51, 0xdf, - 0xd6, 0xe2, 0xd8, 0x5a, 0x95, 0x47, 0x4c, 0x43, - ]), - - .init(name: "many-iterations", - password: "password", salt: "salt", iterations: 4096, - derived: - [ - 0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, - 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, - 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, - 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a, - ]), - - // disabled to keep the CI flowing - - // $0.test(case: KeyDerivationTest.init(name: "absurd-iterations", - // password: "password", salt: "salt", iterations: 16777216, - // derived: - // [ - // 0xcf, 0x81, 0xc6, 0x6f, 0xe8, 0xcf, 0xc0, 0x4d, - // 0x1f, 0x31, 0xec, 0xb6, 0x5d, 0xab, 0x40, 0x89, - // 0xf7, 0xf1, 0x79, 0xe8, 0x9b, 0x3b, 0x0b, 0xcb, - // 0x17, 0xad, 0x10, 0xe3, 0xac, 0x6e, 0xba, 0x46, - // ])) - - .init(name: "multiple-blocks", - password: "passwordPASSWORDpassword", - salt: "saltSALTsaltSALTsaltSALTsaltSALTsalt", - iterations: 4096, - derived: - [ - 0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f, - 0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf, - 0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18, - 0x1c, 0x4e, 0x2a, 0x1f, 0xb8, 0xdd, 0x53, 0xe1, - 0xc6, 0x35, 0x51, 0x8c, 0x7d, 0xac, 0x47, 0xe9, - ]), - - .init(name: "null-bytes", - password: "pass\u{0}word", salt: "sa\u{0}lt", - iterations: 4096, - derived: - [ - 0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89, - 0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87, - ]), - ] - { - if let tests:TestGroup = tests / "pbkdf2-hmac-sha-256" / test.name - { - let (quotient, remainder):(Int, Int) = test.derived.count.quotientAndRemainder( - dividingBy: SHA256.count) - let key:[UInt8] = SHA256.pbkdf2(password: test.password.utf8, - salt: test.salt.utf8, - iterations: test.iterations, - blocks: quotient + max(remainder, 1)) - - tests.expect(key.prefix(test.derived.count) ..? test.derived) - } - } - } -} From c16e2d6390ca5c42da83b121c91eaac72864fa5a Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Mon, 18 Nov 2024 22:21:09 +0000 Subject: [PATCH 7/8] update CI script to actually run our new tests --- Scripts/TestAll | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Scripts/TestAll b/Scripts/TestAll index 628be88..4f245b2 100755 --- a/Scripts/TestAll +++ b/Scripts/TestAll @@ -1,7 +1,6 @@ #!/bin/bash set -e + swift --version -swift build -c release -for f in .build/release/*Tests; do - $f -done +swift build -c release --build-tests +swift test -c release --skip-build From 40421db66280d672863c314f25e446ae64cf41c8 Mon Sep 17 00:00:00 2001 From: Dianna Ma Date: Mon, 18 Nov 2024 22:23:27 +0000 Subject: [PATCH 8/8] update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 284404b..7a06f41 100644 --- a/README.md +++ b/README.md @@ -65,3 +65,7 @@ This package vends the following library products: 1. [`SHA2`](https://swiftinit.org/docs/swift-hash/sha2) Implements the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) hashing function. + +1. [`UUID`](https://swiftinit.org/docs/swift-hash/uuid) + + Provides a UUID type.