Skip to content

Commit e8af7e7

Browse files
authored
Merge pull request #99 from niscy-eudiw/feature/wallet-metadat-post-updates
Implement JAR encryption
2 parents 679a14c + fe8a460 commit e8af7e7

19 files changed

+647
-144
lines changed

Package.resolved

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/Entities/AuthorisationRequest/AuthorizationError.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ public enum AuthorizationError: LocalizedError {
5959
/// Non dispatchable error.
6060
case nonDispatchableError
6161

62+
/// JWT decryption failed.
63+
case jwtDecryptionFailed
64+
65+
/// Invalid algorithms.
66+
case invalidAlgorithms
67+
6268
/// A localized description of the error.
6369
public var errorDescription: String? {
6470
switch self {
@@ -90,6 +96,10 @@ public enum AuthorizationError: LocalizedError {
9096
return ".invalidTransactionData"
9197
case .nonDispatchableError:
9298
return ".nonDispatchableError"
99+
case .jwtDecryptionFailed:
100+
return ".jwtDecryptionFailed"
101+
case .invalidAlgorithms:
102+
return ".invalidAlgorithms"
93103
}
94104
}
95105
}

Sources/Entities/ClientMetaData/SupportedClientIdScheme.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@ public enum SupportedClientIdScheme {
4545
* The Client Identifier is known to the Wallet in advance of the Authorization Request.
4646
*/
4747
case .preregistered:
48-
return "preRegistered"
48+
return "pre-registered"
4949
case .x509SanUri:
50-
return "x509SanUri"
50+
return "x509_san_url"
5151
case .x509SanDns:
52-
return "x509SanDns"
52+
return "x509_san_dns"
5353
case .did:
5454
return "did"
5555
case .verifierAttestation:
56-
return "verifierAttestation"
56+
return "verifier_attestation"
5757
case .redirectUri:
58-
return "redirectUri"
58+
return "redirect_uri"
5959
}
6060
}
6161

Sources/Entities/Types/Configuration/VpFormats.swift

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public struct VpFormats: Equatable {
192192
}
193193

194194
let vpFormatsDictionary: JSON = JSON(dictionaryObject)[Self.vpFormats]
195-
if let formats = try? vpFormatsDictionary.decoded(as: [VpFormatsTO].self) {
195+
if let formats = try? vpFormatsDictionary.decoded(as: VpFormatsTO.self) {
196196
try? self.init(from: formats)
197197
} else {
198198
return nil
@@ -332,14 +332,18 @@ public extension VpFormats {
332332

333333
// Convert VpFormats to JSON
334334
func toJSON() -> JSON {
335-
var jsonArray: [JSON] = []
335+
var mergedFormats: [String: JSON] = [:]
336336

337337
for format in values {
338338
let jsonFormat = format.toJSON()
339-
jsonArray.append(jsonFormat)
339+
340+
// Assuming jsonFormat is [String: JSON] — merge it in
341+
for (key, value) in jsonFormat {
342+
mergedFormats[key] = value
343+
}
340344
}
341345

342-
return JSON([Self.vpFormats: jsonArray])
346+
return JSON([Self.vpFormats: mergedFormats])
343347
}
344348
}
345349

@@ -348,13 +352,16 @@ extension VpFormat {
348352
func toJSON() -> JSON {
349353
switch self {
350354
case .sdJwtVc(let sdJwtAlgorithms, let kbJwtAlgorithms):
351-
return JSON(["sdJwtVc": [
355+
return JSON(["vc+sd-jwt": [
352356
"sd-jwt_alg_values": sdJwtAlgorithms.map { $0.name },
353357
"kb-jwt_alg_values": kbJwtAlgorithms.map { $0.name }
354358
]]
355359
)
356-
case .msoMdoc:
357-
return JSON(["msoMdoc": JSON()])
360+
case .msoMdoc(let algorithms):
361+
return JSON(["mso_mdoc": [
362+
"alg": algorithms.map { $0.name }
363+
]
364+
])
358365

359366
case .jwtVp(let algorithms):
360367
return JSON([
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2023 European Commission
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import Foundation
17+
import JOSESwift
18+
19+
public struct EncryptionRequirementSpecification: Equatable {
20+
public let supportedEncryptionAlgorithm: KeyManagementAlgorithm
21+
public let supportedEncryptionMethod: ContentEncryptionAlgorithm
22+
public let ephemeralEncryptionKeyCurve: ECCurveType
23+
24+
public init(
25+
supportedEncryptionAlgorithm: KeyManagementAlgorithm = .ECDH_ES,
26+
supportedEncryptionMethod: ContentEncryptionAlgorithm = .A128CBCHS256,
27+
ephemeralEncryptionKeyCurve: ECCurveType = .P256
28+
) throws {
29+
self.supportedEncryptionAlgorithm = supportedEncryptionAlgorithm
30+
self.supportedEncryptionMethod = supportedEncryptionMethod
31+
self.ephemeralEncryptionKeyCurve = ephemeralEncryptionKeyCurve
32+
33+
if supportedEncryptionAlgorithm != .ECDH_ES {
34+
throw ValidationError.validationError("Unsupported encryption algorithm \(supportedEncryptionAlgorithm.rawValue)")
35+
}
36+
37+
if supportedEncryptionMethod != .A128CBCHS256 {
38+
throw ValidationError.validationError("Unsupported encryption method \(supportedEncryptionMethod.rawValue)")
39+
}
40+
41+
if ephemeralEncryptionKeyCurve != .P256 {
42+
throw ValidationError.validationError("Unsupported ephemeral encryption key curve \(ephemeralEncryptionKeyCurve.rawValue)")
43+
}
44+
}
45+
}
46+
47+
public enum EncryptionRequirement: Equatable {
48+
/**
49+
* Encryption is not required.
50+
*/
51+
case notRequired
52+
53+
/**
54+
* Encryption is required.
55+
*
56+
* @property EncryptionRequirementSpecification: encryption algorithms supported by the Wallet, only asymmetric
57+
* KeyManagementAlgorithm are supported, supportedEncryptionMethods encryption methods supported by the Wallet,
58+
* the [ECCurveType] to use for generating the ephemeral encryption key
59+
*/
60+
case required(
61+
encryptionRequirementSpecification: EncryptionRequirementSpecification
62+
)
63+
64+
public var isNotRequired: Bool {
65+
return switch self {
66+
case .notRequired: true
67+
case .required: false
68+
}
69+
}
70+
}

Sources/Entities/Types/SupportedRequestUriMethod.swift

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,57 @@
1515
*/
1616
import Foundation
1717

18+
public struct PostOptions {
19+
public let includeWalletMetadata: Bool
20+
public let useWalletNonce: NonceOption
21+
public let jarEncryption: EncryptionRequirement
22+
23+
public init(
24+
includeWalletMetadata: Bool = false,
25+
useWalletNonce: NonceOption = .doNotUse,
26+
jarEncryption: EncryptionRequirement = .notRequired
27+
) throws {
28+
self.includeWalletMetadata = includeWalletMetadata
29+
self.useWalletNonce = useWalletNonce
30+
self.jarEncryption = jarEncryption
31+
32+
if jarEncryption != .notRequired && includeWalletMetadata == false {
33+
throw ValidationError.validationError(
34+
"Wallet Metadata must be included when JAR encryption is required"
35+
)
36+
}
37+
}
38+
}
39+
1840
public enum SupportedRequestUriMethod {
1941
case get
2042
case post(
2143
postOptions: PostOptions
2244
)
2345
case both(
24-
post: PostOptions
46+
postOptions: PostOptions
2547
)
2648

27-
public init?(method: String, includeWalletMetadata: Bool = true, useWalletNonce: NonceOption = .use(byteLength: 32)) {
49+
public init?(
50+
method: String,
51+
includeWalletMetadata: Bool = true,
52+
useWalletNonce: NonceOption = .use(byteLength: 32)
53+
) throws {
2854
switch method.uppercased() {
2955
case "GET":
3056
self = .get
3157
case "POST":
32-
self = .post(postOptions: .init(
58+
guard let options: PostOptions = try? .init(
3359
includeWalletMetadata: includeWalletMetadata,
3460
useWalletNonce: useWalletNonce
35-
))
61+
) else {
62+
return nil
63+
}
64+
self = .post(
65+
postOptions:options
66+
)
3667
default:
37-
return nil // Invalid input returns nil
38-
}
39-
}
40-
41-
public struct PostOptions {
42-
public let includeWalletMetadata: Bool
43-
public let useWalletNonce: NonceOption
44-
45-
public init(includeWalletMetadata: Bool, useWalletNonce: NonceOption) {
46-
self.includeWalletMetadata = includeWalletMetadata
47-
self.useWalletNonce = useWalletNonce
68+
return nil
4869
}
4970
}
5071

@@ -63,18 +84,32 @@ public enum SupportedRequestUriMethod {
6384
switch self {
6485
case .both(let postOptions):
6586
return postOptions
66-
case .post(let options):
67-
return options
87+
case .post(let postOptions):
88+
return postOptions
6889
case .get:
6990
return nil
7091
}
7192
}
7293

73-
/// Default instance
74-
public static let defaultOption: SupportedRequestUriMethod = .both(
75-
post: PostOptions(
94+
public static let encryptionOption: SupportedRequestUriMethod = .both(
95+
postOptions: try! PostOptions(
96+
includeWalletMetadata: true,
97+
useWalletNonce: NonceOption.use(byteLength: 32),
98+
jarEncryption: .required(
99+
encryptionRequirementSpecification: .init(
100+
supportedEncryptionAlgorithm: .ECDH_ES,
101+
supportedEncryptionMethod: .A128CBCHS256,
102+
ephemeralEncryptionKeyCurve: .P256
103+
)
104+
)
105+
)
106+
)
107+
108+
public static let noEncryptionOption: SupportedRequestUriMethod = .both(
109+
postOptions: try! PostOptions(
76110
includeWalletMetadata: true,
77-
useWalletNonce: NonceOption.use(byteLength: 32)
111+
useWalletNonce: NonceOption.use(byteLength: 32),
112+
jarEncryption: .notRequired
78113
)
79114
)
80115
}

0 commit comments

Comments
 (0)