@sphereon/ssi-sdk.sd-jwt 0.34.1-next.7 → 0.34.1-next.86
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/dist/index.cjs +254 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -8
- package/dist/index.d.ts +66 -8
- package/dist/index.js +252 -41
- package/dist/index.js.map +1 -1
- package/package.json +21 -20
- package/src/__tests__/{sd-jwt.test.ts → sd-jwt-vc.test.ts} +6 -4
- package/src/__tests__/sd-jwt-vcdm2.test.ts +316 -0
- package/src/action-handler.ts +83 -36
- package/src/sdJwtVcdm2Instance.ts +155 -0
- package/src/types.ts +40 -6
- package/src/utils.ts +32 -1
package/dist/index.js
CHANGED
|
@@ -3,9 +3,8 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
3
3
|
|
|
4
4
|
// src/action-handler.ts
|
|
5
5
|
import { SDJwt } from "@sd-jwt/core";
|
|
6
|
-
import { SDJwtVcInstance } from "@sd-jwt/sd-jwt-vc";
|
|
6
|
+
import { SDJwtVcInstance as SDJwtVcInstance2 } from "@sd-jwt/sd-jwt-vc";
|
|
7
7
|
import { calculateJwkThumbprint, signatureAlgorithmFromKey } from "@sphereon/ssi-sdk-ext.key-utils";
|
|
8
|
-
import { decodeBase64url } from "@veramo/utils";
|
|
9
8
|
import Debug from "debug";
|
|
10
9
|
|
|
11
10
|
// src/defaultCallbacks.ts
|
|
@@ -81,8 +80,190 @@ function assertValidTypeMetadata(metadata, vct) {
|
|
|
81
80
|
}
|
|
82
81
|
}
|
|
83
82
|
__name(assertValidTypeMetadata, "assertValidTypeMetadata");
|
|
83
|
+
function isVcdm2SdJwtPayload(payload) {
|
|
84
|
+
return "type" in payload && Array.isArray(payload.type) && payload.type.includes("VerifiableCredential") && "@context" in payload && (typeof payload["@context"] === "string" && payload["@context"].length > 0 || Array.isArray(payload["@context"]) && payload["@context"].length > 0 && payload["@context"].includes("https://www.w3.org/ns/credentials/v2"));
|
|
85
|
+
}
|
|
86
|
+
__name(isVcdm2SdJwtPayload, "isVcdm2SdJwtPayload");
|
|
87
|
+
function isSdjwtVcPayload(payload) {
|
|
88
|
+
return !isVcdm2SdJwtPayload(payload) && "vct" in payload && typeof payload.vct === "string";
|
|
89
|
+
}
|
|
90
|
+
__name(isSdjwtVcPayload, "isSdjwtVcPayload");
|
|
91
|
+
function getIssuerFromSdJwt(payload) {
|
|
92
|
+
let issuer;
|
|
93
|
+
if (isVcdm2SdJwtPayload(payload)) {
|
|
94
|
+
issuer = typeof payload.issuer === "string" ? payload.issuer : payload.issuer?.id;
|
|
95
|
+
}
|
|
96
|
+
if (isSdjwtVcPayload(payload)) {
|
|
97
|
+
issuer = payload.iss;
|
|
98
|
+
}
|
|
99
|
+
if (!issuer) {
|
|
100
|
+
throw new Error("No issuer (iss or VCDM 2 issuer) found in SD-JWT or no VCDM2 SD-JWT or SD-JWT VC");
|
|
101
|
+
}
|
|
102
|
+
return issuer;
|
|
103
|
+
}
|
|
104
|
+
__name(getIssuerFromSdJwt, "getIssuerFromSdJwt");
|
|
105
|
+
|
|
106
|
+
// src/sdJwtVcdm2Instance.ts
|
|
107
|
+
import { SDJwtInstance } from "@sd-jwt/core";
|
|
108
|
+
import { SDJWTException } from "@sd-jwt/utils";
|
|
109
|
+
import { SDJwtVcInstance } from "@sd-jwt/sd-jwt-vc";
|
|
110
|
+
|
|
111
|
+
// src/types.ts
|
|
112
|
+
import { contextHasPlugin } from "@sphereon/ssi-sdk.agent-config";
|
|
113
|
+
var sdJwtPluginContextMethods = [
|
|
114
|
+
"createSdJwtVc",
|
|
115
|
+
"createSdJwtPresentation",
|
|
116
|
+
"verifySdJwtVc",
|
|
117
|
+
"verifySdJwtPresentation"
|
|
118
|
+
];
|
|
119
|
+
function contextHasSDJwtPlugin(context) {
|
|
120
|
+
return contextHasPlugin(context, "verifySdJwtVc");
|
|
121
|
+
}
|
|
122
|
+
__name(contextHasSDJwtPlugin, "contextHasSDJwtPlugin");
|
|
123
|
+
function isVcdm2SdJwt(type) {
|
|
124
|
+
return type === "vc+sd-jwt" || type === "vp+sd-jwt";
|
|
125
|
+
}
|
|
126
|
+
__name(isVcdm2SdJwt, "isVcdm2SdJwt");
|
|
127
|
+
|
|
128
|
+
// src/sdJwtVcdm2Instance.ts
|
|
129
|
+
var SDJwtVcdmInstanceFactory = class {
|
|
130
|
+
static {
|
|
131
|
+
__name(this, "SDJwtVcdmInstanceFactory");
|
|
132
|
+
}
|
|
133
|
+
static create(type, config) {
|
|
134
|
+
if (isVcdm2SdJwt(type)) {
|
|
135
|
+
return new SDJwtVcdm2Instance(config);
|
|
136
|
+
}
|
|
137
|
+
return new SDJwtVcInstance(config);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var SDJwtVcdm2Instance = class extends SDJwtInstance {
|
|
141
|
+
static {
|
|
142
|
+
__name(this, "SDJwtVcdm2Instance");
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The type of the SD-JWT VCDM2 set in the header.typ field.
|
|
146
|
+
*/
|
|
147
|
+
static type = "vc+sd-jwt";
|
|
148
|
+
userConfig = {};
|
|
149
|
+
constructor(userConfig) {
|
|
150
|
+
super(userConfig);
|
|
151
|
+
if (userConfig) {
|
|
152
|
+
this.userConfig = userConfig;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Validates if the disclosureFrame contains any reserved fields. If so it will throw an error.
|
|
157
|
+
* @param disclosureFrame
|
|
158
|
+
*/
|
|
159
|
+
validateReservedFields(disclosureFrame) {
|
|
160
|
+
if (disclosureFrame?._sd && Array.isArray(disclosureFrame._sd) && disclosureFrame._sd.length > 0) {
|
|
161
|
+
const reservedNames = [
|
|
162
|
+
"iss",
|
|
163
|
+
"nbf",
|
|
164
|
+
"exp",
|
|
165
|
+
"cnf",
|
|
166
|
+
"@context",
|
|
167
|
+
"type",
|
|
168
|
+
"credentialStatus",
|
|
169
|
+
"credentialSchema",
|
|
170
|
+
"relatedResource"
|
|
171
|
+
];
|
|
172
|
+
const reservedNamesInDisclosureFrame = disclosureFrame._sd.filter((key) => reservedNames.includes(key));
|
|
173
|
+
if (reservedNamesInDisclosureFrame.length > 0) {
|
|
174
|
+
throw new SDJWTException(`Cannot disclose protected field(s): ${reservedNamesInDisclosureFrame.join(", ")}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Verifies the SD-JWT-VC. It will validate the signature, the keybindings when required, the status, and the VCT.
|
|
180
|
+
* @param encodedSDJwt
|
|
181
|
+
* @param options
|
|
182
|
+
*/
|
|
183
|
+
async verify(encodedSDJwt, options) {
|
|
184
|
+
const result = await super.verify(encodedSDJwt, options).then((res) => {
|
|
185
|
+
return {
|
|
186
|
+
payload: res.payload,
|
|
187
|
+
header: res.header,
|
|
188
|
+
kb: res.kb
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Validates the integrity of the response if the integrity is passed. If the integrity does not match, an error is thrown.
|
|
195
|
+
* @param integrity
|
|
196
|
+
* @param response
|
|
197
|
+
*/
|
|
198
|
+
async validateIntegrity(response, url, integrity) {
|
|
199
|
+
if (integrity) {
|
|
200
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
201
|
+
const alg = integrity.split("-")[0];
|
|
202
|
+
const hashBuffer = await this.userConfig.hasher(arrayBuffer, alg);
|
|
203
|
+
const integrityHash = integrity.split("-")[1];
|
|
204
|
+
const hash = Array.from(new Uint8Array(hashBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
205
|
+
if (hash !== integrityHash) {
|
|
206
|
+
throw new Error(`Integrity check for ${url} failed: is ${hash}, but expected ${integrityHash}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Fetches the content from the url with a timeout of 10 seconds.
|
|
212
|
+
* @param url
|
|
213
|
+
* @param integrity
|
|
214
|
+
* @returns
|
|
215
|
+
*/
|
|
216
|
+
async fetch(url, integrity) {
|
|
217
|
+
try {
|
|
218
|
+
const response = await fetch(url, {
|
|
219
|
+
signal: AbortSignal.timeout(this.userConfig.timeout ?? 1e4)
|
|
220
|
+
});
|
|
221
|
+
if (!response.ok) {
|
|
222
|
+
const errorText = await response.text();
|
|
223
|
+
return Promise.reject(new Error(`Error fetching ${url}: ${response.status} ${response.statusText} - ${errorText}`));
|
|
224
|
+
}
|
|
225
|
+
await this.validateIntegrity(response.clone(), url, integrity);
|
|
226
|
+
return response.json();
|
|
227
|
+
} catch (error) {
|
|
228
|
+
if (error.name === "TimeoutError") {
|
|
229
|
+
throw new Error(`Request to ${url} timed out`);
|
|
230
|
+
}
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async issue(payload, disclosureFrame, options) {
|
|
235
|
+
if (payload.iss && !payload.issuer) {
|
|
236
|
+
payload.issuer = {
|
|
237
|
+
id: payload.iss
|
|
238
|
+
};
|
|
239
|
+
delete payload.iss;
|
|
240
|
+
}
|
|
241
|
+
if (payload.nbf && !payload.validFrom) {
|
|
242
|
+
payload.validFrom = toVcdm2Date(payload.nbf);
|
|
243
|
+
delete payload.nbf;
|
|
244
|
+
}
|
|
245
|
+
if (payload.exp && !payload.validUntil) {
|
|
246
|
+
payload.validUntil = toVcdm2Date(payload.exp);
|
|
247
|
+
delete payload.exp;
|
|
248
|
+
}
|
|
249
|
+
if (payload.sub && !Array.isArray(payload.credentialSubject) && !payload.credentialSubject.id) {
|
|
250
|
+
payload.credentialSubject.id = payload.sub;
|
|
251
|
+
delete payload.sub;
|
|
252
|
+
}
|
|
253
|
+
return super.issue(payload, disclosureFrame, options);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
function toVcdm2Date(value) {
|
|
257
|
+
const num = typeof value === "string" ? Number(value) : value;
|
|
258
|
+
if (!Number.isFinite(num)) {
|
|
259
|
+
throw new SDJWTException(`Invalid numeric date: ${value}`);
|
|
260
|
+
}
|
|
261
|
+
return new Date(num * 1e3).toISOString();
|
|
262
|
+
}
|
|
263
|
+
__name(toVcdm2Date, "toVcdm2Date");
|
|
84
264
|
|
|
85
265
|
// src/action-handler.ts
|
|
266
|
+
import * as u8a from "uint8arrays";
|
|
86
267
|
var debug = Debug("@sphereon/ssi-sdk.sd-jwt");
|
|
87
268
|
var SDJwtPlugin = class {
|
|
88
269
|
static {
|
|
@@ -152,7 +333,11 @@ var SDJwtPlugin = class {
|
|
|
152
333
|
* @returns A signed SD-JWT credential.
|
|
153
334
|
*/
|
|
154
335
|
async createSdJwtVc(args, context) {
|
|
155
|
-
const
|
|
336
|
+
const payload = args.credentialPayload;
|
|
337
|
+
const isVcdm2 = isVcdm2SdJwtPayload(payload);
|
|
338
|
+
const isSdJwtVc = isSdjwtVcPayload(payload);
|
|
339
|
+
const type = args.type ?? (isVcdm2 ? "vc+sd-jwt" : "dc+sd-jwt");
|
|
340
|
+
const issuer = getIssuerFromSdJwt(args.credentialPayload);
|
|
156
341
|
if (!issuer) {
|
|
157
342
|
throw new Error("credential.issuer must not be empty");
|
|
158
343
|
}
|
|
@@ -160,24 +345,46 @@ var SDJwtPlugin = class {
|
|
|
160
345
|
identifier: issuer,
|
|
161
346
|
resolution: args.resolution
|
|
162
347
|
}, context);
|
|
163
|
-
const
|
|
348
|
+
const signAlg = alg ?? signingKey?.alg ?? "ES256";
|
|
349
|
+
const hashAlg = /(\d{3})$/.test(signAlg) ? `sha-${signAlg.slice(-3)}` : "sha-256";
|
|
350
|
+
const sdjwt = SDJwtVcdmInstanceFactory.create(type, {
|
|
351
|
+
omitTyp: true,
|
|
164
352
|
signer,
|
|
165
353
|
hasher: this.registeredImplementations.hasher,
|
|
166
354
|
saltGenerator: this.registeredImplementations.saltGenerator,
|
|
167
|
-
signAlg
|
|
168
|
-
hashAlg
|
|
355
|
+
signAlg,
|
|
356
|
+
hashAlg
|
|
169
357
|
});
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
358
|
+
const header = {
|
|
359
|
+
...signingKey?.key.kid !== void 0 && {
|
|
360
|
+
kid: signingKey.key.kid
|
|
361
|
+
},
|
|
362
|
+
...signingKey?.key.x5c !== void 0 && {
|
|
363
|
+
x5c: signingKey.key.x5c
|
|
364
|
+
},
|
|
365
|
+
...type && {
|
|
366
|
+
typ: type
|
|
178
367
|
}
|
|
179
|
-
}
|
|
368
|
+
};
|
|
369
|
+
let credential;
|
|
370
|
+
if (isVcdm2) {
|
|
371
|
+
credential = await sdjwt.issue(
|
|
372
|
+
payload,
|
|
373
|
+
// @ts-ignore
|
|
374
|
+
args.disclosureFrame,
|
|
375
|
+
{
|
|
376
|
+
header
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
} else if (isSdJwtVc) {
|
|
380
|
+
credential = await sdjwt.issue(payload, args.disclosureFrame, {
|
|
381
|
+
header
|
|
382
|
+
});
|
|
383
|
+
} else {
|
|
384
|
+
return Promise.reject(new Error(`invalid_argument: credential '${type}' type is not supported`));
|
|
385
|
+
}
|
|
180
386
|
return {
|
|
387
|
+
type,
|
|
181
388
|
credential
|
|
182
389
|
};
|
|
183
390
|
}
|
|
@@ -303,6 +510,7 @@ var SDJwtPlugin = class {
|
|
|
303
510
|
* @returns A signed SD-JWT presentation.
|
|
304
511
|
*/
|
|
305
512
|
async createSdJwtPresentation(args, context) {
|
|
513
|
+
const type = args.type ?? "dc+sd-jwt";
|
|
306
514
|
const cred = await SDJwt.fromEncode(args.presentation, this.registeredImplementations.hasher);
|
|
307
515
|
const claims = await cred.getClaims(this.registeredImplementations.hasher);
|
|
308
516
|
let holder;
|
|
@@ -323,8 +531,9 @@ var SDJwtPlugin = class {
|
|
|
323
531
|
const { alg, signer } = await this.getSignerForIdentifier({
|
|
324
532
|
identifier: holder
|
|
325
533
|
}, context);
|
|
326
|
-
const sdjwt =
|
|
327
|
-
|
|
534
|
+
const sdjwt = SDJwtVcdmInstanceFactory.create(type, {
|
|
535
|
+
omitTyp: true,
|
|
536
|
+
hasher: this.registeredImplementations.hasher,
|
|
328
537
|
saltGenerator: this.registeredImplementations.saltGenerator,
|
|
329
538
|
kbSigner: signer,
|
|
330
539
|
kbSignAlg: alg ?? "ES256"
|
|
@@ -333,6 +542,7 @@ var SDJwtPlugin = class {
|
|
|
333
542
|
kb: args.kb
|
|
334
543
|
});
|
|
335
544
|
return {
|
|
545
|
+
type,
|
|
336
546
|
presentation
|
|
337
547
|
};
|
|
338
548
|
}
|
|
@@ -343,13 +553,18 @@ var SDJwtPlugin = class {
|
|
|
343
553
|
* @returns
|
|
344
554
|
*/
|
|
345
555
|
async verifySdJwtVc(args, context) {
|
|
346
|
-
const verifier = /* @__PURE__ */ __name(async (data, signature) => this.
|
|
347
|
-
const
|
|
556
|
+
const verifier = /* @__PURE__ */ __name(async (data, signature) => this.verifyCallbackImpl(sdjwt, context, data, signature), "verifier");
|
|
557
|
+
const cred = await SDJwt.fromEncode(args.credential, this.registeredImplementations.hasher);
|
|
558
|
+
const type = isVcdm2SdJwtPayload(cred.jwt?.payload) ? "vc+sd-jwt" : "dc+sd-jwt";
|
|
559
|
+
const sdjwt = SDJwtVcdmInstanceFactory.create(type, {
|
|
348
560
|
verifier,
|
|
349
561
|
hasher: this.registeredImplementations.hasher ?? defaultGenerateDigest
|
|
350
562
|
});
|
|
351
|
-
const { header = {}, payload, kb } = await sdjwt.verify(args.credential
|
|
563
|
+
const { header = {}, payload, kb } = await sdjwt.verify(args.credential, {
|
|
564
|
+
skewSeconds: 60 * 60 * 24 * 5
|
|
565
|
+
});
|
|
352
566
|
return {
|
|
567
|
+
type,
|
|
353
568
|
header,
|
|
354
569
|
payload,
|
|
355
570
|
kb
|
|
@@ -364,7 +579,7 @@ var SDJwtPlugin = class {
|
|
|
364
579
|
* @param payload - The payload of the SD-JWT
|
|
365
580
|
* @returns
|
|
366
581
|
*/
|
|
367
|
-
verifyKb(
|
|
582
|
+
verifyKb(context, data, signature, payload) {
|
|
368
583
|
if (!payload.cnf) {
|
|
369
584
|
throw Error("other method than cnf is not supported yet");
|
|
370
585
|
}
|
|
@@ -378,9 +593,10 @@ var SDJwtPlugin = class {
|
|
|
378
593
|
* @param signature - The signature
|
|
379
594
|
* @returns
|
|
380
595
|
*/
|
|
381
|
-
async
|
|
596
|
+
async verifyCallbackImpl(sdjwt, context, data, signature, opts) {
|
|
382
597
|
const decodedVC = await sdjwt.decode(`${data}.${signature}`);
|
|
383
|
-
const
|
|
598
|
+
const payload = decodedVC.jwt.payload;
|
|
599
|
+
const issuer = getIssuerFromSdJwt(payload);
|
|
384
600
|
const header = decodedVC.jwt.header;
|
|
385
601
|
const x5c = header?.x5c;
|
|
386
602
|
let jwk = header.jwk;
|
|
@@ -446,14 +662,18 @@ var SDJwtPlugin = class {
|
|
|
446
662
|
*/
|
|
447
663
|
async verifySdJwtPresentation(args, context) {
|
|
448
664
|
let sdjwt;
|
|
449
|
-
const verifier = /* @__PURE__ */ __name(async (data, signature) => this.
|
|
450
|
-
const verifierKb = /* @__PURE__ */ __name(async (data, signature, payload) => this.verifyKb(
|
|
451
|
-
sdjwt = new
|
|
665
|
+
const verifier = /* @__PURE__ */ __name(async (data, signature) => this.verifyCallbackImpl(sdjwt, context, data, signature), "verifier");
|
|
666
|
+
const verifierKb = /* @__PURE__ */ __name(async (data, signature, payload) => this.verifyKb(context, data, signature, payload), "verifierKb");
|
|
667
|
+
sdjwt = new SDJwtVcInstance2({
|
|
452
668
|
verifier,
|
|
453
669
|
hasher: this.registeredImplementations.hasher,
|
|
454
670
|
kbVerifier: verifierKb
|
|
455
671
|
});
|
|
456
|
-
|
|
672
|
+
const verifierOpts = {
|
|
673
|
+
requiredClaimKeys: args.requiredClaimKeys,
|
|
674
|
+
keyBindingNonce: args.keyBindingNonce
|
|
675
|
+
};
|
|
676
|
+
return sdjwt.verify(args.presentation, verifierOpts);
|
|
457
677
|
}
|
|
458
678
|
/**
|
|
459
679
|
* Fetch and validate Type Metadata.
|
|
@@ -524,7 +744,7 @@ var SDJwtPlugin = class {
|
|
|
524
744
|
return payload.cnf.jwk;
|
|
525
745
|
} else if (payload.cnf !== void 0 && "kid" in payload.cnf && typeof payload.cnf.kid === "string" && payload.cnf.kid.startsWith("did:jwk:")) {
|
|
526
746
|
const encoded = this.extractBase64FromDIDJwk(payload.cnf.kid);
|
|
527
|
-
const decoded =
|
|
747
|
+
const decoded = u8a.toString(u8a.fromString(encoded, "base64url"), "utf-8");
|
|
528
748
|
const jwt = JSON.parse(decoded);
|
|
529
749
|
return jwt;
|
|
530
750
|
}
|
|
@@ -538,19 +758,6 @@ var SDJwtPlugin = class {
|
|
|
538
758
|
return parts[2].split("#")[0];
|
|
539
759
|
}
|
|
540
760
|
};
|
|
541
|
-
|
|
542
|
-
// src/types.ts
|
|
543
|
-
import { contextHasPlugin } from "@sphereon/ssi-sdk.agent-config";
|
|
544
|
-
var sdJwtPluginContextMethods = [
|
|
545
|
-
"createSdJwtVc",
|
|
546
|
-
"createSdJwtPresentation",
|
|
547
|
-
"verifySdJwtVc",
|
|
548
|
-
"verifySdJwtPresentation"
|
|
549
|
-
];
|
|
550
|
-
function contextHasSDJwtPlugin(context) {
|
|
551
|
-
return contextHasPlugin(context, "verifySdJwtVc");
|
|
552
|
-
}
|
|
553
|
-
__name(contextHasSDJwtPlugin, "contextHasSDJwtPlugin");
|
|
554
761
|
export {
|
|
555
762
|
SDJwtPlugin,
|
|
556
763
|
assertValidTypeMetadata,
|
|
@@ -558,6 +765,10 @@ export {
|
|
|
558
765
|
createIntegrity,
|
|
559
766
|
extractHashFromIntegrity,
|
|
560
767
|
fetchUrlWithErrorHandling,
|
|
768
|
+
getIssuerFromSdJwt,
|
|
769
|
+
isSdjwtVcPayload,
|
|
770
|
+
isVcdm2SdJwt,
|
|
771
|
+
isVcdm2SdJwtPayload,
|
|
561
772
|
sdJwtPluginContextMethods,
|
|
562
773
|
validateIntegrity
|
|
563
774
|
};
|