@pagopa/io-react-native-wallet 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/credential/issuance/06-obtain-credential.js +1 -5
- package/lib/commonjs/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js +33 -21
- package/lib/commonjs/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js +318 -24
- package/lib/commonjs/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js +47 -83
- package/lib/commonjs/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/commonjs/credential/presentation/errors.js +18 -1
- package/lib/commonjs/credential/presentation/errors.js.map +1 -1
- package/lib/commonjs/credential/presentation/index.js +8 -2
- package/lib/commonjs/credential/presentation/index.js.map +1 -1
- package/lib/commonjs/credential/presentation/types.js +6 -2
- package/lib/commonjs/credential/presentation/types.js.map +1 -1
- package/lib/commonjs/entity/trust/chain.js.map +1 -1
- package/lib/commonjs/mdoc/index.js +45 -13
- package/lib/commonjs/mdoc/index.js.map +1 -1
- package/lib/commonjs/sd-jwt/index.js +41 -1
- package/lib/commonjs/sd-jwt/index.js.map +1 -1
- package/lib/commonjs/utils/crypto.js +70 -4
- package/lib/commonjs/utils/crypto.js.map +1 -1
- package/lib/commonjs/utils/string.js +6 -7
- package/lib/commonjs/utils/string.js.map +1 -1
- package/lib/module/credential/issuance/06-obtain-credential.js +1 -5
- package/lib/module/credential/issuance/06-obtain-credential.js.map +1 -1
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js +33 -21
- package/lib/module/credential/issuance/07-verify-and-parse-credential.js.map +1 -1
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js +311 -23
- package/lib/module/credential/presentation/07-evaluate-input-descriptor.js.map +1 -1
- package/lib/module/credential/presentation/08-send-authorization-response.js +46 -81
- package/lib/module/credential/presentation/08-send-authorization-response.js.map +1 -1
- package/lib/module/credential/presentation/errors.js +16 -0
- package/lib/module/credential/presentation/errors.js.map +1 -1
- package/lib/module/credential/presentation/index.js +2 -2
- package/lib/module/credential/presentation/index.js.map +1 -1
- package/lib/module/credential/presentation/types.js +6 -2
- package/lib/module/credential/presentation/types.js.map +1 -1
- package/lib/module/entity/trust/chain.js.map +1 -1
- package/lib/module/mdoc/index.js +43 -12
- package/lib/module/mdoc/index.js.map +1 -1
- package/lib/module/sd-jwt/index.js +40 -1
- package/lib/module/sd-jwt/index.js.map +1 -1
- package/lib/module/utils/crypto.js +67 -2
- package/lib/module/utils/crypto.js.map +1 -1
- package/lib/module/utils/string.js +4 -6
- package/lib/module/utils/string.js.map +1 -1
- package/lib/typescript/credential/issuance/06-obtain-credential.d.ts.map +1 -1
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts +1 -1
- package/lib/typescript/credential/issuance/07-verify-and-parse-credential.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts +106 -9
- package/lib/typescript/credential/presentation/07-evaluate-input-descriptor.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts +4 -33
- package/lib/typescript/credential/presentation/08-send-authorization-response.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/errors.d.ts +11 -0
- package/lib/typescript/credential/presentation/errors.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/index.d.ts +3 -3
- package/lib/typescript/credential/presentation/index.d.ts.map +1 -1
- package/lib/typescript/credential/presentation/types.d.ts +18 -6
- package/lib/typescript/credential/presentation/types.d.ts.map +1 -1
- package/lib/typescript/entity/trust/chain.d.ts.map +1 -1
- package/lib/typescript/mdoc/index.d.ts +6 -2
- package/lib/typescript/mdoc/index.d.ts.map +1 -1
- package/lib/typescript/sd-jwt/index.d.ts +19 -0
- package/lib/typescript/sd-jwt/index.d.ts.map +1 -1
- package/lib/typescript/utils/crypto.d.ts +8 -0
- package/lib/typescript/utils/crypto.d.ts.map +1 -1
- package/lib/typescript/utils/errors.d.ts.map +1 -1
- package/lib/typescript/utils/misc.d.ts.map +1 -1
- package/lib/typescript/utils/string.d.ts +3 -3
- package/lib/typescript/utils/string.d.ts.map +1 -1
- package/package.json +16 -14
- package/src/credential/issuance/06-obtain-credential.ts +1 -7
- package/src/credential/issuance/07-verify-and-parse-credential.ts +37 -16
- package/src/credential/presentation/07-evaluate-input-descriptor.ts +459 -49
- package/src/credential/presentation/08-send-authorization-response.ts +57 -101
- package/src/credential/presentation/errors.ts +16 -0
- package/src/credential/presentation/index.ts +8 -4
- package/src/credential/presentation/types.ts +16 -3
- package/src/entity/trust/chain.ts +14 -10
- package/src/mdoc/index.ts +72 -15
- package/src/sd-jwt/index.ts +49 -1
- package/src/utils/crypto.ts +61 -2
- package/src/utils/errors.ts +2 -2
- package/src/utils/misc.ts +2 -2
- package/src/utils/string.ts +4 -6
@@ -1,23 +1,88 @@
|
|
1
|
-
import { InputDescriptor } from "./types";
|
1
|
+
import { InputDescriptor, type RemotePresentation } from "./types";
|
2
|
+
import { decode, prepareVpToken } from "../../sd-jwt";
|
2
3
|
import { SdJwt4VC, type DisclosureWithEncoded } from "../../sd-jwt/types";
|
4
|
+
import { createCryptoContextFor } from "../../utils/crypto";
|
3
5
|
import { JSONPath } from "jsonpath-plus";
|
4
|
-
import { MissingDataError } from "./errors";
|
6
|
+
import { MissingDataError, CredentialNotFoundError } from "./errors";
|
5
7
|
import Ajv from "ajv";
|
8
|
+
import { CBOR } from "@pagopa/io-react-native-cbor";
|
9
|
+
import { prepareVpTokenMdoc } from "../../mdoc";
|
10
|
+
import { generateRandomAlphaNumericString } from "../../utils/misc";
|
11
|
+
|
6
12
|
const ajv = new Ajv({ allErrors: true });
|
7
|
-
const INDEX_CLAIM_NAME = 1;
|
8
13
|
|
9
|
-
|
10
|
-
requiredDisclosures:
|
11
|
-
optionalDisclosures:
|
12
|
-
|
14
|
+
type EvaluatedDisclosures = {
|
15
|
+
requiredDisclosures: EvaluatedDisclosure[];
|
16
|
+
optionalDisclosures: EvaluatedDisclosure[];
|
17
|
+
};
|
18
|
+
|
19
|
+
export type EvaluatedDisclosure = {
|
20
|
+
namespace?: string;
|
21
|
+
name: string;
|
22
|
+
value: unknown;
|
13
23
|
};
|
14
24
|
|
15
|
-
|
25
|
+
type EvaluateInputDescriptorSdJwt4VC = (
|
16
26
|
inputDescriptor: InputDescriptor,
|
17
27
|
payloadCredential: SdJwt4VC["payload"],
|
18
28
|
disclosures: DisclosureWithEncoded[]
|
19
29
|
) => EvaluatedDisclosures;
|
20
30
|
|
31
|
+
type EvaluateInputDescriptorMdoc = (
|
32
|
+
inputDescriptor: InputDescriptor,
|
33
|
+
issuerSigned: CBOR.IssuerSigned
|
34
|
+
) => EvaluatedDisclosures;
|
35
|
+
|
36
|
+
export type EvaluateInputDescriptors = (
|
37
|
+
descriptors: InputDescriptor[],
|
38
|
+
credentialsSdJwt: [string /* keyTag */, string /* credential */][],
|
39
|
+
credentialsMdoc: [string /* keyTag */, string /* credential */][]
|
40
|
+
) => Promise<
|
41
|
+
{
|
42
|
+
evaluatedDisclosure: EvaluatedDisclosures;
|
43
|
+
inputDescriptor: InputDescriptor;
|
44
|
+
credential: string;
|
45
|
+
keyTag: string;
|
46
|
+
}[]
|
47
|
+
>;
|
48
|
+
|
49
|
+
export type PrepareRemotePresentations = (
|
50
|
+
credentialAndDescriptors: {
|
51
|
+
requestedClaims: string[];
|
52
|
+
inputDescriptor: InputDescriptor;
|
53
|
+
credential: string;
|
54
|
+
keyTag: string;
|
55
|
+
}[],
|
56
|
+
authRequestObject: {
|
57
|
+
nonce: string;
|
58
|
+
clientId: string;
|
59
|
+
responseUri: string;
|
60
|
+
}
|
61
|
+
) => Promise<RemotePresentation>;
|
62
|
+
|
63
|
+
export const disclosureWithEncodedToEvaluatedDisclosure = (
|
64
|
+
disclosure: DisclosureWithEncoded
|
65
|
+
): EvaluatedDisclosure => {
|
66
|
+
const [, claimName, claimValue] = disclosure.decoded;
|
67
|
+
return {
|
68
|
+
name: claimName,
|
69
|
+
value: claimValue,
|
70
|
+
};
|
71
|
+
};
|
72
|
+
|
73
|
+
type DecodedCredentialMdoc = {
|
74
|
+
keyTag: string;
|
75
|
+
credential: string;
|
76
|
+
issuerSigned: CBOR.IssuerSigned;
|
77
|
+
};
|
78
|
+
|
79
|
+
type DecodedCredentialSdJwt = {
|
80
|
+
keyTag: string;
|
81
|
+
credential: string;
|
82
|
+
sdJwt: SdJwt4VC;
|
83
|
+
disclosures: DisclosureWithEncoded[];
|
84
|
+
};
|
85
|
+
|
21
86
|
/**
|
22
87
|
* Transforms an array of DisclosureWithEncoded objects into a key-value map.
|
23
88
|
* @param disclosures - An array of DisclosureWithEncoded, each containing a decoded property with [?, claimName, claimValue].
|
@@ -26,11 +91,39 @@ export type EvaluateInputDescriptorSdJwt4VC = (
|
|
26
91
|
const mapDisclosuresToObject = (
|
27
92
|
disclosures: DisclosureWithEncoded[]
|
28
93
|
): Record<string, unknown> => {
|
29
|
-
return disclosures.reduce(
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
94
|
+
return disclosures.reduce(
|
95
|
+
(obj, { decoded }) => {
|
96
|
+
const [, claimName, claimValue] = decoded;
|
97
|
+
obj[claimName] = claimValue;
|
98
|
+
return obj;
|
99
|
+
},
|
100
|
+
{} as Record<string, unknown>
|
101
|
+
);
|
102
|
+
};
|
103
|
+
|
104
|
+
/**
|
105
|
+
* Transforms the issuer's namespaces from a CBOR structure into a plain JavaScript object.
|
106
|
+
*
|
107
|
+
* @param namespaces - The CBOR-based namespaces object where each key corresponds to a namespace,
|
108
|
+
* and each value is an array of elements containing identifiers and values.
|
109
|
+
* @returns A record (plain object) where each key is a namespace, and its value is another object
|
110
|
+
* mapping element identifiers to their corresponding element values.
|
111
|
+
*/
|
112
|
+
const mapNamespacesToObject = (
|
113
|
+
namespaces: CBOR.IssuerSigned["nameSpaces"]
|
114
|
+
): Record<string, unknown> => {
|
115
|
+
return Object.entries(namespaces).reduce(
|
116
|
+
(obj, [namespace, elements]) => {
|
117
|
+
obj[namespace] = Object.fromEntries(
|
118
|
+
elements.map((element) => [
|
119
|
+
element.elementIdentifier,
|
120
|
+
element.elementValue,
|
121
|
+
])
|
122
|
+
);
|
123
|
+
return obj;
|
124
|
+
},
|
125
|
+
{} as Record<string, unknown>
|
126
|
+
);
|
34
127
|
};
|
35
128
|
|
36
129
|
/**
|
@@ -85,13 +178,110 @@ const extractClaimName = (path: string): string | undefined => {
|
|
85
178
|
return match[1] || match[2];
|
86
179
|
}
|
87
180
|
|
88
|
-
// If the input doesn't match any of the expected formats, return null
|
89
|
-
|
90
181
|
throw new Error(
|
91
182
|
`Invalid input format: "${path}". Expected formats are "$.propertyName", "$['propertyName']", or '$["propertyName"]'.`
|
92
183
|
);
|
93
184
|
};
|
94
185
|
|
186
|
+
/**
|
187
|
+
* Extracts the namespace and claim name from a path in the following format:
|
188
|
+
* $['nameSpace']['propertyName']
|
189
|
+
*
|
190
|
+
* @param path - The path string containing the claim reference.
|
191
|
+
* @returns An object with the extracted namespace and claim name.
|
192
|
+
* @throws An error if the input format is invalid.
|
193
|
+
*/
|
194
|
+
const extractNamespaceAndClaimName = (
|
195
|
+
path: string
|
196
|
+
): { nameSpace?: string; propertyName?: string } => {
|
197
|
+
const regex = /^\$\[(?:'|")([^'"\]]+)(?:'|")\]\[(?:'|")([^'"\]]+)(?:'|")\]$/;
|
198
|
+
const match = path.match(regex);
|
199
|
+
if (match) {
|
200
|
+
return { nameSpace: match[1], propertyName: match[2] };
|
201
|
+
}
|
202
|
+
|
203
|
+
throw new Error(
|
204
|
+
`Invalid input format: "${path}". Expected format is "$['nameSpace']['propertyName']".`
|
205
|
+
);
|
206
|
+
};
|
207
|
+
/**
|
208
|
+
* Evaluates the input descriptor for an mDoc by verifying that the issuerSigned claims meet
|
209
|
+
* the constraints defined in the input descriptor. It categorizes disclosures as either required
|
210
|
+
* or optional based on the field definitions.
|
211
|
+
*
|
212
|
+
* @param inputDescriptor - Contains constraints and field definitions specifying required/optional claims.
|
213
|
+
* @param issuerSigned - Contains the issuerSigned with namespaces and their associated claims.
|
214
|
+
* @returns An object with two arrays: one for required disclosures and one for optional disclosures.
|
215
|
+
* @throws MissingDataError - If a required field is missing or if a claim fails JSON Schema validation.
|
216
|
+
*/
|
217
|
+
export const evaluateInputDescriptorForMdoc: EvaluateInputDescriptorMdoc = (
|
218
|
+
inputDescriptor,
|
219
|
+
issuerSigned
|
220
|
+
) => {
|
221
|
+
if (!inputDescriptor?.constraints?.fields) {
|
222
|
+
// No validation, no field are required
|
223
|
+
return {
|
224
|
+
requiredDisclosures: [],
|
225
|
+
optionalDisclosures: [],
|
226
|
+
};
|
227
|
+
}
|
228
|
+
|
229
|
+
const requiredDisclosures: EvaluatedDisclosure[] = [];
|
230
|
+
const optionalDisclosures: EvaluatedDisclosure[] = [];
|
231
|
+
|
232
|
+
// Convert issuer's namespaces into an object for easier lookup of claim values.
|
233
|
+
const namespacesAsPayload = mapNamespacesToObject(issuerSigned.nameSpaces);
|
234
|
+
|
235
|
+
const allFieldsValid = inputDescriptor.constraints.fields.every((field) => {
|
236
|
+
const [matchedPath, matchedValue] = findMatchedClaim(
|
237
|
+
field.path,
|
238
|
+
namespacesAsPayload
|
239
|
+
);
|
240
|
+
|
241
|
+
// If no matching claim is found, the field is valid only if it's marked as optional.
|
242
|
+
if (matchedValue === undefined || !matchedPath) {
|
243
|
+
return field?.optional;
|
244
|
+
} else {
|
245
|
+
// Extract the namespace and property name from the matched path.
|
246
|
+
const { nameSpace, propertyName } =
|
247
|
+
extractNamespaceAndClaimName(matchedPath);
|
248
|
+
if (nameSpace && propertyName) {
|
249
|
+
(field?.optional ? optionalDisclosures : requiredDisclosures).push({
|
250
|
+
namespace: nameSpace,
|
251
|
+
name: propertyName,
|
252
|
+
value: matchedValue,
|
253
|
+
});
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
if (field.filter) {
|
258
|
+
try {
|
259
|
+
const validateSchema = ajv.compile(field.filter);
|
260
|
+
if (!validateSchema(matchedValue)) {
|
261
|
+
throw new MissingDataError(
|
262
|
+
`Claim value "${matchedValue}" for path "${matchedPath}" does not match the provided JSON Schema.`
|
263
|
+
);
|
264
|
+
}
|
265
|
+
} catch (error) {
|
266
|
+
return false;
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
return true;
|
271
|
+
});
|
272
|
+
|
273
|
+
if (!allFieldsValid) {
|
274
|
+
throw new MissingDataError(
|
275
|
+
"Credential validation failed: Required fields are missing or do not match the input descriptor."
|
276
|
+
);
|
277
|
+
}
|
278
|
+
|
279
|
+
return {
|
280
|
+
requiredDisclosures,
|
281
|
+
optionalDisclosures,
|
282
|
+
};
|
283
|
+
};
|
284
|
+
|
95
285
|
/**
|
96
286
|
* Evaluates an InputDescriptor for an SD-JWT-based verifiable credential.
|
97
287
|
*
|
@@ -100,14 +290,12 @@ const extractClaimName = (path: string): string | undefined => {
|
|
100
290
|
* - Validates whether required fields are present (unless marked optional)
|
101
291
|
* and match any specified JSONPath.
|
102
292
|
* - If a field includes a JSON Schema filter, validates the claim value against that schema.
|
103
|
-
* - Enforces `limit_disclosure` rules by returning only disclosures, required and optional, matching the specified fields
|
104
|
-
* if set to "required". Otherwise also return the array unrequestedDisclosures with disclosures which can be passed for a particular use case.
|
105
293
|
* - Throws an error if a required field is invalid or missing.
|
106
294
|
*
|
107
295
|
* @param inputDescriptor - Describes constraints (fields, filters, etc.) that must be satisfied.
|
108
296
|
* @param payloadCredential - The credential payload to check against.
|
109
297
|
* @param disclosures - An array of DisclosureWithEncoded objects representing selective disclosures.
|
110
|
-
* @returns
|
298
|
+
* @returns An object with two arrays: one for required disclosures and one for optional disclosures.
|
111
299
|
* @throws Will throw an error if any required constraint fails or if JSONPath lookups are invalid.
|
112
300
|
*/
|
113
301
|
export const evaluateInputDescriptorForSdJwt4VC: EvaluateInputDescriptorSdJwt4VC =
|
@@ -117,13 +305,12 @@ export const evaluateInputDescriptorForSdJwt4VC: EvaluateInputDescriptorSdJwt4VC
|
|
117
305
|
return {
|
118
306
|
requiredDisclosures: [],
|
119
307
|
optionalDisclosures: [],
|
120
|
-
unrequestedDisclosures: disclosures,
|
121
308
|
};
|
122
309
|
}
|
123
|
-
const
|
124
|
-
const
|
310
|
+
const requiredDisclosures: EvaluatedDisclosure[] = [];
|
311
|
+
const optionalDisclosures: EvaluatedDisclosure[] = [];
|
125
312
|
|
126
|
-
// Transform disclosures
|
313
|
+
// Transform disclosures into an object for easier lookup of claim values.
|
127
314
|
const disclosuresAsPayload = mapDisclosuresToObject(disclosures);
|
128
315
|
|
129
316
|
// For each field, we need at least one matching path
|
@@ -151,9 +338,10 @@ export const evaluateInputDescriptorForSdJwt4VC: EvaluateInputDescriptorSdJwt4VC
|
|
151
338
|
// if match a disclouse we save which is required or optional
|
152
339
|
const claimName = extractClaimName(matchedPath);
|
153
340
|
if (claimName) {
|
154
|
-
(field?.optional ?
|
155
|
-
|
156
|
-
|
341
|
+
(field?.optional ? optionalDisclosures : requiredDisclosures).push({
|
342
|
+
value: matchedValue,
|
343
|
+
name: claimName,
|
344
|
+
});
|
157
345
|
}
|
158
346
|
}
|
159
347
|
|
@@ -183,33 +371,255 @@ export const evaluateInputDescriptorForSdJwt4VC: EvaluateInputDescriptorSdJwt4VC
|
|
183
371
|
);
|
184
372
|
}
|
185
373
|
|
186
|
-
|
374
|
+
return {
|
375
|
+
requiredDisclosures,
|
376
|
+
optionalDisclosures,
|
377
|
+
};
|
378
|
+
};
|
187
379
|
|
188
|
-
|
189
|
-
|
190
|
-
|
380
|
+
/**
|
381
|
+
* Finds the first credential that satisfies the input descriptor constraints.
|
382
|
+
* @param inputDescriptor The input descriptor to evaluate.
|
383
|
+
* @param decodedSdJwtCredentials An array of decoded SD-JWT credentials.
|
384
|
+
* @returns An object containing the matched evaluation, keyTag, and credential.
|
385
|
+
*/
|
386
|
+
export const findCredentialSdJwt = (
|
387
|
+
inputDescriptor: InputDescriptor,
|
388
|
+
decodedSdJwtCredentials: DecodedCredentialSdJwt[]
|
389
|
+
): {
|
390
|
+
matchedEvaluation: EvaluatedDisclosures;
|
391
|
+
matchedKeyTag: string;
|
392
|
+
matchedCredential: string;
|
393
|
+
} => {
|
394
|
+
for (const {
|
395
|
+
keyTag,
|
396
|
+
credential,
|
397
|
+
sdJwt,
|
398
|
+
disclosures,
|
399
|
+
} of decodedSdJwtCredentials) {
|
400
|
+
try {
|
401
|
+
const evaluatedDisclosure = evaluateInputDescriptorForSdJwt4VC(
|
402
|
+
inputDescriptor,
|
403
|
+
sdJwt.payload,
|
404
|
+
disclosures
|
405
|
+
);
|
191
406
|
|
192
|
-
|
193
|
-
|
194
|
-
|
407
|
+
return {
|
408
|
+
matchedEvaluation: evaluatedDisclosure,
|
409
|
+
matchedKeyTag: keyTag,
|
410
|
+
matchedCredential: credential,
|
411
|
+
};
|
412
|
+
} catch {
|
413
|
+
// skip to next credential
|
414
|
+
continue;
|
415
|
+
}
|
416
|
+
}
|
195
417
|
|
196
|
-
|
197
|
-
|
198
|
-
|
418
|
+
throw new CredentialNotFoundError(
|
419
|
+
"None of the vc+sd-jwt credentials satisfy the requirements."
|
420
|
+
);
|
421
|
+
};
|
199
422
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
423
|
+
/**
|
424
|
+
* Finds the first credential that satisfies the input descriptor constraints.
|
425
|
+
* @param inputDescriptor The input descriptor to evaluate.
|
426
|
+
* @param decodedMdocCredentials An array of decoded MDOC credentials.
|
427
|
+
* @returns An object containing the matched evaluation, keyTag, and credential.
|
428
|
+
*/
|
429
|
+
export const findCredentialMDoc = (
|
430
|
+
inputDescriptor: InputDescriptor,
|
431
|
+
decodedMDocCredentials: DecodedCredentialMdoc[]
|
432
|
+
): {
|
433
|
+
matchedEvaluation: EvaluatedDisclosures;
|
434
|
+
matchedKeyTag: string;
|
435
|
+
matchedCredential: string;
|
436
|
+
} => {
|
437
|
+
for (const { keyTag, credential, issuerSigned } of decodedMDocCredentials) {
|
438
|
+
try {
|
439
|
+
const evaluatedDisclosure = evaluateInputDescriptorForMdoc(
|
440
|
+
inputDescriptor,
|
441
|
+
issuerSigned
|
442
|
+
);
|
209
443
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
444
|
+
return {
|
445
|
+
matchedEvaluation: evaluatedDisclosure,
|
446
|
+
matchedKeyTag: keyTag,
|
447
|
+
matchedCredential: credential,
|
448
|
+
};
|
449
|
+
} catch {
|
450
|
+
// skip to next credential
|
451
|
+
continue;
|
452
|
+
}
|
453
|
+
}
|
454
|
+
|
455
|
+
throw new CredentialNotFoundError(
|
456
|
+
"None of the mso_mdoc credentials satisfy the requirements."
|
457
|
+
);
|
458
|
+
};
|
459
|
+
|
460
|
+
/**
|
461
|
+
* Evaluates multiple input descriptors against provided SD-JWT and MDOC credentials.
|
462
|
+
*
|
463
|
+
* For each input descriptor, this function:
|
464
|
+
* - Checks the credential format.
|
465
|
+
* - Decodes the credential.
|
466
|
+
* - Evaluates the descriptor using the associated disclosures.
|
467
|
+
*
|
468
|
+
* @param inputDescriptors - An array of input descriptors.
|
469
|
+
* @param credentialsSdJwt - An array of tuples containing keyTag and SD-JWT credential.
|
470
|
+
* @param credentialsMdoc - An array of tuples containing keyTag and MDOC credential.
|
471
|
+
* @returns An array of objects, each containing the evaluated disclosures,
|
472
|
+
* the input descriptor, the credential, and the keyTag.
|
473
|
+
* @throws {CredentialNotFoundError} When the credential format is unsupported.
|
474
|
+
*/
|
475
|
+
export const evaluateInputDescriptors: EvaluateInputDescriptors = async (
|
476
|
+
inputDescriptors,
|
477
|
+
credentialsSdJwt,
|
478
|
+
credentialsMdoc
|
479
|
+
) => {
|
480
|
+
// We need decode SD-JWT credentials for evaluation
|
481
|
+
const decodedSdJwtCredentials =
|
482
|
+
credentialsSdJwt?.map(([keyTag, credential]) => {
|
483
|
+
const { sdJwt, disclosures } = decode(credential);
|
484
|
+
return { keyTag, credential, sdJwt, disclosures };
|
485
|
+
}) || [];
|
486
|
+
|
487
|
+
// We need decode Mdoc credentials for evaluation
|
488
|
+
const decodedMdocCredentials =
|
489
|
+
(await Promise.all(
|
490
|
+
credentialsMdoc?.map(async ([keyTag, credential]) => {
|
491
|
+
const issuerSigned = await CBOR.decodeIssuerSigned(credential);
|
492
|
+
if (!issuerSigned) {
|
493
|
+
throw new CredentialNotFoundError(
|
494
|
+
"mso_mdoc credential is not present."
|
495
|
+
);
|
496
|
+
}
|
497
|
+
return { keyTag, credential, issuerSigned };
|
498
|
+
})
|
499
|
+
)) || [];
|
500
|
+
|
501
|
+
const results = Promise.all(
|
502
|
+
inputDescriptors.map(async (descriptor) => {
|
503
|
+
if (descriptor.format?.mso_mdoc) {
|
504
|
+
if (!credentialsMdoc.length) {
|
505
|
+
throw new CredentialNotFoundError(
|
506
|
+
"mso_mdoc credential is not supported."
|
507
|
+
);
|
508
|
+
}
|
509
|
+
|
510
|
+
const { matchedEvaluation, matchedKeyTag, matchedCredential } =
|
511
|
+
findCredentialMDoc(descriptor, decodedMdocCredentials);
|
512
|
+
|
513
|
+
return {
|
514
|
+
evaluatedDisclosure: matchedEvaluation,
|
515
|
+
inputDescriptor: descriptor,
|
516
|
+
credential: matchedCredential,
|
517
|
+
keyTag: matchedKeyTag,
|
518
|
+
};
|
519
|
+
}
|
520
|
+
|
521
|
+
if (descriptor.format?.["vc+sd-jwt"]) {
|
522
|
+
if (!decodedSdJwtCredentials.length) {
|
523
|
+
throw new CredentialNotFoundError(
|
524
|
+
"vc+sd-jwt credential is not supported."
|
525
|
+
);
|
526
|
+
}
|
527
|
+
|
528
|
+
const { matchedEvaluation, matchedKeyTag, matchedCredential } =
|
529
|
+
findCredentialSdJwt(descriptor, decodedSdJwtCredentials);
|
530
|
+
|
531
|
+
return {
|
532
|
+
evaluatedDisclosure: matchedEvaluation,
|
533
|
+
inputDescriptor: descriptor,
|
534
|
+
credential: matchedCredential,
|
535
|
+
keyTag: matchedKeyTag,
|
536
|
+
};
|
537
|
+
}
|
538
|
+
|
539
|
+
throw new CredentialNotFoundError(
|
540
|
+
`${descriptor.format} format is not supported.`
|
541
|
+
);
|
542
|
+
})
|
543
|
+
);
|
544
|
+
|
545
|
+
return results;
|
546
|
+
};
|
547
|
+
|
548
|
+
/**
|
549
|
+
* Prepares remote presentations for a set of credentials based on input descriptors.
|
550
|
+
*
|
551
|
+
* For each credential and its corresponding input descriptor, this function:
|
552
|
+
* - Validates the credential format.
|
553
|
+
* - Generates a verifiable presentation token (vpToken) using the provided nonce and client identifier.
|
554
|
+
*
|
555
|
+
* @param credentialAndDescriptors - An array containing objects with requested claims,
|
556
|
+
* input descriptor, credential, and keyTag.
|
557
|
+
* @param nonce - A unique nonce for the verifiable presentation token.
|
558
|
+
* @param client_id - The client identifier.
|
559
|
+
* @returns A promise that resolves to an array of RemotePresentation objects.
|
560
|
+
* @throws {CredentialNotFoundError} When the credential format is unsupported.
|
561
|
+
*/
|
562
|
+
export const prepareRemotePresentations: PrepareRemotePresentations = async (
|
563
|
+
credentialAndDescriptors,
|
564
|
+
authRequestObject
|
565
|
+
) => {
|
566
|
+
/* In case of ISO 18013-7 we need a nonce, it shall have a minimum entropy of 16 */
|
567
|
+
const generatedNonce = generateRandomAlphaNumericString(16);
|
568
|
+
|
569
|
+
const presentations = await Promise.all(
|
570
|
+
credentialAndDescriptors.map(async (item) => {
|
571
|
+
const descriptor = item.inputDescriptor;
|
572
|
+
|
573
|
+
if (descriptor.format?.mso_mdoc) {
|
574
|
+
const { vp_token } = await prepareVpTokenMdoc(
|
575
|
+
authRequestObject.nonce,
|
576
|
+
generatedNonce,
|
577
|
+
authRequestObject.clientId,
|
578
|
+
authRequestObject.responseUri,
|
579
|
+
descriptor.id,
|
580
|
+
item.keyTag,
|
581
|
+
[
|
582
|
+
item.credential,
|
583
|
+
item.requestedClaims,
|
584
|
+
createCryptoContextFor(item.keyTag),
|
585
|
+
]
|
586
|
+
);
|
587
|
+
|
588
|
+
return {
|
589
|
+
requestedClaims: item.requestedClaims,
|
590
|
+
inputDescriptor: descriptor,
|
591
|
+
vpToken: vp_token,
|
592
|
+
format: "mso_mdoc",
|
593
|
+
};
|
594
|
+
}
|
595
|
+
|
596
|
+
if (descriptor.format?.["vc+sd-jwt"]) {
|
597
|
+
const { vp_token } = await prepareVpToken(
|
598
|
+
authRequestObject.nonce,
|
599
|
+
authRequestObject.clientId,
|
600
|
+
[
|
601
|
+
item.credential,
|
602
|
+
item.requestedClaims,
|
603
|
+
createCryptoContextFor(item.keyTag),
|
604
|
+
]
|
605
|
+
);
|
606
|
+
|
607
|
+
return {
|
608
|
+
requestedClaims: item.requestedClaims,
|
609
|
+
inputDescriptor: descriptor,
|
610
|
+
vpToken: vp_token,
|
611
|
+
format: "vc+sd-jwt",
|
612
|
+
};
|
613
|
+
}
|
614
|
+
|
615
|
+
throw new CredentialNotFoundError(
|
616
|
+
`${descriptor.format} format is not supported.`
|
617
|
+
);
|
618
|
+
})
|
619
|
+
);
|
620
|
+
|
621
|
+
return {
|
622
|
+
presentations,
|
623
|
+
generatedNonce,
|
215
624
|
};
|
625
|
+
};
|