@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,195 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import {
|
|
4
|
+
bbsIetfKeygen,
|
|
5
|
+
bbs2023Sign,
|
|
6
|
+
bbs2023Derive,
|
|
7
|
+
bbs2023Verify,
|
|
8
|
+
} from './index.js';
|
|
9
|
+
|
|
10
|
+
// =====================================================================
|
|
11
|
+
// Helpers
|
|
12
|
+
// =====================================================================
|
|
13
|
+
|
|
14
|
+
function makeKeyPair() {
|
|
15
|
+
const kp = bbsIetfKeygen();
|
|
16
|
+
return { secretKey: kp.secretKey, publicKey: kp.publicKey };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function sampleCredential() {
|
|
20
|
+
return {
|
|
21
|
+
'@context': [
|
|
22
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
23
|
+
{
|
|
24
|
+
name: 'http://schema.org/name',
|
|
25
|
+
age: 'http://schema.org/age',
|
|
26
|
+
email: 'http://schema.org/email',
|
|
27
|
+
alumniOf: 'http://schema.org/alumniOf',
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
type: ['VerifiableCredential'],
|
|
31
|
+
issuer: 'did:example:issuer',
|
|
32
|
+
issuanceDate: '2024-01-01T00:00:00Z',
|
|
33
|
+
credentialSubject: {
|
|
34
|
+
id: 'did:example:user123',
|
|
35
|
+
name: 'Alice',
|
|
36
|
+
age: 30,
|
|
37
|
+
email: 'alice@example.com',
|
|
38
|
+
alumniOf: 'Example University',
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// =====================================================================
|
|
44
|
+
// BBS-2023 Round-Trip: Sign → Verify Base Proof
|
|
45
|
+
// =====================================================================
|
|
46
|
+
|
|
47
|
+
test('bbs-2023: sign and verify base proof', () => {
|
|
48
|
+
const kp = makeKeyPair();
|
|
49
|
+
const doc = sampleCredential();
|
|
50
|
+
|
|
51
|
+
const signed = bbs2023Sign({
|
|
52
|
+
document: doc,
|
|
53
|
+
secretKey: kp.secretKey,
|
|
54
|
+
publicKey: kp.publicKey,
|
|
55
|
+
mandatoryPointers: ['/issuer', '/issuanceDate'],
|
|
56
|
+
verificationMethod: 'did:example:issuer#bbs-key-1',
|
|
57
|
+
proofPurpose: 'assertionMethod',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
assert.ok(signed.proof, 'signed document has proof');
|
|
61
|
+
assert.equal(signed.proof.type, 'DataIntegrityProof');
|
|
62
|
+
assert.equal(signed.proof.cryptosuite, 'bbs-2023');
|
|
63
|
+
assert.ok(signed.proof.proofValue.startsWith('u'), 'proofValue starts with multibase u');
|
|
64
|
+
|
|
65
|
+
// Verify
|
|
66
|
+
const result = bbs2023Verify(signed, kp.publicKey);
|
|
67
|
+
assert.ok(result.verified, 'base proof verified');
|
|
68
|
+
assert.equal(result.error, null);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// =====================================================================
|
|
72
|
+
// BBS-2023 Full Round-Trip: Sign → Derive → Verify
|
|
73
|
+
// =====================================================================
|
|
74
|
+
|
|
75
|
+
test('bbs-2023: sign → derive → verify round-trip', () => {
|
|
76
|
+
const kp = makeKeyPair();
|
|
77
|
+
const doc = sampleCredential();
|
|
78
|
+
|
|
79
|
+
// Issuer signs
|
|
80
|
+
const signed = bbs2023Sign({
|
|
81
|
+
document: doc,
|
|
82
|
+
secretKey: kp.secretKey,
|
|
83
|
+
publicKey: kp.publicKey,
|
|
84
|
+
mandatoryPointers: ['/issuer', '/issuanceDate'],
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Holder derives selective disclosure
|
|
88
|
+
const derived = bbs2023Derive({
|
|
89
|
+
document: signed,
|
|
90
|
+
selectivePointers: ['/credentialSubject/name'],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
assert.ok(derived.proof, 'derived document has proof');
|
|
94
|
+
assert.equal(derived.proof.cryptosuite, 'bbs-2023');
|
|
95
|
+
|
|
96
|
+
// Verifier verifies
|
|
97
|
+
const result = bbs2023Verify(derived, kp.publicKey);
|
|
98
|
+
assert.ok(result.verified, 'derived proof verified');
|
|
99
|
+
assert.equal(result.error, null);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// =====================================================================
|
|
103
|
+
// Wrong Key Rejection
|
|
104
|
+
// =====================================================================
|
|
105
|
+
|
|
106
|
+
test('bbs-2023: verification fails with wrong key', () => {
|
|
107
|
+
const kp = makeKeyPair();
|
|
108
|
+
const wrongKp = makeKeyPair();
|
|
109
|
+
const doc = sampleCredential();
|
|
110
|
+
|
|
111
|
+
const signed = bbs2023Sign({
|
|
112
|
+
document: doc,
|
|
113
|
+
secretKey: kp.secretKey,
|
|
114
|
+
publicKey: kp.publicKey,
|
|
115
|
+
mandatoryPointers: ['/issuer'],
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const result = bbs2023Verify(signed, wrongKp.publicKey);
|
|
119
|
+
assert.equal(result.verified, false, 'should not verify with wrong key');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// =====================================================================
|
|
123
|
+
// Mandatory-Only (No Selective Disclosure)
|
|
124
|
+
// =====================================================================
|
|
125
|
+
|
|
126
|
+
test('bbs-2023: derive with no selective pointers', () => {
|
|
127
|
+
const kp = makeKeyPair();
|
|
128
|
+
const doc = sampleCredential();
|
|
129
|
+
|
|
130
|
+
const signed = bbs2023Sign({
|
|
131
|
+
document: doc,
|
|
132
|
+
secretKey: kp.secretKey,
|
|
133
|
+
publicKey: kp.publicKey,
|
|
134
|
+
mandatoryPointers: ['/issuer', '/issuanceDate'],
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Derive with no selective pointers — only mandatory fields disclosed
|
|
138
|
+
const derived = bbs2023Derive({
|
|
139
|
+
document: signed,
|
|
140
|
+
selectivePointers: [],
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const result = bbs2023Verify(derived, kp.publicKey);
|
|
144
|
+
assert.ok(result.verified, 'mandatory-only derived proof verified');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// =====================================================================
|
|
148
|
+
// All Fields Selective
|
|
149
|
+
// =====================================================================
|
|
150
|
+
|
|
151
|
+
test('bbs-2023: derive revealing all fields', () => {
|
|
152
|
+
const kp = makeKeyPair();
|
|
153
|
+
const doc = sampleCredential();
|
|
154
|
+
|
|
155
|
+
const signed = bbs2023Sign({
|
|
156
|
+
document: doc,
|
|
157
|
+
secretKey: kp.secretKey,
|
|
158
|
+
publicKey: kp.publicKey,
|
|
159
|
+
mandatoryPointers: ['/issuer'],
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Reveal everything that's non-mandatory
|
|
163
|
+
const derived = bbs2023Derive({
|
|
164
|
+
document: signed,
|
|
165
|
+
selectivePointers: [
|
|
166
|
+
'/credentialSubject/name',
|
|
167
|
+
'/credentialSubject/age',
|
|
168
|
+
'/credentialSubject/email',
|
|
169
|
+
'/credentialSubject/alumniOf',
|
|
170
|
+
'/issuanceDate',
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const result = bbs2023Verify(derived, kp.publicKey);
|
|
175
|
+
assert.ok(result.verified, 'all-selective derived proof verified');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// =====================================================================
|
|
179
|
+
// No Mandatory Pointers
|
|
180
|
+
// =====================================================================
|
|
181
|
+
|
|
182
|
+
test('bbs-2023: sign with no mandatory pointers', () => {
|
|
183
|
+
const kp = makeKeyPair();
|
|
184
|
+
const doc = sampleCredential();
|
|
185
|
+
|
|
186
|
+
const signed = bbs2023Sign({
|
|
187
|
+
document: doc,
|
|
188
|
+
secretKey: kp.secretKey,
|
|
189
|
+
publicKey: kp.publicKey,
|
|
190
|
+
mandatoryPointers: [],
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const result = bbs2023Verify(signed, kp.publicKey);
|
|
194
|
+
assert.ok(result.verified, 'base proof with no mandatory pointers verified');
|
|
195
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import {
|
|
4
|
+
bbsIetfKeygen,
|
|
5
|
+
bbsIetfSign,
|
|
6
|
+
bbsIetfVerify,
|
|
7
|
+
bbsIetfProofGen,
|
|
8
|
+
bbsIetfProofVerify,
|
|
9
|
+
} from './index.js';
|
|
10
|
+
|
|
11
|
+
// =====================================================================
|
|
12
|
+
// Helper
|
|
13
|
+
// =====================================================================
|
|
14
|
+
|
|
15
|
+
function makeKeyPair(ciphersuite) {
|
|
16
|
+
// Generate a 32-byte IKM
|
|
17
|
+
const ikm = Buffer.alloc(32);
|
|
18
|
+
for (let i = 0; i < 32; i++) ikm[i] = i + 1;
|
|
19
|
+
|
|
20
|
+
const kp = bbsIetfKeygen(ikm, null, ciphersuite);
|
|
21
|
+
return {
|
|
22
|
+
secretKey: Buffer.from(kp.secretKey, 'hex'),
|
|
23
|
+
publicKey: Buffer.from(kp.publicKey, 'hex'),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// =====================================================================
|
|
28
|
+
// KeyGen Tests
|
|
29
|
+
// =====================================================================
|
|
30
|
+
|
|
31
|
+
test('BBS IETF keygen produces deterministic keys', () => {
|
|
32
|
+
const ikm = Buffer.alloc(32, 42);
|
|
33
|
+
const kp1 = bbsIetfKeygen(ikm);
|
|
34
|
+
const kp2 = bbsIetfKeygen(ikm);
|
|
35
|
+
assert.equal(kp1.secretKey, kp2.secretKey, 'same IKM -> same SK');
|
|
36
|
+
assert.equal(kp1.publicKey, kp2.publicKey, 'same IKM -> same PK');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('BBS IETF keygen with random IKM', () => {
|
|
40
|
+
const kp = bbsIetfKeygen();
|
|
41
|
+
assert.ok(kp.secretKey, 'SK is present');
|
|
42
|
+
assert.ok(kp.publicKey, 'PK is present');
|
|
43
|
+
assert.equal(kp.secretKey.length, 64, 'SK is 32 bytes hex');
|
|
44
|
+
assert.equal(kp.publicKey.length, 192, 'PK is 96 bytes hex');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// =====================================================================
|
|
48
|
+
// Sign / Verify Round-Trip (SHA-256)
|
|
49
|
+
// =====================================================================
|
|
50
|
+
|
|
51
|
+
test('BBS IETF sign/verify round-trip (SHA-256)', () => {
|
|
52
|
+
const kp = makeKeyPair('SHA-256');
|
|
53
|
+
const messages = ['message1', 'message2', 'message3'];
|
|
54
|
+
const header = Buffer.from('test-header');
|
|
55
|
+
|
|
56
|
+
const signature = bbsIetfSign(kp.secretKey, kp.publicKey, header, messages, 'SHA-256');
|
|
57
|
+
assert.equal(signature.length, 80, 'signature is 80 bytes');
|
|
58
|
+
|
|
59
|
+
const verified = bbsIetfVerify(kp.publicKey, signature, header, messages, 'SHA-256');
|
|
60
|
+
assert.ok(verified, 'valid signature verifies');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// =====================================================================
|
|
64
|
+
// Sign / Verify Round-Trip (SHAKE-256)
|
|
65
|
+
// =====================================================================
|
|
66
|
+
|
|
67
|
+
test('BBS IETF sign/verify round-trip (SHAKE-256)', () => {
|
|
68
|
+
const ikm = Buffer.alloc(32, 99);
|
|
69
|
+
const kp = bbsIetfKeygen(ikm, null, 'SHAKE-256');
|
|
70
|
+
const sk = Buffer.from(kp.secretKey, 'hex');
|
|
71
|
+
const pk = Buffer.from(kp.publicKey, 'hex');
|
|
72
|
+
|
|
73
|
+
const messages = ['hello', 'world'];
|
|
74
|
+
const signature = bbsIetfSign(sk, pk, null, messages, 'SHAKE-256');
|
|
75
|
+
assert.equal(signature.length, 80);
|
|
76
|
+
|
|
77
|
+
const verified = bbsIetfVerify(pk, signature, null, messages, 'SHAKE-256');
|
|
78
|
+
assert.ok(verified, 'SHAKE-256 signature verifies');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// =====================================================================
|
|
82
|
+
// Wrong Key Rejection
|
|
83
|
+
// =====================================================================
|
|
84
|
+
|
|
85
|
+
test('BBS IETF wrong key rejected', () => {
|
|
86
|
+
const kp1 = makeKeyPair('SHA-256');
|
|
87
|
+
const kp2Raw = bbsIetfKeygen(); // different key (random IKM)
|
|
88
|
+
const kp2 = {
|
|
89
|
+
secretKey: Buffer.from(kp2Raw.secretKey, 'hex'),
|
|
90
|
+
publicKey: Buffer.from(kp2Raw.publicKey, 'hex'),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const messages = ['msg1', 'msg2'];
|
|
94
|
+
const signature = bbsIetfSign(kp1.secretKey, kp1.publicKey, null, messages, 'SHA-256');
|
|
95
|
+
|
|
96
|
+
const verified = bbsIetfVerify(kp2.publicKey, signature, null, messages, 'SHA-256');
|
|
97
|
+
assert.ok(!verified, 'wrong key should not verify');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// =====================================================================
|
|
101
|
+
// Selective Disclosure (ProofGen -> ProofVerify)
|
|
102
|
+
// =====================================================================
|
|
103
|
+
|
|
104
|
+
test('BBS IETF selective disclosure proof', () => {
|
|
105
|
+
const kp = makeKeyPair('SHA-256');
|
|
106
|
+
const messages = ['claim-a', 'claim-b', 'claim-c', 'claim-d'];
|
|
107
|
+
const header = Buffer.from('proof-header');
|
|
108
|
+
const ph = Buffer.from('presentation-header');
|
|
109
|
+
|
|
110
|
+
// Sign all messages
|
|
111
|
+
const signature = bbsIetfSign(kp.secretKey, kp.publicKey, header, messages, 'SHA-256');
|
|
112
|
+
|
|
113
|
+
// Generate proof disclosing messages at indices 0 and 2
|
|
114
|
+
const disclosedIndices = [0, 2];
|
|
115
|
+
const proof = bbsIetfProofGen(
|
|
116
|
+
kp.publicKey, signature, header, ph, messages, disclosedIndices, 'SHA-256'
|
|
117
|
+
);
|
|
118
|
+
assert.ok(proof.length > 0, 'proof generated');
|
|
119
|
+
|
|
120
|
+
// Build disclosed messages map
|
|
121
|
+
const disclosedMessages = {};
|
|
122
|
+
for (const idx of disclosedIndices) {
|
|
123
|
+
disclosedMessages[idx] = messages[idx];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Verify proof
|
|
127
|
+
const verified = bbsIetfProofVerify(
|
|
128
|
+
kp.publicKey, proof, header, ph, disclosedMessages, messages.length, 'SHA-256'
|
|
129
|
+
);
|
|
130
|
+
assert.ok(verified, 'proof verifies with correct disclosed messages');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// =====================================================================
|
|
134
|
+
// Proof with wrong disclosed message
|
|
135
|
+
// =====================================================================
|
|
136
|
+
|
|
137
|
+
test('BBS IETF proof rejects wrong disclosed message', () => {
|
|
138
|
+
const kp = makeKeyPair('SHA-256');
|
|
139
|
+
const messages = ['msg0', 'msg1', 'msg2'];
|
|
140
|
+
const header = Buffer.from('h');
|
|
141
|
+
|
|
142
|
+
const signature = bbsIetfSign(kp.secretKey, kp.publicKey, header, messages, 'SHA-256');
|
|
143
|
+
|
|
144
|
+
const proof = bbsIetfProofGen(
|
|
145
|
+
kp.publicKey, signature, header, null, messages, [1], 'SHA-256'
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Provide wrong message for disclosed index
|
|
149
|
+
const verified = bbsIetfProofVerify(
|
|
150
|
+
kp.publicKey, proof, header, null, { 1: 'wrong-message' }, messages.length, 'SHA-256'
|
|
151
|
+
);
|
|
152
|
+
assert.ok(!verified, 'wrong disclosed message should not verify');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// =====================================================================
|
|
156
|
+
// Cross-ciphersuite: SHA-256 vs SHAKE-256
|
|
157
|
+
// =====================================================================
|
|
158
|
+
|
|
159
|
+
test('BBS IETF cross-ciphersuite: SHA-256 sig not verifiable with SHAKE-256', () => {
|
|
160
|
+
const kp = makeKeyPair('SHA-256');
|
|
161
|
+
const messages = ['test'];
|
|
162
|
+
|
|
163
|
+
const signature = bbsIetfSign(kp.secretKey, kp.publicKey, null, messages, 'SHA-256');
|
|
164
|
+
|
|
165
|
+
// Try verifying with SHAKE-256 — should fail
|
|
166
|
+
const verified = bbsIetfVerify(kp.publicKey, signature, null, messages, 'SHAKE-256');
|
|
167
|
+
assert.ok(!verified, 'SHA-256 signature should not verify under SHAKE-256');
|
|
168
|
+
});
|
package/test_fuzz.mjs
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { strict as assert } from 'assert';
|
|
2
|
+
|
|
3
|
+
// Import NAPI bindings - matching patterns from test.mjs and test_jose.mjs
|
|
4
|
+
import {
|
|
5
|
+
JsonLd,
|
|
6
|
+
BbsBlsSignature2020,
|
|
7
|
+
BbsBlsSignatureProof2020,
|
|
8
|
+
Bls12381G2KeyPair,
|
|
9
|
+
generateJwk,
|
|
10
|
+
generateKeyPair,
|
|
11
|
+
generalEncryptJson,
|
|
12
|
+
decryptJson,
|
|
13
|
+
compactSignJson,
|
|
14
|
+
compactJsonVerify,
|
|
15
|
+
flattenedSignJson,
|
|
16
|
+
jsonVerify,
|
|
17
|
+
JoseNamedCurve,
|
|
18
|
+
JoseKeyEncryption,
|
|
19
|
+
JoseContentEncryption,
|
|
20
|
+
JoseSigningAlgorithm,
|
|
21
|
+
} from './index.js';
|
|
22
|
+
|
|
23
|
+
let passed = 0;
|
|
24
|
+
let failed = 0;
|
|
25
|
+
|
|
26
|
+
async function test(name, fn) {
|
|
27
|
+
try {
|
|
28
|
+
await fn();
|
|
29
|
+
passed++;
|
|
30
|
+
console.log(` PASS: ${name}`);
|
|
31
|
+
} catch (e) {
|
|
32
|
+
failed++;
|
|
33
|
+
console.log(` FAIL: ${name}`);
|
|
34
|
+
console.log(` ${e.message}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function randomJson(depth = 0) {
|
|
39
|
+
if (depth > 3) return Math.random().toString(36);
|
|
40
|
+
const type = Math.floor(Math.random() * 5);
|
|
41
|
+
switch (type) {
|
|
42
|
+
case 0: return null;
|
|
43
|
+
case 1: return Math.random() * 1000 - 500;
|
|
44
|
+
case 2: return Math.random().toString(36).substring(2);
|
|
45
|
+
case 3: {
|
|
46
|
+
const arr = [];
|
|
47
|
+
for (let i = 0; i < Math.floor(Math.random() * 4); i++) arr.push(randomJson(depth + 1));
|
|
48
|
+
return arr;
|
|
49
|
+
}
|
|
50
|
+
case 4: {
|
|
51
|
+
const obj = {};
|
|
52
|
+
for (let i = 0; i < Math.floor(Math.random() * 4); i++) {
|
|
53
|
+
obj[Math.random().toString(36).substring(2, 7)] = randomJson(depth + 1);
|
|
54
|
+
}
|
|
55
|
+
return obj;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('\n=== NAPI Fuzz Tests ===\n');
|
|
61
|
+
|
|
62
|
+
// Category 1: Random JSON to JsonLd operations (should not crash)
|
|
63
|
+
console.log('--- JSON-LD with random inputs ---');
|
|
64
|
+
for (let i = 0; i < 20; i++) {
|
|
65
|
+
await test(`JsonLd.expand random input #${i}`, async () => {
|
|
66
|
+
const jsonld = new JsonLd();
|
|
67
|
+
try {
|
|
68
|
+
await jsonld.expand(randomJson());
|
|
69
|
+
} catch (e) {
|
|
70
|
+
// Errors are fine, crashes are not
|
|
71
|
+
assert.ok(e instanceof Error, 'should throw Error, not crash');
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (let i = 0; i < 10; i++) {
|
|
77
|
+
await test(`JsonLd.compact random input #${i}`, async () => {
|
|
78
|
+
const jsonld = new JsonLd();
|
|
79
|
+
try {
|
|
80
|
+
await jsonld.compact(randomJson(), randomJson());
|
|
81
|
+
} catch (e) {
|
|
82
|
+
assert.ok(e instanceof Error);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < 10; i++) {
|
|
88
|
+
await test(`JsonLd.frame random input #${i}`, async () => {
|
|
89
|
+
const jsonld = new JsonLd();
|
|
90
|
+
try {
|
|
91
|
+
await jsonld.frame(randomJson(), randomJson());
|
|
92
|
+
} catch (e) {
|
|
93
|
+
assert.ok(e instanceof Error);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Category 2: Invalid key pairs to BBS operations
|
|
99
|
+
console.log('\n--- BBS with invalid inputs ---');
|
|
100
|
+
|
|
101
|
+
await test('BbsBlsSignature2020.createProof with no key (verifier-only)', async () => {
|
|
102
|
+
try {
|
|
103
|
+
const suite = new BbsBlsSignature2020();
|
|
104
|
+
await suite.createProof({ document: {}, contexts: {} });
|
|
105
|
+
} catch (e) {
|
|
106
|
+
assert.ok(e instanceof Error);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await test('BbsBlsSignature2020.verifyProof with random proof', async () => {
|
|
111
|
+
try {
|
|
112
|
+
const suite = new BbsBlsSignature2020();
|
|
113
|
+
await suite.verifyProof({ document: randomJson(), contexts: {} });
|
|
114
|
+
} catch (e) {
|
|
115
|
+
assert.ok(e instanceof Error);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Category 3: Empty/null/undefined arguments
|
|
120
|
+
console.log('\n--- Null/undefined arguments ---');
|
|
121
|
+
|
|
122
|
+
await test('JsonLd.expand with null', async () => {
|
|
123
|
+
const jsonld = new JsonLd();
|
|
124
|
+
try { await jsonld.expand(null); } catch (e) { assert.ok(e instanceof Error); }
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await test('JsonLd.expand with undefined', async () => {
|
|
128
|
+
const jsonld = new JsonLd();
|
|
129
|
+
try { await jsonld.expand(undefined); } catch (e) { assert.ok(e instanceof Error); }
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
await test('JsonLd.compact with null context', async () => {
|
|
133
|
+
const jsonld = new JsonLd();
|
|
134
|
+
try { await jsonld.compact({}, null); } catch (e) { assert.ok(e instanceof Error); }
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
await test('generateJwk with invalid enum value', async () => {
|
|
138
|
+
try { generateJwk(999); } catch (e) { assert.ok(e instanceof Error); }
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Category 4: JOSE with random inputs
|
|
142
|
+
console.log('\n--- JOSE with random inputs ---');
|
|
143
|
+
|
|
144
|
+
await test('compactSignJson with random payload', async () => {
|
|
145
|
+
try {
|
|
146
|
+
const key = generateJwk(JoseNamedCurve.P256);
|
|
147
|
+
compactSignJson(JoseSigningAlgorithm.Es256, randomJson(), key);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
assert.ok(e instanceof Error);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await test('compactJsonVerify with random JWS string', async () => {
|
|
154
|
+
try {
|
|
155
|
+
const key = generateJwk(JoseNamedCurve.P256);
|
|
156
|
+
compactJsonVerify('not.a.jws', key);
|
|
157
|
+
} catch (e) {
|
|
158
|
+
assert.ok(e instanceof Error);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
await test('decryptJson with random JWE', async () => {
|
|
163
|
+
try {
|
|
164
|
+
const key = generateJwk(JoseNamedCurve.X25519);
|
|
165
|
+
decryptJson(randomJson(), key);
|
|
166
|
+
} catch (e) {
|
|
167
|
+
assert.ok(e instanceof Error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Category 5: Concurrent calls
|
|
172
|
+
console.log('\n--- Concurrent operations ---');
|
|
173
|
+
|
|
174
|
+
await test('50 concurrent JsonLd.expand calls', async () => {
|
|
175
|
+
const jsonld = new JsonLd();
|
|
176
|
+
const doc = {"@context": {"name": "http://schema.org/name"}, "name": "test"};
|
|
177
|
+
const promises = Array.from({length: 50}, () => jsonld.expand(doc).catch(() => null));
|
|
178
|
+
const results = await Promise.all(promises);
|
|
179
|
+
// All should succeed or gracefully fail
|
|
180
|
+
assert.ok(results.length === 50);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
await test('50 concurrent JOSE sign operations', async () => {
|
|
184
|
+
const key = generateJwk(JoseNamedCurve.P256);
|
|
185
|
+
const payload = {sub: "test", iat: Date.now()};
|
|
186
|
+
const results = [];
|
|
187
|
+
for (let i = 0; i < 50; i++) {
|
|
188
|
+
try {
|
|
189
|
+
results.push(compactSignJson(JoseSigningAlgorithm.Es256, payload, key));
|
|
190
|
+
} catch (e) {
|
|
191
|
+
results.push(null);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
assert.ok(results.length === 50);
|
|
195
|
+
// All should have succeeded
|
|
196
|
+
const successes = results.filter(r => r !== null);
|
|
197
|
+
assert.ok(successes.length === 50, `Expected 50 successes, got ${successes.length}`);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Summary
|
|
201
|
+
console.log(`\n=== Results: ${passed} passed, ${failed} failed out of ${passed + failed} ===`);
|
|
202
|
+
if (failed > 0) process.exit(1);
|