From f6a94ef15b67f5b14d069e4bc99ecf328002ad8e Mon Sep 17 00:00:00 2001 From: dtsiflit Date: Fri, 13 Dec 2024 11:06:42 +0200 Subject: [PATCH] [fix] async signer updates --- Sources/Entities/Encryption/BindingKey.swift | 19 +++---- Tests/Constants/TestsConstants.swift | 56 ++++++++++++++++++-- Tests/Wallet/VCIFlowWithOffer.swift | 51 +++++++++++++++++- 3 files changed, 107 insertions(+), 19 deletions(-) diff --git a/Sources/Entities/Encryption/BindingKey.swift b/Sources/Entities/Encryption/BindingKey.swift index d92fecd..8765815 100644 --- a/Sources/Entities/Encryption/BindingKey.swift +++ b/Sources/Entities/Encryption/BindingKey.swift @@ -185,19 +185,12 @@ extension BindingKey { return secKeySigner } else if case let .custom(customAsyncSigner) = privateKey { - let signingInput: Data? = [ - header as DataConvertible, - payload as DataConvertible - ].map { - $0.data().base64URLEncodedString() - } - .joined(separator: ".").data(using: .ascii) - - guard let signingInput = signingInput else { - throw ValidationError.error(reason: "Invalid signing input fopr signing data") - } + let headerData = header as DataConvertible + let signature = try await customAsyncSigner.signAsync( + headerData.data(), + payload.data() + ) - let signature = try await customAsyncSigner.signAsync(signingInput) let customSigner = PrecomputedSigner( signature: signature, algorithm: signatureAlgorithm @@ -225,5 +218,5 @@ class PrecomputedSigner: JOSESwift.SignerProtocol { } public protocol AsyncSignerProtocol { - func signAsync(_ signingInput: Data) async throws -> Data + func signAsync(_ header: Data, _ payload: Data) async throws -> Data } diff --git a/Tests/Constants/TestsConstants.swift b/Tests/Constants/TestsConstants.swift index c29c2af..6da786a 100644 --- a/Tests/Constants/TestsConstants.swift +++ b/Tests/Constants/TestsConstants.swift @@ -14,6 +14,8 @@ * limitations under the License. */ import Foundation +import JOSESwift + @testable import OpenID4VCI let CREDENTIAL_ISSUER_PUBLIC_URL = "https://dev.issuer-backend.eudiw.dev" @@ -77,7 +79,7 @@ let MDL_CredentialOffer = """ let config: OpenId4VCIConfig = .init( clientId: "wallet-dev", - authFlowRedirectionURI: URL(string: "urn:ietf:wg:oauth:2.0:oob")!, + authFlowRedirectionURI: URL(string: "urn:ietf:wg:oauth:2.0:oob")!, authorizeIssuanceConfig: .favorScopes ) @@ -146,7 +148,7 @@ struct TestsConstants { path: "credential_issuer_metadata", extension: "json" ) - )) + )) let authorizationServerMetadataResolver = AuthorizationServerMetadataResolver( oidcFetcher: Fetcher(session: NetworkingMock( @@ -179,7 +181,7 @@ struct TestsConstants { path: "openid-credential-issuer_no_encryption", extension: "json" ) - )) + )) let authorizationServerMetadataResolver = AuthorizationServerMetadataResolver( oidcFetcher: Fetcher(session: NetworkingMock( @@ -212,7 +214,7 @@ struct TestsConstants { path: "openid-credential-issuer_no_encryption_batch", extension: "json" ) - )) + )) let authorizationServerMetadataResolver = AuthorizationServerMetadataResolver( oidcFetcher: Fetcher(session: NetworkingMock( @@ -245,7 +247,7 @@ struct TestsConstants { path: "credential_issuer_metadata", extension: "json" ) - )) + )) let authorizationServerMetadataResolver = AuthorizationServerMetadataResolver( oidcFetcher: Fetcher(session: NetworkingMock( @@ -272,3 +274,47 @@ struct TestsConstants { ).get() } } + +class TestSinger: AsyncSignerProtocol { + let privateKey: SecKey + + init(privateKey: SecKey) { + self.privateKey = privateKey + } + + func signAsync(_ header: Data, _ payload: Data) async throws -> Data { + + guard + let jwsHeader = JWSHeader(header), + let algorithm = jwsHeader.algorithm + else { + throw NSError( + domain: "SignerErrorDomain", + code: 1, + userInfo: [NSLocalizedDescriptionKey: "Unable to create signer"] + ) + } + + /// Create the signer + guard let signer = Signer( + signatureAlgorithm: algorithm, + key: privateKey + ) else { + throw NSError( + domain: "SignerErrorDomain", + code: 1, + userInfo: [NSLocalizedDescriptionKey: "Unable to create signer"] + ) + } + + let jws = try JWS( + header: jwsHeader, + payload: .init( + payload + ), + signer: signer + ) + + return jws.signature + } +} diff --git a/Tests/Wallet/VCIFlowWithOffer.swift b/Tests/Wallet/VCIFlowWithOffer.swift index 0cc1052..7ca2b51 100644 --- a/Tests/Wallet/VCIFlowWithOffer.swift +++ b/Tests/Wallet/VCIFlowWithOffer.swift @@ -74,6 +74,55 @@ class VCIFlowWithOffer: XCTestCase { XCTAssert(true) } + func testWithOfferSdJWTWithSigner() async throws { + + let privateKey = try KeyController.generateECDHPrivateKey() + let publicKey = try KeyController.generateECDHPublicKey(from: privateKey) + + let alg = JWSAlgorithm(.ES256) + let publicKeyJWK = try ECPublicKey( + publicKey: publicKey, + additionalParameters: [ + "alg": alg.name, + "use": "sig", + "kid": UUID().uuidString + ]) + + let bindingKey: BindingKey = .jwk( + algorithm: alg, + jwk: publicKeyJWK, + privateKey: .custom( + TestSinger( + privateKey: privateKey + ) + ) + ) + + let user = ActingUser( + username: "tneal", + password: "password" + ) + + let wallet = Wallet( + actingUser: user, + bindingKeys: [bindingKey], + dPoPConstructor: nil, + session: Wallet.walletSession + ) + + do { + try await walletInitiatedIssuanceWithOfferSdJWT( + wallet: wallet + ) + } catch { + + XCTExpectFailure() + XCTAssert(false, error.localizedDescription) + } + + XCTAssert(true) + } + func testWithOfferMdoc() async throws { let privateKey = try KeyController.generateECDHPrivateKey() @@ -315,7 +364,7 @@ private func walletInitiatedIssuanceWithOfferSdJWT( scope: PID_SdJwtVC_config_id, claimSet: claimSet ) - + print("--> [ISSUANCE] Issued credential: \(credential)") }