Skip to content

Commit 8e9e959

Browse files
feat!(pollux): add jwt and anoncreds verification
BREAKING CHANGE: ProofTypes was replaced by ClaimFilter - This commit also has a fix with PRISM DID resolution, now it correctly resolves all public keys - It adds a new utility method to castor to retrieve all public keys from a DID Fixes ATL-6627
1 parent 931c843 commit 8e9e959

File tree

77 files changed

+3961
-335
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+3961
-335
lines changed

AtalaPrismSDK/Apollo/Sources/ApolloImpl+Public.swift

+2-3
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,9 @@ extension ApolloImpl: Apollo {
5858
switch curve {
5959
case .secp256k1:
6060
if
61-
let keyData = parameters[KeyProperties.rawKey.rawValue].flatMap({ Data(base64Encoded: $0) }),
62-
let derivationPathStr = parameters[KeyProperties.derivationPath.rawValue]
61+
let keyData = parameters[KeyProperties.rawKey.rawValue].flatMap({ Data(base64Encoded: $0) })
6362
{
64-
let derivationPath = try DerivationPath(string: derivationPathStr)
63+
let derivationPath = try parameters[KeyProperties.derivationPath.rawValue].map { try DerivationPath(string: $0) } ?? DerivationPath(index:0)
6564
return Secp256k1PrivateKey(raw: keyData, derivationPath: derivationPath)
6665
} else {
6766
guard

AtalaPrismSDK/Builders/Sources/PolluxBuilder.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import Pollux
33

44
public struct PolluxBuilder {
55
private let pluto: Pluto
6+
private let castor: Castor
67

7-
public init(pluto: Pluto) {
8+
public init(pluto: Pluto, castor: Castor) {
89
self.pluto = pluto
10+
self.castor = castor
911
}
1012

1113
public func build() -> Pollux {
12-
PolluxImpl(pluto: pluto)
14+
PolluxImpl(castor: castor, pluto: pluto)
1315
}
1416
}

AtalaPrismSDK/Castor/Sources/CastorImpl+Public.swift

+46
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,50 @@ extension CastorImpl: Castor {
129129
}
130130
return try await resolver.resolve(did: did)
131131
}
132+
133+
public func getDIDPublicKeys(did: DID) async throws -> [PublicKey] {
134+
let document = try await resolveDID(did: did)
135+
136+
return try await document.verificationMethods
137+
.asyncMap { verificationMethod -> PublicKey in
138+
try await verificationMethodToPublicKey(method: verificationMethod)
139+
}
140+
}
141+
142+
private func verificationMethodToPublicKey(method: DIDDocument.VerificationMethod) async throws -> PublicKey {
143+
switch method.type {
144+
case "EcdsaSecp256k1VerificationKey2019", "secp256k1":
145+
guard let multibaseData = method.publicKeyMultibase else {
146+
throw CastorError.cannotRetrievePublicKeyFromDocument
147+
}
148+
return try apollo.createPublicKey(
149+
parameters: [
150+
KeyProperties.type.rawValue: "EC",
151+
KeyProperties.rawKey.rawValue: multibaseData,
152+
KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue
153+
])
154+
case "Ed25519VerificationKey2018", "ed25519":
155+
guard let multibaseData = method.publicKeyMultibase else {
156+
throw CastorError.cannotRetrievePublicKeyFromDocument
157+
}
158+
return try apollo.createPublicKey(
159+
parameters: [
160+
KeyProperties.type.rawValue: "EC",
161+
KeyProperties.rawKey.rawValue: multibaseData,
162+
KeyProperties.curve.rawValue: KnownKeyCurves.ed25519.rawValue
163+
])
164+
case "X25519KeyAgreementKey2019", "x25519":
165+
guard let multibaseData = method.publicKeyMultibase else {
166+
throw CastorError.cannotRetrievePublicKeyFromDocument
167+
}
168+
return try apollo.createPublicKey(
169+
parameters: [
170+
KeyProperties.type.rawValue: "EC",
171+
KeyProperties.rawKey.rawValue: multibaseData,
172+
KeyProperties.curve.rawValue: KnownKeyCurves.x25519.rawValue
173+
])
174+
default:
175+
throw UnknownError.somethingWentWrongError(customMessage: nil, underlyingErrors: nil)
176+
}
177+
}
132178
}

AtalaPrismSDK/Castor/Sources/Operations/VerifyDIDSignatureOperation.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ struct VerifyDIDSignatureOperation {
2323
guard let multibaseData = method.publicKeyMultibase else {
2424
throw CastorError.cannotRetrievePublicKeyFromDocument
2525
}
26-
return try await apollo.createPrivateKey(
26+
return try apollo.createPublicKey(
2727
parameters: [
2828
KeyProperties.type.rawValue: "EC",
2929
KeyProperties.rawKey.rawValue: multibaseData,
3030
KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue
31-
]).publicKey()
31+
])
3232
}
3333
}
3434

AtalaPrismSDK/Castor/Sources/Resolvers/LongFormPrismDIDResolver.swift

+61-15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,42 @@ import Domain
33
import Foundation
44

55
struct LongFormPrismDIDResolver: DIDResolverDomain {
6+
enum KeyType {
7+
case master
8+
case issuing
9+
case authentication
10+
case agreement
11+
case capabilityDelegation
12+
case capabilityInvocation
13+
case revocation
14+
case unknown
15+
16+
init(usage: PrismDIDPublicKey.Usage) {
17+
switch usage {
18+
case .masterKey:
19+
self = .master
20+
case .issuingKey:
21+
self = .issuing
22+
case .keyAgreementKey:
23+
self = .agreement
24+
case .capabilityDelegationKey:
25+
self = .capabilityDelegation
26+
case .capabilityInvocationKey:
27+
self = .capabilityInvocation
28+
case .authenticationKey:
29+
self = .authentication
30+
case .revocationKey:
31+
self = .revocation
32+
case .unknownKey:
33+
self = .unknown
34+
}
35+
}
36+
}
37+
struct PublicKeyDecoded {
38+
let id: String
39+
let keyType: KeyType
40+
let method: DIDDocument.VerificationMethod
41+
}
642
let apollo: Apollo
743
let logger: PrismLogger
844

@@ -25,16 +61,21 @@ struct LongFormPrismDIDResolver: DIDResolverDomain {
2561
encodedData: data
2662
)
2763

28-
let authenticate = verificationMethods.first.map {
29-
DIDDocument.Authentication(urls: [$0.key], verificationMethods: [])
64+
let authenticates = verificationMethods.filter { $0.keyType == .authentication }.map {
65+
DIDDocument.Authentication(urls: [$0.id], verificationMethods: [])
66+
}
67+
68+
let agreements = verificationMethods.filter { $0.keyType == .authentication }.map {
69+
DIDDocument.KeyAgreement(urls: [$0.id], verificationMethods: [])
3070
}
3171

3272
let servicesProperty = DIDDocument.Services(values: services)
3373

34-
let verificationMethodsProperty = DIDDocument.VerificationMethods(values: Array(verificationMethods.values))
74+
let verificationMethodsProperty = DIDDocument.VerificationMethods(values: verificationMethods.map(\.method))
3575

3676
let properties = [
37-
authenticate,
77+
authenticates,
78+
agreements,
3879
servicesProperty,
3980
verificationMethodsProperty
4081
].compactMap { $0 as? DIDDocumentCoreProperty }
@@ -49,7 +90,7 @@ struct LongFormPrismDIDResolver: DIDResolverDomain {
4990
did: DID,
5091
stateHash: String,
5192
encodedData: Data
52-
) throws -> ([String: DIDDocument.VerificationMethod], [DIDDocument.Service]) {
93+
) throws -> ([PublicKeyDecoded], [DIDDocument.Service]) {
5394
let verifyEncodedState = encodedData.sha256()
5495
guard stateHash == verifyEncodedState else { throw CastorError.initialStateOfDIDChanged }
5596
let operation = try Io_Iohk_Atala_Prism_Protos_AtalaOperation(serializedData: encodedData)
@@ -63,30 +104,35 @@ struct LongFormPrismDIDResolver: DIDResolverDomain {
63104
throw error
64105
}
65106
}
107+
66108
let services = operation.createDid.didData.services.map {
67109
DIDDocument.Service(
68110
id: $0.id,
69111
type: [$0.type],
70112
serviceEndpoint: $0.serviceEndpoint.map { .init(uri: $0) }
71113
)
72114
}
73-
return (publicKeys.reduce(
74-
[String: DIDDocument.VerificationMethod]())
75-
{ partialResult, publicKey in
115+
116+
let decodedPublicKeys = publicKeys.map {
76117
let didUrl = DIDUrl(
77118
did: did,
78-
fragment: publicKey.id
119+
fragment: $0.id
79120
)
80121

81122
let method = DIDDocument.VerificationMethod(
82123
id: didUrl,
83124
controller: did,
84-
type: publicKey.keyData.getProperty(.curve) ?? "",
85-
publicKeyMultibase: publicKey.keyData.raw.base64EncodedString()
125+
type: $0.keyData.getProperty(.curve) ?? "",
126+
publicKeyMultibase: $0.keyData.raw.base64EncodedString()
127+
)
128+
129+
return PublicKeyDecoded(
130+
id: didUrl.string,
131+
keyType: .init(usage: $0.usage),
132+
method: method
86133
)
87-
var result = partialResult
88-
result[didUrl.string] = method
89-
return result
90-
}, services)
134+
}
135+
136+
return (decodedPublicKeys, services)
91137
}
92138
}

AtalaPrismSDK/Domain/Sources/BBs/Apollo.swift

+4
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,9 @@ public protocol Apollo {
3636
/// - Throws: An error if the private key could not be created. The specific error will depend on the underlying key creation process.
3737
func createPublicKey(parameters: [String: String]) throws -> PublicKey
3838

39+
/// Creates a new link secret, which is used in anoncreds processes.
40+
///
41+
/// - Returns: A newly generated link secret as `Key`.
42+
/// - Throws: An error if the key generation fails.
3943
func createNewLinkSecret() throws -> Key
4044
}

AtalaPrismSDK/Domain/Sources/BBs/Castor.swift

+7
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ public protocol Castor {
6969
challenge: Data,
7070
signature: Data
7171
) async throws -> Bool
72+
73+
/// Retrieves the public keys associated with a specific decentralized identifier (DID).
74+
///
75+
/// - Parameter did: The decentralized identifier (DID) whose public keys are to be retrieved.
76+
/// - Returns: An array of `PublicKey` objects associated with the given DID.
77+
/// - Throws: An error if the retrieval process fails.
78+
func getDIDPublicKeys(did: DID) async throws -> [PublicKey]
7279
}
7380

7481
extension Castor {

AtalaPrismSDK/Domain/Sources/BBs/Pollux.swift

+30
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,36 @@ public protocol Pollux {
4242
offerMessage: Message,
4343
options: [CredentialOperationsOptions]
4444
) async throws -> String
45+
46+
/// Creates a presentation request for credentials of a specified type, directed to a specific DID, with additional metadata and filtering options.
47+
///
48+
/// - Parameters:
49+
/// - type: The type of credential being requested (e.g., JWT, AnonCred).
50+
/// - toDID: The decentralized identifier (DID) of the entity to which the presentation request is being sent.
51+
/// - name: A descriptive name for the presentation request.
52+
/// - version: The version of the presentation request format or protocol.
53+
/// - claimFilters: A collection of filters specifying the claims required in the credential.
54+
/// - Returns: The serialized presentation request as `Data`.
55+
/// - Throws: An error if the request creation fails.
56+
func createPresentationRequest(
57+
type: CredentialType,
58+
toDID: DID,
59+
name: String,
60+
version: String,
61+
claimFilters: [ClaimFilter]
62+
) throws -> Data
63+
64+
/// Verifies the validity of a presentation contained within a message, using specified options.
65+
///
66+
/// - Parameters:
67+
/// - message: The message containing the presentation to be verified.
68+
/// - options: An array of options that influence how the presentation verification is conducted.
69+
/// - Returns: A Boolean value indicating whether the presentation is valid (`true`) or not (`false`).
70+
/// - Throws: An error if there is a problem verifying the presentation.
71+
func verifyPresentation(
72+
message: Message,
73+
options: [CredentialOperationsOptions]
74+
) async throws -> Bool
4575
}
4676

4777
public extension Pollux {
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import Foundation
22

3+
/// `Downloader` is a protocol that defines functionality for downloading data from a given endpoint.
34
public protocol Downloader {
5+
/// Downloads data from the specified endpoint URL or DID.
6+
///
7+
/// - Parameter urlOrDID: The URL or decentralized identifier (DID) from which the data should be downloaded.
8+
/// - Returns: The downloaded data as `Data`.
9+
/// - Throws: An error if the download fails or the endpoint is unreachable.
410
func downloadFromEndpoint(urlOrDID: String) async throws -> Data
511
}

AtalaPrismSDK/Domain/Sources/Models/Credentials/Credential.swift

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import Foundation
22

3+
/// `CredentialType` is an enumeration that defines the types of credentials supported in the system.
4+
public enum CredentialType {
5+
/// Represents a credential in JWT (JSON Web Token) format.
6+
case jwt
7+
8+
/// Represents a credential in AnonCred (Anonymous Credentials) format.
9+
case anoncred
10+
}
11+
312
/// `Claim` represents a claim in a credential. Claims are the attributes associated with the subject of a credential.
413
public struct Claim {
514
/// `ClaimType` represents the type of value a `Claim` can hold. This can be a string, boolean, date, data, or number.

AtalaPrismSDK/Domain/Sources/Models/Credentials/ProvableCredential.swift

+9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ public protocol ProvableCredential {
1010
/// - Returns: The proof as a `String`.
1111
/// - Throws: If there is an error creating the proof.
1212
func presentation(request: Message, options: [CredentialOperationsOptions]) throws -> String
13+
14+
/// Validates if the credential can be used for the given presentation request, using the specified options.
15+
///
16+
/// - Parameters:
17+
/// - request: The presentation request message to be validated against.
18+
/// - options: Options that may influence the validation process.
19+
/// - Returns: A Boolean indicating whether the credential is valid for the presentation (`true`) or not (`false`).
20+
/// - Throws: If there is an error during the validation process.
21+
func isValidForPresentation(request: Message, options: [CredentialOperationsOptions]) throws -> Bool
1322
}
1423

1524
public extension Credential {

0 commit comments

Comments
 (0)