@arcblock/vc 1.29.19 → 1.29.21
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/esm/index.d.mts +23 -10
- package/esm/index.mjs +79 -38
- package/lib/index.cjs +79 -38
- package/lib/index.d.cts +23 -10
- package/package.json +5 -5
package/esm/index.d.mts
CHANGED
|
@@ -2,18 +2,15 @@ import { WalletObject } from "@ocap/wallet";
|
|
|
2
2
|
import stringify from "json-stable-stringify";
|
|
3
3
|
|
|
4
4
|
//#region src/index.d.ts
|
|
5
|
-
interface VerificationMethod {
|
|
6
|
-
id: string;
|
|
7
|
-
type: string;
|
|
8
|
-
controller: string;
|
|
9
|
-
publicKeyMultibase: string;
|
|
10
|
-
}
|
|
11
5
|
interface Proof {
|
|
12
6
|
type: string;
|
|
13
7
|
created: string;
|
|
14
8
|
proofPurpose: string;
|
|
15
9
|
jws: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
signer?: string;
|
|
16
12
|
pk?: string;
|
|
13
|
+
previousProof?: string | string[];
|
|
17
14
|
verificationMethod?: string;
|
|
18
15
|
}
|
|
19
16
|
interface Issuer {
|
|
@@ -38,7 +35,6 @@ interface VerifiableCredential {
|
|
|
38
35
|
issuanceDate: string;
|
|
39
36
|
expirationDate?: string;
|
|
40
37
|
credentialSubject: CredentialSubject;
|
|
41
|
-
verificationMethod?: VerificationMethod[];
|
|
42
38
|
proof: Proof | Proof[];
|
|
43
39
|
tag?: string;
|
|
44
40
|
credentialStatus?: CredentialStatus;
|
|
@@ -58,12 +54,10 @@ interface Credential {
|
|
|
58
54
|
id: string;
|
|
59
55
|
issued: string;
|
|
60
56
|
issuer: Issuer;
|
|
61
|
-
verificationMethod?: VerificationMethod[];
|
|
62
57
|
proof: Proof | Proof[];
|
|
63
58
|
claim: unknown;
|
|
64
59
|
}
|
|
65
60
|
declare const proofTypes: Record<number, string>;
|
|
66
|
-
declare const verificationKeyTypes: Record<number, string>;
|
|
67
61
|
/**
|
|
68
62
|
* Create a valid verifiable credential
|
|
69
63
|
*
|
|
@@ -120,6 +114,25 @@ declare function verify({
|
|
|
120
114
|
trustedIssuers: string | string[];
|
|
121
115
|
ignoreExpired?: boolean;
|
|
122
116
|
}): Promise<boolean>;
|
|
117
|
+
/**
|
|
118
|
+
* Counter-sign an existing verifiable credential
|
|
119
|
+
* Adds a new proof from a different signer to the VC
|
|
120
|
+
*
|
|
121
|
+
* @param params
|
|
122
|
+
* @param params.vc - The already-signed verifiable credential
|
|
123
|
+
* @param params.wallet - The counter-signer's wallet
|
|
124
|
+
* @param params.mode - 'set' (independent proof) or 'chain' (references previous proofs)
|
|
125
|
+
* @returns Promise<VerifiableCredential>
|
|
126
|
+
*/
|
|
127
|
+
declare function counterSign({
|
|
128
|
+
vc,
|
|
129
|
+
wallet,
|
|
130
|
+
mode
|
|
131
|
+
}: {
|
|
132
|
+
vc: VerifiableCredential;
|
|
133
|
+
wallet: WalletObject;
|
|
134
|
+
mode?: 'set' | 'chain';
|
|
135
|
+
}): Promise<VerifiableCredential>;
|
|
123
136
|
/**
|
|
124
137
|
* Verify that the Presentation is valid
|
|
125
138
|
* - It is signed by VC's owner
|
|
@@ -162,4 +175,4 @@ declare function verifyCredentialList({
|
|
|
162
175
|
}): Promise<unknown[]>;
|
|
163
176
|
declare const stableStringify: typeof stringify;
|
|
164
177
|
//#endregion
|
|
165
|
-
export { create, createCredentialList, proofTypes, stableStringify,
|
|
178
|
+
export { counterSign, create, createCredentialList, proofTypes, stableStringify, verify, verifyCredentialList, verifyPresentation };
|
package/esm/index.mjs
CHANGED
|
@@ -22,18 +22,6 @@ const proofTypes = {
|
|
|
22
22
|
[types.KeyType.SECP256K1]: "Secp256k1Signature",
|
|
23
23
|
[types.KeyType.ETHEREUM]: "EthereumSignature"
|
|
24
24
|
};
|
|
25
|
-
const verificationKeyTypes = {
|
|
26
|
-
[types.KeyType.ED25519]: "Ed25519VerificationKey2020",
|
|
27
|
-
[types.KeyType.SECP256K1]: "EcdsaSecp256k1VerificationKey2019",
|
|
28
|
-
[types.KeyType.ETHEREUM]: "EcdsaSecp256k1VerificationKey2019"
|
|
29
|
-
};
|
|
30
|
-
function resolvePublicKey(proof, verificationMethods, fallbackPk) {
|
|
31
|
-
if (proof.verificationMethod && verificationMethods?.length) {
|
|
32
|
-
const keyEntry = verificationMethods.find((k) => k.id === proof.verificationMethod);
|
|
33
|
-
if (keyEntry?.publicKeyMultibase) return keyEntry.publicKeyMultibase;
|
|
34
|
-
}
|
|
35
|
-
return fallbackPk;
|
|
36
|
-
}
|
|
37
25
|
/**
|
|
38
26
|
* Create a valid verifiable credential
|
|
39
27
|
*
|
|
@@ -68,7 +56,6 @@ async function create({ type, subject, issuer, issuanceDate, expirationDate, tag
|
|
|
68
56
|
if (!proofTypes[pkType]) throw new Error("Unsupported signer type when create verifiable credential");
|
|
69
57
|
const issuanceDateValue = issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
|
|
70
58
|
const issuerPk = toBase58(wallet.publicKey);
|
|
71
|
-
const keyId = `did:abt:${issuerDid}#key-1`;
|
|
72
59
|
const vcObj = {
|
|
73
60
|
"@context": ["https://www.w3.org/2018/credentials/v1", "https://schema.arcblock.io/v0.1/context.jsonld"],
|
|
74
61
|
id: vcDid,
|
|
@@ -80,13 +67,7 @@ async function create({ type, subject, issuer, issuanceDate, expirationDate, tag
|
|
|
80
67
|
},
|
|
81
68
|
issuanceDate: issuanceDateValue,
|
|
82
69
|
expirationDate,
|
|
83
|
-
credentialSubject: subject
|
|
84
|
-
verificationMethod: [{
|
|
85
|
-
id: keyId,
|
|
86
|
-
type: verificationKeyTypes[pkType],
|
|
87
|
-
controller: `did:abt:${issuerDid}`,
|
|
88
|
-
publicKeyMultibase: issuerPk
|
|
89
|
-
}]
|
|
70
|
+
credentialSubject: subject
|
|
90
71
|
};
|
|
91
72
|
if (tag) vcObj.tag = tag;
|
|
92
73
|
if (endpoint) vcObj.credentialStatus = {
|
|
@@ -100,7 +81,9 @@ async function create({ type, subject, issuer, issuanceDate, expirationDate, tag
|
|
|
100
81
|
type: proofTypes[pkType],
|
|
101
82
|
created: issuanceDateValue,
|
|
102
83
|
proofPurpose: "assertionMethod",
|
|
103
|
-
|
|
84
|
+
id: crypto.randomUUID(),
|
|
85
|
+
signer: issuerDid,
|
|
86
|
+
pk: issuerPk,
|
|
104
87
|
jws: toBase64(signature)
|
|
105
88
|
},
|
|
106
89
|
...vcObj
|
|
@@ -135,16 +118,72 @@ async function verify({ vc, ownerDid, trustedIssuers, ignoreExpired = false }) {
|
|
|
135
118
|
if (vc.issuanceDate === void 0) throw Error("Invalid verifiable credential issue date");
|
|
136
119
|
if (new Date(vc.issuanceDate).getTime() > Date.now()) throw Error("Verifiable credential has not take effect");
|
|
137
120
|
if (!ignoreExpired && vc.expirationDate !== void 0 && new Date(vc.expirationDate).getTime() < Date.now()) throw Error("Verifiable credential has expired");
|
|
138
|
-
const
|
|
139
|
-
if (!
|
|
140
|
-
if (!isFromPublicKey(issuerDid, vc.issuer.pk)) throw new Error("Verifiable credential not issuer pk not match with issuer did");
|
|
121
|
+
const issuers = Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers];
|
|
122
|
+
if (!isFromPublicKey(vc.issuer.id, vc.issuer.pk)) throw new Error("Verifiable credential not issuer pk not match with issuer did");
|
|
141
123
|
if (ownerDid !== vc.credentialSubject.id) throw new Error("Verifiable credential not owned by specified owner did");
|
|
124
|
+
for (const proof of proofList) {
|
|
125
|
+
const proofPk = proof.pk || vc.issuer.pk;
|
|
126
|
+
const proofSigner = proof.signer || vc.issuer.id;
|
|
127
|
+
if (!issuers.includes(proofSigner)) throw new Error("Proof signer not in trusted issuers");
|
|
128
|
+
if (proof.signer && proof.pk && !isFromPublicKey(proof.signer, proof.pk)) throw new Error("Proof pk does not match signer");
|
|
129
|
+
const clone = cloneDeep(vc);
|
|
130
|
+
delete clone.signature;
|
|
131
|
+
const prevProofRefs = proof.previousProof;
|
|
132
|
+
if (prevProofRefs && (typeof prevProofRefs === "string" || Array.isArray(prevProofRefs) && prevProofRefs.length > 0)) {
|
|
133
|
+
const prevIds = Array.isArray(prevProofRefs) ? prevProofRefs : [prevProofRefs];
|
|
134
|
+
const matchingProofs = proofList.filter((p) => p.id && prevIds.includes(p.id));
|
|
135
|
+
if (matchingProofs.length !== prevIds.length) throw new Error("Referenced previous proof not found");
|
|
136
|
+
clone.proof = matchingProofs;
|
|
137
|
+
} else delete clone.proof;
|
|
138
|
+
const signedContent = stringify(clone);
|
|
139
|
+
if (await fromPublicKey(proofPk, toTypeInfo(proofSigner)).verify(signedContent, fromBase64(proof.jws)) !== true) throw Error("Verifiable credential signature not valid");
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Counter-sign an existing verifiable credential
|
|
145
|
+
* Adds a new proof from a different signer to the VC
|
|
146
|
+
*
|
|
147
|
+
* @param params
|
|
148
|
+
* @param params.vc - The already-signed verifiable credential
|
|
149
|
+
* @param params.wallet - The counter-signer's wallet
|
|
150
|
+
* @param params.mode - 'set' (independent proof) or 'chain' (references previous proofs)
|
|
151
|
+
* @returns Promise<VerifiableCredential>
|
|
152
|
+
*/
|
|
153
|
+
async function counterSign({ vc, wallet, mode = "set" }) {
|
|
154
|
+
if (!vc) throw new Error("Cannot counter-sign empty verifiable credential");
|
|
155
|
+
const existingProofs = Array.isArray(vc.proof) ? vc.proof : vc.proof ? [vc.proof] : [];
|
|
156
|
+
if (existingProofs.length === 0) throw new Error("Cannot counter-sign verifiable credential without existing proof");
|
|
157
|
+
const signerDid = wallet.address;
|
|
158
|
+
const pkType = toTypeInfo(signerDid).pk;
|
|
159
|
+
if (!proofTypes[pkType]) throw new Error("Unsupported signer type when counter-signing verifiable credential");
|
|
160
|
+
const signerPk = toBase58(wallet.publicKey);
|
|
161
|
+
const proofsWithIds = existingProofs.map((p) => p.id ? p : {
|
|
162
|
+
...p,
|
|
163
|
+
id: crypto.randomUUID()
|
|
164
|
+
});
|
|
142
165
|
const clone = cloneDeep(vc);
|
|
143
|
-
delete clone.proof;
|
|
144
166
|
delete clone.signature;
|
|
167
|
+
const newProof = {
|
|
168
|
+
type: proofTypes[pkType],
|
|
169
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
170
|
+
proofPurpose: "assertionMethod",
|
|
171
|
+
id: crypto.randomUUID(),
|
|
172
|
+
signer: signerDid,
|
|
173
|
+
pk: signerPk,
|
|
174
|
+
jws: ""
|
|
175
|
+
};
|
|
176
|
+
if (mode === "chain") {
|
|
177
|
+
clone.proof = proofsWithIds;
|
|
178
|
+
const prevIds = proofsWithIds.map((p) => p.id);
|
|
179
|
+
newProof.previousProof = prevIds.length === 1 ? prevIds[0] : prevIds;
|
|
180
|
+
} else delete clone.proof;
|
|
145
181
|
const signedContent = stringify(clone);
|
|
146
|
-
|
|
147
|
-
return
|
|
182
|
+
newProof.jws = toBase64(await wallet.sign(signedContent));
|
|
183
|
+
return {
|
|
184
|
+
...vc,
|
|
185
|
+
proof: [...proofsWithIds, newProof]
|
|
186
|
+
};
|
|
148
187
|
}
|
|
149
188
|
/**
|
|
150
189
|
* Verify that the Presentation is valid
|
|
@@ -194,7 +233,6 @@ async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
|
194
233
|
const issued = issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
|
|
195
234
|
const pkType = typeInfo.pk;
|
|
196
235
|
const issuerPk = toBase58(wallet.publicKey);
|
|
197
|
-
const keyId = `did:abt:${issuerDid}#key-1`;
|
|
198
236
|
return await Promise.all(claims.map(async (x) => {
|
|
199
237
|
const vc = { claim: x };
|
|
200
238
|
vc.id = fromPublicKeyHash(wallet.hash(stringify(vc.claim)), vcType);
|
|
@@ -204,18 +242,14 @@ async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
|
204
242
|
pk: issuerPk,
|
|
205
243
|
name: name$1 || issuerDid
|
|
206
244
|
};
|
|
207
|
-
vc.verificationMethod = [{
|
|
208
|
-
id: keyId,
|
|
209
|
-
type: verificationKeyTypes[pkType],
|
|
210
|
-
controller: `did:abt:${issuerDid}`,
|
|
211
|
-
publicKeyMultibase: issuerPk
|
|
212
|
-
}];
|
|
213
245
|
const signature = await wallet.sign(stringify(vc));
|
|
214
246
|
vc.proof = {
|
|
215
247
|
type: proofTypes[pkType],
|
|
216
248
|
created: issued,
|
|
217
249
|
proofPurpose: "assertionMethod",
|
|
218
|
-
|
|
250
|
+
id: crypto.randomUUID(),
|
|
251
|
+
signer: issuerDid,
|
|
252
|
+
pk: issuerPk,
|
|
219
253
|
jws: toBase64(signature)
|
|
220
254
|
};
|
|
221
255
|
return vc;
|
|
@@ -224,7 +258,8 @@ async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
|
224
258
|
async function verifyCredentialList({ credentials, trustedIssuers }) {
|
|
225
259
|
if (!credentials || !Array.isArray(credentials)) throw new Error("Can not verify with empty credentials list");
|
|
226
260
|
return Promise.all(credentials.map(async (x) => {
|
|
227
|
-
const
|
|
261
|
+
const issuers = Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers];
|
|
262
|
+
const issuerDid = issuers.find((d) => d === x.issuer.id);
|
|
228
263
|
if (!issuerDid) throw new Error("Credential not issued by trusted issuers");
|
|
229
264
|
if (!isFromPublicKey(issuerDid, x.issuer.pk)) throw new Error("Credential not issuer pk not match with issuer did");
|
|
230
265
|
const proofList = Array.isArray(x.proof) ? x.proof : x.proof ? [x.proof] : [];
|
|
@@ -232,11 +267,17 @@ async function verifyCredentialList({ credentials, trustedIssuers }) {
|
|
|
232
267
|
const clone = cloneDeep(x);
|
|
233
268
|
delete clone.proof;
|
|
234
269
|
const signedContent = stringify(clone);
|
|
235
|
-
for (const proof of proofList)
|
|
270
|
+
for (const proof of proofList) {
|
|
271
|
+
const proofPk = proof.pk || x.issuer.pk;
|
|
272
|
+
const proofSigner = proof.signer || x.issuer.id;
|
|
273
|
+
if (!issuers.includes(proofSigner)) throw new Error("Proof signer not in trusted issuers");
|
|
274
|
+
if (proof.signer && proof.pk && !isFromPublicKey(proof.signer, proof.pk)) throw new Error("Proof pk does not match signer");
|
|
275
|
+
if (await fromPublicKey(proofPk, toTypeInfo(proofSigner)).verify(signedContent, fromBase64(proof.jws)) !== true) throw Error("Status credential signature not valid");
|
|
276
|
+
}
|
|
236
277
|
return x.claim;
|
|
237
278
|
}));
|
|
238
279
|
}
|
|
239
280
|
const stableStringify = stringify;
|
|
240
281
|
|
|
241
282
|
//#endregion
|
|
242
|
-
export { create, createCredentialList, proofTypes, stableStringify,
|
|
283
|
+
export { counterSign, create, createCredentialList, proofTypes, stableStringify, verify, verifyCredentialList, verifyPresentation };
|
package/lib/index.cjs
CHANGED
|
@@ -27,18 +27,6 @@ const proofTypes = {
|
|
|
27
27
|
[_ocap_mcrypto.types.KeyType.SECP256K1]: "Secp256k1Signature",
|
|
28
28
|
[_ocap_mcrypto.types.KeyType.ETHEREUM]: "EthereumSignature"
|
|
29
29
|
};
|
|
30
|
-
const verificationKeyTypes = {
|
|
31
|
-
[_ocap_mcrypto.types.KeyType.ED25519]: "Ed25519VerificationKey2020",
|
|
32
|
-
[_ocap_mcrypto.types.KeyType.SECP256K1]: "EcdsaSecp256k1VerificationKey2019",
|
|
33
|
-
[_ocap_mcrypto.types.KeyType.ETHEREUM]: "EcdsaSecp256k1VerificationKey2019"
|
|
34
|
-
};
|
|
35
|
-
function resolvePublicKey(proof, verificationMethods, fallbackPk) {
|
|
36
|
-
if (proof.verificationMethod && verificationMethods?.length) {
|
|
37
|
-
const keyEntry = verificationMethods.find((k) => k.id === proof.verificationMethod);
|
|
38
|
-
if (keyEntry?.publicKeyMultibase) return keyEntry.publicKeyMultibase;
|
|
39
|
-
}
|
|
40
|
-
return fallbackPk;
|
|
41
|
-
}
|
|
42
30
|
/**
|
|
43
31
|
* Create a valid verifiable credential
|
|
44
32
|
*
|
|
@@ -73,7 +61,6 @@ async function create({ type, subject, issuer, issuanceDate, expirationDate, tag
|
|
|
73
61
|
if (!proofTypes[pkType]) throw new Error("Unsupported signer type when create verifiable credential");
|
|
74
62
|
const issuanceDateValue = issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
|
|
75
63
|
const issuerPk = (0, _ocap_util.toBase58)(wallet.publicKey);
|
|
76
|
-
const keyId = `did:abt:${issuerDid}#key-1`;
|
|
77
64
|
const vcObj = {
|
|
78
65
|
"@context": ["https://www.w3.org/2018/credentials/v1", "https://schema.arcblock.io/v0.1/context.jsonld"],
|
|
79
66
|
id: vcDid,
|
|
@@ -85,13 +72,7 @@ async function create({ type, subject, issuer, issuanceDate, expirationDate, tag
|
|
|
85
72
|
},
|
|
86
73
|
issuanceDate: issuanceDateValue,
|
|
87
74
|
expirationDate,
|
|
88
|
-
credentialSubject: subject
|
|
89
|
-
verificationMethod: [{
|
|
90
|
-
id: keyId,
|
|
91
|
-
type: verificationKeyTypes[pkType],
|
|
92
|
-
controller: `did:abt:${issuerDid}`,
|
|
93
|
-
publicKeyMultibase: issuerPk
|
|
94
|
-
}]
|
|
75
|
+
credentialSubject: subject
|
|
95
76
|
};
|
|
96
77
|
if (tag) vcObj.tag = tag;
|
|
97
78
|
if (endpoint) vcObj.credentialStatus = {
|
|
@@ -105,7 +86,9 @@ async function create({ type, subject, issuer, issuanceDate, expirationDate, tag
|
|
|
105
86
|
type: proofTypes[pkType],
|
|
106
87
|
created: issuanceDateValue,
|
|
107
88
|
proofPurpose: "assertionMethod",
|
|
108
|
-
|
|
89
|
+
id: crypto.randomUUID(),
|
|
90
|
+
signer: issuerDid,
|
|
91
|
+
pk: issuerPk,
|
|
109
92
|
jws: (0, _ocap_util.toBase64)(signature)
|
|
110
93
|
},
|
|
111
94
|
...vcObj
|
|
@@ -140,16 +123,72 @@ async function verify({ vc, ownerDid, trustedIssuers, ignoreExpired = false }) {
|
|
|
140
123
|
if (vc.issuanceDate === void 0) throw Error("Invalid verifiable credential issue date");
|
|
141
124
|
if (new Date(vc.issuanceDate).getTime() > Date.now()) throw Error("Verifiable credential has not take effect");
|
|
142
125
|
if (!ignoreExpired && vc.expirationDate !== void 0 && new Date(vc.expirationDate).getTime() < Date.now()) throw Error("Verifiable credential has expired");
|
|
143
|
-
const
|
|
144
|
-
if (!
|
|
145
|
-
if (!(0, _arcblock_did.isFromPublicKey)(issuerDid, vc.issuer.pk)) throw new Error("Verifiable credential not issuer pk not match with issuer did");
|
|
126
|
+
const issuers = Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers];
|
|
127
|
+
if (!(0, _arcblock_did.isFromPublicKey)(vc.issuer.id, vc.issuer.pk)) throw new Error("Verifiable credential not issuer pk not match with issuer did");
|
|
146
128
|
if (ownerDid !== vc.credentialSubject.id) throw new Error("Verifiable credential not owned by specified owner did");
|
|
129
|
+
for (const proof of proofList) {
|
|
130
|
+
const proofPk = proof.pk || vc.issuer.pk;
|
|
131
|
+
const proofSigner = proof.signer || vc.issuer.id;
|
|
132
|
+
if (!issuers.includes(proofSigner)) throw new Error("Proof signer not in trusted issuers");
|
|
133
|
+
if (proof.signer && proof.pk && !(0, _arcblock_did.isFromPublicKey)(proof.signer, proof.pk)) throw new Error("Proof pk does not match signer");
|
|
134
|
+
const clone = (0, lodash_cloneDeep.default)(vc);
|
|
135
|
+
delete clone.signature;
|
|
136
|
+
const prevProofRefs = proof.previousProof;
|
|
137
|
+
if (prevProofRefs && (typeof prevProofRefs === "string" || Array.isArray(prevProofRefs) && prevProofRefs.length > 0)) {
|
|
138
|
+
const prevIds = Array.isArray(prevProofRefs) ? prevProofRefs : [prevProofRefs];
|
|
139
|
+
const matchingProofs = proofList.filter((p) => p.id && prevIds.includes(p.id));
|
|
140
|
+
if (matchingProofs.length !== prevIds.length) throw new Error("Referenced previous proof not found");
|
|
141
|
+
clone.proof = matchingProofs;
|
|
142
|
+
} else delete clone.proof;
|
|
143
|
+
const signedContent = (0, json_stable_stringify.default)(clone);
|
|
144
|
+
if (await (0, _ocap_wallet.fromPublicKey)(proofPk, (0, _arcblock_did.toTypeInfo)(proofSigner)).verify(signedContent, (0, _ocap_util.fromBase64)(proof.jws)) !== true) throw Error("Verifiable credential signature not valid");
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Counter-sign an existing verifiable credential
|
|
150
|
+
* Adds a new proof from a different signer to the VC
|
|
151
|
+
*
|
|
152
|
+
* @param params
|
|
153
|
+
* @param params.vc - The already-signed verifiable credential
|
|
154
|
+
* @param params.wallet - The counter-signer's wallet
|
|
155
|
+
* @param params.mode - 'set' (independent proof) or 'chain' (references previous proofs)
|
|
156
|
+
* @returns Promise<VerifiableCredential>
|
|
157
|
+
*/
|
|
158
|
+
async function counterSign({ vc, wallet, mode = "set" }) {
|
|
159
|
+
if (!vc) throw new Error("Cannot counter-sign empty verifiable credential");
|
|
160
|
+
const existingProofs = Array.isArray(vc.proof) ? vc.proof : vc.proof ? [vc.proof] : [];
|
|
161
|
+
if (existingProofs.length === 0) throw new Error("Cannot counter-sign verifiable credential without existing proof");
|
|
162
|
+
const signerDid = wallet.address;
|
|
163
|
+
const pkType = (0, _arcblock_did.toTypeInfo)(signerDid).pk;
|
|
164
|
+
if (!proofTypes[pkType]) throw new Error("Unsupported signer type when counter-signing verifiable credential");
|
|
165
|
+
const signerPk = (0, _ocap_util.toBase58)(wallet.publicKey);
|
|
166
|
+
const proofsWithIds = existingProofs.map((p) => p.id ? p : {
|
|
167
|
+
...p,
|
|
168
|
+
id: crypto.randomUUID()
|
|
169
|
+
});
|
|
147
170
|
const clone = (0, lodash_cloneDeep.default)(vc);
|
|
148
|
-
delete clone.proof;
|
|
149
171
|
delete clone.signature;
|
|
172
|
+
const newProof = {
|
|
173
|
+
type: proofTypes[pkType],
|
|
174
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
175
|
+
proofPurpose: "assertionMethod",
|
|
176
|
+
id: crypto.randomUUID(),
|
|
177
|
+
signer: signerDid,
|
|
178
|
+
pk: signerPk,
|
|
179
|
+
jws: ""
|
|
180
|
+
};
|
|
181
|
+
if (mode === "chain") {
|
|
182
|
+
clone.proof = proofsWithIds;
|
|
183
|
+
const prevIds = proofsWithIds.map((p) => p.id);
|
|
184
|
+
newProof.previousProof = prevIds.length === 1 ? prevIds[0] : prevIds;
|
|
185
|
+
} else delete clone.proof;
|
|
150
186
|
const signedContent = (0, json_stable_stringify.default)(clone);
|
|
151
|
-
|
|
152
|
-
return
|
|
187
|
+
newProof.jws = (0, _ocap_util.toBase64)(await wallet.sign(signedContent));
|
|
188
|
+
return {
|
|
189
|
+
...vc,
|
|
190
|
+
proof: [...proofsWithIds, newProof]
|
|
191
|
+
};
|
|
153
192
|
}
|
|
154
193
|
/**
|
|
155
194
|
* Verify that the Presentation is valid
|
|
@@ -199,7 +238,6 @@ async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
|
199
238
|
const issued = issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
|
|
200
239
|
const pkType = typeInfo.pk;
|
|
201
240
|
const issuerPk = (0, _ocap_util.toBase58)(wallet.publicKey);
|
|
202
|
-
const keyId = `did:abt:${issuerDid}#key-1`;
|
|
203
241
|
return await Promise.all(claims.map(async (x) => {
|
|
204
242
|
const vc = { claim: x };
|
|
205
243
|
vc.id = (0, _arcblock_did.fromPublicKeyHash)(wallet.hash((0, json_stable_stringify.default)(vc.claim)), vcType);
|
|
@@ -209,18 +247,14 @@ async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
|
209
247
|
pk: issuerPk,
|
|
210
248
|
name: name$1 || issuerDid
|
|
211
249
|
};
|
|
212
|
-
vc.verificationMethod = [{
|
|
213
|
-
id: keyId,
|
|
214
|
-
type: verificationKeyTypes[pkType],
|
|
215
|
-
controller: `did:abt:${issuerDid}`,
|
|
216
|
-
publicKeyMultibase: issuerPk
|
|
217
|
-
}];
|
|
218
250
|
const signature = await wallet.sign((0, json_stable_stringify.default)(vc));
|
|
219
251
|
vc.proof = {
|
|
220
252
|
type: proofTypes[pkType],
|
|
221
253
|
created: issued,
|
|
222
254
|
proofPurpose: "assertionMethod",
|
|
223
|
-
|
|
255
|
+
id: crypto.randomUUID(),
|
|
256
|
+
signer: issuerDid,
|
|
257
|
+
pk: issuerPk,
|
|
224
258
|
jws: (0, _ocap_util.toBase64)(signature)
|
|
225
259
|
};
|
|
226
260
|
return vc;
|
|
@@ -229,7 +263,8 @@ async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
|
229
263
|
async function verifyCredentialList({ credentials, trustedIssuers }) {
|
|
230
264
|
if (!credentials || !Array.isArray(credentials)) throw new Error("Can not verify with empty credentials list");
|
|
231
265
|
return Promise.all(credentials.map(async (x) => {
|
|
232
|
-
const
|
|
266
|
+
const issuers = Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers];
|
|
267
|
+
const issuerDid = issuers.find((d) => d === x.issuer.id);
|
|
233
268
|
if (!issuerDid) throw new Error("Credential not issued by trusted issuers");
|
|
234
269
|
if (!(0, _arcblock_did.isFromPublicKey)(issuerDid, x.issuer.pk)) throw new Error("Credential not issuer pk not match with issuer did");
|
|
235
270
|
const proofList = Array.isArray(x.proof) ? x.proof : x.proof ? [x.proof] : [];
|
|
@@ -237,18 +272,24 @@ async function verifyCredentialList({ credentials, trustedIssuers }) {
|
|
|
237
272
|
const clone = (0, lodash_cloneDeep.default)(x);
|
|
238
273
|
delete clone.proof;
|
|
239
274
|
const signedContent = (0, json_stable_stringify.default)(clone);
|
|
240
|
-
for (const proof of proofList)
|
|
275
|
+
for (const proof of proofList) {
|
|
276
|
+
const proofPk = proof.pk || x.issuer.pk;
|
|
277
|
+
const proofSigner = proof.signer || x.issuer.id;
|
|
278
|
+
if (!issuers.includes(proofSigner)) throw new Error("Proof signer not in trusted issuers");
|
|
279
|
+
if (proof.signer && proof.pk && !(0, _arcblock_did.isFromPublicKey)(proof.signer, proof.pk)) throw new Error("Proof pk does not match signer");
|
|
280
|
+
if (await (0, _ocap_wallet.fromPublicKey)(proofPk, (0, _arcblock_did.toTypeInfo)(proofSigner)).verify(signedContent, (0, _ocap_util.fromBase64)(proof.jws)) !== true) throw Error("Status credential signature not valid");
|
|
281
|
+
}
|
|
241
282
|
return x.claim;
|
|
242
283
|
}));
|
|
243
284
|
}
|
|
244
285
|
const stableStringify = json_stable_stringify.default;
|
|
245
286
|
|
|
246
287
|
//#endregion
|
|
288
|
+
exports.counterSign = counterSign;
|
|
247
289
|
exports.create = create;
|
|
248
290
|
exports.createCredentialList = createCredentialList;
|
|
249
291
|
exports.proofTypes = proofTypes;
|
|
250
292
|
exports.stableStringify = stableStringify;
|
|
251
|
-
exports.verificationKeyTypes = verificationKeyTypes;
|
|
252
293
|
exports.verify = verify;
|
|
253
294
|
exports.verifyCredentialList = verifyCredentialList;
|
|
254
295
|
exports.verifyPresentation = verifyPresentation;
|
package/lib/index.d.cts
CHANGED
|
@@ -2,18 +2,15 @@ import { WalletObject } from "@ocap/wallet";
|
|
|
2
2
|
import stringify from "json-stable-stringify";
|
|
3
3
|
|
|
4
4
|
//#region src/index.d.ts
|
|
5
|
-
interface VerificationMethod {
|
|
6
|
-
id: string;
|
|
7
|
-
type: string;
|
|
8
|
-
controller: string;
|
|
9
|
-
publicKeyMultibase: string;
|
|
10
|
-
}
|
|
11
5
|
interface Proof {
|
|
12
6
|
type: string;
|
|
13
7
|
created: string;
|
|
14
8
|
proofPurpose: string;
|
|
15
9
|
jws: string;
|
|
10
|
+
id?: string;
|
|
11
|
+
signer?: string;
|
|
16
12
|
pk?: string;
|
|
13
|
+
previousProof?: string | string[];
|
|
17
14
|
verificationMethod?: string;
|
|
18
15
|
}
|
|
19
16
|
interface Issuer {
|
|
@@ -38,7 +35,6 @@ interface VerifiableCredential {
|
|
|
38
35
|
issuanceDate: string;
|
|
39
36
|
expirationDate?: string;
|
|
40
37
|
credentialSubject: CredentialSubject;
|
|
41
|
-
verificationMethod?: VerificationMethod[];
|
|
42
38
|
proof: Proof | Proof[];
|
|
43
39
|
tag?: string;
|
|
44
40
|
credentialStatus?: CredentialStatus;
|
|
@@ -58,12 +54,10 @@ interface Credential {
|
|
|
58
54
|
id: string;
|
|
59
55
|
issued: string;
|
|
60
56
|
issuer: Issuer;
|
|
61
|
-
verificationMethod?: VerificationMethod[];
|
|
62
57
|
proof: Proof | Proof[];
|
|
63
58
|
claim: unknown;
|
|
64
59
|
}
|
|
65
60
|
declare const proofTypes: Record<number, string>;
|
|
66
|
-
declare const verificationKeyTypes: Record<number, string>;
|
|
67
61
|
/**
|
|
68
62
|
* Create a valid verifiable credential
|
|
69
63
|
*
|
|
@@ -120,6 +114,25 @@ declare function verify({
|
|
|
120
114
|
trustedIssuers: string | string[];
|
|
121
115
|
ignoreExpired?: boolean;
|
|
122
116
|
}): Promise<boolean>;
|
|
117
|
+
/**
|
|
118
|
+
* Counter-sign an existing verifiable credential
|
|
119
|
+
* Adds a new proof from a different signer to the VC
|
|
120
|
+
*
|
|
121
|
+
* @param params
|
|
122
|
+
* @param params.vc - The already-signed verifiable credential
|
|
123
|
+
* @param params.wallet - The counter-signer's wallet
|
|
124
|
+
* @param params.mode - 'set' (independent proof) or 'chain' (references previous proofs)
|
|
125
|
+
* @returns Promise<VerifiableCredential>
|
|
126
|
+
*/
|
|
127
|
+
declare function counterSign({
|
|
128
|
+
vc,
|
|
129
|
+
wallet,
|
|
130
|
+
mode
|
|
131
|
+
}: {
|
|
132
|
+
vc: VerifiableCredential;
|
|
133
|
+
wallet: WalletObject;
|
|
134
|
+
mode?: 'set' | 'chain';
|
|
135
|
+
}): Promise<VerifiableCredential>;
|
|
123
136
|
/**
|
|
124
137
|
* Verify that the Presentation is valid
|
|
125
138
|
* - It is signed by VC's owner
|
|
@@ -162,4 +175,4 @@ declare function verifyCredentialList({
|
|
|
162
175
|
}): Promise<unknown[]>;
|
|
163
176
|
declare const stableStringify: typeof stringify;
|
|
164
177
|
//#endregion
|
|
165
|
-
export { create, createCredentialList, proofTypes, stableStringify,
|
|
178
|
+
export { counterSign, create, createCredentialList, proofTypes, stableStringify, verify, verifyCredentialList, verifyPresentation };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcblock/vc",
|
|
3
|
-
"version": "1.29.
|
|
3
|
+
"version": "1.29.21",
|
|
4
4
|
"description": "TypeScript lib to work with ArcBlock Verifiable Credentials",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arcblock",
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
"url": "https://github.com/ArcBlock/blockchain/issues"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@arcblock/did": "1.29.
|
|
76
|
-
"@ocap/mcrypto": "1.29.
|
|
77
|
-
"@ocap/util": "1.29.
|
|
78
|
-
"@ocap/wallet": "1.29.
|
|
75
|
+
"@arcblock/did": "1.29.21",
|
|
76
|
+
"@ocap/mcrypto": "1.29.21",
|
|
77
|
+
"@ocap/util": "1.29.21",
|
|
78
|
+
"@ocap/wallet": "1.29.21",
|
|
79
79
|
"debug": "^4.4.3",
|
|
80
80
|
"is-absolute-url": "^3.0.3",
|
|
81
81
|
"json-stable-stringify": "^1.0.1",
|