From f7c53ff32bae638d322251640906cbce0e977fb4 Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Mon, 16 Jan 2023 23:46:59 +0100 Subject: [PATCH 01/19] Allow public init from P384 PublicKey Allows to initialize a public key directly from a P384 Public Key. --- .../Version3/Public/V3PublicAsymmetricPublicKey.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index dd301f7..5f164d5 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -68,7 +68,7 @@ extension Version3.Public { self.key = key } - init (key: P384.Signing.PublicKey) { + public init (key: P384.Signing.PublicKey) { self.key = key } } From 9817eefd5135efafea7a014504df52fd35cec9b9 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 13 Feb 2023 23:37:53 +0000 Subject: [PATCH 02/19] Prefer new dedicated compressed importer if available --- .../Public/V3PublicAsymmetricPublicKey.swift | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index 5f164d5..3808dcd 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -60,10 +60,23 @@ extension Version3.Public { ) } - guard - let key = try? P384.Signing.PublicKey(x963Representation: material) else { - throw Exception.badKey("Public key is invalid") + if #available(macOS 13, iOS 16, watchOS 9, tvOS 16, macCatalyst 16, *) { + guard + let key = try? P384.Signing.PublicKey(compressedRepresentation: material) else { + throw Exception.badKey("Public key is invalid") + } + self.key = key + } else { + // Note that this would actually fail on newer versions, so it seems there was a BC break + // here which means this constructor will no longer accept compressed points like it once + // did. + guard + let key = try? P384.Signing.PublicKey(x963Representation: material) else { + throw Exception.badKey("Public key is invalid") + } + self.key = key } + } self.key = key } From 13b960aa4157a1bfad0d44f463a62e3e7ddcac51 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 13 Feb 2023 23:38:40 +0000 Subject: [PATCH 03/19] Extract compress func into static method --- .../Version3/Public/V3PublicAsymmetricPublicKey.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index 3808dcd..a105509 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -8,7 +8,7 @@ extension Version3.Public { let key: P384.Signing.PublicKey - var compressed: Bytes { + fileprivate static func compress(key: P384.Signing.PublicKey) -> Bytes { let x963Representation = key.x963Representation.bytes guard x963Representation[0] == 04 else { @@ -49,6 +49,10 @@ extension Version3.Public { return [prefix] + xBytes } + var compressed: Bytes { + Self.compress(key: key) + } + public var material: Bytes { compressed } From 15a21ac8a6c680b4dd6a7bcb5d16d4bf658acfe9 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 13 Feb 2023 23:39:03 +0000 Subject: [PATCH 04/19] Expose safe public importer for keys --- .../Public/V3PublicAsymmetricPublicKey.swift | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index a105509..9212e5b 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -82,11 +82,30 @@ extension Version3.Public { } } + init (_ key: P384.Signing.PublicKey) { self.key = key } - public init (key: P384.Signing.PublicKey) { - self.key = key + // Imports a P384.Signing.PublicKey. This method will throw if the public key + // contains an invalid curve point. + public init (key: P384.Signing.PublicKey) throws { + // Rather than explicitly checking the co-ordinates here, the stratergy is + // to export the public key raw and compressed, then use the safe compressed + // constructor. If we detect a change between the imported key and the + // original key then we error. + + // store starting bytes + let givenRawBytes = key.rawRepresentation.bytes + + // compress and parse as compressed + try self.init(material: Self.compress(key: key)) + + let parsedRawBytes = self.key.rawRepresentation.bytes + + // assert that parsed compressed bytes match input bytes + guard Util.equals(givenRawBytes, parsedRawBytes) else { + throw Exception.badKey("Public key is invalid") + } } } } From ac6a8bc793465e8ce04233f532ff0c3bd63f95ec Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 13 Feb 2023 23:45:49 +0000 Subject: [PATCH 05/19] Use non-throwing constructor internally when we know we have a valid public key --- .../Version3/Public/V3PublicAsymmetricSecretKey.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricSecretKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricSecretKey.swift index 22cba2c..3f1883d 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricSecretKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricSecretKey.swift @@ -46,9 +46,7 @@ extension Version3.Public.AsymmetricSecretKey: Paseto.AsymmetricSecretKey { } public var publicKey: Version3.Public.AsymmetricPublicKey { - return Version3.Public.AsymmetricPublicKey ( - key: key.publicKey - ) + return Version3.Public.AsymmetricPublicKey(key.publicKey) } } From 2e79c60751b57b3f951a38d3e88becc2def8efa7 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Mon, 13 Feb 2023 23:54:36 +0000 Subject: [PATCH 06/19] Test invalid point --- Tests/PasetoTests/KeyTest.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Tests/PasetoTests/KeyTest.swift diff --git a/Tests/PasetoTests/KeyTest.swift b/Tests/PasetoTests/KeyTest.swift new file mode 100644 index 0000000..0c3cc6d --- /dev/null +++ b/Tests/PasetoTests/KeyTest.swift @@ -0,0 +1,17 @@ +import XCTest +@testable import Paseto +import CryptoKit +import Sodium + +class KeyTest: XCTestCase { + func testInvalidKeyImport() { + let material = sodium.utils.hex2bin( "04fbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fbfbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fb")! + + // import invalid key + let pubKey = try! P384.Signing.PublicKey(x963Representation: material) + + // should detect invalid key + XCTAssertThrowsError(try Paseto.Version3.AsymmetricPublicKey(key: pubKey)) + } +} + From 5554eb8a4625eb4adbabbc9e0ac3eadc574b2952 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Tue, 14 Feb 2023 00:16:30 +0000 Subject: [PATCH 07/19] Actions versions --- .github/workflows/push.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index d601270..b9c139a 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -5,11 +5,11 @@ jobs: name: Unit Tests strategy: matrix: - os: [macos-latest, macos-11, macos-10.15] + os: [macos-latest, macos-12, macos-11, macos-10.15] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Run Tests run: swift test @@ -17,7 +17,7 @@ jobs: name: Check Test Vectors Up To Date runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Check for updates run: | cd Tests/PasetoTests/TestVectors From f7b5c6b7739be43e9e923566418b03c7bab2bdd3 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Wed, 1 Mar 2023 20:00:39 +0000 Subject: [PATCH 08/19] Tests run on PR --- .github/workflows/push.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index b9c139a..b7b3760 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -1,5 +1,10 @@ name: Swift -on: [push] +on: + push: + branches: + - main + pull_request: + jobs: units: name: Unit Tests From 11f41eaac1e67070dd4f272595a8e83051dda1fd Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Wed, 1 Mar 2023 20:18:52 +0000 Subject: [PATCH 09/19] Don't fail fast, run all OS versions even if one fails --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index b7b3760..a7b4049 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -9,6 +9,7 @@ jobs: units: name: Unit Tests strategy: + fail-fast: false matrix: os: [macos-latest, macos-12, macos-11, macos-10.15] From 7df0c5fcc6732afc0362575529975d00ec24e25a Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 2 Mar 2023 00:23:04 +0000 Subject: [PATCH 10/19] Add manual decompression logic Apple seeimingly broke the x963Representation constructor when releasing the new compressedRepresentaion constructor, the former no longer accepts compressed points which is a BC break. This would be fine, except the new method cannot be used by newer OS versions if we want to support older OS versions at the same time. Either we need to avoid using the x963 constructor, or only support macOS 13 and similar. This is pretty new, and not even GitHub supports it yet for pipelines. It's not ideal, but it seems the only workable alternative is the implement handling of compressed points manually. This is annoted pretty heavily to follow the steps and notation in the spec. --- .../Public/V3PublicAsymmetricPublicKey.swift | 92 +++++++++++++++---- 1 file changed, 76 insertions(+), 16 deletions(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index 9212e5b..7e0d490 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -1,5 +1,26 @@ import Foundation import CryptoKit +import CryptoSwift + +fileprivate let zeroBn = BigUInteger(0) +fileprivate let oneBn = BigUInteger(1) +fileprivate let twoBn = BigUInteger(2) +fileprivate let fourBn = BigUInteger(4) + +// The following consts are extracted from the NIST spec. +// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186-draft.pdf + +// p = 2^384 − 2^128 − 2^96 + 2^32 − 1 +fileprivate let pBn = twoBn.power(384) - twoBn.power(128) - twoBn.power(96) + twoBn.power(32) - 1 + +// a = -3 mod p +// = 3940200619639447921227904010014361380507973927046544666794\ +// 8293404245721771496870329047266088258938001861606973112316 +fileprivate let aBn = BigUInteger("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112316", radix: 10)! + +// b = 2758019355995970587784901184038904809305690585636156852142\ +// 8707301988689241309860865136260764883745107765439761230575 +fileprivate let bBn = BigUInteger("27580193559959705877849011840389048093056905856361568521428707301988689241309860865136260764883745107765439761230575", radix: 10)! @available(macOS 11, iOS 14, watchOS 7, tvOS 14, macCatalyst 14, *) extension Version3.Public { @@ -8,6 +29,60 @@ extension Version3.Public { let key: P384.Signing.PublicKey + fileprivate static func decompressToCoords(compressedKey: Bytes) throws -> (x: Bytes, y: Bytes) { + guard compressedKey.count == 1 + 48 else { + throw Exception.badKey("Bad public key length") + } + + if compressedKey == Bytes(repeating: 0, count: 49) { + return (x: Bytes(repeating: 0, count: 48), y: Bytes(repeating: 0, count: 48)) + } + + let prefix = compressedKey[0] + let x = compressedKey[1...].bytes + + let yTildeP: BigUInteger + switch prefix { + case 02: + yTildeP = zeroBn + case 03: + yTildeP = oneBn + default: + throw Exception.badKey("Bad public key prefix") + } + + let xBn = BigUInteger(Data(x)) + + let alpha = (xBn.power(3) + (aBn * xBn) + bBn) % pBn + + // Take square root mod p + // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm + // For prime p = 3 (mod 4), r = n ^ ((p+1)/n) (mod p) is a root of r^2 = n (mod p) + let beta = alpha.power((pBn + 1)/4, modulus: pBn) + + let yBn: BigUInteger + if beta % 2 == yTildeP { + yBn = beta + } else { + yBn = pBn - beta + } + + let y = Bytes(yBn.serialize()) + + guard y.count <= 48 else { + throw Exception.badKey("Invalid y byte length") + } + + let yPadded = Bytes(repeating: 0, count: 48 - y.count) + y + + return (x, yPadded) + } + + fileprivate static func decompress(compressedKey: Bytes) throws -> P384.Signing.PublicKey { + let (x, y) = try decompressToCoords(compressedKey: compressedKey) + return try P384.Signing.PublicKey(rawRepresentation: x + y) + } + fileprivate static func compress(key: P384.Signing.PublicKey) -> Bytes { let x963Representation = key.x963Representation.bytes @@ -64,22 +139,7 @@ extension Version3.Public { ) } - if #available(macOS 13, iOS 16, watchOS 9, tvOS 16, macCatalyst 16, *) { - guard - let key = try? P384.Signing.PublicKey(compressedRepresentation: material) else { - throw Exception.badKey("Public key is invalid") - } - self.key = key - } else { - // Note that this would actually fail on newer versions, so it seems there was a BC break - // here which means this constructor will no longer accept compressed points like it once - // did. - guard - let key = try? P384.Signing.PublicKey(x963Representation: material) else { - throw Exception.badKey("Public key is invalid") - } - self.key = key - } + self.key = try Self.decompress(compressedKey: material) } init (_ key: P384.Signing.PublicKey) { From 955836efa7c9d2e9b2f61bc28c5a6b3c46fb3e6b Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 2 Mar 2023 00:31:41 +0000 Subject: [PATCH 11/19] Explicit skip on old OS --- Tests/PasetoTests/KeyTest.swift | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Tests/PasetoTests/KeyTest.swift b/Tests/PasetoTests/KeyTest.swift index 0c3cc6d..0114656 100644 --- a/Tests/PasetoTests/KeyTest.swift +++ b/Tests/PasetoTests/KeyTest.swift @@ -5,13 +5,17 @@ import Sodium class KeyTest: XCTestCase { func testInvalidKeyImport() { - let material = sodium.utils.hex2bin( "04fbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fbfbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fb")! - - // import invalid key - let pubKey = try! P384.Signing.PublicKey(x963Representation: material) - - // should detect invalid key - XCTAssertThrowsError(try Paseto.Version3.AsymmetricPublicKey(key: pubKey)) + if #available(macOS 11, iOS 14, watchOS 7, tvOS 14, macCatalyst 14, *) { + let material = sodium.utils.hex2bin( "04fbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fbfbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fb")! + + // import invalid key + let pubKey = try! P384.Signing.PublicKey(x963Representation: material) + + // should detect invalid key + XCTAssertThrowsError(try Paseto.Version3.AsymmetricPublicKey(key: pubKey)) + } else { + _ = XCTSkip("Skipping key test where key is not supported") + } } } From 9f56a4eb97a4b79f014cb918961d0e079b1079d9 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 2 Mar 2023 23:17:35 +0000 Subject: [PATCH 12/19] Add link to spec --- .../Version3/Public/V3PublicAsymmetricPublicKey.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index 7e0d490..cf1ee6e 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -29,6 +29,7 @@ extension Version3.Public { let key: P384.Signing.PublicKey + // https://www.secg.org/sec1-v2.pdf fileprivate static func decompressToCoords(compressedKey: Bytes) throws -> (x: Bytes, y: Bytes) { guard compressedKey.count == 1 + 48 else { throw Exception.badKey("Bad public key length") From b8fdb87654e82969e94a77e55adb62f68f29d5cf Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 2 Mar 2023 23:17:52 +0000 Subject: [PATCH 13/19] Fail if square root does not exist --- .../Version3/Public/V3PublicAsymmetricPublicKey.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index cf1ee6e..770623a 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -58,9 +58,14 @@ extension Version3.Public { // Take square root mod p // https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm - // For prime p = 3 (mod 4), r = n ^ ((p+1)/n) (mod p) is a root of r^2 = n (mod p) + // For prime p = 3 (mod 4), r = n ^ ((p+1)/n) (mod p) is a root of r^2 = n (mod p), if a square root exists let beta = alpha.power((pBn + 1)/4, modulus: pBn) + // check square root exists + guard beta.power(2, modulus: pBn) == alpha else { + throw Exception.badKey("Square root not found") + } + let yBn: BigUInteger if beta % 2 == yTildeP { yBn = beta From f306613e97ae4706bf48a36390c2aae54d6c1afd Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 2 Mar 2023 23:34:13 +0000 Subject: [PATCH 14/19] Better document sections being followed in spec --- .../Public/V3PublicAsymmetricPublicKey.swift | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index 770623a..40d4a40 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -29,19 +29,24 @@ extension Version3.Public { let key: P384.Signing.PublicKey - // https://www.secg.org/sec1-v2.pdf + // https://www.secg.org/sec1-v2.pdf section 2.3.4, supporting only action 2. for compressed points fileprivate static func decompressToCoords(compressedKey: Bytes) throws -> (x: Bytes, y: Bytes) { + // precondition for 2. guard compressedKey.count == 1 + 48 else { throw Exception.badKey("Bad public key length") } - if compressedKey == Bytes(repeating: 0, count: 49) { - return (x: Bytes(repeating: 0, count: 48), y: Bytes(repeating: 0, count: 48)) - } - + // 2.1 let prefix = compressedKey[0] let x = compressedKey[1...].bytes + // 2.2 + let xBn = BigUInteger(Data(x)) + guard xBn >= 0 && xBn <= pBn - 1 else { + throw Exception.badKey("Invalid x supplied") + } + + // 2.3 let yTildeP: BigUInteger switch prefix { case 02: @@ -52,8 +57,7 @@ extension Version3.Public { throw Exception.badKey("Bad public key prefix") } - let xBn = BigUInteger(Data(x)) - + // 2.4, 2.4.1 path let alpha = (xBn.power(3) + (aBn * xBn) + bBn) % pBn // Take square root mod p @@ -75,10 +79,12 @@ extension Version3.Public { let y = Bytes(yBn.serialize()) + // must fit in 48 bytes to be valid guard y.count <= 48 else { throw Exception.badKey("Invalid y byte length") } + // prefix pad y bytes to fill 48 bytes let yPadded = Bytes(repeating: 0, count: 48 - y.count) + y return (x, yPadded) From 11387533d1872cf3f0fbf5d78ed0a281532174b2 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 2 Mar 2023 23:34:32 +0000 Subject: [PATCH 15/19] Fuzzing tests for good keys and possibly junk keys --- Tests/PasetoTests/KeyTest.swift | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Tests/PasetoTests/KeyTest.swift b/Tests/PasetoTests/KeyTest.swift index 0114656..4161f47 100644 --- a/Tests/PasetoTests/KeyTest.swift +++ b/Tests/PasetoTests/KeyTest.swift @@ -17,5 +17,36 @@ class KeyTest: XCTestCase { _ = XCTSkip("Skipping key test where key is not supported") } } + + func testGeneratedKeyImport() { + if #available(macOS 13, *) { + for _ in 1...100 { + let privKey = P384.Signing.PrivateKey(compactRepresentable: false) + + let pubKey = privKey.publicKey + + let pasetoPubKey = Paseto.Version3.AsymmetricPublicKey(bytes: pubKey.compressedRepresentation)! + + XCTAssertEqual(pubKey.rawRepresentation.bytes, pasetoPubKey.key.rawRepresentation.bytes) + } + } else { + _ = XCTSkip("Skipping key test where key is not supported") + } + } + + func testRandomKeyImport() { + if #available(macOS 13, *) { + for _ in 1...100 { + let bytes = [Util.random(length: 1)[0] % 2 == 0 ? 02 : 03] + Util.random(length: 48) + + let pasetoPubKey = Paseto.Version3.AsymmetricPublicKey(bytes: bytes) + let pubKey = try? P384.Signing.PublicKey(compressedRepresentation: bytes) + + XCTAssertEqual(pubKey?.rawRepresentation.bytes, pasetoPubKey?.key.rawRepresentation.bytes) + } + } else { + _ = XCTSkip("Skipping key test where key is not supported") + } + } } From 3f87846f47700f3eea74806364446bc5ab2280ed Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Thu, 2 Mar 2023 23:54:27 +0000 Subject: [PATCH 16/19] Use available annotation instead of embedded if --- Tests/PasetoTests/KeyTest.swift | 45 +++++++++++++-------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/Tests/PasetoTests/KeyTest.swift b/Tests/PasetoTests/KeyTest.swift index 4161f47..f00628d 100644 --- a/Tests/PasetoTests/KeyTest.swift +++ b/Tests/PasetoTests/KeyTest.swift @@ -4,48 +4,39 @@ import CryptoKit import Sodium class KeyTest: XCTestCase { + @available(macOS 11, iOS 14, watchOS 7, tvOS 14, macCatalyst 14, *) func testInvalidKeyImport() { - if #available(macOS 11, iOS 14, watchOS 7, tvOS 14, macCatalyst 14, *) { - let material = sodium.utils.hex2bin( "04fbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fbfbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fb")! + let material = sodium.utils.hex2bin( "04fbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fbfbcb7c69ee1c60579be7a334134878d9c5c5bf35d552dab63c0140397ed14cef637d7720925c44699ea30e72874c72fb")! - // import invalid key - let pubKey = try! P384.Signing.PublicKey(x963Representation: material) + // import invalid key + let pubKey = try! P384.Signing.PublicKey(x963Representation: material) - // should detect invalid key - XCTAssertThrowsError(try Paseto.Version3.AsymmetricPublicKey(key: pubKey)) - } else { - _ = XCTSkip("Skipping key test where key is not supported") - } + // should detect invalid key + XCTAssertThrowsError(try Paseto.Version3.AsymmetricPublicKey(key: pubKey)) } + @available(macOS 13, *) func testGeneratedKeyImport() { - if #available(macOS 13, *) { - for _ in 1...100 { - let privKey = P384.Signing.PrivateKey(compactRepresentable: false) + for _ in 1...100 { + let privKey = P384.Signing.PrivateKey(compactRepresentable: false) - let pubKey = privKey.publicKey + let pubKey = privKey.publicKey - let pasetoPubKey = Paseto.Version3.AsymmetricPublicKey(bytes: pubKey.compressedRepresentation)! + let pasetoPubKey = Paseto.Version3.AsymmetricPublicKey(bytes: pubKey.compressedRepresentation)! - XCTAssertEqual(pubKey.rawRepresentation.bytes, pasetoPubKey.key.rawRepresentation.bytes) - } - } else { - _ = XCTSkip("Skipping key test where key is not supported") + XCTAssertEqual(pubKey.rawRepresentation.bytes, pasetoPubKey.key.rawRepresentation.bytes) } } + @available(macOS 13, *) func testRandomKeyImport() { - if #available(macOS 13, *) { - for _ in 1...100 { - let bytes = [Util.random(length: 1)[0] % 2 == 0 ? 02 : 03] + Util.random(length: 48) + for _ in 1...100 { + let bytes = [Util.random(length: 1)[0] % 2 == 0 ? 02 : 03] + Util.random(length: 48) - let pasetoPubKey = Paseto.Version3.AsymmetricPublicKey(bytes: bytes) - let pubKey = try? P384.Signing.PublicKey(compressedRepresentation: bytes) + let pasetoPubKey = Paseto.Version3.AsymmetricPublicKey(bytes: bytes) + let pubKey = try? P384.Signing.PublicKey(compressedRepresentation: bytes) - XCTAssertEqual(pubKey?.rawRepresentation.bytes, pasetoPubKey?.key.rawRepresentation.bytes) - } - } else { - _ = XCTSkip("Skipping key test where key is not supported") + XCTAssertEqual(pubKey?.rawRepresentation.bytes, pasetoPubKey?.key.rawRepresentation.bytes) } } } From 4cd61c7eb1b2be91d6de63400e413f8c7041ea3d Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Fri, 3 Mar 2023 18:25:42 +0000 Subject: [PATCH 17/19] Gate on most recent compiler version This isn't a great practice and can't be used in the main package code, is fine for the sake of making the tests compile --- Tests/PasetoTests/KeyTest.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Tests/PasetoTests/KeyTest.swift b/Tests/PasetoTests/KeyTest.swift index f00628d..1e54332 100644 --- a/Tests/PasetoTests/KeyTest.swift +++ b/Tests/PasetoTests/KeyTest.swift @@ -15,6 +15,7 @@ class KeyTest: XCTestCase { XCTAssertThrowsError(try Paseto.Version3.AsymmetricPublicKey(key: pubKey)) } +#if compiler(>=5.7) @available(macOS 13, *) func testGeneratedKeyImport() { for _ in 1...100 { @@ -39,5 +40,6 @@ class KeyTest: XCTestCase { XCTAssertEqual(pubKey?.rawRepresentation.bytes, pasetoPubKey?.key.rawRepresentation.bytes) } } +#endif } From 905fcbec62a8dff8afea5d7f5f38f0a1cfbdcc65 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Fri, 3 Mar 2023 22:02:44 +0000 Subject: [PATCH 18/19] Explicit end range --- .../Version3/Public/V3PublicAsymmetricPublicKey.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index 40d4a40..ce77c48 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -38,7 +38,7 @@ extension Version3.Public { // 2.1 let prefix = compressedKey[0] - let x = compressedKey[1...].bytes + let x = compressedKey[1..<49].bytes // 2.2 let xBn = BigUInteger(Data(x)) From fa0c69cd40b4864f816947bac71eda380d21cbf5 Mon Sep 17 00:00:00 2001 From: Aidan Woods Date: Fri, 3 Mar 2023 22:12:00 +0000 Subject: [PATCH 19/19] Complete spec annotations --- .../Version3/Public/V3PublicAsymmetricPublicKey.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift index ce77c48..e1b8cb1 100644 --- a/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift +++ b/Sources/Paseto/Implementations/Version3/Public/V3PublicAsymmetricPublicKey.swift @@ -87,6 +87,7 @@ extension Version3.Public { // prefix pad y bytes to fill 48 bytes let yPadded = Bytes(repeating: 0, count: 48 - y.count) + y + // 2.5 return (x, yPadded) }