@sphereon/ssi-sdk.sd-jwt 0.32.1-next.54 → 0.33.1-feature.vcdm2.4
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/action-handler.d.ts.map +1 -1
- package/dist/action-handler.js +246 -247
- package/dist/action-handler.js.map +1 -1
- package/dist/defaultCallbacks.d.ts +2 -2
- package/dist/defaultCallbacks.d.ts.map +1 -1
- package/dist/defaultCallbacks.js +12 -50
- package/dist/defaultCallbacks.js.map +1 -1
- package/dist/index.js +3 -21
- package/dist/index.js.map +1 -1
- package/dist/trustAnchors.js +2 -5
- package/dist/trustAnchors.js.map +1 -1
- package/dist/types.d.ts +4 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -8
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +15 -2
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +32 -52
- package/dist/utils.js.map +1 -1
- package/package.json +20 -20
- package/src/__tests__/resources/BoardingPassCredential-vct.json +196 -0
- package/src/__tests__/resources/LoyaltyProgram Account VC Schema V0.1 sd-jwt-schema.json +97 -0
- package/src/__tests__/resources/LoyaltyProgramAccountCredential-vct.json +126 -0
- package/src/__tests__/resources/boarding Pass VC Schema V1.0 sd-jwt.json +156 -0
- package/src/__tests__/resources/boardingpass-logo.png +0 -0
- package/src/__tests__/resources/boardingpass.svg +1 -0
- package/src/__tests__/resources/e-passport.svg +1 -0
- package/src/__tests__/resources/ePassport VC Schema V1.0.sd-jwt.json +226 -0
- package/src/__tests__/resources/ePassportCredential-vct.json +226 -0
- package/src/__tests__/resources/epassport-logo.png +0 -0
- package/src/__tests__/resources/loyaltyprogramaccount-icon.png +0 -0
- package/src/__tests__/resources/loyaltyprogramaccount.png +0 -0
- package/src/__tests__/resources/loyaltyprogramaccount.svg +1 -0
- package/src/__tests__/resources/travel-agency VC Employee v0.1 sd-jwt-schema.json +115 -0
- package/src/__tests__/resources/travel-agency-EmployeeAgencyCredential-vct.json +146 -0
- package/src/__tests__/resources/travel-agency-vc-employee-logo.png +0 -0
- package/src/__tests__/resources/travel-agency-vc-employee.svg +1 -0
- package/src/__tests__/sd-jwt-integrity.test.ts +100 -0
- package/src/__tests__/sd-jwt.test.ts +4 -4
- package/src/action-handler.ts +51 -18
- package/src/defaultCallbacks.ts +5 -4
- package/src/types.ts +4 -4
- package/src/utils.ts +48 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-handler.d.ts","sourceRoot":"","sources":["../src/action-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAkB,MAAM,mBAAmB,CAAA;AACnE,OAAO,
|
|
1
|
+
{"version":3,"file":"action-handler.d.ts","sourceRoot":"","sources":["../src/action-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAkB,MAAM,mBAAmB,CAAA;AACnE,OAAO,EAAsE,MAAM,EAAY,MAAM,eAAe,CAAA;AAEpH,OAAO,EAAE,kCAAkC,EAAE,MAAM,kCAAkC,CAAA;AACrF,OAAO,EAAmB,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAM3C,OAAO,EAEL,oCAAoC,EAGpC,4BAA4B,EAC5B,8BAA8B,EAC9B,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,4BAA4B,EAC5B,8BAA8B,EAC9B,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EAEnB,WAAW,EACX,aAAa,EACd,MAAM,SAAS,CAAA;AAIhB;;;GAGG;AACH,qBAAa,WAAY,YAAW,YAAY;IAE9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAU;IAC5C,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqB;IAC/D,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,cAAc,CAAC,CAAQ;gBAG7B,yBAAyB,CAAC,EAAE,mBAAmB,GAAG;QAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB,EACD,iBAAiB,CAAC,EAAE,MAAM,EAAE;IAoB9B,QAAQ,CAAC,OAAO,EAAE,YAAY,CAM7B;YAEa,sBAAsB;IAiBpC;;;;;OAKG;IACG,aAAa,CAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAwBvG;;;;;OAKG;IACG,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;IA8CtF;;;;;OAKG;IACG,uBAAuB,CAAC,IAAI,EAAE,4BAA4B,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,8BAA8B,CAAC;IA8BrI;;;;;OAKG;IACG,aAAa,CAAC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IASvG;;;;;;;;OAQG;IACH,OAAO,CAAC,QAAQ;IAOhB;;;;;;;OAOG;IACG,MAAM,CACV,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,kCAAkC,CAAA;KAAE,GAC5D,OAAO,CAAC,OAAO,CAAC;IAgEnB;;;;;OAKG;IACG,uBAAuB,CAAC,IAAI,EAAE,4BAA4B,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,8BAA8B,CAAC;IAcrI;;;;;OAKG;IACG,gCAAgC,CAAC,IAAI,EAAE,oCAAoC,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiDzI,OAAO,CAAC,uBAAuB;IAQ/B,OAAO,CAAC,MAAM;IAcd,OAAO,CAAC,uBAAuB;CAOhC"}
|
package/dist/action-handler.js
CHANGED
|
@@ -1,73 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.SDJwtPlugin = void 0;
|
|
16
|
-
const core_1 = require("@sd-jwt/core");
|
|
17
|
-
const sd_jwt_vc_1 = require("@sd-jwt/sd-jwt-vc");
|
|
18
|
-
const ssi_sdk_ext_key_utils_1 = require("@sphereon/ssi-sdk-ext.key-utils");
|
|
19
|
-
const utils_1 = require("@veramo/utils");
|
|
20
|
-
const debug_1 = __importDefault(require("debug"));
|
|
21
|
-
const defaultCallbacks_1 = require("./defaultCallbacks");
|
|
22
|
-
const trustAnchors_1 = require("./trustAnchors");
|
|
23
|
-
const utils_2 = require("./utils");
|
|
24
|
-
const debug = (0, debug_1.default)('@sphereon/ssi-sdk.sd-jwt');
|
|
1
|
+
import { SDJwt } from '@sd-jwt/core';
|
|
2
|
+
import { SDJwtVcInstance } from '@sd-jwt/sd-jwt-vc';
|
|
3
|
+
import { calculateJwkThumbprint, signatureAlgorithmFromKey } from '@sphereon/ssi-sdk-ext.key-utils';
|
|
4
|
+
import { decodeBase64url } from '@veramo/utils';
|
|
5
|
+
import Debug from 'debug';
|
|
6
|
+
import { defaultGenerateDigest, defaultGenerateSalt, defaultVerifySignature } from './defaultCallbacks';
|
|
7
|
+
import { funkeTestCA, sphereonCA } from './trustAnchors';
|
|
8
|
+
import { assertValidTypeMetadata, fetchUrlWithErrorHandling, validateIntegrity } from './utils';
|
|
9
|
+
const debug = Debug('@sphereon/ssi-sdk.sd-jwt');
|
|
25
10
|
/**
|
|
26
11
|
* @beta
|
|
27
12
|
* SD-JWT plugin
|
|
28
13
|
*/
|
|
29
|
-
class SDJwtPlugin {
|
|
14
|
+
export class SDJwtPlugin {
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
trustAnchorsInPEM;
|
|
17
|
+
registeredImplementations;
|
|
18
|
+
_signers;
|
|
19
|
+
_defaultSigner;
|
|
30
20
|
constructor(registeredImplementations, trustAnchorsInPEM) {
|
|
31
|
-
|
|
32
|
-
// map the methods your plugin is declaring to their implementation
|
|
33
|
-
this.methods = {
|
|
34
|
-
createSdJwtVc: this.createSdJwtVc.bind(this),
|
|
35
|
-
createSdJwtPresentation: this.createSdJwtPresentation.bind(this),
|
|
36
|
-
verifySdJwtVc: this.verifySdJwtVc.bind(this),
|
|
37
|
-
verifySdJwtPresentation: this.verifySdJwtPresentation.bind(this),
|
|
38
|
-
fetchSdJwtTypeMetadataFromVctUrl: this.fetchSdJwtTypeMetadataFromVctUrl.bind(this),
|
|
39
|
-
};
|
|
40
|
-
this.trustAnchorsInPEM = trustAnchorsInPEM !== null && trustAnchorsInPEM !== void 0 ? trustAnchorsInPEM : [];
|
|
21
|
+
this.trustAnchorsInPEM = trustAnchorsInPEM ?? [];
|
|
41
22
|
if (!registeredImplementations) {
|
|
42
23
|
registeredImplementations = {};
|
|
43
24
|
}
|
|
44
|
-
if (typeof
|
|
45
|
-
registeredImplementations.hasher =
|
|
25
|
+
if (typeof registeredImplementations?.hasher !== 'function') {
|
|
26
|
+
registeredImplementations.hasher = defaultGenerateDigest;
|
|
46
27
|
}
|
|
47
|
-
if (typeof
|
|
48
|
-
registeredImplementations.saltGenerator =
|
|
28
|
+
if (typeof registeredImplementations?.saltGenerator !== 'function') {
|
|
29
|
+
registeredImplementations.saltGenerator = defaultGenerateSalt;
|
|
49
30
|
}
|
|
50
31
|
this.registeredImplementations = registeredImplementations;
|
|
51
|
-
this._signers =
|
|
52
|
-
this._defaultSigner = registeredImplementations
|
|
32
|
+
this._signers = registeredImplementations?.signers ?? {};
|
|
33
|
+
this._defaultSigner = registeredImplementations?.defaultSigner;
|
|
53
34
|
// Verify signature default is used below in the methods if not provided here, as it needs the context of the agent
|
|
54
35
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return { signer
|
|
70
|
-
}
|
|
36
|
+
// map the methods your plugin is declaring to their implementation
|
|
37
|
+
methods = {
|
|
38
|
+
createSdJwtVc: this.createSdJwtVc.bind(this),
|
|
39
|
+
createSdJwtPresentation: this.createSdJwtPresentation.bind(this),
|
|
40
|
+
verifySdJwtVc: this.verifySdJwtVc.bind(this),
|
|
41
|
+
verifySdJwtPresentation: this.verifySdJwtPresentation.bind(this),
|
|
42
|
+
fetchSdJwtTypeMetadataFromVctUrl: this.fetchSdJwtTypeMetadataFromVctUrl.bind(this),
|
|
43
|
+
};
|
|
44
|
+
async getSignerForIdentifier(args, context) {
|
|
45
|
+
const { identifier, resolution } = args;
|
|
46
|
+
if (Object.keys(this._signers).includes(identifier) && typeof this._signers[identifier] === 'function') {
|
|
47
|
+
return { signer: this._signers[identifier] };
|
|
48
|
+
}
|
|
49
|
+
else if (typeof this._defaultSigner === 'function') {
|
|
50
|
+
return { signer: this._defaultSigner };
|
|
51
|
+
}
|
|
52
|
+
const signingKey = await this.getSignKey({ identifier, vmRelationship: 'assertionMethod', resolution }, context);
|
|
53
|
+
const { key, alg } = signingKey;
|
|
54
|
+
const signer = async (data) => {
|
|
55
|
+
return context.agent.keyManagerSign({ keyRef: key.kmsKeyRef, data });
|
|
56
|
+
};
|
|
57
|
+
return { signer, alg, signingKey };
|
|
71
58
|
}
|
|
72
59
|
/**
|
|
73
60
|
* Create a signed SD-JWT credential.
|
|
@@ -75,25 +62,26 @@ class SDJwtPlugin {
|
|
|
75
62
|
* @param context - This reserved param is automatically added and handled by the framework, *do not override*
|
|
76
63
|
* @returns A signed SD-JWT credential.
|
|
77
64
|
*/
|
|
78
|
-
createSdJwtVc(args, context) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
hashAlg: 'SHA-256',
|
|
91
|
-
});
|
|
92
|
-
const credential = yield sdjwt.issue(args.credentialPayload, args.disclosureFrame, {
|
|
93
|
-
header: Object.assign(Object.assign({}, ((signingKey === null || signingKey === void 0 ? void 0 : signingKey.key.kid) !== undefined && { kid: signingKey.key.kid })), ((signingKey === null || signingKey === void 0 ? void 0 : signingKey.key.x5c) !== undefined && { x5c: signingKey.key.x5c })),
|
|
94
|
-
});
|
|
95
|
-
return { credential };
|
|
65
|
+
async createSdJwtVc(args, context) {
|
|
66
|
+
const issuer = args.credentialPayload.iss;
|
|
67
|
+
if (!issuer) {
|
|
68
|
+
throw new Error('credential.issuer must not be empty');
|
|
69
|
+
}
|
|
70
|
+
const { alg, signer, signingKey } = await this.getSignerForIdentifier({ identifier: issuer, resolution: args.resolution }, context);
|
|
71
|
+
const sdjwt = new SDJwtVcInstance({
|
|
72
|
+
signer,
|
|
73
|
+
hasher: this.registeredImplementations.hasher,
|
|
74
|
+
saltGenerator: this.registeredImplementations.saltGenerator,
|
|
75
|
+
signAlg: alg ?? 'ES256',
|
|
76
|
+
hashAlg: 'sha-256',
|
|
96
77
|
});
|
|
78
|
+
const credential = await sdjwt.issue(args.credentialPayload, args.disclosureFrame, {
|
|
79
|
+
header: {
|
|
80
|
+
...(signingKey?.key.kid !== undefined && { kid: signingKey.key.kid }),
|
|
81
|
+
...(signingKey?.key.x5c !== undefined && { x5c: signingKey.key.x5c }),
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
return { credential };
|
|
97
85
|
}
|
|
98
86
|
/**
|
|
99
87
|
* Get the key to sign the SD-JWT
|
|
@@ -101,58 +89,55 @@ class SDJwtPlugin {
|
|
|
101
89
|
* @param context - agent instance
|
|
102
90
|
* @returns the key to sign the SD-JWT
|
|
103
91
|
*/
|
|
104
|
-
getSignKey(args, context) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return { alg, key: { kid: resolution.kid, kmsKeyRef: resolution.kmsKeyRef } };
|
|
125
|
-
}
|
|
126
|
-
}
|
|
92
|
+
async getSignKey(args, context) {
|
|
93
|
+
// TODO Using identifierManagedGetByDid now (new managed identifier resolution). Evaluate of we need to implement more identifier types here
|
|
94
|
+
const { identifier, resolution } = { ...args };
|
|
95
|
+
if (resolution) {
|
|
96
|
+
const key = resolution.key;
|
|
97
|
+
const alg = await signatureAlgorithmFromKey({ key });
|
|
98
|
+
switch (resolution.method) {
|
|
99
|
+
case 'did':
|
|
100
|
+
debug(`Signing key ${key.publicKeyHex} found for identifier ${identifier}`);
|
|
101
|
+
return { alg, key: { ...key, kmsKeyRef: resolution.kmsKeyRef, kid: resolution.kid } };
|
|
102
|
+
default:
|
|
103
|
+
if (key.meta?.x509 && key.meta.x509.x5c) {
|
|
104
|
+
return { alg, key: { kid: resolution.kid, kmsKeyRef: resolution.kmsKeyRef, x5c: key.meta.x509.x5c } };
|
|
105
|
+
}
|
|
106
|
+
else if (key.meta?.jwkThumbprint) {
|
|
107
|
+
return { alg, key: { kid: resolution.kid, kmsKeyRef: resolution.kmsKeyRef, jwkThumbprint: key.meta.jwkThumbprint } };
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
return { alg, key: { kid: resolution.kid, kmsKeyRef: resolution.kmsKeyRef } };
|
|
111
|
+
}
|
|
127
112
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
113
|
+
}
|
|
114
|
+
else if (identifier.startsWith('did:')) {
|
|
115
|
+
const didIdentifier = await context.agent.identifierManagedGetByDid({ identifier });
|
|
116
|
+
if (!didIdentifier) {
|
|
117
|
+
throw new Error(`No identifier found with the given did: ${identifier}`);
|
|
118
|
+
}
|
|
119
|
+
const key = didIdentifier.key;
|
|
120
|
+
const alg = await signatureAlgorithmFromKey({ key });
|
|
121
|
+
debug(`Signing key ${key.publicKeyHex} found for identifier ${identifier}`);
|
|
122
|
+
return { alg, key: { ...key, kmsKeyRef: didIdentifier.kmsKeyRef, kid: didIdentifier.kid } };
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const kidIdentifier = await context.agent.identifierManagedGetByKid({ identifier });
|
|
126
|
+
if (!kidIdentifier) {
|
|
127
|
+
throw new Error(`No identifier found with the given kid: ${identifier}`);
|
|
128
|
+
}
|
|
129
|
+
const key = kidIdentifier.key;
|
|
130
|
+
const alg = await signatureAlgorithmFromKey({ key });
|
|
131
|
+
if (key.meta?.x509 && key.meta.x509.x5c) {
|
|
132
|
+
return { alg, key: { kid: kidIdentifier.kid, kmsKeyRef: kidIdentifier.kmsKeyRef, x5c: key.meta.x509.x5c } };
|
|
133
|
+
}
|
|
134
|
+
else if (key.meta?.jwkThumbprint) {
|
|
135
|
+
return { alg, key: { kid: kidIdentifier.kid, kmsKeyRef: kidIdentifier.kmsKeyRef, jwkThumbprint: key.meta.jwkThumbprint } };
|
|
137
136
|
}
|
|
138
137
|
else {
|
|
139
|
-
|
|
140
|
-
if (!kidIdentifier) {
|
|
141
|
-
throw new Error(`No identifier found with the given kid: ${identifier}`);
|
|
142
|
-
}
|
|
143
|
-
const key = kidIdentifier.key;
|
|
144
|
-
const alg = yield (0, ssi_sdk_ext_key_utils_1.signatureAlgorithmFromKey)({ key });
|
|
145
|
-
if (((_c = key.meta) === null || _c === void 0 ? void 0 : _c.x509) && key.meta.x509.x5c) {
|
|
146
|
-
return { alg, key: { kid: kidIdentifier.kid, kmsKeyRef: kidIdentifier.kmsKeyRef, x5c: key.meta.x509.x5c } };
|
|
147
|
-
}
|
|
148
|
-
else if ((_d = key.meta) === null || _d === void 0 ? void 0 : _d.jwkThumbprint) {
|
|
149
|
-
return { alg, key: { kid: kidIdentifier.kid, kmsKeyRef: kidIdentifier.kmsKeyRef, jwkThumbprint: key.meta.jwkThumbprint } };
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
return { alg, key: { kid: kidIdentifier.kid, kmsKeyRef: kidIdentifier.kmsKeyRef } };
|
|
153
|
-
}
|
|
138
|
+
return { alg, key: { kid: kidIdentifier.kid, kmsKeyRef: kidIdentifier.kmsKeyRef } };
|
|
154
139
|
}
|
|
155
|
-
}
|
|
140
|
+
}
|
|
156
141
|
}
|
|
157
142
|
/**
|
|
158
143
|
* Create a signed SD-JWT presentation.
|
|
@@ -160,39 +145,36 @@ class SDJwtPlugin {
|
|
|
160
145
|
* @param context - This reserved param is automatically added and handled by the framework, *do not override*
|
|
161
146
|
* @returns A signed SD-JWT presentation.
|
|
162
147
|
*/
|
|
163
|
-
createSdJwtPresentation(args, context) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
kbSigner: signer,
|
|
191
|
-
kbSignAlg: alg !== null && alg !== void 0 ? alg : 'ES256',
|
|
192
|
-
});
|
|
193
|
-
const presentation = yield sdjwt.present(args.presentation, args.presentationFrame, { kb: args.kb });
|
|
194
|
-
return { presentation };
|
|
148
|
+
async createSdJwtPresentation(args, context) {
|
|
149
|
+
const cred = await SDJwt.fromEncode(args.presentation, this.registeredImplementations.hasher);
|
|
150
|
+
const claims = await cred.getClaims(this.registeredImplementations.hasher);
|
|
151
|
+
let holder;
|
|
152
|
+
// we primarly look for a cnf field, if it's not there we look for a sub field. If this is also not given, we throw an error since we can not sign it.
|
|
153
|
+
if (args.holder) {
|
|
154
|
+
holder = args.holder;
|
|
155
|
+
}
|
|
156
|
+
else if (claims.cnf?.jwk) {
|
|
157
|
+
const jwk = claims.cnf.jwk;
|
|
158
|
+
holder = calculateJwkThumbprint({ jwk: jwk });
|
|
159
|
+
}
|
|
160
|
+
else if (claims.cnf?.kid) {
|
|
161
|
+
holder = claims.cnf?.kid;
|
|
162
|
+
}
|
|
163
|
+
else if (claims.sub) {
|
|
164
|
+
holder = claims.sub;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
throw new Error('invalid_argument: credential does not include a holder reference');
|
|
168
|
+
}
|
|
169
|
+
const { alg, signer } = await this.getSignerForIdentifier({ identifier: holder }, context);
|
|
170
|
+
const sdjwt = new SDJwtVcInstance({
|
|
171
|
+
hasher: this.registeredImplementations.hasher ?? defaultGenerateDigest,
|
|
172
|
+
saltGenerator: this.registeredImplementations.saltGenerator,
|
|
173
|
+
kbSigner: signer,
|
|
174
|
+
kbSignAlg: alg ?? 'ES256',
|
|
195
175
|
});
|
|
176
|
+
const presentation = await sdjwt.present(args.presentation, args.presentationFrame, { kb: args.kb });
|
|
177
|
+
return { presentation };
|
|
196
178
|
}
|
|
197
179
|
/**
|
|
198
180
|
* Verify a signed SD-JWT credential.
|
|
@@ -200,14 +182,12 @@ class SDJwtPlugin {
|
|
|
200
182
|
* @param context - This reserved param is automatically added and handled by the framework, *do not override*
|
|
201
183
|
* @returns
|
|
202
184
|
*/
|
|
203
|
-
verifySdJwtVc(args, context) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
return { header, payload: payload, kb };
|
|
210
|
-
});
|
|
185
|
+
async verifySdJwtVc(args, context) {
|
|
186
|
+
// callback
|
|
187
|
+
const verifier = async (data, signature) => this.verify(sdjwt, context, data, signature);
|
|
188
|
+
const sdjwt = new SDJwtVcInstance({ verifier, hasher: this.registeredImplementations.hasher ?? defaultGenerateDigest });
|
|
189
|
+
const { header = {}, payload, kb } = await sdjwt.verify(args.credential);
|
|
190
|
+
return { header, payload: payload, kb };
|
|
211
191
|
}
|
|
212
192
|
/**
|
|
213
193
|
* Verify the key binding of a SD-JWT by validating the signature of the key bound to the SD-JWT
|
|
@@ -232,66 +212,63 @@ class SDJwtPlugin {
|
|
|
232
212
|
* @param signature - The signature
|
|
233
213
|
* @returns
|
|
234
214
|
*/
|
|
235
|
-
verify(sdjwt, context, data, signature, opts) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
trustAnchors.add(trustAnchors_1.sphereonCA);
|
|
247
|
-
trustAnchors.add(trustAnchors_1.funkeTestCA);
|
|
248
|
-
}
|
|
249
|
-
const certificateValidationResult = yield context.agent.x509VerifyCertificateChain({
|
|
250
|
-
chain: x5c,
|
|
251
|
-
trustAnchors: Array.from(trustAnchors),
|
|
252
|
-
// TODO: Defaults to allowing untrusted certs! Fine for now, not when wallets go mainstream
|
|
253
|
-
opts: (_a = opts === null || opts === void 0 ? void 0 : opts.x5cValidation) !== null && _a !== void 0 ? _a : { trustRootWhenNoAnchors: true, allowNoTrustAnchorsFound: true },
|
|
254
|
-
});
|
|
255
|
-
if (certificateValidationResult.error || !(certificateValidationResult === null || certificateValidationResult === void 0 ? void 0 : certificateValidationResult.certificateChain)) {
|
|
256
|
-
return Promise.reject(Error(`Certificate chain validation failed. ${certificateValidationResult.message}`));
|
|
257
|
-
}
|
|
258
|
-
const certInfo = certificateValidationResult.certificateChain[0];
|
|
259
|
-
jwk = certInfo.publicKeyJWK;
|
|
215
|
+
async verify(sdjwt, context, data, signature, opts) {
|
|
216
|
+
const decodedVC = await sdjwt.decode(`${data}.${signature}`);
|
|
217
|
+
const issuer = decodedVC.jwt.payload.iss;
|
|
218
|
+
const header = decodedVC.jwt.header;
|
|
219
|
+
const x5c = header?.x5c;
|
|
220
|
+
let jwk = header.jwk;
|
|
221
|
+
if (x5c) {
|
|
222
|
+
const trustAnchors = new Set([...this.trustAnchorsInPEM]);
|
|
223
|
+
if (trustAnchors.size === 0) {
|
|
224
|
+
trustAnchors.add(sphereonCA);
|
|
225
|
+
trustAnchors.add(funkeTestCA);
|
|
260
226
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
throw new Error('invalid_issuer: issuer did document does not include referenced key');
|
|
270
|
-
}
|
|
271
|
-
//FIXME SDK-21: in case it's another did method, the value of the key can be also encoded as a base64url
|
|
272
|
-
// needs more checks. some DID methods do not expose the keys as publicKeyJwk
|
|
273
|
-
jwk = didDocumentKey.publicKeyJwk;
|
|
227
|
+
const certificateValidationResult = await context.agent.x509VerifyCertificateChain({
|
|
228
|
+
chain: x5c,
|
|
229
|
+
trustAnchors: Array.from(trustAnchors),
|
|
230
|
+
// TODO: Defaults to allowing untrusted certs! Fine for now, not when wallets go mainstream
|
|
231
|
+
opts: opts?.x5cValidation ?? { trustRootWhenNoAnchors: true, allowNoTrustAnchorsFound: true },
|
|
232
|
+
});
|
|
233
|
+
if (certificateValidationResult.error || !certificateValidationResult?.certificateChain) {
|
|
234
|
+
return Promise.reject(Error(`Certificate chain validation failed. ${certificateValidationResult.message}`));
|
|
274
235
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const didDocumentKey = (_f = (_e = didDoc.didDocument) === null || _e === void 0 ? void 0 : _e.verificationMethod) === null || _f === void 0 ? void 0 : _f.find((key) => key.id);
|
|
283
|
-
if (!didDocumentKey) {
|
|
284
|
-
throw new Error('invalid_issuer: issuer did document does not include referenced key');
|
|
285
|
-
}
|
|
286
|
-
//FIXME SDK-21: in case it's another did method, the value of the key can be also encoded as a base64url
|
|
287
|
-
// needs more checks. some DID methods do not expose the keys as publicKeyJwk
|
|
288
|
-
jwk = didDocumentKey.publicKeyJwk;
|
|
236
|
+
const certInfo = certificateValidationResult.certificateChain[0];
|
|
237
|
+
jwk = certInfo.publicKeyJWK;
|
|
238
|
+
}
|
|
239
|
+
if (!jwk && header.kid?.includes('did:')) {
|
|
240
|
+
const didDoc = await context.agent.resolveDid({ didUrl: header.kid });
|
|
241
|
+
if (!didDoc) {
|
|
242
|
+
throw new Error('invalid_issuer: issuer did not resolve to a did document');
|
|
289
243
|
}
|
|
290
|
-
|
|
291
|
-
|
|
244
|
+
//TODO SDK-20: This should be checking for an assertionMethod and not just an verificationMethod with an id
|
|
245
|
+
const didDocumentKey = didDoc.didDocument?.verificationMethod?.find((key) => key.id);
|
|
246
|
+
if (!didDocumentKey) {
|
|
247
|
+
throw new Error('invalid_issuer: issuer did document does not include referenced key');
|
|
292
248
|
}
|
|
293
|
-
|
|
294
|
-
|
|
249
|
+
//FIXME SDK-21: in case it's another did method, the value of the key can be also encoded as a base64url
|
|
250
|
+
// needs more checks. some DID methods do not expose the keys as publicKeyJwk
|
|
251
|
+
jwk = didDocumentKey.publicKeyJwk;
|
|
252
|
+
}
|
|
253
|
+
if (!jwk && issuer.includes('did:')) {
|
|
254
|
+
// TODO refactor
|
|
255
|
+
const didDoc = await context.agent.resolveDid({ didUrl: issuer });
|
|
256
|
+
if (!didDoc) {
|
|
257
|
+
throw new Error('invalid_issuer: issuer did not resolve to a did document');
|
|
258
|
+
}
|
|
259
|
+
//TODO SDK-20: This should be checking for an assertionMethod and not just an verificationMethod with an id
|
|
260
|
+
const didDocumentKey = didDoc.didDocument?.verificationMethod?.find((key) => key.id);
|
|
261
|
+
if (!didDocumentKey) {
|
|
262
|
+
throw new Error('invalid_issuer: issuer did document does not include referenced key');
|
|
263
|
+
}
|
|
264
|
+
//FIXME SDK-21: in case it's another did method, the value of the key can be also encoded as a base64url
|
|
265
|
+
// needs more checks. some DID methods do not expose the keys as publicKeyJwk
|
|
266
|
+
jwk = didDocumentKey.publicKeyJwk;
|
|
267
|
+
}
|
|
268
|
+
if (!jwk) {
|
|
269
|
+
throw new Error('No valid public key found for signature verification');
|
|
270
|
+
}
|
|
271
|
+
return this.verifySignatureCallback(context)(data, signature, jwk);
|
|
295
272
|
}
|
|
296
273
|
/**
|
|
297
274
|
* Verify a signed SD-JWT presentation.
|
|
@@ -299,18 +276,16 @@ class SDJwtPlugin {
|
|
|
299
276
|
* @param context - This reserved param is automatically added and handled by the framework, *do not override*
|
|
300
277
|
* @returns
|
|
301
278
|
*/
|
|
302
|
-
verifySdJwtPresentation(args, context) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
kbVerifier: verifierKb,
|
|
311
|
-
});
|
|
312
|
-
return sdjwt.verify(args.presentation, args.requiredClaimKeys, args.kb);
|
|
279
|
+
async verifySdJwtPresentation(args, context) {
|
|
280
|
+
let sdjwt;
|
|
281
|
+
const verifier = async (data, signature) => this.verify(sdjwt, context, data, signature);
|
|
282
|
+
const verifierKb = async (data, signature, payload) => this.verifyKb(sdjwt, context, data, signature, payload);
|
|
283
|
+
sdjwt = new SDJwtVcInstance({
|
|
284
|
+
verifier,
|
|
285
|
+
hasher: this.registeredImplementations.hasher,
|
|
286
|
+
kbVerifier: verifierKb,
|
|
313
287
|
});
|
|
288
|
+
return sdjwt.verify(args.presentation, args.requiredClaimKeys, args.kb);
|
|
314
289
|
}
|
|
315
290
|
/**
|
|
316
291
|
* Fetch and validate Type Metadata.
|
|
@@ -318,37 +293,62 @@ class SDJwtPlugin {
|
|
|
318
293
|
* @param context - This reserved param is automatically added and handled by the framework, *do not override*
|
|
319
294
|
* @returns
|
|
320
295
|
*/
|
|
321
|
-
fetchSdJwtTypeMetadataFromVctUrl(args, context) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
|
|
296
|
+
async fetchSdJwtTypeMetadataFromVctUrl(args, context) {
|
|
297
|
+
const { vct, vctIntegrity, opts } = args;
|
|
298
|
+
const url = new URL(vct);
|
|
299
|
+
const response = await fetchUrlWithErrorHandling(url.toString());
|
|
300
|
+
const metadata = await response.json();
|
|
301
|
+
assertValidTypeMetadata(metadata, vct);
|
|
302
|
+
const validate = async (vct, input, integrityValue, hasher) => {
|
|
303
|
+
if (hasher && integrityValue) {
|
|
304
|
+
const validation = await validateIntegrity({ integrityValue, input, hasher });
|
|
305
|
+
if (!validation) {
|
|
306
|
+
return Promise.reject(Error(`Integrity check failed for vct: ${vct}, extends: ${metadata.extends}, integrity: ${integrityValue}}`));
|
|
331
307
|
}
|
|
332
308
|
}
|
|
333
|
-
|
|
334
|
-
|
|
309
|
+
};
|
|
310
|
+
const hasher = (opts?.hasher ?? this.registeredImplementations.hasher ?? defaultGenerateDigest);
|
|
311
|
+
if (hasher) {
|
|
312
|
+
if (vctIntegrity) {
|
|
313
|
+
await validate(vct, metadata, vctIntegrity, hasher);
|
|
314
|
+
const vctValidation = await validateIntegrity({ integrityValue: vctIntegrity, input: metadata, hasher });
|
|
315
|
+
if (!vctValidation) {
|
|
316
|
+
return Promise.reject(Error(`Integrity check failed for vct: ${vct}, integrity: ${vctIntegrity}`));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (metadata['extends#integrity']) {
|
|
320
|
+
const extendsMetadata = await this.fetchSdJwtTypeMetadataFromVctUrl({ vct: metadata['extends#integrity'], opts }, context);
|
|
321
|
+
await validate(vct, extendsMetadata, metadata['extends#integrity'], hasher);
|
|
322
|
+
}
|
|
323
|
+
if (metadata['schema_uri#integrity']) {
|
|
324
|
+
const schemaResponse = await fetchUrlWithErrorHandling(metadata.schema_uri);
|
|
325
|
+
const schema = await schemaResponse.json();
|
|
326
|
+
await validate(vct, schema, metadata['schema_uri#integrity'], hasher);
|
|
327
|
+
}
|
|
328
|
+
metadata.display?.forEach((display) => {
|
|
329
|
+
const simpleLogoIntegrity = display.rendering?.simple?.logo?.['uri#integrity'];
|
|
330
|
+
if (simpleLogoIntegrity) {
|
|
331
|
+
console.log('TODO: Logo integrity check');
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
return metadata;
|
|
335
336
|
}
|
|
336
337
|
verifySignatureCallback(context) {
|
|
337
338
|
if (typeof this.registeredImplementations.verifySignature === 'function') {
|
|
338
339
|
return this.registeredImplementations.verifySignature;
|
|
339
340
|
}
|
|
340
|
-
return
|
|
341
|
+
return defaultVerifySignature(context);
|
|
341
342
|
}
|
|
342
343
|
getJwk(payload) {
|
|
343
|
-
|
|
344
|
-
if (((_a = payload.cnf) === null || _a === void 0 ? void 0 : _a.jwk) !== undefined) {
|
|
344
|
+
if (payload.cnf?.jwk !== undefined) {
|
|
345
345
|
return payload.cnf.jwk;
|
|
346
346
|
}
|
|
347
347
|
else if (payload.cnf !== undefined && 'kid' in payload.cnf && typeof payload.cnf.kid === 'string' && payload.cnf.kid.startsWith('did:jwk:')) {
|
|
348
348
|
// extract JWK from kid FIXME isn't there a did function for this already? Otherwise create one
|
|
349
349
|
// FIXME this is a quick-fix to make verification but we need a real solution
|
|
350
350
|
const encoded = this.extractBase64FromDIDJwk(payload.cnf.kid);
|
|
351
|
-
const decoded =
|
|
351
|
+
const decoded = decodeBase64url(encoded);
|
|
352
352
|
const jwt = JSON.parse(decoded);
|
|
353
353
|
return jwt;
|
|
354
354
|
}
|
|
@@ -362,5 +362,4 @@ class SDJwtPlugin {
|
|
|
362
362
|
return parts[2].split('#')[0];
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
|
-
exports.SDJwtPlugin = SDJwtPlugin;
|
|
366
365
|
//# sourceMappingURL=action-handler.js.map
|