@arcblock/vc 1.28.9 → 1.29.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/README.md +0 -1
- package/esm/index.d.mts +155 -0
- package/esm/index.mjs +209 -0
- package/esm/package.mjs +5 -0
- package/lib/_virtual/rolldown_runtime.cjs +29 -0
- package/lib/index.cjs +220 -0
- package/lib/index.d.cts +155 -0
- package/lib/package.cjs +11 -0
- package/package.json +41 -15
- package/lib/index.d.ts +0 -84
- package/lib/index.js +0 -341
package/README.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
-
[](https://github.com/prettier/prettier)
|
|
4
3
|
[](https://docs.arcblock.io)
|
|
5
4
|
[](https://gitter.im/ArcBlock/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
|
6
5
|
|
package/esm/index.d.mts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { WalletObject } from "@ocap/wallet";
|
|
2
|
+
import stringify from "json-stable-stringify";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
interface Proof {
|
|
6
|
+
type: string;
|
|
7
|
+
created: string;
|
|
8
|
+
proofPurpose: string;
|
|
9
|
+
jws: string;
|
|
10
|
+
pk?: string;
|
|
11
|
+
}
|
|
12
|
+
interface Issuer {
|
|
13
|
+
id: string;
|
|
14
|
+
pk: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
interface CredentialSubject {
|
|
18
|
+
id: string;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
interface CredentialStatus {
|
|
22
|
+
id: string;
|
|
23
|
+
type: string;
|
|
24
|
+
scope: string;
|
|
25
|
+
}
|
|
26
|
+
interface VerifiableCredential {
|
|
27
|
+
'@context': string;
|
|
28
|
+
id: string;
|
|
29
|
+
type: string;
|
|
30
|
+
issuer: Issuer;
|
|
31
|
+
issuanceDate: string;
|
|
32
|
+
expirationDate?: string;
|
|
33
|
+
credentialSubject: CredentialSubject;
|
|
34
|
+
proof: Proof;
|
|
35
|
+
tag?: string;
|
|
36
|
+
credentialStatus?: CredentialStatus;
|
|
37
|
+
signature?: unknown;
|
|
38
|
+
}
|
|
39
|
+
interface IssuerInfo {
|
|
40
|
+
wallet: WalletObject;
|
|
41
|
+
name?: string;
|
|
42
|
+
}
|
|
43
|
+
interface Presentation {
|
|
44
|
+
challenge: string;
|
|
45
|
+
verifiableCredential: string | string[];
|
|
46
|
+
proof: Proof | Proof[];
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
interface Credential {
|
|
50
|
+
id: string;
|
|
51
|
+
issued: string;
|
|
52
|
+
issuer: Issuer;
|
|
53
|
+
proof: Proof;
|
|
54
|
+
claim: unknown;
|
|
55
|
+
}
|
|
56
|
+
declare const proofTypes: Record<number, string>;
|
|
57
|
+
/**
|
|
58
|
+
* Create a valid verifiable credential
|
|
59
|
+
*
|
|
60
|
+
* @param params
|
|
61
|
+
* @param params.type - The type of credential
|
|
62
|
+
* @param params.subject - The content of credential
|
|
63
|
+
* @param params.issuer - The issuer name and wallet
|
|
64
|
+
* @param params.issuanceDate
|
|
65
|
+
* @param params.expirationDate
|
|
66
|
+
* @param params.endpoint - Status endpoint url
|
|
67
|
+
* @param params.endpointScope - Endpoint scope, either be public or private
|
|
68
|
+
* @returns Promise<object>
|
|
69
|
+
*/
|
|
70
|
+
declare function create({
|
|
71
|
+
type,
|
|
72
|
+
subject,
|
|
73
|
+
issuer,
|
|
74
|
+
issuanceDate,
|
|
75
|
+
expirationDate,
|
|
76
|
+
tag,
|
|
77
|
+
endpoint,
|
|
78
|
+
endpointScope
|
|
79
|
+
}: {
|
|
80
|
+
type: string;
|
|
81
|
+
subject: CredentialSubject;
|
|
82
|
+
issuer: IssuerInfo;
|
|
83
|
+
issuanceDate?: string;
|
|
84
|
+
expirationDate?: string;
|
|
85
|
+
tag?: string;
|
|
86
|
+
endpoint?: string;
|
|
87
|
+
endpointScope?: string;
|
|
88
|
+
}): Promise<VerifiableCredential | null>;
|
|
89
|
+
/**
|
|
90
|
+
* Verify that the verifiable credential is valid
|
|
91
|
+
* - It is signed by a whitelist of issuers
|
|
92
|
+
* - It is owned by the vc.subject.id
|
|
93
|
+
* - It has valid signature by the issuer
|
|
94
|
+
* - It is not expired
|
|
95
|
+
*
|
|
96
|
+
* @param vc - the verifiable credential object
|
|
97
|
+
* @param ownerDid - vc holder/owner did
|
|
98
|
+
* @param trustedIssuers - list of issuer did
|
|
99
|
+
* @throws Error
|
|
100
|
+
* @returns Promise<boolean>
|
|
101
|
+
*/
|
|
102
|
+
declare function verify({
|
|
103
|
+
vc,
|
|
104
|
+
ownerDid,
|
|
105
|
+
trustedIssuers,
|
|
106
|
+
ignoreExpired
|
|
107
|
+
}: {
|
|
108
|
+
vc: VerifiableCredential | null;
|
|
109
|
+
ownerDid: string;
|
|
110
|
+
trustedIssuers: string | string[];
|
|
111
|
+
ignoreExpired?: boolean;
|
|
112
|
+
}): Promise<boolean>;
|
|
113
|
+
/**
|
|
114
|
+
* Verify that the Presentation is valid
|
|
115
|
+
* - It is signed by VC's owner
|
|
116
|
+
* - It contain challenge
|
|
117
|
+
* - It has valid signature by the issuer
|
|
118
|
+
* - It is not expired
|
|
119
|
+
*
|
|
120
|
+
* @param presentation - the presentation object
|
|
121
|
+
* @param trustedIssuers - list of issuer did
|
|
122
|
+
* @param challenge - Random byte you want
|
|
123
|
+
* @throws Error
|
|
124
|
+
* @returns Promise<boolean>
|
|
125
|
+
*/
|
|
126
|
+
declare function verifyPresentation({
|
|
127
|
+
presentation,
|
|
128
|
+
trustedIssuers,
|
|
129
|
+
challenge,
|
|
130
|
+
ignoreExpired
|
|
131
|
+
}: {
|
|
132
|
+
presentation: Presentation;
|
|
133
|
+
trustedIssuers: string[];
|
|
134
|
+
challenge: string;
|
|
135
|
+
ignoreExpired?: boolean;
|
|
136
|
+
}): Promise<boolean>;
|
|
137
|
+
declare function createCredentialList({
|
|
138
|
+
claims,
|
|
139
|
+
issuer,
|
|
140
|
+
issuanceDate
|
|
141
|
+
}: {
|
|
142
|
+
claims: unknown[];
|
|
143
|
+
issuer: IssuerInfo;
|
|
144
|
+
issuanceDate?: string;
|
|
145
|
+
}): Promise<Credential[]>;
|
|
146
|
+
declare function verifyCredentialList({
|
|
147
|
+
credentials,
|
|
148
|
+
trustedIssuers
|
|
149
|
+
}: {
|
|
150
|
+
credentials: Credential[];
|
|
151
|
+
trustedIssuers: string | string[];
|
|
152
|
+
}): Promise<unknown[]>;
|
|
153
|
+
declare const stableStringify: typeof stringify;
|
|
154
|
+
//#endregion
|
|
155
|
+
export { create, createCredentialList, proofTypes, stableStringify, verify, verifyCredentialList, verifyPresentation };
|
package/esm/index.mjs
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { name } from "./package.mjs";
|
|
2
|
+
import { fromPublicKeyHash, isFromPublicKey, isValid, toTypeInfo } from "@arcblock/did";
|
|
3
|
+
import { types } from "@ocap/mcrypto";
|
|
4
|
+
import { fromBase58, fromBase64, toBase58, toBase64 } from "@ocap/util";
|
|
5
|
+
import { fromPublicKey } from "@ocap/wallet";
|
|
6
|
+
import Debug from "debug";
|
|
7
|
+
import isAbsoluteUrl from "is-absolute-url";
|
|
8
|
+
import stringify from "json-stable-stringify";
|
|
9
|
+
import cloneDeep from "lodash/cloneDeep.js";
|
|
10
|
+
|
|
11
|
+
//#region src/index.ts
|
|
12
|
+
/**
|
|
13
|
+
* @fileOverview Utility functions to create/verify vc
|
|
14
|
+
*
|
|
15
|
+
* @module @arcblock/vc
|
|
16
|
+
* @requires @arcblock/did
|
|
17
|
+
* @requires @ocap/util
|
|
18
|
+
*/
|
|
19
|
+
const debug = Debug(name);
|
|
20
|
+
const proofTypes = {
|
|
21
|
+
[types.KeyType.ED25519]: "Ed25519Signature",
|
|
22
|
+
[types.KeyType.SECP256K1]: "Secp256k1Signature",
|
|
23
|
+
[types.KeyType.ETHEREUM]: "EthereumSignature"
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Create a valid verifiable credential
|
|
27
|
+
*
|
|
28
|
+
* @param params
|
|
29
|
+
* @param params.type - The type of credential
|
|
30
|
+
* @param params.subject - The content of credential
|
|
31
|
+
* @param params.issuer - The issuer name and wallet
|
|
32
|
+
* @param params.issuanceDate
|
|
33
|
+
* @param params.expirationDate
|
|
34
|
+
* @param params.endpoint - Status endpoint url
|
|
35
|
+
* @param params.endpointScope - Endpoint scope, either be public or private
|
|
36
|
+
* @returns Promise<object>
|
|
37
|
+
*/
|
|
38
|
+
async function create({ type, subject, issuer, issuanceDate, expirationDate, tag = "", endpoint = "", endpointScope = "public" }) {
|
|
39
|
+
if (!type) throw new Error("Can not create verifiable credential without empty type");
|
|
40
|
+
if (!subject) throw new Error("Can not create verifiable credential from empty subject");
|
|
41
|
+
if (!subject.id) throw new Error("Can not create verifiable credential without holder");
|
|
42
|
+
if (!isValid(subject.id)) throw new Error("Can not create verifiable credential invalid holder did");
|
|
43
|
+
if (endpoint && isAbsoluteUrl(endpoint) === false) throw new Error("VC Endpoint must be absolute url");
|
|
44
|
+
if (endpointScope && ["public", "private"].includes(endpointScope) === false) throw new Error("VC Endpoint scope must be either public or private");
|
|
45
|
+
const { wallet, name: issuerName } = issuer;
|
|
46
|
+
const issuerDid = wallet.address;
|
|
47
|
+
const typeInfo = toTypeInfo(issuerDid);
|
|
48
|
+
const vcType = {
|
|
49
|
+
...typeInfo,
|
|
50
|
+
role: types.RoleType.ROLE_VC
|
|
51
|
+
};
|
|
52
|
+
const vcDid = fromPublicKeyHash(wallet.hash(stringify(subject)), vcType);
|
|
53
|
+
const issuanceDateValue = issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
|
|
54
|
+
const vcObj = {
|
|
55
|
+
"@context": "https://schema.arcblock.io/v0.1/context.jsonld",
|
|
56
|
+
id: vcDid,
|
|
57
|
+
type,
|
|
58
|
+
issuer: {
|
|
59
|
+
id: issuerDid,
|
|
60
|
+
pk: toBase58(wallet.publicKey),
|
|
61
|
+
name: issuerName || issuerDid
|
|
62
|
+
},
|
|
63
|
+
issuanceDate: issuanceDateValue,
|
|
64
|
+
expirationDate,
|
|
65
|
+
credentialSubject: subject
|
|
66
|
+
};
|
|
67
|
+
if (tag) vcObj.tag = tag;
|
|
68
|
+
if (endpoint) vcObj.credentialStatus = {
|
|
69
|
+
id: endpoint,
|
|
70
|
+
type: "NFTStatusList2021",
|
|
71
|
+
scope: endpointScope || "public"
|
|
72
|
+
};
|
|
73
|
+
const pkType = typeInfo.pk;
|
|
74
|
+
if (!proofTypes[pkType]) throw new Error("Unsupported signer type when create verifiable credential");
|
|
75
|
+
const signature = await wallet.sign(stringify(vcObj));
|
|
76
|
+
const result = {
|
|
77
|
+
proof: {
|
|
78
|
+
type: proofTypes[pkType],
|
|
79
|
+
created: issuanceDateValue,
|
|
80
|
+
proofPurpose: "assertionMethod",
|
|
81
|
+
jws: toBase64(signature)
|
|
82
|
+
},
|
|
83
|
+
...vcObj
|
|
84
|
+
};
|
|
85
|
+
debug("create", result);
|
|
86
|
+
if (await verify({
|
|
87
|
+
vc: result,
|
|
88
|
+
ownerDid: subject.id,
|
|
89
|
+
trustedIssuers: [issuerDid]
|
|
90
|
+
})) return result;
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Verify that the verifiable credential is valid
|
|
95
|
+
* - It is signed by a whitelist of issuers
|
|
96
|
+
* - It is owned by the vc.subject.id
|
|
97
|
+
* - It has valid signature by the issuer
|
|
98
|
+
* - It is not expired
|
|
99
|
+
*
|
|
100
|
+
* @param vc - the verifiable credential object
|
|
101
|
+
* @param ownerDid - vc holder/owner did
|
|
102
|
+
* @param trustedIssuers - list of issuer did
|
|
103
|
+
* @throws Error
|
|
104
|
+
* @returns Promise<boolean>
|
|
105
|
+
*/
|
|
106
|
+
async function verify({ vc, ownerDid, trustedIssuers, ignoreExpired = false }) {
|
|
107
|
+
if (!vc) throw new Error("Empty verifiable credential object");
|
|
108
|
+
if (!vc.issuer || !vc.issuer.id || !vc.issuer.pk || !isValid(vc.issuer.id)) throw new Error("Invalid verifiable credential issuer");
|
|
109
|
+
if (!vc.credentialSubject || !vc.credentialSubject.id || !isValid(vc.credentialSubject.id)) throw new Error("Invalid verifiable credential subject");
|
|
110
|
+
if (!vc.proof || !vc.proof.jws) throw new Error("Invalid verifiable credential proof");
|
|
111
|
+
if (vc.issuanceDate === void 0) throw Error("Invalid verifiable credential issue date");
|
|
112
|
+
if (new Date(vc.issuanceDate).getTime() > Date.now()) throw Error("Verifiable credential has not take effect");
|
|
113
|
+
if (!ignoreExpired && vc.expirationDate !== void 0 && new Date(vc.expirationDate).getTime() < Date.now()) throw Error("Verifiable credential has expired");
|
|
114
|
+
const issuerDid = (Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers]).find((x) => x === vc.issuer.id);
|
|
115
|
+
if (!issuerDid) throw new Error("Verifiable credential not issued by trusted issuers");
|
|
116
|
+
if (!isFromPublicKey(issuerDid, vc.issuer.pk)) throw new Error("Verifiable credential not issuer pk not match with issuer did");
|
|
117
|
+
if (ownerDid !== vc.credentialSubject.id) throw new Error("Verifiable credential not owned by specified owner did");
|
|
118
|
+
const issuerWallet = fromPublicKey(vc.issuer.pk, toTypeInfo(issuerDid));
|
|
119
|
+
const clone = cloneDeep(vc);
|
|
120
|
+
const signatureStr = clone.proof.jws;
|
|
121
|
+
delete clone.proof;
|
|
122
|
+
delete clone.signature;
|
|
123
|
+
if (await issuerWallet.verify(stringify(clone), fromBase64(signatureStr)) !== true) throw Error("Verifiable credential signature not valid");
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Verify that the Presentation is valid
|
|
128
|
+
* - It is signed by VC's owner
|
|
129
|
+
* - It contain challenge
|
|
130
|
+
* - It has valid signature by the issuer
|
|
131
|
+
* - It is not expired
|
|
132
|
+
*
|
|
133
|
+
* @param presentation - the presentation object
|
|
134
|
+
* @param trustedIssuers - list of issuer did
|
|
135
|
+
* @param challenge - Random byte you want
|
|
136
|
+
* @throws Error
|
|
137
|
+
* @returns Promise<boolean>
|
|
138
|
+
*/
|
|
139
|
+
async function verifyPresentation({ presentation, trustedIssuers, challenge, ignoreExpired = false }) {
|
|
140
|
+
if (!presentation.challenge || challenge !== presentation.challenge) throw Error("Invalid challenge included on vc presentation");
|
|
141
|
+
const vcList = Array.isArray(presentation.verifiableCredential) ? presentation.verifiableCredential : [presentation.verifiableCredential];
|
|
142
|
+
const proofList = Array.isArray(presentation.proof) ? presentation.proof : [presentation.proof];
|
|
143
|
+
const clone = cloneDeep(presentation);
|
|
144
|
+
delete clone.proof;
|
|
145
|
+
await Promise.all(vcList.map(async (vcStr) => {
|
|
146
|
+
const vcObj = JSON.parse(vcStr);
|
|
147
|
+
const proof = proofList.find((x) => isFromPublicKey(vcObj.credentialSubject.id, x.pk));
|
|
148
|
+
if (!proof) throw Error(`VC does not have corresponding proof: ${vcStr}`);
|
|
149
|
+
const signatureStr = proof.jws;
|
|
150
|
+
if (await fromPublicKey(fromBase58(proof.pk), toTypeInfo(vcObj.credentialSubject.id)).verify(stringify(clone), fromBase64(signatureStr)) !== true) throw Error("Presentation signature invalid");
|
|
151
|
+
await verify({
|
|
152
|
+
vc: vcObj,
|
|
153
|
+
ownerDid: vcObj.credentialSubject.id,
|
|
154
|
+
trustedIssuers,
|
|
155
|
+
ignoreExpired
|
|
156
|
+
});
|
|
157
|
+
}));
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
161
|
+
if (!claims || !Array.isArray(claims)) throw new Error("Can not create credential list with empty claim list");
|
|
162
|
+
if (!issuer || !issuer.wallet || !issuer.name) throw new Error("Can not create credential list with empty issuer name or wallet");
|
|
163
|
+
if (typeof issuer.wallet.sign !== "function") throw new Error("Can not create credential list with invalid issuer wallet");
|
|
164
|
+
const { wallet, name: name$1 } = issuer;
|
|
165
|
+
const issuerDid = wallet.address;
|
|
166
|
+
const typeInfo = toTypeInfo(issuerDid);
|
|
167
|
+
const vcType = {
|
|
168
|
+
...typeInfo,
|
|
169
|
+
role: types.RoleType.ROLE_VC
|
|
170
|
+
};
|
|
171
|
+
const issued = issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
|
|
172
|
+
const pkType = typeInfo.pk;
|
|
173
|
+
return await Promise.all(claims.map(async (x) => {
|
|
174
|
+
const vc = { claim: x };
|
|
175
|
+
vc.id = fromPublicKeyHash(wallet.hash(stringify(vc.claim)), vcType);
|
|
176
|
+
vc.issued = issued;
|
|
177
|
+
vc.issuer = {
|
|
178
|
+
id: issuerDid,
|
|
179
|
+
pk: toBase58(wallet.publicKey),
|
|
180
|
+
name: name$1 || issuerDid
|
|
181
|
+
};
|
|
182
|
+
const signature = await wallet.sign(stringify(vc));
|
|
183
|
+
vc.proof = {
|
|
184
|
+
type: proofTypes[pkType],
|
|
185
|
+
created: issued,
|
|
186
|
+
proofPurpose: "assertionMethod",
|
|
187
|
+
jws: toBase64(signature)
|
|
188
|
+
};
|
|
189
|
+
return vc;
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
async function verifyCredentialList({ credentials, trustedIssuers }) {
|
|
193
|
+
if (!credentials || !Array.isArray(credentials)) throw new Error("Can not verify with empty credentials list");
|
|
194
|
+
return Promise.all(credentials.map(async (x) => {
|
|
195
|
+
const issuerDid = (Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers]).find((d) => d === x.issuer.id);
|
|
196
|
+
if (!issuerDid) throw new Error("Credential not issued by trusted issuers");
|
|
197
|
+
if (!isFromPublicKey(issuerDid, x.issuer.pk)) throw new Error("Credential not issuer pk not match with issuer did");
|
|
198
|
+
const issuerWallet = fromPublicKey(x.issuer.pk, toTypeInfo(issuerDid));
|
|
199
|
+
const clone = cloneDeep(x);
|
|
200
|
+
const signatureStr = clone.proof.jws;
|
|
201
|
+
delete clone.proof;
|
|
202
|
+
if (await issuerWallet.verify(stringify(clone), fromBase64(signatureStr)) !== true) throw Error("Status credential signature not valid");
|
|
203
|
+
return x.claim;
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
const stableStringify = stringify;
|
|
207
|
+
|
|
208
|
+
//#endregion
|
|
209
|
+
export { create, createCredentialList, proofTypes, stableStringify, verify, verifyCredentialList, verifyPresentation };
|
package/esm/package.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
|
|
29
|
+
exports.__toESM = __toESM;
|
package/lib/index.cjs
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
|
+
const require_package = require('./package.cjs');
|
|
3
|
+
let _arcblock_did = require("@arcblock/did");
|
|
4
|
+
let _ocap_mcrypto = require("@ocap/mcrypto");
|
|
5
|
+
let _ocap_util = require("@ocap/util");
|
|
6
|
+
let _ocap_wallet = require("@ocap/wallet");
|
|
7
|
+
let debug = require("debug");
|
|
8
|
+
debug = require_rolldown_runtime.__toESM(debug);
|
|
9
|
+
let is_absolute_url = require("is-absolute-url");
|
|
10
|
+
is_absolute_url = require_rolldown_runtime.__toESM(is_absolute_url);
|
|
11
|
+
let json_stable_stringify = require("json-stable-stringify");
|
|
12
|
+
json_stable_stringify = require_rolldown_runtime.__toESM(json_stable_stringify);
|
|
13
|
+
let lodash_cloneDeep = require("lodash/cloneDeep");
|
|
14
|
+
lodash_cloneDeep = require_rolldown_runtime.__toESM(lodash_cloneDeep);
|
|
15
|
+
|
|
16
|
+
//#region src/index.ts
|
|
17
|
+
/**
|
|
18
|
+
* @fileOverview Utility functions to create/verify vc
|
|
19
|
+
*
|
|
20
|
+
* @module @arcblock/vc
|
|
21
|
+
* @requires @arcblock/did
|
|
22
|
+
* @requires @ocap/util
|
|
23
|
+
*/
|
|
24
|
+
const debug$1 = (0, debug.default)(require_package.name);
|
|
25
|
+
const proofTypes = {
|
|
26
|
+
[_ocap_mcrypto.types.KeyType.ED25519]: "Ed25519Signature",
|
|
27
|
+
[_ocap_mcrypto.types.KeyType.SECP256K1]: "Secp256k1Signature",
|
|
28
|
+
[_ocap_mcrypto.types.KeyType.ETHEREUM]: "EthereumSignature"
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Create a valid verifiable credential
|
|
32
|
+
*
|
|
33
|
+
* @param params
|
|
34
|
+
* @param params.type - The type of credential
|
|
35
|
+
* @param params.subject - The content of credential
|
|
36
|
+
* @param params.issuer - The issuer name and wallet
|
|
37
|
+
* @param params.issuanceDate
|
|
38
|
+
* @param params.expirationDate
|
|
39
|
+
* @param params.endpoint - Status endpoint url
|
|
40
|
+
* @param params.endpointScope - Endpoint scope, either be public or private
|
|
41
|
+
* @returns Promise<object>
|
|
42
|
+
*/
|
|
43
|
+
async function create({ type, subject, issuer, issuanceDate, expirationDate, tag = "", endpoint = "", endpointScope = "public" }) {
|
|
44
|
+
if (!type) throw new Error("Can not create verifiable credential without empty type");
|
|
45
|
+
if (!subject) throw new Error("Can not create verifiable credential from empty subject");
|
|
46
|
+
if (!subject.id) throw new Error("Can not create verifiable credential without holder");
|
|
47
|
+
if (!(0, _arcblock_did.isValid)(subject.id)) throw new Error("Can not create verifiable credential invalid holder did");
|
|
48
|
+
if (endpoint && (0, is_absolute_url.default)(endpoint) === false) throw new Error("VC Endpoint must be absolute url");
|
|
49
|
+
if (endpointScope && ["public", "private"].includes(endpointScope) === false) throw new Error("VC Endpoint scope must be either public or private");
|
|
50
|
+
const { wallet, name: issuerName } = issuer;
|
|
51
|
+
const issuerDid = wallet.address;
|
|
52
|
+
const typeInfo = (0, _arcblock_did.toTypeInfo)(issuerDid);
|
|
53
|
+
const vcType = {
|
|
54
|
+
...typeInfo,
|
|
55
|
+
role: _ocap_mcrypto.types.RoleType.ROLE_VC
|
|
56
|
+
};
|
|
57
|
+
const vcDid = (0, _arcblock_did.fromPublicKeyHash)(wallet.hash((0, json_stable_stringify.default)(subject)), vcType);
|
|
58
|
+
const issuanceDateValue = issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
|
|
59
|
+
const vcObj = {
|
|
60
|
+
"@context": "https://schema.arcblock.io/v0.1/context.jsonld",
|
|
61
|
+
id: vcDid,
|
|
62
|
+
type,
|
|
63
|
+
issuer: {
|
|
64
|
+
id: issuerDid,
|
|
65
|
+
pk: (0, _ocap_util.toBase58)(wallet.publicKey),
|
|
66
|
+
name: issuerName || issuerDid
|
|
67
|
+
},
|
|
68
|
+
issuanceDate: issuanceDateValue,
|
|
69
|
+
expirationDate,
|
|
70
|
+
credentialSubject: subject
|
|
71
|
+
};
|
|
72
|
+
if (tag) vcObj.tag = tag;
|
|
73
|
+
if (endpoint) vcObj.credentialStatus = {
|
|
74
|
+
id: endpoint,
|
|
75
|
+
type: "NFTStatusList2021",
|
|
76
|
+
scope: endpointScope || "public"
|
|
77
|
+
};
|
|
78
|
+
const pkType = typeInfo.pk;
|
|
79
|
+
if (!proofTypes[pkType]) throw new Error("Unsupported signer type when create verifiable credential");
|
|
80
|
+
const signature = await wallet.sign((0, json_stable_stringify.default)(vcObj));
|
|
81
|
+
const result = {
|
|
82
|
+
proof: {
|
|
83
|
+
type: proofTypes[pkType],
|
|
84
|
+
created: issuanceDateValue,
|
|
85
|
+
proofPurpose: "assertionMethod",
|
|
86
|
+
jws: (0, _ocap_util.toBase64)(signature)
|
|
87
|
+
},
|
|
88
|
+
...vcObj
|
|
89
|
+
};
|
|
90
|
+
debug$1("create", result);
|
|
91
|
+
if (await verify({
|
|
92
|
+
vc: result,
|
|
93
|
+
ownerDid: subject.id,
|
|
94
|
+
trustedIssuers: [issuerDid]
|
|
95
|
+
})) return result;
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Verify that the verifiable credential is valid
|
|
100
|
+
* - It is signed by a whitelist of issuers
|
|
101
|
+
* - It is owned by the vc.subject.id
|
|
102
|
+
* - It has valid signature by the issuer
|
|
103
|
+
* - It is not expired
|
|
104
|
+
*
|
|
105
|
+
* @param vc - the verifiable credential object
|
|
106
|
+
* @param ownerDid - vc holder/owner did
|
|
107
|
+
* @param trustedIssuers - list of issuer did
|
|
108
|
+
* @throws Error
|
|
109
|
+
* @returns Promise<boolean>
|
|
110
|
+
*/
|
|
111
|
+
async function verify({ vc, ownerDid, trustedIssuers, ignoreExpired = false }) {
|
|
112
|
+
if (!vc) throw new Error("Empty verifiable credential object");
|
|
113
|
+
if (!vc.issuer || !vc.issuer.id || !vc.issuer.pk || !(0, _arcblock_did.isValid)(vc.issuer.id)) throw new Error("Invalid verifiable credential issuer");
|
|
114
|
+
if (!vc.credentialSubject || !vc.credentialSubject.id || !(0, _arcblock_did.isValid)(vc.credentialSubject.id)) throw new Error("Invalid verifiable credential subject");
|
|
115
|
+
if (!vc.proof || !vc.proof.jws) throw new Error("Invalid verifiable credential proof");
|
|
116
|
+
if (vc.issuanceDate === void 0) throw Error("Invalid verifiable credential issue date");
|
|
117
|
+
if (new Date(vc.issuanceDate).getTime() > Date.now()) throw Error("Verifiable credential has not take effect");
|
|
118
|
+
if (!ignoreExpired && vc.expirationDate !== void 0 && new Date(vc.expirationDate).getTime() < Date.now()) throw Error("Verifiable credential has expired");
|
|
119
|
+
const issuerDid = (Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers]).find((x) => x === vc.issuer.id);
|
|
120
|
+
if (!issuerDid) throw new Error("Verifiable credential not issued by trusted issuers");
|
|
121
|
+
if (!(0, _arcblock_did.isFromPublicKey)(issuerDid, vc.issuer.pk)) throw new Error("Verifiable credential not issuer pk not match with issuer did");
|
|
122
|
+
if (ownerDid !== vc.credentialSubject.id) throw new Error("Verifiable credential not owned by specified owner did");
|
|
123
|
+
const issuerWallet = (0, _ocap_wallet.fromPublicKey)(vc.issuer.pk, (0, _arcblock_did.toTypeInfo)(issuerDid));
|
|
124
|
+
const clone = (0, lodash_cloneDeep.default)(vc);
|
|
125
|
+
const signatureStr = clone.proof.jws;
|
|
126
|
+
delete clone.proof;
|
|
127
|
+
delete clone.signature;
|
|
128
|
+
if (await issuerWallet.verify((0, json_stable_stringify.default)(clone), (0, _ocap_util.fromBase64)(signatureStr)) !== true) throw Error("Verifiable credential signature not valid");
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Verify that the Presentation is valid
|
|
133
|
+
* - It is signed by VC's owner
|
|
134
|
+
* - It contain challenge
|
|
135
|
+
* - It has valid signature by the issuer
|
|
136
|
+
* - It is not expired
|
|
137
|
+
*
|
|
138
|
+
* @param presentation - the presentation object
|
|
139
|
+
* @param trustedIssuers - list of issuer did
|
|
140
|
+
* @param challenge - Random byte you want
|
|
141
|
+
* @throws Error
|
|
142
|
+
* @returns Promise<boolean>
|
|
143
|
+
*/
|
|
144
|
+
async function verifyPresentation({ presentation, trustedIssuers, challenge, ignoreExpired = false }) {
|
|
145
|
+
if (!presentation.challenge || challenge !== presentation.challenge) throw Error("Invalid challenge included on vc presentation");
|
|
146
|
+
const vcList = Array.isArray(presentation.verifiableCredential) ? presentation.verifiableCredential : [presentation.verifiableCredential];
|
|
147
|
+
const proofList = Array.isArray(presentation.proof) ? presentation.proof : [presentation.proof];
|
|
148
|
+
const clone = (0, lodash_cloneDeep.default)(presentation);
|
|
149
|
+
delete clone.proof;
|
|
150
|
+
await Promise.all(vcList.map(async (vcStr) => {
|
|
151
|
+
const vcObj = JSON.parse(vcStr);
|
|
152
|
+
const proof = proofList.find((x) => (0, _arcblock_did.isFromPublicKey)(vcObj.credentialSubject.id, x.pk));
|
|
153
|
+
if (!proof) throw Error(`VC does not have corresponding proof: ${vcStr}`);
|
|
154
|
+
const signatureStr = proof.jws;
|
|
155
|
+
if (await (0, _ocap_wallet.fromPublicKey)((0, _ocap_util.fromBase58)(proof.pk), (0, _arcblock_did.toTypeInfo)(vcObj.credentialSubject.id)).verify((0, json_stable_stringify.default)(clone), (0, _ocap_util.fromBase64)(signatureStr)) !== true) throw Error("Presentation signature invalid");
|
|
156
|
+
await verify({
|
|
157
|
+
vc: vcObj,
|
|
158
|
+
ownerDid: vcObj.credentialSubject.id,
|
|
159
|
+
trustedIssuers,
|
|
160
|
+
ignoreExpired
|
|
161
|
+
});
|
|
162
|
+
}));
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
166
|
+
if (!claims || !Array.isArray(claims)) throw new Error("Can not create credential list with empty claim list");
|
|
167
|
+
if (!issuer || !issuer.wallet || !issuer.name) throw new Error("Can not create credential list with empty issuer name or wallet");
|
|
168
|
+
if (typeof issuer.wallet.sign !== "function") throw new Error("Can not create credential list with invalid issuer wallet");
|
|
169
|
+
const { wallet, name: name$1 } = issuer;
|
|
170
|
+
const issuerDid = wallet.address;
|
|
171
|
+
const typeInfo = (0, _arcblock_did.toTypeInfo)(issuerDid);
|
|
172
|
+
const vcType = {
|
|
173
|
+
...typeInfo,
|
|
174
|
+
role: _ocap_mcrypto.types.RoleType.ROLE_VC
|
|
175
|
+
};
|
|
176
|
+
const issued = issuanceDate || (/* @__PURE__ */ new Date()).toISOString();
|
|
177
|
+
const pkType = typeInfo.pk;
|
|
178
|
+
return await Promise.all(claims.map(async (x) => {
|
|
179
|
+
const vc = { claim: x };
|
|
180
|
+
vc.id = (0, _arcblock_did.fromPublicKeyHash)(wallet.hash((0, json_stable_stringify.default)(vc.claim)), vcType);
|
|
181
|
+
vc.issued = issued;
|
|
182
|
+
vc.issuer = {
|
|
183
|
+
id: issuerDid,
|
|
184
|
+
pk: (0, _ocap_util.toBase58)(wallet.publicKey),
|
|
185
|
+
name: name$1 || issuerDid
|
|
186
|
+
};
|
|
187
|
+
const signature = await wallet.sign((0, json_stable_stringify.default)(vc));
|
|
188
|
+
vc.proof = {
|
|
189
|
+
type: proofTypes[pkType],
|
|
190
|
+
created: issued,
|
|
191
|
+
proofPurpose: "assertionMethod",
|
|
192
|
+
jws: (0, _ocap_util.toBase64)(signature)
|
|
193
|
+
};
|
|
194
|
+
return vc;
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
async function verifyCredentialList({ credentials, trustedIssuers }) {
|
|
198
|
+
if (!credentials || !Array.isArray(credentials)) throw new Error("Can not verify with empty credentials list");
|
|
199
|
+
return Promise.all(credentials.map(async (x) => {
|
|
200
|
+
const issuerDid = (Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers]).find((d) => d === x.issuer.id);
|
|
201
|
+
if (!issuerDid) throw new Error("Credential not issued by trusted issuers");
|
|
202
|
+
if (!(0, _arcblock_did.isFromPublicKey)(issuerDid, x.issuer.pk)) throw new Error("Credential not issuer pk not match with issuer did");
|
|
203
|
+
const issuerWallet = (0, _ocap_wallet.fromPublicKey)(x.issuer.pk, (0, _arcblock_did.toTypeInfo)(issuerDid));
|
|
204
|
+
const clone = (0, lodash_cloneDeep.default)(x);
|
|
205
|
+
const signatureStr = clone.proof.jws;
|
|
206
|
+
delete clone.proof;
|
|
207
|
+
if (await issuerWallet.verify((0, json_stable_stringify.default)(clone), (0, _ocap_util.fromBase64)(signatureStr)) !== true) throw Error("Status credential signature not valid");
|
|
208
|
+
return x.claim;
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
const stableStringify = json_stable_stringify.default;
|
|
212
|
+
|
|
213
|
+
//#endregion
|
|
214
|
+
exports.create = create;
|
|
215
|
+
exports.createCredentialList = createCredentialList;
|
|
216
|
+
exports.proofTypes = proofTypes;
|
|
217
|
+
exports.stableStringify = stableStringify;
|
|
218
|
+
exports.verify = verify;
|
|
219
|
+
exports.verifyCredentialList = verifyCredentialList;
|
|
220
|
+
exports.verifyPresentation = verifyPresentation;
|
package/lib/index.d.cts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { WalletObject } from "@ocap/wallet";
|
|
2
|
+
import stringify from "json-stable-stringify";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
interface Proof {
|
|
6
|
+
type: string;
|
|
7
|
+
created: string;
|
|
8
|
+
proofPurpose: string;
|
|
9
|
+
jws: string;
|
|
10
|
+
pk?: string;
|
|
11
|
+
}
|
|
12
|
+
interface Issuer {
|
|
13
|
+
id: string;
|
|
14
|
+
pk: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
interface CredentialSubject {
|
|
18
|
+
id: string;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
interface CredentialStatus {
|
|
22
|
+
id: string;
|
|
23
|
+
type: string;
|
|
24
|
+
scope: string;
|
|
25
|
+
}
|
|
26
|
+
interface VerifiableCredential {
|
|
27
|
+
'@context': string;
|
|
28
|
+
id: string;
|
|
29
|
+
type: string;
|
|
30
|
+
issuer: Issuer;
|
|
31
|
+
issuanceDate: string;
|
|
32
|
+
expirationDate?: string;
|
|
33
|
+
credentialSubject: CredentialSubject;
|
|
34
|
+
proof: Proof;
|
|
35
|
+
tag?: string;
|
|
36
|
+
credentialStatus?: CredentialStatus;
|
|
37
|
+
signature?: unknown;
|
|
38
|
+
}
|
|
39
|
+
interface IssuerInfo {
|
|
40
|
+
wallet: WalletObject;
|
|
41
|
+
name?: string;
|
|
42
|
+
}
|
|
43
|
+
interface Presentation {
|
|
44
|
+
challenge: string;
|
|
45
|
+
verifiableCredential: string | string[];
|
|
46
|
+
proof: Proof | Proof[];
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
interface Credential {
|
|
50
|
+
id: string;
|
|
51
|
+
issued: string;
|
|
52
|
+
issuer: Issuer;
|
|
53
|
+
proof: Proof;
|
|
54
|
+
claim: unknown;
|
|
55
|
+
}
|
|
56
|
+
declare const proofTypes: Record<number, string>;
|
|
57
|
+
/**
|
|
58
|
+
* Create a valid verifiable credential
|
|
59
|
+
*
|
|
60
|
+
* @param params
|
|
61
|
+
* @param params.type - The type of credential
|
|
62
|
+
* @param params.subject - The content of credential
|
|
63
|
+
* @param params.issuer - The issuer name and wallet
|
|
64
|
+
* @param params.issuanceDate
|
|
65
|
+
* @param params.expirationDate
|
|
66
|
+
* @param params.endpoint - Status endpoint url
|
|
67
|
+
* @param params.endpointScope - Endpoint scope, either be public or private
|
|
68
|
+
* @returns Promise<object>
|
|
69
|
+
*/
|
|
70
|
+
declare function create({
|
|
71
|
+
type,
|
|
72
|
+
subject,
|
|
73
|
+
issuer,
|
|
74
|
+
issuanceDate,
|
|
75
|
+
expirationDate,
|
|
76
|
+
tag,
|
|
77
|
+
endpoint,
|
|
78
|
+
endpointScope
|
|
79
|
+
}: {
|
|
80
|
+
type: string;
|
|
81
|
+
subject: CredentialSubject;
|
|
82
|
+
issuer: IssuerInfo;
|
|
83
|
+
issuanceDate?: string;
|
|
84
|
+
expirationDate?: string;
|
|
85
|
+
tag?: string;
|
|
86
|
+
endpoint?: string;
|
|
87
|
+
endpointScope?: string;
|
|
88
|
+
}): Promise<VerifiableCredential | null>;
|
|
89
|
+
/**
|
|
90
|
+
* Verify that the verifiable credential is valid
|
|
91
|
+
* - It is signed by a whitelist of issuers
|
|
92
|
+
* - It is owned by the vc.subject.id
|
|
93
|
+
* - It has valid signature by the issuer
|
|
94
|
+
* - It is not expired
|
|
95
|
+
*
|
|
96
|
+
* @param vc - the verifiable credential object
|
|
97
|
+
* @param ownerDid - vc holder/owner did
|
|
98
|
+
* @param trustedIssuers - list of issuer did
|
|
99
|
+
* @throws Error
|
|
100
|
+
* @returns Promise<boolean>
|
|
101
|
+
*/
|
|
102
|
+
declare function verify({
|
|
103
|
+
vc,
|
|
104
|
+
ownerDid,
|
|
105
|
+
trustedIssuers,
|
|
106
|
+
ignoreExpired
|
|
107
|
+
}: {
|
|
108
|
+
vc: VerifiableCredential | null;
|
|
109
|
+
ownerDid: string;
|
|
110
|
+
trustedIssuers: string | string[];
|
|
111
|
+
ignoreExpired?: boolean;
|
|
112
|
+
}): Promise<boolean>;
|
|
113
|
+
/**
|
|
114
|
+
* Verify that the Presentation is valid
|
|
115
|
+
* - It is signed by VC's owner
|
|
116
|
+
* - It contain challenge
|
|
117
|
+
* - It has valid signature by the issuer
|
|
118
|
+
* - It is not expired
|
|
119
|
+
*
|
|
120
|
+
* @param presentation - the presentation object
|
|
121
|
+
* @param trustedIssuers - list of issuer did
|
|
122
|
+
* @param challenge - Random byte you want
|
|
123
|
+
* @throws Error
|
|
124
|
+
* @returns Promise<boolean>
|
|
125
|
+
*/
|
|
126
|
+
declare function verifyPresentation({
|
|
127
|
+
presentation,
|
|
128
|
+
trustedIssuers,
|
|
129
|
+
challenge,
|
|
130
|
+
ignoreExpired
|
|
131
|
+
}: {
|
|
132
|
+
presentation: Presentation;
|
|
133
|
+
trustedIssuers: string[];
|
|
134
|
+
challenge: string;
|
|
135
|
+
ignoreExpired?: boolean;
|
|
136
|
+
}): Promise<boolean>;
|
|
137
|
+
declare function createCredentialList({
|
|
138
|
+
claims,
|
|
139
|
+
issuer,
|
|
140
|
+
issuanceDate
|
|
141
|
+
}: {
|
|
142
|
+
claims: unknown[];
|
|
143
|
+
issuer: IssuerInfo;
|
|
144
|
+
issuanceDate?: string;
|
|
145
|
+
}): Promise<Credential[]>;
|
|
146
|
+
declare function verifyCredentialList({
|
|
147
|
+
credentials,
|
|
148
|
+
trustedIssuers
|
|
149
|
+
}: {
|
|
150
|
+
credentials: Credential[];
|
|
151
|
+
trustedIssuers: string | string[];
|
|
152
|
+
}): Promise<unknown[]>;
|
|
153
|
+
declare const stableStringify: typeof stringify;
|
|
154
|
+
//#endregion
|
|
155
|
+
export { create, createCredentialList, proofTypes, stableStringify, verify, verifyCredentialList, verifyPresentation };
|
package/lib/package.cjs
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcblock/vc",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.29.0",
|
|
4
|
+
"description": "TypeScript lib to work with ArcBlock Verifiable Credentials",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arcblock",
|
|
7
7
|
"blockchain",
|
|
@@ -19,24 +19,50 @@
|
|
|
19
19
|
"contributors": [
|
|
20
20
|
"wangshijun <shijun@arcblock.io> (https://github.com/wangshijun)"
|
|
21
21
|
],
|
|
22
|
-
"homepage": "https://github.com/ArcBlock/blockchain/tree/master/
|
|
22
|
+
"homepage": "https://github.com/ArcBlock/blockchain/tree/master/asset/vc",
|
|
23
23
|
"license": "Apache-2.0",
|
|
24
|
-
"
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./lib/index.cjs",
|
|
26
|
+
"module": "./esm/index.mjs",
|
|
27
|
+
"types": "./esm/index.d.mts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./esm/index.d.mts",
|
|
31
|
+
"import": "./esm/index.mjs",
|
|
32
|
+
"default": "./lib/index.cjs"
|
|
33
|
+
},
|
|
34
|
+
"./lib/*.js": {
|
|
35
|
+
"types": "./esm/*.d.mts",
|
|
36
|
+
"import": "./esm/*.mjs",
|
|
37
|
+
"default": "./lib/*.cjs"
|
|
38
|
+
},
|
|
39
|
+
"./lib/*": {
|
|
40
|
+
"types": "./esm/*.d.mts",
|
|
41
|
+
"import": "./esm/*.mjs",
|
|
42
|
+
"default": "./lib/*.cjs"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
25
45
|
"files": [
|
|
26
|
-
"lib"
|
|
46
|
+
"lib",
|
|
47
|
+
"esm"
|
|
27
48
|
],
|
|
28
|
-
"devDependencies": {
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/debug": "^4.1.12",
|
|
51
|
+
"@types/json-stable-stringify": "^1.1.0",
|
|
52
|
+
"@types/lodash": "^4.17.0"
|
|
53
|
+
},
|
|
29
54
|
"repository": {
|
|
30
55
|
"type": "git",
|
|
31
56
|
"url": "git+https://github.com/ArcBlock/blockchain.git"
|
|
32
57
|
},
|
|
33
58
|
"scripts": {
|
|
59
|
+
"build": "tsdown",
|
|
60
|
+
"prebuild": "rm -rf lib esm",
|
|
34
61
|
"lint": "biome check",
|
|
35
62
|
"lint:fix": "biome check --write",
|
|
36
|
-
"docs": "bun run gen-
|
|
63
|
+
"docs": "bun run gen-docs && bun run cleanup-docs && bun run format-docs",
|
|
37
64
|
"cleanup-docs": "node ../../scripts/cleanup-docs.js docs/README.md $npm_package_name",
|
|
38
|
-
"gen-docs": "jsdoc2md
|
|
39
|
-
"gen-dts": "j2d lib/index.js",
|
|
65
|
+
"gen-docs": "jsdoc2md src/index.ts > docs/README.md",
|
|
40
66
|
"format-docs": "remark . -o",
|
|
41
67
|
"test": "bun test",
|
|
42
68
|
"coverage": "npm run test -- --coverage"
|
|
@@ -45,13 +71,13 @@
|
|
|
45
71
|
"url": "https://github.com/ArcBlock/blockchain/issues"
|
|
46
72
|
},
|
|
47
73
|
"dependencies": {
|
|
48
|
-
"@arcblock/did": "1.
|
|
49
|
-
"@ocap/mcrypto": "1.
|
|
50
|
-
"@ocap/util": "1.
|
|
51
|
-
"@ocap/wallet": "1.
|
|
52
|
-
"debug": "^4.3
|
|
74
|
+
"@arcblock/did": "1.29.0",
|
|
75
|
+
"@ocap/mcrypto": "1.29.0",
|
|
76
|
+
"@ocap/util": "1.29.0",
|
|
77
|
+
"@ocap/wallet": "1.29.0",
|
|
78
|
+
"debug": "^4.4.3",
|
|
53
79
|
"is-absolute-url": "^3.0.3",
|
|
54
80
|
"json-stable-stringify": "^1.0.1",
|
|
55
|
-
"lodash": "^4.17.
|
|
81
|
+
"lodash": "^4.17.23"
|
|
56
82
|
}
|
|
57
83
|
}
|
package/lib/index.d.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
// Generate by [js2dts@0.3.3](https://github.com/whxaxes/js2dts#readme)
|
|
2
|
-
|
|
3
|
-
import * as jsonStableStringify from 'json-stable-stringify';
|
|
4
|
-
/**
|
|
5
|
-
* Create a valid verifiable credential
|
|
6
|
-
*
|
|
7
|
-
* @param {object} params
|
|
8
|
-
* @param {string} params.type - The type of credential
|
|
9
|
-
* @param {object} params.subject - The content of credential
|
|
10
|
-
* @param {object} params.issuer - The issuer name and wallet
|
|
11
|
-
* @param {Date} params.issuanceDate
|
|
12
|
-
* @param {Date} params.expirationDate
|
|
13
|
-
* @param {String} params.endpoint - Status endpoint url
|
|
14
|
-
* @param {String} params.endpointScope - Endpoint scope, either be public or private
|
|
15
|
-
* @returns {Promise<object>}
|
|
16
|
-
*/
|
|
17
|
-
declare function create(T100: _Lib.T101): Promise<any>;
|
|
18
|
-
/**
|
|
19
|
-
* Verify that the verifiable credential is valid
|
|
20
|
-
* - It is signed by a whitelist of issuers
|
|
21
|
-
* - It is owned by the vc.subject.id
|
|
22
|
-
* - It has valid signature by the issuer
|
|
23
|
-
* - It is not expired
|
|
24
|
-
*
|
|
25
|
-
* @param {object} vc - the verifiable credential object
|
|
26
|
-
* @param {string} ownerDid - vc holder/owner did
|
|
27
|
-
* @param {Array} trustedIssuers - list of issuer did
|
|
28
|
-
* @throws {Error}
|
|
29
|
-
* @returns {Promise<boolean>}
|
|
30
|
-
*/
|
|
31
|
-
declare function verify(T102: any): Promise<boolean>;
|
|
32
|
-
/**
|
|
33
|
-
* Verify that the Presentation is valid
|
|
34
|
-
* - It is signed by VC's owner
|
|
35
|
-
* - It contain challenge
|
|
36
|
-
* - It has valid signature by the issuer
|
|
37
|
-
* - It is not expired
|
|
38
|
-
*
|
|
39
|
-
* @param {object} presentation - the presentation object
|
|
40
|
-
* @param {Array} trustedIssuers - list of issuer did
|
|
41
|
-
* @param {String} challenge - Random byte you want
|
|
42
|
-
* @throws {Error}
|
|
43
|
-
* @returns {Promise<boolean>}
|
|
44
|
-
*/
|
|
45
|
-
declare function verifyPresentation(T103: any): Promise<boolean>;
|
|
46
|
-
declare function createCredentialList(T105: _Lib.T106): Promise<_Lib.T107[]>;
|
|
47
|
-
declare function verifyCredentialList(T108: _Lib.T109): Promise<any[]>;
|
|
48
|
-
declare const _Lib: _Lib.T110;
|
|
49
|
-
declare namespace _Lib {
|
|
50
|
-
export interface T101 {
|
|
51
|
-
type: string;
|
|
52
|
-
subject: any;
|
|
53
|
-
issuer: any;
|
|
54
|
-
issuanceDate: Date;
|
|
55
|
-
expirationDate: Date;
|
|
56
|
-
endpoint: string;
|
|
57
|
-
endpointScope: string;
|
|
58
|
-
}
|
|
59
|
-
export interface T104 {
|
|
60
|
-
[key: string]: any;
|
|
61
|
-
}
|
|
62
|
-
export interface T106 {
|
|
63
|
-
claims: any;
|
|
64
|
-
issuer: any;
|
|
65
|
-
issuanceDate: any;
|
|
66
|
-
}
|
|
67
|
-
export interface T107 {
|
|
68
|
-
claim: any;
|
|
69
|
-
}
|
|
70
|
-
export interface T109 {
|
|
71
|
-
credentials: any;
|
|
72
|
-
trustedIssuers: any;
|
|
73
|
-
}
|
|
74
|
-
export interface T110 {
|
|
75
|
-
create: typeof create;
|
|
76
|
-
verify: typeof verify;
|
|
77
|
-
verifyPresentation: typeof verifyPresentation;
|
|
78
|
-
stableStringify: typeof jsonStableStringify.default;
|
|
79
|
-
proofTypes: _Lib.T104;
|
|
80
|
-
createCredentialList: typeof createCredentialList;
|
|
81
|
-
verifyCredentialList: typeof verifyCredentialList;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
export = _Lib;
|
package/lib/index.js
DELETED
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileOverview Utility functions to create/verify vc
|
|
3
|
-
*
|
|
4
|
-
* @module @arcblock/vc
|
|
5
|
-
* @requires @arcblock/did
|
|
6
|
-
* @requires @ocap/util
|
|
7
|
-
*/
|
|
8
|
-
const isAbsoluteUrl = require('is-absolute-url');
|
|
9
|
-
const stringify = require('json-stable-stringify');
|
|
10
|
-
const cloneDeep = require('lodash/cloneDeep');
|
|
11
|
-
const { types } = require('@ocap/mcrypto');
|
|
12
|
-
const { fromPublicKey } = require('@ocap/wallet');
|
|
13
|
-
const { toTypeInfo, isValid, isFromPublicKey, fromPublicKeyHash } = require('@arcblock/did');
|
|
14
|
-
const { toBase58, toBase64, fromBase64, fromBase58 } = require('@ocap/util');
|
|
15
|
-
|
|
16
|
-
const debug = require('debug')(require('../package.json').name);
|
|
17
|
-
|
|
18
|
-
const proofTypes = {
|
|
19
|
-
[types.KeyType.ED25519]: 'Ed25519Signature',
|
|
20
|
-
[types.KeyType.SECP256K1]: 'Secp256k1Signature',
|
|
21
|
-
[types.KeyType.ETHEREUM]: 'EthereumSignature',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Create a valid verifiable credential
|
|
26
|
-
*
|
|
27
|
-
* @param {object} params
|
|
28
|
-
* @param {string} params.type - The type of credential
|
|
29
|
-
* @param {object} params.subject - The content of credential
|
|
30
|
-
* @param {object} params.issuer - The issuer name and wallet
|
|
31
|
-
* @param {Date} params.issuanceDate
|
|
32
|
-
* @param {Date} params.expirationDate
|
|
33
|
-
* @param {String} params.endpoint - Status endpoint url
|
|
34
|
-
* @param {String} params.endpointScope - Endpoint scope, either be public or private
|
|
35
|
-
* @returns {Promise<object>}
|
|
36
|
-
*/
|
|
37
|
-
async function create({
|
|
38
|
-
type,
|
|
39
|
-
subject,
|
|
40
|
-
issuer,
|
|
41
|
-
issuanceDate,
|
|
42
|
-
expirationDate,
|
|
43
|
-
tag = '',
|
|
44
|
-
endpoint = '',
|
|
45
|
-
endpointScope = 'public',
|
|
46
|
-
}) {
|
|
47
|
-
if (!type) {
|
|
48
|
-
throw new Error('Can not create verifiable credential without empty type');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!subject) {
|
|
52
|
-
throw new Error('Can not create verifiable credential from empty subject');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Should have an owner
|
|
56
|
-
if (!subject.id) {
|
|
57
|
-
throw new Error('Can not create verifiable credential without holder');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!isValid(subject.id)) {
|
|
61
|
-
throw new Error('Can not create verifiable credential invalid holder did');
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (endpoint && isAbsoluteUrl(endpoint) === false) {
|
|
65
|
-
throw new Error('VC Endpoint must be absolute url');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (endpointScope && ['public', 'private'].includes(endpointScope) === false) {
|
|
69
|
-
throw new Error('VC Endpoint scope must be either public or private');
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const { wallet, name: issuerName } = issuer;
|
|
73
|
-
const issuerDid = wallet.address;
|
|
74
|
-
const typeInfo = toTypeInfo(issuerDid);
|
|
75
|
-
|
|
76
|
-
// The { pk, hash } type should be same as issuer, role type must be `ROLE_VC`
|
|
77
|
-
const vcType = { ...typeInfo, role: types.RoleType.ROLE_VC };
|
|
78
|
-
const vcDid = fromPublicKeyHash(wallet.hash(stringify(subject)), vcType);
|
|
79
|
-
|
|
80
|
-
issuanceDate = issuanceDate || new Date().toISOString();
|
|
81
|
-
|
|
82
|
-
const vcObj = {
|
|
83
|
-
'@context': 'https://schema.arcblock.io/v0.1/context.jsonld',
|
|
84
|
-
id: vcDid,
|
|
85
|
-
type,
|
|
86
|
-
issuer: {
|
|
87
|
-
id: issuerDid,
|
|
88
|
-
pk: toBase58(wallet.publicKey),
|
|
89
|
-
name: issuerName || issuerDid,
|
|
90
|
-
},
|
|
91
|
-
issuanceDate,
|
|
92
|
-
expirationDate,
|
|
93
|
-
credentialSubject: subject,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
if (tag) {
|
|
97
|
-
vcObj.tag = tag;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (endpoint) {
|
|
101
|
-
vcObj.credentialStatus = {
|
|
102
|
-
id: endpoint,
|
|
103
|
-
type: 'NFTStatusList2021',
|
|
104
|
-
scope: endpointScope || 'public',
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!proofTypes[typeInfo.pk]) {
|
|
109
|
-
throw new Error('Unsupported signer type when create verifiable credential');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const signature = await wallet.sign(stringify(vcObj));
|
|
113
|
-
const proof = {
|
|
114
|
-
type: proofTypes[typeInfo.pk],
|
|
115
|
-
created: issuanceDate,
|
|
116
|
-
proofPurpose: 'assertionMethod',
|
|
117
|
-
jws: toBase64(signature),
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
// NOTE: we should be able to verify the vc before return
|
|
121
|
-
const result = { proof, ...vcObj };
|
|
122
|
-
|
|
123
|
-
debug('create', result);
|
|
124
|
-
|
|
125
|
-
if (await verify({ vc: result, ownerDid: subject.id, trustedIssuers: [issuerDid] })) {
|
|
126
|
-
return result;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Verify that the verifiable credential is valid
|
|
134
|
-
* - It is signed by a whitelist of issuers
|
|
135
|
-
* - It is owned by the vc.subject.id
|
|
136
|
-
* - It has valid signature by the issuer
|
|
137
|
-
* - It is not expired
|
|
138
|
-
*
|
|
139
|
-
* @param {object} vc - the verifiable credential object
|
|
140
|
-
* @param {string} ownerDid - vc holder/owner did
|
|
141
|
-
* @param {Array} trustedIssuers - list of issuer did
|
|
142
|
-
* @throws {Error}
|
|
143
|
-
* @returns {Promise<boolean>}
|
|
144
|
-
*/
|
|
145
|
-
async function verify({ vc, ownerDid, trustedIssuers, ignoreExpired = false }) {
|
|
146
|
-
// Integrity check
|
|
147
|
-
if (!vc) {
|
|
148
|
-
throw new Error('Empty verifiable credential object');
|
|
149
|
-
}
|
|
150
|
-
if (!vc.issuer || !vc.issuer.id || !vc.issuer.pk || !isValid(vc.issuer.id)) {
|
|
151
|
-
throw new Error('Invalid verifiable credential issuer');
|
|
152
|
-
}
|
|
153
|
-
if (!vc.credentialSubject || !vc.credentialSubject.id || !isValid(vc.credentialSubject.id)) {
|
|
154
|
-
throw new Error('Invalid verifiable credential subject');
|
|
155
|
-
}
|
|
156
|
-
if (!vc.proof || !vc.proof.jws) {
|
|
157
|
-
throw new Error('Invalid verifiable credential proof');
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Verify dates
|
|
161
|
-
if (vc.issuanceDate === undefined) {
|
|
162
|
-
throw Error('Invalid verifiable credential issue date');
|
|
163
|
-
}
|
|
164
|
-
if (new Date(vc.issuanceDate).getTime() > Date.now()) {
|
|
165
|
-
throw Error('Verifiable credential has not take effect');
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (!ignoreExpired && vc.expirationDate !== undefined && new Date(vc.expirationDate).getTime() < Date.now()) {
|
|
169
|
-
throw Error('Verifiable credential has expired');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Verify issuer
|
|
173
|
-
const issuers = Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers];
|
|
174
|
-
const issuerDid = issuers.find((x) => x === vc.issuer.id);
|
|
175
|
-
if (!issuerDid) {
|
|
176
|
-
throw new Error('Verifiable credential not issued by trusted issuers');
|
|
177
|
-
}
|
|
178
|
-
if (!isFromPublicKey(issuerDid, vc.issuer.pk)) {
|
|
179
|
-
throw new Error('Verifiable credential not issuer pk not match with issuer did');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Verify owner
|
|
183
|
-
if (ownerDid !== vc.credentialSubject.id) {
|
|
184
|
-
throw new Error('Verifiable credential not owned by specified owner did');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Construct the issuer wallet
|
|
188
|
-
const issuer = fromPublicKey(vc.issuer.pk, toTypeInfo(issuerDid));
|
|
189
|
-
|
|
190
|
-
// NOTE: we are ignoring other fields of the proof
|
|
191
|
-
const clone = cloneDeep(vc);
|
|
192
|
-
const signature = clone.proof.jws;
|
|
193
|
-
delete clone.proof;
|
|
194
|
-
delete clone.signature;
|
|
195
|
-
|
|
196
|
-
// Verify signature
|
|
197
|
-
if ((await issuer.verify(stringify(clone), fromBase64(signature))) !== true) {
|
|
198
|
-
throw Error('Verifiable credential signature not valid');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// TODO: support verify revoked from endpoint
|
|
202
|
-
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Verify that the Presentation is valid
|
|
208
|
-
* - It is signed by VC's owner
|
|
209
|
-
* - It contain challenge
|
|
210
|
-
* - It has valid signature by the issuer
|
|
211
|
-
* - It is not expired
|
|
212
|
-
*
|
|
213
|
-
* @param {object} presentation - the presentation object
|
|
214
|
-
* @param {Array} trustedIssuers - list of issuer did
|
|
215
|
-
* @param {String} challenge - Random byte you want
|
|
216
|
-
* @throws {Error}
|
|
217
|
-
* @returns {Promise<boolean>}
|
|
218
|
-
*/
|
|
219
|
-
async function verifyPresentation({ presentation, trustedIssuers, challenge, ignoreExpired = false }) {
|
|
220
|
-
if (!presentation.challenge || challenge !== presentation.challenge) {
|
|
221
|
-
throw Error('Invalid challenge included on vc presentation');
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const vcList = Array.isArray(presentation.verifiableCredential)
|
|
225
|
-
? presentation.verifiableCredential
|
|
226
|
-
: [presentation.verifiableCredential];
|
|
227
|
-
|
|
228
|
-
const proofList = Array.isArray(presentation.proof) ? presentation.proof : [presentation.proof];
|
|
229
|
-
const clone = cloneDeep(presentation);
|
|
230
|
-
delete clone.proof;
|
|
231
|
-
|
|
232
|
-
await Promise.all(
|
|
233
|
-
vcList.map(async (vcStr) => {
|
|
234
|
-
const vcObj = JSON.parse(vcStr);
|
|
235
|
-
const proof = proofList.find((x) => isFromPublicKey(vcObj.credentialSubject.id, x.pk));
|
|
236
|
-
|
|
237
|
-
if (!proof) {
|
|
238
|
-
throw Error(`VC does not have corresponding proof: ${vcStr}`);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const signature = proof.jws;
|
|
242
|
-
const holder = fromPublicKey(fromBase58(proof.pk), toTypeInfo(vcObj.credentialSubject.id));
|
|
243
|
-
if ((await holder.verify(stringify(clone), fromBase64(signature))) !== true) {
|
|
244
|
-
throw Error('Presentation signature invalid');
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
await verify({ vc: vcObj, ownerDid: vcObj.credentialSubject.id, trustedIssuers, ignoreExpired });
|
|
248
|
-
})
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
async function createCredentialList({ claims, issuer, issuanceDate }) {
|
|
255
|
-
if (!claims || !Array.isArray(claims)) {
|
|
256
|
-
throw new Error('Can not create credential list with empty claim list');
|
|
257
|
-
}
|
|
258
|
-
if (!issuer || !issuer.wallet || !issuer.name) {
|
|
259
|
-
throw new Error('Can not create credential list with empty issuer name or wallet');
|
|
260
|
-
}
|
|
261
|
-
if (typeof issuer.wallet.sign !== 'function') {
|
|
262
|
-
throw new Error('Can not create credential list with invalid issuer wallet');
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const { wallet, name } = issuer;
|
|
266
|
-
const issuerDid = wallet.address;
|
|
267
|
-
const typeInfo = toTypeInfo(issuerDid);
|
|
268
|
-
const vcType = { ...typeInfo, role: types.RoleType.ROLE_VC };
|
|
269
|
-
|
|
270
|
-
const issued = issuanceDate || new Date().toISOString();
|
|
271
|
-
|
|
272
|
-
const results = await Promise.all(
|
|
273
|
-
claims.map(async (x) => {
|
|
274
|
-
const vc = { claim: x };
|
|
275
|
-
|
|
276
|
-
vc.id = fromPublicKeyHash(wallet.hash(stringify(vc.claim)), vcType);
|
|
277
|
-
vc.issued = issued;
|
|
278
|
-
vc.issuer = {
|
|
279
|
-
id: issuerDid,
|
|
280
|
-
pk: toBase58(wallet.publicKey),
|
|
281
|
-
name: name || issuerDid,
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const signature = await wallet.sign(stringify(vc));
|
|
285
|
-
vc.proof = {
|
|
286
|
-
type: proofTypes[typeInfo.pk],
|
|
287
|
-
created: issued,
|
|
288
|
-
proofPurpose: 'assertionMethod',
|
|
289
|
-
jws: toBase64(signature),
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
return vc;
|
|
293
|
-
})
|
|
294
|
-
);
|
|
295
|
-
return results;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
async function verifyCredentialList({ credentials, trustedIssuers }) {
|
|
299
|
-
if (!credentials || !Array.isArray(credentials)) {
|
|
300
|
-
throw new Error('Can not verify with empty credentials list');
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return Promise.all(
|
|
304
|
-
credentials.map(async (x) => {
|
|
305
|
-
// Verify issuer
|
|
306
|
-
const issuers = Array.isArray(trustedIssuers) ? trustedIssuers : [trustedIssuers];
|
|
307
|
-
const issuerDid = issuers.find((d) => d === x.issuer.id);
|
|
308
|
-
if (!issuerDid) {
|
|
309
|
-
throw new Error('Credential not issued by trusted issuers');
|
|
310
|
-
}
|
|
311
|
-
if (!isFromPublicKey(issuerDid, x.issuer.pk)) {
|
|
312
|
-
throw new Error('Credential not issuer pk not match with issuer did');
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Construct the issuer wallet
|
|
316
|
-
const issuer = fromPublicKey(x.issuer.pk, toTypeInfo(issuerDid));
|
|
317
|
-
|
|
318
|
-
// NOTE: we are ignoring other fields of the proof
|
|
319
|
-
const clone = cloneDeep(x);
|
|
320
|
-
const signature = clone.proof.jws;
|
|
321
|
-
delete clone.proof;
|
|
322
|
-
|
|
323
|
-
// Verify signature
|
|
324
|
-
if ((await issuer.verify(stringify(clone), fromBase64(signature))) !== true) {
|
|
325
|
-
throw Error('Status credential signature not valid');
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return x.claim;
|
|
329
|
-
})
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
module.exports = {
|
|
334
|
-
create,
|
|
335
|
-
verify,
|
|
336
|
-
verifyPresentation,
|
|
337
|
-
stableStringify: stringify,
|
|
338
|
-
proofTypes,
|
|
339
|
-
createCredentialList,
|
|
340
|
-
verifyCredentialList,
|
|
341
|
-
};
|