Skip to content

Commit 6a014fb

Browse files
authored
Merge branch 'EWC-consortium:main' into main
2 parents 7be9a3d + a0c8d68 commit 6a014fb

17 files changed

+833
-202
lines changed

Sources/eudiWalletOidcIos/Helper/EncryptionKeyHandler/Crypto Kit/CryptoKitHandler.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,26 @@ import CryptoKit
1111
public class CryptoKitHandler:NSObject, SecureKeyProtocol{
1212

1313
public var keyStorageType: SecureKeyTypes = .cryptoKit
14+
var secureKeyData: SecureKeyData? = nil
1415

15-
public func generateSecureKey() -> SecureKeyData?{
16-
let privateKey = P256.Signing.PrivateKey()
17-
return SecureKeyData(publicKey: privateKey.publicKey.rawRepresentation, privateKey: privateKey.rawRepresentation)
16+
public init(secureKeyData: SecureKeyData? = nil) {
17+
super.init()
18+
self.secureKeyData = secureKeyData
19+
self.keyStorageType = .cryptoKit
1820
}
1921

20-
22+
public func generateSecureKey() -> SecureKeyData?{
23+
if let keys = secureKeyData{
24+
return keys
25+
} else {
26+
let privateKey = P256.Signing.PrivateKey()
27+
return SecureKeyData(publicKey: privateKey.publicKey.rawRepresentation, privateKey: privateKey.rawRepresentation)
28+
}
29+
}
30+
2131
public func sign(payload: String, header: Data, withKey privateKey: Data?) -> String?{
2232
if let privateKeyData = privateKey{
2333
do{
24-
2534
let payloadData = Data(payload.utf8)
2635
let unsignedToken = "\(header.base64URLEncodedString()).\(payloadData.base64URLEncodedString())"
2736
if let data = unsignedToken.data(using: .utf8){
@@ -30,7 +39,6 @@ public class CryptoKitHandler:NSObject, SecureKeyProtocol{
3039
let idToken = "\(unsignedToken).\(signedData.rawRepresentation.base64URLEncodedString())"
3140
return idToken
3241
}
33-
3442
}
3543
catch{
3644
return nil

Sources/eudiWalletOidcIos/Helper/EncryptionKeyHandler/EDDSA/EDDSAKeyHandler.swift

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,26 @@ import Crypto
1111
public class EDDSAKeyHandler:NSObject, SecureKeyProtocol{
1212

1313
public var keyStorageType: SecureKeyTypes = .eddsa
14+
var secureKeyData: SecureKeyData? = nil
15+
16+
public init(secureKeyData: SecureKeyData? = nil) {
17+
super.init()
18+
self.secureKeyData = secureKeyData
19+
self.keyStorageType = .eddsa
20+
}
1421

1522
public func generateSecureKey() -> SecureKeyData?{
16-
let privateKey = Curve25519.Signing.PrivateKey()
17-
return SecureKeyData(publicKey: privateKey.publicKey.rawRepresentation, privateKey: privateKey.rawRepresentation)
23+
if let keys = secureKeyData{
24+
return keys
25+
} else {
26+
let privateKey = Curve25519.Signing.PrivateKey()
27+
return SecureKeyData(publicKey: privateKey.publicKey.rawRepresentation, privateKey: privateKey.rawRepresentation)
28+
}
1829
}
1930

20-
2131
public func sign(payload: String, header: Data, withKey privateKey: Data?) -> String?{
2232
if let privateKeyData = privateKey{
2333
do{
24-
2534
let payloadData = Data(payload.utf8)
2635
let unsignedToken = "\(header.base64URLEncodedString()).\(payloadData.base64URLEncodedString())"
2736
if let data = unsignedToken.data(using: .utf8){
@@ -30,7 +39,6 @@ public class EDDSAKeyHandler:NSObject, SecureKeyProtocol{
3039
let idToken = "\(unsignedToken).\(signedData.urlSafeBase64EncodedString()))"
3140
return idToken
3241
}
33-
3442
}
3543
catch{
3644
return nil

Sources/eudiWalletOidcIos/Helper/EncryptionKeyHandler/Secure Enclave/SecureEnclaveHandler.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,21 @@ public class SecureEnclaveHandler: NSObject, SecureKeyProtocol{
1111

1212
var privateKeyLabel = ""
1313
var publicKeyLabel = ""
14-
var organisationID = ""
14+
var keyID = ""
1515
var secureEnclaveHandler: SecureEnclave?
1616
public var keyStorageType: SecureKeyTypes = .cryptoKit
1717

18-
public init(organisationID: String) {
18+
public init(keyID: String) {
1919
super.init()
20-
self.organisationID = organisationID
20+
self.keyID = keyID
2121
self.keyStorageType = .secureEnclave
2222
}
2323

2424
//Creating secure enclave instance with unique identifer for the keys
2525
private func createSecureEnclaveHandlerFor() -> Bool{
26-
if !organisationID.isEmpty{
26+
if !keyID.isEmpty{
2727
secureEnclaveHandler = nil
28-
privateKeyLabel = "com.EudiWallet.\(organisationID).PrivateKey"
28+
privateKeyLabel = "com.EudiWallet.\(keyID).PrivateKey"
2929
secureEnclaveHandler = SecureEnclave(privateKeyApplicationTag: privateKeyLabel)
3030
return true
3131
} else{

Sources/eudiWalletOidcIos/Model/PresentationDefinitionModel.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public struct Field: Codable {
4747
public struct Filter: Codable {
4848
public let type: String?
4949
public let contains: Contains?
50+
public let const: String?
5051
public let pattern: String?
5152
}
5253
// MARK: - Contains

Sources/eudiWalletOidcIos/Model/PresentationRequest.swift

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public struct PresentationRequest: Codable {
1212
public var clientMetaData: String?
1313
public var presentationDefinitionUri: String?
1414
public var clientMetaDataUri: String?
15+
public var transactionData: [String]?
1516
enum CodingKeys: String, CodingKey {
1617
case state = "state"
1718
case clientId = "client_id"
@@ -27,8 +28,9 @@ public struct PresentationRequest: Codable {
2728
case clientMetaData = "client_metadata"
2829
case presentationDefinitionUri = "presentation_definition_uri"
2930
case clientMetaDataUri = "client_metadata_uri"
31+
case transactionData = "transaction_data"
3032
}
31-
public init(state: String?, clientId: String?, redirectUri: String?, responseUri: String?, responseType: String?, responseMode: String?, scope: String?, nonce: String?, requestUri: String?, presentationDefinition: String?, clientMetaData: String?, presentationDefinitionUri: String?, clientMetaDataUri: String?, clientIDScheme: String?) {
33+
public init(state: String?, clientId: String?, redirectUri: String?, responseUri: String?, responseType: String?, responseMode: String?, scope: String?, nonce: String?, requestUri: String?, presentationDefinition: String?, clientMetaData: String?, presentationDefinitionUri: String?, clientMetaDataUri: String?, clientIDScheme: String?, transactionData: [String]) {
3234
self.state = state
3335
self.clientId = clientId
3436
self.redirectUri = redirectUri
@@ -43,6 +45,7 @@ public struct PresentationRequest: Codable {
4345
self.presentationDefinitionUri = presentationDefinitionUri
4446
self.clientMetaDataUri = clientMetaDataUri
4547
self.clientIDScheme = clientIDScheme
48+
self.transactionData = transactionData
4649
}
4750

4851
public init(from decoder: Decoder) throws {
@@ -65,11 +68,15 @@ public struct PresentationRequest: Codable {
6568
} else {
6669
presentationDefinition = nil
6770
}
68-
if let clientMetaDataModel = try? container.decode(ClientMetaData.self, forKey: . clientMetaData) {
71+
if let clientMetaDataModel = try? container.decode(ClientMetaData.self, forKey: .clientMetaData) {
6972
clientMetaData = clientMetaDataModel.toJSONString()
7073
}
7174
presentationDefinitionUri = try container.decodeIfPresent(String.self, forKey: .presentationDefinitionUri)
7275
clientMetaDataUri = try container.decodeIfPresent(String.self, forKey: .clientMetaDataUri)
76+
transactionData = try container.decodeIfPresent([String].self, forKey: .transactionData)
77+
// if let transactionDataModel = try? container.decode(TransactionData.self, forKey: .transactionData) {
78+
// transactionData = transactionDataModel.toJSONString()
79+
// }
7380
}
7481
}
7582
public struct ClientMetaData: Codable {
@@ -99,3 +106,64 @@ public struct ClientMetaData: Codable {
99106
}
100107

101108
}
109+
public struct TransactionData: Codable {
110+
public var type: String?
111+
public var credentialIDs: [String]?
112+
public var paymentData: PaymentData?
113+
enum CodingKeys: String, CodingKey {
114+
case type = "type"
115+
case credentialIDs = "credential_ids"
116+
case paymentData = "payment_data"
117+
}
118+
public init(type: String?, credentialIDs: [String]?, paymentData: PaymentData?) {
119+
self.type = type
120+
self.credentialIDs = credentialIDs
121+
self.paymentData = paymentData
122+
}
123+
124+
public init(from decoder: Decoder) throws {
125+
let container = try decoder.container(keyedBy: CodingKeys.self)
126+
type = try container.decodeIfPresent(String.self, forKey: .type)
127+
credentialIDs = try container.decodeIfPresent([String].self, forKey: .credentialIDs)
128+
paymentData = try container.decodeIfPresent(PaymentData.self, forKey: .paymentData)
129+
}
130+
131+
}
132+
public struct PaymentData: Codable {
133+
public var payee: String?
134+
public var currencyAmount: CurrencyAmount?
135+
136+
enum CodingKeys: String, CodingKey {
137+
case payee = "payee"
138+
case currencyAmount = "currency_amount"
139+
}
140+
public init(payee: String?, currencyAmount: CurrencyAmount?) {
141+
self.payee = payee
142+
self.currencyAmount = currencyAmount
143+
}
144+
145+
public init(from decoder: Decoder) throws {
146+
let container = try decoder.container(keyedBy: CodingKeys.self)
147+
payee = try container.decodeIfPresent(String.self, forKey: .payee)
148+
currencyAmount = try container.decodeIfPresent(CurrencyAmount.self, forKey: .currencyAmount)
149+
}
150+
}
151+
public struct CurrencyAmount: Codable {
152+
public var currency: String?
153+
public var value: Double?
154+
155+
enum CodingKeys: String, CodingKey {
156+
case currency = "currency"
157+
case value = "value"
158+
}
159+
public init(currency: String?, value: Double?) {
160+
self.currency = currency
161+
self.value = value
162+
}
163+
164+
public init(from decoder: Decoder) throws {
165+
let container = try decoder.container(keyedBy: CodingKeys.self)
166+
currency = try container.decodeIfPresent(String.self, forKey: .currency)
167+
value = try container.decodeIfPresent(Double.self, forKey: .value)
168+
}
169+
}

Sources/eudiWalletOidcIos/Service/CredentialValidation/CredentialValidatorService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class CredentialValidatorService: CredentialValidaorProtocol {
1616
public init() {}
1717

1818
public func validateCredential(jwt: String?, jwksURI: String?, format: String) async throws {
19-
let isJWTExpired = ExpiryValidator.validateExpiryDate(jwt: jwt, format: format) ?? false
19+
let isJWTExpired = ExpiryValidator().validateExpiryDate(jwt: jwt, format: format) ?? false
2020
let isSignatureExpied = try await SignatureValidator.validateSign(jwt: jwt, jwksURI: jwksURI, format: format) ?? false
2121
if isJWTExpired {
2222
throw ValidationError.JWTExpired

Sources/eudiWalletOidcIos/Service/CredentialValidation/ExpiryValidator.swift

Lines changed: 59 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,29 @@
44
//
55
// Created by iGrant on 25/07/24.
66
//
7-
87
import Foundation
98
import SwiftCBOR
10-
11-
12-
class ExpiryValidator {
13-
14-
static func validateExpiryDate(jwt: String?, format: String) -> Bool? {
15-
var expirationDate: String = ""
16-
if format == "mso_mdoc" {
17-
if let issuerAuthData = getIssuerAuth(credential: jwt ?? "") {
18-
expirationDate = getExpiryFromIssuerAuth(cborData: issuerAuthData) ?? ""
9+
public class ExpiryValidator {
10+
11+
public init () {}
12+
13+
public func validateExpiryDate(jwt: String?, format: String) -> Bool? {
14+
var expirationDate: String = ""
15+
if format == "mso_mdoc" {
16+
if let issuerAuthData = ExpiryValidator.getIssuerAuth(credential: jwt ?? "") {
17+
expirationDate = ExpiryValidator.getExpiryFromIssuerAuth(cborData: issuerAuthData) ?? ""
18+
} else {
19+
return false
20+
}
1921
} else {
20-
return false
22+
guard let split = jwt?.split(separator: "."), split.count > 1, let jsonString = "\(split[1])".decodeBase64(),
23+
let jsonObject = UIApplicationUtils.shared.convertStringToDictionary(text: jsonString) else { return false }
24+
guard let vc = jsonObject["vc"] as? [String: Any], let expiryDate = vc["expirationDate"] as? String else { return false }
25+
expirationDate = expiryDate
2126
}
22-
} else {
23-
24-
guard let split = jwt?.split(separator: "."), split.count > 1, let jsonString = "\(split[1])".decodeBase64(),
25-
let jsonObject = UIApplicationUtils.shared.convertStringToDictionary(text: jsonString) else { return false }
26-
guard let vc = jsonObject["vc"] as? [String: Any], let expiryDate = vc["expirationDate"] as? String else { return false }
27-
expirationDate = expiryDate
28-
}
29-
let dateFormatter = DateFormatter()
27+
let dateFormatter = DateFormatter()
3028
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
31-
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
29+
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
3230
guard let expiryDate = dateFormatter.date(from: expirationDate) else { return false}
3331
let currentDate = Date()
3432
if currentDate <= expiryDate {
@@ -37,9 +35,7 @@ class ExpiryValidator {
3735
return true
3836
}
3937
}
40-
41-
42-
static func getIssuerAuth(credential: String) -> CBOR? {
38+
static func getIssuerAuth(credential: String) -> CBOR? {
4339
// Convert the base64 URL encoded credential to Data
4440
if let data = Data(base64URLEncoded: credential) {
4541
do {
@@ -63,60 +59,57 @@ static func getIssuerAuth(credential: String) -> CBOR? {
6359

6460
return nil // Return nil if "issuerAuth" is not found
6561
}
66-
67-
68-
static func getExpiryFromIssuerAuth(cborData: CBOR) -> String? {
69-
guard case let CBOR.array(elements) = cborData else {
70-
print("Expected CBOR array, but got something else.")
71-
return nil
72-
}
73-
var docType: String? = ""
74-
for element in elements {
75-
if case let CBOR.byteString(byteString) = element {
76-
if let nestedCBOR = try? CBOR.decode(byteString) {
77-
if case let CBOR.tagged(tag, item) = nestedCBOR, tag.rawValue == 24 {
78-
if case let CBOR.byteString(data) = item {
79-
if let decodedInnerCBOR = try? CBOR.decode([UInt8](data)) {
80-
docType = extractExpiry(cborData: decodedInnerCBOR )
62+
static func getExpiryFromIssuerAuth(cborData: CBOR) -> String? {
63+
guard case let CBOR.array(elements) = cborData else {
64+
print("Expected CBOR array, but got something else.")
65+
return nil
66+
}
67+
var docType: String? = ""
68+
for element in elements {
69+
if case let CBOR.byteString(byteString) = element {
70+
if let nestedCBOR = try? CBOR.decode(byteString) {
71+
if case let CBOR.tagged(tag, item) = nestedCBOR, tag.rawValue == 24 {
72+
if case let CBOR.byteString(data) = item {
73+
if let decodedInnerCBOR = try? CBOR.decode([UInt8](data)) {
74+
docType = extractExpiry(cborData: decodedInnerCBOR )
75+
} else {
76+
print("Failed to decode inner ByteString under Tag 24.")
77+
}
78+
}
79+
}
8180
} else {
82-
print("Failed to decode inner ByteString under Tag 24.")
81+
print("Could not decode ByteString as CBOR, inspecting data directly.")
82+
print("ByteString data: \(byteString)")
8383
}
84-
}
85-
}
8684
} else {
87-
print("Could not decode ByteString as CBOR, inspecting data directly.")
88-
print("ByteString data: \(byteString)")
85+
print("Element: \(element)")
8986
}
90-
} else {
91-
print("Element: \(element)")
9287
}
88+
return docType ?? ""
9389
}
94-
return docType ?? ""
95-
}
96-
97-
static func extractExpiry(cborData: CBOR) -> String? {
98-
guard case let CBOR.map(map) = cborData else {
99-
return nil
100-
}
101-
for (key, value) in map {
102-
if case let CBOR.utf8String(keyString) = key, keyString == "validityInfo" {
103-
if case let CBOR.map(validityMap) = value {
104-
for (validityKey, validityValue) in validityMap {
105-
if case let CBOR.utf8String(validityKeyString) = validityKey, validityKeyString == "validUntil" {
106-
if case let CBOR.tagged(_, CBOR.utf8String(validUntilString)) = validityValue {
107-
return validUntilString
108-
} else {
109-
print("The value associated with 'validUntil' is not in the expected format.")
90+
static func extractExpiry(cborData: CBOR) -> String? {
91+
guard case let CBOR.map(map) = cborData else {
92+
return nil
93+
}
94+
for (key, value) in map {
95+
if case let CBOR.utf8String(keyString) = key, keyString == "validityInfo" {
96+
if case let CBOR.map(validityMap) = value {
97+
for (validityKey, validityValue) in validityMap {
98+
if case let CBOR.utf8String(validityKeyString) = validityKey, validityKeyString == "validUntil" {
99+
if case let CBOR.tagged(_, CBOR.utf8String(validUntilString)) = validityValue {
100+
return validUntilString
101+
} else {
102+
print("The value associated with 'validUntil' is not in the expected format.")
103+
}
110104
}
111105
}
106+
} else {
107+
print("The value associated with 'validityInfo' is not a map.")
112108
}
113-
} else {
114-
print("The value associated with 'validityInfo' is not a map.")
115109
}
116110
}
111+
print("validityInfo not found in the CBOR map.")
112+
return nil
117113
}
118-
print("validityInfo not found in the CBOR map.")
119-
return nil
120-
}
121114

122115
}

0 commit comments

Comments
 (0)