@blamejs/blamejs-shop 0.1.0 → 0.1.1

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +1 -1
  3. package/lib/admin.js +274 -7
  4. package/lib/vendor/MANIFEST.json +2 -2
  5. package/lib/vendor/blamejs/CHANGELOG.md +14 -0
  6. package/lib/vendor/blamejs/README.md +6 -5
  7. package/lib/vendor/blamejs/SECURITY.md +2 -0
  8. package/lib/vendor/blamejs/api-snapshot.json +166 -3
  9. package/lib/vendor/blamejs/lib/cose.js +284 -10
  10. package/lib/vendor/blamejs/lib/crypto.js +119 -0
  11. package/lib/vendor/blamejs/lib/did.js +69 -20
  12. package/lib/vendor/blamejs/lib/mdoc.js +122 -0
  13. package/lib/vendor/blamejs/lib/network-dnssec.js +328 -0
  14. package/lib/vendor/blamejs/lib/network.js +1 -0
  15. package/lib/vendor/blamejs/lib/vc.js +231 -33
  16. package/lib/vendor/blamejs/package.json +1 -1
  17. package/lib/vendor/blamejs/release-notes/v0.12.42.json +18 -0
  18. package/lib/vendor/blamejs/release-notes/v0.12.43.json +18 -0
  19. package/lib/vendor/blamejs/release-notes/v0.12.44.json +18 -0
  20. package/lib/vendor/blamejs/release-notes/v0.12.45.json +18 -0
  21. package/lib/vendor/blamejs/release-notes/v0.12.46.json +18 -0
  22. package/lib/vendor/blamejs/release-notes/v0.12.47.json +18 -0
  23. package/lib/vendor/blamejs/release-notes/v0.12.48.json +22 -0
  24. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +38 -1
  25. package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +101 -2
  26. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-self-test.test.js +74 -0
  27. package/lib/vendor/blamejs/test/layer-0-primitives/did.test.js +29 -0
  28. package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +130 -0
  29. package/lib/vendor/blamejs/test/layer-0-primitives/mdoc.test.js +52 -0
  30. package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +63 -0
  31. package/package.json +1 -1
@@ -170,12 +170,12 @@ async function testValidation() {
170
170
  }
171
171
  check("sign/verify: malformed args throw the right codes", ok);
172
172
 
173
- // Detached payload (nil) is explicitly unsupported in v1.
173
+ // A detached (nil) payload without opts.externalPayload is refused.
174
174
  var protBstr = b.cbor.encode(new Map([[1, -7]]));
175
175
  var detached = b.cbor.encode(new b.cbor.Tag(18, [protBstr, new Map(), null, Buffer.from([0, 0])]));
176
176
  var det = null;
177
177
  try { await b.cose.verify(detached, { algorithms: ["ES256"], publicKey: EC.publicKey }); } catch (e) { det = e; }
178
- check("verify: detached payload refused (v1 attached-only)", det && det.code === "cose/detached-unsupported");
178
+ check("verify: detached payload without externalPayload refused", det && det.code === "cose/detached-no-payload");
179
179
 
180
180
  // Codex P2 on PR #184 — a non-byte payload (text string here) must
181
181
  // be refused, not returned as a non-Buffer.
@@ -192,6 +192,61 @@ async function testValidation() {
192
192
  check("verify: non-map unprotected header refused", bu && bu.code === "cose/malformed");
193
193
  }
194
194
 
195
+ async function testDetachedPayload() {
196
+ var payload = Buffer.from("detached COSE payload", "utf8");
197
+ var det = await b.cose.sign(payload, { alg: "ES256", privateKey: EC.privateKey, detached: true });
198
+ // payload slot must be nil
199
+ var arr = b.cbor.decode(det, { allowedTags: [18] }).value;
200
+ check("detached: payload slot is nil", arr[2] === null);
201
+ var out = await b.cose.verify(det, { algorithms: ["ES256"], publicKey: EC.publicKey, externalPayload: payload });
202
+ check("detached: verify with externalPayload returns it", out.payload.equals(payload));
203
+
204
+ var noPay = null;
205
+ try { await b.cose.verify(det, { algorithms: ["ES256"], publicKey: EC.publicKey }); } catch (e) { noPay = e; }
206
+ check("detached: verify without externalPayload refused", noPay && noPay.code === "cose/detached-no-payload");
207
+
208
+ var wrong = null;
209
+ try { await b.cose.verify(det, { algorithms: ["ES256"], publicKey: EC.publicKey, externalPayload: Buffer.from("x") }); } catch (e) { wrong = e; }
210
+ check("detached: wrong externalPayload fails signature", wrong && wrong.code === "cose/bad-signature");
211
+
212
+ // attached token + externalPayload is ambiguous
213
+ var att = await b.cose.sign(payload, { alg: "ES256", privateKey: EC.privateKey });
214
+ var amb = null;
215
+ try { await b.cose.verify(att, { algorithms: ["ES256"], publicKey: EC.publicKey, externalPayload: payload }); } catch (e) { amb = e; }
216
+ check("detached: externalPayload on an attached token refused", amb && amb.code === "cose/payload-ambiguous");
217
+ }
218
+
219
+ async function testImportKey() {
220
+ // EC2 P-256 COSE_Key built from the JWK, then used to verify.
221
+ var jwk = EC.publicKey.export({ format: "jwk" });
222
+ var coseKey = new Map([[1, 2], [-1, 1], [-2, Buffer.from(jwk.x, "base64url")], [-3, Buffer.from(jwk.y, "base64url")]]);
223
+ var imported = b.cose.importKey(coseKey);
224
+ check("importKey: EC2 P-256 matches the JWK x", imported.export({ format: "jwk" }).x === jwk.x);
225
+ var att = await b.cose.sign(Buffer.from("k"), { alg: "ES256", privateKey: EC.privateKey });
226
+ check("importKey: imported key verifies a COSE_Sign1", (await b.cose.verify(att, { algorithms: ["ES256"], publicKey: imported })).payload.toString() === "k");
227
+
228
+ // OKP Ed25519
229
+ var ejwk = ED.publicKey.export({ format: "jwk" });
230
+ var okp = new Map([[1, 1], [-1, 6], [-2, Buffer.from(ejwk.x, "base64url")]]);
231
+ check("importKey: OKP Ed25519", b.cose.importKey(okp).asymmetricKeyType === "ed25519");
232
+
233
+ // unsupported curve / kty refused
234
+ var bad = null;
235
+ try { b.cose.importKey(new Map([[1, 2], [-1, 99], [-2, Buffer.from(jwk.x, "base64url")], [-3, Buffer.from(jwk.y, "base64url")]])); } catch (e) { bad = e; }
236
+ check("importKey: unsupported EC2 curve refused", bad && bad.code === "cose/unsupported-key");
237
+ var badKty = null;
238
+ try { b.cose.importKey(new Map([[1, 4], [-2, Buffer.from(jwk.x, "base64url")]])); } catch (e) { badKty = e; }
239
+ check("importKey: unsupported kty refused", badKty && (badKty.code === "cose/unsupported-key" || badKty.code === "cose/bad-cose-key"));
240
+
241
+ // secp256k1 (crv id 8) is refused — b.cose has no ES256K path, so
242
+ // accepting it would let it verify under ES256 (alg/curve mis-binding).
243
+ var k1 = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "secp256k1" });
244
+ var k1jwk = k1.publicKey.export({ format: "jwk" });
245
+ var secpBad = null;
246
+ try { b.cose.importKey(new Map([[1, 2], [-1, 8], [-2, Buffer.from(k1jwk.x, "base64url")], [-3, Buffer.from(k1jwk.y, "base64url")]])); } catch (e) { secpBad = e; }
247
+ check("importKey: secp256k1 refused (no ES256K binding)", secpBad && secpBad.code === "cose/unsupported-key");
248
+ }
249
+
195
250
  async function run() {
196
251
  testSurface();
197
252
  testEncrypt0();
@@ -201,6 +256,50 @@ async function run() {
201
256
  await testTamperAndAllowlist();
202
257
  await testCritBypassDefense();
203
258
  await testValidation();
259
+ await testDetachedPayload();
260
+ await testImportKey();
261
+ testMac0();
262
+ }
263
+
264
+ function testMac0() {
265
+ var key = nodeCrypto.randomBytes(32);
266
+ var pt = Buffer.from("cose mac0 payload", "utf8");
267
+ var m = b.cose.mac0(pt, { alg: "HMAC-256/256", key: key });
268
+ check("mac0: tagged COSE_Mac0 (tag 17 → 0xd1)", m[0] === 0xd1);
269
+ var out = b.cose.macVerify0(m, { algorithms: ["HMAC-256/256"], key: key });
270
+ check("mac0: round-trips payload + alg", out.payload.equals(pt) && out.alg === "HMAC-256/256");
271
+
272
+ var wrongKey = null;
273
+ try { b.cose.macVerify0(m, { algorithms: ["HMAC-256/256"], key: nodeCrypto.randomBytes(32) }); } catch (e) { wrongKey = e; }
274
+ check("mac0: wrong key refused", wrongKey && wrongKey.code === "cose/bad-tag");
275
+
276
+ var tampered = Buffer.from(m); tampered[tampered.length - 1] ^= 0xff;
277
+ var tamp = null;
278
+ try { b.cose.macVerify0(tampered, { algorithms: ["HMAC-256/256"], key: key }); } catch (e) { tamp = e; }
279
+ check("mac0: tampered tag refused", tamp && tamp.code === "cose/bad-tag");
280
+
281
+ var notAllowed = null;
282
+ try { b.cose.macVerify0(m, { algorithms: ["HMAC-512/512"], key: key }); } catch (e) { notAllowed = e; }
283
+ check("mac0: alg not in allowlist refused", notAllowed && notAllowed.code === "cose/alg-not-allowed");
284
+
285
+ var md = b.cose.mac0(pt, { alg: "HMAC-384/384", key: key, detached: true });
286
+ check("mac0: detached verify with externalPayload", b.cose.macVerify0(md, { algorithms: ["HMAC-384/384"], key: key, externalPayload: pt }).payload.equals(pt));
287
+ var noPay = null;
288
+ try { b.cose.macVerify0(md, { algorithms: ["HMAC-384/384"], key: key }); } catch (e) { noPay = e; }
289
+ check("mac0: detached without externalPayload refused", noPay && noPay.code === "cose/detached-no-payload");
290
+
291
+ var ma = b.cose.mac0(pt, { alg: "HMAC-512/512", key: key, externalAad: Buffer.from("ctx-A", "utf8") });
292
+ var aadBad = null;
293
+ try { b.cose.macVerify0(ma, { algorithms: ["HMAC-512/512"], key: key, externalAad: Buffer.from("ctx-B", "utf8") }); } catch (e) { aadBad = e; }
294
+ check("mac0: external_aad mismatch refused", aadBad && aadBad.code === "cose/bad-tag");
295
+
296
+ // crit-bypass defense: a COSE_Mac0 marking an unknown critical header
297
+ // is refused (matching b.cose.verify).
298
+ var protCrit = b.cbor.encode(new Map([[1, 5], [2, [99]]]));
299
+ var macCrit = b.cbor.encode(new b.cbor.Tag(17, [protCrit, new Map(), Buffer.from("x", "utf8"), Buffer.alloc(32)]));
300
+ var critErr = null;
301
+ try { b.cose.macVerify0(macCrit, { algorithms: ["HMAC-256/256"], key: key }); } catch (e) { critErr = e; }
302
+ check("mac0: unknown crit header refused", critErr && critErr.code === "cose/crit-unknown");
204
303
  }
205
304
 
206
305
  module.exports = { run: run };
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * Layer 0 — b.crypto.selfTest (FIPS 140-3-style power-on self-test).
4
+ * Confirms the KATs (NIST FIPS 202 SHA3/SHAKE vectors), the AEAD
5
+ * round-trip + tamper-detect, and the PQC pairwise-consistency +
6
+ * negative checks all run and pass, and that the report shape +
7
+ * throwOnFailure contract hold.
8
+ */
9
+
10
+ var b = require("../../index");
11
+ var helpers = require("../helpers");
12
+ var check = helpers.check;
13
+ var nodeCrypto = require("node:crypto");
14
+
15
+ var EXPECTED = [
16
+ "SHA3-512 KAT (FIPS 202)",
17
+ "SHA3-256 KAT (FIPS 202)",
18
+ "SHAKE256 KAT (FIPS 202)",
19
+ "HMAC-SHA3-512 determinism",
20
+ "XChaCha20-Poly1305 round-trip + tamper-detect",
21
+ "ML-KEM-1024 encaps/decaps pairwise consistency",
22
+ "ML-DSA-87 sign/verify + negative",
23
+ "SLH-DSA-SHAKE-256f sign/verify + negative",
24
+ ];
25
+
26
+ function testSurface() {
27
+ check("b.crypto.selfTest is a function", typeof b.crypto.selfTest === "function");
28
+ }
29
+
30
+ function testAllPass() {
31
+ var r = b.crypto.selfTest();
32
+ check("selfTest: ok true", r.ok === true);
33
+ check("selfTest: no failures", r.failures.length === 0);
34
+ check("selfTest: ranAt is an ISO timestamp", typeof r.ranAt === "string" && !isNaN(Date.parse(r.ranAt)));
35
+ // every expected check ran and passed
36
+ var names = r.results.map(function (x) { return x.name; });
37
+ EXPECTED.forEach(function (name) {
38
+ check("selfTest ran + passed: " + name, names.indexOf(name) !== -1 && r.results[names.indexOf(name)].ok === true);
39
+ });
40
+ }
41
+
42
+ function testKatMatchesNistVectors() {
43
+ // Independent cross-check of the KAT references against node's own
44
+ // FIPS-validated SHA3 (the self-test's hash digests are the standard
45
+ // FIPS 202 answers, not framework-vs-framework).
46
+ check("SHA3-512(abc) is the FIPS 202 vector",
47
+ b.crypto.sha3Hash("abc") === nodeCrypto.createHash("sha3-512").update("abc").digest("hex"));
48
+ check("SHA3-512(abc) starts with the published prefix",
49
+ b.crypto.sha3Hash("abc").indexOf("b751850b1a57168a") === 0);
50
+ }
51
+
52
+ function testReportContract() {
53
+ // throwOnFailure:false always returns the structured report.
54
+ var r = b.crypto.selfTest({ throwOnFailure: false });
55
+ check("throwOnFailure:false returns a report", r && Array.isArray(r.results) && typeof r.ok === "boolean");
56
+ // each result carries a name + ok boolean
57
+ check("each result has name + ok", r.results.every(function (x) { return typeof x.name === "string" && typeof x.ok === "boolean"; }));
58
+ }
59
+
60
+ async function run() {
61
+ testSurface();
62
+ testAllPass();
63
+ testKatMatchesNistVectors();
64
+ testReportContract();
65
+ }
66
+
67
+ module.exports = { run: run };
68
+
69
+ if (require.main === module) {
70
+ run().then(
71
+ function () { console.log("[crypto-self-test] OK — " + helpers.getChecks() + " checks passed"); },
72
+ function (e) { console.error("FAIL:", e && e.stack || e); process.exit(1); }
73
+ );
74
+ }
@@ -128,12 +128,41 @@ function testRefusals() {
128
128
  check("RSA key → unsupported", code(function () { b.did.keyToDid(rsa.publicKey); }) === "did/unsupported-key");
129
129
  }
130
130
 
131
+ function testDidJwk() {
132
+ var ec = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
133
+ var ed = nodeCrypto.generateKeyPairSync("ed25519");
134
+
135
+ var dj = b.did.keyToDid(ec.publicKey, { method: "jwk" });
136
+ check("did:jwk: keyToDid produces a did:jwk", dj.indexOf("did:jwk:") === 0);
137
+ check("did:jwk: parse method", b.did.parse(dj).method === "jwk");
138
+ var r = b.did.resolve(dj);
139
+ check("did:jwk: resolves to the key", _spkiEq(r.verificationMethods[0].publicKey, ec.publicKey));
140
+ check("did:jwk: vm type JsonWebKey2020 + publicKeyJwk", r.verificationMethods[0].type === "JsonWebKey2020" && !!r.didDocument.verificationMethod[0].publicKeyJwk);
141
+ check("did:jwk: Ed25519 round-trips", _spkiEq(b.did.resolve(b.did.keyToDid(ed.publicKey, { method: "jwk" })).verificationMethods[0].publicKey, ed.publicKey));
142
+ // keyToDid default is still did:key
143
+ check("did:jwk: keyToDid default is did:key", b.did.keyToDid(ec.publicKey).indexOf("did:key:") === 0);
144
+ // private member is stripped from the produced did:jwk
145
+ var decoded = JSON.parse(Buffer.from(dj.slice("did:jwk:".length), "base64url").toString("utf8"));
146
+ check("did:jwk: no private 'd' member encoded", decoded.d === undefined && typeof decoded.x === "string");
147
+
148
+ function code(fn) { try { fn(); return "NO-THROW"; } catch (e) { return e.code; } }
149
+ check("did:jwk: non-JSON id refused", code(function () { b.did.resolve("did:jwk:" + Buffer.from("not json", "utf8").toString("base64url")); }) === "did/bad-jwk");
150
+ check("did:jwk: unsupported kty refused", code(function () {
151
+ b.did.resolve("did:jwk:" + Buffer.from(JSON.stringify({ kty: "RSA", n: "x", e: "AQAB" }), "utf8").toString("base64url"));
152
+ }) === "did/unsupported-key");
153
+ // keyToDid must also refuse an unsupported key when emitting did:jwk —
154
+ // generation and resolution share the allowlist so the DID round-trips.
155
+ var rsa = nodeCrypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
156
+ check("did:jwk: keyToDid refuses RSA (round-trip guarantee)", code(function () { b.did.keyToDid(rsa.publicKey, { method: "jwk" }); }) === "did/unsupported-key");
157
+ }
158
+
131
159
  async function run() {
132
160
  testSurface();
133
161
  testSpecVector();
134
162
  testRoundTrips();
135
163
  await testCredentialIntegration();
136
164
  testDidWeb();
165
+ testDidJwk();
137
166
  testRefusals();
138
167
  }
139
168
 
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * Layer 0 — b.network.dns.dnssec (local DNSSEC RRSIG verification).
4
+ * The oracles are REAL captured DNSKEY responses (Cloudflare DoH,
5
+ * application/dns-message) for an ECDSA-P256 zone (cloudflare.com) and
6
+ * an RSA/SHA-256 zone (verisign.com): a byte off in the RFC 4034
7
+ * canonicalisation would fail these real-world signatures. Verified at a
8
+ * fixed instant inside each RRSIG's window so the fixture never expires.
9
+ */
10
+
11
+ var b = require("../../index");
12
+ var helpers = require("../helpers");
13
+ var check = helpers.check;
14
+
15
+ // Real `cloudflare.com` DNSKEY response (ECDSA P-256 / alg 13), captured
16
+ // via Cloudflare DoH with the DO bit set.
17
+ var CF_HEX = "000081a000010003000000010a636c6f7564666c61726503636f6d0000300001c00c003000010000071200440100030da09311112cf9138818cd2feae970ebbd4d6a30f6088c25b325a39abbc5cd1197aa098283e5aaf421177c2aa5d714992a9957d1bcc18f98cd71f1f1806b65e148c00c003000010000071200440101030d99db2cc14cabdc33d6d77da63a2f15f71112584f234e8d1dc428e39e8a4a97e1aa271a555dc90701e17e2a4c4b6f120b7c32d44f4ac02bd894cf2d4be7778a19c00c002e000100000712006200300d0200000e106a604d3e6a0fe1be09430a636c6f7564666c61726503636f6d008a40d46e9ff1d7eae7a3824de100af4eba33c5cc88751c29fb7bf1a931d68ab1d25c19c8be0f06da464aebaad3f171be9ed92e20ae1b1bf25e54cc18939a2a5e00002904d0000080000000";
18
+ // Real `verisign.com` DNSKEY response (RSA/SHA-256 / alg 8).
19
+ var VS_HEX = "000081a0000100040000000108766572697369676e03636f6d0000300001c00c0030000100000cb900880100030803010001a24a87ea79dcc74adda5bad2b0ad3e5514b06c30dd14dcfe1ef3503e1591ccd67a486bf4d87e496c9c5f7009f6796eb54324157d5baa5815063816323bd2f1d2846d278c34c551cd4cf67c1adf21427836603e37c4230bc47ce59845fb697f53471a824be691e683580b92020f12c7a8efeb02f12fc475dbdeca65a0bb0a4f6fc00c0030000100000cb900880100030803010001cc288a535db6a1b542355d84ad07d77cd1729f6eb191b24698becb69ec43aaddbc1714102fd9a8745683d211a33bc88103e96a893e83cff7d27bce7cfeafc6ac2b3b85d0f95e2952d8c413a54aaeee378701f8627805ac97a778a0cd323ce1585139a3a84a9e5a28850ed427e8038fd2f0600f96e0188f46f33acede7811d711c00c0030000100000cb901080101030803010001bfb6a7aca1ef6f910e6dc358935f7132dd3a6fc716550739861b28f4c06ab0e6a4d8082bc7615fa898ed12594cb03e8c89d918d3c2c0d1036bfeae7b73f831ac49634c46cdb3d0c307dd53298f32c52fc16764729b133c105a4ec701ba2a3fbbb60ee6cf9c21dcc0ffbb270e3bc5e6bbf4cf1d07d1b00f50655c5d8e7724f951b3ac69d748265351aa014269ffd31678248ce15168aed3fcfbb1a32704743d76b15fc1abdb157c17b3d7deee5a6742019c32ee87a9bce449281122f586964d3f23cd502fbbb2c0504611876c50ca780ea958313dc9f7dec485aa90cd15bf42a66d80da99df2c46c3974ead4f88332a810b83d6e2a416d7ee8318f3c4c671eaa3c00c002e000100000cb901200030080200000e106a3b146f6a13876fd7a408766572697369676e03636f6d0075ae4e787092f2120d8592cf56d3cd87f06813e38aadd90111ba7e656e90ee1c969591cda2ed4838db2648a68326fa04cbd3886ff2fd48f954284bff78459c8a78c4ecb8b2462f0bd1636555dd96b1e83f7cf322d1a4806480eef57e16b65cf5d2229184cab0c573f30a16f5af94b4cd15c05a04c62cd2ca8afc2f39c6067ec2fed95cc044f88f4a746388de20fe58decccda4b1cbc50d8f011cd56055c56c375464b9999e3e04d6a7180ca5fce5801b445cfc9f33b6fdac4f5c9d9714deaa77420ee4f147f1fefb63187230cfb93c2a3c218130c707fb42dfb4d445a33197ebfbf00087d1012f2dc0f4b29857908a2de33aef8ebbcc326cf4e1ef60181cf82a00002904d0000080000000";
20
+
21
+ function _rdName(buf, off) {
22
+ var ls = [], jumped = false, end = off;
23
+ for (;;) {
24
+ var len = buf[off];
25
+ if (len === 0) { off++; if (!jumped) end = off; break; }
26
+ if ((len & 0xc0) === 0xc0) { if (!jumped) end = off + 2; off = ((len & 0x3f) << 8) | buf[off + 1]; jumped = true; continue; }
27
+ off++; ls.push(buf.slice(off, off + len).toString("ascii")); off += len;
28
+ }
29
+ return { name: ls.join(".") + (ls.length ? "." : ""), end: end };
30
+ }
31
+
32
+ // Parse a DNSKEY DoH response → { keys: [{rdata, alg, pubkey}], rrsig }.
33
+ function _parse(hex) {
34
+ var buf = Buffer.from(hex, "hex");
35
+ var qd = buf.readUInt16BE(4), an = buf.readUInt16BE(6), off = 12;
36
+ for (var q = 0; q < qd; q++) off = _rdName(buf, off).end + 4;
37
+ var keys = [], rrsig = null;
38
+ for (var i = 0; i < an; i++) {
39
+ off = _rdName(buf, off).end;
40
+ var type = buf.readUInt16BE(off), rdlen = buf.readUInt16BE(off + 8);
41
+ off += 10;
42
+ var rd = buf.slice(off, off + rdlen); off += rdlen;
43
+ if (type === 48) keys.push({ rdata: rd, alg: rd[3], pubkey: rd.slice(4) });
44
+ else if (type === 46) {
45
+ var sn = _rdName(rd, 18);
46
+ rrsig = { algorithm: rd[2], labels: rd[3], originalTtl: rd.readUInt32BE(4), expiration: rd.readUInt32BE(8), inception: rd.readUInt32BE(12), keyTag: rd.readUInt16BE(16), signerName: sn.name, signature: rd.slice(sn.end) };
47
+ }
48
+ }
49
+ return { keys: keys, rrsig: rrsig };
50
+ }
51
+
52
+ function _vector(hex, zone) {
53
+ var p = _parse(hex);
54
+ var ksk = p.keys.find(function (k) { return b.network.dns.dnssec.keyTag(k.rdata) === p.rrsig.keyTag; });
55
+ return { zone: zone, keys: p.keys, rrsig: p.rrsig, ksk: ksk, at: new Date(p.rrsig.inception * 1000 + 1000) };
56
+ }
57
+
58
+ function testSurface() {
59
+ check("b.network.dns.dnssec.verifyRrset is a function", typeof b.network.dns.dnssec.verifyRrset === "function");
60
+ check("b.network.dns.dnssec.verifyDs is a function", typeof b.network.dns.dnssec.verifyDs === "function");
61
+ check("b.network.dns.dnssec.keyTag is a function", typeof b.network.dns.dnssec.keyTag === "function");
62
+ }
63
+
64
+ function testRealVectors() {
65
+ var cf = _vector(CF_HEX, "cloudflare.com");
66
+ check("keyTag computes the real RRSIG key tag (ECDSA)", cf.ksk !== undefined);
67
+ var ecOut = b.network.dns.dnssec.verifyRrset({ name: cf.zone, type: "DNSKEY", rdatas: cf.keys.map(function (k) { return k.rdata; }), rrsig: cf.rrsig, dnskey: { algorithm: cf.ksk.alg, publicKey: cf.ksk.pubkey }, at: cf.at });
68
+ check("verifyRrset: real cloudflare.com DNSKEY self-sig verifies (ECDSAP256SHA256)", ecOut.ok && ecOut.algorithm === "ECDSAP256SHA256");
69
+
70
+ var vs = _vector(VS_HEX, "verisign.com");
71
+ check("keyTag computes the real RRSIG key tag (RSA)", vs.ksk !== undefined);
72
+ var rsaOut = b.network.dns.dnssec.verifyRrset({ name: vs.zone, type: "DNSKEY", rdatas: vs.keys.map(function (k) { return k.rdata; }), rrsig: vs.rrsig, dnskey: { algorithm: vs.ksk.alg, publicKey: vs.ksk.pubkey }, at: vs.at });
73
+ check("verifyRrset: real verisign.com DNSKEY self-sig verifies (RSASHA256)", rsaOut.ok && rsaOut.algorithm === "RSASHA256");
74
+ }
75
+
76
+ function testRefusals() {
77
+ var cf = _vector(CF_HEX, "cloudflare.com");
78
+ function code(fn) { try { fn(); return "NO-THROW"; } catch (e) { return e.code; } }
79
+ var base = { name: cf.zone, type: "DNSKEY", rrsig: cf.rrsig, dnskey: { algorithm: cf.ksk.alg, publicKey: cf.ksk.pubkey }, at: cf.at };
80
+ function withRdatas(rd) { return Object.assign({}, base, { rdatas: rd }); }
81
+
82
+ var tampered = cf.keys.map(function (k) { return Buffer.from(k.rdata); });
83
+ tampered[0][tampered[0].length - 1] ^= 0xff;
84
+ check("verifyRrset: tampered RRset refused", code(function () { b.network.dns.dnssec.verifyRrset(withRdatas(tampered)); }) === "dnssec/bad-signature");
85
+
86
+ var rd = cf.keys.map(function (k) { return k.rdata; });
87
+ check("verifyRrset: expired RRSIG refused", code(function () { b.network.dns.dnssec.verifyRrset(Object.assign({}, base, { rdatas: rd, at: new Date((cf.rrsig.expiration + 60) * 1000) })); }) === "dnssec/expired");
88
+ check("verifyRrset: not-yet-valid RRSIG refused", code(function () { b.network.dns.dnssec.verifyRrset(Object.assign({}, base, { rdatas: rd, at: new Date((cf.rrsig.inception - 60) * 1000) })); }) === "dnssec/not-yet-valid");
89
+ check("verifyRrset: invalid opts.at refused", code(function () { b.network.dns.dnssec.verifyRrset(Object.assign({}, base, { rdatas: rd, at: new Date("nope") })); }) === "dnssec/bad-at");
90
+ check("verifyRrset: name-bearing RR type refused (not mis-validated)", code(function () { b.network.dns.dnssec.verifyRrset(Object.assign({}, base, { type: "NS", rdatas: rd })); }) === "dnssec/uncanonicalizable-type");
91
+ check("verifyRrset: DNSKEY/RRSIG algorithm mismatch refused", code(function () { b.network.dns.dnssec.verifyRrset(Object.assign({}, base, { rdatas: rd, dnskey: { algorithm: 8, publicKey: cf.ksk.pubkey } })); }) === "dnssec/alg-mismatch");
92
+ }
93
+
94
+ function testVerifyDs() {
95
+ var cf = _vector(CF_HEX, "cloudflare.com");
96
+ var nodeCrypto = require("node:crypto");
97
+ // Build the SHA-256 DS digest over (canonical owner name || DNSKEY rdata),
98
+ // then confirm verifyDs accepts it and rejects a tampered digest / key tag.
99
+ function canonName(name) {
100
+ var n = name.replace(/\.$/, ""), parts = [];
101
+ n.split(".").forEach(function (l) { var b2 = Buffer.from(l.toLowerCase(), "ascii"); parts.push(Buffer.from([b2.length]), b2); });
102
+ parts.push(Buffer.from([0]));
103
+ return Buffer.concat(parts);
104
+ }
105
+ var tag = b.network.dns.dnssec.keyTag(cf.ksk.rdata);
106
+ var digest = nodeCrypto.createHash("sha256").update(Buffer.concat([canonName("cloudflare.com"), cf.ksk.rdata])).digest();
107
+ var ds = { keyTag: tag, algorithm: cf.ksk.alg, digestType: 2, digest: digest };
108
+ check("verifyDs: matching DS accepted", b.network.dns.dnssec.verifyDs({ ownerName: "cloudflare.com", dnskeyRdata: cf.ksk.rdata, ds: ds }).ok === true);
109
+
110
+ function code(fn) { try { fn(); return "NO-THROW"; } catch (e) { return e.code; } }
111
+ var badDigest = Buffer.from(digest); badDigest[0] ^= 0xff;
112
+ check("verifyDs: tampered digest refused", code(function () { b.network.dns.dnssec.verifyDs({ ownerName: "cloudflare.com", dnskeyRdata: cf.ksk.rdata, ds: Object.assign({}, ds, { digest: badDigest }) }); }) === "dnssec/ds-mismatch");
113
+ check("verifyDs: key-tag mismatch refused", code(function () { b.network.dns.dnssec.verifyDs({ ownerName: "cloudflare.com", dnskeyRdata: cf.ksk.rdata, ds: Object.assign({}, ds, { keyTag: (tag + 1) & 0xffff }) }); }) === "dnssec/keytag-mismatch");
114
+ }
115
+
116
+ async function run() {
117
+ testSurface();
118
+ testRealVectors();
119
+ testRefusals();
120
+ testVerifyDs();
121
+ }
122
+
123
+ module.exports = { run: run };
124
+
125
+ if (require.main === module) {
126
+ run().then(
127
+ function () { console.log("[dnssec] OK — " + helpers.getChecks() + " checks passed"); },
128
+ function (e) { console.error("FAIL:", e && e.stack || e); process.exit(1); }
129
+ );
130
+ }
@@ -76,6 +76,14 @@ async function _makeMdoc(cert, opts) {
76
76
  ["validUntil", validUntilTag],
77
77
  ])],
78
78
  ]);
79
+ // Optional deviceKeyInfo.deviceKey (a COSE_Key) for device-auth tests.
80
+ if (opts.deviceJwk) {
81
+ mso.set("deviceKeyInfo", new Map([["deviceKey", new Map([
82
+ [1, 2], [-1, 1],
83
+ [-2, Buffer.from(opts.deviceJwk.x, "base64url")],
84
+ [-3, Buffer.from(opts.deviceJwk.y, "base64url")],
85
+ ])]]));
86
+ }
79
87
  var payload = cbor.encode(new cbor.Tag(24, cbor.encode(mso)));
80
88
  var signed = await b.cose.sign(payload, { alg: opts.alg || "ES256", privateKey: cert.key, unprotectedHeaders: { 33: cert.certDer } });
81
89
  var issuerAuth = cbor.decode(signed, { allowedTags: [18, 24] }).value;
@@ -212,12 +220,56 @@ async function testChainAndInputGuards() {
212
220
  check("verify: missing algorithms refused", e4 && e4.code === "mdoc/algorithms-required");
213
221
  }
214
222
 
223
+ async function testDeviceAuth() {
224
+ var cert = _makeCert();
225
+ var device = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
226
+ var deviceJwk = device.publicKey.export({ format: "jwk" });
227
+ // issuer-signed mdoc carrying the device key in the MSO
228
+ var mdoc = await _makeMdoc(cert, { deviceJwk: deviceJwk });
229
+ var issuer = await b.mdoc.verifyIssuerSigned(mdoc, { algorithms: ["ES256"] });
230
+ check("verifyIssuerSigned: returns the deviceKey", issuer.deviceKey instanceof Map || (issuer.deviceKey && typeof issuer.deviceKey === "object"));
231
+
232
+ // build a DeviceSigned: detached COSE_Sign1 over DeviceAuthentication
233
+ var sessionTranscript = ["DE-bytes", "ER-bytes", ["handover", 1]];
234
+ var deviceNsBytes = new cbor.Tag(24, cbor.encode(new Map()));
235
+ var da = ["DeviceAuthentication", sessionTranscript, DOCTYPE, deviceNsBytes];
236
+ var daBytes = cbor.encode(new cbor.Tag(24, cbor.encode(da)));
237
+ var sig = await b.cose.sign(daBytes, { alg: "ES256", privateKey: device.privateKey, detached: true });
238
+ var sigArr = cbor.decode(sig, { allowedTags: [18, 24] }).value;
239
+ var deviceSigned = new Map([["nameSpaces", deviceNsBytes], ["deviceAuth", new Map([["deviceSignature", sigArr]])]]);
240
+
241
+ var out = await b.mdoc.verifyDeviceAuth({ deviceKey: issuer.deviceKey, deviceSigned: deviceSigned, docType: DOCTYPE, sessionTranscript: sessionTranscript, algorithms: ["ES256"] });
242
+ check("verifyDeviceAuth: verifies with the MSO device key", out.docType === DOCTYPE && out.alg === "ES256");
243
+
244
+ // wrong session transcript → signature fails (binding works)
245
+ var e1 = null;
246
+ try { await b.mdoc.verifyDeviceAuth({ deviceKey: issuer.deviceKey, deviceSigned: deviceSigned, docType: DOCTYPE, sessionTranscript: ["tampered"], algorithms: ["ES256"] }); } catch (e) { e1 = e; }
247
+ check("verifyDeviceAuth: wrong sessionTranscript refused", e1 && e1.code === "cose/bad-signature");
248
+
249
+ // wrong docType → signature fails
250
+ var e2 = null;
251
+ try { await b.mdoc.verifyDeviceAuth({ deviceKey: issuer.deviceKey, deviceSigned: deviceSigned, docType: "org.iso.18013.5.1.other", sessionTranscript: sessionTranscript, algorithms: ["ES256"] }); } catch (e) { e2 = e; }
252
+ check("verifyDeviceAuth: wrong docType refused", e2 && e2.code === "cose/bad-signature");
253
+
254
+ // MAC variant → unsupported (deferred)
255
+ var dsMac = new Map([["nameSpaces", deviceNsBytes], ["deviceAuth", new Map([["deviceMac", [Buffer.alloc(3), new Map(), null, Buffer.alloc(8)]]])]]);
256
+ var e3 = null;
257
+ try { await b.mdoc.verifyDeviceAuth({ deviceKey: issuer.deviceKey, deviceSigned: dsMac, docType: DOCTYPE, sessionTranscript: sessionTranscript, algorithms: ["ES256"] }); } catch (e) { e3 = e; }
258
+ check("verifyDeviceAuth: MAC variant refused (deferred)", e3 && e3.code === "mdoc/device-mac-unsupported");
259
+
260
+ // missing sessionTranscript
261
+ var e4 = null;
262
+ try { await b.mdoc.verifyDeviceAuth({ deviceKey: issuer.deviceKey, deviceSigned: deviceSigned, docType: DOCTYPE, algorithms: ["ES256"] }); } catch (e) { e4 = e; }
263
+ check("verifyDeviceAuth: missing sessionTranscript refused", e4 && e4.code === "mdoc/no-session-transcript");
264
+ }
265
+
215
266
  async function run() {
216
267
  testSurface();
217
268
  await testRoundTrip();
218
269
  await testDigestAndSignatureRefusals();
219
270
  await testValidityAndDocType();
220
271
  await testChainAndInputGuards();
272
+ await testDeviceAuth();
221
273
  }
222
274
 
223
275
  module.exports = { run: run };
@@ -170,12 +170,75 @@ async function testTemporalAndStructural() {
170
170
  check("verify: object issuer id extracted", oo.issuer === "did:example:obj");
171
171
  }
172
172
 
173
+ async function testPresentation() {
174
+ var HOLDER = nodeCrypto.generateKeyPairSync("ed25519");
175
+ var holderId = "did:example:holder";
176
+ var jws = await b.vc.issue(_cred(), { securing: "jose", alg: "ES256", privateKey: EC.privateKey });
177
+
178
+ // jose VP wrapping a VC, with nonce + audience holder-binding
179
+ var vp = await b.vc.present({
180
+ credentials: [jws], holder: holderId, securing: "jose", alg: "EdDSA", privateKey: HOLDER.privateKey,
181
+ nonce: "chal-1", audience: "https://verifier.example",
182
+ });
183
+ check("present: VP is a compact JWS", typeof vp === "string" && vp.split(".").length === 3);
184
+ check("present: typ vp+jwt", JSON.parse(Buffer.from(vp.split(".")[0], "base64url").toString("utf8")).typ === "vp+jwt");
185
+
186
+ var out = await b.vc.verifyPresentation(vp, {
187
+ algorithms: ["EdDSA"], publicKey: HOLDER.publicKey, expectedHolder: holderId,
188
+ nonce: "chal-1", audience: "https://verifier.example",
189
+ verifyCredentials: true, credentialOpts: { algorithms: ["ES256"], publicKey: EC.publicKey },
190
+ });
191
+ check("verifyPresentation: holder returned", out.holder === holderId && out.securing === "jose");
192
+ check("verifyPresentation: enclosed VC verified", out.credentials.length === 1 && out.credentials[0].credential.credentialSubject.degree === "BS");
193
+
194
+ // cose VP
195
+ var coseVc = await b.vc.issue(_cred(), { securing: "cose", alg: "ES256", privateKey: EC.privateKey });
196
+ var vpc = await b.vc.present({ credentials: [coseVc], holder: holderId, securing: "cose", alg: "EdDSA", privateKey: HOLDER.privateKey });
197
+ var outc = await b.vc.verifyPresentation(vpc, { algorithms: ["EdDSA"], publicKey: HOLDER.publicKey, verifyCredentials: true, credentialOpts: { algorithms: ["ES256"], publicKey: EC.publicKey } });
198
+ check("cose VP: round-trips + enclosed VC verified", outc.holder === holderId && outc.credentials.length === 1);
199
+
200
+ // refusals
201
+ var e1 = null;
202
+ try { await b.vc.verifyPresentation(vp, { algorithms: ["EdDSA"], publicKey: HOLDER.publicKey, nonce: "wrong" }); } catch (e) { e1 = e; }
203
+ check("verifyPresentation: nonce mismatch refused", e1 && e1.code === "vc/nonce-mismatch");
204
+ var e2 = null;
205
+ try { await b.vc.verifyPresentation(vp, { algorithms: ["EdDSA"], publicKey: HOLDER.publicKey, audience: "https://other" }); } catch (e) { e2 = e; }
206
+ check("verifyPresentation: audience mismatch refused", e2 && e2.code === "vc/audience-mismatch");
207
+ var e3 = null;
208
+ try { await b.vc.verifyPresentation(vp, { algorithms: ["EdDSA"], publicKey: HOLDER.publicKey, expectedHolder: "did:example:other" }); } catch (e) { e3 = e; }
209
+ check("verifyPresentation: holder mismatch refused", e3 && e3.code === "vc/holder-mismatch");
210
+ // verifying the VP with the wrong typ (a VC) must fail typ check
211
+ var e4 = null;
212
+ try { await b.vc.verifyPresentation(jws, { algorithms: ["ES256"], publicKey: EC.publicKey }); } catch (e) { e4 = e; }
213
+ check("verifyPresentation: a VC (vc+jwt) is refused as a VP", e4 && e4.code === "vc/bad-typ");
214
+ // a VP cannot be verified as a VC
215
+ var e5 = null;
216
+ try { await b.vc.verify(vp, { algorithms: ["EdDSA"], publicKey: HOLDER.publicKey }); } catch (e) { e5 = e; }
217
+ check("verify: a VP (vp+jwt) is refused as a VC", e5 && e5.code === "vc/bad-typ");
218
+ // present requires credentials + holder
219
+ var e6 = null;
220
+ try { await b.vc.present({ credentials: [], holder: holderId, securing: "jose", alg: "EdDSA", privateKey: HOLDER.privateKey }); } catch (e) { e6 = e; }
221
+ check("present: empty credentials refused", e6 && e6.code === "vc/no-credentials");
222
+
223
+ // A holder-signed VP with a NON-ARRAY verifiableCredential must fail
224
+ // closed (not coerce to [] and skip credential verification).
225
+ var badVp = { "@context": ["https://www.w3.org/ns/credentials/v2"], type: ["VerifiablePresentation"], holder: holderId, verifiableCredential: { foo: "bar" } };
226
+ var h = Buffer.from(JSON.stringify({ alg: "EdDSA", typ: "vp+jwt" }), "utf8").toString("base64url");
227
+ var p = Buffer.from(JSON.stringify(badVp), "utf8").toString("base64url");
228
+ var si = h + "." + p;
229
+ var badVpJws = si + "." + nodeCrypto.sign(null, Buffer.from(si, "ascii"), HOLDER.privateKey).toString("base64url");
230
+ var e7 = null;
231
+ try { await b.vc.verifyPresentation(badVpJws, { algorithms: ["EdDSA"], publicKey: HOLDER.publicKey, verifyCredentials: true, credentialOpts: { algorithms: ["ES256"], publicKey: EC.publicKey } }); } catch (e) { e7 = e; }
232
+ check("verifyPresentation: non-array verifiableCredential refused (no bypass)", e7 && e7.code === "vc/bad-presentation");
233
+ }
234
+
173
235
  async function run() {
174
236
  testSurface();
175
237
  await testJoseRoundTrip();
176
238
  await testCoseRoundTrip();
177
239
  await testRefusals();
178
240
  await testTemporalAndStructural();
241
+ await testPresentation();
179
242
  }
180
243
 
181
244
  module.exports = { run: run };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Open-source framework built on blamejs. Vendored stack, zero npm runtime deps, PQC-first crypto, security-on by default.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {