@nuggetslife/vc 0.0.21 → 0.0.24
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/generate_golden_files.mjs +181 -0
- package/index.d.ts +154 -0
- package/index.js +27 -1
- package/package.json +8 -11
- package/src/bbs_2023.rs +71 -0
- package/src/bbs_ietf.rs +255 -0
- package/src/bls_signatures/bbs_bls_holder_bound_signature_2022/mod.rs +7 -38
- package/src/bls_signatures/bbs_bls_holder_bound_signature_proof_2022/mod.rs +3 -13
- package/src/bls_signatures/bbs_bls_signature_2020/mod.rs +4 -34
- package/src/bls_signatures/bbs_bls_signature_2020/types.rs +0 -1
- package/src/bls_signatures/bbs_bls_signature_proof_2020/mod.rs +3 -13
- package/src/bls_signatures/bls_12381_g2_keypair/mod.rs +16 -16
- package/src/bls_signatures/bound_bls_12381_g2_keypair/mod.rs +3 -3
- package/src/jose.rs +415 -0
- package/src/jsonld.rs +4 -26
- package/src/ld_signatures.rs +29 -24
- package/src/lib.rs +4 -0
- package/src/sd_jwt.rs +133 -0
- package/test-data/golden/inputDocument-minimal.json +17 -0
- package/test-data/golden/inputDocument-rich.json +29 -0
- package/test-data/golden/mattrglobal-derived-prc.json +36 -0
- package/test-data/golden/mattrglobal-signed-minimal.json +30 -0
- package/test-data/golden/mattrglobal-signed-prc.json +42 -0
- package/test-data/golden/mattrglobal-signed-rich.json +42 -0
- package/test.mjs +38 -0
- package/test_backward_compat.mjs +287 -0
- package/test_bbs_2023.mjs +195 -0
- package/test_bbs_ietf.mjs +168 -0
- package/test_fuzz.mjs +202 -0
- package/test_jose.mjs +497 -0
- package/test_jsonld_crossverify.mjs +1 -1
- package/test_sd_jwt.mjs +197 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
4
|
+
"https://w3id.org/citizenship/v1",
|
|
5
|
+
"https://w3id.org/security/bbs/v1"
|
|
6
|
+
],
|
|
7
|
+
"id": "https://issuer.example.com/credentials/minimal-001",
|
|
8
|
+
"type": ["VerifiableCredential", "PermanentResidentCard"],
|
|
9
|
+
"issuer": "did:example:489398593",
|
|
10
|
+
"issuanceDate": "2024-01-15T10:00:00Z",
|
|
11
|
+
"credentialSubject": {
|
|
12
|
+
"id": "did:example:user123",
|
|
13
|
+
"type": ["PermanentResident", "Person"],
|
|
14
|
+
"givenName": "Alice",
|
|
15
|
+
"familyName": "Johnson"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
4
|
+
"https://w3id.org/citizenship/v1",
|
|
5
|
+
"https://w3id.org/security/bbs/v1"
|
|
6
|
+
],
|
|
7
|
+
"id": "https://issuer.example.com/credentials/rich-001",
|
|
8
|
+
"type": ["VerifiableCredential", "PermanentResidentCard"],
|
|
9
|
+
"issuer": "did:example:489398593",
|
|
10
|
+
"identifier": "RICH-001",
|
|
11
|
+
"name": "Extended Resident Card",
|
|
12
|
+
"description": "Credential with many fields to test BBS+ signatures over larger statement counts.",
|
|
13
|
+
"issuanceDate": "2023-06-20T08:30:00Z",
|
|
14
|
+
"expirationDate": "2033-06-20T08:30:00Z",
|
|
15
|
+
"credentialSubject": {
|
|
16
|
+
"id": "did:example:user456",
|
|
17
|
+
"type": ["PermanentResident", "Person"],
|
|
18
|
+
"givenName": "Robert",
|
|
19
|
+
"familyName": "Williams",
|
|
20
|
+
"gender": "Male",
|
|
21
|
+
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
|
|
22
|
+
"residentSince": "2018-03-15",
|
|
23
|
+
"lprCategory": "C12",
|
|
24
|
+
"lprNumber": "888-777-666",
|
|
25
|
+
"commuterClassification": "C2",
|
|
26
|
+
"birthCountry": "Canada",
|
|
27
|
+
"birthDate": "1985-11-23"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
4
|
+
"https://w3id.org/citizenship/v1",
|
|
5
|
+
"https://w3id.org/security/bbs/v1"
|
|
6
|
+
],
|
|
7
|
+
"id": "https://issuer.oidp.uscis.gov/credentials/83627465",
|
|
8
|
+
"type": [
|
|
9
|
+
"PermanentResidentCard",
|
|
10
|
+
"VerifiableCredential"
|
|
11
|
+
],
|
|
12
|
+
"description": "Government of Example Permanent Resident Card.",
|
|
13
|
+
"identifier": "83627465",
|
|
14
|
+
"name": "Permanent Resident Card",
|
|
15
|
+
"credentialSubject": {
|
|
16
|
+
"id": "did:example:b34ca6cd37bbf23",
|
|
17
|
+
"type": [
|
|
18
|
+
"Person",
|
|
19
|
+
"PermanentResident"
|
|
20
|
+
],
|
|
21
|
+
"familyName": "SMITH",
|
|
22
|
+
"gender": "Male",
|
|
23
|
+
"givenName": "JOHN"
|
|
24
|
+
},
|
|
25
|
+
"expirationDate": "2029-12-03T12:19:52Z",
|
|
26
|
+
"issuanceDate": "2019-12-03T12:19:52Z",
|
|
27
|
+
"issuer": "did:example:489398593",
|
|
28
|
+
"proof": {
|
|
29
|
+
"type": "BbsBlsSignatureProof2020",
|
|
30
|
+
"created": "2026-02-24T18:31:50Z",
|
|
31
|
+
"nonce": "Mb2PKdz8oLnBh2DaIYDN2qp0B/BzqlZ0bomtWLnPkCuUX+LOLozA67gzRX8IFW+prJ4=",
|
|
32
|
+
"proofPurpose": "assertionMethod",
|
|
33
|
+
"proofValue": "ABkB/wbvlXznxDJB6mUBWnQAVCFn1BOG3oGhhrmH4CkfgHwIko6cE2ib8BSnHoIgsvxJbf8ql85AoX28XZNkogIRP9k1rvTS0TN1OBMx20q0+4FQX/jhGs7tVgXoeDJsTIe/kFtOhpoixTV0hNpMpwR3/zF+npQFipsk3Awc0v0Ne/Xc5ExLytwpS+UdtYnCBZWbgTAfAAAAdK8ARGBjzOUY0MsGvW37vMsegFaUHN8iu+vjk/RSzcb45sCwzA3I/dZIfonaPMKmBQAAAAIdxKYEnrgvyA3UPoQR6yAx9mBquPkFq8y9EnMloOj6eVi3eLIrFfaChASa2INbf43O09CGGXcC7ocGmG6GEg8GrgHAIPHm7ecOwUFEhJT8CC5j7t2z4MFrltfk1xW+PD+gsZ4WSjuTuqhHOBJNWXMcAAAACT/T2kIOroFI9tWJrprwQ+NK9W59cgn7V6UV/ynxlhBnDvFcCd7hYv5yDUuQuz9AiSxEveiMViWkHTBDFZ/Z/LhKYHOGGCKG9CJ9OVstwzzfe1jpfTYy4ZnLuuoJCSvcOm72DGumCHr2JVolSYEv5WKxXbN4MY46zL7hLBY6p88zXi7wzo8Odj+jMcptD0vLsKv0FjdSRK4XVXW2FEIy4IASrRFPuZo0Pb30wG3MzFnq7c17sSLUz/2EA929J2YxfmzulFTvsJm4LfwCJ7VA0iY6Suq1qBVy+fJnFMlqDwGoBl4izvhq9J9jBFVysZBdKlK4rwJtj0WqY75lnCPT/30ap7eC0YNnULtSHn3aYNF2JlO8wyu8zDODuXvRF2i5Kw==",
|
|
34
|
+
"verificationMethod": "did:example:489398593#test"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
4
|
+
"https://w3id.org/citizenship/v1",
|
|
5
|
+
"https://w3id.org/security/bbs/v1"
|
|
6
|
+
],
|
|
7
|
+
"id": "https://issuer.example.com/credentials/minimal-001",
|
|
8
|
+
"type": [
|
|
9
|
+
"VerifiableCredential",
|
|
10
|
+
"PermanentResidentCard"
|
|
11
|
+
],
|
|
12
|
+
"issuer": "did:example:489398593",
|
|
13
|
+
"issuanceDate": "2024-01-15T10:00:00Z",
|
|
14
|
+
"credentialSubject": {
|
|
15
|
+
"id": "did:example:user123",
|
|
16
|
+
"type": [
|
|
17
|
+
"PermanentResident",
|
|
18
|
+
"Person"
|
|
19
|
+
],
|
|
20
|
+
"givenName": "Alice",
|
|
21
|
+
"familyName": "Johnson"
|
|
22
|
+
},
|
|
23
|
+
"proof": {
|
|
24
|
+
"type": "BbsBlsSignature2020",
|
|
25
|
+
"created": "2026-02-24T18:31:50Z",
|
|
26
|
+
"proofPurpose": "assertionMethod",
|
|
27
|
+
"proofValue": "h9O11RGxkmaZQhOZQQRkuy0mGJ+1krOlSO9UMAc1zQf8O7wv2Fi3Ev8UMZxmX2Q4MKVg9X44OT7IyqxonIH7xLuNJjBkG7KYma9urLMBCo4x5JoTWPaG1p7URXapIpy1ng+avITVXJin9XQoxPxyNA==",
|
|
28
|
+
"verificationMethod": "did:example:489398593#test"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
4
|
+
"https://w3id.org/citizenship/v1",
|
|
5
|
+
"https://w3id.org/security/bbs/v1"
|
|
6
|
+
],
|
|
7
|
+
"id": "https://issuer.oidp.uscis.gov/credentials/83627465",
|
|
8
|
+
"type": [
|
|
9
|
+
"VerifiableCredential",
|
|
10
|
+
"PermanentResidentCard"
|
|
11
|
+
],
|
|
12
|
+
"issuer": "did:example:489398593",
|
|
13
|
+
"identifier": "83627465",
|
|
14
|
+
"name": "Permanent Resident Card",
|
|
15
|
+
"description": "Government of Example Permanent Resident Card.",
|
|
16
|
+
"issuanceDate": "2019-12-03T12:19:52Z",
|
|
17
|
+
"expirationDate": "2029-12-03T12:19:52Z",
|
|
18
|
+
"credentialSubject": {
|
|
19
|
+
"id": "did:example:b34ca6cd37bbf23",
|
|
20
|
+
"type": [
|
|
21
|
+
"PermanentResident",
|
|
22
|
+
"Person"
|
|
23
|
+
],
|
|
24
|
+
"givenName": "JOHN",
|
|
25
|
+
"familyName": "SMITH",
|
|
26
|
+
"gender": "Male",
|
|
27
|
+
"image": "data:image/png;base64,iVBORw0KGgokJggg==",
|
|
28
|
+
"residentSince": "2015-01-01",
|
|
29
|
+
"lprCategory": "C09",
|
|
30
|
+
"lprNumber": "999-999-999",
|
|
31
|
+
"commuterClassification": "C1",
|
|
32
|
+
"birthCountry": "Bahamas",
|
|
33
|
+
"birthDate": "1958-07-17"
|
|
34
|
+
},
|
|
35
|
+
"proof": {
|
|
36
|
+
"type": "BbsBlsSignature2020",
|
|
37
|
+
"created": "2026-02-24T18:31:50Z",
|
|
38
|
+
"proofPurpose": "assertionMethod",
|
|
39
|
+
"proofValue": "lvx5LJssQ6BVqMX3931+8EApBMmkYwydWl7xUeNBMnVNZSamwHmS5WLlqicauVofYUHlKfzccE4m7waZyoLEkBLFiK2g54Q2i+CdtYBgDdkUDsoULSBMcH1MwGHwdjfXpldFNFrHFx/IAvLVniyeMQ==",
|
|
40
|
+
"verificationMethod": "did:example:489398593#test"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@context": [
|
|
3
|
+
"https://www.w3.org/2018/credentials/v1",
|
|
4
|
+
"https://w3id.org/citizenship/v1",
|
|
5
|
+
"https://w3id.org/security/bbs/v1"
|
|
6
|
+
],
|
|
7
|
+
"id": "https://issuer.example.com/credentials/rich-001",
|
|
8
|
+
"type": [
|
|
9
|
+
"VerifiableCredential",
|
|
10
|
+
"PermanentResidentCard"
|
|
11
|
+
],
|
|
12
|
+
"issuer": "did:example:489398593",
|
|
13
|
+
"identifier": "RICH-001",
|
|
14
|
+
"name": "Extended Resident Card",
|
|
15
|
+
"description": "Credential with many fields to test BBS+ signatures over larger statement counts.",
|
|
16
|
+
"issuanceDate": "2023-06-20T08:30:00Z",
|
|
17
|
+
"expirationDate": "2033-06-20T08:30:00Z",
|
|
18
|
+
"credentialSubject": {
|
|
19
|
+
"id": "did:example:user456",
|
|
20
|
+
"type": [
|
|
21
|
+
"PermanentResident",
|
|
22
|
+
"Person"
|
|
23
|
+
],
|
|
24
|
+
"givenName": "Robert",
|
|
25
|
+
"familyName": "Williams",
|
|
26
|
+
"gender": "Male",
|
|
27
|
+
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
|
|
28
|
+
"residentSince": "2018-03-15",
|
|
29
|
+
"lprCategory": "C12",
|
|
30
|
+
"lprNumber": "888-777-666",
|
|
31
|
+
"commuterClassification": "C2",
|
|
32
|
+
"birthCountry": "Canada",
|
|
33
|
+
"birthDate": "1985-11-23"
|
|
34
|
+
},
|
|
35
|
+
"proof": {
|
|
36
|
+
"type": "BbsBlsSignature2020",
|
|
37
|
+
"created": "2026-02-24T18:31:50Z",
|
|
38
|
+
"proofPurpose": "assertionMethod",
|
|
39
|
+
"proofValue": "h9Ax+SZHQsWCv0rXIFglLNAIJSNXF8yp7+MC+tlZsSzp2CWFPhRv8/dkgLTt9DRdBZv2+0Ch0WCIYewp/jE2bGDy4XALHFGj8hM5lW5hB4kio0Kglkol4OlKw+eZ8ujstHAB9XhFu7/XwAcKOB02TQ==",
|
|
40
|
+
"verificationMethod": "did:example:489398593#test"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/test.mjs
CHANGED
|
@@ -240,6 +240,44 @@ test('full demo_single flow: sign → verify → derive → verify derived', asy
|
|
|
240
240
|
assert.equal(derivedVerifyResult.verified, true, `derived verify failed: ${derivedVerifyResult.error}`);
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
+
test('demo_multi flow: sign twice → verify → derive → verify derived', async () => {
|
|
244
|
+
// Step 1: First sign
|
|
245
|
+
const signed = await ldSign({
|
|
246
|
+
document: inputDocument,
|
|
247
|
+
keyPair,
|
|
248
|
+
contexts,
|
|
249
|
+
});
|
|
250
|
+
assert.ok(signed.proof, 'signed document has proof');
|
|
251
|
+
assert.ok(!Array.isArray(signed.proof), 'single sign produces a single proof (not array)');
|
|
252
|
+
|
|
253
|
+
// Step 2: Second sign (creates multi-proof array)
|
|
254
|
+
const multiSigned = await ldSign({
|
|
255
|
+
document: signed,
|
|
256
|
+
keyPair,
|
|
257
|
+
contexts,
|
|
258
|
+
});
|
|
259
|
+
assert.ok(Array.isArray(multiSigned.proof), 'double sign produces proof array');
|
|
260
|
+
assert.equal(multiSigned.proof.length, 2, 'proof array has 2 proofs');
|
|
261
|
+
assert.equal(multiSigned.proof[0].type, 'BbsBlsSignature2020');
|
|
262
|
+
assert.equal(multiSigned.proof[1].type, 'BbsBlsSignature2020');
|
|
263
|
+
|
|
264
|
+
// Step 3: Verify multi-proof document
|
|
265
|
+
const verifyResult = await ldVerify({ document: multiSigned, contexts });
|
|
266
|
+
assert.equal(verifyResult.verified, true, `multi-proof verify failed: ${verifyResult.error}`);
|
|
267
|
+
|
|
268
|
+
// Step 4: Derive selective disclosure from multi-proof document
|
|
269
|
+
const derived = await ldDeriveProof({
|
|
270
|
+
document: multiSigned,
|
|
271
|
+
revealDocument,
|
|
272
|
+
contexts,
|
|
273
|
+
});
|
|
274
|
+
assert.ok(derived.proof, 'derived document has proof');
|
|
275
|
+
|
|
276
|
+
// Step 5: Verify derived proof
|
|
277
|
+
const derivedVerifyResult = await ldVerify({ document: derived, contexts });
|
|
278
|
+
assert.equal(derivedVerifyResult.verified, true, `multi-proof derived verify failed: ${derivedVerifyResult.error}`);
|
|
279
|
+
});
|
|
280
|
+
|
|
243
281
|
|
|
244
282
|
//
|
|
245
283
|
// BbsBlsSignature2020 class-based API tests
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// Backward compatibility tests: verify that VCs signed by the old @mattrglobal
|
|
2
|
+
// library still verify correctly with the new NAPI implementation.
|
|
3
|
+
//
|
|
4
|
+
// These tests use static "golden file" fixtures generated by
|
|
5
|
+
// generate_golden_files.mjs using @mattrglobal/jsonld-signatures-bbs.
|
|
6
|
+
// The golden files represent VCs that may already exist in user wallets.
|
|
7
|
+
//
|
|
8
|
+
// Key findings:
|
|
9
|
+
// - BbsBlsSignature2020 (original signed VCs) are fully cross-compatible
|
|
10
|
+
// - BbsBlsSignatureProof2020 (derived proofs) are NOT cross-compatible between
|
|
11
|
+
// implementations, but this is acceptable because derived proofs are generated
|
|
12
|
+
// on-the-fly for presentations and never stored in wallets. The critical path
|
|
13
|
+
// is: load wallet VC → derive new proof with current implementation → verify.
|
|
14
|
+
//
|
|
15
|
+
// Run: cd vc/js && node test_backward_compat.mjs
|
|
16
|
+
|
|
17
|
+
import test from 'node:test';
|
|
18
|
+
import assert from 'node:assert';
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
20
|
+
import { resolve, dirname } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import { createRequire } from 'node:module';
|
|
23
|
+
|
|
24
|
+
// NAPI binding (new implementation)
|
|
25
|
+
import { ldVerify, ldDeriveProof } from './index.js';
|
|
26
|
+
|
|
27
|
+
// @mattrglobal library (old implementation) — for generation verification
|
|
28
|
+
const require = createRequire(import.meta.url);
|
|
29
|
+
const { BbsBlsSignature2020 } = require('@mattrglobal/jsonld-signatures-bbs');
|
|
30
|
+
const { verify: mattrVerify, purposes, extendContextLoader } = require('jsonld-signatures');
|
|
31
|
+
|
|
32
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
33
|
+
const dataDir = resolve(__dirname, 'test-data');
|
|
34
|
+
const goldenDir = resolve(dataDir, 'golden');
|
|
35
|
+
|
|
36
|
+
const loadJson = (name) => JSON.parse(readFileSync(resolve(dataDir, name), 'utf8'));
|
|
37
|
+
const loadGolden = (name) => JSON.parse(readFileSync(resolve(goldenDir, name), 'utf8'));
|
|
38
|
+
|
|
39
|
+
// Load context files
|
|
40
|
+
const keyPair = loadJson('keyPair.json');
|
|
41
|
+
const controllerDocument = loadJson('controllerDocument.json');
|
|
42
|
+
const citizenVocab = loadJson('citizenVocab.json');
|
|
43
|
+
const bbsContext = loadJson('bbs.json');
|
|
44
|
+
const credentialsContext = loadJson('credentialsContext.json');
|
|
45
|
+
const suiteContext = loadJson('suiteContext.json');
|
|
46
|
+
const revealDocument = loadJson('deriveProofFrame.json');
|
|
47
|
+
|
|
48
|
+
// NAPI contexts map
|
|
49
|
+
const napiContexts = {
|
|
50
|
+
"did:example:489398593#test": keyPair,
|
|
51
|
+
"did:example:489398593": controllerDocument,
|
|
52
|
+
"https://w3id.org/citizenship/v1": citizenVocab,
|
|
53
|
+
"https://w3id.org/security/bbs/v1": bbsContext,
|
|
54
|
+
"https://www.w3.org/2018/credentials/v1": credentialsContext,
|
|
55
|
+
"https://w3id.org/security/suites/jws-2020/v1": suiteContext,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// @mattrglobal document loader
|
|
59
|
+
const customDocLoader = (url) => {
|
|
60
|
+
const context = napiContexts[url];
|
|
61
|
+
if (context) return { contextUrl: null, document: context, documentUrl: url };
|
|
62
|
+
throw new Error(`Attempted to remote load context: '${url}'`);
|
|
63
|
+
};
|
|
64
|
+
const documentLoader = extendContextLoader(customDocLoader);
|
|
65
|
+
|
|
66
|
+
// Load golden files (signed by @mattrglobal)
|
|
67
|
+
const signedPRC = loadGolden('mattrglobal-signed-prc.json');
|
|
68
|
+
const signedMinimal = loadGolden('mattrglobal-signed-minimal.json');
|
|
69
|
+
const signedRich = loadGolden('mattrglobal-signed-rich.json');
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
//
|
|
73
|
+
// Wallet VC verification — the critical path
|
|
74
|
+
// VCs in user wallets have BbsBlsSignature2020 proofs signed by the old library.
|
|
75
|
+
// The new NAPI implementation must verify these without re-signing.
|
|
76
|
+
//
|
|
77
|
+
|
|
78
|
+
test('wallet-compat: NAPI ldVerify accepts @mattrglobal-signed VC', async () => {
|
|
79
|
+
const result = await ldVerify({
|
|
80
|
+
document: signedPRC,
|
|
81
|
+
contexts: napiContexts,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
assert.equal(result.verified, true,
|
|
85
|
+
`NAPI ldVerify failed on @mattrglobal-signed VC: ${result.error || 'unknown error'}`);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('wallet-compat: @mattrglobal verifies its own golden-file VC', async () => {
|
|
89
|
+
const result = await mattrVerify(signedPRC, {
|
|
90
|
+
suite: new BbsBlsSignature2020(),
|
|
91
|
+
purpose: new purposes.AssertionProofPurpose(),
|
|
92
|
+
documentLoader,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
assert.equal(result.verified, true,
|
|
96
|
+
`@mattrglobal verify failed on its own golden-file VC: ${JSON.stringify(result.error)}`);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
//
|
|
101
|
+
// Selective disclosure — the real-world flow for wallet VCs:
|
|
102
|
+
// 1. Load signed VC from wallet (signed by old library)
|
|
103
|
+
// 2. Derive a proof using the current (NAPI) implementation
|
|
104
|
+
// 3. Verify the derived proof using the current (NAPI) implementation
|
|
105
|
+
//
|
|
106
|
+
|
|
107
|
+
test('wallet-compat: NAPI ldDeriveProof works on @mattrglobal-signed VC', async () => {
|
|
108
|
+
const derived = await ldDeriveProof({
|
|
109
|
+
document: signedPRC,
|
|
110
|
+
revealDocument,
|
|
111
|
+
contexts: napiContexts,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
assert.ok(derived, 'ldDeriveProof returned a result');
|
|
115
|
+
assert.ok(derived.proof, 'derived document has a proof');
|
|
116
|
+
assert.equal(derived.proof.type, 'BbsBlsSignatureProof2020');
|
|
117
|
+
|
|
118
|
+
// Verify selective disclosure: only requested fields present
|
|
119
|
+
assert.equal(derived.credentialSubject.givenName, 'JOHN');
|
|
120
|
+
assert.equal(derived.credentialSubject.familyName, 'SMITH');
|
|
121
|
+
assert.equal(derived.credentialSubject.gender, 'Male');
|
|
122
|
+
assert.equal(derived.credentialSubject.birthDate, undefined, 'birthDate should be hidden');
|
|
123
|
+
assert.equal(derived.credentialSubject.lprNumber, undefined, 'lprNumber should be hidden');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('wallet-compat: NAPI verifies its own derived proof from @mattrglobal-signed VC', async () => {
|
|
127
|
+
const derived = await ldDeriveProof({
|
|
128
|
+
document: signedPRC,
|
|
129
|
+
revealDocument,
|
|
130
|
+
contexts: napiContexts,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const result = await ldVerify({
|
|
134
|
+
document: derived,
|
|
135
|
+
contexts: napiContexts,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
assert.equal(result.verified, true,
|
|
139
|
+
`NAPI ldVerify failed on NAPI-derived proof: ${result.error || 'unknown error'}`);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
//
|
|
144
|
+
// Tamper detection — NAPI must reject modified @mattrglobal-signed VCs
|
|
145
|
+
//
|
|
146
|
+
|
|
147
|
+
test('wallet-compat: NAPI rejects tampered @mattrglobal-signed VC', async () => {
|
|
148
|
+
const tampered = JSON.parse(JSON.stringify(signedPRC));
|
|
149
|
+
tampered.credentialSubject.givenName = 'TAMPERED';
|
|
150
|
+
|
|
151
|
+
const result = await ldVerify({
|
|
152
|
+
document: tampered,
|
|
153
|
+
contexts: napiContexts,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
assert.equal(result.verified, false, 'tampered VC should not verify');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('wallet-compat: NAPI rejects @mattrglobal-signed VC with altered proof', async () => {
|
|
160
|
+
const altered = JSON.parse(JSON.stringify(signedPRC));
|
|
161
|
+
// Flip a character in the proof value
|
|
162
|
+
altered.proof.proofValue = 'X' + altered.proof.proofValue.slice(1);
|
|
163
|
+
|
|
164
|
+
// The Rust BBS crate may panic on malformed proof data rather than returning
|
|
165
|
+
// verified: false. Both behaviors correctly reject the tampered proof.
|
|
166
|
+
try {
|
|
167
|
+
const result = await ldVerify({
|
|
168
|
+
document: altered,
|
|
169
|
+
contexts: napiContexts,
|
|
170
|
+
});
|
|
171
|
+
assert.equal(result.verified, false, 'altered proof should not verify');
|
|
172
|
+
} catch (err) {
|
|
173
|
+
// Panic or error thrown — proof was rejected, which is the correct behavior
|
|
174
|
+
assert.ok(true, `proof correctly rejected with error: ${err.message}`);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
//
|
|
180
|
+
// Multiple selective disclosure frames — verify different field combinations
|
|
181
|
+
// work when deriving from an @mattrglobal-signed VC
|
|
182
|
+
//
|
|
183
|
+
|
|
184
|
+
test('wallet-compat: derive minimal proof (type only) from @mattrglobal-signed VC', async () => {
|
|
185
|
+
const minimalFrame = {
|
|
186
|
+
"@context": signedPRC["@context"],
|
|
187
|
+
"type": ["VerifiableCredential", "PermanentResidentCard"],
|
|
188
|
+
"credentialSubject": {
|
|
189
|
+
"@explicit": true,
|
|
190
|
+
"type": ["PermanentResident", "Person"],
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const derived = await ldDeriveProof({
|
|
195
|
+
document: signedPRC,
|
|
196
|
+
revealDocument: minimalFrame,
|
|
197
|
+
contexts: napiContexts,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
assert.ok(derived.proof, 'derived document has a proof');
|
|
201
|
+
assert.equal(derived.proof.type, 'BbsBlsSignatureProof2020');
|
|
202
|
+
// Only type should be present, no PII fields
|
|
203
|
+
assert.equal(derived.credentialSubject.givenName, undefined);
|
|
204
|
+
assert.equal(derived.credentialSubject.familyName, undefined);
|
|
205
|
+
|
|
206
|
+
// Verify the minimal derived proof
|
|
207
|
+
const result = await ldVerify({ document: derived, contexts: napiContexts });
|
|
208
|
+
assert.equal(result.verified, true,
|
|
209
|
+
`minimal derived proof failed verification: ${result.error || 'unknown error'}`);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
//
|
|
214
|
+
// VC variant tests — different field counts exercise different BBS+ statement counts.
|
|
215
|
+
// BBS+ signatures encode each field as a separate "message" (statement). Verifying
|
|
216
|
+
// VCs with different field counts ensures the NAPI implementation handles varying
|
|
217
|
+
// statement counts correctly, matching the old library's behavior.
|
|
218
|
+
//
|
|
219
|
+
|
|
220
|
+
test('variant: NAPI ldVerify accepts @mattrglobal-signed minimal VC (few fields)', async () => {
|
|
221
|
+
const result = await ldVerify({
|
|
222
|
+
document: signedMinimal,
|
|
223
|
+
contexts: napiContexts,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
assert.equal(result.verified, true,
|
|
227
|
+
`NAPI ldVerify failed on minimal VC: ${result.error || 'unknown error'}`);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('variant: NAPI ldVerify accepts @mattrglobal-signed rich VC (many fields)', async () => {
|
|
231
|
+
const result = await ldVerify({
|
|
232
|
+
document: signedRich,
|
|
233
|
+
contexts: napiContexts,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
assert.equal(result.verified, true,
|
|
237
|
+
`NAPI ldVerify failed on rich VC: ${result.error || 'unknown error'}`);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('variant: NAPI ldDeriveProof + ldVerify on minimal VC', async () => {
|
|
241
|
+
// Minimal VC only has givenName and familyName — derive with type-only frame
|
|
242
|
+
const minimalFrame = {
|
|
243
|
+
"@context": signedMinimal["@context"],
|
|
244
|
+
"type": ["VerifiableCredential", "PermanentResidentCard"],
|
|
245
|
+
"credentialSubject": {
|
|
246
|
+
"@explicit": true,
|
|
247
|
+
"type": ["PermanentResident", "Person"],
|
|
248
|
+
"givenName": {},
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const derived = await ldDeriveProof({
|
|
253
|
+
document: signedMinimal,
|
|
254
|
+
revealDocument: minimalFrame,
|
|
255
|
+
contexts: napiContexts,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
assert.ok(derived.proof, 'derived document has a proof');
|
|
259
|
+
assert.equal(derived.credentialSubject.givenName, 'Alice');
|
|
260
|
+
assert.equal(derived.credentialSubject.familyName, undefined, 'familyName should be hidden');
|
|
261
|
+
|
|
262
|
+
const result = await ldVerify({ document: derived, contexts: napiContexts });
|
|
263
|
+
assert.equal(result.verified, true,
|
|
264
|
+
`minimal VC derived proof failed: ${result.error || 'unknown error'}`);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('variant: NAPI ldDeriveProof + ldVerify on rich VC', async () => {
|
|
268
|
+
// Rich VC has many fields — derive revealing only a few
|
|
269
|
+
const derived = await ldDeriveProof({
|
|
270
|
+
document: signedRich,
|
|
271
|
+
revealDocument,
|
|
272
|
+
contexts: napiContexts,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
assert.ok(derived.proof, 'derived document has a proof');
|
|
276
|
+
assert.equal(derived.credentialSubject.givenName, 'Robert');
|
|
277
|
+
assert.equal(derived.credentialSubject.familyName, 'Williams');
|
|
278
|
+
assert.equal(derived.credentialSubject.gender, 'Male');
|
|
279
|
+
// Fields not in reveal frame should be hidden
|
|
280
|
+
assert.equal(derived.credentialSubject.birthDate, undefined, 'birthDate should be hidden');
|
|
281
|
+
assert.equal(derived.credentialSubject.lprNumber, undefined, 'lprNumber should be hidden');
|
|
282
|
+
assert.equal(derived.credentialSubject.birthCountry, undefined, 'birthCountry should be hidden');
|
|
283
|
+
|
|
284
|
+
const result = await ldVerify({ document: derived, contexts: napiContexts });
|
|
285
|
+
assert.equal(result.verified, true,
|
|
286
|
+
`rich VC derived proof failed: ${result.error || 'unknown error'}`);
|
|
287
|
+
});
|