• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hyperledger / identus-edge-agent-sdk-swift / 11052319165

26 Sep 2024 09:51AM UTC coverage: 42.623% (+1.9%) from 40.771%
11052319165

push

github

goncalo-frade-iohk
feat(edgeagent): adds support for connectionless issuance and presentation

Signed-off-by: goncalo-frade-iohk <goncalo.frade@iohk.io>

70 of 134 new or added lines in 19 files covered. (52.24%)

2 existing lines in 2 files now uncovered.

5460 of 12810 relevant lines covered (42.62%)

100.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.6
/EdgeAgentSDK/EdgeAgent/Sources/DIDCommAgent/DIDCommAgent+Credentials.swift
1
import Core
2
import Combine
3
import Domain
4
import Foundation
5
import Logging
6
import JSONWebToken
7

8
public extension DIDCommAgent {
9

10
    /// This function initiates a presentation request for a specific type of credential, specifying the sender's and receiver's DIDs, and any claim filters applicable.
11
    ///
12
    /// - Parameters:
13
    ///   - type: The type of the credential for which the presentation is requested.
14
    ///   - fromDID: The decentralized identifier (DID) of the entity initiating the request.
15
    ///   - toDID: The decentralized identifier (DID) of the entity to which the request is being sent.
16
    ///   - claimFilters: A collection of filters specifying the claims required in the credential.
17
    /// - Returns: The initiated request for presentation.
18
    /// - Throws: EdgeAgentError, if there is a problem initiating the presentation request.
19
    func initiatePresentationRequest(
20
        type: CredentialType,
21
        fromDID: DID,
22
        toDID: DID,
23
        claimFilters: [ClaimFilter]
24
    ) async throws -> RequestPresentation {
8✔
25
        let rqstStr = try await edgeAgent.initiatePresentationRequest(
8✔
26
            type: type,
8✔
27
            fromDID: fromDID,
8✔
28
            toDID: toDID,
8✔
29
            claimFilters: claimFilters
8✔
30
        )
8✔
31
        let attachment: AttachmentDescriptor
8✔
32
        switch type {
8✔
33
        case .jwt:
8✔
34
            let data = try AttachmentBase64(base64: rqstStr.tryToData().base64URLEncoded())
6✔
35
            attachment = AttachmentDescriptor(
6✔
36
                mediaType: "application/json",
6✔
37
                data: data,
6✔
38
                format: "dif/presentation-exchange/definitions@v1.0"
6✔
39
            )
6✔
40
        case .anoncred:
8✔
41
            let data = try AttachmentBase64(base64: rqstStr.tryToData().base64URLEncoded())
2✔
42
            attachment = AttachmentDescriptor(
2✔
43
                mediaType: "application/json",
2✔
44
                data: data,
2✔
45
                format: "anoncreds/proof-request@v1.0"
2✔
46
            )
2✔
47
        }
8✔
48

8✔
49
        return RequestPresentation(
8✔
50
            body: .init(
8✔
51
                proofTypes: [ProofTypes(
8✔
52
                    schema: "",
8✔
53
                    requiredFields: claimFilters.flatMap(\.paths),
10✔
54
                    trustIssuers: nil
8✔
55
                )]
8✔
56
            ),
8✔
57
            attachments: [attachment],
8✔
58
            thid: nil,
8✔
59
            from: fromDID,
8✔
60
            to: toDID
8✔
61
        )
8✔
62
    }
8✔
63

64
    /// This function verifies the presentation contained within a message.
65
    ///
66
    /// - Parameters:
67
    ///   - message: The message containing the presentation to be verified.
68
    /// - Returns: A Boolean value indicating whether the presentation is valid (`true`) or not (`false`).
69
    /// - Throws: EdgeAgentError, if there is a problem verifying the presentation.
70

71
    func verifyPresentation(message: Message) async throws -> Bool {
×
72
        do {
×
73
            let downloader = DownloadDataWithResolver(castor: castor)
×
74
            guard
×
75
                let attachment = message.attachments.first,
×
76
                let requestId = message.thid
×
77
            else {
×
78
                throw PolluxError.couldNotFindPresentationInAttachments
×
79
            }
×
80

×
81
            let jsonData: Data
×
82
            switch attachment.data {
×
83
            case let attchedData as AttachmentBase64:
×
84
                guard let decoded = Data(fromBase64URL: attchedData.base64) else {
×
85
                    throw CommonError.invalidCoding(message: "Invalid base64 url attachment")
×
86
                }
×
87
                jsonData = decoded
×
88
            case let attchedData as AttachmentJsonData:
×
NEW
89
                jsonData = try JSONEncoder.didComm().encode(attchedData.json)
×
90
            default:
×
91
                throw EdgeAgentError.invalidAttachmentFormat(nil)
×
92
            }
×
93

×
94
            guard let format = attachment.format else {
×
95
                throw EdgeAgentError.invalidAttachmentFormat(nil)
×
96
            }
×
97

×
98
            return try await pollux.verifyPresentation(
×
99
                type: format,
×
100
                presentationPayload: jsonData,
×
101
                options: [
×
102
                    .presentationRequestId(requestId),
×
103
                    .credentialDefinitionDownloader(downloader: downloader),
×
104
                    .schemaDownloader(downloader: downloader)
×
105
            ])
×
106
        } catch {
×
107
            logger.error(error: error)
×
108
            throw error
×
109
        }
×
110
    }
×
111

112
    /// This function parses an issued credential message, stores and returns the verifiable credential.
113
    ///
114
    /// - Parameters:
115
    ///   - message: Issue credential Message.
116
    /// - Returns: The parsed verifiable credential.
117
    /// - Throws: EdgeAgentError, if there is a problem parsing the credential.
118
    func processIssuedCredentialMessage(message: IssueCredential3_0) async throws -> Credential {
×
119
        guard
×
120
            let linkSecret = try await pluto.getLinkSecret().first().await()
×
121
        else { throw EdgeAgentError.cannotFindDIDKeyPairIndex }
×
122

×
123
        let restored = try await self.apollo.restoreKey(linkSecret)
×
124
        guard
×
125
            let linkSecretString = String(data: restored.raw, encoding: .utf8)
×
126
        else { throw EdgeAgentError.cannotFindDIDKeyPairIndex }
×
127

×
128
        let downloader = DownloadDataWithResolver(castor: castor)
×
129
        guard 
×
130
            let attachment = message.attachments.first,
×
131
            let format = attachment.format
×
132
        else {
×
133
            throw PolluxError.unsupportedIssuedMessage
×
134
        }
×
135

×
136
        let jsonData: Data
×
137
        switch attachment.data {
×
138
        case let attchedData as AttachmentBase64:
×
139
            guard let decoded = Data(fromBase64URL: attchedData.base64) else {
×
140
                throw CommonError.invalidCoding(message: "Invalid base64 url attachment")
×
141
            }
×
142
            jsonData = decoded
×
143
        case let attchedData as AttachmentJsonData:
×
NEW
144
            jsonData = try JSONEncoder.didComm().encode(attchedData.json)
×
145
        default:
×
146
            throw EdgeAgentError.invalidAttachmentFormat(nil)
×
147
        }
×
148

×
149
        let credential = try await pollux.parseCredential(
×
150
            type: format,
×
151
            credentialPayload: jsonData,
×
152
            options: [
×
153
                .linkSecret(id: "", secret: linkSecretString),
×
154
                .credentialDefinitionDownloader(downloader: downloader),
×
155
                .schemaDownloader(downloader: downloader)
×
156
            ]
×
157
        )
×
158

×
159
        guard let storableCredential = credential.storable else {
×
160
            return credential
×
161
        }
×
162
        try await pluto
×
163
            .storeCredential(credential: storableCredential)
×
164
            .first()
×
165
            .await()
×
166
        return credential
×
167
    }
×
168

169
    /// This function prepares a request credential from an offer given the subject DID.
170
    ///
171
    /// - Parameters:
172
    ///   - did: Subject DID.
173
    ///   - did: Received offer credential.
174
    /// - Returns: Created request credential
175
    /// - Throws: EdgeAgentError, if there is a problem creating the request credential.
176
    func prepareRequestCredentialWithIssuer(did: DID, offer: OfferCredential3_0) async throws -> RequestCredential3_0? {
×
177
        guard did.method == "prism" else { throw PolluxError.invalidPrismDID }
×
178
        let didInfo = try await pluto
×
179
            .getDIDInfo(did: did)
×
180
            .first()
×
181
            .await()
×
182

×
183
        guard let storedPrivateKey = didInfo?.privateKeys.first else { throw EdgeAgentError.cannotFindDIDKeyPairIndex }
×
184

×
185
        let privateKey = try await apollo.restorePrivateKey(storedPrivateKey)
×
186

×
187
        guard
×
188
            let exporting = privateKey.exporting,
×
189
            let linkSecret = try await pluto.getLinkSecret().first().await()
×
190
        else { throw EdgeAgentError.cannotFindDIDKeyPairIndex }
×
191

×
192
        let restored = try await self.apollo.restoreKey(linkSecret)
×
193
        guard
×
194
            let linkSecretString = String(data: restored.raw, encoding: .utf8)
×
195
        else { throw EdgeAgentError.cannotFindDIDKeyPairIndex }
×
196

×
197
        let downloader = DownloadDataWithResolver(castor: castor)
×
198
        guard
×
199
            let attachment = offer.attachments.first,
×
200
            let offerFormat = attachment.format
×
201
        else {
×
202
            throw PolluxError.unsupportedIssuedMessage
×
203
        }
×
204

×
205
        let jsonData: Data
×
206
        switch attachment.data {
×
207
        case let attchedData as AttachmentBase64:
×
208
            guard let decoded = Data(fromBase64URL: attchedData.base64) else {
×
209
                throw CommonError.invalidCoding(message: "Invalid base64 url attachment")
×
210
            }
×
211
            jsonData = decoded
×
212
        case let attchedData as AttachmentJsonData:
×
NEW
213
            jsonData = try JSONEncoder.didComm().encode(attchedData.json)
×
214
        default:
×
215
            throw EdgeAgentError.invalidAttachmentFormat(nil)
×
216
        }
×
217
        let requestString = try await pollux.processCredentialRequest(
×
218
            type: offerFormat,
×
219
            offerPayload: jsonData,
×
220
            options: [
×
221
                .exportableKey(exporting),
×
222
                .subjectDID(did),
×
223
                .linkSecret(id: did.string, secret: linkSecretString),
×
224
                .credentialDefinitionDownloader(downloader: downloader),
×
225
                .schemaDownloader(downloader: downloader)
×
226
            ]
×
227
        )
×
228

×
229
        guard
×
230
            let base64String = requestString.data(using: .utf8)?.base64EncodedString()
×
231
        else {
×
232
            throw CommonError.invalidCoding(message: "Could not encode to base64")
×
233
        }
×
234
        guard
×
235
            let offerPiuri = ProtocolTypes(rawValue: offer.type)
×
236
        else {
×
237
            throw EdgeAgentError.invalidMessageType(
×
238
                type: offer.type,
×
239
                shouldBe: [
×
240
                    ProtocolTypes.didcommOfferCredential3_0.rawValue
×
241
                ]
×
242
            )
×
243
        }
×
244
        let format: String
×
245
        switch offerFormat {
×
246
        case "prism/jwt":
×
247
            format = "prism/jwt"
×
248
        case "vc+sd-jwt":
×
249
            format = "vc+sd-jwt"
×
250
        case "anoncreds/credential-offer@v1.0":
×
251
            format = "anoncreds/credential-request@v1.0"
×
252
        default:
×
253
            throw EdgeAgentError.invalidMessageType(
×
254
                type: offerFormat,
×
255
                shouldBe: [
×
256
                    "prism/jwt",
×
257
                    "anoncreds/credential-offer@v1.0"
×
258
                ]
×
259
            )
×
260
        }
×
261

×
262
        let type = offerPiuri == .didcommOfferCredential ?
×
263
            ProtocolTypes.didcommRequestCredential :
×
264
            ProtocolTypes.didcommRequestCredential3_0
×
265

×
266
        let requestCredential = RequestCredential3_0(
×
267
            body: .init(
×
268
                goalCode: offer.body.goalCode,
×
269
                comment: offer.body.comment
×
270
            ),
×
271
            type: type.rawValue,
×
272
            attachments: [.init(
×
273
                data: AttachmentBase64(base64: base64String),
×
274
                format: format
×
275
            )],
×
276
            thid: offer.thid,
×
277
            from: offer.to,
×
278
            to: offer.from
×
279
        )
×
280
        return requestCredential
×
281
    }
×
282
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc