Skip to content

Commit

Permalink
Merge branch 'eu-digital-identity-wallet:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
dtsiflit authored Dec 20, 2023
2 parents 8355172 + a088e75 commit 15bd53a
Show file tree
Hide file tree
Showing 37 changed files with 1,361 additions and 532 deletions.
27 changes: 0 additions & 27 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,6 @@
"version" : "2.4.1"
}
},
{
"identity" : "swift-asn1",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-asn1.git",
"state" : {
"revision" : "c7e239b5c1492ffc3ebd7fbcc7a92548ce4e78f0",
"version" : "1.1.0"
}
},
{
"identity" : "swift-certificates",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-certificates.git",
"state" : {
"revision" : "01d7664523af5c169f26038f1e5d444ce47ae5ff",
"version" : "1.0.1"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "b51f1d6845b353a2121de1c6a670738ec33561a6",
"version" : "3.1.0"
}
},
{
"identity" : "swiftsoup",
"kind" : "remoteSourceControl",
Expand Down
2 changes: 0 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ let package = Package(
.package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),
.package(url: "https://github.com/niscy-eudiw/JOSESwift.git", from: "2.4.1"),
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.6.0"),
.package(url: "https://github.com/apple/swift-asn1.git", .upToNextMajor(from: "1.0.0")),
.package(url: "https://github.com/apple/swift-certificates.git", .upToNextMajor(from: "1.0.0")),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion Sources/Entities/CredentialIssuer/CredentialIssuerId.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct CredentialIssuerId: Codable, Equatable {

guard
let validURL = URL(string: string),
// validURL.scheme == "https",
validURL.scheme == "https",
validURL.fragment == nil
else {
throw CredentialError.genericError
Expand Down
77 changes: 44 additions & 33 deletions Sources/Entities/CredentialIssuer/CredentialIssuerMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@ import JOSESwift

public struct CredentialIssuerMetadata: Codable, Equatable {
let credentialIssuerIdentifier: CredentialIssuerId
let authorizationServer: URL
let authorizationServers: [URL]
let credentialEndpoint: CredentialIssuerEndpoint
let batchCredentialEndpoint: CredentialIssuerEndpoint?
let deferredCredentialEndpoint: CredentialIssuerEndpoint?
let credentialResponseEncryption: CredentialResponseEncryption
let requireCredentialResponseEncryption: Bool
let credentialsSupported: [SupportedCredential]
let credentialsSupported: [CredentialIdentifier: SupportedCredential]

let display: [Display]

enum CodingKeys: String, CodingKey {
case credentialIssuerIdentifier = "credential_issuer"
case authorizationServer = "authorization_server"
case authorizationServers = "authorization_servers"
case credentialEndpoint = "credential_endpoint"
case batchCredentialEndpoint = "batch_credential_endpoint"
case deferredCredentialEndpoint = "deferred_credential_endpoint"
Expand All @@ -43,17 +44,17 @@ public struct CredentialIssuerMetadata: Codable, Equatable {

public init(
credentialIssuerIdentifier: CredentialIssuerId,
authorizationServer: URL,
authorizationServers: [URL],
credentialEndpoint: CredentialIssuerEndpoint,
batchCredentialEndpoint: CredentialIssuerEndpoint?,
deferredCredentialEndpoint: CredentialIssuerEndpoint?,
credentialResponseEncryption: CredentialResponseEncryption = .notRequired,
requireCredentialResponseEncryption: Bool?,
credentialsSupported: [SupportedCredential],
credentialsSupported: [CredentialIdentifier: SupportedCredential],
display: [Display]?
) {
self.credentialIssuerIdentifier = credentialIssuerIdentifier
self.authorizationServer = authorizationServer
self.authorizationServers = authorizationServers
self.credentialEndpoint = credentialEndpoint
self.batchCredentialEndpoint = batchCredentialEndpoint
self.deferredCredentialEndpoint = deferredCredentialEndpoint
Expand All @@ -69,13 +70,13 @@ public struct CredentialIssuerMetadata: Codable, Equatable {

// Decode each property as necessary, handling optionals and conversions.
credentialIssuerIdentifier = try container.decode(CredentialIssuerId.self, forKey: .credentialIssuerIdentifier)
authorizationServer = try container.decode(URL.self, forKey: .authorizationServer)
authorizationServers = try container.decode([URL].self, forKey: .authorizationServers)
credentialEndpoint = try container.decode(CredentialIssuerEndpoint.self, forKey: .credentialEndpoint)
batchCredentialEndpoint = try container.decodeIfPresent(CredentialIssuerEndpoint.self, forKey: .batchCredentialEndpoint)
deferredCredentialEndpoint = try container.decodeIfPresent(CredentialIssuerEndpoint.self, forKey: .deferredCredentialEndpoint)

if let credentialResponseEncryptionAlgorithmsSupported = try container.decodeIfPresent([JWEAlgorithm].self, forKey: .credentialResponseEncryptionAlgorithmsSupported),
let credentialResponseEncryptionMethodsSupported = try container.decodeIfPresent([JOSEEncryptionMethod].self, forKey: .credentialResponseEncryptionMethodsSupported) {
if let credentialResponseEncryptionAlgorithmsSupported = try? container.decodeIfPresent([JWEAlgorithm].self, forKey: .credentialResponseEncryptionAlgorithmsSupported),
let credentialResponseEncryptionMethodsSupported = try? container.decodeIfPresent([JOSEEncryptionMethod].self, forKey: .credentialResponseEncryptionMethodsSupported) {
credentialResponseEncryption = .required(
algorithmsSupported: credentialResponseEncryptionAlgorithmsSupported,
encryptionMethodsSupported: credentialResponseEncryptionMethodsSupported
Expand All @@ -85,31 +86,41 @@ public struct CredentialIssuerMetadata: Codable, Equatable {
}

requireCredentialResponseEncryption = try container.decodeIfPresent(Bool.self, forKey: .requireCredentialResponseEncryption) ?? false
let credentialsSupportedJSON = try container.decodeIfPresent([JSON].self, forKey: .credentialsSupported) ?? []
credentialsSupported = try credentialsSupportedJSON.map { json in
guard let format = json["format"].string else {
throw ValidationError.error(reason: "Profile format not found")
}

switch format {
case MsoMdocProfile.FORMAT:
let profile = try MsoMdocProfile.CredentialSupported(json: json)
return .msoMdoc(profile)
case W3CSignedJwtProfile.FORMAT:
let profile = try W3CSignedJwtProfile.CredentialSupported(json: json)
return .w3CSignedJwt(profile)
case SdJwtVcProfile.FORMAT:
let profile = try SdJwtVcProfile.CredentialSupported(json: json)
return .sdJwtVc(profile)
case W3CJsonLdSignedJwtProfile.FORMAT:
let profile = try W3CJsonLdSignedJwtProfile.CredentialSupported(json: json)
return .w3CJsonLdSignedJwt(profile)
case W3CJsonLdDataIntegrityProfile.FORMAT:
let profile = try W3CJsonLdDataIntegrityProfile.CredentialSupported(json: json)
return .w3CJsonLdDataIntegrity(profile)
default: throw ValidationError.error(reason: "Unknow credential format")

let json = try container.decodeIfPresent(JSON.self, forKey: .credentialsSupported) ?? []
var mapIdentifierCredential: [CredentialIdentifier: SupportedCredential] = [:]
for (key, value): (String, JSON) in json {
if let dictionary = value.dictionary,
let credJson = JSON(rawValue: dictionary) {

let credentialIdentifier: CredentialIdentifier = try .init(value: key)
guard let format = credJson["format"].string else {
throw ValidationError.error(reason: "Profile format not found")
}

switch format {
case MsoMdocFormat.FORMAT:
let profile = try MsoMdocFormat.CredentialSupported(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .msoMdoc(profile)
case W3CSignedJwtFormat.FORMAT:
let profile = try W3CSignedJwtFormat.CredentialSupported(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .w3CSignedJwt(profile)
case SdJwtVcFormat.FORMAT:
let profile = try SdJwtVcFormat.CredentialSupported(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .sdJwtVc(profile)
case W3CJsonLdSignedJwtFormat.FORMAT:
let profile = try W3CJsonLdSignedJwtFormat.CredentialSupported(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .w3CJsonLdSignedJwt(profile)
case W3CJsonLdDataIntegrityFormat.FORMAT:
let profile = try W3CJsonLdDataIntegrityFormat.CredentialSupported(json: credJson)
mapIdentifierCredential[credentialIdentifier] = .w3CJsonLdDataIntegrity(profile)
default: throw ValidationError.error(reason: "Unknow credential format")
}
}
}

credentialsSupported = mapIdentifierCredential

display = try container.decodeIfPresent([Display].self, forKey: .display) ?? []
}

Expand All @@ -119,7 +130,7 @@ public struct CredentialIssuerMetadata: Codable, Equatable {

// Encode each property as necessary, handling optionals and conversions.
try container.encode(credentialIssuerIdentifier, forKey: .credentialIssuerIdentifier)
try container.encode(authorizationServer, forKey: .authorizationServer)
try container.encode(authorizationServers, forKey: .authorizationServers)
try container.encode(credentialEndpoint, forKey: .credentialEndpoint)
try container.encode(batchCredentialEndpoint, forKey: .batchCredentialEndpoint)
try container.encode(deferredCredentialEndpoint, forKey: .deferredCredentialEndpoint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,34 @@ public enum RequestedCredentialResponseEncryption {
)

// Validate algorithm provided is for asymmetric encryption
private static func validateAlgorithm(_ responseEncryptionAlg: JWEAlgorithm) throws {
guard JWEAlgorithm.Family.parse(.ASYMMETRIC).contains(responseEncryptionAlg) else {
private static func validateAlgorithm(_ responseEncryptionAlg: JWEAlgorithm?) throws {
guard
let responseEncryptionAlg = responseEncryptionAlg,
JWEAlgorithm.Family.parse(.ASYMMETRIC).contains(responseEncryptionAlg)
else {
throw ValidationError.error(reason: "Provided encryption algorithm is not an asymmetric encryption algorithm")
}
}

// Validate algorithm matches key
private static func validateKeyAlgorithmMatch(_ encryptionJwk: JWK, _ responseEncryptionAlg: JWEAlgorithm) throws {
// guard encryptionJwk.keyType == KeyType.forAlgorithm(responseEncryptionAlg) else {
// throw ValidationError.error(reason: "Encryption key and encryption algorithm do not match")
// }
private static func validateKeyAlgorithmMatch(_ encryptionJwk: JWK?, _ responseEncryptionAlg: JWEAlgorithm?) throws {
/*guard encryptionJwk.keyType == KeyType.forAlgorithm(responseEncryptionAlg) else {
throw ValidationError.error(reason: "Encryption key and encryption algorithm do not match")
}*/
}

// Validate key is for encryption operation
private static func validateKeyUse(_ encryptionJwk: JWK) throws {
// guard encryptionJwk.keyUse == KeyUse.encryption else {
// throw ValidationError.error(reason: "Provided key use is not encryption")
// }
private static func validateKeyUse(_ encryptionJwk: JWK?) throws {
guard encryptionJwk?.parameters["use"] == "enc" else {
throw ValidationError.error(reason: "Provided key use is not encryption")
}
}

// Validate the requested encryption parameters
private static func validate(
encryptionJwk: JWK,
responseEncryptionAlg: JWEAlgorithm,
responseEncryptionMethod: JOSEEncryptionMethod
encryptionJwk: JWK?,
responseEncryptionAlg: JWEAlgorithm?,
responseEncryptionMethod: JOSEEncryptionMethod?
) throws {
try validateAlgorithm(responseEncryptionAlg)
try validateKeyAlgorithmMatch(encryptionJwk, responseEncryptionAlg)
Expand All @@ -72,7 +75,11 @@ public enum RequestedCredentialResponseEncryption {
responseEncryptionAlg: JWEAlgorithm?,
responseEncryptionMethod: JOSEEncryptionMethod?
) throws {
//try Self.validate(encryptionJwk: encryptionJwk, responseEncryptionAlg: responseEncryptionAlg, responseEncryptionMethod: responseEncryptionMethod)
try Self.validate(
encryptionJwk: encryptionJwk,
responseEncryptionAlg: responseEncryptionAlg,
responseEncryptionMethod: responseEncryptionMethod
)

guard
let encryptionJwk,
Expand All @@ -92,4 +99,3 @@ public enum RequestedCredentialResponseEncryption {
)
}
}

10 changes: 5 additions & 5 deletions Sources/Entities/CredentialMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import Foundation

public enum CredentialMetadata {
case scope(Scope)
case msoMdoc(MsoMdocProfile)
case w3CSignedJwt(W3CSignedJwtProfile)
case w3CJsonLdSignedJwt(W3CJsonLdSignedJwtProfile)
case w3CJsonLdDataIntegrity(W3CJsonLdDataIntegrityProfile)
case sdJwtVc(SdJwtVcProfile)
case msoMdoc(MsoMdocFormat)
case w3CSignedJwt(W3CSignedJwtFormat)
case w3CJsonLdSignedJwt(W3CJsonLdSignedJwtFormat)
case w3CJsonLdDataIntegrity(W3CJsonLdDataIntegrityFormat)
case sdJwtVc(SdJwtVcFormat)
}
60 changes: 53 additions & 7 deletions Sources/Entities/CredentialSupported/SupportedCredential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,32 @@ import Foundation

public enum SupportedCredential: Codable {
case scope(Scope)
case msoMdoc(MsoMdocProfile.CredentialSupported)
case w3CSignedJwt(W3CSignedJwtProfile.CredentialSupported)
case w3CJsonLdSignedJwt(W3CJsonLdSignedJwtProfile.CredentialSupported)
case w3CJsonLdDataIntegrity(W3CJsonLdDataIntegrityProfile.CredentialSupported)
case sdJwtVc(SdJwtVcProfile.CredentialSupported)
case msoMdoc(MsoMdocFormat.CredentialSupported)
case w3CSignedJwt(W3CSignedJwtFormat.CredentialSupported)
case w3CJsonLdSignedJwt(W3CJsonLdSignedJwtFormat.CredentialSupported)
case w3CJsonLdDataIntegrity(W3CJsonLdDataIntegrityFormat.CredentialSupported)
case sdJwtVc(SdJwtVcFormat.CredentialSupported)
}

public extension SupportedCredential {

func getScope() -> String? {
switch self {
case .scope(let scope):
return scope.value
case .msoMdoc(let credential):
return credential.scope
case .w3CSignedJwt(let credential):
return credential.scope
case .w3CJsonLdSignedJwt(let credential):
return credential.scope
case .w3CJsonLdDataIntegrity(let credential):
return credential.scope
case .sdJwtVc(let credential):
return credential.scope
}
}

func toIssuanceRequest(
requester: IssuanceRequesterType,
claimSet: ClaimSet?,
Expand All @@ -36,17 +54,45 @@ public extension SupportedCredential {
if let proof,
let proofTypesSupported = credentialSupported.proofTypesSupported,
proofTypesSupported.contains(proof.type()) {

if !proofTypesSupported.contains(proof.type()) {
throw ValidationError.error(reason: "Provided proof type \(proof.type()) is not one of supported [\(proofTypesSupported)].")
}
}

let issuerEncryption = requester.issuerMetadata.credentialResponseEncryption
let responseEncryptionSpec = responseEncryptionSpecProvider(issuerEncryption)

if let responseEncryptionSpec {
switch issuerEncryption {
case .notRequired:
throw CredentialIssuanceError.issuerDoesNotSupportEncryptedResponses
case .required(
let algorithmsSupported,
let encryptionMethodsSupported
):
if !algorithmsSupported.contains(responseEncryptionSpec.algorithm) {
throw CredentialIssuanceError.responseEncryptionAlgorithmNotSupportedByIssuer
}

if !encryptionMethodsSupported.contains(responseEncryptionSpec.encryptionMethod) {
throw CredentialIssuanceError.responseEncryptionMethodNotSupportedByIssuer
}
}
}

return try credentialSupported.toIssuanceRequest(
responseEncryptionSpec: responseEncryptionSpec,
claimSet: claimSet,
proof: proof
)

case .sdJwtVc(let credentialSupported):
if let proof,
let proofTypesSupported = credentialSupported.proofTypesSupported,
proofTypesSupported.contains(proof.type()) {

if !proofTypesSupported.contains(proof.type()) {
throw ValidationError.error(reason: "Provided proof type \(proof.type()) is not one of supported [\(proofTypesSupported)].")
}
}

let issuerEncryption = requester.issuerMetadata.credentialResponseEncryption
Expand Down
Loading

0 comments on commit 15bd53a

Please sign in to comment.