diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9d8b8d7a..0bad457e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,7 @@ jobs: uses: vapor/ci/.github/workflows/run-unit-tests.yml@main with: with_api_check: ${{ github.event_name == 'pull_request' }} + warnings_as_errors: true secrets: inherit ios-tests: diff --git a/Sources/JWTKit/Claims/LocaleClaim.swift b/Sources/JWTKit/Claims/LocaleClaim.swift index ee30aae2..e358afd1 100644 --- a/Sources/JWTKit/Claims/LocaleClaim.swift +++ b/Sources/JWTKit/Claims/LocaleClaim.swift @@ -1,4 +1,8 @@ -import struct Foundation.Locale +#if compiler(<6.0) && !canImport(Darwin) + @preconcurrency import Foundation +#else + import Foundation +#endif public struct LocaleClaim: JWTClaim, Equatable, ExpressibleByStringLiteral { /// See ``JWTClaim``. diff --git a/Sources/JWTKit/Utilities/CustomizedJSONCoders.swift b/Sources/JWTKit/Utilities/CustomizedJSONCoders.swift index 6ed3e70c..1855fb53 100644 --- a/Sources/JWTKit/Utilities/CustomizedJSONCoders.swift +++ b/Sources/JWTKit/Utilities/CustomizedJSONCoders.swift @@ -8,12 +8,16 @@ public protocol JWTJSONEncoder: Sendable { func encode(_ value: T) throws -> Data } -extension JSONDecoder: JWTJSONDecoder {} - -extension JSONEncoder: JWTJSONEncoder {} - -public extension JSONDecoder.DateDecodingStrategy { - static var integerSecondsSince1970: Self { +#if compiler(<6.0) && !canImport(Darwin) + extension JSONDecoder: JWTJSONDecoder, @unchecked Sendable {} + extension JSONEncoder: JWTJSONEncoder, @unchecked Sendable {} +#else + extension JSONDecoder: JWTJSONDecoder {} + extension JSONEncoder: JWTJSONEncoder {} +#endif + +extension JSONDecoder.DateDecodingStrategy { + public static var integerSecondsSince1970: Self { .custom { decoder in let container = try decoder.singleValueContainer() return try Date(timeIntervalSince1970: Double(container.decode(Int.self))) @@ -21,8 +25,8 @@ public extension JSONDecoder.DateDecodingStrategy { } } -public extension JSONEncoder.DateEncodingStrategy { - static var integerSecondsSince1970: Self { +extension JSONEncoder.DateEncodingStrategy { + public static var integerSecondsSince1970: Self { .custom { date, encoder in var container = encoder.singleValueContainer() try container.encode(Int(date.timeIntervalSince1970.rounded(.towardZero))) @@ -30,16 +34,16 @@ public extension JSONEncoder.DateEncodingStrategy { } } -public extension JWTJSONEncoder where Self == JSONEncoder { - static var defaultForJWT: any JWTJSONEncoder { +extension JWTJSONEncoder where Self == JSONEncoder { + public static var defaultForJWT: any JWTJSONEncoder { let encoder = JSONEncoder() encoder.dateEncodingStrategy = .secondsSince1970 return encoder } } -public extension JWTJSONDecoder where Self == JSONDecoder { - static var defaultForJWT: any JWTJSONDecoder { +extension JWTJSONDecoder where Self == JSONDecoder { + public static var defaultForJWT: any JWTJSONDecoder { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .secondsSince1970 return decoder diff --git a/Tests/JWTKitTests/ClaimTests.swift b/Tests/JWTKitTests/ClaimTests.swift index c34937b9..6ec7d32b 100644 --- a/Tests/JWTKitTests/ClaimTests.swift +++ b/Tests/JWTKitTests/ClaimTests.swift @@ -1,7 +1,7 @@ import JWTKit import XCTest -final class ClaimTests: XCTestCase { +final class ClaimTests: XCTestCase, @unchecked Sendable { func testBoolClaim() throws { let str = #"{"trueStr":"true","trueBool":true,"falseStr":"false","falseBool":false}"# var data = Data(str.utf8) @@ -23,13 +23,15 @@ final class ClaimTests: XCTestCase { let brazillianPortugese = try LocalePayload.from(ptBR) let nadizaDialectSlovenia = try LocalePayload.from(#"{"locale":"sl-nedis"}"#) let germanSwissPost1996 = try LocalePayload.from(#"{"locale":"de-CH-1996"}"#) - let chineseTraditionalTwoPrivate = try LocalePayload.from(#"{"locale":"zh-Hant-CN-x-private1-private2"}"#) + let chineseTraditionalTwoPrivate = try LocalePayload.from( + #"{"locale":"zh-Hant-CN-x-private1-private2"}"#) XCTAssertEqual(plainEnglish.locale.value.identifier, "en") XCTAssertEqual(brazillianPortugese.locale.value.identifier, "pt-BR") XCTAssertEqual(nadizaDialectSlovenia.locale.value.identifier, "sl-nedis") XCTAssertEqual(germanSwissPost1996.locale.value.identifier, "de-CH-1996") - XCTAssertEqual(chineseTraditionalTwoPrivate.locale.value.identifier, "zh-Hant-CN-x-private1-private2") + XCTAssertEqual( + chineseTraditionalTwoPrivate.locale.value.identifier, "zh-Hant-CN-x-private1-private2") let encoded = try JSONEncoder().encode(brazillianPortugese) let string = String(bytes: encoded, encoding: .utf8)! @@ -44,7 +46,9 @@ final class ClaimTests: XCTestCase { XCTAssertEqual(decoded.audience.value, [id.uuidString]) XCTAssertNoThrow(try decoded.audience.verifyIntendedAudience(includes: id.uuidString)) - XCTAssertThrowsError(try decoded.audience.verifyIntendedAudience(includes: UUID().uuidString)) { + XCTAssertThrowsError( + try decoded.audience.verifyIntendedAudience(includes: UUID().uuidString) + ) { guard let jwtError = try? XCTUnwrap($0 as? JWTError) else { return } XCTAssertEqual(jwtError.errorType, .claimVerificationFailure) XCTAssert(jwtError.failedClaim is AudienceClaim) @@ -53,7 +57,8 @@ final class ClaimTests: XCTestCase { } func testMultipleAudienceClaim() throws { - let id1 = UUID(), id2 = UUID() + let id1 = UUID() + let id2 = UUID() let str = "{\"audience\":[\"\(id1.uuidString)\", \"\(id2.uuidString)\"]}" let data = Data(str.utf8) let decoded = try! JSONDecoder().decode(AudiencePayload.self, from: data) @@ -61,18 +66,22 @@ final class ClaimTests: XCTestCase { XCTAssertEqual(decoded.audience.value, [id1.uuidString, id2.uuidString]) XCTAssertNoThrow(try decoded.audience.verifyIntendedAudience(includes: id1.uuidString)) XCTAssertNoThrow(try decoded.audience.verifyIntendedAudience(includes: id2.uuidString)) - XCTAssertThrowsError(try decoded.audience.verifyIntendedAudience(includes: UUID().uuidString)) { + XCTAssertThrowsError( + try decoded.audience.verifyIntendedAudience(includes: UUID().uuidString) + ) { guard let jwtError = try? XCTUnwrap($0 as? JWTError) else { return } XCTAssertEqual(jwtError.errorType, .claimVerificationFailure) XCTAssert(jwtError.failedClaim is AudienceClaim) - XCTAssertEqual((jwtError.failedClaim as? AudienceClaim)?.value, [id1.uuidString, id2.uuidString]) + XCTAssertEqual( + (jwtError.failedClaim as? AudienceClaim)?.value, [id1.uuidString, id2.uuidString]) } } func testExpirationEncoding() async throws { let exp = ExpirationClaim(value: Date(timeIntervalSince1970: 2_000_000_000)) let parser = DefaultJWTParser() - let keyCollection = await JWTKeyCollection().add(hmac: .init(from: "secret".bytes), digestAlgorithm: .sha256, parser: parser) + let keyCollection = await JWTKeyCollection().add( + hmac: .init(from: "secret".bytes), digestAlgorithm: .sha256, parser: parser) let jwt = try await keyCollection.sign(ExpirationPayload(exp: exp)) let parsed = try parser.parse(jwt.bytes, as: ExpirationPayload.self) let header = parsed.header diff --git a/Tests/JWTKitTests/ECDSATests.swift b/Tests/JWTKitTests/ECDSATests.swift index 03e471b8..a0710820 100644 --- a/Tests/JWTKitTests/ECDSATests.swift +++ b/Tests/JWTKitTests/ECDSATests.swift @@ -3,7 +3,7 @@ import Crypto import JWTKit import XCTest -final class ECDSATests: XCTestCase { +final class ECDSATests: XCTestCase, @unchecked Sendable { func testECDSADocs() async throws { XCTAssertNoThrow(try ES256PublicKey(pem: ecdsaPublicKey)) } @@ -55,7 +55,8 @@ final class ECDSATests: XCTestCase { let key = ES256PrivateKey() await keyCollection.add(ecdsa: key) let token = try await keyCollection.sign(payload) - try await XCTAssertEqualAsync(await keyCollection.verify(token, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(token, as: TestPayload.self), payload) } func testECDSAPublicPrivate() async throws { @@ -69,7 +70,7 @@ final class ECDSATests: XCTestCase { admin: false, exp: .init(value: .init(timeIntervalSince1970: 2_000_000_000)) ) - for _ in 0 ..< 1000 { + for _ in 0..<1000 { let token = try await keys.sign(payload, kid: "private") // test private signer decoding try await XCTAssertEqualAsync(await keys.verify(token, as: TestPayload.self), payload) @@ -98,18 +99,18 @@ final class ECDSATests: XCTestCase { // verify using jwks without alg let jwksString = """ - { - "keys": [ - { - "kty": "EC", - "use": "sig", - "kid": "vapor", - "x": "\(x)", - "y": "\(y)" - } - ] - } - """ + { + "keys": [ + { + "kty": "EC", + "use": "sig", + "kid": "vapor", + "x": "\(x)", + "y": "\(y)" + } + ] + } + """ try await keys.add(jwksJSON: jwksString) let foo = try await keys.verify(jwt, as: Foo.self) @@ -137,18 +138,18 @@ final class ECDSATests: XCTestCase { // verify using jwks without alg let jwksString = """ - { - "keys": [ - { - "kty": "EC", - "use": "sig", - "kid": "vapor", - "x": "\(x)", - "y": "\(y)" - } - ] - } - """ + { + "keys": [ + { + "kty": "EC", + "use": "sig", + "kid": "vapor", + "x": "\(x)", + "y": "\(y)" + } + ] + } + """ try await keys.add(jwksJSON: jwksString) let foo = try await keys.verify(jwt, as: Foo.self) @@ -176,18 +177,18 @@ final class ECDSATests: XCTestCase { // verify using jwks without alg let jwksString = """ - { - "keys": [ - { - "kty": "EC", - "use": "sig", - "kid": "vapor", - "x": "\(x)", - "y": "\(y)" - } - ] - } - """ + { + "keys": [ + { + "kty": "EC", + "use": "sig", + "kid": "vapor", + "x": "\(x)", + "y": "\(y)" + } + ] + } + """ try await keys.add(jwksJSON: jwksString) let foo = try await keys.verify(jwt, as: Foo.self) @@ -244,7 +245,8 @@ final class ECDSATests: XCTestCase { let params = ec.parameters! try await keys.add(ecdsa: ES256PublicKey(parameters: params), kid: "params") - try await XCTAssertTrueAsync(try await keys.getKey(for: "params").verify(signature, signs: message)) + try await XCTAssertTrueAsync( + try await keys.getKey(for: "params").verify(signature, signs: message)) XCTAssertEqual(ec.curve, .p256) } @@ -258,7 +260,8 @@ final class ECDSATests: XCTestCase { let params = ec.parameters! try await keys.add(ecdsa: ES384PublicKey(parameters: params), kid: "params") - try await XCTAssertTrueAsync(try await keys.getKey(for: "params").verify(signature, signs: message)) + try await XCTAssertTrueAsync( + try await keys.getKey(for: "params").verify(signature, signs: message)) XCTAssertEqual(ec.curve, .p384) } @@ -272,22 +275,23 @@ final class ECDSATests: XCTestCase { let params = ec.parameters! try await keys.add(ecdsa: ES512PublicKey(parameters: params), kid: "params") - try await XCTAssertTrueAsync(try await keys.getKey(for: "params").verify(signature, signs: message)) + try await XCTAssertTrueAsync( + try await keys.getKey(for: "params").verify(signature, signs: message)) XCTAssertEqual(ec.curve, .p521) } } let ecdsaPrivateKey = """ ------BEGIN PRIVATE KEY----- -MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg2sD+kukkA8GZUpmm -jRa4fJ9Xa/JnIG4Hpi7tNO66+OGgCgYIKoZIzj0DAQehRANCAATZp0yt0btpR9kf -ntp4oUUzTV0+eTELXxJxFvhnqmgwGAm1iVW132XLrdRG/ntlbQ1yzUuJkHtYBNve -y+77Vzsd ------END PRIVATE KEY----- -""" + -----BEGIN PRIVATE KEY----- + MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg2sD+kukkA8GZUpmm + jRa4fJ9Xa/JnIG4Hpi7tNO66+OGgCgYIKoZIzj0DAQehRANCAATZp0yt0btpR9kf + ntp4oUUzTV0+eTELXxJxFvhnqmgwGAm1iVW132XLrdRG/ntlbQ1yzUuJkHtYBNve + y+77Vzsd + -----END PRIVATE KEY----- + """ let ecdsaPublicKey = """ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2adMrdG7aUfZH57aeKFFM01dPnkx -C18ScRb4Z6poMBgJtYlVtd9ly63URv57ZW0Ncs1LiZB7WATb3svu+1c7HQ== ------END PUBLIC KEY----- -""" + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2adMrdG7aUfZH57aeKFFM01dPnkx + C18ScRb4Z6poMBgJtYlVtd9ly63URv57ZW0Ncs1LiZB7WATb3svu+1c7HQ== + -----END PUBLIC KEY----- + """ diff --git a/Tests/JWTKitTests/EdDSATests.swift b/Tests/JWTKitTests/EdDSATests.swift index 83c040bb..b54d2b3a 100644 --- a/Tests/JWTKitTests/EdDSATests.swift +++ b/Tests/JWTKitTests/EdDSATests.swift @@ -1,7 +1,7 @@ import JWTKit import XCTest -final class EdDSATests: XCTestCase { +final class EdDSATests: XCTestCase, @unchecked Sendable { func testEdDSAGenerate() async throws { let payload = TestPayload( sub: "vapor", @@ -14,7 +14,8 @@ final class EdDSATests: XCTestCase { .add(eddsa: EdDSA.PrivateKey(curve: .ed25519)) let token = try await keyCollection.sign(payload) - try await XCTAssertEqualAsync(await keyCollection.verify(token, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(token, as: TestPayload.self), payload) } func testEdDSAPublicPrivate() async throws { @@ -28,7 +29,7 @@ final class EdDSATests: XCTestCase { admin: false, exp: .init(value: .init(timeIntervalSince1970: 2_000_000_000)) ) - for _ in 0 ..< 1000 { + for _ in 0..<1000 { let token = try await keys.sign(payload, kid: "private") // test public signer decoding try await XCTAssertEqualAsync(await keys.verify(token, as: TestPayload.self), payload) @@ -53,19 +54,19 @@ final class EdDSATests: XCTestCase { // verify using jwks let jwksString = """ - { - "keys": [ - { - "kty": "OKP", - "crv": "Ed25519", - "use": "sig", - "kid": "vapor", - "x": "\(x)", - "d": "\(d)" - } - ] - } - """ + { + "keys": [ + { + "kty": "OKP", + "crv": "Ed25519", + "use": "sig", + "kid": "vapor", + "x": "\(x)", + "d": "\(d)" + } + ] + } + """ try await keyCollection.add(jwksJSON: jwksString) let foo = try await keyCollection.verify(jwt, as: Foo.self) @@ -90,19 +91,19 @@ final class EdDSATests: XCTestCase { // verify using jwks without alg let jwksString = """ - { - "keys": [ - { - "kty": "OKP", - "crv": "Ed25519", - "use": "sig", - "kid": "vapor", - "x": "\(x)", - "d": "\(d)" - } - ] - } - """ + { + "keys": [ + { + "kty": "OKP", + "crv": "Ed25519", + "use": "sig", + "kid": "vapor", + "x": "\(x)", + "d": "\(d)" + } + ] + } + """ try await keyCollection.add(jwksJSON: jwksString) let foo = try await keyCollection.verify(jwt, as: Foo.self) @@ -127,19 +128,19 @@ final class EdDSATests: XCTestCase { // verify using jwks without alg let jwksString = """ - { - "keys": [ - { - "kty": "OKP", - "crv": "Ed25519", - "use": "sig", - "kid": "vapor", - "x": "\(x)", - "d": "\(d)" - } - ] - } - """ + { + "keys": [ + { + "kty": "OKP", + "crv": "Ed25519", + "use": "sig", + "kid": "vapor", + "x": "\(x)", + "d": "\(d)" + } + ] + } + """ try await keyCollection.add(jwksJSON: jwksString) let foo = try await keyCollection.verify(jwt, as: Foo.self) diff --git a/Tests/JWTKitTests/JWTKitTests.swift b/Tests/JWTKitTests/JWTKitTests.swift index 72971c87..9a1d642e 100644 --- a/Tests/JWTKitTests/JWTKitTests.swift +++ b/Tests/JWTKitTests/JWTKitTests.swift @@ -2,7 +2,7 @@ import JWTKit import X509 import XCTest -class JWTKitTests: XCTestCase { +final class JWTKitTests: XCTestCase, @unchecked Sendable { func testGettingStarted() async throws { // JWT payload structure. struct TestPayload: JWTPayload, Equatable { @@ -39,7 +39,8 @@ class JWTKitTests: XCTestCase { .add(hmac: "secret", digestAlgorithm: .sha256) do { - let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2YXBvciIsImV4cCI6NjQwOTIyMTEyMDAsImFkbWluIjp0cnVlfQ.lS5lpwfRNSZDvpGQk6x5JI1g40gkYCOWqbc3J_ghowo" + let jwt = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2YXBvciIsImV4cCI6NjQwOTIyMTEyMDAsImFkbWluIjp0cnVlfQ.lS5lpwfRNSZDvpGQk6x5JI1g40gkYCOWqbc3J_ghowo" let payload = try await keyCollection.verify(jwt, as: TestPayload.self) XCTAssertEqual(payload.admin, true) } @@ -55,7 +56,8 @@ class JWTKitTests: XCTestCase { } func testParse() async throws { - let data = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OTk5fQ.Ks7KcdjrlUTYaSNeAO5SzBla_sFCHkUh4vvJYn6q29U" + let data = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OTk5fQ.Ks7KcdjrlUTYaSNeAO5SzBla_sFCHkUh4vvJYn6q29U" let test = try await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) .verify(data, as: TestPayload.self) @@ -66,7 +68,8 @@ class JWTKitTests: XCTestCase { } func testExpired() async throws { - let data = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MX0.-x_DAYIg4R4R9oZssqgWyJP_oWO1ESj8DgKrGCk7i5o" + let data = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MX0.-x_DAYIg4R4R9oZssqgWyJP_oWO1ESj8DgKrGCk7i5o" do { _ = try await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) @@ -74,12 +77,14 @@ class JWTKitTests: XCTestCase { } catch let error as JWTError { XCTAssertEqual(error.errorType, .claimVerificationFailure) XCTAssert(error.failedClaim is ExpirationClaim) - XCTAssertEqual((error.failedClaim as? ExpirationClaim)?.value, Date(timeIntervalSince1970: 1)) + XCTAssertEqual( + (error.failedClaim as? ExpirationClaim)?.value, Date(timeIntervalSince1970: 1)) } } func testExpirationDecoding() async throws { - let data = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIwMDAwMDAwMDB9.JgCO_GqUQnbS0z2hCxJLE9Tpt5SMoZObHBxzGBWuTYQ" + let data = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIwMDAwMDAwMDB9.JgCO_GqUQnbS0z2hCxJLE9Tpt5SMoZObHBxzGBWuTYQ" let test = try await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) .verify(data, as: ExpirationPayload.self) @@ -87,8 +92,10 @@ class JWTKitTests: XCTestCase { } func testSigners() async throws { - let data = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OTk5OTl9.Gf7leJ8i30LmMI7GBTpWDMXV60y1wkTOCOBudP9v9ms" - let keyCollection = await JWTKeyCollection().add(hmac: "bar", digestAlgorithm: .sha256, kid: "foo") + let data = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6OTk5OTk5OTk5OTk5OTl9.Gf7leJ8i30LmMI7GBTpWDMXV60y1wkTOCOBudP9v9ms" + let keyCollection = await JWTKeyCollection().add( + hmac: "bar", digestAlgorithm: .sha256, kid: "foo") let payload = try await keyCollection.verify(data, as: TestPayload.self) XCTAssertEqual(payload.name, "John Doe") } @@ -105,21 +112,25 @@ class JWTKitTests: XCTestCase { let keyCollection = await JWTKeyCollection().addUnsecuredNone() let token = try await keyCollection.sign(payload) - try await XCTAssertEqualAsync(await keyCollection.verify(token.bytes, as: TestPayload.self), payload) - try await XCTAssertEqualAsync(await keyCollection.verify(data.bytes, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(token.bytes, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(data.bytes, as: TestPayload.self), payload) XCTAssertTrue(token.hasSuffix(".")) } func testJWTioExample() async throws { - let token = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HWP_3cYHBw7AhHale5wky6-sVA" - let corruptedToken = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HwP_3cYHBw7AhHale5wky6-sVA" + let token = + "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HWP_3cYHBw7AhHale5wky6-sVA" + let corruptedToken = + "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HwP_3cYHBw7AhHale5wky6-sVA" let publicKey = """ - -----BEGIN PUBLIC KEY----- - MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9 - q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg== - -----END PUBLIC KEY----- - """ + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9 + q9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg== + -----END PUBLIC KEY----- + """ // { // "sub": "1234567890", @@ -160,27 +171,27 @@ class JWTKitTests: XCTestCase { func testJWKSigner() async throws { let privateKey = """ - { - "kty": "RSA", - "d": "\(rsaPrivateExponent)", - "e": "AQAB", - "use": "sig", - "kid": "1234", - "alg": "RS256", - "n": "\(rsaModulus)" - } - """ + { + "kty": "RSA", + "d": "\(rsaPrivateExponent)", + "e": "AQAB", + "use": "sig", + "kid": "1234", + "alg": "RS256", + "n": "\(rsaModulus)" + } + """ let publicKey = """ - { - "kty": "RSA", - "e": "AQAB", - "use": "sig", - "kid": "1234", - "alg": "RS256", - "n": "\(rsaModulus)" - } - """ + { + "kty": "RSA", + "e": "AQAB", + "use": "sig", + "kid": "1234", + "alg": "RS256", + "n": "\(rsaModulus)" + } + """ let keyCollection = try await JWTKeyCollection() .add(jwk: .init(json: publicKey)) @@ -195,25 +206,28 @@ class JWTKitTests: XCTestCase { let data = try await keyCollection.sign(payload, kid: "1234") // test private signer decoding - try await XCTAssertEqualAsync(await keyCollection.verify(data, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(data, as: TestPayload.self), payload) // test public signer decoding - try await XCTAssertEqualAsync(await keyCollection.verify(data, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(data, as: TestPayload.self), payload) } func testJWKS() async throws { let json = """ - { - "keys": [ - {"kty": "RSA", "alg": "RS256", "kid": "a", "n": "\(rsaModulus)", "e": "AQAB"}, - {"kty": "RSA", "alg": "RS512", "kid": "b", "n": "\(rsaModulus)", "e": "AQAB"}, - ] - } - """ + { + "keys": [ + {"kty": "RSA", "alg": "RS256", "kid": "a", "n": "\(rsaModulus)", "e": "AQAB"}, + {"kty": "RSA", "alg": "RS512", "kid": "b", "n": "\(rsaModulus)", "e": "AQAB"}, + ] + } + """ let keyCollection = try await JWTKeyCollection().add(jwksJSON: json) await XCTAssertNoThrowAsync(try await keyCollection.getKey()) - let a: JWTAlgorithm, b: JWTAlgorithm + let a: JWTAlgorithm + let b: JWTAlgorithm do { a = try await keyCollection.getKey(for: "a") } catch { @@ -260,9 +274,11 @@ class JWTKitTests: XCTestCase { func testAlgorithmInJWTHeaderOnly() async throws { // rsa key - let modulus = "mSfWGBcXRBPgnwnL_ymDCkBaL6vcMcLpBEomzf-wZPajcQFiq4n4MHScyo85Te6GU-YuErVvHKK0D72JhMNWAQXbiF5Hh7swSYX9QsycWwHBgOBNfp51Fm_HTU7ikDBEdSonrmSep8wNqi_PX2_jVBsoxYNeiCQyDLFLHOAAcbIE4Y6lpJy76GpdHJscMO2RsUznjv5VPOQVa_BlQRIIZ0YoSsq9EEZna9O370wZy8jnOthQIXoegQ7sItS1JMKk4X5DdoRenIfbfWLy88XxKOPlIHA5ekT8TyzeI2Uqkg3YMETTDPrSROVO1Qdl2W1uMdfIZ94DgKpZN2VW-w0fLw" + let modulus = + "mSfWGBcXRBPgnwnL_ymDCkBaL6vcMcLpBEomzf-wZPajcQFiq4n4MHScyo85Te6GU-YuErVvHKK0D72JhMNWAQXbiF5Hh7swSYX9QsycWwHBgOBNfp51Fm_HTU7ikDBEdSonrmSep8wNqi_PX2_jVBsoxYNeiCQyDLFLHOAAcbIE4Y6lpJy76GpdHJscMO2RsUznjv5VPOQVa_BlQRIIZ0YoSsq9EEZna9O370wZy8jnOthQIXoegQ7sItS1JMKk4X5DdoRenIfbfWLy88XxKOPlIHA5ekT8TyzeI2Uqkg3YMETTDPrSROVO1Qdl2W1uMdfIZ94DgKpZN2VW-w0fLw" let exponent = "AQAB" - let privateExponent = "awDmF9aqLqokmXjiydda8mKboArWwP2Ih7K3Ad3Og_u9nUp2gZrXiCMxGGSQiN5Jg3yiW_ffNYaHfyfRWKyQ_g31n4UfPLmPtw6iL3V9GChV5ZDRE9HpxE88U8r1h__xFFrrdnBeWKW8NldI70jg7vY6uiRae4uuXCfSbs4iAUxmRVKWCnV7JE6sObQKUV_EJkBcyND5Y97xsmWD0nPmXCnloQ84gF-eTErJoZBvQhJ4BhmBeUlREHmDKssaxVOCK4l335DKHD1vbuPk9e49M71BK7r2y4Atqk3TEetnwzMs3u-L9RqHaGIBw5u324uGweY7QeD7HFdAUtpjOq_MQQ" + let privateExponent = + "awDmF9aqLqokmXjiydda8mKboArWwP2Ih7K3Ad3Og_u9nUp2gZrXiCMxGGSQiN5Jg3yiW_ffNYaHfyfRWKyQ_g31n4UfPLmPtw6iL3V9GChV5ZDRE9HpxE88U8r1h__xFFrrdnBeWKW8NldI70jg7vY6uiRae4uuXCfSbs4iAUxmRVKWCnV7JE6sObQKUV_EJkBcyND5Y97xsmWD0nPmXCnloQ84gF-eTErJoZBvQhJ4BhmBeUlREHmDKssaxVOCK4l335DKHD1vbuPk9e49M71BK7r2y4Atqk3TEetnwzMs3u-L9RqHaGIBw5u324uGweY7QeD7HFdAUtpjOq_MQQ" // sign jwt let keyCollection = try await JWTKeyCollection().add( @@ -282,18 +298,18 @@ class JWTKitTests: XCTestCase { // verify using jwks without alg let jwksString = """ - { - "keys": [ - { - "kty": "RSA", - "use": "sig", - "kid": "vapor", - "n": "\(modulus)", - "e": "\(exponent)" - } - ] - } - """ + { + "keys": [ + { + "kty": "RSA", + "use": "sig", + "kid": "vapor", + "n": "\(modulus)", + "e": "\(exponent)" + } + ] + } + """ try await keyCollection.add(jwksJSON: jwksString) let foo = try await keyCollection.verify(jwt, as: Foo.self) @@ -306,14 +322,18 @@ class JWTKitTests: XCTestCase { func testFirebaseJWTAndCertificate() async throws { let payload = try await JWTKeyCollection() - .add(rsa: Insecure.RSA.PublicKey(certificatePEM: firebaseCert), digestAlgorithm: .sha256) + .add( + rsa: Insecure.RSA.PublicKey(certificatePEM: firebaseCert), digestAlgorithm: .sha256 + ) .verify(firebaseJWT, as: FirebasePayload.self) XCTAssertEqual(payload.userID, "y8wiKThXGKM88xxrQWDZzKnBuqv2") } func testCustomJSONCoders() async throws { - let encoder = JSONEncoder(); encoder.dateEncodingStrategy = .iso8601 - let decoder = JSONDecoder(); decoder.dateDecodingStrategy = .iso8601 + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .iso8601 + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 let data = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJleHAiOiIyMDMzLTA1LTE4VDAzOjMzOjIwWiIsImFkbWluIjpmYWxzZSwibmFtZSI6IkZvbyIsInN1YiI6InZhcG9yIn0." @@ -323,11 +343,18 @@ class JWTKitTests: XCTestCase { admin: false, exp: .init(value: .init(timeIntervalSince1970: 2_000_000_000)) ) - let keyCollection = await JWTKeyCollection().addUnsecuredNone(parser: DefaultJWTParser(jsonDecoder: decoder), serializer: DefaultJWTSerializer(jsonEncoder: encoder)) + let keyCollection = await JWTKeyCollection().addUnsecuredNone( + parser: DefaultJWTParser(jsonDecoder: decoder), + serializer: DefaultJWTSerializer(jsonEncoder: encoder)) let token = try await keyCollection.sign(payload) - XCTAssert((token.split(separator: ".").dropFirst(1).first.map { String(decoding: Data($0.utf8).base64URLDecodedBytes(), as: UTF8.self) } ?? "").contains(#""exp":""#)) - try await XCTAssertEqualAsync(await keyCollection.verify(token.bytes, as: TestPayload.self), payload) - try await XCTAssertEqualAsync(await keyCollection.verify(data.bytes, as: TestPayload.self), payload) + XCTAssert( + (token.split(separator: ".").dropFirst(1).first.map { + String(decoding: Data($0.utf8).base64URLDecodedBytes(), as: UTF8.self) + } ?? "").contains(#""exp":""#)) + try await XCTAssertEqualAsync( + await keyCollection.verify(token.bytes, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(data.bytes, as: TestPayload.self), payload) XCTAssertTrue(token.hasSuffix(".")) } @@ -361,16 +388,21 @@ class JWTKitTests: XCTestCase { struct CustomParser: JWTParser { var jsonDecoder: JWTJSONDecoder = .defaultForJWT - func parse(_ token: some DataProtocol, as _: Payload.Type) throws -> (header: JWTHeader, payload: Payload, signature: Data) where Payload: JWTPayload { + func parse(_ token: some DataProtocol, as _: Payload.Type) throws -> ( + header: JWTHeader, payload: Payload, signature: Data + ) where Payload: JWTPayload { let (encodedHeader, encodedPayload, encodedSignature) = try getTokenParts(token) - let header = try jsonDecoder.decode(JWTHeader.self, from: .init(encodedHeader.base64URLDecodedBytes())) + let header = try jsonDecoder.decode( + JWTHeader.self, from: .init(encodedHeader.base64URLDecodedBytes())) - let payload = if header.b64?.asBool ?? true { - try self.jsonDecoder.decode(Payload.self, from: .init(encodedPayload.base64URLDecodedBytes())) - } else { - try self.jsonDecoder.decode(Payload.self, from: .init(encodedPayload)) - } + let payload = + if header.b64?.asBool ?? true { + try self.jsonDecoder.decode( + Payload.self, from: .init(encodedPayload.base64URLDecodedBytes())) + } else { + try self.jsonDecoder.decode(Payload.self, from: .init(encodedPayload)) + } let signature = Data(encodedSignature.base64URLDecodedBytes()) @@ -379,9 +411,13 @@ class JWTKitTests: XCTestCase { } let keyCollection = await JWTKeyCollection() - .add(hmac: "secret", digestAlgorithm: .sha256, parser: CustomParser(), serializer: CustomSerializer()) + .add( + hmac: "secret", digestAlgorithm: .sha256, parser: CustomParser(), + serializer: CustomSerializer()) - let payload = TestPayload(sub: "vapor", name: "Foo", admin: false, exp: .init(value: .init(timeIntervalSince1970: 2_000_000_000))) + let payload = TestPayload( + sub: "vapor", name: "Foo", admin: false, + exp: .init(value: .init(timeIntervalSince1970: 2_000_000_000))) let token = try await keyCollection.sign(payload, header: ["b64": true]) let verified = try await keyCollection.verify(token, as: TestPayload.self) @@ -411,7 +447,8 @@ class JWTKitTests: XCTestCase { // remove last "." from token let corruptedToken = String(token.dropLast()) - XCTAssertThrowsError(try parser.parse(corruptedToken.bytes, as: TestPayload.self)) { error in + XCTAssertThrowsError(try parser.parse(corruptedToken.bytes, as: TestPayload.self)) { + error in guard let error = error as? JWTError else { XCTFail("Unexpected error: \(error)") return @@ -440,23 +477,25 @@ class JWTKitTests: XCTestCase { let encodedHeader = try JSONEncoder().encode(parsed.header) let jsonFields = """ - { - "alg": "HS256", - "typ": "JWT", - "foo": "bar", - "baz": 42 - } - """ + { + "alg": "HS256", + "typ": "JWT", + "foo": "bar", + "baz": 42 + } + """ let jsonDecoder = JSONDecoder() XCTAssertEqual( try jsonDecoder.decode([String: JWTHeaderField].self, from: encodedHeader), - try jsonDecoder.decode([String: JWTHeaderField].self, from: jsonFields.data(using: .utf8)!) + try jsonDecoder.decode( + [String: JWTHeaderField].self, from: jsonFields.data(using: .utf8)!) ) } func testCommonHeaderFields() async throws { - let keyCollection = await JWTKeyCollection().add(hmac: .init(key: .init(size: .bits256)), digestAlgorithm: .sha384) + let keyCollection = await JWTKeyCollection().add( + hmac: .init(key: .init(size: .bits256)), digestAlgorithm: .sha384) let payload = TestPayload( sub: "vapor", @@ -536,7 +575,12 @@ class JWTKitTests: XCTestCase { XCTAssertEqual(iss, "C=UK, ST=England, L=London, O=Acme Ltd.") let tan = parsed.header[dynamicMember: "http://openbanking.org.uk/tan"]?.asString XCTAssertEqual(tan, "openbanking.org.uk") - XCTAssertEqual(parsed.header.crit, ["b64", "http://openbanking.org.uk/iat", "http://openbanking.org.uk/iss", "http://openbanking.org.uk/tan"]) + XCTAssertEqual( + parsed.header.crit, + [ + "b64", "http://openbanking.org.uk/iat", "http://openbanking.org.uk/iss", + "http://openbanking.org.uk/tan", + ]) XCTAssertEqual(parsed.header.kid, "90210ABAD") } @@ -554,12 +598,14 @@ class JWTKitTests: XCTestCase { ) let _ = try await keyCollection.sign(payload, header: ["kid": "private"]) - await XCTAssertThrowsErrorAsync(try await keyCollection.sign(payload, header: ["kid": "public"])) + await XCTAssertThrowsErrorAsync( + try await keyCollection.sign(payload, header: ["kid": "public"])) let _ = try await keyCollection.sign(payload, kid: "private") await XCTAssertThrowsErrorAsync(try await keyCollection.sign(payload, kid: "public")) let _ = try await keyCollection.sign(payload, kid: "private", header: ["kid": "public"]) - await XCTAssertThrowsErrorAsync(try await keyCollection.sign(payload, kid: "public", header: ["kid": "private"])) + await XCTAssertThrowsErrorAsync( + try await keyCollection.sign(payload, kid: "public", header: ["kid": "private"])) } func testCustomObjectHeader() async throws { @@ -585,19 +631,20 @@ class JWTKitTests: XCTestCase { func testKeyCollectionIteration() async throws { let hmacToken = """ - eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MjAwMDAwMDAwMH0.GW-OvOyauZXQeFuzFHRFL7saTXJrudGQ_qHtpbVWW9Y - """ + eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MjAwMDAwMDAwMH0.GW-OvOyauZXQeFuzFHRFL7saTXJrudGQ_qHtpbVWW9Y + """ let ecdsaToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MjAwMDAwMDAwMH0.bxLwoupZk9MW5Ys650FNn1CpedHBOPKLf9dRVjmETs3KUn4VIfcxSIK7tOEEeuExgpKssRxYEMpLlFyY6jsLRg - """ - - let ecdsaPrivateKey = try ES256PrivateKey(pem: """ - -----BEGIN PRIVATE KEY----- - MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2 - OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r - 1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G - -----END PRIVATE KEY----- - """) + eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MjAwMDAwMDAwMH0.bxLwoupZk9MW5Ys650FNn1CpedHBOPKLf9dRVjmETs3KUn4VIfcxSIK7tOEEeuExgpKssRxYEMpLlFyY6jsLRg + """ + + let ecdsaPrivateKey = try ES256PrivateKey( + pem: """ + -----BEGIN PRIVATE KEY----- + MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgevZzL1gdAFr88hb2 + OF/2NxApJCzGCEDdfSp6VQO30hyhRANCAAQRWz+jn65BtOMvdyHKcvjBeBSDZH2r + 1RTwjmYSi9R/zpBnuQ4EiMnCqfMPWiZqB4QdbAd0E7oH50VpuZ1P087G + -----END PRIVATE KEY----- + """) let keyCollection = await JWTKeyCollection() .add(hmac: "secret", digestAlgorithm: .sha256, kid: "hmac") @@ -609,15 +656,19 @@ class JWTKitTests: XCTestCase { // The tokens don't have a KID, which means, since we're not iterating // over all the keys in the key collection, only the default (first added) // signer will be used. - await XCTAssertThrowsErrorAsync(try await keyCollection.verify(ecdsaToken, as: TestPayload.self)) { + await XCTAssertThrowsErrorAsync( + try await keyCollection.verify(ecdsaToken, as: TestPayload.self) + ) { guard let error = $0 as? JWTError else { return } XCTAssertEqual(error.errorType, .signatureVerificationFailed) } - let hmacIteratinglyVerified = try await keyCollection.verify(hmacToken, as: TestPayload.self, iteratingKeys: true) + let hmacIteratinglyVerified = try await keyCollection.verify( + hmacToken, as: TestPayload.self, iteratingKeys: true) XCTAssertEqual(hmacIteratinglyVerified.sub, "1234567890") - let ecdsaIteratinglyVerified = try await keyCollection.verify(ecdsaToken, as: TestPayload.self, iteratingKeys: true) + let ecdsaIteratinglyVerified = try await keyCollection.verify( + ecdsaToken, as: TestPayload.self, iteratingKeys: true) XCTAssertEqual(ecdsaIteratinglyVerified.sub, "1234567890") } @@ -659,7 +710,9 @@ class JWTKitTests: XCTestCase { func testJWTErrorDescription() { XCTAssertEqual( - JWTError.claimVerificationFailure(failedClaim: ExpirationClaim(value: .init(timeIntervalSince1970: 1)), reason: "test").description, + JWTError.claimVerificationFailure( + failedClaim: ExpirationClaim(value: .init(timeIntervalSince1970: 1)), reason: "test" + ).description, "JWTKitError(errorType: claimVerificationFailure, failedClaim: JWTKit.ExpirationClaim(value: 1970-01-01 00:00:01 +0000), reason: \"test\")" ) XCTAssertEqual( @@ -764,52 +817,52 @@ struct ExpirationPayload: JWTPayload { } let microsoftJWKS = """ -{ - "keys":[ - { - "kty":"RSA", - "use":"sig", - "kid":"huN95IvPfehq34GzBDZ1GXGirnM", - "x5t":"huN95IvPfehq34GzBDZ1GXGirnM", - "n":"6lldKm5Rc_vMKa1RM_TtUv3tmtj52wLRrJqu13yGM3_h0dwru2ZP53y65wDfz6_tLCjoYuRCuVsjoW37-0zXUORJvZ0L90CAX-58lW7NcE4bAzA1pXv7oR9kQw0X8dp0atU4HnHeaTU8LZxcjJO79_H9cxgwa-clKfGxllcos8TsuurM8xi2dx5VqwzqNMB2s62l3MTN7AzctHUiQCiX2iJArGjAhs-mxS1wmyMIyOSipdodhjQWRAcseW-aFVyRTFVi8okl2cT1HJjPXdx0b1WqYSOzeRdrrLUcA0oR2Tzp7xzOYJZSGNnNLQqa9f6h6h52XbX0iAgxKgEDlRpbJw", - "e":"AQAB", - "x5c":[ - "MIIDBTCCAe2gAwIBAgIQPCxFbySVSLZOggeWRzBWOjANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTIwMDYwNzAwMDAwMFoXDTI1MDYwNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOpZXSpuUXP7zCmtUTP07VL97ZrY+dsC0ayartd8hjN/4dHcK7tmT+d8uucA38+v7Swo6GLkQrlbI6Ft+/tM11DkSb2dC/dAgF/ufJVuzXBOGwMwNaV7+6EfZEMNF/HadGrVOB5x3mk1PC2cXIyTu/fx/XMYMGvnJSnxsZZXKLPE7LrqzPMYtnceVasM6jTAdrOtpdzEzewM3LR1IkAol9oiQKxowIbPpsUtcJsjCMjkoqXaHYY0FkQHLHlvmhVckUxVYvKJJdnE9RyYz13cdG9VqmEjs3kXa6y1HANKEdk86e8czmCWUhjZzS0KmvX+oeoedl219IgIMSoBA5UaWycCAwEAAaMhMB8wHQYDVR0OBBYEFFXP0ODFhjf3RS6oRijM5Tb+yB8CMA0GCSqGSIb3DQEBCwUAA4IBAQB9GtVikLTbJWIu5x9YCUTTKzNhi44XXogP/v8VylRSUHI5YTMdnWwvDIt/Y1sjNonmSy9PrioEjcIiI1U8nicveafMwIq5VLn+gEY2lg6KDJAzgAvA88CXqwfHHvtmYBovN7goolp8TY/kddMTf6TpNzN3lCTM2MK4Ye5xLLVGdp4bqWCOJ/qjwDxpTRSydYIkLUDwqNjv+sYfOElJpYAB4rTL/aw3ChJ1iaA4MtXEt6OjbUtbOa21lShfLzvNRbYK3+ukbrhmRl9lemJEeUls51vPuIe+jg+Ssp43aw7PQjxt4/MpfNMS2BfZ5F8GVSVG7qNb352cLLeJg5rc398Z" - ] - }, - { - "kty":"RSA", - "use":"sig", - "kid":"jibNbkFSSbmxPYrN9CFqRk4K4gw", - "x5t":"jibNbkFSSbmxPYrN9CFqRk4K4gw", - "n":"2YX-YDuuTzPiaiZKt04IuUzAjCjPLLmBCVA6npKuZyIouMuaSEuM7BP8QctfCprUY16Rq2-KDrAEvaaKJvsD5ZONddt79yFdCs1E8wKlYIPO74fSpePdVDizflr5W-QCFH9tokbZrHBBuluFojgtbvPMXAhHfZTGC4ItZ0i_Lc9eXwtENHJQC4e4m7olweK1ExM-OzsKGzDlOsOUOU5pN2sHY74nXPqQRH1dQKfB0NT0YrfkbnR8fiq8z-soixfECUXkF8FzWnMnqL6X90wngnuIi8OtH2mvDcnsvUVh3K2JgvSgjRWZbsDx6G-mVQL2vEuHXMXoIoe8hd1ZpV16pQ", - "e":"AQAB", - "x5c":[ - "MIIDBTCCAe2gAwIBAgIQUUG7iptQUoVA7bYvX2tHlDANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTIwMDcxODAwMDAwMFoXDTI1MDcxODAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANmF/mA7rk8z4momSrdOCLlMwIwozyy5gQlQOp6SrmciKLjLmkhLjOwT/EHLXwqa1GNekatvig6wBL2miib7A+WTjXXbe/chXQrNRPMCpWCDzu+H0qXj3VQ4s35a+VvkAhR/baJG2axwQbpbhaI4LW7zzFwIR32UxguCLWdIvy3PXl8LRDRyUAuHuJu6JcHitRMTPjs7Chsw5TrDlDlOaTdrB2O+J1z6kER9XUCnwdDU9GK35G50fH4qvM/rKIsXxAlF5BfBc1pzJ6i+l/dMJ4J7iIvDrR9prw3J7L1FYdytiYL0oI0VmW7A8ehvplUC9rxLh1zF6CKHvIXdWaVdeqUCAwEAAaMhMB8wHQYDVR0OBBYEFFOUEOWLUJOTFTOlr7P+6GxsmM90MA0GCSqGSIb3DQEBCwUAA4IBAQCP+LLZw7SSYnWQmRGWHmksBwwJ4Gy32C6g7+wZZv3ombHW9mwLQuzsir97/PP042i/ZIxePHJavpeLm/z3KMSpGIPmiPtmgNcK4HtLTEDnoTprnllobOAqU0TREFWogjkockNo98AvpsmHxNMXuwDikto9o/d9ACBtpkpatS2xgVOZxZtqyMpwZzSJARD5A4qcKov4zdqntVyjpZGK4N6ZaedRbEVd12m1VI+dtDB9+EJRqtTn8zamPYljVTEPNCbDAFgKBDtrhwBnrrrnKTq4/LEOouNQZuUucBTMOGDn4FEejNh3qbxNdWR6tSZbXUnJ+NIQ99IqZMvvMqm9ndL7" - ] - }, - { - "kty":"RSA", - "use":"sig", - "kid":"M6pX7RHoraLsprfJeRCjSxuURhc", - "x5t":"M6pX7RHoraLsprfJeRCjSxuURhc", - "n":"xHScZMPo8FifoDcrgncWQ7mGJtiKhrsho0-uFPXg-OdnRKYudTD7-Bq1MDjcqWRf3IfDVjFJixQS61M7wm9wALDj--lLuJJ9jDUAWTA3xWvQLbiBM-gqU0sj4mc2lWm6nPfqlyYeWtQcSC0sYkLlayNgX4noKDaXivhVOp7bwGXq77MRzeL4-9qrRYKjuzHfZL7kNBCsqO185P0NI2Jtmw-EsqYsrCaHsfNRGRrTvUHUq3hWa859kK_5uNd7TeY2ZEwKVD8ezCmSfR59ZzyxTtuPpkCSHS9OtUvS3mqTYit73qcvprjl3R8hpjXLb8oftfpWr3hFRdpxrwuoQEO4QQ", - "e":"AQAB", - "x5c":[ - "MIIC8TCCAdmgAwIBAgIQfEWlTVc1uINEc9RBi6qHMjANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwHhcNMTgxMDE0MDAwMDAwWhcNMjAxMDE0MDAwMDAwWjAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEdJxkw+jwWJ+gNyuCdxZDuYYm2IqGuyGjT64U9eD452dEpi51MPv4GrUwONypZF/ch8NWMUmLFBLrUzvCb3AAsOP76Uu4kn2MNQBZMDfFa9AtuIEz6CpTSyPiZzaVabqc9+qXJh5a1BxILSxiQuVrI2BfiegoNpeK+FU6ntvAZervsxHN4vj72qtFgqO7Md9kvuQ0EKyo7Xzk/Q0jYm2bD4SypiysJoex81EZGtO9QdSreFZrzn2Qr/m413tN5jZkTApUPx7MKZJ9Hn1nPLFO24+mQJIdL061S9LeapNiK3vepy+muOXdHyGmNctvyh+1+laveEVF2nGvC6hAQ7hBAgMBAAGjITAfMB0GA1UdDgQWBBQ5TKadw06O0cvXrQbXW0Nb3M3h/DANBgkqhkiG9w0BAQsFAAOCAQEAI48JaFtwOFcYS/3pfS5+7cINrafXAKTL+/+he4q+RMx4TCu/L1dl9zS5W1BeJNO2GUznfI+b5KndrxdlB6qJIDf6TRHh6EqfA18oJP5NOiKhU4pgkF2UMUw4kjxaZ5fQrSoD9omjfHAFNjradnHA7GOAoF4iotvXDWDBWx9K4XNZHWvD11Td66zTg5IaEQDIZ+f8WS6nn/98nAVMDtR9zW7Te5h9kGJGfe6WiHVaGRPpBvqC4iypGHjbRwANwofZvmp5wP08hY1CsnKY5tfP+E2k/iAQgKKa6QoxXToYvP7rsSkglak8N5g/+FJGnq4wP6cOzgZpjdPMwaVt5432GA==" - ] - } - ] -} -""" + { + "keys":[ + { + "kty":"RSA", + "use":"sig", + "kid":"huN95IvPfehq34GzBDZ1GXGirnM", + "x5t":"huN95IvPfehq34GzBDZ1GXGirnM", + "n":"6lldKm5Rc_vMKa1RM_TtUv3tmtj52wLRrJqu13yGM3_h0dwru2ZP53y65wDfz6_tLCjoYuRCuVsjoW37-0zXUORJvZ0L90CAX-58lW7NcE4bAzA1pXv7oR9kQw0X8dp0atU4HnHeaTU8LZxcjJO79_H9cxgwa-clKfGxllcos8TsuurM8xi2dx5VqwzqNMB2s62l3MTN7AzctHUiQCiX2iJArGjAhs-mxS1wmyMIyOSipdodhjQWRAcseW-aFVyRTFVi8okl2cT1HJjPXdx0b1WqYSOzeRdrrLUcA0oR2Tzp7xzOYJZSGNnNLQqa9f6h6h52XbX0iAgxKgEDlRpbJw", + "e":"AQAB", + "x5c":[ + "MIIDBTCCAe2gAwIBAgIQPCxFbySVSLZOggeWRzBWOjANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTIwMDYwNzAwMDAwMFoXDTI1MDYwNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOpZXSpuUXP7zCmtUTP07VL97ZrY+dsC0ayartd8hjN/4dHcK7tmT+d8uucA38+v7Swo6GLkQrlbI6Ft+/tM11DkSb2dC/dAgF/ufJVuzXBOGwMwNaV7+6EfZEMNF/HadGrVOB5x3mk1PC2cXIyTu/fx/XMYMGvnJSnxsZZXKLPE7LrqzPMYtnceVasM6jTAdrOtpdzEzewM3LR1IkAol9oiQKxowIbPpsUtcJsjCMjkoqXaHYY0FkQHLHlvmhVckUxVYvKJJdnE9RyYz13cdG9VqmEjs3kXa6y1HANKEdk86e8czmCWUhjZzS0KmvX+oeoedl219IgIMSoBA5UaWycCAwEAAaMhMB8wHQYDVR0OBBYEFFXP0ODFhjf3RS6oRijM5Tb+yB8CMA0GCSqGSIb3DQEBCwUAA4IBAQB9GtVikLTbJWIu5x9YCUTTKzNhi44XXogP/v8VylRSUHI5YTMdnWwvDIt/Y1sjNonmSy9PrioEjcIiI1U8nicveafMwIq5VLn+gEY2lg6KDJAzgAvA88CXqwfHHvtmYBovN7goolp8TY/kddMTf6TpNzN3lCTM2MK4Ye5xLLVGdp4bqWCOJ/qjwDxpTRSydYIkLUDwqNjv+sYfOElJpYAB4rTL/aw3ChJ1iaA4MtXEt6OjbUtbOa21lShfLzvNRbYK3+ukbrhmRl9lemJEeUls51vPuIe+jg+Ssp43aw7PQjxt4/MpfNMS2BfZ5F8GVSVG7qNb352cLLeJg5rc398Z" + ] + }, + { + "kty":"RSA", + "use":"sig", + "kid":"jibNbkFSSbmxPYrN9CFqRk4K4gw", + "x5t":"jibNbkFSSbmxPYrN9CFqRk4K4gw", + "n":"2YX-YDuuTzPiaiZKt04IuUzAjCjPLLmBCVA6npKuZyIouMuaSEuM7BP8QctfCprUY16Rq2-KDrAEvaaKJvsD5ZONddt79yFdCs1E8wKlYIPO74fSpePdVDizflr5W-QCFH9tokbZrHBBuluFojgtbvPMXAhHfZTGC4ItZ0i_Lc9eXwtENHJQC4e4m7olweK1ExM-OzsKGzDlOsOUOU5pN2sHY74nXPqQRH1dQKfB0NT0YrfkbnR8fiq8z-soixfECUXkF8FzWnMnqL6X90wngnuIi8OtH2mvDcnsvUVh3K2JgvSgjRWZbsDx6G-mVQL2vEuHXMXoIoe8hd1ZpV16pQ", + "e":"AQAB", + "x5c":[ + "MIIDBTCCAe2gAwIBAgIQUUG7iptQUoVA7bYvX2tHlDANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTIwMDcxODAwMDAwMFoXDTI1MDcxODAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANmF/mA7rk8z4momSrdOCLlMwIwozyy5gQlQOp6SrmciKLjLmkhLjOwT/EHLXwqa1GNekatvig6wBL2miib7A+WTjXXbe/chXQrNRPMCpWCDzu+H0qXj3VQ4s35a+VvkAhR/baJG2axwQbpbhaI4LW7zzFwIR32UxguCLWdIvy3PXl8LRDRyUAuHuJu6JcHitRMTPjs7Chsw5TrDlDlOaTdrB2O+J1z6kER9XUCnwdDU9GK35G50fH4qvM/rKIsXxAlF5BfBc1pzJ6i+l/dMJ4J7iIvDrR9prw3J7L1FYdytiYL0oI0VmW7A8ehvplUC9rxLh1zF6CKHvIXdWaVdeqUCAwEAAaMhMB8wHQYDVR0OBBYEFFOUEOWLUJOTFTOlr7P+6GxsmM90MA0GCSqGSIb3DQEBCwUAA4IBAQCP+LLZw7SSYnWQmRGWHmksBwwJ4Gy32C6g7+wZZv3ombHW9mwLQuzsir97/PP042i/ZIxePHJavpeLm/z3KMSpGIPmiPtmgNcK4HtLTEDnoTprnllobOAqU0TREFWogjkockNo98AvpsmHxNMXuwDikto9o/d9ACBtpkpatS2xgVOZxZtqyMpwZzSJARD5A4qcKov4zdqntVyjpZGK4N6ZaedRbEVd12m1VI+dtDB9+EJRqtTn8zamPYljVTEPNCbDAFgKBDtrhwBnrrrnKTq4/LEOouNQZuUucBTMOGDn4FEejNh3qbxNdWR6tSZbXUnJ+NIQ99IqZMvvMqm9ndL7" + ] + }, + { + "kty":"RSA", + "use":"sig", + "kid":"M6pX7RHoraLsprfJeRCjSxuURhc", + "x5t":"M6pX7RHoraLsprfJeRCjSxuURhc", + "n":"xHScZMPo8FifoDcrgncWQ7mGJtiKhrsho0-uFPXg-OdnRKYudTD7-Bq1MDjcqWRf3IfDVjFJixQS61M7wm9wALDj--lLuJJ9jDUAWTA3xWvQLbiBM-gqU0sj4mc2lWm6nPfqlyYeWtQcSC0sYkLlayNgX4noKDaXivhVOp7bwGXq77MRzeL4-9qrRYKjuzHfZL7kNBCsqO185P0NI2Jtmw-EsqYsrCaHsfNRGRrTvUHUq3hWa859kK_5uNd7TeY2ZEwKVD8ezCmSfR59ZzyxTtuPpkCSHS9OtUvS3mqTYit73qcvprjl3R8hpjXLb8oftfpWr3hFRdpxrwuoQEO4QQ", + "e":"AQAB", + "x5c":[ + "MIIC8TCCAdmgAwIBAgIQfEWlTVc1uINEc9RBi6qHMjANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwHhcNMTgxMDE0MDAwMDAwWhcNMjAxMDE0MDAwMDAwWjAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEdJxkw+jwWJ+gNyuCdxZDuYYm2IqGuyGjT64U9eD452dEpi51MPv4GrUwONypZF/ch8NWMUmLFBLrUzvCb3AAsOP76Uu4kn2MNQBZMDfFa9AtuIEz6CpTSyPiZzaVabqc9+qXJh5a1BxILSxiQuVrI2BfiegoNpeK+FU6ntvAZervsxHN4vj72qtFgqO7Md9kvuQ0EKyo7Xzk/Q0jYm2bD4SypiysJoex81EZGtO9QdSreFZrzn2Qr/m413tN5jZkTApUPx7MKZJ9Hn1nPLFO24+mQJIdL061S9LeapNiK3vepy+muOXdHyGmNctvyh+1+laveEVF2nGvC6hAQ7hBAgMBAAGjITAfMB0GA1UdDgQWBBQ5TKadw06O0cvXrQbXW0Nb3M3h/DANBgkqhkiG9w0BAQsFAAOCAQEAI48JaFtwOFcYS/3pfS5+7cINrafXAKTL+/+he4q+RMx4TCu/L1dl9zS5W1BeJNO2GUznfI+b5KndrxdlB6qJIDf6TRHh6EqfA18oJP5NOiKhU4pgkF2UMUw4kjxaZ5fQrSoD9omjfHAFNjradnHA7GOAoF4iotvXDWDBWx9K4XNZHWvD11Td66zTg5IaEQDIZ+f8WS6nn/98nAVMDtR9zW7Te5h9kGJGfe6WiHVaGRPpBvqC4iypGHjbRwANwofZvmp5wP08hY1CsnKY5tfP+E2k/iAQgKKa6QoxXToYvP7rsSkglak8N5g/+FJGnq4wP6cOzgZpjdPMwaVt5432GA==" + ] + } + ] + } + """ let rsaModulus = """ -gWu7yhI35FScdKARYboJoAm-T7yJfJ9JTvAok_RKOJYcL8oLIRSeLqQX83PPZiWdKTdXaiGWntpDu6vW7VAb-HWPF6tNYSLKDSmR3sEu2488ibWijZtNTCKOSb_1iAKAI5BJ80LTqyQtqaKzT0XUBtMsde8vX1nKI05UxujfTX3kqUtkZgLv1Yk1ZDpUoLOWUTtCm68zpjtBrPiN8bU2jqCGFyMyyXys31xFRzz4MyJ5tREHkQCzx0g7AvW0ge_sBTPQ2U6NSkcZvQyDbfDv27cMUHij1Sjx16SY9a2naTuOgamjtUzyClPLVpchX-McNyS0tjdxWY_yRL9MYuw4AQ -""" + gWu7yhI35FScdKARYboJoAm-T7yJfJ9JTvAok_RKOJYcL8oLIRSeLqQX83PPZiWdKTdXaiGWntpDu6vW7VAb-HWPF6tNYSLKDSmR3sEu2488ibWijZtNTCKOSb_1iAKAI5BJ80LTqyQtqaKzT0XUBtMsde8vX1nKI05UxujfTX3kqUtkZgLv1Yk1ZDpUoLOWUTtCm68zpjtBrPiN8bU2jqCGFyMyyXys31xFRzz4MyJ5tREHkQCzx0g7AvW0ge_sBTPQ2U6NSkcZvQyDbfDv27cMUHij1Sjx16SY9a2naTuOgamjtUzyClPLVpchX-McNyS0tjdxWY_yRL9MYuw4AQ + """ let rsaPrivateExponent = """ -L4z0tz7QWE0aGuOA32YqCSnrSYKdBTPFDILCdfHonzfP7WMPibz4jWxu_FzNk9s4Dh-uN2lV3NGW10pAsnqffD89LtYanRjaIdHnLW_PFo5fEL2yltK7qMB9hO1JegppKCfoc79W4-dr-4qy1Op0B3npOP-DaUYlNamfDmIbQW32UKeJzdGIn-_ryrBT7hQW6_uHLS2VFPPk0rNkPPKZYoNaqGnJ0eaFFF-dFwiThXIpPz--dxTAL8xYf275rjG8C9lh6awOfJSIdXMVuQITWf62E0mSQPR2-219bShMKriDYcYLbT3BJEgOkRBBHGuHo9R5TN298anxZqV1u5jtUQ -""" + L4z0tz7QWE0aGuOA32YqCSnrSYKdBTPFDILCdfHonzfP7WMPibz4jWxu_FzNk9s4Dh-uN2lV3NGW10pAsnqffD89LtYanRjaIdHnLW_PFo5fEL2yltK7qMB9hO1JegppKCfoc79W4-dr-4qy1Op0B3npOP-DaUYlNamfDmIbQW32UKeJzdGIn-_ryrBT7hQW6_uHLS2VFPPk0rNkPPKZYoNaqGnJ0eaFFF-dFwiThXIpPz--dxTAL8xYf275rjG8C9lh6awOfJSIdXMVuQITWf62E0mSQPR2-219bShMKriDYcYLbT3BJEgOkRBBHGuHo9R5TN298anxZqV1u5jtUQ + """ struct FirebasePayload: JWTPayload, Equatable { enum CodingKeys: String, CodingKey { @@ -838,27 +891,27 @@ struct FirebasePayload: JWTPayload, Equatable { } let firebaseJWT = """ -eyJhbGciOiJSUzI1NiIsImtpZCI6IjU1NGE3NTQ3Nzg1ODdjOTRjMTY3M2U4ZWEyNDQ2MTZjMGMwNDNjYmMiLCJ0eXAiOiJKV1QifQ.eyJwcm92aWRlcl9pZCI6ImFub255bW91cyIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS92enNnLXNjaGVkdWxlLXRlc3QiLCJhdWQiOiJ2enNnLXNjaGVkdWxlLXRlc3QiLCJhdXRoX3RpbWUiOjE1OTYyMzg5ODIsInVzZXJfaWQiOiJ5OHdpS1RoWEdLTTg4eHhyUVdEWnpLbkJ1cXYyIiwic3ViIjoieTh3aUtUaFhHS004OHh4clFXRFp6S25CdXF2MiIsImlhdCI6MTU5NjIzODk4MiwiZXhwIjoxNTk2MjQyNTgyLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7fSwic2lnbl9pbl9wcm92aWRlciI6ImFub255bW91cyJ9fQ.vW5N3RqN8ba_P56GgjyMY-RE3hr_ciEw-E_oBtVjMJw3pgIO7MDHj0eRqTDTbjapN0BhkxTjkOA-L5pGO-9uA7afO-45vmiyaFDaN_oIYHNCewDgVaphDy_CYQ1PJugZHVjumk-qgzdS9nen_6oXmWZ1CYMop-g8UEyVHUaU-yjnvYSvvRWcas--HaErcsPY6uDx9DR8R2_mC-_VHBD58zN1svjTELkeVIZtkvA2Pxy1WO1NKxc0hWiz7w6RTu6P56_DJ1OqyMwxQavblaufdjccuC3bnv_MGKM8xhtsYLFWPnwFD762A50cHyS6SondruP7UnFQc1owlB6gaxEihw -""" + eyJhbGciOiJSUzI1NiIsImtpZCI6IjU1NGE3NTQ3Nzg1ODdjOTRjMTY3M2U4ZWEyNDQ2MTZjMGMwNDNjYmMiLCJ0eXAiOiJKV1QifQ.eyJwcm92aWRlcl9pZCI6ImFub255bW91cyIsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS92enNnLXNjaGVkdWxlLXRlc3QiLCJhdWQiOiJ2enNnLXNjaGVkdWxlLXRlc3QiLCJhdXRoX3RpbWUiOjE1OTYyMzg5ODIsInVzZXJfaWQiOiJ5OHdpS1RoWEdLTTg4eHhyUVdEWnpLbkJ1cXYyIiwic3ViIjoieTh3aUtUaFhHS004OHh4clFXRFp6S25CdXF2MiIsImlhdCI6MTU5NjIzODk4MiwiZXhwIjoxNTk2MjQyNTgyLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7fSwic2lnbl9pbl9wcm92aWRlciI6ImFub255bW91cyJ9fQ.vW5N3RqN8ba_P56GgjyMY-RE3hr_ciEw-E_oBtVjMJw3pgIO7MDHj0eRqTDTbjapN0BhkxTjkOA-L5pGO-9uA7afO-45vmiyaFDaN_oIYHNCewDgVaphDy_CYQ1PJugZHVjumk-qgzdS9nen_6oXmWZ1CYMop-g8UEyVHUaU-yjnvYSvvRWcas--HaErcsPY6uDx9DR8R2_mC-_VHBD58zN1svjTELkeVIZtkvA2Pxy1WO1NKxc0hWiz7w6RTu6P56_DJ1OqyMwxQavblaufdjccuC3bnv_MGKM8xhtsYLFWPnwFD762A50cHyS6SondruP7UnFQc1owlB6gaxEihw + """ let firebaseCert = """ ------BEGIN CERTIFICATE----- -MIIDHDCCAgSgAwIBAgIIOvZ+ZDrIgmQwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE -AxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMjAw -NzI0MDkyMDAxWhcNMjAwODA5MjEzNTAxWjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl -bi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBANSBPQydBvIITxwMsm0adXL5ToKR6Aihi3fCepGZj1Oq2pdq -r9ObfFcDX4GKHF7w6pm8WXxoZnjO37waSJc1ECmZt11tR0Ei/f0huLqDqNItGWRc -ApogR3Af8C12IwFbxvp5tPj4s8H7Ldnrr97zzXogrTKvQCVJQJE43SfqcOO0T1br -gfskj+G863Uy5JN7S8OijDLFK3YGIIvQDv6jp0tVrRwUUedJ4qET3IVWLkW5jAcd -WAy7/RmIVVZFXuqjyunU6xNd6gLw5uZPZdLjSW9CccFmZQfinuNKyFGLhdF00TMq -Torq8EOjFanRbxRi3mb9g01hVKY8WcsK1CE4RCMCAwEAAaM4MDYwDAYDVR0TAQH/ -BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ -KoZIhvcNAQEFBQADggEBAGMRck+Afw3zQF3SqgJ80bCgFJy4CidQuoNuElA673Y+ -H4ulR5n/UV3feelR2+q0PvbZIVNf3Y5Yt+AWK9uK3LPprouFnx4U2X+mxsLHlHUC -Kl+wKoLuDvAmiDHu5JIjoYO0el6JJYNVnG3wCrSLLc6ehA32hfngdtJmkDN0/OoM -xmbj7X3JWctiJw0NxmH8wrKbeZLVIsaCwfc8iKjwcqRyA6hUxTobcsNs3IZsYv2W -g/5ZupoI8k2foTq4OdXJH/hkq4N5AyLp9S/RSodW6X+gexxohtgJxGx0gojotMzX -sb7NLsl7DkvjjxTz7I98xaGbfhofgYympeKT6UO+tmc= ------END CERTIFICATE----- -""" + -----BEGIN CERTIFICATE----- + MIIDHDCCAgSgAwIBAgIIOvZ+ZDrIgmQwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE + AxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMjAw + NzI0MDkyMDAxWhcNMjAwODA5MjEzNTAxWjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl + bi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD + ggEPADCCAQoCggEBANSBPQydBvIITxwMsm0adXL5ToKR6Aihi3fCepGZj1Oq2pdq + r9ObfFcDX4GKHF7w6pm8WXxoZnjO37waSJc1ECmZt11tR0Ei/f0huLqDqNItGWRc + ApogR3Af8C12IwFbxvp5tPj4s8H7Ldnrr97zzXogrTKvQCVJQJE43SfqcOO0T1br + gfskj+G863Uy5JN7S8OijDLFK3YGIIvQDv6jp0tVrRwUUedJ4qET3IVWLkW5jAcd + WAy7/RmIVVZFXuqjyunU6xNd6gLw5uZPZdLjSW9CccFmZQfinuNKyFGLhdF00TMq + Torq8EOjFanRbxRi3mb9g01hVKY8WcsK1CE4RCMCAwEAAaM4MDYwDAYDVR0TAQH/ + BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ + KoZIhvcNAQEFBQADggEBAGMRck+Afw3zQF3SqgJ80bCgFJy4CidQuoNuElA673Y+ + H4ulR5n/UV3feelR2+q0PvbZIVNf3Y5Yt+AWK9uK3LPprouFnx4U2X+mxsLHlHUC + Kl+wKoLuDvAmiDHu5JIjoYO0el6JJYNVnG3wCrSLLc6ehA32hfngdtJmkDN0/OoM + xmbj7X3JWctiJw0NxmH8wrKbeZLVIsaCwfc8iKjwcqRyA6hUxTobcsNs3IZsYv2W + g/5ZupoI8k2foTq4OdXJH/hkq4N5AyLp9S/RSodW6X+gexxohtgJxGx0gojotMzX + sb7NLsl7DkvjjxTz7I98xaGbfhofgYympeKT6UO+tmc= + -----END CERTIFICATE----- + """ diff --git a/Tests/JWTKitTests/PSSTests.swift b/Tests/JWTKitTests/PSSTests.swift index 1cdd1de9..54fac1d8 100644 --- a/Tests/JWTKitTests/PSSTests.swift +++ b/Tests/JWTKitTests/PSSTests.swift @@ -1,17 +1,23 @@ import JWTKit import XCTest -final class PSSTests: XCTestCase { +final class PSSTests: XCTestCase, @unchecked Sendable { func testPSSDocs() async throws { await XCTAssertNoThrowAsync( - try await JWTKeyCollection().add(pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256) + try await JWTKeyCollection().add( + pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256) ) } func testSigning() async throws { let keyCollection = try await JWTKeyCollection() - .add(pss: Insecure.RSA.PrivateKey(pem: privateKey), digestAlgorithm: .sha256, kid: "private") - .add(pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public") + .add( + pss: Insecure.RSA.PrivateKey(pem: privateKey), digestAlgorithm: .sha256, + kid: "private" + ) + .add( + pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public" + ) let payload = TestPayload( sub: "vapor", @@ -21,12 +27,15 @@ final class PSSTests: XCTestCase { ) let token = try await keyCollection.sign(payload, header: ["kid": "private"]) - try await XCTAssertEqualAsync(await keyCollection.verify(token, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(token, as: TestPayload.self), payload) } func testSigningWithPublic() async throws { let keyCollection = try await JWTKeyCollection() - .add(pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public") + .add( + pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public" + ) let payload = TestPayload( sub: "vapor", @@ -52,11 +61,17 @@ final class PSSTests: XCTestCase { } let keyCollection = try await JWTKeyCollection() - .add(pss: Insecure.RSA.PrivateKey(pem: privateKey), digestAlgorithm: .sha256, kid: "private") - .add(pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public") + .add( + pss: Insecure.RSA.PrivateKey(pem: privateKey), digestAlgorithm: .sha256, + kid: "private" + ) + .add( + pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public" + ) do { - let token = try await keyCollection.sign(Payload(foo: "qux"), header: ["kid": "private"]) + let token = try await keyCollection.sign( + Payload(foo: "qux"), header: ["kid": "private"]) _ = try await keyCollection.verify(token, as: Payload.self) } catch let error as NotBar { XCTAssertEqual(error.foo, "qux") @@ -83,16 +98,20 @@ final class PSSTests: XCTestCase { func testExportPublicKeyWhenKeyIsPrivate() async throws { let privateKey = try Insecure.RSA.PrivateKey(pem: privateKey) - let publicKeyFromPrivate = try Insecure.RSA.PublicKey(pem: privateKey.publicKey.pemRepresentation) + let publicKeyFromPrivate = try Insecure.RSA.PublicKey( + pem: privateKey.publicKey.pemRepresentation) let publicKey = try Insecure.RSA.PublicKey(pem: publicKey) XCTAssertEqual(publicKeyFromPrivate, publicKey) } func testPS256InJWT() async throws { - let token = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MjAwMDAwMDAwMH0.dCaprjSiEw1w_cS2JzjWlp1mxdF9MV86VMylKiEZf6gM8NZhNo3hgnI3Gg7G_WL_bSzys9Z0QtNpWZeW1Mooa29qDqZolQLKbzyjiIMDFBslz_Hei-tI5318UdFLKIlMT0VyDThwFjyPCiVEvOkKokWSXXGZCHArGXouTWvaTND9C0gOMwSkE8cHU7e0u-_pDEfdv9MRQiGy1Wj-9T_ZN6a0g8yFMQcOU6voo-WSY-m98oylYOifiOighitlD0xNScDnxBH5Qp7yyU81m-s2-xoYVQJhGduvi8mxbo_bU48WIJfmdAYX3aAUh_xpvgcd55bdeMT55G_qnkDBDSLvbQ" + let token = + "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MjAwMDAwMDAwMH0.dCaprjSiEw1w_cS2JzjWlp1mxdF9MV86VMylKiEZf6gM8NZhNo3hgnI3Gg7G_WL_bSzys9Z0QtNpWZeW1Mooa29qDqZolQLKbzyjiIMDFBslz_Hei-tI5318UdFLKIlMT0VyDThwFjyPCiVEvOkKokWSXXGZCHArGXouTWvaTND9C0gOMwSkE8cHU7e0u-_pDEfdv9MRQiGy1Wj-9T_ZN6a0g8yFMQcOU6voo-WSY-m98oylYOifiOighitlD0xNScDnxBH5Qp7yyU81m-s2-xoYVQJhGduvi8mxbo_bU48WIJfmdAYX3aAUh_xpvgcd55bdeMT55G_qnkDBDSLvbQ" let keyCollection = try await JWTKeyCollection() - .add(pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public") + .add( + pss: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public" + ) let payload = try await keyCollection.verify(token, as: TestPayload.self) XCTAssertEqual(payload.sub.value, "1234567890") @@ -103,29 +122,29 @@ final class PSSTests: XCTestCase { func testPS256InJWK() async throws { let publicJWK = """ - { - "kty": "RSA", - "kid": "public", - "use": "sig", - "alg": "PS256", - "kid": "public", - "n": "\(modulus)", - "e": "\(publicExponent)" - } - """ + { + "kty": "RSA", + "kid": "public", + "use": "sig", + "alg": "PS256", + "kid": "public", + "n": "\(modulus)", + "e": "\(publicExponent)" + } + """ let privateJWK = """ - { - "kty": "RSA", - "kid": "private", - "use": "sig", - "alg": "PS256", - "kid": "private", - "n": "\(modulus)", - "e": "\(publicExponent)", - "d": "\(privateExponent)" - } - """ + { + "kty": "RSA", + "kid": "private", + "use": "sig", + "alg": "PS256", + "kid": "private", + "n": "\(modulus)", + "e": "\(publicExponent)", + "d": "\(privateExponent)" + } + """ let keyCollection = try await JWTKeyCollection() .add(jwk: .init(json: publicJWK)) @@ -139,61 +158,62 @@ final class PSSTests: XCTestCase { ) let token = try await keyCollection.sign(payload, kid: "private") - try await XCTAssertEqualAsync(await keyCollection.verify(token, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(token, as: TestPayload.self), payload) } // openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private_key.pem let privateKey = """ - -----BEGIN PRIVATE KEY----- - MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCaCfo4dC9OBEl1 - 497MgHOq6OL2Amz7Jcn3xuNZjyPcfzneSVA+2SmNdxx37xrqNf+ej17MpqQSlaiS - 0X9WkD+tFO7VvkiSfPbNcIbMXauoVQioOX5i2yga27Dx9ywyfF7OIjz1ighgvw1T - dLqzDqd3uT19BSX00TpEZA+3rMKoOaEPuHLSDep7IWZhwa8cm3b3aHSlyjSlt6Gh - rJv4u852rDeeT4iIfCsbstUK+Ag4IdlN2Fmuv4jlZ+uamcZJjBZ6C3+ql9ykdhuJ - tTy+qvFstbQm7pYHjJLGt7t5EHkqZBmWXPJOft/8wJf0t7Joj9kbx7wxKjM37Jvm - Q2lZu5KJAgMBAAECggEAQksa+iLenPfxWaBJKb/6h8qUqwWeO3Qm+NEK1WdqKqJC - mGz68SFq5awmf2NTNQsqSOYxCWiKYkkwdIdfAzUvgmDo7Opot0q6uO29xcRmdRqr - kCK2RvtExlJYU7pptgyajKJlk9LlCiYPKSSqmRcscbUyRlTp4fQN3JMnxIfAer8v - dKGYVvZ3FXWcFS7c/ogDlAkatFmy1J7fJpAitH1BALEC2j3uB9+AZ1ILTcGWrj4i - VaLuPto8ySlGsGoq2a5uMMZ9l8+AEIvDSdvPOpe6uzFqLDNDsb02YFm44Q/zrfr1 - Tsg3PIRH0dKi2kMVkUabZg0Ius8j0LtS/4DhgDqFBQKBgQDQlVuBRqnt6H5VKkY7 - jqVvackUBeKeY9tAzErHoLc4UKr32A/gPA341YTQTG8Jx2AmII9HfWv8Bk3E9Txm - YFZRyFNzsup9MVWAJzjmmBelbA+sAnFbpvuHKwEw6Mm3B68g113PNFKZFKtv8Ioi - 5oxLdAj02CA5b7H3rrgcwcGWxwKBgQC9Dl8c8VtPJDv/7+f8RNLERPWyR+oQKywr - xyp4VKRRVOcMt6M7cB0yL1uMl7dhI4lI6ZUGA5z7Tz/pAHdGvAHJ9zqbhGWgAC0f - 9UcKAUyIx2Ja1QMfxjR7rbhBLswezBXChFTBMo57Cl6BdMV2/5Dy0u+X9hknpXlo - QoJwyhP8LwKBgQDOSekmAe0uDjJjqFutq3aSqdzkoK1wWPIPM/0BUkHiwGVWmamZ - 68slvoaMPAvVcAn3q1wJKFIT/2gK0z/ZQI4edDGUy+59wrz88c2kwechA668P+48 - 5vj8xdt3s8NL8Z2SrW1p8CWAoKCtJQh5W+qE9U2mWdoE9CLfAz2zsyzzIQKBgD27 - q6MvzLkTA+SW2hGuB4S/X9tPUEbnUg0Zg+y29tD4AFpOvKZz/ZSdki9eeyrlB7cf - TuIf2+rT/fJ/jHM0gQEKEcEmgmi0pgeBeCj0M6GWOa+fTt3ZQtn/5+Kg/VYxHgne - XC6Z65yRzjpHfxNUcGhaKJJecehYSESbMyzRT6VRAoGBAIcrVYjy0losUQx1jGR7 - XrUn2I12rLjiNmaxub78SJxWRd0TXywSh/CPdX42xs9ku2Rur0Ar+NweEb7YQjZX - c9YrKwe4kFm7wawf9Sy+9ZvkSqNJ6TNgsw9W+0wRfPg20XuIva0GkNjIZOuPIZuh - 6Bk7+8zqXKzCGABDnrK4h9Ss - -----END PRIVATE KEY----- - """ + -----BEGIN PRIVATE KEY----- + MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCaCfo4dC9OBEl1 + 497MgHOq6OL2Amz7Jcn3xuNZjyPcfzneSVA+2SmNdxx37xrqNf+ej17MpqQSlaiS + 0X9WkD+tFO7VvkiSfPbNcIbMXauoVQioOX5i2yga27Dx9ywyfF7OIjz1ighgvw1T + dLqzDqd3uT19BSX00TpEZA+3rMKoOaEPuHLSDep7IWZhwa8cm3b3aHSlyjSlt6Gh + rJv4u852rDeeT4iIfCsbstUK+Ag4IdlN2Fmuv4jlZ+uamcZJjBZ6C3+ql9ykdhuJ + tTy+qvFstbQm7pYHjJLGt7t5EHkqZBmWXPJOft/8wJf0t7Joj9kbx7wxKjM37Jvm + Q2lZu5KJAgMBAAECggEAQksa+iLenPfxWaBJKb/6h8qUqwWeO3Qm+NEK1WdqKqJC + mGz68SFq5awmf2NTNQsqSOYxCWiKYkkwdIdfAzUvgmDo7Opot0q6uO29xcRmdRqr + kCK2RvtExlJYU7pptgyajKJlk9LlCiYPKSSqmRcscbUyRlTp4fQN3JMnxIfAer8v + dKGYVvZ3FXWcFS7c/ogDlAkatFmy1J7fJpAitH1BALEC2j3uB9+AZ1ILTcGWrj4i + VaLuPto8ySlGsGoq2a5uMMZ9l8+AEIvDSdvPOpe6uzFqLDNDsb02YFm44Q/zrfr1 + Tsg3PIRH0dKi2kMVkUabZg0Ius8j0LtS/4DhgDqFBQKBgQDQlVuBRqnt6H5VKkY7 + jqVvackUBeKeY9tAzErHoLc4UKr32A/gPA341YTQTG8Jx2AmII9HfWv8Bk3E9Txm + YFZRyFNzsup9MVWAJzjmmBelbA+sAnFbpvuHKwEw6Mm3B68g113PNFKZFKtv8Ioi + 5oxLdAj02CA5b7H3rrgcwcGWxwKBgQC9Dl8c8VtPJDv/7+f8RNLERPWyR+oQKywr + xyp4VKRRVOcMt6M7cB0yL1uMl7dhI4lI6ZUGA5z7Tz/pAHdGvAHJ9zqbhGWgAC0f + 9UcKAUyIx2Ja1QMfxjR7rbhBLswezBXChFTBMo57Cl6BdMV2/5Dy0u+X9hknpXlo + QoJwyhP8LwKBgQDOSekmAe0uDjJjqFutq3aSqdzkoK1wWPIPM/0BUkHiwGVWmamZ + 68slvoaMPAvVcAn3q1wJKFIT/2gK0z/ZQI4edDGUy+59wrz88c2kwechA668P+48 + 5vj8xdt3s8NL8Z2SrW1p8CWAoKCtJQh5W+qE9U2mWdoE9CLfAz2zsyzzIQKBgD27 + q6MvzLkTA+SW2hGuB4S/X9tPUEbnUg0Zg+y29tD4AFpOvKZz/ZSdki9eeyrlB7cf + TuIf2+rT/fJ/jHM0gQEKEcEmgmi0pgeBeCj0M6GWOa+fTt3ZQtn/5+Kg/VYxHgne + XC6Z65yRzjpHfxNUcGhaKJJecehYSESbMyzRT6VRAoGBAIcrVYjy0losUQx1jGR7 + XrUn2I12rLjiNmaxub78SJxWRd0TXywSh/CPdX42xs9ku2Rur0Ar+NweEb7YQjZX + c9YrKwe4kFm7wawf9Sy+9ZvkSqNJ6TNgsw9W+0wRfPg20XuIva0GkNjIZOuPIZuh + 6Bk7+8zqXKzCGABDnrK4h9Ss + -----END PRIVATE KEY----- + """ // openssl rsa -pubout -in private_key.pem -out public_key.pem let publicKey = """ - -----BEGIN PUBLIC KEY----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmgn6OHQvTgRJdePezIBz - quji9gJs+yXJ98bjWY8j3H853klQPtkpjXccd+8a6jX/no9ezKakEpWoktF/VpA/ - rRTu1b5Iknz2zXCGzF2rqFUIqDl+YtsoGtuw8fcsMnxeziI89YoIYL8NU3S6sw6n - d7k9fQUl9NE6RGQPt6zCqDmhD7hy0g3qeyFmYcGvHJt292h0pco0pbehoayb+LvO - dqw3nk+IiHwrG7LVCvgIOCHZTdhZrr+I5WfrmpnGSYwWegt/qpfcpHYbibU8vqrx - bLW0Ju6WB4ySxre7eRB5KmQZllzyTn7f/MCX9LeyaI/ZG8e8MSozN+yb5kNpWbuS - iQIDAQAB - -----END PUBLIC KEY----- - """ + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmgn6OHQvTgRJdePezIBz + quji9gJs+yXJ98bjWY8j3H853klQPtkpjXccd+8a6jX/no9ezKakEpWoktF/VpA/ + rRTu1b5Iknz2zXCGzF2rqFUIqDl+YtsoGtuw8fcsMnxeziI89YoIYL8NU3S6sw6n + d7k9fQUl9NE6RGQPt6zCqDmhD7hy0g3qeyFmYcGvHJt292h0pco0pbehoayb+LvO + dqw3nk+IiHwrG7LVCvgIOCHZTdhZrr+I5WfrmpnGSYwWegt/qpfcpHYbibU8vqrx + bLW0Ju6WB4ySxre7eRB5KmQZllzyTn7f/MCX9LeyaI/ZG8e8MSozN+yb5kNpWbuS + iQIDAQAB + -----END PUBLIC KEY----- + """ let modulus = """ - gWu7yhI35FScdKARYboJoAm-T7yJfJ9JTvAok_RKOJYcL8oLIRSeLqQX83PPZiWdKTdXaiGWntpDu6vW7VAb-HWPF6tNYSLKDSmR3sEu2488ibWijZtNTCKOSb_1iAKAI5BJ80LTqyQtqaKzT0XUBtMsde8vX1nKI05UxujfTX3kqUtkZgLv1Yk1ZDpUoLOWUTtCm68zpjtBrPiN8bU2jqCGFyMyyXys31xFRzz4MyJ5tREHkQCzx0g7AvW0ge_sBTPQ2U6NSkcZvQyDbfDv27cMUHij1Sjx16SY9a2naTuOgamjtUzyClPLVpchX-McNyS0tjdxWY_yRL9MYuw4AQ - """ + gWu7yhI35FScdKARYboJoAm-T7yJfJ9JTvAok_RKOJYcL8oLIRSeLqQX83PPZiWdKTdXaiGWntpDu6vW7VAb-HWPF6tNYSLKDSmR3sEu2488ibWijZtNTCKOSb_1iAKAI5BJ80LTqyQtqaKzT0XUBtMsde8vX1nKI05UxujfTX3kqUtkZgLv1Yk1ZDpUoLOWUTtCm68zpjtBrPiN8bU2jqCGFyMyyXys31xFRzz4MyJ5tREHkQCzx0g7AvW0ge_sBTPQ2U6NSkcZvQyDbfDv27cMUHij1Sjx16SY9a2naTuOgamjtUzyClPLVpchX-McNyS0tjdxWY_yRL9MYuw4AQ + """ let publicExponent = "AQAB" let privateExponent = """ - L4z0tz7QWE0aGuOA32YqCSnrSYKdBTPFDILCdfHonzfP7WMPibz4jWxu_FzNk9s4Dh-uN2lV3NGW10pAsnqffD89LtYanRjaIdHnLW_PFo5fEL2yltK7qMB9hO1JegppKCfoc79W4-dr-4qy1Op0B3npOP-DaUYlNamfDmIbQW32UKeJzdGIn-_ryrBT7hQW6_uHLS2VFPPk0rNkPPKZYoNaqGnJ0eaFFF-dFwiThXIpPz--dxTAL8xYf275rjG8C9lh6awOfJSIdXMVuQITWf62E0mSQPR2-219bShMKriDYcYLbT3BJEgOkRBBHGuHo9R5TN298anxZqV1u5jtUQ - """ + L4z0tz7QWE0aGuOA32YqCSnrSYKdBTPFDILCdfHonzfP7WMPibz4jWxu_FzNk9s4Dh-uN2lV3NGW10pAsnqffD89LtYanRjaIdHnLW_PFo5fEL2yltK7qMB9hO1JegppKCfoc79W4-dr-4qy1Op0B3npOP-DaUYlNamfDmIbQW32UKeJzdGIn-_ryrBT7hQW6_uHLS2VFPPk0rNkPPKZYoNaqGnJ0eaFFF-dFwiThXIpPz--dxTAL8xYf275rjG8C9lh6awOfJSIdXMVuQITWf62E0mSQPR2-219bShMKriDYcYLbT3BJEgOkRBBHGuHo9R5TN298anxZqV1u5jtUQ + """ } diff --git a/Tests/JWTKitTests/RSATests.swift b/Tests/JWTKitTests/RSATests.swift index ec9fd9ba..c4d0e41f 100644 --- a/Tests/JWTKitTests/RSATests.swift +++ b/Tests/JWTKitTests/RSATests.swift @@ -1,21 +1,27 @@ -import _CryptoExtras import BigInt import JWTKit import XCTest +import _CryptoExtras -final class RSATests: XCTestCase { +final class RSATests: XCTestCase, @unchecked Sendable { func testCalculatePrimeFactors() throws { try wycheproof(fileName: "rsa_oaep_2048_sha1_mgf1sha1_test", testFunction: testPrimeFactors) - try wycheproof(fileName: "rsa_oaep_2048_sha224_mgf1sha1_test", testFunction: testPrimeFactors) - try wycheproof(fileName: "rsa_oaep_2048_sha256_mgf1sha256_test", testFunction: testPrimeFactors) + try wycheproof( + fileName: "rsa_oaep_2048_sha224_mgf1sha1_test", testFunction: testPrimeFactors) + try wycheproof( + fileName: "rsa_oaep_2048_sha256_mgf1sha256_test", testFunction: testPrimeFactors) } func testRSADocs() async throws { - await XCTAssertNoThrowAsync(try await JWTKeyCollection().add(rsa: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256)) + await XCTAssertNoThrowAsync( + try await JWTKeyCollection().add( + rsa: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256)) } func testPrivateKeyInitialization() throws { - XCTAssertNoThrow(try Insecure.RSA.PrivateKey(modulus: modulus, exponent: publicExponent, privateExponent: privateExponent)) + XCTAssertNoThrow( + try Insecure.RSA.PrivateKey( + modulus: modulus, exponent: publicExponent, privateExponent: privateExponent)) } func testPublicKeyInitialization() throws { @@ -38,8 +44,13 @@ final class RSATests: XCTestCase { func testSigning() async throws { let keyCollection = try await JWTKeyCollection() - .add(rsa: Insecure.RSA.PrivateKey(pem: privateKey), digestAlgorithm: .sha256, kid: "private") - .add(rsa: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public") + .add( + rsa: Insecure.RSA.PrivateKey(pem: privateKey), digestAlgorithm: .sha256, + kid: "private" + ) + .add( + rsa: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public" + ) let payload = TestPayload( sub: "vapor", @@ -49,12 +60,15 @@ final class RSATests: XCTestCase { ) let privateSigned = try await keyCollection.sign(payload, kid: "private") - try await XCTAssertEqualAsync(await keyCollection.verify(privateSigned, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(privateSigned, as: TestPayload.self), payload) } func testSigningWithPublic() async throws { let keyCollection = try await JWTKeyCollection() - .add(rsa: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public") + .add( + rsa: Insecure.RSA.PublicKey(pem: publicKey), digestAlgorithm: .sha256, kid: "public" + ) let payload = TestPayload( sub: "vapor", @@ -71,11 +85,17 @@ final class RSATests: XCTestCase { } func testSigningWithRawBuiltPrivateKey() async throws { - let privateKey = try Insecure.RSA.PrivateKey(modulus: modulus, exponent: publicExponent, privateExponent: privateExponent) + let privateKey = try Insecure.RSA.PrivateKey( + modulus: modulus, exponent: publicExponent, privateExponent: privateExponent) let keyCollection = try await JWTKeyCollection() - .add(rsa: Insecure.RSA.PrivateKey(pem: privateKey.pemRepresentation), digestAlgorithm: .sha256, kid: "private") - .add(rsa: Insecure.RSA.PublicKey(pem: privateKey.publicKey.pemRepresentation), digestAlgorithm: .sha256, kid: "public") + .add( + rsa: Insecure.RSA.PrivateKey(pem: privateKey.pemRepresentation), + digestAlgorithm: .sha256, kid: "private" + ) + .add( + rsa: Insecure.RSA.PublicKey(pem: privateKey.publicKey.pemRepresentation), + digestAlgorithm: .sha256, kid: "public") let payload = TestPayload( sub: "vapor", @@ -85,8 +105,10 @@ final class RSATests: XCTestCase { ) let privateSigned = try await keyCollection.sign(payload) - try await XCTAssertEqualAsync(await keyCollection.verify(privateSigned, as: TestPayload.self), payload) - try await XCTAssertEqualAsync(await keyCollection.verify(privateSigned, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(privateSigned, as: TestPayload.self), payload) + try await XCTAssertEqualAsync( + await keyCollection.verify(privateSigned, as: TestPayload.self), payload) } func testGetPublicKeyPrimitives() async throws { @@ -97,7 +119,8 @@ final class RSATests: XCTestCase { } func testGetPrivateKeyPrimitives() async throws { - let privateKey = try Insecure.RSA.PrivateKey(modulus: modulus, exponent: publicExponent, privateExponent: privateExponent) + let privateKey = try Insecure.RSA.PrivateKey( + modulus: modulus, exponent: publicExponent, privateExponent: privateExponent) let (keyModulus, exponent, keyPrivateExponent) = try privateKey.getKeyPrimitives() XCTAssertEqual(keyModulus, modulus) XCTAssertEqual(exponent, publicExponent) @@ -105,23 +128,24 @@ final class RSATests: XCTestCase { } func testGetPrivateKeyPrimitivesFromNonRawBuiltKey() async throws { - let privateKey = try Insecure.RSA.PrivateKey(pem: """ - -----BEGIN RSA PRIVATE KEY----- - MIIEowIBAAKCAQEAgWu7yhI35FScdKARYboJoAm+T7yJfJ9JTvAok/RKOJYcL8oL\nIRSeLqQX83PPZiWdKTdXaiGWntpDu6vW7VAb+HWPF6tNYSLKDSmR3sEu2488ibWi - jZtNTCKOSb/1iAKAI5BJ80LTqyQtqaKzT0XUBtMsde8vX1nKI05UxujfTX3kqUtk\nZgLv1Yk1ZDpUoLOWUTtCm68zpjtBrPiN8bU2jqCGFyMyyXys31xFRzz4MyJ5tREH - kQCzx0g7AvW0ge/sBTPQ2U6NSkcZvQyDbfDv27cMUHij1Sjx16SY9a2naTuOgamj\ntUzyClPLVpchX+McNyS0tjdxWY/yRL9MYuw4AQIDAQABAoIBAC+M9Lc+0FhNGhrj - gN9mKgkp60mCnQUzxQyCwnXx6J83z+1jD4m8+I1sbvxczZPbOA4frjdpVdzRltdK\nQLJ6n3w/PS7WGp0Y2iHR5y1vzxaOXxC9spbSu6jAfYTtSXoKaSgn6HO/VuPna/uK - stTqdAd56Tj/g2lGJTWpnw5iG0Ft9lCnic3RiJ/v68qwU+4UFuv7hy0tlRTz5NKz\nZDzymWKDWqhpydHmhRRfnRcIk4VyKT8/vncUwC/MWH9u+a4xvAvZYemsDnyUiHVz - FbkCE1n+thNJkkD0dvttfW0oTCq4g2HGC209wSRIDpEQQRxrh6PUeUzdvfGp8Wal\ndbuY7VECgYEA+kVqK0URfwbZGEnO8JnagCunkOKgqAqv+I44/lmZwmj/Z9uvFXRo - 5TQNwpSNuYB9V5ujpoVgJaJ4BWUCnD/uwqNwlqcQydsXzB3u4GKI5jZrpCN8i7+s\nhP9UuV1pfU8+n3VuWkIhfrHEmSgn7+AhCkzETho2qPvfv7u8bxou4DUCgYEAhGIj - QyEZWORJI2FJ+APp146v/nndXwCGIbPCbp8rHFFL4dYQsgJI6tGQDMO9xcMoz0jt\n/lJTUu4hBIL7jm1S/bYez6JqlbjUhNpvSUp/M0SWlS36LLQqrc49IZ8H7AXjDiG5 - az6zVHMtz8CJY0/YT5CUjDszhN8u56vdAEBHyh0CgYEAwwhVNGMev18Wz1a1bcp3\n/GoIq1/w0wOBHrG2uIAa0uYAI2+Pgai2Fef60SfzShxXkW44mgxWYP27initEBbC - eevkUYLgEm4qnWa2QSaIiN7gA4mkBUPZrctMuyeQjZaztpBM7wmaEKF4E+K3PLft\nB5nLYRIMhqPCOiiTMAG3hgECgYAyI00BnqaP8R32JWGzaiAFgMgNFDCQS42BdCh+ - ZxAX0H5x0PZPxOfC742kF/pmzQxGvXNNr/ZY4VFl+Qm3Hpag+nne37+IZxEuI+Ck\nHG/iheaWJ2ypw66qVwL2GdoRPQWKk6E7Ces3X8wI8/3UvCfLspFgLwfLGhAUtBWm - g7HszQKBgEGa1OX9PQFrOojSizXK2jcalVJLiy01+cJZB1ZqIwFAYG9VTEOo3IrH\nhUGJzX0PZGGW8+r+S50ORYlJ7hl0xGZrcnAv4ftONtYN4GmB7t/QKheShWTX0Q+C - eGwWRyV8jo3G+nJDtGEb3MTHVXPK3hviJRXDHHGhw+sh+JdL49x4 - -----END RSA PRIVATE KEY----- - """) + let privateKey = try Insecure.RSA.PrivateKey( + pem: """ + -----BEGIN RSA PRIVATE KEY----- + MIIEowIBAAKCAQEAgWu7yhI35FScdKARYboJoAm+T7yJfJ9JTvAok/RKOJYcL8oL\nIRSeLqQX83PPZiWdKTdXaiGWntpDu6vW7VAb+HWPF6tNYSLKDSmR3sEu2488ibWi + jZtNTCKOSb/1iAKAI5BJ80LTqyQtqaKzT0XUBtMsde8vX1nKI05UxujfTX3kqUtk\nZgLv1Yk1ZDpUoLOWUTtCm68zpjtBrPiN8bU2jqCGFyMyyXys31xFRzz4MyJ5tREH + kQCzx0g7AvW0ge/sBTPQ2U6NSkcZvQyDbfDv27cMUHij1Sjx16SY9a2naTuOgamj\ntUzyClPLVpchX+McNyS0tjdxWY/yRL9MYuw4AQIDAQABAoIBAC+M9Lc+0FhNGhrj + gN9mKgkp60mCnQUzxQyCwnXx6J83z+1jD4m8+I1sbvxczZPbOA4frjdpVdzRltdK\nQLJ6n3w/PS7WGp0Y2iHR5y1vzxaOXxC9spbSu6jAfYTtSXoKaSgn6HO/VuPna/uK + stTqdAd56Tj/g2lGJTWpnw5iG0Ft9lCnic3RiJ/v68qwU+4UFuv7hy0tlRTz5NKz\nZDzymWKDWqhpydHmhRRfnRcIk4VyKT8/vncUwC/MWH9u+a4xvAvZYemsDnyUiHVz + FbkCE1n+thNJkkD0dvttfW0oTCq4g2HGC209wSRIDpEQQRxrh6PUeUzdvfGp8Wal\ndbuY7VECgYEA+kVqK0URfwbZGEnO8JnagCunkOKgqAqv+I44/lmZwmj/Z9uvFXRo + 5TQNwpSNuYB9V5ujpoVgJaJ4BWUCnD/uwqNwlqcQydsXzB3u4GKI5jZrpCN8i7+s\nhP9UuV1pfU8+n3VuWkIhfrHEmSgn7+AhCkzETho2qPvfv7u8bxou4DUCgYEAhGIj + QyEZWORJI2FJ+APp146v/nndXwCGIbPCbp8rHFFL4dYQsgJI6tGQDMO9xcMoz0jt\n/lJTUu4hBIL7jm1S/bYez6JqlbjUhNpvSUp/M0SWlS36LLQqrc49IZ8H7AXjDiG5 + az6zVHMtz8CJY0/YT5CUjDszhN8u56vdAEBHyh0CgYEAwwhVNGMev18Wz1a1bcp3\n/GoIq1/w0wOBHrG2uIAa0uYAI2+Pgai2Fef60SfzShxXkW44mgxWYP27initEBbC + eevkUYLgEm4qnWa2QSaIiN7gA4mkBUPZrctMuyeQjZaztpBM7wmaEKF4E+K3PLft\nB5nLYRIMhqPCOiiTMAG3hgECgYAyI00BnqaP8R32JWGzaiAFgMgNFDCQS42BdCh+ + ZxAX0H5x0PZPxOfC742kF/pmzQxGvXNNr/ZY4VFl+Qm3Hpag+nne37+IZxEuI+Ck\nHG/iheaWJ2ypw66qVwL2GdoRPQWKk6E7Ces3X8wI8/3UvCfLspFgLwfLGhAUtBWm + g7HszQKBgEGa1OX9PQFrOojSizXK2jcalVJLiy01+cJZB1ZqIwFAYG9VTEOo3IrH\nhUGJzX0PZGGW8+r+S50ORYlJ7hl0xGZrcnAv4ftONtYN4GmB7t/QKheShWTX0Q+C + eGwWRyV8jo3G+nJDtGEb3MTHVXPK3hviJRXDHHGhw+sh+JdL49x4 + -----END RSA PRIVATE KEY----- + """) let (keyModulus, exponent, keyPrivateExponent) = try privateKey.getKeyPrimitives() XCTAssertEqual(keyModulus, modulus) XCTAssertEqual(exponent, publicExponent) @@ -136,8 +160,13 @@ final class RSATests: XCTestCase { exp: .init(value: .distantFuture) ) let keyCollection = try await JWTKeyCollection() - .add(rsa: Insecure.RSA.PrivateKey(pem: certPrivateKey), digestAlgorithm: .sha256, kid: "private") - .add(rsa: Insecure.RSA.PublicKey(certificatePEM: cert), digestAlgorithm: .sha256, kid: "cert") + .add( + rsa: Insecure.RSA.PrivateKey(pem: certPrivateKey), digestAlgorithm: .sha256, + kid: "private" + ) + .add( + rsa: Insecure.RSA.PublicKey(certificatePEM: cert), digestAlgorithm: .sha256, + kid: "cert") let jwt = try await keyCollection.sign(test, kid: "private") let payload = try await keyCollection.verify(jwt, as: TestPayload.self) @@ -145,13 +174,15 @@ final class RSATests: XCTestCase { } func testKeySizeTooSmall() async throws { - await XCTAssertThrowsErrorAsync(try await JWTKeyCollection().add(rsa: Insecure.RSA.PrivateKey(pem: _512BytesKey), digestAlgorithm: .sha256)) + await XCTAssertThrowsErrorAsync( + try await JWTKeyCollection().add( + rsa: Insecure.RSA.PrivateKey(pem: _512BytesKey), digestAlgorithm: .sha256)) } func testRS256Verification() async throws { let token = """ - eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2YXBvciIsIm5hbWUiOiJmb28iLCJhZG1pbiI6dHJ1ZSwiZXhwIjoyMDAwMDAwMDAwfQ.JZ3uuzojbqbkZBoCKOrjzu4ICNjFt_H4XNqO4I8sM8PRmxzg-_kY2_MhVJkKga30afWp00z5FNoT14CsdKXWKEaWCwXgYTatLQI3yt77aqj7-RC_eBCl6qRDnPH7Aq5KkBNGsoMwUAWKeHB7ZHZulqqqaeRUyEIXmUJiyUy7TjZyVhk1WsXANGxDWvutsVG6dmiFhaSWqj1RsmyWqbuDoyd3uIHzyHy4mx1Y-nwxMofoS0k-SkyZcEPVJ2Am99VZ4rwSJbH2QcmaZr5o1rS8sJiReVYfyEF2YghN9tLj3FF11scgtpjDMzcIkbsIntclaYmU1b7GlIFB6897sdjJpA - """ + eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ2YXBvciIsIm5hbWUiOiJmb28iLCJhZG1pbiI6dHJ1ZSwiZXhwIjoyMDAwMDAwMDAwfQ.JZ3uuzojbqbkZBoCKOrjzu4ICNjFt_H4XNqO4I8sM8PRmxzg-_kY2_MhVJkKga30afWp00z5FNoT14CsdKXWKEaWCwXgYTatLQI3yt77aqj7-RC_eBCl6qRDnPH7Aq5KkBNGsoMwUAWKeHB7ZHZulqqqaeRUyEIXmUJiyUy7TjZyVhk1WsXANGxDWvutsVG6dmiFhaSWqj1RsmyWqbuDoyd3uIHzyHy4mx1Y-nwxMofoS0k-SkyZcEPVJ2Am99VZ4rwSJbH2QcmaZr5o1rS8sJiReVYfyEF2YghN9tLj3FF11scgtpjDMzcIkbsIntclaYmU1b7GlIFB6897sdjJpA + """ let testPayload = TestPayload( sub: "vapor", name: "foo", @@ -159,8 +190,13 @@ final class RSATests: XCTestCase { exp: .init(value: .init(timeIntervalSince1970: 2_000_000_000)) ) let keyCollection = try await JWTKeyCollection() - .add(rsa: Insecure.RSA.PrivateKey(pem: privateKey2), digestAlgorithm: .sha256, kid: "private") - .add(rsa: Insecure.RSA.PublicKey(pem: publicKey2), digestAlgorithm: .sha256, kid: "public") + .add( + rsa: Insecure.RSA.PrivateKey(pem: privateKey2), digestAlgorithm: .sha256, + kid: "private" + ) + .add( + rsa: Insecure.RSA.PublicKey(pem: publicKey2), digestAlgorithm: .sha256, + kid: "public") let payload = try await keyCollection.verify(token, as: TestPayload.self) XCTAssertEqual(payload, testPayload) @@ -180,173 +216,175 @@ final class RSATests: XCTestCase { func testExportPublicKeyWhenKeyIsPrivate() async throws { let privateKey = try Insecure.RSA.PrivateKey(pem: privateKey) - let publicKeyFromPrivate = try Insecure.RSA.PublicKey(pem: privateKey.publicKey.pemRepresentation) + let publicKeyFromPrivate = try Insecure.RSA.PublicKey( + pem: privateKey.publicKey.pemRepresentation) let publicKey = try Insecure.RSA.PublicKey(pem: publicKey) XCTAssertEqual(publicKeyFromPrivate, publicKey) } func testExportKeyAsPEMWhenRawBuilt() async throws { - let key = try Insecure.RSA.PrivateKey(modulus: modulus, exponent: publicExponent, privateExponent: privateExponent) + let key = try Insecure.RSA.PrivateKey( + modulus: modulus, exponent: publicExponent, privateExponent: privateExponent) let key2 = try Insecure.RSA.PrivateKey(pem: key.pemRepresentation) XCTAssertEqual(key, key2) } let modulus = """ - gWu7yhI35FScdKARYboJoAm-T7yJfJ9JTvAok_RKOJYcL8oLIRSeLqQX83PPZiWdKTdXaiGWntpDu6vW7VAb-HWPF6tNYSLKDSmR3sEu2488ibWijZtNTCKOSb_1iAKAI5BJ80LTqyQtqaKzT0XUBtMsde8vX1nKI05UxujfTX3kqUtkZgLv1Yk1ZDpUoLOWUTtCm68zpjtBrPiN8bU2jqCGFyMyyXys31xFRzz4MyJ5tREHkQCzx0g7AvW0ge_sBTPQ2U6NSkcZvQyDbfDv27cMUHij1Sjx16SY9a2naTuOgamjtUzyClPLVpchX-McNyS0tjdxWY_yRL9MYuw4AQ - """ + gWu7yhI35FScdKARYboJoAm-T7yJfJ9JTvAok_RKOJYcL8oLIRSeLqQX83PPZiWdKTdXaiGWntpDu6vW7VAb-HWPF6tNYSLKDSmR3sEu2488ibWijZtNTCKOSb_1iAKAI5BJ80LTqyQtqaKzT0XUBtMsde8vX1nKI05UxujfTX3kqUtkZgLv1Yk1ZDpUoLOWUTtCm68zpjtBrPiN8bU2jqCGFyMyyXys31xFRzz4MyJ5tREHkQCzx0g7AvW0ge_sBTPQ2U6NSkcZvQyDbfDv27cMUHij1Sjx16SY9a2naTuOgamjtUzyClPLVpchX-McNyS0tjdxWY_yRL9MYuw4AQ + """ let publicExponent = "AQAB" let privateExponent = """ - L4z0tz7QWE0aGuOA32YqCSnrSYKdBTPFDILCdfHonzfP7WMPibz4jWxu_FzNk9s4Dh-uN2lV3NGW10pAsnqffD89LtYanRjaIdHnLW_PFo5fEL2yltK7qMB9hO1JegppKCfoc79W4-dr-4qy1Op0B3npOP-DaUYlNamfDmIbQW32UKeJzdGIn-_ryrBT7hQW6_uHLS2VFPPk0rNkPPKZYoNaqGnJ0eaFFF-dFwiThXIpPz--dxTAL8xYf275rjG8C9lh6awOfJSIdXMVuQITWf62E0mSQPR2-219bShMKriDYcYLbT3BJEgOkRBBHGuHo9R5TN298anxZqV1u5jtUQ - """ + L4z0tz7QWE0aGuOA32YqCSnrSYKdBTPFDILCdfHonzfP7WMPibz4jWxu_FzNk9s4Dh-uN2lV3NGW10pAsnqffD89LtYanRjaIdHnLW_PFo5fEL2yltK7qMB9hO1JegppKCfoc79W4-dr-4qy1Op0B3npOP-DaUYlNamfDmIbQW32UKeJzdGIn-_ryrBT7hQW6_uHLS2VFPPk0rNkPPKZYoNaqGnJ0eaFFF-dFwiThXIpPz--dxTAL8xYf275rjG8C9lh6awOfJSIdXMVuQITWf62E0mSQPR2-219bShMKriDYcYLbT3BJEgOkRBBHGuHo9R5TN298anxZqV1u5jtUQ + """ let publicKey = """ - -----BEGIN PUBLIC KEY----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy/FtQ/cOcx6ZgyaqU54C - ESfkpttXuNnEZ07nYXXo8ylIiUFpB0r0Fecgv/tIhF1LFCWBHUsqyoSRQz0/iBRn - YyIsG+yF/q1K3ll5Q/2GAS9/28jBuJGKDuKIj6dgPlr33si6bjeePTl4ZO6OZFxG - Yyn4x035pwGwjKGFuQRKYh0AtxwHiWeRIsAJ/B2Z+VGOpcSXH+x/YUfN8Q9FuyGU - zcsVLuGizbooRSMSSoD/y/8veWOnXWbMsh0KKTON/+yTmAcLn2tOzFmsYgHQXatW - 0f2XjrdmmWl4VfiekFKFDvGenxum9nEJrzIJOMm6qHnIiyCNA3xbMqmr7oqeIUa+ - fQIDAQAB - -----END PUBLIC KEY----- - """ + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy/FtQ/cOcx6ZgyaqU54C + ESfkpttXuNnEZ07nYXXo8ylIiUFpB0r0Fecgv/tIhF1LFCWBHUsqyoSRQz0/iBRn + YyIsG+yF/q1K3ll5Q/2GAS9/28jBuJGKDuKIj6dgPlr33si6bjeePTl4ZO6OZFxG + Yyn4x035pwGwjKGFuQRKYh0AtxwHiWeRIsAJ/B2Z+VGOpcSXH+x/YUfN8Q9FuyGU + zcsVLuGizbooRSMSSoD/y/8veWOnXWbMsh0KKTON/+yTmAcLn2tOzFmsYgHQXatW + 0f2XjrdmmWl4VfiekFKFDvGenxum9nEJrzIJOMm6qHnIiyCNA3xbMqmr7oqeIUa+ + fQIDAQAB + -----END PUBLIC KEY----- + """ let privateKey = """ - -----BEGIN PRIVATE KEY----- - MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDL8W1D9w5zHpmD - JqpTngIRJ+Sm21e42cRnTudhdejzKUiJQWkHSvQV5yC/+0iEXUsUJYEdSyrKhJFD - PT+IFGdjIiwb7IX+rUreWXlD/YYBL3/byMG4kYoO4oiPp2A+WvfeyLpuN549OXhk - 7o5kXEZjKfjHTfmnAbCMoYW5BEpiHQC3HAeJZ5EiwAn8HZn5UY6lxJcf7H9hR83x - D0W7IZTNyxUu4aLNuihFIxJKgP/L/y95Y6ddZsyyHQopM43/7JOYBwufa07MWaxi - AdBdq1bR/ZeOt2aZaXhV+J6QUoUO8Z6fG6b2cQmvMgk4ybqoeciLII0DfFsyqavu - ip4hRr59AgMBAAECggEAUIw994XwMw922hG/W98gOd5jtHMVJnD73UGQqTGEm+VG - PM+Ux8iWtr/ec3Svo3elW4OkhwlVET9ikAf0u64zVzf769ty4K9YzpDQEEZlUrqL - 6SZVPKxetppKDVKx9G7BT0BAQZ+947h7EIIXwxOeyTOeijkFzSwhqqlwwy4qoqzV - FTQS20QHE62hxzwuS5HBqw8ds183qAg9NbzR0Cp4za9qTiBB6C8KEcLqeatO+q+d - VCDsJcAMZOvW14N6BozKgbQ/WXZQ/3kNUPBndZLzzqaILFNmB1Zf2DVVJ9gU7+EK - xOac60StIfG81NllCTBrmRVq8yitNqwmutHMlxrIkQKBgQDvp39MkEHtNunFGkI5 - R8IB5BZjtx5OdRBKkmPasmNU8U0XoQAJUKY/9piIpCtRi87tMXv8WWmlbULi66pu - 4BnMIisw78xlIWRZTSizFrkFcEoVgEnbZBtSrOg/J5PAcjLEGCQoAdmMXAekR2/m - htv7FPijHPNUjyIFLaxwjl9izwKBgQDZ2mQeKNRHjIb5ZBzB0ZCvUy2y4+kaLrhZ - +CWMN1flL4dd1KuZKvCEfHY9kWOjqw6XneN4yT0aPmbBft4fihiiNW0Sm8i+fSpy - g0klw2HJl49wnwctBpRgTdMKGo9n14OGeu0xKOAy7I4j1tKrUXiRWnP9R583Ti7c - w7YHgdHM8wKBgEV147SaPzF08A6bzMPzY2zO4hpmsdcFoQIsKdryR04QXkrR9EO+ - 52C0pYM9Kf0Jq6Ed7ZS3iaJT58YDjjNyqqd648/cQP6yzfYAIiK+HERSRnay5zU6 - b5zn1qyvWOi3cLVbVedumdJPvjtEJU/ImKvOaT5FntVMYwzjLw60hTsLAoGAZJnt - UeAY51GFovUQMpDL96q5l7qXknewuhtVe4KzHCrun+3tsDWcDBJNp/DTymjbvDg1 - KzoC9XOLkB8+A+KJrZ5uWAGImi7Cw07NIJsxNR7AJonJjolTS4Wkxy2su49SNW/e - yKzPm7SRjwtNDb/5pWXX2kaQx8Fa8qeOD7lrYPECgYAwQ6o0vYmr+L1tOZZgMVv9 - Jusa8beVUH5hyduJjmxbYOtFTkggAozdx7rs4BgyRsmDlV48cEmcVf/7IH4gMJLb - O+bbERwCYUChe+piANhnwfwDHzbRd8mmQus54P06X7bWu6Rmi7gbQGVN/Z6VhbIm - D2cOo0w4bk/3yb01xz1MEw== - -----END PRIVATE KEY----- - """ + -----BEGIN PRIVATE KEY----- + MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDL8W1D9w5zHpmD + JqpTngIRJ+Sm21e42cRnTudhdejzKUiJQWkHSvQV5yC/+0iEXUsUJYEdSyrKhJFD + PT+IFGdjIiwb7IX+rUreWXlD/YYBL3/byMG4kYoO4oiPp2A+WvfeyLpuN549OXhk + 7o5kXEZjKfjHTfmnAbCMoYW5BEpiHQC3HAeJZ5EiwAn8HZn5UY6lxJcf7H9hR83x + D0W7IZTNyxUu4aLNuihFIxJKgP/L/y95Y6ddZsyyHQopM43/7JOYBwufa07MWaxi + AdBdq1bR/ZeOt2aZaXhV+J6QUoUO8Z6fG6b2cQmvMgk4ybqoeciLII0DfFsyqavu + ip4hRr59AgMBAAECggEAUIw994XwMw922hG/W98gOd5jtHMVJnD73UGQqTGEm+VG + PM+Ux8iWtr/ec3Svo3elW4OkhwlVET9ikAf0u64zVzf769ty4K9YzpDQEEZlUrqL + 6SZVPKxetppKDVKx9G7BT0BAQZ+947h7EIIXwxOeyTOeijkFzSwhqqlwwy4qoqzV + FTQS20QHE62hxzwuS5HBqw8ds183qAg9NbzR0Cp4za9qTiBB6C8KEcLqeatO+q+d + VCDsJcAMZOvW14N6BozKgbQ/WXZQ/3kNUPBndZLzzqaILFNmB1Zf2DVVJ9gU7+EK + xOac60StIfG81NllCTBrmRVq8yitNqwmutHMlxrIkQKBgQDvp39MkEHtNunFGkI5 + R8IB5BZjtx5OdRBKkmPasmNU8U0XoQAJUKY/9piIpCtRi87tMXv8WWmlbULi66pu + 4BnMIisw78xlIWRZTSizFrkFcEoVgEnbZBtSrOg/J5PAcjLEGCQoAdmMXAekR2/m + htv7FPijHPNUjyIFLaxwjl9izwKBgQDZ2mQeKNRHjIb5ZBzB0ZCvUy2y4+kaLrhZ + +CWMN1flL4dd1KuZKvCEfHY9kWOjqw6XneN4yT0aPmbBft4fihiiNW0Sm8i+fSpy + g0klw2HJl49wnwctBpRgTdMKGo9n14OGeu0xKOAy7I4j1tKrUXiRWnP9R583Ti7c + w7YHgdHM8wKBgEV147SaPzF08A6bzMPzY2zO4hpmsdcFoQIsKdryR04QXkrR9EO+ + 52C0pYM9Kf0Jq6Ed7ZS3iaJT58YDjjNyqqd648/cQP6yzfYAIiK+HERSRnay5zU6 + b5zn1qyvWOi3cLVbVedumdJPvjtEJU/ImKvOaT5FntVMYwzjLw60hTsLAoGAZJnt + UeAY51GFovUQMpDL96q5l7qXknewuhtVe4KzHCrun+3tsDWcDBJNp/DTymjbvDg1 + KzoC9XOLkB8+A+KJrZ5uWAGImi7Cw07NIJsxNR7AJonJjolTS4Wkxy2su49SNW/e + yKzPm7SRjwtNDb/5pWXX2kaQx8Fa8qeOD7lrYPECgYAwQ6o0vYmr+L1tOZZgMVv9 + Jusa8beVUH5hyduJjmxbYOtFTkggAozdx7rs4BgyRsmDlV48cEmcVf/7IH4gMJLb + O+bbERwCYUChe+piANhnwfwDHzbRd8mmQus54P06X7bWu6Rmi7gbQGVN/Z6VhbIm + D2cOo0w4bk/3yb01xz1MEw== + -----END PRIVATE KEY----- + """ let _512BytesKey = """ - -----BEGIN PRIVATE KEY----- - MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAtgeOpWeiRIq0Blbc - qq4P7sKnyDmj1mpQq7OyRKZM0qbwyyMM5Nisf5Y+RSDM7JDwqMeLspGo5znLBzN5 - L14JIQIDAQABAkBlMWRSfX9O3VDhKU65L9S5pcsCW1DCdQ3tthMHaO/SNn4jhmbf - MamrK4TWctjuau+CwUtQz/kS/fjveYBSVklVAiEA2r1fExLdTwo1pRzCqvUhq7MO - 4wu1dPvv8mJZZvGxQGMCIQDVCVsmeiN+s9erwd95wUZKb4zBkT6MQC0r1fGQBnEN - qwIgBBT6nDmC5cG0BJPH0jbm3PRnd7c1OKym6qgJMRGblC8CICh9Zr2haS2jsNIM - PxU9DscG/JGtsV2mtO8n8omVL9eRAiEA1ccs/gJCMAwJ/jeA8tZwOF3GEb/9tGow - RR8+JsDsJY8= - -----END PRIVATE KEY----- - """ + -----BEGIN PRIVATE KEY----- + MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAtgeOpWeiRIq0Blbc + qq4P7sKnyDmj1mpQq7OyRKZM0qbwyyMM5Nisf5Y+RSDM7JDwqMeLspGo5znLBzN5 + L14JIQIDAQABAkBlMWRSfX9O3VDhKU65L9S5pcsCW1DCdQ3tthMHaO/SNn4jhmbf + MamrK4TWctjuau+CwUtQz/kS/fjveYBSVklVAiEA2r1fExLdTwo1pRzCqvUhq7MO + 4wu1dPvv8mJZZvGxQGMCIQDVCVsmeiN+s9erwd95wUZKb4zBkT6MQC0r1fGQBnEN + qwIgBBT6nDmC5cG0BJPH0jbm3PRnd7c1OKym6qgJMRGblC8CICh9Zr2haS2jsNIM + PxU9DscG/JGtsV2mtO8n8omVL9eRAiEA1ccs/gJCMAwJ/jeA8tZwOF3GEb/9tGow + RR8+JsDsJY8= + -----END PRIVATE KEY----- + """ let certPrivateKey = """ - -----BEGIN PRIVATE KEY----- - MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQC9hpMHhjS6UX5G - k5so60/p+J2xDgiOw/1Kgp33t+oTJ1SnYUwp3CSUQ8y5cPNQGdQ4yequ1UwPW9uR - A4zNEzeEFIMGWOiC0S7p74ryuYTWg4HMqxed4q0Zt3KVFq/MbsZYwvb88ksbtZ9+ - unccIEeg/loxfsX7aJkFgl58Nz8KtLcO3LreP0ySbxBwWrN3TTtAWej2ofo39viR - uxUJxWmpEHySc+ioSJLHxCG2iuxR+BMGieHtGQ7Ri0K0f1RlLwRlhQfZrhGIarqr - Mw2plcyVFUfAptOD3jmkzHmy7VrNfeyOEpj2roiCLq2NqC+Gf7N+fLPMj3w28kCj - x3ZEk2ZRAgMBAAECggEBAIgNQSLXnqZZtfJoJ6waMAXfqSPe1RnXa86/MTMQ3YHe - bBCz8f7iv4eHnEFK6f+IayZRHJ1hFPa5lEbna34T23h/WQeHb3HpRGo+wVo4/zkW - smkAMTXv8R9S53hLDuwMYWp6mt8999juao6IwNR5/7F8pbZ+MRWnIqIn0jgNWL4P - evDtOaxDGW7FYVU8ew44xjBl6tpCKOvDYxKJ/Ze4FW0XQPj8P+rbA8DeCSmEZb63 - Y7ZDwgw6y6rf5aDqNAo3g1Gur7kpPhOrbGzr2b9RXiSSPIJyRMXF4I6qJ4q/MUXR - 0oL4TaiCwvx/m6g5nyG3hx7VbXpl1BMBhfYqqLqcAzkCgYEA8dZslkCqS6a84miR - 5HbUpl6xYwrra/sy5LwI82QrU/rYlOxU6JXCimOI+VMlV+hrgmPb3nYA9YR3byvr - yTj+M45g3UJvzsYcBEdsQsgP8gxeohDEsGKkeQ/GHrGiV/bxh8fDpMRxDDD15yaP - MCVEy4CUIwjnG0b7N4pc3WaNEmsCgYEAyJ/myS3+qwAY3vOQm30rQYAt0l9kvfAV - N2EAvQZJtS+PCJcg6/GY90BiPQOQVQCIfg9kf1XPN1cBTP5ty9vSigeBUSDSBd/v - AdQesMnCYHMCwb1XYjfWPJRmj0296dAI0RlwXz9fpmUUd/WPQWCxeLjwq+3a0ZJO - 8WBIa3Tm8TMCgYEAhI8nWDi739nWgTgWeCeWqlcPXp22q6q2m+Bh+5+1jEPcgc0F - QbQNPbQPebLUrlnszD0WYNtH7Uwd92cYyGSgGfx6Je3rwWigJMxNkFF/RAr5uFX+ - qjx3sRAvZdWyigsHG4kpOWCgIrGXqItfQ2G6Ut341TdlDnOa8je6bXVv8F8CgYEA - wPJv7anrlB/qy3lp6PCPilYxO3L9G2LrtK/5GtIST0vm/vcB9YkMeTaVhGKKDAYQ - P1SkbYZkXK+zk43aoMXQDWm8Z/7tnjLI1XRg89uGsmXKD/P+N3rF8ssye73j2Rt3 - b0pM9X2oiwoJjnk/BjxtUlJjPKbr3MQeYiwcWiQ6+1sCgYEAzqcM2JFGpY3P5TnV - LD3Jb4zewqGbgJ5T2qLD/cJYDzskQos+Y2vojbh1wswsBwFh/RyIzuf3+yfvUT4+ - EA5DyKDppexIsWXavQ7718i/OSFPCOcWP/vfu5Mr5S/CQcbFbHxiBSa2ZmDD/VXr - 5T9BDCRf/HiTZpuyRcLStfOquYk= - -----END PRIVATE KEY----- - """ + -----BEGIN PRIVATE KEY----- + MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQC9hpMHhjS6UX5G + k5so60/p+J2xDgiOw/1Kgp33t+oTJ1SnYUwp3CSUQ8y5cPNQGdQ4yequ1UwPW9uR + A4zNEzeEFIMGWOiC0S7p74ryuYTWg4HMqxed4q0Zt3KVFq/MbsZYwvb88ksbtZ9+ + unccIEeg/loxfsX7aJkFgl58Nz8KtLcO3LreP0ySbxBwWrN3TTtAWej2ofo39viR + uxUJxWmpEHySc+ioSJLHxCG2iuxR+BMGieHtGQ7Ri0K0f1RlLwRlhQfZrhGIarqr + Mw2plcyVFUfAptOD3jmkzHmy7VrNfeyOEpj2roiCLq2NqC+Gf7N+fLPMj3w28kCj + x3ZEk2ZRAgMBAAECggEBAIgNQSLXnqZZtfJoJ6waMAXfqSPe1RnXa86/MTMQ3YHe + bBCz8f7iv4eHnEFK6f+IayZRHJ1hFPa5lEbna34T23h/WQeHb3HpRGo+wVo4/zkW + smkAMTXv8R9S53hLDuwMYWp6mt8999juao6IwNR5/7F8pbZ+MRWnIqIn0jgNWL4P + evDtOaxDGW7FYVU8ew44xjBl6tpCKOvDYxKJ/Ze4FW0XQPj8P+rbA8DeCSmEZb63 + Y7ZDwgw6y6rf5aDqNAo3g1Gur7kpPhOrbGzr2b9RXiSSPIJyRMXF4I6qJ4q/MUXR + 0oL4TaiCwvx/m6g5nyG3hx7VbXpl1BMBhfYqqLqcAzkCgYEA8dZslkCqS6a84miR + 5HbUpl6xYwrra/sy5LwI82QrU/rYlOxU6JXCimOI+VMlV+hrgmPb3nYA9YR3byvr + yTj+M45g3UJvzsYcBEdsQsgP8gxeohDEsGKkeQ/GHrGiV/bxh8fDpMRxDDD15yaP + MCVEy4CUIwjnG0b7N4pc3WaNEmsCgYEAyJ/myS3+qwAY3vOQm30rQYAt0l9kvfAV + N2EAvQZJtS+PCJcg6/GY90BiPQOQVQCIfg9kf1XPN1cBTP5ty9vSigeBUSDSBd/v + AdQesMnCYHMCwb1XYjfWPJRmj0296dAI0RlwXz9fpmUUd/WPQWCxeLjwq+3a0ZJO + 8WBIa3Tm8TMCgYEAhI8nWDi739nWgTgWeCeWqlcPXp22q6q2m+Bh+5+1jEPcgc0F + QbQNPbQPebLUrlnszD0WYNtH7Uwd92cYyGSgGfx6Je3rwWigJMxNkFF/RAr5uFX+ + qjx3sRAvZdWyigsHG4kpOWCgIrGXqItfQ2G6Ut341TdlDnOa8je6bXVv8F8CgYEA + wPJv7anrlB/qy3lp6PCPilYxO3L9G2LrtK/5GtIST0vm/vcB9YkMeTaVhGKKDAYQ + P1SkbYZkXK+zk43aoMXQDWm8Z/7tnjLI1XRg89uGsmXKD/P+N3rF8ssye73j2Rt3 + b0pM9X2oiwoJjnk/BjxtUlJjPKbr3MQeYiwcWiQ6+1sCgYEAzqcM2JFGpY3P5TnV + LD3Jb4zewqGbgJ5T2qLD/cJYDzskQos+Y2vojbh1wswsBwFh/RyIzuf3+yfvUT4+ + EA5DyKDppexIsWXavQ7718i/OSFPCOcWP/vfu5Mr5S/CQcbFbHxiBSa2ZmDD/VXr + 5T9BDCRf/HiTZpuyRcLStfOquYk= + -----END PRIVATE KEY----- + """ let cert = """ - -----BEGIN CERTIFICATE----- - MIICljCCAX4CCQDNA8gK8Kol/DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJJ - VDAeFw0yMzEwMTIxNDEzMzVaFw0yMzExMTExNDEzMzVaMA0xCzAJBgNVBAYTAklU - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYaTB4Y0ulF+RpObKOtP - 6fidsQ4IjsP9SoKd97fqEydUp2FMKdwklEPMuXDzUBnUOMnqrtVMD1vbkQOMzRM3 - hBSDBljogtEu6e+K8rmE1oOBzKsXneKtGbdylRavzG7GWML2/PJLG7Wffrp3HCBH - oP5aMX7F+2iZBYJefDc/CrS3Dty63j9Mkm8QcFqzd007QFno9qH6N/b4kbsVCcVp - qRB8knPoqEiSx8QhtorsUfgTBonh7RkO0YtCtH9UZS8EZYUH2a4RiGq6qzMNqZXM - lRVHwKbTg945pMx5su1azX3sjhKY9q6Igi6tjagvhn+zfnyzzI98NvJAo8d2RJNm - UQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAMEYdPGEik0I6gfrAZ1096kJWODyt+ - SEc6KLxFKTWPXoy8QjuYazxrf7oNXF0ZxqUzv42AfyQR3sFYNHf9CAFv+oBav1Q9 - MHPVpBn+DE092fvU2cRHhmbJFJaRPARxsonNeFwczJPOuseNSbjA65K4Bqlm9ywv - 7p5F+TqI080mpeMMw/KA1VcIqxbJLO7IUDg9w25XotTBgplFh/SCE5FgWB0g2Iff - la4Op9AHh7N6hiTGJwn6MyxfxFm8+2wATNX3BglUXwiPtfMwGnNy4ft5Nxi6ZI7m - QkJUDkYq0ZsPjk6/4fYP1abrsDcWua0BrYtzBZqLVWKQWJ0xftGmX2m6 - -----END CERTIFICATE----- - """ + -----BEGIN CERTIFICATE----- + MIICljCCAX4CCQDNA8gK8Kol/DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJJ + VDAeFw0yMzEwMTIxNDEzMzVaFw0yMzExMTExNDEzMzVaMA0xCzAJBgNVBAYTAklU + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYaTB4Y0ulF+RpObKOtP + 6fidsQ4IjsP9SoKd97fqEydUp2FMKdwklEPMuXDzUBnUOMnqrtVMD1vbkQOMzRM3 + hBSDBljogtEu6e+K8rmE1oOBzKsXneKtGbdylRavzG7GWML2/PJLG7Wffrp3HCBH + oP5aMX7F+2iZBYJefDc/CrS3Dty63j9Mkm8QcFqzd007QFno9qH6N/b4kbsVCcVp + qRB8knPoqEiSx8QhtorsUfgTBonh7RkO0YtCtH9UZS8EZYUH2a4RiGq6qzMNqZXM + lRVHwKbTg945pMx5su1azX3sjhKY9q6Igi6tjagvhn+zfnyzzI98NvJAo8d2RJNm + UQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAMEYdPGEik0I6gfrAZ1096kJWODyt+ + SEc6KLxFKTWPXoy8QjuYazxrf7oNXF0ZxqUzv42AfyQR3sFYNHf9CAFv+oBav1Q9 + MHPVpBn+DE092fvU2cRHhmbJFJaRPARxsonNeFwczJPOuseNSbjA65K4Bqlm9ywv + 7p5F+TqI080mpeMMw/KA1VcIqxbJLO7IUDg9w25XotTBgplFh/SCE5FgWB0g2Iff + la4Op9AHh7N6hiTGJwn6MyxfxFm8+2wATNX3BglUXwiPtfMwGnNy4ft5Nxi6ZI7m + QkJUDkYq0ZsPjk6/4fYP1abrsDcWua0BrYtzBZqLVWKQWJ0xftGmX2m6 + -----END CERTIFICATE----- + """ let privateKey2 = """ - -----BEGIN PRIVATE KEY----- - MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj - MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu - NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ - qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg - p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR - ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi - VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV - laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8 - sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H - mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY - dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw - ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ - DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T - N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t - 0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv - t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU - AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk - 48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL - DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK - xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA - mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh - 2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz - et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr - VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD - TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc - dn/RsYEONbwQSjIfMPkvxF+8HQ== - -----END PRIVATE KEY----- - """ + -----BEGIN PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj + MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu + NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ + qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg + p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR + ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi + VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV + laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8 + sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H + mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY + dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw + ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ + DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T + N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t + 0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv + t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU + AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk + 48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL + DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK + xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA + mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh + 2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz + et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr + VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD + TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc + dn/RsYEONbwQSjIfMPkvxF+8HQ== + -----END PRIVATE KEY----- + """ let publicKey2 = """ - -----BEGIN PUBLIC KEY----- - MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo - 4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u - +qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh - kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ - 0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg - cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc - mwIDAQAB - -----END PUBLIC KEY----- - """ + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo + 4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u + +qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh + kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ + 0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg + cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc + mwIDAQAB + -----END PUBLIC KEY----- + """ } diff --git a/Tests/JWTKitTests/Utils/Bundle+Sendable.swift b/Tests/JWTKitTests/Utils/Bundle+Sendable.swift new file mode 100644 index 00000000..25843d55 --- /dev/null +++ b/Tests/JWTKitTests/Utils/Bundle+Sendable.swift @@ -0,0 +1,5 @@ +import Foundation + +#if compiler(<6.0) && !canImport(Darwin) +extension Foundation.Bundle: @unchecked Swift.Sendable {} +#endif diff --git a/Tests/JWTKitTests/VendorTokenTests.swift b/Tests/JWTKitTests/VendorTokenTests.swift index 1bd1d781..07b5a137 100644 --- a/Tests/JWTKitTests/VendorTokenTests.swift +++ b/Tests/JWTKitTests/VendorTokenTests.swift @@ -1,7 +1,7 @@ import JWTKit import XCTest -final class VendorTokenTests: XCTestCase { +final class VendorTokenTests: XCTestCase, @unchecked Sendable { func testGoogleIDToken() async throws { let token = GoogleIdentityToken( issuer: "https://accounts.google.com", @@ -53,7 +53,9 @@ final class VendorTokenTests: XCTestCase { let collection = await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) let jwt = try await collection.sign(token) - await XCTAssertThrowsErrorAsync(try await collection.verify(jwt, as: GoogleIdentityToken.self)) { error in + await XCTAssertThrowsErrorAsync( + try await collection.verify(jwt, as: GoogleIdentityToken.self) + ) { error in guard let error = error as? JWTError else { return XCTFail("Unexpected error: \(error)") } @@ -86,7 +88,9 @@ final class VendorTokenTests: XCTestCase { let collection = await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) let jwt = try await collection.sign(token) - await XCTAssertThrowsErrorAsync(try await collection.verify(jwt, as: GoogleIdentityToken.self)) { error in + await XCTAssertThrowsErrorAsync( + try await collection.verify(jwt, as: GoogleIdentityToken.self) + ) { error in guard let error = error as? JWTError else { return XCTFail("Unexpected error: \(error)") } @@ -136,7 +140,9 @@ final class VendorTokenTests: XCTestCase { let collection = await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) let jwt = try await collection.sign(token) - await XCTAssertThrowsErrorAsync(try await collection.verify(jwt, as: AppleIdentityToken.self)) { error in + await XCTAssertThrowsErrorAsync( + try await collection.verify(jwt, as: AppleIdentityToken.self) + ) { error in guard let error = error as? JWTError else { return XCTFail("Unexpected error: \(error)") } @@ -172,7 +178,8 @@ final class VendorTokenTests: XCTestCase { let collection = await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) let jwt = try await collection.sign(token) - await XCTAssertNoThrowAsync(try await collection.verify(jwt, as: MicrosoftIdentityToken.self)) + await XCTAssertNoThrowAsync( + try await collection.verify(jwt, as: MicrosoftIdentityToken.self)) } func testMicrosoftIDTokenNotFromMicrosoft() async throws { @@ -200,7 +207,9 @@ final class VendorTokenTests: XCTestCase { let collection = await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) let jwt = try await collection.sign(token) - await XCTAssertThrowsErrorAsync(try await collection.verify(jwt, as: MicrosoftIdentityToken.self)) { error in + await XCTAssertThrowsErrorAsync( + try await collection.verify(jwt, as: MicrosoftIdentityToken.self) + ) { error in guard let error = error as? JWTError else { return XCTFail("Unexpected error: \(error)") } @@ -234,7 +243,9 @@ final class VendorTokenTests: XCTestCase { let collection = await JWTKeyCollection().add(hmac: "secret", digestAlgorithm: .sha256) let jwt = try await collection.sign(token) - await XCTAssertThrowsErrorAsync(try await collection.verify(jwt, as: MicrosoftIdentityToken.self)) { error in + await XCTAssertThrowsErrorAsync( + try await collection.verify(jwt, as: MicrosoftIdentityToken.self) + ) { error in guard let error = error as? JWTError else { return XCTFail("Unexpected error: \(error)") } diff --git a/Tests/JWTKitTests/X5CTests.swift b/Tests/JWTKitTests/X5CTests.swift index b241b36a..2fc7f85f 100644 --- a/Tests/JWTKitTests/X5CTests.swift +++ b/Tests/JWTKitTests/X5CTests.swift @@ -19,7 +19,7 @@ import XCTest /// Only tokens with an x5c chain that starts with "Leaf" /// and ends in either "Intermediate" or "Root" should /// successfully be verified. -final class X5CTests: XCTestCase { +final class X5CTests: XCTestCase, @unchecked Sendable { let verifier = try! X5CVerifier(rootCertificates: [ // Trusted root: """ @@ -36,7 +36,7 @@ final class X5CTests: XCTestCase { cjBmGrKKNlYWLLoZ6DiauzdJAiEA9GSAIGhfM9kbWlkcjMs6lA4pwf4RfUEFeghY pZKbqFo= -----END CERTIFICATE----- - """, + """ ]) func check( @@ -52,14 +52,17 @@ final class X5CTests: XCTestCase { /// /// Should pass validation. func testValidChain() async throws { - await XCTAssertNoThrowAsync(try await check(token: validToken), "Valid certificate chain was not verified.") + await XCTAssertNoThrowAsync( + try await check(token: validToken), "Valid certificate chain was not verified.") } /// x5c: [leaf, root] /// /// Should fail validation. func testMissingIntermediate() async throws { - await XCTAssertThrowsErrorAsync(try await check(token: missingIntermediateToken), "Missing intermediate cert should throw an error.") + await XCTAssertThrowsErrorAsync( + try await check(token: missingIntermediateToken), + "Missing intermediate cert should throw an error.") } /// x5c: [leaf, intermediate] @@ -83,28 +86,34 @@ final class X5CTests: XCTestCase { /// /// Should fail validation. func testMissingLeaf() async throws { - await XCTAssertThrowsErrorAsync(try await check(token: missingLeafToken), "Missing leaf cert should throw an error.") + await XCTAssertThrowsErrorAsync( + try await check(token: missingLeafToken), "Missing leaf cert should throw an error.") } /// x5c: [root] /// /// Should fail validation. func testMissingLeafAndIntermediate() async throws { - await XCTAssertThrowsErrorAsync(try await check(token: missingLeafAndIntermediateToken), "Missing leaf/intermediate cert should throw an error.") + await XCTAssertThrowsErrorAsync( + try await check(token: missingLeafAndIntermediateToken), + "Missing leaf/intermediate cert should throw an error.") } /// x5c: [leaf] /// /// Should fail validation. func testMissingIntermediateAndRoot() async throws { - await XCTAssertThrowsErrorAsync(try await check(token: missingIntermediateAndRootToken), "Missing intermediate/root cert should throw an error.") + await XCTAssertThrowsErrorAsync( + try await check(token: missingIntermediateAndRootToken), + "Missing intermediate/root cert should throw an error.") } /// x5c: [expired_leaf, intermediate, root] /// /// Should fail validation because leaf is epxired. func testExpiredLeaf() async throws { - await XCTAssertThrowsErrorAsync(try await check(token: expiredLeafToken), "Expired leaf token is invalid.") + await XCTAssertThrowsErrorAsync( + try await check(token: expiredLeafToken), "Expired leaf token is invalid.") } /// x5c: [leaf, intermediate, root] @@ -114,27 +123,29 @@ final class X5CTests: XCTestCase { /// This is a test to make sure that the claims actually /// get verified. func testValidButNotCool() async throws { - await XCTAssertThrowsErrorAsync(try await check(token: validButNotCoolToken), "Token isn't cool. Claims weren't verified.") + await XCTAssertThrowsErrorAsync( + try await check(token: validButNotCoolToken), + "Token isn't cool. Claims weren't verified.") } func testAppStoreJWT() async throws { let cert = """ - -----BEGIN CERTIFICATE----- - MIIBXDCCAQICCQCfjTUGLDnR9jAKBggqhkjOPQQDAzA2MQswCQYDVQQGEwJVUzET - MBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMB4XDTIzMDEw - NDE2MjAzMloXDTMzMDEwMTE2MjAzMlowNjELMAkGA1UEBhMCVVMxEzARBgNVBAgM - CkNhbGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzBZMBMGByqGSM49AgEGCCqG - SM49AwEHA0IABHPvwZfoKLKaOrX/We4qObXSna5TdWHVZ6hIRA1w0oc3QCT0Io2p - lyDB3/MVlk2tc4KGE8TiqW7ibQ6Zc9V64k0wCgYIKoZIzj0EAwMDSAAwRQIhAMTH - hWtbAQN0hSxIXcP4CKrDCH/gsxWpx6jTZLTeZ+FPAiB35nwk5q0zcIpefvYJ0MU/ - yGGHSWez0bq0pDYUO/nmDw== - -----END CERTIFICATE----- - """ + -----BEGIN CERTIFICATE----- + MIIBXDCCAQICCQCfjTUGLDnR9jAKBggqhkjOPQQDAzA2MQswCQYDVQQGEwJVUzET + MBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ3VwZXJ0aW5vMB4XDTIzMDEw + NDE2MjAzMloXDTMzMDEwMTE2MjAzMlowNjELMAkGA1UEBhMCVVMxEzARBgNVBAgM + CkNhbGlmb3JuaWExEjAQBgNVBAcMCUN1cGVydGlubzBZMBMGByqGSM49AgEGCCqG + SM49AwEHA0IABHPvwZfoKLKaOrX/We4qObXSna5TdWHVZ6hIRA1w0oc3QCT0Io2p + lyDB3/MVlk2tc4KGE8TiqW7ibQ6Zc9V64k0wCgYIKoZIzj0EAwMDSAAwRQIhAMTH + hWtbAQN0hSxIXcP4CKrDCH/gsxWpx6jTZLTeZ+FPAiB35nwk5q0zcIpefvYJ0MU/ + yGGHSWez0bq0pDYUO/nmDw== + -----END CERTIFICATE----- + """ // https://github.com/apple/app-store-server-library-swift/blob/main/Tests/AppStoreServerLibraryTests/SignedDataVerifierTests.swift#L98 let token = """ - eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJDekFLQmdncWhrak9QUVFEQWpCTk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1SVXdFd1lEVlFRS0RBeEpiblJsY20xbFpHbGhkR1V3SGhjTk1qTXdNVEEwTVRZek56TXhXaGNOTXpJeE1qTXhNVFl6TnpNeFdqQkZNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVNNQkFHQTFVRUJ3d0pRM1Z3WlhKMGFXNXZNUTB3Q3dZRFZRUUtEQVJNWldGbU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTRyV0J4R21GYm5QSVBRSTB6c0JLekx4c2o4cEQydnFicjB5UElTVXgyV1F5eG1yTnFsOWZoSzhZRUV5WUZWNysrcDVpNFlVU1Ivbzl1UUlnQ1BJaHJLTWZNQjB3Q1FZRFZSMFRCQUl3QURBUUJnb3Foa2lHOTJOa0Jnc0JCQUlUQURBS0JnZ3Foa2pPUFFRREFnTklBREJGQWlFQWtpRVprb0ZNa2o0Z1huK1E5alhRWk1qWjJnbmpaM2FNOE5ZcmdmVFVpdlFDSURKWVowRmFMZTduU0lVMkxXTFRrNXRYVENjNEU4R0pTWWYvc1lSeEVGaWUiLCJNSUlCbHpDQ0FUMmdBd0lCQWdJQkJqQUtCZ2dxaGtqT1BRUURBakEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qWXdNVm9YRFRNeU1USXpNVEUyTWpZd01Wb3dUVEVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekVWTUJNR0ExVUVDZ3dNU1c1MFpYSnRaV1JwWVhSbE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUZRM2xYMnNxTjlHSXdBaWlNUURRQy9reW5TZ1g0N1J3dmlET3RNWFh2eUtkUWU2Q1BzUzNqbzJ1UkR1RXFBeFdlT2lDcmpsRFdzeXo1d3dkVTBndGFxTWxNQ013RHdZRFZSMFRCQWd3QmdFQi93SUJBREFRQmdvcWhraUc5Mk5rQmdJQkJBSVRBREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBdm56TWNWMjY4Y1JiMS9GcHlWMUVoVDNXRnZPenJCVVdQNi9Ub1RoRmF2TUNJRmJhNXQ2WUt5MFIySkR0eHF0T2pKeTY2bDZWN2QvUHJBRE5wa21JUFcraSIsIk1JSUJYRENDQVFJQ0NRQ2ZqVFVHTERuUjlqQUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qQXpNbG9YRFRNek1ERXdNVEUyTWpBek1sb3dOakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSFB2d1pmb0tMS2FPclgvV2U0cU9iWFNuYTVUZFdIVlo2aElSQTF3MG9jM1FDVDBJbzJwbHlEQjMvTVZsazJ0YzRLR0U4VGlxVzdpYlE2WmM5VjY0azB3Q2dZSUtvWkl6ajBFQXdNRFNBQXdSUUloQU1USGhXdGJBUU4waFN4SVhjUDRDS3JEQ0gvZ3N4V3B4NmpUWkxUZVorRlBBaUIzNW53azVxMHpjSXBlZnZZSjBNVS95R0dIU1dlejBicTBwRFlVTy9ubUR3PT0iXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJkYXRhIjp7ImFwcEFwcGxlSWQiOjEyMzQsImVudmlyb25tZW50IjoiU2FuZGJveCIsImJ1bmRsZUlkIjoiY29tLmV4YW1wbGUifSwibm90aWZpY2F0aW9uVVVJRCI6IjlhZDU2YmQyLTBiYzYtNDJlMC1hZjI0LWZkOTk2ZDg3YTFlNiIsInNpZ25lZERhdGUiOjE2ODEzMTQzMjQwMDAsIm5vdGlmaWNhdGlvblR5cGUiOiJURVNUIn0.VVXYwuNm2Y3XsOUva-BozqatRCsDuykA7xIe_CCRw6aIAAxJ1nb2sw871jfZ6dcgNhUuhoZ93hfbc1v_5zB7Og - """ + eyJ4NWMiOlsiTUlJQm9EQ0NBVWFnQXdJQkFnSUJDekFLQmdncWhrak9QUVFEQWpCTk1Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFU01CQUdBMVVFQnd3SlEzVndaWEowYVc1dk1SVXdFd1lEVlFRS0RBeEpiblJsY20xbFpHbGhkR1V3SGhjTk1qTXdNVEEwTVRZek56TXhXaGNOTXpJeE1qTXhNVFl6TnpNeFdqQkZNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1EyRnNhV1p2Y201cFlURVNNQkFHQTFVRUJ3d0pRM1Z3WlhKMGFXNXZNUTB3Q3dZRFZRUUtEQVJNWldGbU1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRTRyV0J4R21GYm5QSVBRSTB6c0JLekx4c2o4cEQydnFicjB5UElTVXgyV1F5eG1yTnFsOWZoSzhZRUV5WUZWNysrcDVpNFlVU1Ivbzl1UUlnQ1BJaHJLTWZNQjB3Q1FZRFZSMFRCQUl3QURBUUJnb3Foa2lHOTJOa0Jnc0JCQUlUQURBS0JnZ3Foa2pPUFFRREFnTklBREJGQWlFQWtpRVprb0ZNa2o0Z1huK1E5alhRWk1qWjJnbmpaM2FNOE5ZcmdmVFVpdlFDSURKWVowRmFMZTduU0lVMkxXTFRrNXRYVENjNEU4R0pTWWYvc1lSeEVGaWUiLCJNSUlCbHpDQ0FUMmdBd0lCQWdJQkJqQUtCZ2dxaGtqT1BRUURBakEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qWXdNVm9YRFRNeU1USXpNVEUyTWpZd01Wb3dUVEVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekVWTUJNR0ExVUVDZ3dNU1c1MFpYSnRaV1JwWVhSbE1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUZRM2xYMnNxTjlHSXdBaWlNUURRQy9reW5TZ1g0N1J3dmlET3RNWFh2eUtkUWU2Q1BzUzNqbzJ1UkR1RXFBeFdlT2lDcmpsRFdzeXo1d3dkVTBndGFxTWxNQ013RHdZRFZSMFRCQWd3QmdFQi93SUJBREFRQmdvcWhraUc5Mk5rQmdJQkJBSVRBREFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBdm56TWNWMjY4Y1JiMS9GcHlWMUVoVDNXRnZPenJCVVdQNi9Ub1RoRmF2TUNJRmJhNXQ2WUt5MFIySkR0eHF0T2pKeTY2bDZWN2QvUHJBRE5wa21JUFcraSIsIk1JSUJYRENDQVFJQ0NRQ2ZqVFVHTERuUjlqQUtCZ2dxaGtqT1BRUURBekEyTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tRMkZzYVdadmNtNXBZVEVTTUJBR0ExVUVCd3dKUTNWd1pYSjBhVzV2TUI0WERUSXpNREV3TkRFMk1qQXpNbG9YRFRNek1ERXdNVEUyTWpBek1sb3dOakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNrTmhiR2xtYjNKdWFXRXhFakFRQmdOVkJBY01DVU4xY0dWeWRHbHViekJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSFB2d1pmb0tMS2FPclgvV2U0cU9iWFNuYTVUZFdIVlo2aElSQTF3MG9jM1FDVDBJbzJwbHlEQjMvTVZsazJ0YzRLR0U4VGlxVzdpYlE2WmM5VjY0azB3Q2dZSUtvWkl6ajBFQXdNRFNBQXdSUUloQU1USGhXdGJBUU4waFN4SVhjUDRDS3JEQ0gvZ3N4V3B4NmpUWkxUZVorRlBBaUIzNW53azVxMHpjSXBlZnZZSjBNVS95R0dIU1dlejBicTBwRFlVTy9ubUR3PT0iXSwidHlwIjoiSldUIiwiYWxnIjoiRVMyNTYifQ.eyJkYXRhIjp7ImFwcEFwcGxlSWQiOjEyMzQsImVudmlyb25tZW50IjoiU2FuZGJveCIsImJ1bmRsZUlkIjoiY29tLmV4YW1wbGUifSwibm90aWZpY2F0aW9uVVVJRCI6IjlhZDU2YmQyLTBiYzYtNDJlMC1hZjI0LWZkOTk2ZDg3YTFlNiIsInNpZ25lZERhdGUiOjE2ODEzMTQzMjQwMDAsIm5vdGlmaWNhdGlvblR5cGUiOiJURVNUIn0.VVXYwuNm2Y3XsOUva-BozqatRCsDuykA7xIe_CCRw6aIAAxJ1nb2sw871jfZ6dcgNhUuhoZ93hfbc1v_5zB7Og + """ struct StoreKitPayload: ValidationTimePayload { struct DataClass: Codable { @@ -157,7 +168,8 @@ final class X5CTests: XCTestCase { var payload: StoreKitPayload? do { - payload = try await verifier.verifyJWS(token, as: StoreKitPayload.self, jsonDecoder: jsonDecoder) + payload = try await verifier.verifyJWS( + token, as: StoreKitPayload.self, jsonDecoder: jsonDecoder) } catch { XCTFail("Failed with error: \(error.localizedDescription)") } @@ -168,7 +180,8 @@ final class X5CTests: XCTestCase { } func testDERInit() async throws { - let testsDirectory: String = URL(fileURLWithPath: "\(#filePath)").pathComponents.dropLast(1).joined(separator: "/") + let testsDirectory: String = URL(fileURLWithPath: "\(#filePath)").pathComponents.dropLast(1) + .joined(separator: "/") let path = "\(testsDirectory)/TestCertificates/testCA.der" let fileHandle = try FileHandle(forReadingFrom: URL(fileURLWithPath: path)) let data = fileHandle.readDataToEndOfFile() @@ -179,9 +192,12 @@ final class X5CTests: XCTestCase { func testValidCerts() async throws { let verifier = try X5CVerifier(rootCertificates: [rootCA]) - let result = try await verifier.verifyChain(certificates: [leaf, intermediate], policy: { - RFC5280Policy(validationTime: Date(timeIntervalSince1970: TimeInterval(1_681_312_846))) - }) + let result = try await verifier.verifyChain( + certificates: [leaf, intermediate], + policy: { + RFC5280Policy( + validationTime: Date(timeIntervalSince1970: TimeInterval(1_681_312_846))) + }) switch result { case let .couldNotValidate(failures): @@ -194,9 +210,12 @@ final class X5CTests: XCTestCase { func testValidCertsWithExpiredValidationTime() async throws { let verifier = try X5CVerifier(rootCertificates: [rootCA]) - let result = try await verifier.verifyChain(certificates: [leaf, intermediate], policy: { - RFC5280Policy(validationTime: Date(timeIntervalSince1970: TimeInterval(2_280_946_846))) - }) + let result = try await verifier.verifyChain( + certificates: [leaf, intermediate], + policy: { + RFC5280Policy( + validationTime: Date(timeIntervalSince1970: TimeInterval(2_280_946_846))) + }) switch result { case .couldNotValidate: @@ -207,7 +226,8 @@ final class X5CTests: XCTestCase { } func testSigningWithX5CChain() async throws { - let keyCollection = try await JWTKeyCollection().add(ecdsa: ES256PrivateKey(pem: x5cLeafCertKey)) + let keyCollection = try await JWTKeyCollection().add( + ecdsa: ES256PrivateKey(pem: x5cLeafCertKey)) let payload = TestPayload( sub: "vapor", @@ -227,7 +247,8 @@ final class X5CTests: XCTestCase { } func testSigningWithInvalidX5CChain() async throws { - let keyCollection = try await JWTKeyCollection().add(ecdsa: ES256PrivateKey(pem: x5cLeafCertKey)) + let keyCollection = try await JWTKeyCollection().add( + ecdsa: ES256PrivateKey(pem: x5cLeafCertKey)) let payload = TestPayload( sub: "vapor", @@ -261,7 +282,9 @@ final class X5CTests: XCTestCase { pemLines.append("-----BEGIN CERTIFICATE-----") while encoded.count > 0 { - let prefixIndex = encoded.index(encoded.startIndex, offsetBy: 64, limitedBy: encoded.endIndex) ?? encoded.endIndex + let prefixIndex = + encoded.index(encoded.startIndex, offsetBy: 64, limitedBy: encoded.endIndex) + ?? encoded.endIndex pemLines.append(encoded[..