Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated signing logic #99

Merged
merged 6 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/scinfu/SwiftSoup.git",
"state" : {
"revision" : "028487d4a8a291b2fe1b4392b5425b6172056148",
"version" : "2.7.2"
"revision" : "0837db354faf9c9deb710dc597046edaadf5360f",
"version" : "2.7.6"
}
},
{
Expand Down
4 changes: 0 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ let package = Package(
name: "SwiftyJSON",
package: "SwiftyJSON"
),
.product(
name: "JOSESwift",
package: "JOSESwift"
),
.product(
name: "SwiftSoup",
package: "SwiftSoup"
Expand Down
25 changes: 14 additions & 11 deletions Sources/DPoP/DPoPConstructor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import JOSESwift
import CryptoKit

public protocol DPoPConstructorType {
func jwt(endpoint: URL, accessToken: String?) throws -> String
func jwt(endpoint: URL, accessToken: String?) async throws -> String
}

public class DPoPConstructor: DPoPConstructorType {
Expand All @@ -36,15 +36,18 @@ public class DPoPConstructor: DPoPConstructorType {

public let algorithm: JWSAlgorithm
public let jwk: JWK
public let privateKey: SecKey
public let privateKey: SigningKeyProxy

public init(algorithm: JWSAlgorithm, jwk: JWK, privateKey: SecKey) {
public init(algorithm: JWSAlgorithm, jwk: JWK, privateKey: SigningKeyProxy) {
self.algorithm = algorithm
self.jwk = jwk
self.privateKey = privateKey
}

public func jwt(endpoint: URL, accessToken: String?) throws -> String {
public func jwt(
endpoint: URL,
accessToken: String?
) async throws -> String {

let header = try JWSHeader(parameters: [
"typ": "dpop+jwt",
Expand All @@ -71,13 +74,13 @@ public class DPoPConstructor: DPoPConstructorType {
throw CredentialIssuanceError.cryptographicAlgorithmNotSupported
}

guard let signer = Signer(
signatureAlgorithm: signatureAlgorithm,
key: privateKey
) else {
throw ValidationError.error(reason: "Unable to create JWS signer")
}

let signer = try await BindingKey.createSigner(
with: header,
and: payload,
for: privateKey,
and: signatureAlgorithm
)
let jws = try JWS(
header: header,
payload: payload,
Expand Down
100 changes: 77 additions & 23 deletions Sources/Entities/Encryption/BindingKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@
import Foundation
import JOSESwift

public enum SigningKeyProxy {
case custom(any AsyncSignerProtocol)
case secKey(SecKey)
}

public enum BindingKey {

// JWK Binding Key
case jwk(
algorithm: JWSAlgorithm,
jwk: JWK,
privateKey: SecKey,
privateKey: SigningKeyProxy,
issuer: String? = nil
)

Expand All @@ -39,7 +44,7 @@ public extension BindingKey {
issuanceRequester: IssuanceRequesterType,
credentialSpec: CredentialSupported,
cNonce: String?
) throws -> Proof {
) async throws -> Proof {
switch self {
case .jwk(
let algorithm,
Expand Down Expand Up @@ -84,13 +89,13 @@ public extension BindingKey {
guard let signatureAlgorithm = SignatureAlgorithm(rawValue: algorithm.name) else {
throw CredentialIssuanceError.cryptographicAlgorithmNotSupported
}

guard let signer = Signer(
signatureAlgorithm: signatureAlgorithm,
key: privateKey
) else {
throw ValidationError.error(reason: "Unable to create JWS signer")
}
let signer: Signer = try await Self.createSigner(
with: header,
and: payload,
for: privateKey,
and: signatureAlgorithm
)

let jws = try JWS(
header: header,
Expand All @@ -111,13 +116,6 @@ public extension BindingKey {
throw CredentialIssuanceError.proofTypeNotSupported
}

/*
let bindings = spec.cryptographicBindingMethodsSupported.contains { $0 == .jwk }
guard bindings else {
throw CredentialIssuanceError.cryptographicBindingMethodNotSupported
}
*/

let aud = issuanceRequester.issuerMetadata.credentialIssuerIdentifier.url.absoluteString

let header = try JWSHeader(parameters: [
Expand All @@ -144,12 +142,12 @@ public extension BindingKey {
throw CredentialIssuanceError.cryptographicAlgorithmNotSupported
}

guard let signer = Signer(
signatureAlgorithm: signatureAlgorithm,
key: privateKey
) else {
throw ValidationError.error(reason: "Unable to create JWS signer")
}
let signer: Signer = try await Self.createSigner(
with: header,
and: payload,
for: privateKey,
and: signatureAlgorithm
)

let jws = try JWS(
header: header,
Expand All @@ -170,6 +168,62 @@ public extension BindingKey {
}
}

private extension BindingKey {
extension BindingKey {

static func createSigner(
with header: JWSHeader,
and payload: Payload,
for privateKey: SigningKeyProxy,
and signatureAlgorithm: SignatureAlgorithm
) async throws -> Signer {

if case let .secKey(secKey) = privateKey,
let secKeySigner = Signer(
signatureAlgorithm: signatureAlgorithm,
key: secKey
) {
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 signature = try await customAsyncSigner.signAsync(signingInput)
let customSigner = PrecomputedSigner(
signature: signature,
algorithm: signatureAlgorithm
)
return Signer(customSigner: customSigner)

} else {
throw ValidationError.error(reason: "Unable to create JWS signer")
}
}
}

class PrecomputedSigner: JOSESwift.SignerProtocol {
var algorithm: JOSESwift.SignatureAlgorithm
let signature: Data

init(signature: Data, algorithm: JOSESwift.SignatureAlgorithm) {
self.algorithm = algorithm
self.signature = signature
}

func sign(_ signingInput: Data) throws -> Data {
return signature
}
}

public protocol AsyncSignerProtocol {
func signAsync(_ signingInput: Data) async throws -> Data
}
5 changes: 3 additions & 2 deletions Sources/Entities/IssuanceAccessToken.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,14 @@ public extension IssuanceAccessToken {
func dPoPOrBearerAuthorizationHeader(
dpopConstructor: DPoPConstructorType?,
endpoint: URL?
) throws -> [String: String] {
) async throws -> [String: String] {
if tokenType == TokenType.bearer {
return ["Authorization": "\(TokenType.bearer.rawValue) \(accessToken)"]
} else if let dpopConstructor, tokenType == TokenType.dpop, let endpoint {
let jwt = try await dpopConstructor.jwt(endpoint: endpoint, accessToken: accessToken)
return [
"Authorization": "\(TokenType.dpop.rawValue) \(accessToken)",
TokenType.dpop.rawValue: try dpopConstructor.jwt(endpoint: endpoint, accessToken: accessToken)
TokenType.dpop.rawValue: jwt
]
}
return ["Authorization": "\(TokenType.bearer.rawValue) \(accessToken)"]
Expand Down
8 changes: 4 additions & 4 deletions Sources/Issuers/IssuanceRequester.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public actor IssuanceRequester: IssuanceRequesterType {
let endpoint = issuerMetadata.credentialEndpoint.url

do {
let authorizationHeader: [String: String] = try accessToken.dPoPOrBearerAuthorizationHeader(
let authorizationHeader: [String: String] = try await accessToken.dPoPOrBearerAuthorizationHeader(
dpopConstructor: dpopConstructor,
endpoint: endpoint
)
Expand Down Expand Up @@ -192,7 +192,7 @@ public actor IssuanceRequester: IssuanceRequesterType {
}

do {
let authorizationHeader: [String: Any] = try accessToken.dPoPOrBearerAuthorizationHeader(
let authorizationHeader: [String: Any] = try await accessToken.dPoPOrBearerAuthorizationHeader(
dpopConstructor: dpopConstructor,
endpoint: endpoint
)
Expand Down Expand Up @@ -224,7 +224,7 @@ public actor IssuanceRequester: IssuanceRequesterType {
throw CredentialError.issuerDoesNotSupportDeferredIssuance
}

let authorizationHeader: [String: String] = try accessToken.dPoPOrBearerAuthorizationHeader(
let authorizationHeader: [String: String] = try await accessToken.dPoPOrBearerAuthorizationHeader(
dpopConstructor: dpopConstructor,
endpoint: deferredCredentialEndpoint.url
)
Expand Down Expand Up @@ -298,7 +298,7 @@ public actor IssuanceRequester: IssuanceRequesterType {
}

let endpoint = notificationEndpoint.url
let authorizationHeader: [String: String] = try accessToken.dPoPOrBearerAuthorizationHeader(
let authorizationHeader: [String: String] = try await accessToken.dPoPOrBearerAuthorizationHeader(
dpopConstructor: dpopConstructor,
endpoint: endpoint
)
Expand Down
12 changes: 6 additions & 6 deletions Sources/Issuers/Issuer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -520,15 +520,15 @@ public actor Issuer: IssuerType {
switch proofRequest {
case .proofRequired(let token, _, let cNonce, _, _):
return try await requestIssuance(token: token) {
let credentialRequests: [CredentialIssuanceRequest] = try requestPayload.map { identifier in
let credentialRequests: [CredentialIssuanceRequest] = try await requestPayload.asyncMap { identifier in
guard let supportedCredential = issuerMetadata
.credentialsSupported[identifier.credentialConfigurationIdentifier] else {
throw ValidationError.error(reason: "Invalid Supported credential for requestBatch")
}
return try supportedCredential.toIssuanceRequest(
requester: issuanceRequester,
claimSet: identifier.claimSet,
proof: bindingKey.toSupportedProof(
proof: try await bindingKey.toSupportedProof(
issuanceRequester: issuanceRequester,
credentialSpec: supportedCredential,
cNonce: cNonce.value
Expand Down Expand Up @@ -559,9 +559,9 @@ private extension Issuer {

private func requestIssuance(
token: IssuanceAccessToken,
issuanceRequestSupplier: () throws -> CredentialIssuanceRequest
issuanceRequestSupplier: () async throws -> CredentialIssuanceRequest
) async throws -> Result<SubmittedRequest, Error> {
let credentialRequest = try issuanceRequestSupplier()
let credentialRequest = try await issuanceRequestSupplier()
switch credentialRequest {
case .single(let single, let encryptionSpec):
self.deferredResponseEncryptionSpec = encryptionSpec
Expand Down Expand Up @@ -674,7 +674,7 @@ private extension Issuer {
return try supportedCredential.toIssuanceRequest(
requester: issuanceRequester,
claimSet: claimSet,
proof: bindingKey?.toSupportedProof(
proof: try await bindingKey?.toSupportedProof(
issuanceRequester: issuanceRequester,
credentialSpec: supportedCredential,
cNonce: cNonce?.value
Expand All @@ -699,7 +699,7 @@ private extension Issuer {
}

return try await requestIssuance(token: token) {
return try supportedCredential.toIssuanceRequest(
return try await supportedCredential.toIssuanceRequest(
requester: issuanceRequester,
proof: bindingKey?.toSupportedProof(
issuanceRequester: issuanceRequester,
Expand Down
5 changes: 3 additions & 2 deletions Sources/Main/Authorisers/AuthorizationServerClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,10 @@ public actor AuthorizationServerClient: AuthorizationServerClientType {

private extension AuthorizationServerClient {

func tokenEndPointHeaders() throws -> [String: String] {
func tokenEndPointHeaders() async throws -> [String: String] {
if let dpopConstructor {
return ["DPoP": try dpopConstructor.jwt(endpoint: tokenEndpoint, accessToken: nil)]
let jwt = try await dpopConstructor.jwt(endpoint: tokenEndpoint, accessToken: nil)
return ["DPoP": jwt]
} else {
return [:]
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/Constants/TestsConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let PID_MsoMdoc_config_id = "eu.europa.ec.eudi.pid_mso_mdoc"
let PID_SdJwtVC_config_id = "eu.europa.ec.eudi.pid_vc_sd_jwt"

//let CREDENTIAL_ISSUER_PUBLIC_URL = "https://dev.issuer.eudiw.dev"
//let PID_SdJwtVC_config_id = "eu.europa.ec.eudi.mdl_jwt_vc_json"
//let PID_SdJwtVC_config_id = "eu.europa.ec.eudi.pid_jwt_vc_json"
//let PID_MsoMdoc_config_id = "eu.europa.ec.eudi.pid_mdoc"
//let MDL_config_id = "eu.europa.ec.eudi.mdl_mdoc"

Expand Down
10 changes: 6 additions & 4 deletions Tests/Issuance/IssuanceAuthorizationTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ class IssuanceAuthorizationTest: XCTestCase {
let bindingKey: BindingKey = .jwk(
algorithm: alg,
jwk: publicKeyJWK,
privateKey: privateKey,
privateKey: .secKey(privateKey),
issuer: "218232426"
)

Expand Down Expand Up @@ -452,6 +452,7 @@ class IssuanceAuthorizationTest: XCTestCase {
XCTAssert(false, "Unexpected grant type")
}

/// Change the transaction code with the one obtained https://dev.tester.issuer.eudiw.dev/
let result = await issuer.authorizeWithPreAuthorizationCode(
credentialOffer: offer,
authorizationCode: try .init(
Expand All @@ -477,7 +478,7 @@ class IssuanceAuthorizationTest: XCTestCase {
let bindingKey: BindingKey = .jwk(
algorithm: alg,
jwk: publicKeyJWK,
privateKey: privateKey,
privateKey: .secKey(privateKey),
issuer: "218232426"
)

Expand Down Expand Up @@ -528,6 +529,7 @@ class IssuanceAuthorizationTest: XCTestCase {
let privateKey = try KeyController.generateECDHPrivateKey()
let publicKey = try KeyController.generateECDHPublicKey(from: privateKey)

let privateKeyProxy: SigningKeyProxy = .secKey(privateKey)
let alg = JWSAlgorithm(.ES256)
let jwk = try ECPublicKey(
publicKey: publicKey,
Expand All @@ -540,7 +542,7 @@ class IssuanceAuthorizationTest: XCTestCase {
let dpopConstructor: DPoPConstructor = .init(
algorithm: alg,
jwk: jwk,
privateKey: privateKey
privateKey: privateKeyProxy
)

let offer: CredentialOffer = try resolution.get()
Expand Down Expand Up @@ -576,7 +578,7 @@ class IssuanceAuthorizationTest: XCTestCase {
let bindingKey: BindingKey = .jwk(
algorithm: alg,
jwk: jwk,
privateKey: privateKey,
privateKey: .secKey(privateKey),
issuer: "218232426"
)

Expand Down
Loading