From 8f968e2fbd88fe386cfa0a8b8014702ce831caee Mon Sep 17 00:00:00 2001 From: goncalo-frade-iohk Date: Wed, 14 Feb 2024 22:28:08 +0000 Subject: [PATCH] feat!: move to swift based framework for didcomm, jose and peer did BREAKING CHANGE: Updated Pluto and Message public interface This changes are required to provide the following. - the old didcomm library didnt allow for extra headers that was essencial to prism mediator - this peer did library is updated with last specifications specs required by the agent and prism mediator - this jose library provides a full implementation of the jose capabilities we require The amount of changes is big because the peer did changes required a few changes on how we process keys and resolve secrets Besides that since the previous libraries were not build in swift, we are actually getting rid of 37MB of framework size --- .../Sources/ApolloImpl+KeyRestoration.swift | 32 +- .../Apollo/Sources/Model/Ed25519Key.swift | 14 +- .../Apollo/Sources/Model/LinkSecret.swift | 1 + .../Model/Secp256k1Key+Exportable.swift | 16 +- .../Apollo/Sources/Model/Secp256k1Key.swift | 19 +- .../Apollo/Sources/Model/X25519Key.swift | 14 +- .../CreateEd25519KeyPairOperation.swift | 7 +- .../CreateX25519KeyPairOperation.swift | 7 +- .../Castor/Sources/CastorImpl+Public.swift | 18 - .../Castor/Sources/DID/PeerDID/PeerDID.swift | 65 ---- .../Castor/Sources/DID/PeerDID/Types.swift | 66 ---- .../Castor/Sources/Helpers/JWK+Helper.swift | 45 --- .../Operations/CreatePeerDIDOperation.swift | 187 +++------- .../Sources/Resolvers/PeerDIDResolver.swift | 326 +++++++++--------- .../Tests/EncumbasisEncodeDecodeTests.swift | 31 -- .../Castor/Tests/PeerDIDCreationTests.swift | 4 +- AtalaPrismSDK/Domain/Sources/BBs/Castor.swift | 23 -- AtalaPrismSDK/Domain/Sources/BBs/Pluto.swift | 9 + .../Domain/Sources/Models/DIDDocument.swift | 16 + .../Sources/Models/KeyManagement/Keys.swift | 2 + .../Models/KeyManagement/StorableKey.swift | 3 + .../Sources/Models/Message+Codable.swift | 32 +- .../Domain/Sources/Models/Message.swift | 4 +- .../Sources/Models/MessageAttachment.swift | 12 +- .../DIDCommDIDResolverWrapper.swift | 125 +++---- .../DIDCommSecretsResolverWrapper.swift | 166 +++------ .../PackEncryptedOperation.swift | 96 +----- .../DIDCommWrappers/UnpackOperation.swift | 93 +---- .../Helpers/DIDCommMessage+DomainParse.swift | 117 ++++--- .../Mercury/Sources/MercuryImpl+Public.swift | 2 +- .../Mercury/Sources/MercuryImpl.swift | 6 +- .../Domain/Providers/KeyProvider.swift | 8 + .../Sources/Domain/StorableKeyModel.swift | 1 + .../Sources/Helpers/Message+Codable.swift | 30 +- ...DDIDPrivateKeyDAO+DIDPrivateKeyStore.swift | 15 +- .../DAO/CDKeyDAO+KeyProvider.swift | 21 ++ .../DAO/CDKeyDAO+LinkSecretStore.swift | 10 +- .../PersistentStorage/DAO/CDKeyDAO.swift | 2 + .../DAO/CDMessageDAO+MessageProvider.swift | 2 +- .../Pluto/Sources/PlutoImpl+Public.swift | 8 + .../Pluto/Tests/Helper/PrivateKey+Test.swift | 2 + .../AnonCreds/AnoncredsCredentialStack.swift | 2 +- .../Sources/Models/JWT/JWTPresentation.swift | 105 ++++-- .../W3C/W3CVerifiableCredential+Codable.swift | 4 +- .../JWT/CreateJWTCredentialRequest.swift | 90 ++++- .../PolluxImpl+CredentialRequest.swift | 4 +- AtalaPrismSDK/Pollux/Tests/JWTTests.swift | 1 - .../Pollux/Tests/Mocks/MockPluto.swift | 10 +- .../Sources/PrismAgent+Credentials.swift | 2 +- .../PrismAgent+DIDHigherFucntions.swift | 8 +- .../PrismAgent/Sources/PrismAgent+Proof.swift | 3 +- .../PrismAgent/Sources/PrismAgent.swift | 38 +- .../Mediation/MediationKeysUpdateList.swift | 3 +- .../Mediation/MediationRequest.swift | 3 +- .../Protocols/Pickup/PickupRequest.swift | 3 +- .../Protocols/Pickup/PrickupReceived.swift | 3 +- Package.swift | 17 +- .../WalletDemo2/Main/Main2Router.swift | 45 +-- .../MediatorPage/MediatorPageView.swift | 2 +- .../DIDChat/DIDChat.xcodeproj/project.pbxproj | 4 +- .../ContactsView/ContactsViewModel.swift | 1 - .../Modules/MediatorView/MediatorView.swift | 2 +- .../MediatorView/MediatorViewModel.swift | 4 +- 63 files changed, 821 insertions(+), 1190 deletions(-) delete mode 100644 AtalaPrismSDK/Castor/Sources/DID/PeerDID/PeerDID.swift delete mode 100644 AtalaPrismSDK/Castor/Sources/DID/PeerDID/Types.swift delete mode 100644 AtalaPrismSDK/Castor/Sources/Helpers/JWK+Helper.swift delete mode 100644 AtalaPrismSDK/Castor/Tests/EncumbasisEncodeDecodeTests.swift create mode 100644 AtalaPrismSDK/Pluto/Sources/Domain/Providers/KeyProvider.swift create mode 100644 AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO+KeyProvider.swift diff --git a/AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift b/AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift index b8457565..9c1149f4 100644 --- a/AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift +++ b/AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift @@ -20,11 +20,22 @@ extension ApolloImpl: KeyRestoration { guard let index = key.index else { throw ApolloError.restoratonFailedNoIdentifierOrInvalid } - return Secp256k1PrivateKey(internalKey: .init(raw: key.storableData.toKotlinByteArray()), derivationPath: DerivationPath(index: index)) + return Secp256k1PrivateKey( + identifier: key.identifier, + internalKey: .init(raw: key.storableData.toKotlinByteArray()), derivationPath: DerivationPath(index: index) + ) case "x25519+priv": - return try CreateX25519KeyPairOperation(logger: Self.logger).compute(fromPrivateKey: key.storableData) + return try CreateX25519KeyPairOperation(logger: Self.logger) + .compute( + identifier: key.identifier, + fromPrivateKey: key.storableData + ) case "ed25519+priv": - return try CreateEd25519KeyPairOperation(logger: Self.logger).compute(fromPrivateKey: key.storableData) + return try CreateEd25519KeyPairOperation(logger: Self.logger) + .compute( + identifier: key.identifier, + fromPrivateKey: key.storableData + ) default: throw ApolloError.restoratonFailedNoIdentifierOrInvalid } @@ -34,11 +45,20 @@ extension ApolloImpl: KeyRestoration { public func restorePublicKey(_ key: StorableKey) throws -> PublicKey { switch key.restorationIdentifier { case "secp256k1+pub": - return Secp256k1PublicKey(internalKey: .init(raw: key.storableData.toKotlinByteArray())) + return Secp256k1PublicKey( + identifier: key.identifier, + internalKey: .init(raw: key.storableData.toKotlinByteArray()) + ) case "x25519+pub": - return X25519PublicKey(internalKey: .init(raw: key.storableData.toKotlinByteArray())) + return X25519PublicKey( + identifier: key.identifier, + internalKey: .init(raw: key.storableData.toKotlinByteArray()) + ) case "ed25519+pub": - return Ed25519PublicKey(internalKey: .init(raw: key.storableData.toKotlinByteArray())) + return Ed25519PublicKey( + identifier: key.identifier, + internalKey: .init(raw: key.storableData.toKotlinByteArray()) + ) default: throw ApolloError.restoratonFailedNoIdentifierOrInvalid } diff --git a/AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift b/AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift index 7905e143..290a0316 100644 --- a/AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift +++ b/AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift @@ -9,10 +9,15 @@ struct Ed25519PrivateKey: PrivateKey { let keySpecifications: [String : String] = [ "curve" : "Ed25519" ] + var identifier: String var size: Int { raw.count } var raw: Data { internalKey.raw.toData() } - init(internalKey: ApolloLibrary.KMMEdPrivateKey) { + init( + identifier: String = UUID().uuidString, + internalKey: ApolloLibrary.KMMEdPrivateKey + ) { + self.identifier = identifier self.internalKey = internalKey } @@ -53,10 +58,15 @@ struct Ed25519PublicKey: PublicKey { let keySpecifications: [String : String] = [ "curve" : "Ed25519" ] + var identifier: String var size: Int { raw.count } var raw: Data { internalKey.raw.toData() } - init(internalKey: ApolloLibrary.KMMEdPublicKey) { + init( + identifier: String = UUID().uuidString, + internalKey: ApolloLibrary.KMMEdPublicKey + ) { + self.identifier = identifier self.internalKey = internalKey } diff --git a/AtalaPrismSDK/Apollo/Sources/Model/LinkSecret.swift b/AtalaPrismSDK/Apollo/Sources/Model/LinkSecret.swift index 039dbbd9..fa91d108 100644 --- a/AtalaPrismSDK/Apollo/Sources/Model/LinkSecret.swift +++ b/AtalaPrismSDK/Apollo/Sources/Model/LinkSecret.swift @@ -6,6 +6,7 @@ struct LinkSecret: Key { let keyType = "LinkSecret" let keySpecifications = [String : String]() let raw: Data + var identifier = "linkSecret" var size: Int { raw.count } let anoncred: AnoncredsSwift.LinkSecret diff --git a/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key+Exportable.swift b/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key+Exportable.swift index 7794bae8..413d6f51 100644 --- a/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key+Exportable.swift +++ b/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key+Exportable.swift @@ -11,9 +11,9 @@ extension Secp256k1PrivateKey: ExportableKey { var jwk: JWK { JWK( - kty: "OKP", + kty: "EC", d: raw.base64UrlEncodedString(), - crv: getProperty(.curve)?.capitalized, + crv: getProperty(.curve)?.lowercased(), x: publicKey().getProperty(.curvePointX).flatMap { Data(fromBase64URL: $0)?.base64UrlEncodedString() }, y: publicKey().getProperty(.curvePointY).flatMap { Data(fromBase64URL: $0)?.base64UrlEncodedString() } ) @@ -21,10 +21,10 @@ extension Secp256k1PrivateKey: ExportableKey { func jwkWithKid(kid: String) -> JWK { JWK( - kty: "OKP", + kty: "EC", kid: kid, d: raw.base64UrlEncodedString(), - crv: getProperty(.curve)?.capitalized, + crv: getProperty(.curve)?.lowercased(), x: publicKey().getProperty(.curvePointX).flatMap { Data(fromBase64URL: $0)?.base64UrlEncodedString() }, y: publicKey().getProperty(.curvePointY).flatMap { Data(fromBase64URL: $0)?.base64UrlEncodedString() } ) @@ -41,8 +41,8 @@ extension Secp256k1PublicKey: ExportableKey { var jwk: JWK { JWK( - kty: "OKP", - crv: getProperty(.curve)?.capitalized, + kty: "EC", + crv: getProperty(.curve)?.lowercased(), x: getProperty(.curvePointX) .flatMap { Data(fromBase64URL: $0)?.base64UrlEncodedString() } ?? raw.base64UrlEncodedString(), y: getProperty(.curvePointY).flatMap { Data(fromBase64URL: $0)?.base64UrlEncodedString() } @@ -51,9 +51,9 @@ extension Secp256k1PublicKey: ExportableKey { func jwkWithKid(kid: String) -> JWK { JWK( - kty: "OKP", + kty: "EC", kid: kid, - crv: getProperty(.curve)?.capitalized, + crv: getProperty(.curve)?.lowercased(), x: getProperty(.curvePointX) .flatMap { Data(fromBase64URL: $0)?.base64UrlEncodedString() } ?? raw.base64UrlEncodedString(), y: getProperty(.curvePointY).flatMap { Data(fromBase64URL: $0)?.base64UrlEncodedString() } diff --git a/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift b/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift index 12ef644b..fd5cdf60 100644 --- a/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift +++ b/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift @@ -5,14 +5,19 @@ import Foundation struct Secp256k1PrivateKey: PrivateKey { private let internalKey: KMMECSecp256k1PrivateKey - let keyType: String = "EC" let keySpecifications: [String : String] let size: Int let raw: Data let derivationPath: Domain.DerivationPath - - init(internalKey: KMMECSecp256k1PrivateKey, derivationPath: Domain.DerivationPath) { + var identifier: String + + init( + identifier: String = UUID().uuidString, + internalKey: KMMECSecp256k1PrivateKey, + derivationPath: Domain.DerivationPath + ) { + self.identifier = identifier self.internalKey = internalKey self.derivationPath = derivationPath self.keySpecifications = [ @@ -60,13 +65,17 @@ extension Secp256k1PrivateKey: KeychainStorableKey { struct Secp256k1PublicKey: PublicKey { private let internalKey: ApolloLibrary.KMMECSecp256k1PublicKey - let keyType: String = "EC" let keySpecifications: [String : String] let size: Int let raw: Data + var identifier = UUID().uuidString - init(internalKey: ApolloLibrary.KMMECSecp256k1PublicKey) { + init( + identifier: String = UUID().uuidString, + internalKey: ApolloLibrary.KMMECSecp256k1PublicKey + ) { + self.identifier = identifier self.internalKey = internalKey var specs: [String: String] = [ KeyProperties.curve.rawValue: "secp256k1", diff --git a/AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift b/AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift index 063eb0b0..7595525d 100644 --- a/AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift +++ b/AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift @@ -8,10 +8,15 @@ struct X25519PrivateKey: PrivateKey { let keySpecifications: [String : String] = [ "curve" : "x25519" ] + var identifier:String var size: Int { raw.count } var raw: Data { internalKey.raw.toData() } - init(internalKey: ApolloLibrary.KMMX25519PrivateKey) { + init( + identifier: String = UUID().uuidString, + internalKey: ApolloLibrary.KMMX25519PrivateKey + ) { + self.identifier = identifier self.internalKey = internalKey } @@ -41,10 +46,15 @@ struct X25519PublicKey: PublicKey { let keySpecifications: [String : String] = [ "curve" : "x25519" ] + var identifier: String var size: Int { raw.count } var raw: Data { internalKey.raw.toData() } - init(internalKey: ApolloLibrary.KMMX25519PublicKey) { + init( + identifier: String = UUID().uuidString, + internalKey: ApolloLibrary.KMMX25519PublicKey + ) { + self.identifier = identifier self.internalKey = internalKey } diff --git a/AtalaPrismSDK/Apollo/Sources/Operations/CreateEd25519KeyPairOperation.swift b/AtalaPrismSDK/Apollo/Sources/Operations/CreateEd25519KeyPairOperation.swift index 6df5da18..96dca525 100644 --- a/AtalaPrismSDK/Apollo/Sources/Operations/CreateEd25519KeyPairOperation.swift +++ b/AtalaPrismSDK/Apollo/Sources/Operations/CreateEd25519KeyPairOperation.swift @@ -12,7 +12,10 @@ struct CreateEd25519KeyPairOperation { } - func compute(fromPrivateKey: Data) throws -> PrivateKey { - return Ed25519PrivateKey(internalKey: KMMEdPrivateKey(raw: fromPrivateKey.toKotlinByteArray())) + func compute(identifier: String = UUID().uuidString, fromPrivateKey: Data) throws -> PrivateKey { + return Ed25519PrivateKey( + identifier: identifier, + internalKey: KMMEdPrivateKey(raw: fromPrivateKey.toKotlinByteArray()) + ) } } diff --git a/AtalaPrismSDK/Apollo/Sources/Operations/CreateX25519KeyPairOperation.swift b/AtalaPrismSDK/Apollo/Sources/Operations/CreateX25519KeyPairOperation.swift index e8ad07b4..e77edc83 100644 --- a/AtalaPrismSDK/Apollo/Sources/Operations/CreateX25519KeyPairOperation.swift +++ b/AtalaPrismSDK/Apollo/Sources/Operations/CreateX25519KeyPairOperation.swift @@ -13,8 +13,11 @@ struct CreateX25519KeyPairOperation { } - func compute(fromPrivateKey: Data) throws -> PrivateKey { + func compute(identifier: String = UUID().uuidString, fromPrivateKey: Data) throws -> PrivateKey { let privateKey = KMMX25519PrivateKey(raw: fromPrivateKey.toKotlinByteArray()) - return X25519PrivateKey(internalKey: privateKey) + return X25519PrivateKey( + identifier: identifier, + internalKey: privateKey + ) } } diff --git a/AtalaPrismSDK/Castor/Sources/CastorImpl+Public.swift b/AtalaPrismSDK/Castor/Sources/CastorImpl+Public.swift index 4b985f8f..ee44e23b 100644 --- a/AtalaPrismSDK/Castor/Sources/CastorImpl+Public.swift +++ b/AtalaPrismSDK/Castor/Sources/CastorImpl+Public.swift @@ -129,22 +129,4 @@ extension CastorImpl: Castor { } return try await resolver.resolve(did: did) } - - /// getEcnumbasis generates a unique ECNUM basis string for a given DID and key pair. This function may throw an error if the DID or key pair are invalid. - /// - /// - Parameters: - /// - did: The DID associated with the key pair - /// - keyPair: The key pair to use for generating the ECNUM basis - /// - Returns: The ECNUM basis string - /// - Throws: An error if the DID or key pair are invalid - public func getEcnumbasis(did: DID, publicKey: PublicKey) throws -> String { - logger.debug(message: "Getting ecnumbasis", metadata: [ - .maskedMetadataByLevel(key: "DID", value: did.string, level: .debug) - ]) - return try CreatePeerDIDOperation( - autenticationPublicKey: publicKey, - agreementPublicKey: publicKey, - services: [] - ).computeEcnumbasis(did: did, publicKey: publicKey) - } } diff --git a/AtalaPrismSDK/Castor/Sources/DID/PeerDID/PeerDID.swift b/AtalaPrismSDK/Castor/Sources/DID/PeerDID/PeerDID.swift deleted file mode 100644 index be7b92af..00000000 --- a/AtalaPrismSDK/Castor/Sources/DID/PeerDID/PeerDID.swift +++ /dev/null @@ -1,65 +0,0 @@ -import Domain -import Foundation -import Multibase - -struct PeerDID { - struct Service: Codable { - enum CodingKeys: String, CodingKey { - case type = "t" - case serviceEndpoint = "s" - case routingKeys = "r" - case accept = "a" - } - - let type: String - let serviceEndpoint: String - let routingKeys: [String] - let accept: [String] - - init( - type: String, - serviceEndpoint: String, - routingKeys: [String], - accept: [String] - ) { - self.type = type - self.serviceEndpoint = serviceEndpoint - self.routingKeys = routingKeys - self.accept = accept - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode( - type.replacingOccurrences(of: "DIDCommMessaging", with: "dm"), - forKey: .type - ) - try container.encode(serviceEndpoint, forKey: .serviceEndpoint) - try container.encode(routingKeys, forKey: .routingKeys) - try container.encode(accept, forKey: .accept) - } - - init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let type = try container.decode(String.self, forKey: .type) - self.type = type == "dm" ? "DIDCommMessaging" : type - self.serviceEndpoint = try container.decode(String.self, forKey: .serviceEndpoint) - self.routingKeys = try container.decodeIfPresent([String].self, forKey: .routingKeys) ?? [] - self.accept = try container.decodeIfPresent([String].self, forKey: .accept) ?? [] - } - } - - init(did: DID) throws { - let regex = """ -(([01](z)([1-9a-km-zA-HJ-NP-Z]{46,47}))|(2((\\.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]{46,47}))+(\\.(S)[0-9a-zA-Z=]*)?)))$ -""" - guard - did.schema == "did", - did.method == "peer", - did.methodId.range(of: regex, options: .regularExpression) != nil - else { throw CastorError.methodIdIsDoesNotSatisfyRegex(regex: regex) } - self.did = did - } - - let did: DID -} diff --git a/AtalaPrismSDK/Castor/Sources/DID/PeerDID/Types.swift b/AtalaPrismSDK/Castor/Sources/DID/PeerDID/Types.swift deleted file mode 100644 index 59716fed..00000000 --- a/AtalaPrismSDK/Castor/Sources/DID/PeerDID/Types.swift +++ /dev/null @@ -1,66 +0,0 @@ -import Foundation - -enum VerificationMaterialFormatPeerDID { - case jwk -} - -protocol VerificationMethodTypePeerDID { - var value: String { get } -} - -enum VerificationMethodTypeAgreement: String, VerificationMethodTypePeerDID { - case jsonWebKey2020 = "JsonWebKey2020" - case x25519KeyAgreementKey2019 = "X25519KeyAgreementKey2019" - case x25519KeyAgreementKey2020 = "X25519KeyAgreementKey2020" - - var value: String { self.rawValue } -} - -enum VerificationMethodTypeAuthentication: String, VerificationMethodTypePeerDID { - case jsonWebKey2020 = "JsonWebKey2020" - case ed25519KeyAgreementKey2018 = "Ed25519VerificationKey2018" - case ed25519KeyAgreementKey2020 = "Ed25519VerificationKey2020" - - var value: String { self.rawValue } -} - -extension VerificationMethodTypePeerDID { - var agreement: VerificationMethodTypeAgreement? { - self as? VerificationMethodTypeAgreement - } - var authentication: VerificationMethodTypeAuthentication? { - self as? VerificationMethodTypeAuthentication - } -} - -protocol VerificationMaterialPeerDID { - var keyType: VerificationMethodTypePeerDID { get } - var value: String { get } -} - -extension VerificationMaterialPeerDID { - var agreement: VerificationMaterialAgreement? { - self as? VerificationMaterialAgreement - } - var authentication: VerificationMaterialAuthentication? { - self as? VerificationMaterialAuthentication - } -} - -struct VerificationMaterialAgreement: VerificationMaterialPeerDID { - let format: VerificationMaterialFormatPeerDID - let value: String - let type: VerificationMethodTypeAgreement - - var keyType: VerificationMethodTypePeerDID { type } -} - -struct VerificationMaterialAuthentication: VerificationMaterialPeerDID { - let format: VerificationMaterialFormatPeerDID - let value: String - let type: VerificationMethodTypeAuthentication - - var keyType: VerificationMethodTypePeerDID { type } -} - -typealias JSON = String diff --git a/AtalaPrismSDK/Castor/Sources/Helpers/JWK+Helper.swift b/AtalaPrismSDK/Castor/Sources/Helpers/JWK+Helper.swift deleted file mode 100644 index 5451427c..00000000 --- a/AtalaPrismSDK/Castor/Sources/Helpers/JWK+Helper.swift +++ /dev/null @@ -1,45 +0,0 @@ -import Core -import Domain -import Foundation - -struct JWKHelper { - func fromJWK(material: VerificationMaterialAgreement) throws -> Data? { - guard - let jsonDic = try convertToDictionary(string: material.value), - let crv = jsonDic["crv"], - let xKey = jsonDic["x"], - crv == "X25519" - else { throw CastorError.invalidJWKError } - - return Data(base64URLEncoded: xKey) - } - - func fromJWK(material: VerificationMaterialAuthentication) throws -> Data? { - guard - let jsonDic = try convertToDictionary(string: material.value), - let crv = jsonDic["crv"], - let xKey = jsonDic["x"], - crv == "Ed25519" - else { throw CastorError.invalidJWKError } - - return Data(base64URLEncoded: xKey) - } - - func toJWK(publicKey: Data, material: VerificationMethodTypePeerDID) throws -> String? { - let xKeyString = publicKey.base64UrlEncodedString() - let crv: String - switch material { - case let agreement as VerificationMethodTypeAgreement where agreement == .jsonWebKey2020: - crv = "X25519" - case let authentication as VerificationMethodTypeAuthentication where authentication == .jsonWebKey2020: - crv = "Ed25519" - default: - throw CastorError.invalidJWKError - } - return try convertToJsonString(dic: [ - "kty" : "OKP", - "crv" : crv, - "x" : xKeyString - ]) - } -} diff --git a/AtalaPrismSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift b/AtalaPrismSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift index ae50b369..d4b2773c 100644 --- a/AtalaPrismSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift +++ b/AtalaPrismSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift @@ -1,172 +1,63 @@ import Core +import DIDCore import Domain import Foundation import Multibase +import PeerDID struct CreatePeerDIDOperation { - enum Numalgo2Prefix: String { - case authentication = "V" - case keyAgreement = "E" - case service = "S" - } - - struct OctetPublicKey: Codable { - enum CodingKeys: String, CodingKey { - case kty - case crv - case key = "x" - } - - let kty = "OKP" - let crv: String - let key: String - } - private let method: DIDMethod = "peer" let autenticationPublicKey: PublicKey let agreementPublicKey: PublicKey - let services: [DIDDocument.Service] - - func compute() throws -> DID { - return try createPeerDID( - encryptionKeys: [try keyAgreementFromPublicKey(publicKey: agreementPublicKey)], - signingKeys: [try authenticationFromPublicKey(publicKey: autenticationPublicKey)], - services: services - ).did - } - - func computeEcnumbasis(did: DID, publicKey: PublicKey) throws -> String { - guard - let curve = publicKey.getProperty(.curve)?.lowercased() - else { throw ApolloError.missingKeyParameters(missing: [KeyProperties.curve.rawValue]) } - switch curve { - case KnownKeyCurves.x25519.rawValue: - let material = try keyAgreementFromPublicKey(publicKey: agreementPublicKey) - let multibaseEcnumbasis = try createMultibaseEncnumbasis(material: material) - return String(multibaseEcnumbasis.dropFirst()) - case KnownKeyCurves.ed25519.rawValue: - let material = try authenticationFromPublicKey(publicKey: autenticationPublicKey) - let multibaseEcnumbasis = try createMultibaseEncnumbasis(material: material) - return String(multibaseEcnumbasis.dropFirst()) - default: - throw CastorError.keyCurveNotSupported(curve: curve) - } - } - - private func createPeerDID( - encryptionKeys: [VerificationMaterialAgreement], - signingKeys: [VerificationMaterialAuthentication], - services: [DIDDocument.Service] - ) throws -> PeerDID { - let encodedEncryptionKeysStr = try encryptionKeys - .map { try createMultibaseEncnumbasis(material: $0) } - .map { - ".\(Numalgo2Prefix.keyAgreement.rawValue)\($0)" + let services: [Domain.DIDDocument.Service] + + func compute() throws -> Domain.DID { + let did = try PeerDIDHelper.createAlgo2( + authenticationKeys: [authenticationFromPublicKey(publicKey: autenticationPublicKey)], + agreementKeys: [keyAgreementFromPublicKey(publicKey: agreementPublicKey)], + services: services.flatMap { service in + service.serviceEndpoint.map { + DIDCore.DIDDocument.Service( + id: service.id, + type: service.type.first ?? "", + serviceEndpoint: AnyCodable( + dictionaryLiteral: + ("uri", $0.uri), + ("accept", $0.accept), + ("routing_keys", $0.routingKeys) + ) + ) + } } - .joined() - let encodedSigningKeysStr = try signingKeys - .map { try createMultibaseEncnumbasis(material: $0) } - .map { - ".\(Numalgo2Prefix.authentication.rawValue)\($0)" - } - .joined() - let encodedService = try encodeService(services: services) - - return try PeerDID(did: .init( - method: "peer", - methodId: "2" - + encodedEncryptionKeysStr - + encodedSigningKeysStr - + "." - + Numalgo2Prefix.service.rawValue - + encodedService - )) + ) + return try .init(string: did.string) } - private func keyAgreementFromPublicKey(publicKey: PublicKey) throws -> VerificationMaterialAgreement { + private func keyAgreementFromPublicKey(publicKey: PublicKey) throws -> PeerDIDVerificationMaterial { guard - let exportable = publicKey.exporting, - publicKey.getProperty(.curve)?.lowercased() == KnownKeyCurves.x25519.rawValue, - let jwkString = String(data: try JSONEncoder.didComm().encode(exportable.jwk), encoding: .utf8) + publicKey.getProperty(.curve)?.lowercased() == KnownKeyCurves.x25519.rawValue else { throw CastorError.invalidPublicKeyCoding(didMethod: "peer", curve: KnownKeyCurves.x25519.rawValue) } - return .init( + return try .init( format: .jwk, - value: jwkString, - type: .jsonWebKey2020 + key: publicKey.raw, + type: .agreement(.jsonWebKey2020) ) } - private func authenticationFromPublicKey(publicKey: PublicKey) throws -> VerificationMaterialAuthentication { + private func authenticationFromPublicKey(publicKey: PublicKey) throws -> PeerDIDVerificationMaterial { guard - let exportable = publicKey.exporting, - publicKey.getProperty(.curve)?.lowercased() == KnownKeyCurves.ed25519.rawValue, - let jwkString = String(data: try JSONEncoder.didComm().encode(exportable.jwk), encoding: .utf8) - else { throw CastorError.invalidPublicKeyCoding(didMethod: "peer", curve: KnownKeyCurves.ed25519.rawValue) } - return .init( - format: .jwk, - value: jwkString, - type: .jsonWebKey2020 - ) - } - - private func toBase58Multibase(value: Data) -> String { - value.asString(base: .base58btc, withMultibasePrefix: true) - } - - private func createMultibaseEncnumbasis(material: VerificationMaterialAgreement) throws -> String { - let decodedKey: Data? - switch material.format { - case .jwk: - decodedKey = try JWKHelper().fromJWK(material: material) - } - guard let decodedKey else { throw CastorError.invalidJWKError } - try validateRawKeyLength(key: decodedKey) - let multiCodec = Multicodec(value: decodedKey, keyType: .agreement).value - return toBase58Multibase(value: multiCodec) - } - - private func createMultibaseEncnumbasis(material: VerificationMaterialAuthentication) throws -> String { - let decodedKey: Data? - switch material.format { - case .jwk: - decodedKey = try JWKHelper().fromJWK(material: material) - } - guard let decodedKey else { throw CastorError.invalidJWKError } - try validateRawKeyLength(key: decodedKey) - let multiCodec = Multicodec(value: decodedKey, keyType: .authenticate).value - return toBase58Multibase(value: multiCodec) - } - - private func encodeService(services: [DIDDocument.Service]) throws -> String { - let peerDidServices: [PeerDID.Service] = services.map { service in - guard - let type = service.type.first, - let endpoint = service.serviceEndpoint.first - else { return nil } - return PeerDID.Service( - type: type, - serviceEndpoint: endpoint.uri, - routingKeys: endpoint.routingKeys, - accept: endpoint.accept + publicKey.getProperty(.curve)?.lowercased() == KnownKeyCurves.ed25519.rawValue + else { + throw CastorError.invalidPublicKeyCoding( + didMethod: "peer", + curve: KnownKeyCurves.ed25519.rawValue ) - }.compactMap { $0 } - let encoder = JSONEncoder.didComm() - if - peerDidServices.count == 1, - let peerDidService = peerDidServices.first - { - return try encoder.encode(peerDidService).base64UrlEncodedString() - } else { - return try encoder.encode(peerDidServices).base64UrlEncodedString() } - } - private func validateRawKeyLength(key: Data) throws { - guard key.count == 32 else { - throw UnknownError.somethingWentWrongError( - customMessage: "Invalid secp256k1 key size of 32 bytes", - underlyingErrors: nil - ) - } + return try .init( + format: .jwk, + key: publicKey.raw, + type: .authentication(.jsonWebKey2020) + ) } } diff --git a/AtalaPrismSDK/Castor/Sources/Resolvers/PeerDIDResolver.swift b/AtalaPrismSDK/Castor/Sources/Resolvers/PeerDIDResolver.swift index a3268e42..b871e9a9 100644 --- a/AtalaPrismSDK/Castor/Sources/Resolvers/PeerDIDResolver.swift +++ b/AtalaPrismSDK/Castor/Sources/Resolvers/PeerDIDResolver.swift @@ -1,204 +1,186 @@ -import Core +import DIDCore import Domain import Foundation -import Multibase +import PeerDID struct PeerDIDResolver: DIDResolverDomain { var method = "peer" - func resolve(did: DID) async throws -> DIDDocument { - guard - did.method == "peer", - did.methodId.prefix(1) == "2" - else { throw CastorError.notPossibleToResolveDID( - did: did.string, - reason: "Method or method id are invalid" - )} - - return try buildDIDDocumentAlgo2(did: did, format: .jwk) + func resolve(did: Domain.DID) async throws -> Domain.DIDDocument { + try PeerDIDHelper.resolve(peerDIDStr: did.string).toDomain() } +} - private func buildDIDDocumentAlgo2( - did: DID, - format: VerificationMaterialFormatPeerDID - ) throws -> DIDDocument { - let composition = did.methodId.components(separatedBy: ".").dropFirst() - var authenticationMethods = [DIDDocument.VerificationMethod]() - var keyAgreementMethods = [DIDDocument.VerificationMethod]() - var services = [DIDDocument.Service]() - try composition.forEach { - switch $0.prefix(1) { - case CreatePeerDIDOperation.Numalgo2Prefix.authentication.rawValue: - let decoded = try decodeMultibaseEncnumbasisAuth( - did: did, - multibase: String($0.dropFirst()), - format: .jwk - ) - authenticationMethods.append(try getVerificationMethod(did: did, decodedEncumbasis: decoded)) - case CreatePeerDIDOperation.Numalgo2Prefix.keyAgreement.rawValue: - let decoded = try decodeMultibaseEncnumbasisAgreement( - did: did, - multibase: String($0.dropFirst()), - format: .jwk - ) - keyAgreementMethods.append(try getVerificationMethod(did: did, decodedEncumbasis: decoded)) - case CreatePeerDIDOperation.Numalgo2Prefix.service.rawValue: - services.append(contentsOf: try decodeService( - did: did, - encodedString: String($0.dropFirst()) - )) - default: - break - } +extension DIDCore.DIDDocument { + + init(from: Domain.DIDDocument) throws { + let verificationMethods = try from.verificationMethods.map { + try DIDCore.DIDDocument.VerificationMethod(from: $0) } + let verificationMethodsIds = verificationMethods.map(\.id) - return DIDDocument( - id: did, - coreProperties: [ - DIDDocument.VerificationMethods( - values: authenticationMethods + keyAgreementMethods - ), - DIDDocument.Authentication( - urls: authenticationMethods.map { $0.id.string }, - verificationMethods: [] - ), - DIDDocument.KeyAgreement( - urls: keyAgreementMethods.map { $0.id.string }, - verificationMethods: [] - ), - DIDDocument.Services(values: services) - ] - ) - } + let authenticationMethods = try from.authenticate + .filter { + verificationMethodsIds.contains($0.id.string) + } + .map { + try DIDCore.DIDDocument.VerificationMethod(from: $0) + } + let authenticationIds = from.authenticate.map(\.id.string) - func decodeMultibaseEncnumbasisAuth( - did: DID, - multibase: String, - format: VerificationMaterialFormatPeerDID - ) throws -> (String, VerificationMaterialAuthentication) { - let (decoded, verMaterial) = try decodeMultibaseEncnumbasis( - multibase: multibase, - format: format, defaultCodec: .ed25519 - ) - guard let material = verMaterial.authentication else { - throw CastorError.notPossibleToResolveDID( - did: did.string, - reason: "Could not decode authentication multibase" - ) + let keyAgreementMethods = try from.keyAgreement + .filter { + verificationMethodsIds.contains($0.id.string) + } + .map { + try DIDCore.DIDDocument.VerificationMethod(from: $0) + } + + let keyAgreementIds = from.keyAgreement.map(\.id.string) + + let services = from.services.flatMap { service in + service.serviceEndpoint.map { + DIDCore.DIDDocument.Service( + id: service.id, + type: service.type.first ?? "", + serviceEndpoint: AnyCodable( + dictionaryLiteral: + ("uri", $0.uri), + ("accept", $0.accept), + ("routing_keys", $0.routingKeys) + ) + ) + } } - return (decoded, material) - } - private func decodeMultibaseEncnumbasisAgreement( - did: DID, - multibase: String, - format: VerificationMaterialFormatPeerDID - ) throws -> (String, VerificationMaterialAgreement) { - let (decoded, verMaterial) = try decodeMultibaseEncnumbasis( - multibase: multibase, - format: format, defaultCodec: .x25519 + self.init( + id: from.id.string, + verificationMethods: verificationMethods + authenticationMethods + keyAgreementMethods, + authentication: authenticationIds.map { .stringValue($0) }, + assertionMethod: nil, + capabilityDelegation: nil, + keyAgreement: keyAgreementIds.map { .stringValue($0) }, + services: services ) - guard let material = verMaterial.agreement else { - throw CastorError.notPossibleToResolveDID( - did: did.string, - reason: "Could not decode key agreement multibase" - )} - - return (decoded, material) } - private func decodeMultibaseEncnumbasis( - multibase: String, - format: VerificationMaterialFormatPeerDID, - defaultCodec: Multicodec.Codec - ) throws -> (String, VerificationMaterialPeerDID) { - let (encnum, encnumData) = try fromBase58Multibase(multibase: multibase) - let (codec, decodedEncnum) = try Multicodec(value: encnumData).decode() - try validateRawKeyLength(key: decodedEncnum) - switch format { - case .jwk: - switch codec { - case .x25519: - guard let jwkJsonString = try JWKHelper().toJWK( - publicKey: decodedEncnum, - material: VerificationMethodTypeAgreement.jsonWebKey2020 - ) else { throw CastorError.invalidJWKError } - - return (encnum, VerificationMaterialAgreement( - format: format, - value: jwkJsonString, - type: .jsonWebKey2020 - )) - case .ed25519: - guard let jwkJsonString = try JWKHelper().toJWK( - publicKey: decodedEncnum, - material: VerificationMethodTypeAuthentication.jsonWebKey2020 - ) else { throw CastorError.invalidJWKError } - - return (encnum, VerificationMaterialAuthentication( - format: format, - value: jwkJsonString, - type: .jsonWebKey2020 - )) + func toDomain() throws -> Domain.DIDDocument { + let authenticationUrls = self.verificationMethods + .filter { + guard let type = KnownVerificationMaterialType(rawValue: $0.type) else { + return false + } + switch type { + case .authentication: + return true + default: + return false + } } + .map { $0.id } + + let keyAgreementUrls = self.verificationMethods + .filter { + guard let type = KnownVerificationMaterialType(rawValue: $0.type) else { + return false + } + switch type { + case .agreement: + return true + default: + return false + } + } + .map { $0.id } + + let verificationMethods = try self.verificationMethods.map { + try $0.toDomain() } - } - private func fromBase58Multibase(multibase: String) throws -> (String, Data) { - let multibaseDecoding = try BaseEncoding.decode(multibase) - return (String(multibase.dropFirst()), multibaseDecoding.data) - } + let services = try self.services?.map { + guard + let endpoint = $0.serviceEndpoint.value as? [String: Any], + let uri = endpoint["uri"] as? String + else { + throw CastorError.notPossibleToResolveDID(did: $0.id, reason: "Invalid service") + } + return Domain.DIDDocument.Service( + id: $0.id, + type: [$0.type], + serviceEndpoint: [ + .init( + uri: uri, + accept: endpoint["accept"] as? [String] ?? [], + routingKeys: endpoint["routing_keys"] as? [String] ?? [] + ) + ] + ) + } ?? [Domain.DIDDocument.Service]() - private func getVerificationMethod( - did: DID, - decodedEncumbasis: (String, VerificationMaterialPeerDID) - ) throws -> DIDDocument.VerificationMethod { - var jsonDic = try convertToDictionary(string: decodedEncumbasis.1.value) - jsonDic?["kid"] = did.string + "#" + decodedEncumbasis.0 - return .init( - id: .init(did: did, fragment: decodedEncumbasis.0), - controller: did, - type: decodedEncumbasis.1.keyType.value, - publicKeyJwk: jsonDic + return Domain.DIDDocument( + id: try DID(string: self.id), + coreProperties: [ + Domain.DIDDocument.Authentication( + urls: authenticationUrls, + verificationMethods: [] + ), + Domain.DIDDocument.KeyAgreement( + urls: keyAgreementUrls, + verificationMethods: [] + ), + Domain.DIDDocument.VerificationMethods(values: verificationMethods), + Domain.DIDDocument.Services(values: services) + ] ) } +} - private func decodeService(did: DID, encodedString: String) throws -> [DIDDocument.Service] { - guard let jsonData = Data(fromBase64URL: encodedString) else { - throw CastorError.notPossibleToResolveDID( - did: did.string, - reason: "Could not parse Service JSON" +extension DIDCore.DIDDocument.VerificationMethod { + + init(from: Domain.DIDDocument.VerificationMethod) throws { + if let publicKeyMultibase = from.publicKeyMultibase { + self.init( + id: from.id.string, + controller: from.controller.string, + type: from.type, + material: .init( + format: .multibase, + value: try publicKeyMultibase.tryData(using: .utf8) + ) ) - } - let services = try jsonDecoderForServicePeerDIDService(jsonData: jsonData) - return services.enumerated().map { - DIDDocument.Service( - id: did.string + $0.element.type.lowercased() + "-\($0.offset)", - type: [$0.element.type], - serviceEndpoint: [.init( - uri: $0.element.serviceEndpoint, - accept: $0.element.accept, - routingKeys: $0.element.routingKeys - )] + } else if let publicKeyJwk = from.publicKeyJwk { + self.init( + id: from.id.string, + controller: from.controller.string, + type: from.type, + material: .init( + format: .jwk, + value: try JSONSerialization.data(withJSONObject: publicKeyJwk) + ) ) + } else { + throw PeerDIDError.invalidMaterialType("") } } - private func jsonDecoderForServicePeerDIDService(jsonData: Data) throws -> [PeerDID.Service] { - do { - return try JSONDecoder().decode([PeerDID.Service].self, from: jsonData) - } catch { - let decoded = try JSONDecoder().decode(PeerDID.Service.self, from: jsonData) - return [decoded] - } - } - - private func validateRawKeyLength(key: Data) throws { - guard key.count == 32 else { - throw UnknownError.somethingWentWrongError( - customMessage: "Invalid secp256k1 key size of 32 bytes", - underlyingErrors: nil + func toDomain() throws -> Domain.DIDDocument.VerificationMethod { + switch material.format { + case .jwk: + return Domain.DIDDocument.VerificationMethod( + id: try DIDUrl(string: id), + controller: try DID(string: controller), + type: type, + publicKeyJwk: try JSONSerialization.jsonObject(with: material.value) as? [String: String] + ) + case .multibase: + return Domain.DIDDocument.VerificationMethod( + id: try DIDUrl(string: id), + controller: try DID(string: controller), + type: type, + publicKeyMultibase: String(data: material.value, encoding: .utf8) ) + default: + throw CastorError.notPossibleToResolveDID(did: id, reason: "Invalid did peer") } } } diff --git a/AtalaPrismSDK/Castor/Tests/EncumbasisEncodeDecodeTests.swift b/AtalaPrismSDK/Castor/Tests/EncumbasisEncodeDecodeTests.swift deleted file mode 100644 index 4e920c3d..00000000 --- a/AtalaPrismSDK/Castor/Tests/EncumbasisEncodeDecodeTests.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Core -import Domain -@testable import Castor -import XCTest - -final class EncumbasisEncodeDecodeTests: XCTestCase { - func testDecodeEcnumbasis() throws { - let valueDic = [ - "crv" : "Ed25519", - "kty" : "OKP", - "x" : "owBhCbktDjkfS6PdQddT0D3yjSitaSysP3YimJ_YgmA" - ] - let valueJson = try convertToJsonString(dic: valueDic)! - let ecnumBasis = "z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V" - let result = VerificationMaterialAuthentication( - format: .jwk, - value: valueJson, - type: .jsonWebKey2020 - ) - - let ecnumbasisResult = try PeerDIDResolver().decodeMultibaseEncnumbasisAuth( - did: DID(method: "test", methodId: "test1"), - multibase: ecnumBasis, - format: .jwk - ) - - XCTAssertEqual(result.type, ecnumbasisResult.1.type) - XCTAssertEqual(result.value, ecnumbasisResult.1.value) - XCTAssertEqual(result.format, ecnumbasisResult.1.format) - } -} diff --git a/AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift b/AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift index 39634fbb..d7f90bb3 100644 --- a/AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift +++ b/AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift @@ -5,7 +5,7 @@ import XCTest final class PeerDIDCreationTests: XCTestCase { func testPeerDIDCreation() throws { - let validPeerDID = "did:peer:2.Ez6LSoHkfN1Y4nK9RCjx7vopWsLrMGNFNgTNZgoCNQrTzmb1n.Vz6MknRZmapV7uYZQuZez9n9N3tQotjRN18UGS68Vcfo6gR4h.SeyJhIjpbXSwiciI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInQiOiJkbSJ9" + let validPeerDID = "did:peer:2.Ez6LSoHkfN1Y4nK9RCjx7vopWsLrMGNFNgTNZgoCNQrTzmb1n.Vz6MknRZmapV7uYZQuZez9n9N3tQotjRN18UGS68Vcfo6gR4h.SeyJzIjp7ImEiOltdLCJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInVyaSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vZW5kcG9pbnQifSwidCI6ImRtIn0" let apollo = ApolloImpl() let castor = CastorImpl(apollo: apollo) let keyAgreementPrivateKey = try apollo.createPrivateKey(parameters: [ @@ -35,7 +35,6 @@ final class PeerDIDCreationTests: XCTestCase { services: [service] ) - print(did.string) XCTAssertEqual(did.string, validPeerDID) } @@ -59,6 +58,5 @@ final class PeerDIDCreationTests: XCTestCase { let apollo = ApolloImpl() let castor = CastorImpl(apollo: apollo) let document = try await castor.resolveDID(did: mypeerDID) - print(document) } } diff --git a/AtalaPrismSDK/Domain/Sources/BBs/Castor.swift b/AtalaPrismSDK/Domain/Sources/BBs/Castor.swift index 889b3432..4ed4ce33 100644 --- a/AtalaPrismSDK/Domain/Sources/BBs/Castor.swift +++ b/AtalaPrismSDK/Domain/Sources/BBs/Castor.swift @@ -21,20 +21,6 @@ public protocol Castor { services: [DIDDocument.Service] ) throws -> DID -// /// createPeerDID creates a DID for a peer (a device or server that acts as a DID subject) using given key agreement and authentication key pairs and a list of services. This function may throw an error if the key pairs or services are invalid. -// /// -// /// - Parameters: -// /// - keyAgreementKeyPair: The key pair used for key agreement (establishing secure communication between peers) -// /// - authenticationKeyPair: The key pair used for authentication (verifying the identity of a peer) -// /// - services: The list of services offered by the peer -// /// - Returns: The DID of the peer -// /// - Throws: An error if the key pairs or services are invalid -// func createPeerDID( -// keyAgreementKeyPair: KeyPair, -// authenticationKeyPair: KeyPair, -// services: [DIDDocument.Service] -// ) throws -> DID - /// createPeerDID creates a DID for a peer (a device or server that acts as a DID subject) using given key agreement and authentication key pairs and a list of services. This function may throw an error if the key pairs or services are invalid. /// /// - Parameters: @@ -83,15 +69,6 @@ public protocol Castor { challenge: Data, signature: Data ) async throws -> Bool - - /// getEcnumbasis generates a unique ECNUM basis string for a given DID and key pair. This function may throw an error if the DID or key pair are invalid. - /// - /// - Parameters: - /// - did: The DID associated with the key pair - /// - keyPair: The key pair to use for generating the ECNUM basis - /// - Returns: The ECNUM basis string - /// - Throws: An error if the DID or key pair are invalid - func getEcnumbasis(did: DID, publicKey: PublicKey) throws -> String } extension Castor { diff --git a/AtalaPrismSDK/Domain/Sources/BBs/Pluto.swift b/AtalaPrismSDK/Domain/Sources/BBs/Pluto.swift index afc5c15f..7b48cd3d 100644 --- a/AtalaPrismSDK/Domain/Sources/BBs/Pluto.swift +++ b/AtalaPrismSDK/Domain/Sources/BBs/Pluto.swift @@ -151,6 +151,15 @@ public protocol Pluto { /// - Returns: A publisher that emits an array of DID pairs. func getAllDidPairs() -> AnyPublisher<[DIDPair], Error> + /// Returns all stored Keys. + /// - Returns: A publisher that emits an array of StorableKey. + func getAllKeys() -> AnyPublisher<[StorableKey], Error> + + /// Returns the key with the given id. + /// - Parameter id: The key id. + /// - Returns: A publisher that emits an key if it exists. + func getKeyById(id: String) -> AnyPublisher + /// Returns the stored DID pair that includes a given DID. /// - Parameter otherDID: The DID to search for. /// - Returns: A publisher that emits an optional DID pair that includes the given DID. diff --git a/AtalaPrismSDK/Domain/Sources/Models/DIDDocument.swift b/AtalaPrismSDK/Domain/Sources/Models/DIDDocument.swift index 07880625..a6fd24c2 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/DIDDocument.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/DIDDocument.swift @@ -248,6 +248,22 @@ public struct DIDDocument { return authenticateProperty.verificationMethods } + public var keyAgreement: [VerificationMethod] { + guard + let property = coreProperties + .first(where: { $0 as? KeyAgreement != nil }) + .map({ $0 as? KeyAgreement }), + let keyAgreementProperty = property + else { return [] } + + guard keyAgreementProperty.urls.isEmpty else { + return keyAgreementProperty.urls.compactMap { uri in + verificationMethods.first { $0.id.string == uri } + } + keyAgreementProperty.verificationMethods + } + return keyAgreementProperty.verificationMethods + } + public var verificationMethods: [VerificationMethod] { guard let property = coreProperties diff --git a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/Keys.swift b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/Keys.swift index b3830dac..91baaf69 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/Keys.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/Keys.swift @@ -3,6 +3,8 @@ import Foundation /// The Key protocol defines a cryptographic key with essential properties. /// Each key has a type (e.g., "RSA", "ECC"), a set of specifications, a size, and a raw data representation. public protocol Key { + /// The key identifier + var identifier: String { get set } /// The type of the key (e.g., "RSA", "ECC") var keyType: String { get } diff --git a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift index fb8e9ba6..e89456e0 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift @@ -2,6 +2,9 @@ import Foundation /// The `StorableKey` protocol defines a cryptographic key that can be stored persistently. public protocol StorableKey { + /// The key identifier + var identifier: String { get set } + /// An identifier used for restoring the key. var restorationIdentifier: String { get } diff --git a/AtalaPrismSDK/Domain/Sources/Models/Message+Codable.swift b/AtalaPrismSDK/Domain/Sources/Models/Message+Codable.swift index b738781d..0569eaed 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/Message+Codable.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/Message+Codable.swift @@ -38,17 +38,17 @@ extension Message: Codable { let container = try decoder.container(keyedBy: CodingKeys.self) let id = try container.decode(String.self, forKey: .id) let piuri = try container.decode(String.self, forKey: .piuri) - let body = try container.decode(Data.self, forKey: .body) - let extraHeaders = try container.decode([String: String].self, forKey: .extraHeaders) - let createdTime = try container.decode(Date.self, forKey: .createdTime) - let expiresTimePlus = try container.decode(Date.self, forKey: .expiresTimePlus) - let attachments = try container.decode([AttachmentDescriptor].self, forKey: .attachments) - let ack = try container.decode([String].self, forKey: .ack) - let from = try? container.decode(String.self, forKey: .from) - let to = try? container.decode(String.self, forKey: .to) - let fromPrior = try? container.decode(String.self, forKey: .fromPrior) - let thid = try? container.decode(String.self, forKey: .thid) - let pthid = try? container.decode(String.self, forKey: .pthid) + let body = try container.decodeIfPresent(Data.self, forKey: .body) + let extraHeaders = try container.decodeIfPresent([String: String].self, forKey: .extraHeaders) + let createdTime = try container.decodeIfPresent(Date.self, forKey: .createdTime) + let expiresTimePlus = try container.decodeIfPresent(Date.self, forKey: .expiresTimePlus) + let attachments = try container.decodeIfPresent([AttachmentDescriptor].self, forKey: .attachments) + let ack = try container.decodeIfPresent([String].self, forKey: .ack) + let from = try? container.decodeIfPresent(String.self, forKey: .from) + let to = try? container.decodeIfPresent(String.self, forKey: .to) + let fromPrior = try? container.decodeIfPresent(String.self, forKey: .fromPrior) + let thid = try? container.decodeIfPresent(String.self, forKey: .thid) + let pthid = try? container.decodeIfPresent(String.self, forKey: .pthid) self.init( id: id, @@ -56,14 +56,14 @@ extension Message: Codable { from: try from.map { try DID(string: $0) }, to: try to.map { try DID(string: $0) }, fromPrior: fromPrior, - body: body, - extraHeaders: extraHeaders, - createdTime: createdTime, + body: body ?? Data(), + extraHeaders: extraHeaders ?? [:], + createdTime: createdTime ?? Date(), expiresTimePlus: expiresTimePlus, - attachments: attachments, + attachments: attachments ?? [], thid: thid, pthid: pthid, - ack: ack + ack: ack ?? [] ) } } diff --git a/AtalaPrismSDK/Domain/Sources/Models/Message.swift b/AtalaPrismSDK/Domain/Sources/Models/Message.swift index d0c0c0cc..8c848950 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/Message.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/Message.swift @@ -33,7 +33,7 @@ public struct Message: Identifiable, Hashable { public let createdTime: Date /// The time at which the message will expire. - public let expiresTimePlus: Date + public let expiresTimePlus: Date? /// Descriptors for any attachments included in the message. public let attachments: [AttachmentDescriptor] @@ -75,7 +75,7 @@ public struct Message: Identifiable, Hashable { body: Data, extraHeaders: [String : String] = [:], createdTime: Date = Date(), - expiresTimePlus: Date = Date(), + expiresTimePlus: Date? = nil, attachments: [AttachmentDescriptor] = [], thid: String? = nil, pthid: String? = nil, diff --git a/AtalaPrismSDK/Domain/Sources/Models/MessageAttachment.swift b/AtalaPrismSDK/Domain/Sources/Models/MessageAttachment.swift index af4483b9..cd61d309 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/MessageAttachment.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/MessageAttachment.swift @@ -190,12 +190,12 @@ extension AttachmentDescriptor: Codable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let id = try container.decode(String.self, forKey: .id) - let mediaType = try? container.decode(String.self, forKey: .mediaType) - let filename = try? container.decode([String].self, forKey: .filename) - let format = try? container.decode(String.self, forKey: .format) - let lastmodTime = try? container.decode(Date.self, forKey: .lastmodTime) - let byteCount = try? container.decode(Int.self, forKey: .byteCount) - let description = try? container.decode(String.self, forKey: .description) + let mediaType = try? container.decodeIfPresent(String.self, forKey: .mediaType) + let filename = try? container.decodeIfPresent([String].self, forKey: .filename) + let format = try? container.decodeIfPresent(String.self, forKey: .format) + let lastmodTime = try? container.decodeIfPresent(Date.self, forKey: .lastmodTime) + let byteCount = try? container.decodeIfPresent(Int.self, forKey: .byteCount) + let description = try? container.decodeIfPresent(String.self, forKey: .description) let data: AttachmentData? if let attchData = try? container.decode(AttachmentBase64.self, forKey: .data) { data = attchData diff --git a/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/DIDCommDIDResolverWrapper.swift b/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/DIDCommDIDResolverWrapper.swift index 5ebfde81..086d1b24 100644 --- a/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/DIDCommDIDResolverWrapper.swift +++ b/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/DIDCommDIDResolverWrapper.swift @@ -1,13 +1,14 @@ import Combine import Core -import DIDCommxSwift +import DIDCommSwift +import DIDCore import Domain import Foundation class DIDCommDIDResolverWrapper { let logger: PrismLogger let castor: Castor - var publisher = PassthroughSubject() + var publisher = PassthroughSubject() var cancellables = [AnyCancellable]() init(castor: Castor, logger: PrismLogger) { @@ -23,99 +24,67 @@ class DIDCommDIDResolverWrapper { } } -extension DIDCommDIDResolverWrapper: DidResolver { - func resolve(did: String, cb: OnDidResolverResult) -> ErrorCode { - publisher - .first() - .sink { [weak self] in - switch $0 { - case .finished: - break - case let .failure(error): - self?.logger.error(message: "Error trying to resolve DID", metadata: [ - .publicMetadata(key: "Error", value: error.localizedDescription) - ]) - try? cb.error( - err: ErrorKind.DidNotResolved(message: error.localizedDescription), - msg: error.localizedDescription - ) - } - } receiveValue: { [weak self] in - do { - self?.logger.debug(message: "Success resolving DID", metadata: [ - .maskedMetadataByLevel(key: "DID", value: did, level: .debug) - ]) - try cb.success(result: try DidDoc(from: $0)) - } catch { - self?.logger.error(message: "Error trying to resolve DID", metadata: [ - .publicMetadata(key: "Error", value: error.localizedDescription) - ]) - try? cb.error( - err: ErrorKind.DidNotResolved(message: error.localizedDescription), - msg: error.localizedDescription - ) - } - } - .store(in: &cancellables) - resolve(did: did) - return .success +extension DIDCommDIDResolverWrapper: DIDResolver { + func resolve(did: DIDCore.DID) async throws -> DIDCore.DIDDocument { + let document = try await castor.resolveDID(did: DID(string: did.description)) + return try .init(from: document) } } -extension DidDoc { - init(from: DIDDocument) throws { - let did = from.id.string +extension DIDCore.DIDDocument { + init(from: Domain.DIDDocument) throws { var authentications = [String]() var keyAgreements = [String]() let verificationMethods: [VerificationMethod] = try from.verificationMethods.compactMap { - guard - let jsonKeys = try $0.publicKeyJwk?.convertToJsonString(), - let crv = $0.publicKeyJwk?["crv"] - else { return nil } - switch crv { - case "X25519": + switch KnownVerificationMaterialType(rawValue: $0.type) { + case .agreement: keyAgreements.append($0.id.string) - case "Ed25519": + case .authentication: authentications.append($0.id.string) default: - break + return nil } - return VerificationMethod( - id: $0.id.string, - type: .jsonWebKey2020, - controller: $0.controller.string, - verificationMaterial: .jwk(value: jsonKeys) - ) - } - let services = from.services.compactMap { service in - if service.type.contains("DIDCommMessaging") { - return service.serviceEndpoint.first.map { - Service( - id: service.id, - kind: .didCommMessaging( - value: .init( - serviceEndpoint: $0.uri, - accept: $0.accept, - routingKeys: $0.routingKeys - ) - ) - ) - } + if + let jsonKeys = try $0.publicKeyJwk?.convertToJsonString() + { + return .init( + id: $0.id.string, + controller: $0.controller.string, + type: $0.type, + material: try .fromJWK(jwk: JSONDecoder().decode(JWK.self, from: jsonKeys.tryToData())) + ) + } else if let multibase = $0.publicKeyMultibase { + return .init( + id: $0.id.string, + controller: $0.controller.string, + type: $0.type, + material: .init(format: .multibase, value: try multibase.tryToData()) + ) } else { - return service.serviceEndpoint.first.map { - Service( - id: service.id, - kind: .other(value: $0.uri) + return nil + } + } + + let services = from.services.flatMap { service in + service.serviceEndpoint.map { + return Service( + id: service.id, + type: service.type.first ?? "", + serviceEndpoint: AnyCodable( + dictionaryLiteral: + ("uri", $0.uri), + ("accept", $0.accept), + ("routing_keys", $0.routingKeys) ) - } + ) } } self.init( - did: did, - keyAgreements: keyAgreements, - authentications: authentications, + id: from.id.string, verificationMethods: verificationMethods, + authentication: authentications.map { .stringValue($0) }, + keyAgreement: keyAgreements.map { .stringValue($0) }, services: services ) } diff --git a/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/DIDCommSecretsResolverWrapper.swift b/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/DIDCommSecretsResolverWrapper.swift index b48e9f10..e8104f70 100644 --- a/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/DIDCommSecretsResolverWrapper.swift +++ b/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/DIDCommSecretsResolverWrapper.swift @@ -1,6 +1,7 @@ import Combine import Core -import DIDCommxSwift +import DIDCommSwift +import DIDCore import Domain import Foundation @@ -22,135 +23,78 @@ class DIDCommSecretsResolverWrapper { } fileprivate func getListOfAllSecrets() async throws -> [Domain.Secret] { -// try await pluto -// .getAllPeerDIDs() -// .first() -// .tryMap { -// try $0.map { did, privateKeys, _ in -// try self.parsePrivateKeys(did: did, privateKeys: privateKeys) -// } -// } -// .map { $0.compactMap { $0 }.flatMap { $0 } } try await secretsStream .first() .await() } - - private func parsePrivateKeys( - did: DID, - privateKeys: [PrivateKey] - ) throws -> [Domain.Secret] { - return try privateKeys - .map { $0 as? (PrivateKey & ExportableKey) } - .compactMap { $0 } - .map { privateKey in - let ecnumbasis = try castor.getEcnumbasis(did: did, publicKey: privateKey.publicKey()) - return (did, privateKey, ecnumbasis) - } - .map { did, privateKey, ecnumbasis in - try parseToSecret(did: did, privateKey: privateKey, ecnumbasis: ecnumbasis) - } - } - - private func parseToSecret(did: DID, privateKey: PrivateKey & ExportableKey, ecnumbasis: String) throws -> Domain.Secret { - let id = did.string + "#" + ecnumbasis - let jwk = privateKey.jwk - guard - let dataJson = try? JSONEncoder().encode(jwk), - let stringJson = String(data: dataJson, encoding: .utf8) - else { - throw CommonError.invalidCoding(message: "Could not encode privateKey.jwk") - } - return .init( - id: id, - type: .jsonWebKey2020, - secretMaterial: .jwk(value: stringJson) - ) - } } -extension DIDCommSecretsResolverWrapper: SecretsResolver { - func getSecret( - secretid: String, - cb: OnGetSecretResult - ) -> ErrorCode { - Task { - do { - // Fix: Fixes a bug currently happening on didcomm library that is adding a / before the fragment sign - let secretidsaux = secretid.replacingOccurrences(of: "/#", with: "#") - let secret = try await getListOfAllSecrets() - .first { $0.id == secretidsaux } - // Fix: Fixes a bug currently happening on didcomm library that is adding a / before the fragment sign -// .map { -// Domain.Secret( -// id: secretid, -// type: $0.type, -// secretMaterial: $0.secretMaterial -// ) -// } - try cb.success(result: secret.map { DIDCommxSwift.Secret(from: $0) }) - } catch let error { - let mercuryError = MercuryError.didcommError( - msg: "Could not find secret \(secretid)", - underlyingErrors: [error] - ) - logger.error(error: mercuryError) - } +extension DIDCommSecretsResolverWrapper: DIDCommSwift.SecretResolver { + func findKey(kid: String) async throws -> DIDCommSwift.Secret? { + guard + let secret = try? await getListOfAllSecrets() + .first(where: { + $0.id == kid + }) + else { + let error = MercuryError.didcommError( + msg: "Could not find secret \(kid)", + underlyingErrors: nil + ) + logger.error(error: error) + throw error } - return .success + return try .init(from: secret) + } - - func findSecrets( - secretids: [String], - cb: OnFindSecretsResult - ) -> ErrorCode { - Task { - do { - // Fixes a bug currently happening on didcomm library that is adding a / before the fragment sign - let secretidsaux = secretids.map { $0.replacingOccurrences(of: "/#", with: "#") } - let secrets = try await getListOfAllSecrets() - .filter { secretidsaux.contains($0.id) } - .map { $0.id } - let secretsSet = Set(secretidsaux) - let resultsSet = Set(secrets) - let missingSecrets = secretsSet.subtracting(resultsSet) - if !missingSecrets.isEmpty { - logger.error(message: -""" -Could not find secrets the following secrets: \(missingSecrets.joined(separator: ", ")) -""" - ) - } - try cb.success(result: secretids) - } catch { - let mercuryError = MercuryError.didcommError( - msg: "Could not find secrets \(secretids.joined(separator: "\n"))", - underlyingErrors: [error] - ) - logger.error(error: mercuryError) - } + func findKeys(kids: Set) async throws -> Set { + let secretidsaux = kids.map { $0.replacingOccurrences(of: "/#", with: "#") } + let secrets = try await getListOfAllSecrets() + .filter { secretidsaux.contains($0.id) } + .map { $0.id } + let secretsSet = Set(secretidsaux) + let resultsSet = Set(secrets) + let missingSecrets = secretsSet.subtracting(resultsSet) + if !missingSecrets.isEmpty { + let mercuryError = MercuryError.didcommError( + msg: "Could not find secrets \(missingSecrets.joined(separator: "\n"))", + underlyingErrors: nil + ) + logger.error(error: mercuryError) } - return .success + return kids } } -extension DIDCommxSwift.Secret { - init(from: Domain.Secret) { - let type: SecretType - let material: SecretMaterial - switch from.type { - case .jsonWebKey2020: - type = .jsonWebKey2020 +extension DIDCommSwift.Secret { + init(from: Domain.Secret) throws { + let type: KnownVerificationMaterialType + let material: VerificationMaterial + let jwkData: Data + switch from.secretMaterial { + case .jwk(let value): + jwkData = try value.tryToData() + } + + let jwk = try JSONDecoder().decode(DIDCore.JWK.self, from: jwkData) + + switch (from.type, jwk.crv?.lowercased()) { + case (.jsonWebKey2020, "x25519"): + type = .agreement(.jsonWebKey2020) + case (.jsonWebKey2020, "ed25519"): + type = .authentication(.jsonWebKey2020) + default: + type = .authentication(.jsonWebKey2020) } switch from.secretMaterial { case let .jwk(value): - material = .jwk(value: value) + material = try .fromJWK(jwk: jwk) } self.init( - id: from.id, + kid: from.id, type: type, - secretMaterial: material + verificationMaterial: material ) } } diff --git a/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/PackEncryptedOperation.swift b/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/PackEncryptedOperation.swift index 07925b66..91a76acb 100644 --- a/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/PackEncryptedOperation.swift +++ b/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/PackEncryptedOperation.swift @@ -1,17 +1,17 @@ import Combine import Core -import DIDCommxSwift +import DIDCommSwift import Domain import Foundation -final class PackEncryptedOperation: OnPackEncryptedResult { - private let didcomm: DIDCommProtocol +final class PackEncryptedOperation { + private let didcomm: DIDComm private let logger: PrismLogger private let message: Domain.Message private var published = CurrentValueSubject(nil) private var cancellable: AnyCancellable? - init(didcomm: DIDCommProtocol, message: Domain.Message, logger: PrismLogger) { + init(didcomm: DIDComm, message: Domain.Message, logger: PrismLogger) { self.didcomm = didcomm self.logger = logger self.message = message @@ -20,86 +20,12 @@ final class PackEncryptedOperation: OnPackEncryptedResult { func packEncrypted() async throws -> String { guard let fromDID = message.from else { throw MercuryError.noSenderDIDSetError } guard let toDID = message.to else { throw MercuryError.noRecipientDIDSetError } - - let result: String = try await withCheckedThrowingContinuation { [weak self] continuation in - guard let self else { return } - self.cancellable = self.published - .drop(while: { $0 == nil }) - .first() - .sink(receiveCompletion: { [weak self] in - switch $0 { - case .finished: - break - case let .failure(error): - self?.logger.error( - message: "Could not pack message", - metadata: [ - .publicMetadata(key: "Error", value: error.localizedDescription) - ] - ) - continuation.resume(throwing: error) - } - }, receiveValue: { - guard let result = $0 else { return } - continuation.resume(returning: result) - }) - do { - logger.debug(message: "Packing message \(message.piuri)", metadata: [ - .maskedMetadataByLevel(key: "Sender", value: fromDID.string, level: .debug), - .maskedMetadataByLevel(key: "Receiver", value: toDID.string, level: .debug) - ]) - let status = didcomm.packEncrypted( - msg: try DIDCommxSwift.Message(domain: message, mediaType: .contentTypePlain), - to: toDID.string, - from: fromDID.string, - signBy: nil, - options: .init( - protectSender: false, - forward: false, - forwardHeaders: nil, - messagingService: nil, - encAlgAuth: .a256cbcHs512Ecdh1puA256kw, - encAlgAnon: .xc20pEcdhEsA256kw - ), - cb: self - ) - switch status { - case.success: - break - case .error: - continuation.resume(throwing: MercuryError.didcommError( - msg: "Unknown error on initializing pack encrypted function" - )) - } - } catch { - continuation.resume(throwing: MercuryError.didcommError( - msg: "Error on parsing Domain message to DIDComm library model: \(error.localizedDescription)" - )) - } - } - return result - } - - func success(result: String, metadata: PackEncryptedMetadata) { - published.send(result) - published.send(completion: .finished) - } - - func error(err: DIDCommxSwift.ErrorKind, msg: String) { - let error = MercuryError.didcommError( - msg: """ -Error on trying to pack encrypted a message of type \(message.piuri): \(msg) -""" - ) - logger.error( - message: "Packing message failed with error", - metadata: [ - .publicMetadata( - key: "Error", - value: error.errorDescription ?? "" - ) - ] - ) - published.send(completion: .failure(error)) + + return try await didcomm.packEncrypted(params: .init( + message: .init(domain: message, mediaType: .contentTypeEncrypted), + to: [toDID.string], + from: fromDID.string, + encAlgAuth: .a256CBCHS512 + )).packedMessage } } diff --git a/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/UnpackOperation.swift b/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/UnpackOperation.swift index 5fa78939..cfc7612d 100644 --- a/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/UnpackOperation.swift +++ b/AtalaPrismSDK/Mercury/Sources/DIDCommWrappers/UnpackOperation.swift @@ -1,104 +1,25 @@ import Combine import Core -import DIDCommxSwift +import DIDCommSwift import Domain import Foundation -final class UnpackOperation: OnUnpackResult { - private let didcomm: DIDCommProtocol +final class UnpackOperation { + private let didcomm: DIDComm private let castor: Castor private let logger: PrismLogger private var published = CurrentValueSubject(nil) private var cancellable: AnyCancellable? - init(didcomm: DIDCommProtocol, castor: Castor, logger: PrismLogger) { + init(didcomm: DIDComm, castor: Castor, logger: PrismLogger) { self.didcomm = didcomm self.castor = castor self.logger = logger } func unpackEncrypted(messageString: String) async throws -> Domain.Message { - let status = didcomm.unpack( - msg: messageString, - options: .init(expectDecryptByAllKeys: false, unwrapReWrappingForward: false), - cb: self - ) - - switch status { - case.success: - return try await withCheckedThrowingContinuation { [weak self] continuation in - guard let self else { return } - self.cancellable = self.published - .drop(while: { $0 == nil }) - .first() - .sink(receiveCompletion: { [weak self] in - switch $0 { - case .finished: - break - case let .failure(error): - self?.logger.error( - message: "Could not unpack message", - metadata: [ - .publicMetadata(key: "Error", value: error.localizedDescription) - ] - ) - continuation.resume(throwing: error) - } - }, receiveValue: { - guard let result = $0 else { return } - continuation.resume(returning: result) - }) - } - case .error: - throw MercuryError.didcommError( - msg: "Unknown error on initializing unpack message function" - ) - } - } - - func success(result: DIDCommxSwift.Message, metadata: DIDCommxSwift.UnpackMetadata) { - do { - let message: Domain.Message = try result.toDomain(castor: castor) - published.send(message) - } catch let error as LocalizedError { - logger.error( - message: "Could not unpack message", - metadata: [ - .publicMetadata(key: "Error", value: error.localizedDescription) - ] - ) - published.send(completion: .failure(MercuryError.didcommError( - msg: -""" -Error on parsing DIDComm library model message to Domain message : \(error.errorDescription ?? "") -""" - ))) - } catch { - published.send(completion: .failure(MercuryError.didcommError( - msg: -""" -Error on parsing DIDComm library model message to Domain message : \(error.localizedDescription) -""" - ))) - } - } - - func error(err: DIDCommxSwift.ErrorKind, msg: String) { - let error = MercuryError.didcommError( - msg: -""" -Error on trying to unpack a message: \(msg) -""" - ) - logger.error( - message: "Unpack message failed with error", - metadata: [ - .publicMetadata( - key: "Error", - value: error.errorDescription ?? "" - ) - ] - ) - published.send(completion: .failure(error)) + return try await didcomm.unpack(params: .init( + packedMessage: messageString + )).message.toDomain(castor: castor) } } diff --git a/AtalaPrismSDK/Mercury/Sources/Helpers/DIDCommMessage+DomainParse.swift b/AtalaPrismSDK/Mercury/Sources/Helpers/DIDCommMessage+DomainParse.swift index 4a960695..fdafc48c 100644 --- a/AtalaPrismSDK/Mercury/Sources/Helpers/DIDCommMessage+DomainParse.swift +++ b/AtalaPrismSDK/Mercury/Sources/Helpers/DIDCommMessage+DomainParse.swift @@ -1,112 +1,129 @@ import Core -import DIDCommxSwift +import DIDCommSwift import Domain import Foundation -extension DIDCommxSwift.Message { +extension DIDCommSwift.Message { init(domain: Domain.Message, mediaType: MediaType) throws { - let jsonString = String(data: domain.body, encoding: .utf8) ?? "{}" let from = domain.from?.string - let to = domain.to?.string self.init( id: domain.id, - typ: mediaType.rawValue, + body: try domain.body.isEmpty ? "{}".tryToData() : domain.body, type: domain.piuri, - body: jsonString.isEmpty ? "{}" : jsonString, - from: domain.from?.string, + typ: .plainText, + from: from, to: domain.to.map { [$0.string] }, + createdTime: domain.createdTime, + expiresTime: domain.expiresTimePlus, + fromPrior: domain.fromPrior.flatMap { + try? JSONDecoder().decode(FromPrior.self, from: $0.tryToData()) + }, + fromPriorJwt: nil, + attachments: try domain.attachments.map { try .init(domain: $0) }, + pleaseAck: nil, + ack: domain.ack.first, thid: domain.thid, pthid: domain.pthid, - extraHeaders: domain.extraHeaders, - createdTime: domain.createdTime.millisecondsSince1970, - expiresTime: domain.expiresTimePlus.millisecondsSince1970, - fromPrior: domain.fromPrior, - attachments: try domain.attachments.map { - try Attachment(domain: $0) - } + customHeaders: domain.extraHeaders ) } func toDomain(castor: Castor) throws -> Domain.Message { - guard let data = self.body.data(using: .utf8) else { - throw MercuryError.messageInvalidBodyDataError - } - let message = Domain.Message( + Domain.Message( id: self.id, piuri: self.type, - from: try self.from.map { try DID(string: $0) }, - to: try self.to?.first.map { try DID(string: $0) }, - fromPrior: self.fromPrior, - body: data, - extraHeaders: self.extraHeaders, - createdTime: self.createdTime - .map { Date(milliseconds: $0) } ?? Date(), - expiresTimePlus: self.expiresTime - .map { Date(milliseconds: $0) } ?? Date(), + from: try self.from.map { try Domain.DID(string: $0) }, + to: try self.to?.first.map { try Domain.DID(string: $0) }, + fromPrior: try self.fromPrior.map { try JSONEncoder.didComm().encode($0) }?.tryToString(), + body: self.body ?? Data(), + extraHeaders: self.customHeaders ?? [:], + createdTime: self.createdTime ?? Date(), + expiresTimePlus: self.expiresTime ?? Date(), attachments: try self.attachments?.map { try $0.toDomain() } ?? [], thid: self.thid, pthid: self.pthid, - ack: [] + ack: self.ack.map { [$0] } ?? [] ) - return message } } -extension DIDCommxSwift.Attachment { +extension DIDCommSwift.Attachment { init(domain: Domain.AttachmentDescriptor) throws { self.init( - data: try .init(domain: domain.data), id: domain.id, + data: try domain.data.toDIDComm(), description: domain.description, - filename: domain.filename?.joined(separator: "/"), + filename: domain.filename?.first, mediaType: domain.mediaType, format: domain.format, - lastmodTime: domain.lastmodTime.map { UInt64($0.timeIntervalSince1970) }, - byteCount: domain.byteCount.map { UInt64($0) } + lastModTime: domain.lastmodTime, + byteCount: domain.byteCount ) } func toDomain() throws -> Domain.AttachmentDescriptor { - guard let id = self.id else { throw MercuryError.messageAttachmentWithoutIDError } return .init( id: id, mediaType: self.mediaType, data: try self.data.toDomain(), filename: self.filename?.components(separatedBy: "/"), format: self.format, - lastmodTime: self.lastmodTime.map { Date(timeIntervalSince1970: TimeInterval($0)) }, + lastmodTime: self.lastModTime, byteCount: self.byteCount.map { Int($0) }, description: self.description ) } } -extension DIDCommxSwift.AttachmentData { - init(domain: Domain.AttachmentData) throws { - if let base64Data = domain as? AttachmentBase64 { - self = .base64(value: .init(base64: base64Data.base64, jws: nil)) - } else if let linkData = domain as? AttachmentLinkData { - self = .links(value: .init(links: linkData.links, hash: linkData.hash, jws: nil)) - } else if let jsonData = domain as? AttachmentJsonData { - self = .json(value: .init(json: String(data: jsonData.data, encoding: .utf8)!, jws: nil)) - } else if let jwsData = domain as? AttachmentJwsData { - self = .base64(value: .init(base64: jwsData.base64, jws: jwsData.jws.signature)) +extension Domain.AttachmentData { + func toDIDComm() throws -> DIDCommSwift.AttachmentData { + if let base64Data = self as? AttachmentBase64 { + return Base64AttachmentData( + hash: nil, + jws: nil, + base64: base64Data.base64 + ) + } else if let linkData = self as? AttachmentLinkData { + return LinksAttachmentData( + hash: linkData.hash, + jws: nil, + links: linkData.links + ) + } else if let jsonData = self as? AttachmentJsonData { + return try JsonAttachmentData( + hash: nil, + jws: nil, + json: jsonData.data.tryToString() + ) + } else if let jwsData = self as? AttachmentJwsData { + return Base64AttachmentData( + hash: nil, + jws: jwsData.base64, + base64: jwsData.jws.signature + ) } else { throw MercuryError.unknownAttachmentDataTypeError } } +} +extension DIDCommSwift.AttachmentData { func toDomain() throws -> Domain.AttachmentData { switch self { - case let .base64(value): + case let value as Base64AttachmentData: return AttachmentBase64(base64: value.base64) - case let .links(value): - return AttachmentLinkData(links: value.links, hash: value.hash) - case let .json(value): + case let value as LinksAttachmentData: + guard let hash = value.hash else { + throw MercuryError.unknownAttachmentDataTypeError + } + return AttachmentLinkData(links: value.links, hash: hash) + case let value as JsonAttachmentData: guard let jsonData = value.json.data(using: .utf8) else { throw MercuryError.unknownAttachmentDataTypeError } return AttachmentJsonData(data: jsonData) + default: + throw MercuryError.unknownAttachmentDataTypeError } } } diff --git a/AtalaPrismSDK/Mercury/Sources/MercuryImpl+Public.swift b/AtalaPrismSDK/Mercury/Sources/MercuryImpl+Public.swift index d54c3a6c..889338e7 100644 --- a/AtalaPrismSDK/Mercury/Sources/MercuryImpl+Public.swift +++ b/AtalaPrismSDK/Mercury/Sources/MercuryImpl+Public.swift @@ -1,5 +1,5 @@ import Core -import DIDCommxSwift +import DIDCommSwift import Domain import Foundation diff --git a/AtalaPrismSDK/Mercury/Sources/MercuryImpl.swift b/AtalaPrismSDK/Mercury/Sources/MercuryImpl.swift index 9f44cee4..3db2afca 100644 --- a/AtalaPrismSDK/Mercury/Sources/MercuryImpl.swift +++ b/AtalaPrismSDK/Mercury/Sources/MercuryImpl.swift @@ -1,6 +1,6 @@ import Combine import Core -import DIDCommxSwift +import DIDCommSwift import Domain import Foundation @@ -23,14 +23,14 @@ public struct MercuryImpl { self.castor = castor } - func getDidcomm() -> DidComm { + func getDidcomm() -> DIDComm { let didResolver = DIDCommDIDResolverWrapper(castor: castor, logger: logger) let secretsResolver = DIDCommSecretsResolverWrapper( secretsStream: secretsStream, castor: castor, logger: logger ) - return DidComm( + return DIDComm( didResolver: didResolver, secretResolver: secretsResolver ) diff --git a/AtalaPrismSDK/Pluto/Sources/Domain/Providers/KeyProvider.swift b/AtalaPrismSDK/Pluto/Sources/Domain/Providers/KeyProvider.swift new file mode 100644 index 00000000..fa95e10d --- /dev/null +++ b/AtalaPrismSDK/Pluto/Sources/Domain/Providers/KeyProvider.swift @@ -0,0 +1,8 @@ +import Combine +import Domain +import Foundation + +protocol KeyProvider { + func getAll() -> AnyPublisher<[StorableKey], Error> + func getKeyById(id: String) -> AnyPublisher +} diff --git a/AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift b/AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift index 4a0b44c6..439d9079 100644 --- a/AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift +++ b/AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift @@ -2,6 +2,7 @@ import Domain import Foundation struct StorableKeyModel: StorableKey { + var identifier: String let restorationIdentifier: String let storableData: Data let index: Int? diff --git a/AtalaPrismSDK/Pluto/Sources/Helpers/Message+Codable.swift b/AtalaPrismSDK/Pluto/Sources/Helpers/Message+Codable.swift index 39a1bfdb..b235852b 100644 --- a/AtalaPrismSDK/Pluto/Sources/Helpers/Message+Codable.swift +++ b/AtalaPrismSDK/Pluto/Sources/Helpers/Message+Codable.swift @@ -48,17 +48,17 @@ struct CodableMessage: Codable { let id = try container.decode(String.self, forKey: .id) let piuri = try container.decode(String.self, forKey: .piuri) let body = try container.decode(Data.self, forKey: .body) - let extraHeaders = try container.decode([String: String].self, forKey: .extraHeaders) - let createdTime = try container.decode(Date.self, forKey: .createdTime) - let expiresTimePlus = try container.decode(Date.self, forKey: .expiresTimePlus) - let attachments = try container.decode([AttachmentDescriptor].self, forKey: .attachments) - let ack = try container.decode([String].self, forKey: .ack) - let from = try? container.decode(CodableDID.self, forKey: .from).did - let to = try? container.decode(CodableDID.self, forKey: .to).did - let fromPrior = try? container.decode(String.self, forKey: .fromPrior) - let thid = try? container.decode(String.self, forKey: .thid) - let pthid = try? container.decode(String.self, forKey: .pthid) - let directionRaw = try? container.decode(String.self, forKey: .direction) + let extraHeaders = try container.decodeIfPresent([String: String].self, forKey: .extraHeaders) + let createdTime = try container.decodeIfPresent(Date.self, forKey: .createdTime) + let expiresTimePlus = try container.decodeIfPresent(Date.self, forKey: .expiresTimePlus) + let attachments = try container.decodeIfPresent([AttachmentDescriptor].self, forKey: .attachments) + let ack = try container.decodeIfPresent([String].self, forKey: .ack) + let from = try? container.decodeIfPresent(CodableDID.self, forKey: .from)?.did + let to = try? container.decodeIfPresent(CodableDID.self, forKey: .to)?.did + let fromPrior = try? container.decodeIfPresent(String.self, forKey: .fromPrior) + let thid = try? container.decodeIfPresent(String.self, forKey: .thid) + let pthid = try? container.decodeIfPresent(String.self, forKey: .pthid) + let directionRaw = try container.decodeIfPresent(String.self, forKey: .direction) let direction = directionRaw.flatMap { Message.Direction(rawValue: $0) } self.init(message: .init( @@ -68,13 +68,13 @@ struct CodableMessage: Codable { to: to, fromPrior: fromPrior, body: body, - extraHeaders: extraHeaders, - createdTime: createdTime, + extraHeaders: extraHeaders ?? [:], + createdTime: createdTime ?? Date(), expiresTimePlus: expiresTimePlus, - attachments: attachments, + attachments: attachments ?? [], thid: thid, pthid: pthid, - ack: ack, + ack: ack ?? [], direction: direction ?? .sent )) } diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyStore.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyStore.swift index 9fc4a8d4..ee670bf9 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyStore.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyStore.swift @@ -10,19 +10,18 @@ extension CDDIDPrivateKeyDAO: DIDPrivateKeyStore { let keys = try privateKeys.map { switch $0 { case let keychainKey as KeychainStorableKey: - let identifier = computeStorableKeyIdentifier(keychainKey, did: did.string) try storeKeychainKey( did: did, keychainKey: keychainKey, service: self.keyDao.keychainDao.keychainService, - account: identifier, + account: keychainKey.identifier, keychain: self.keyDao.keychainDao.keychain ) let cdkey = CDKeychainKey(entity: CDKeychainKey.entity(), insertInto: context) cdkey.parseFromStorableKey( keychainKey, did: cdobj, - identifier: identifier, + identifier: keychainKey.identifier, service: self.keyDao.keychainDao.keychainService ) return cdkey as CDKey @@ -31,7 +30,7 @@ extension CDDIDPrivateKeyDAO: DIDPrivateKeyStore { cdkey.parseFromStorableKey( $0, did: cdobj, - identifier: computeStorableKeyIdentifier($0, did: did.string) + identifier: $0.identifier ) return cdkey as CDKey } @@ -64,14 +63,6 @@ private func storeKeychainKey( ) } -private func computeStorableKeyIdentifier(_ key: StorableKey, did: String) -> String { - var returnData = Data() - returnData += did.data(using: .utf8) ?? Data() - returnData += key.restorationIdentifier.data(using: .utf8) ?? Data() - returnData += key.storableData.base64EncodedData() - return SHA256.hash(data: returnData).string -} - private extension CDDIDPrivateKey { func parseFrom(did: DID, alias: String?) { self.alias = alias diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO+KeyProvider.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO+KeyProvider.swift new file mode 100644 index 00000000..5ae5c4a2 --- /dev/null +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO+KeyProvider.swift @@ -0,0 +1,21 @@ +import Combine +import Foundation +import Domain + +extension CDKeyDAO: KeyProvider { + func getAll() -> AnyPublisher<[StorableKey], Error> { + fetchController(context: readContext) + .tryMap { + try $0.map { try $0.parseToStorableKey(keychain: self.keychainDao.keychain) } + } + .eraseToAnyPublisher() + } + + func getKeyById(id: String) -> AnyPublisher { + fetchByIDsPublisher(id, context: readContext) + .tryMap { + try $0?.parseToStorableKey(keychain: self.keychainDao.keychain) + } + .eraseToAnyPublisher() + } +} diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO+LinkSecretStore.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO+LinkSecretStore.swift index c27bcccf..546e43ae 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO+LinkSecretStore.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO+LinkSecretStore.swift @@ -6,26 +6,26 @@ extension CDKeyDAO: LinkSecretStore { func addLinkSecret(_ linkSecret: StorableKey) -> AnyPublisher { switch linkSecret { case let keychainKey as KeychainStorableKey: - return keychainDao.updateOrCreate("linkSecret", context: writeContext) { cdobj, context in + return keychainDao.updateOrCreate(linkSecret.identifier, context: writeContext) { cdobj, context in try storeKeychainKey( keychainKey: keychainKey, service: self.keychainDao.keychainService, - account: "linkSecret", + account: linkSecret.identifier, keychain: self.keychainDao.keychain ) cdobj.parseFromStorableKey( keychainKey, - identifier: "linkSecret", + identifier: linkSecret.identifier, service: self.keychainDao.keychainService ) } .map { _ in } .eraseToAnyPublisher() default: - return databaseDAO.updateOrCreate("linkSecret", context: writeContext) { cdobj, context in + return databaseDAO.updateOrCreate(linkSecret.identifier, context: writeContext) { cdobj, context in cdobj.parseFromStorableKey( linkSecret, - identifier: "linkSecret" + identifier: linkSecret.identifier ) } .map { _ in } diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO.swift index 81ca0b66..509e8aa1 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDKeyDAO.swift @@ -51,12 +51,14 @@ extension CDKey { ) return StorableKeyModel( + identifier: keychainKey.identifier, restorationIdentifier: keychainKey.restorationIdentifier, storableData: keyData, index: keychainKey.index?.intValue ) case let databaseKey as CDDatabaseKey: return StorableKeyModel( + identifier: databaseKey.identifier, restorationIdentifier: databaseKey.restorationIdentifier, storableData: databaseKey.storableData, index: databaseKey.index?.intValue diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageProvider.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageProvider.swift index 3801de96..3258730f 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageProvider.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDMessageDAO+MessageProvider.swift @@ -84,6 +84,6 @@ extension CDMessageDAO: MessageProvider { private extension CDMessage { func toDomain() throws -> Message { - return try JSONDecoder().decode(CodableMessage.self, from: dataJson).message + try JSONDecoder().decode(CodableMessage.self, from: dataJson).message } } diff --git a/AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift b/AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift index 4f4c550c..1c67b254 100644 --- a/AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift +++ b/AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift @@ -112,6 +112,14 @@ extension PlutoImpl: Pluto { pairDIDDao.getAll() } + public func getAllKeys() -> AnyPublisher<[StorableKey], Error> { + keyDao.getAll() + } + + public func getKeyById(id: String) -> AnyPublisher { + keyDao.getKeyById(id: id) + } + public func getPair(otherDID: DID) -> AnyPublisher { pairDIDDao.getPair(otherDID: otherDID) } diff --git a/AtalaPrismSDK/Pluto/Tests/Helper/PrivateKey+Test.swift b/AtalaPrismSDK/Pluto/Tests/Helper/PrivateKey+Test.swift index e70971df..d98d28c8 100644 --- a/AtalaPrismSDK/Pluto/Tests/Helper/PrivateKey+Test.swift +++ b/AtalaPrismSDK/Pluto/Tests/Helper/PrivateKey+Test.swift @@ -6,6 +6,7 @@ struct MockPrivateKey: PrivateKey, StorableKey, Equatable { let keySpecifications: [String : String] let size = 0 let raw: Data + var identifier = "TestMockPrivKey" var index: Int? = 0 let restorationIdentifier = "MockPrivate" @@ -39,6 +40,7 @@ struct MockPublicKey: PublicKey, Equatable { let keySpecifications = [String : String]() let size = 0 let raw: Data + var identifier = "TestMockPubKey" init(str: String = "TestPublic") { self.raw = str.data(using: .utf8)! diff --git a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack.swift b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack.swift index 414309e1..b13bad32 100644 --- a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack.swift +++ b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack.swift @@ -16,7 +16,7 @@ extension AnoncredsCredentialStack: Domain.Credential { assert(false, "This should never happen") return "" } - return jsonData.sha256.hex + return jsonData.sha256().hex } var issuer: String { diff --git a/AtalaPrismSDK/Pollux/Sources/Models/JWT/JWTPresentation.swift b/AtalaPrismSDK/Pollux/Sources/Models/JWT/JWTPresentation.swift index e5781424..93c052c2 100644 --- a/AtalaPrismSDK/Pollux/Sources/Models/JWT/JWTPresentation.swift +++ b/AtalaPrismSDK/Pollux/Sources/Models/JWT/JWTPresentation.swift @@ -1,9 +1,10 @@ import Domain import Foundation -import SwiftJWT - -struct VerifiablePresentationPayload: Claims { +import JSONWebSignature +import JSONWebToken +struct VerifiablePresentationPayload: JWTRegisteredFieldsClaims { + struct VerifiablePresentation: Codable { enum CodingKeys: String, CodingKey { case context = "@context" @@ -16,10 +17,17 @@ struct VerifiablePresentationPayload: Claims { let verifiableCredential: [String] } - let iss: String - let aud: String + let issuer: String? + let subject: String? + let audience: [String]? + let expirationTime: Date? + let notBeforeTime: Date? + let issuedAt: Date? + let jwtID: String? let nonce: String let vp: [VerifiablePresentation] + + func validateExtraClaims() throws {} } struct JWTPresentation { @@ -67,23 +75,53 @@ struct JWTPresentation { let domain = findValue(forKey: "domain", in: jsonObject), let challenge = findValue(forKey: "challenge", in: jsonObject) else { throw PolluxError.offerDoesntProvideEnoughInformation } - - let jwt = JWT(claims: ClaimsProofPresentationJWT( - iss: did.string, - aud: domain, - nonce: challenge, - vp: .init( - context: .init(["https://www.w3.org/2018/presentations/v1"]), - type: .init(["VerifiablePresentation"]), - verifiableCredential: [credential.jwtString] + + let keyJWK = exportableKey.jwk + + let jwt = try JWT.signed( + payload: ClaimsProofPresentationJWT( + issuer: did.string, + subject: nil, + audience: [domain], + expirationTime: nil, + notBeforeTime: nil, + issuedAt: nil, + jwtID: nil, + nonce: challenge, + vp: .init( + context: .init(["https://www.w3.org/2018/presentations/v1"]), + type: .init(["VerifiablePresentation"]), + verifiableCredential: [credential.jwtString] + ) + ), + protectedHeader: DefaultJWSHeaderImpl(algorithm: .ES256K), + key: .init( + keyType: .init(rawValue: keyJWK.kty)!, + keyID: keyJWK.kid, + x: keyJWK.x.flatMap { Data(fromBase64URL: $0) }, + y: keyJWK.y.flatMap { Data(fromBase64URL: $0) }, + d: keyJWK.d.flatMap { Data(fromBase64URL: $0) } ) - )) - let jwtString = try JWTEncoder(jwtSigner: .es256k(privateKey: pemData)).encodeToString(jwt) - return jwtString + ) + + // We need to do for now this process so the signatures of secp256k1 Bitcoin can be verified by Bouncy castle + let jwtString = jwt.jwtString + var components = jwtString.components(separatedBy: ".") + guard + let signature = components.last, + let signatureData = Data(fromBase64URL: signature) + else { + return jwtString + } + + let (r, s) = extractRS(from: signatureData) + let fipsSignature = (Data(r.reversed()) + Data(s.reversed())).base64UrlEncodedString() + _ = components.removeLast() + return (components + [fipsSignature]).joined(separator: ".") } } -private struct ClaimsProofPresentationJWT: Claims { +private struct ClaimsProofPresentationJWT: JWTRegisteredFieldsClaims { struct VerifiablePresentation: Codable { enum CodingKeys: String, CodingKey { case context = "@context" @@ -96,8 +134,35 @@ private struct ClaimsProofPresentationJWT: Claims { let verifiableCredential: [String] } - let iss: String - let aud: String + let issuer: String? + let subject: String? + let audience: [String]? + let expirationTime: Date? + let notBeforeTime: Date? + let issuedAt: Date? + let jwtID: String? let nonce: String let vp: VerifiablePresentation + + func validateExtraClaims() throws {} + + enum CodingKeys: String, CodingKey { + case issuer = "iss" + case subject = "sub" + case audience = "aud" + case expirationTime = "exp" + case notBeforeTime = "nbf" + case issuedAt = "iat" + case jwtID = "jti" + case nonce + case vp + } +} + +private func extractRS(from signature: Data) -> (r: Data, s: Data) { + let rIndex = signature.startIndex + let sIndex = signature.index(rIndex, offsetBy: 32) + let r = signature[rIndex.. String { + static func create(didStr: String, key: ExportableKey, offerData: Data) throws -> String { let jsonObject = try JSONSerialization.jsonObject(with: offerData) guard let domain = findValue(forKey: "domain", in: jsonObject), let challenge = findValue(forKey: "challenge", in: jsonObject) else { throw PolluxError.offerDoesntProvideEnoughInformation } - let jwt = JWT(claims: ClaimsRequestSignatureJWT( - iss: didStr, - aud: domain, - nonce: challenge, - vp: .init(context: .init([ - "https://www.w3.org/2018/presentations/v1" - ]), type: .init([ - "VerifiablePresentation" - ])) - )) + let keyJWK = key.jwk - return try JWTEncoder(jwtSigner: .es256k(privateKey: pem)).encodeToString(jwt) + let jwt = try JWT.signed( + payload: ClaimsRequestSignatureJWT( + issuer: didStr, + subject: nil, + audience: [domain], + expirationTime: nil, + notBeforeTime: nil, + issuedAt: nil, + jwtID: nil, + nonce: challenge, + vp: .init(context: .init([ + "https://www.w3.org/2018/presentations/v1" + ]), type: .init([ + "VerifiablePresentation" + ])) + ), + protectedHeader: DefaultJWSHeaderImpl(algorithm: .ES256K), + key: .init( + keyType: .init(rawValue: keyJWK.kty)!, + keyID: keyJWK.kid, + x: keyJWK.x.flatMap { Data(fromBase64URL: $0) }, + y: keyJWK.y.flatMap { Data(fromBase64URL: $0) }, + d: keyJWK.d.flatMap { Data(fromBase64URL: $0) } + ) + ) + + // We need to do for now this process so the signatures of secp256k1 Bitcoin can be verified by Bouncy castle + let jwtString = jwt.jwtString + var components = jwtString.components(separatedBy: ".") + guard + let signature = components.last, + let signatureData = Data(fromBase64URL: signature) + else { + return jwtString + } + + let (r, s) = extractRS(from: signatureData) + let fipsSignature = (Data(r.reversed()) + Data(s.reversed())).base64UrlEncodedString() + _ = components.removeLast() + return (components + [fipsSignature]).joined(separator: ".") } } -struct ClaimsRequestSignatureJWT: Claims { +struct ClaimsRequestSignatureJWT: JWTRegisteredFieldsClaims { struct VerifiablePresentation: Codable { enum CodingKeys: String, CodingKey { case context = "@context" @@ -44,10 +75,29 @@ struct ClaimsRequestSignatureJWT: Claims { let type: Set } - let iss: String - let aud: String + let issuer: String? + let subject: String? + let audience: [String]? + let expirationTime: Date? + let notBeforeTime: Date? + let issuedAt: Date? + let jwtID: String? let nonce: String let vp: VerifiablePresentation + + func validateExtraClaims() throws {} + + enum CodingKeys: String, CodingKey { + case issuer = "iss" + case subject = "sub" + case audience = "aud" + case expirationTime = "exp" + case notBeforeTime = "nbf" + case issuedAt = "iat" + case jwtID = "jti" + case nonce + case vp + } } @@ -71,3 +121,11 @@ func findValue(forKey key: String, in json: Any) -> String? { } return nil } + +private func extractRS(from signature: Data) -> (r: Data, s: Data) { + let rIndex = signature.startIndex + let sIndex = signature.index(rIndex, offsetBy: 32) + let r = signature[rIndex.. AnyPublisher<[Domain.StorableKey]?, Error> { Just(nil).tryMap { $0 }.eraseToAnyPublisher() } - + + func getAllKeys() -> AnyPublisher<[StorableKey], Error> { + Just([]).tryMap { $0 }.eraseToAnyPublisher() + } + + func getKeyById(id: String) -> AnyPublisher { + Just(nil).tryMap { $0 }.eraseToAnyPublisher() + } + func getAllDidPairs() -> AnyPublisher<[Domain.DIDPair], Error> { Just([]).tryMap { $0 }.eraseToAnyPublisher() } diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Credentials.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Credentials.swift index 3346efc2..c65fa6fc 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Credentials.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Credentials.swift @@ -2,7 +2,7 @@ import Core import Combine import Domain import Foundation -import SwiftJWT +import JSONWebToken // MARK: Verifiable credentials functionalities public extension PrismAgent { diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift index 49fa0c2d..980efd5b 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift @@ -133,12 +133,12 @@ Could not find key in storage please use Castor instead and provide the private alias: String? = "", updateMediator: Bool ) async throws -> DID { - let keyAgreementPrivateKey = try apollo.createPrivateKey(parameters: [ + var keyAgreementPrivateKey = try apollo.createPrivateKey(parameters: [ KeyProperties.type.rawValue: "EC", KeyProperties.curve.rawValue: KnownKeyCurves.x25519.rawValue ]) - let authenticationPrivateKey = try apollo.createPrivateKey(parameters: [ + var authenticationPrivateKey = try apollo.createPrivateKey(parameters: [ KeyProperties.type.rawValue: "EC", KeyProperties.curve.rawValue: KnownKeyCurves.ed25519.rawValue ]) @@ -161,6 +161,10 @@ Could not find key in storage please use Castor instead and provide the private services: withServices ) + let didDocument = try await castor.resolveDID(did: newDID) + didDocument.authenticate.first.map { authenticationPrivateKey.identifier = $0.id.string } + didDocument.keyAgreement.first.map { keyAgreementPrivateKey.identifier = $0.id.string } + logger.debug(message: "Created new Peer DID", metadata: [ .maskedMetadataByLevel(key: "DID", value: newDID.string, level: .debug) ]) diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Proof.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Proof.swift index adc2ce6d..e2db1860 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Proof.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Proof.swift @@ -2,7 +2,7 @@ import Core import Combine import Domain import Foundation -import SwiftJWT +import JSONWebToken // MARK: Credentials proof functionalities public extension PrismAgent { @@ -85,7 +85,6 @@ public extension PrismAgent { comment: request.body.comment ), attachments: [.init( - mediaType: "prism/jwt", data: AttachmentBase64(base64: base64String) )], thid: request.thid, diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent.swift index 02571fd6..9d440af5 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent.swift @@ -219,49 +219,41 @@ private func createSecretsStream( pluto: Pluto, castor: Castor ) -> AnyPublisher<[Secret], Error> { - pluto.getAllPeerDIDs() + pluto.getAllKeys() .first() - .flatMap { array in + .flatMap { keys in Future { - try await array.asyncMap { did, privateKeys, _ in - let privateKeys = try await privateKeys.asyncMap { - try await keyRestoration.restorePrivateKey($0) - } - return try parsePrivateKeys( - did: did, - privateKeys: privateKeys, - castor: castor - ) - } + let privateKeys = await keys.asyncMap { + try? await keyRestoration.restorePrivateKey($0) + }.compactMap { $0 } + return try parsePrivateKeys( + privateKeys: privateKeys, + castor: castor + ) } } - .map { $0.compactMap { $0 }.flatMap { $0 } } .eraseToAnyPublisher() } private func parsePrivateKeys( - did: DID, privateKeys: [PrivateKey], castor: Castor ) throws -> [Domain.Secret] { return try privateKeys - .map { $0 as? (PrivateKey & ExportableKey) } + .map { $0 as? (PrivateKey & ExportableKey & StorableKey) } .compactMap { $0 } .map { privateKey in - let ecnumbasis = try castor.getEcnumbasis(did: did, publicKey: privateKey.publicKey()) - return (did, privateKey, ecnumbasis) + return privateKey } - .map { did, privateKey, ecnumbasis in + .map { privateKey in try parseToSecret( - did: did, privateKey: privateKey, - ecnumbasis: ecnumbasis + identifier: privateKey.identifier ) } } -private func parseToSecret(did: DID, privateKey: PrivateKey & ExportableKey, ecnumbasis: String) throws -> Domain.Secret { - let id = did.string + "#" + ecnumbasis +private func parseToSecret(privateKey: PrivateKey & ExportableKey, identifier: String) throws -> Domain.Secret { let jwk = privateKey.jwk guard let dataJson = try? JSONEncoder().encode(jwk), @@ -270,7 +262,7 @@ private func parseToSecret(did: DID, privateKey: PrivateKey & ExportableKey, ecn throw CommonError.invalidCoding(message: "Could not encode privateKey.jwk") } return .init( - id: id, + id: identifier, type: .jsonWebKey2020, secretMaterial: .jwk(value: stringJson) ) diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationKeysUpdateList.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationKeysUpdateList.swift index f0a626e9..f004d916 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationKeysUpdateList.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationKeysUpdateList.swift @@ -39,7 +39,8 @@ struct MediationKeysUpdateList { piuri: type, from: from, to: to, - body: try JSONEncoder.didComm().encode(body) + body: try JSONEncoder.didComm().encode(body), + extraHeaders: ["return_route":"all"] ) } } diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationRequest.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationRequest.swift index 0ee4594a..b7ca7454 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationRequest.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationRequest.swift @@ -23,7 +23,8 @@ struct MediationRequest { piuri: type, from: from, to: to, - body: Data() + body: Data(), + extraHeaders: ["return_route":"all"] ) } } diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PickupRequest.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PickupRequest.swift index 936323ec..71d87e10 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PickupRequest.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PickupRequest.swift @@ -26,7 +26,8 @@ struct PickUpRequest { piuri: ProtocolTypes.pickupRequest.rawValue, from: from, to: to, - body: body + body: body, + extraHeaders: ["return_route":"all"] ) } } diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PrickupReceived.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PrickupReceived.swift index e3962557..83f87dd9 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PrickupReceived.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PrickupReceived.swift @@ -21,7 +21,8 @@ struct PickUpReceived { piuri: ProtocolTypes.pickupReceived.rawValue, from: from, to: to, - body: body + body: body, + extraHeaders: ["return_route":"all"] ) } } diff --git a/Package.swift b/Package.swift index 536ea64a..23b9d772 100644 --- a/Package.swift +++ b/Package.swift @@ -56,10 +56,9 @@ let package = Package( from: "1.4.4" ), .package(url: "git@github.com:apple/swift-protobuf.git", from: "1.7.0"), - .package(url: "git@github.com:input-output-hk/atala-prism-didcomm-swift.git", from: "0.3.6"), - .package(url: "git@github.com:swift-libp2p/swift-multibase.git", from: "0.0.1"), - .package(url: "git@github.com:GigaBitcoin/secp256k1.swift.git", exact: "0.10.0"), - .package(url: "git@github.com:goncalo-frade-iohk/Swift-JWT.git", from: "4.1.3"), + .package(url: "https://github.com/beatt83/didcomm-swift.git", from: "0.1.1"), + .package(url: "https://github.com/beatt83/jose-swift.git", from: "1.2.1"), + .package(url: "https://github.com/beatt83/peerdid-swift.git", from: "2.0.2"), .package(url: "https://github.com/input-output-hk/anoncreds-rs.git", exact: "0.4.1"), .package(url: "https://github.com/input-output-hk/atala-prism-apollo.git", exact: "1.2.10"), ], @@ -91,7 +90,6 @@ let package = Package( dependencies: [ "Domain", "Core", - .product(name: "secp256k1", package: "secp256k1.swift"), .product(name: "AnoncredsSwift", package: "anoncreds-rs"), .product(name: "ApolloLibrary", package: "atala-prism-apollo") ], @@ -102,7 +100,7 @@ let package = Package( dependencies: [ "Domain", "Core", - .product(name: "Multibase", package: "swift-multibase"), + .product(name: "PeerDID", package: "peerdid-swift"), .product(name: "SwiftProtobuf", package: "swift-protobuf") ], path: "AtalaPrismSDK/Castor/Sources" @@ -117,7 +115,7 @@ let package = Package( dependencies: [ "Domain", "Core", - .product(name: "SwiftJWT", package: "Swift-JWT"), + "jose-swift", .product(name: "AnoncredsSwift", package: "anoncreds-rs") ], path: "AtalaPrismSDK/Pollux/Sources" @@ -132,7 +130,7 @@ let package = Package( dependencies: [ "Domain", "Core", - .product(name: "DIDCommxSwift", package: "atala-prism-didcomm-swift") + "didcomm-swift" ], path: "AtalaPrismSDK/Mercury/Sources" ), @@ -165,8 +163,7 @@ let package = Package( dependencies: [ "Domain", "Builders", - "Core", - .product(name: "SwiftJWT", package: "Swift-JWT") + "Core" ], path: "AtalaPrismSDK/PrismAgent/Sources" ), diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift index b0677028..d97ddf06 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift @@ -91,56 +91,41 @@ private func createSecretsStream( pluto: Pluto, castor: Castor ) -> AnyPublisher<[Secret], Error> { - pluto.getAllPeerDIDs() + pluto.getAllKeys() .first() - .flatMap { array in + .flatMap { keys in Future { - try await array.asyncMap { did, privateKeys, _ in - let privateKeys = try await privateKeys.asyncMap { - try await keyRestoration.restorePrivateKey($0) - } - let secrets = try parsePrivateKeys( - did: did, - privateKeys: privateKeys, - castor: castor - ) - - return secrets - } + let privateKeys = await keys.asyncMap { + try? await keyRestoration.restorePrivateKey($0) + }.compactMap { $0 } + return try parsePrivateKeys( + privateKeys: privateKeys, + castor: castor + ) } } - .map { - $0.compactMap { - $0 - }.flatMap { - $0 - } } .eraseToAnyPublisher() } private func parsePrivateKeys( - did: DID, privateKeys: [PrivateKey], castor: Castor ) throws -> [Domain.Secret] { return try privateKeys - .map { $0 as? (PrivateKey & ExportableKey) } + .map { $0 as? (PrivateKey & ExportableKey & StorableKey) } .compactMap { $0 } .map { privateKey in - let ecnumbasis = try castor.getEcnumbasis(did: did, publicKey: privateKey.publicKey()) - return (did, privateKey, ecnumbasis) + return privateKey } - .map { did, privateKey, ecnumbasis in + .map { privateKey in try parseToSecret( - did: did, privateKey: privateKey, - ecnumbasis: ecnumbasis + identifier: privateKey.identifier ) } } -private func parseToSecret(did: DID, privateKey: PrivateKey & ExportableKey, ecnumbasis: String) throws -> Domain.Secret { - let id = did.string + "#" + ecnumbasis +private func parseToSecret(privateKey: PrivateKey & ExportableKey, identifier: String) throws -> Domain.Secret { let jwk = privateKey.jwk guard let dataJson = try? JSONEncoder().encode(jwk), @@ -149,7 +134,7 @@ private func parseToSecret(did: DID, privateKey: PrivateKey & ExportableKey, ecn throw CommonError.invalidCoding(message: "Could not encode privateKey.jwk") } return .init( - id: id, + id: identifier, type: .jsonWebKey2020, secretMaterial: .jwk(value: stringJson) ) diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Mediator/MediatorPage/MediatorPageView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Mediator/MediatorPage/MediatorPageView.swift index 6290dd2d..b6f8b076 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Mediator/MediatorPage/MediatorPageView.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Mediator/MediatorPage/MediatorPageView.swift @@ -12,7 +12,7 @@ protocol MediatorPageViewModel: ObservableObject { struct MediatorPageView: View { @StateObject var viewModel: ViewModel - @State var didInput = "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0" + @State var didInput = "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vc2l0LXByaXNtLW1lZGlhdG9yLmF0YWxhcHJpc20uaW8iLCJhIjpbImRpZGNvbW0vdjIiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3NpdC1wcmlzbS1tZWRpYXRvci5hdGFsYXByaXNtLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIl19fQ" var body: some View { NavigationStack { diff --git a/Sample/DIDChat/DIDChat.xcodeproj/project.pbxproj b/Sample/DIDChat/DIDChat.xcodeproj/project.pbxproj index 1472b080..3e6aa42f 100644 --- a/Sample/DIDChat/DIDChat.xcodeproj/project.pbxproj +++ b/Sample/DIDChat/DIDChat.xcodeproj/project.pbxproj @@ -404,7 +404,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DIDChat/Preview Content\""; - DEVELOPMENT_TEAM = 89TW38X994; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; @@ -442,7 +442,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DIDChat/Preview Content\""; - DEVELOPMENT_TEAM = 89TW38X994; + DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; diff --git a/Sample/DIDChat/DIDChat/Modules/ContactsView/ContactsViewModel.swift b/Sample/DIDChat/DIDChat/Modules/ContactsView/ContactsViewModel.swift index c57c957f..29f32e4d 100644 --- a/Sample/DIDChat/DIDChat/Modules/ContactsView/ContactsViewModel.swift +++ b/Sample/DIDChat/DIDChat/Modules/ContactsView/ContactsViewModel.swift @@ -64,7 +64,6 @@ class ContactsViewModelImpl: ContactsViewModel { guard let self else { return } do { let holderDID = try await self.agent.createNewPeerDID(alias: alias, updateMediator: true) - print(holderDID.string) await MainActor.run { self.createdPeerDID = holderDID.string self.createdPeerDIDAlias = alias diff --git a/Sample/DIDChat/DIDChat/Modules/MediatorView/MediatorView.swift b/Sample/DIDChat/DIDChat/Modules/MediatorView/MediatorView.swift index 78ec7c78..20920f4d 100644 --- a/Sample/DIDChat/DIDChat/Modules/MediatorView/MediatorView.swift +++ b/Sample/DIDChat/DIDChat/Modules/MediatorView/MediatorView.swift @@ -22,7 +22,7 @@ protocol MediatorRouter { struct MediatorView: View { @StateObject var viewModel: ViewModel - @State var mediatorDID: String = "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0" + @State var mediatorDID: String = "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHBzOi8vc2l0LXByaXNtLW1lZGlhdG9yLmF0YWxhcHJpc20uaW8iLCJhIjpbImRpZGNvbW0vdjIiXX19.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6IndzczovL3NpdC1wcmlzbS1tZWRpYXRvci5hdGFsYXByaXNtLmlvL3dzIiwiYSI6WyJkaWRjb21tL3YyIl19fQ" @State var router: Router var body: some View { diff --git a/Sample/DIDChat/DIDChat/Modules/MediatorView/MediatorViewModel.swift b/Sample/DIDChat/DIDChat/Modules/MediatorView/MediatorViewModel.swift index 0acd34c4..959f5dec 100644 --- a/Sample/DIDChat/DIDChat/Modules/MediatorView/MediatorViewModel.swift +++ b/Sample/DIDChat/DIDChat/Modules/MediatorView/MediatorViewModel.swift @@ -36,7 +36,9 @@ class MediatorViewModelImpl: MediatorViewModel { await MainActor.run { [weak self] in self?.routeToContactsList = true } - } catch {} + } catch { + print(error) + } } }