@blamejs/blamejs-shop 0.0.129 → 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 (127) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +1 -1
  3. package/lib/admin.js +275 -9
  4. package/lib/affiliates.js +4 -3
  5. package/lib/analytics.js +3 -2
  6. package/lib/api-keys.js +1 -1
  7. package/lib/assembly-instructions.js +2 -1
  8. package/lib/auto-replenish.js +4 -3
  9. package/lib/backorder.js +2 -1
  10. package/lib/business-hours.js +8 -1
  11. package/lib/carrier-accounts.js +1 -1
  12. package/lib/carrier-rates.js +1 -1
  13. package/lib/cart-abandonment.js +3 -2
  14. package/lib/cart-bulk-ops.js +2 -1
  15. package/lib/cart-recovery.js +5 -4
  16. package/lib/cart.js +6 -2
  17. package/lib/catalog-drafts.js +1 -1
  18. package/lib/click-and-collect.js +3 -2
  19. package/lib/clickstream.js +4 -3
  20. package/lib/config.js +2 -1
  21. package/lib/cookie-consent.js +2 -1
  22. package/lib/credit-limits.js +2 -1
  23. package/lib/currency-display.js +2 -1
  24. package/lib/customer-activity.js +3 -2
  25. package/lib/customer-impersonation.js +3 -3
  26. package/lib/customer-merge.js +4 -3
  27. package/lib/customer-portal.js +4 -4
  28. package/lib/customer-risk-profile.js +2 -1
  29. package/lib/customer-segments.js +2 -1
  30. package/lib/customer-surveys.js +6 -3
  31. package/lib/delivery-estimate.js +2 -2
  32. package/lib/demand-forecast.js +2 -1
  33. package/lib/discount-analytics.js +2 -2
  34. package/lib/dunning.js +4 -1
  35. package/lib/email-warmup.js +6 -1
  36. package/lib/email.js +1 -8
  37. package/lib/error-log.js +3 -2
  38. package/lib/event-log.js +3 -2
  39. package/lib/fraud-screen.js +3 -1
  40. package/lib/fulfillment-sla.js +3 -1
  41. package/lib/index.js +11 -3
  42. package/lib/inventory-allocations.js +3 -0
  43. package/lib/inventory-snapshots.js +2 -1
  44. package/lib/invoice-renderer.js +2 -1
  45. package/lib/line-gift-wrap.js +6 -1
  46. package/lib/live-chat.js +2 -1
  47. package/lib/loyalty-redemption.js +2 -1
  48. package/lib/newsletter.js +6 -1
  49. package/lib/operator-activity-feed.js +4 -3
  50. package/lib/operator-sessions.js +7 -7
  51. package/lib/order-exchanges.js +1 -0
  52. package/lib/order-timeline.js +2 -1
  53. package/lib/payment-retries.js +2 -1
  54. package/lib/payment.js +5 -4
  55. package/lib/pixel-events.js +6 -5
  56. package/lib/preorder.js +2 -1
  57. package/lib/print-queue.js +2 -1
  58. package/lib/product-compare.js +2 -1
  59. package/lib/product-qa.js +2 -1
  60. package/lib/push-notifications.js +6 -5
  61. package/lib/recently-viewed.js +7 -2
  62. package/lib/recommendations.js +7 -2
  63. package/lib/referral-leaderboard.js +2 -1
  64. package/lib/refund-automation.js +1 -1
  65. package/lib/refund-policy.js +1 -1
  66. package/lib/reorder-reminders.js +2 -1
  67. package/lib/reorder-thresholds.js +2 -1
  68. package/lib/robots-config.js +1 -0
  69. package/lib/sales-reports.js +17 -14
  70. package/lib/sales-tax-filings.js +2 -1
  71. package/lib/save-for-later.js +2 -1
  72. package/lib/search-suggestions.js +1 -1
  73. package/lib/shipping-insurance.js +2 -1
  74. package/lib/shipping-labels.js +3 -2
  75. package/lib/shipping-zones.js +1 -0
  76. package/lib/shrinkage-report.js +9 -8
  77. package/lib/sms-dispatcher.js +6 -5
  78. package/lib/stock-alerts.js +1 -1
  79. package/lib/stock-receipts.js +2 -1
  80. package/lib/store-credit.js +2 -1
  81. package/lib/storefront-forms.js +1 -1
  82. package/lib/storefront.js +93 -112
  83. package/lib/subscription-analytics.js +7 -2
  84. package/lib/subscription-controls.js +9 -8
  85. package/lib/subscription-gifts.js +2 -1
  86. package/lib/subscriptions.js +2 -0
  87. package/lib/support-tickets.js +4 -4
  88. package/lib/tax-cert-renewals.js +2 -1
  89. package/lib/tax-remittance.js +2 -1
  90. package/lib/theme-assets.js +1 -1
  91. package/lib/vendor/MANIFEST.json +2 -2
  92. package/lib/vendor/blamejs/CHANGELOG.md +16 -0
  93. package/lib/vendor/blamejs/README.md +6 -4
  94. package/lib/vendor/blamejs/SECURITY.md +2 -0
  95. package/lib/vendor/blamejs/api-snapshot.json +255 -2
  96. package/lib/vendor/blamejs/index.js +1 -0
  97. package/lib/vendor/blamejs/lib/cose.js +284 -10
  98. package/lib/vendor/blamejs/lib/crypto.js +119 -0
  99. package/lib/vendor/blamejs/lib/did.js +416 -0
  100. package/lib/vendor/blamejs/lib/mdoc.js +122 -0
  101. package/lib/vendor/blamejs/lib/network-dnssec.js +328 -0
  102. package/lib/vendor/blamejs/lib/network.js +1 -0
  103. package/lib/vendor/blamejs/lib/vc.js +231 -33
  104. package/lib/vendor/blamejs/package.json +1 -1
  105. package/lib/vendor/blamejs/release-notes/v0.12.41.json +18 -0
  106. package/lib/vendor/blamejs/release-notes/v0.12.42.json +18 -0
  107. package/lib/vendor/blamejs/release-notes/v0.12.43.json +18 -0
  108. package/lib/vendor/blamejs/release-notes/v0.12.44.json +18 -0
  109. package/lib/vendor/blamejs/release-notes/v0.12.45.json +18 -0
  110. package/lib/vendor/blamejs/release-notes/v0.12.46.json +18 -0
  111. package/lib/vendor/blamejs/release-notes/v0.12.47.json +18 -0
  112. package/lib/vendor/blamejs/release-notes/v0.12.48.json +22 -0
  113. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +47 -2
  114. package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +101 -2
  115. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-self-test.test.js +74 -0
  116. package/lib/vendor/blamejs/test/layer-0-primitives/did.test.js +176 -0
  117. package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +130 -0
  118. package/lib/vendor/blamejs/test/layer-0-primitives/mdoc.test.js +52 -0
  119. package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +63 -0
  120. package/lib/vendor-invoices.js +1 -1
  121. package/lib/webhook-receiver.js +8 -2
  122. package/lib/webhook-subscriptions.js +1 -1
  123. package/lib/webhooks.js +6 -5
  124. package/lib/winback-campaigns.js +2 -1
  125. package/lib/wishlist-alerts.js +2 -1
  126. package/lib/wishlist-digest.js +2 -1
  127. package/package.json +1 -1
@@ -53,6 +53,7 @@
53
53
 
54
54
  var nodeCrypto = require("node:crypto");
55
55
  var cbor = require("./cbor");
56
+ var bCrypto = require("./crypto");
56
57
  var validateOpts = require("./validate-opts");
57
58
  var { defineClass } = require("./framework-error");
58
59
 
@@ -143,6 +144,7 @@ function _toBeSigned(protectedBstr, externalAad, payload) {
143
144
  * externalAad?: Buffer, // default empty — bound into the signature
144
145
  * unprotectedHeaders?: object, // extra unprotected map entries (numeric keys)
145
146
  * protectedHeaders?: object, // extra INTEGRITY-PROTECTED map entries (numeric keys); label 1 (alg) is reserved
147
+ * detached?: boolean, // emit a nil payload (RFC 9052 §4.1) — signature still covers it; caller transmits the payload separately
146
148
  * }
147
149
  *
148
150
  * @example
@@ -152,7 +154,7 @@ function _toBeSigned(protectedBstr, externalAad, payload) {
152
154
  */
153
155
  async function sign(payload, opts) {
154
156
  validateOpts.requireObject(opts, "cose.sign", CoseError);
155
- validateOpts(opts, ["alg", "privateKey", "kid", "contentType", "externalAad", "unprotectedHeaders", "protectedHeaders"], "cose.sign");
157
+ validateOpts(opts, ["alg", "privateKey", "kid", "contentType", "externalAad", "unprotectedHeaders", "protectedHeaders", "detached"], "cose.sign");
156
158
  if (SIGNABLE.indexOf(opts.alg) === -1) {
157
159
  throw new CoseError("cose/unsignable-alg",
158
160
  "cose.sign: alg must be one of " + SIGNABLE.join(" / ") +
@@ -213,7 +215,10 @@ async function sign(payload, opts) {
213
215
  ? nodeCrypto.sign(null, toBeSigned, key)
214
216
  : nodeCrypto.sign(params.nodeAlg, toBeSigned, { key: key, dsaEncoding: params.dsaEncoding });
215
217
 
216
- var sign1 = [protectedBstr, unprot, payloadBytes, signature];
218
+ // Detached payload (RFC 9052 §4.1): the COSE_Sign1 carries nil in the
219
+ // payload slot; the signature still covers the payload (above), and the
220
+ // caller transmits / re-supplies it out of band as externalPayload.
221
+ var sign1 = [protectedBstr, unprot, opts.detached ? null : payloadBytes, signature];
217
222
  return cbor.encode(new cbor.Tag(COSE_SIGN1_TAG, sign1));
218
223
  }
219
224
 
@@ -237,6 +242,7 @@ async function sign(payload, opts) {
237
242
  * publicKey?: object, // the verification key (KeyObject / PEM)
238
243
  * keyResolver?: function, // (protectedHeaders, unprotectedHeaders) → key
239
244
  * externalAad?: Buffer, // must match what was signed
245
+ * externalPayload?: Buffer, // required when the COSE_Sign1 payload is detached (nil); bound into the Sig_structure
240
246
  * maxBytes?: number, // forwarded to b.cbor.decode
241
247
  * maxDepth?: number,
242
248
  * }
@@ -247,7 +253,7 @@ async function sign(payload, opts) {
247
253
  */
248
254
  async function verify(coseSign1, opts) {
249
255
  validateOpts.requireObject(opts, "cose.verify", CoseError);
250
- validateOpts(opts, ["algorithms", "publicKey", "keyResolver", "externalAad", "maxBytes", "maxDepth"], "cose.verify");
256
+ validateOpts(opts, ["algorithms", "publicKey", "keyResolver", "externalAad", "externalPayload", "maxBytes", "maxDepth"], "cose.verify");
251
257
  if (!Array.isArray(opts.algorithms) || opts.algorithms.length === 0) {
252
258
  throw new CoseError("cose/algorithms-required",
253
259
  "cose.verify: opts.algorithms is required (no defaults — name the accepted algorithms)");
@@ -278,14 +284,23 @@ async function verify(coseSign1, opts) {
278
284
  if (!Buffer.isBuffer(protectedBstr) || !Buffer.isBuffer(signature)) {
279
285
  throw new CoseError("cose/malformed", "cose.verify: protected header and signature must be byte strings");
280
286
  }
287
+ // Detached payload (RFC 9052 §4.1): a nil payload slot means the caller
288
+ // must supply the payload out of band via opts.externalPayload, which
289
+ // is then bound into the Sig_structure. Supplying externalPayload for
290
+ // an attached (non-nil) token is ambiguous and refused.
281
291
  if (payload === null || payload === undefined) {
282
- throw new CoseError("cose/detached-unsupported",
283
- "cose.verify: detached payload (nil) is not supported in v1 — attached payload only");
284
- }
285
- // COSE_Sign1 payload is a bstr (RFC 9052 §4.2) — refuse a non-byte
286
- // payload rather than return a value that violates the documented
287
- // { payload: Buffer } shape.
288
- if (!Buffer.isBuffer(payload)) {
292
+ if (opts.externalPayload == null) {
293
+ throw new CoseError("cose/detached-no-payload",
294
+ "cose.verify: COSE_Sign1 has a detached (nil) payload — pass opts.externalPayload to verify it");
295
+ }
296
+ payload = _bstr(opts.externalPayload);
297
+ } else if (opts.externalPayload != null) {
298
+ throw new CoseError("cose/payload-ambiguous",
299
+ "cose.verify: opts.externalPayload was supplied but the COSE_Sign1 carries an attached payload");
300
+ } else if (!Buffer.isBuffer(payload)) {
301
+ // COSE_Sign1 payload is a bstr (RFC 9052 §4.2) — refuse a non-byte
302
+ // payload rather than return a value that violates the documented
303
+ // { payload: Buffer } shape.
289
304
  throw new CoseError("cose/malformed", "cose.verify: payload must be a byte string (bstr)");
290
305
  }
291
306
  // The unprotected header is a CBOR map — refuse a non-map rather
@@ -534,12 +549,271 @@ function decrypt0(coseEncrypt0, opts) {
534
549
  return { plaintext: pt, alg: algName, protectedHeaders: protMap, unprotectedHeaders: unprotected };
535
550
  }
536
551
 
552
+ // ---- COSE_Mac0 (RFC 9052 §6.2) — single shared-key MAC ----
553
+
554
+ var COSE_MAC0_TAG = 17; // allow:raw-byte-literal — RFC 9052 COSE_Mac0 CBOR tag
555
+ // HMAC algorithms (RFC 9053 §3.1). Only the full-length tags are offered —
556
+ // the truncated HMAC 256/64 (id 4) is omitted. HMAC is symmetric, so its
557
+ // post-quantum strength is fine; these are the COSE-standard MAC algs.
558
+ var HMAC_NAME_TO_ID = { "HMAC-256/256": 5, "HMAC-384/384": 6, "HMAC-512/512": 7 }; // allow:raw-byte-literal — COSE HMAC algorithm ids (RFC 9053)
559
+ var HMAC_ID_TO_NAME = {};
560
+ Object.keys(HMAC_NAME_TO_ID).forEach(function (k) { HMAC_ID_TO_NAME[HMAC_NAME_TO_ID[k]] = k; });
561
+ function _hmacHash(algId) {
562
+ switch (algId) {
563
+ case 5: return "sha256";
564
+ case 6: return "sha384";
565
+ case 7: return "sha512";
566
+ default: throw new CoseError("cose/unknown-alg", "cose: unrecognized HMAC COSE alg id " + algId);
567
+ }
568
+ }
569
+
570
+ // MAC_structure (§6.3) = [ "MAC0", body_protected (bstr), external_aad (bstr), payload (bstr) ].
571
+ function _macStructure(protectedBstr, externalAad, payload) {
572
+ return cbor.encode(["MAC0", protectedBstr, externalAad, payload]);
573
+ }
574
+
575
+ /**
576
+ * @primitive b.cose.mac0
577
+ * @signature b.cose.mac0(payload, opts)
578
+ * @since 0.12.47
579
+ * @status stable
580
+ * @related b.cose.macVerify0, b.cose.sign
581
+ *
582
+ * Produce a tagged COSE_Mac0 (RFC 9052 §6.2) — a single shared-key MAC
583
+ * over <code>payload</code>. The MAC is HMAC-SHA-256 / 384 / 512 (the
584
+ * COSE-standard MAC algorithms; HMAC is symmetric, so post-quantum
585
+ * strength is preserved). Use when both parties hold a shared key (e.g.
586
+ * an ECDH-derived key) and a non-repudiable signature is not wanted.
587
+ * <code>detached: true</code> emits a nil payload, verified later with
588
+ * <code>opts.externalPayload</code>.
589
+ *
590
+ * @opts
591
+ * {
592
+ * alg: string, // "HMAC-256/256" | "HMAC-384/384" | "HMAC-512/512"
593
+ * key: Buffer, // shared symmetric key
594
+ * externalAad?: Buffer, // bound into the MAC
595
+ * detached?: boolean, // emit a nil payload (caller re-supplies it on verify)
596
+ * unprotectedHeaders?: object,
597
+ * }
598
+ *
599
+ * @example
600
+ * var mac = b.cose.mac0(Buffer.from("data"), { alg: "HMAC-256/256", key: sharedKey });
601
+ */
602
+ function mac0(payload, opts) {
603
+ validateOpts.requireObject(opts, "cose.mac0", CoseError);
604
+ validateOpts(opts, ["alg", "key", "externalAad", "detached", "unprotectedHeaders"], "cose.mac0");
605
+ if (!(opts.alg in HMAC_NAME_TO_ID)) {
606
+ throw new CoseError("cose/unsignable-alg", "cose.mac0: alg must be one of " + Object.keys(HMAC_NAME_TO_ID).join(" / "));
607
+ }
608
+ var key = _bstr(opts.key);
609
+ var algId = HMAC_NAME_TO_ID[opts.alg];
610
+ var protMap = new Map();
611
+ protMap.set(HDR_ALG, algId);
612
+ var protectedBstr = cbor.encode(protMap);
613
+
614
+ var unprot = new Map();
615
+ if (opts.unprotectedHeaders && typeof opts.unprotectedHeaders === "object") {
616
+ var uk = Object.keys(opts.unprotectedHeaders);
617
+ for (var i = 0; i < uk.length; i++) unprot.set(Number(uk[i]), opts.unprotectedHeaders[uk[i]]);
618
+ }
619
+
620
+ var payloadBytes = _bstr(payload);
621
+ var externalAad = opts.externalAad == null ? Buffer.alloc(0) : _bstr(opts.externalAad);
622
+ var tag = nodeCrypto.createHmac(_hmacHash(algId), key).update(_macStructure(protectedBstr, externalAad, payloadBytes)).digest();
623
+
624
+ var mac0arr = [protectedBstr, unprot, opts.detached ? null : payloadBytes, tag];
625
+ return cbor.encode(new cbor.Tag(COSE_MAC0_TAG, mac0arr));
626
+ }
627
+
628
+ /**
629
+ * @primitive b.cose.macVerify0
630
+ * @signature b.cose.macVerify0(coseMac0, opts)
631
+ * @since 0.12.47
632
+ * @status stable
633
+ * @related b.cose.mac0
634
+ *
635
+ * Verify a COSE_Mac0 (RFC 9052 §6.2) and return its payload. The HMAC
636
+ * tag is recomputed over the MAC_structure and compared in constant
637
+ * time; the <code>alg</code> from the protected header must be in
638
+ * <code>opts.algorithms</code>. A detached (nil) payload is supplied via
639
+ * <code>opts.externalPayload</code>.
640
+ *
641
+ * @opts
642
+ * {
643
+ * algorithms: string[], // required — accepted HMAC alg names (allowlist)
644
+ * key: Buffer, // the shared symmetric key
645
+ * externalAad?: Buffer,
646
+ * externalPayload?: Buffer, // required for a detached payload
647
+ * maxBytes?: number,
648
+ * maxDepth?: number,
649
+ * }
650
+ *
651
+ * @example
652
+ * var out = b.cose.macVerify0(mac, { algorithms: ["HMAC-256/256"], key: sharedKey });
653
+ * // → { payload: <Buffer>, alg: "HMAC-256/256", protectedHeaders: Map, unprotectedHeaders: Map }
654
+ */
655
+ function macVerify0(coseMac0, opts) {
656
+ validateOpts.requireObject(opts, "cose.macVerify0", CoseError);
657
+ validateOpts(opts, ["algorithms", "key", "externalAad", "externalPayload", "maxBytes", "maxDepth"], "cose.macVerify0");
658
+ if (!Array.isArray(opts.algorithms) || opts.algorithms.length === 0) {
659
+ throw new CoseError("cose/algorithms-required", "cose.macVerify0: opts.algorithms is required");
660
+ }
661
+ for (var ai = 0; ai < opts.algorithms.length; ai++) {
662
+ if (!(opts.algorithms[ai] in HMAC_NAME_TO_ID)) {
663
+ throw new CoseError("cose/unknown-alg", "cose.macVerify0: unknown algorithm '" + opts.algorithms[ai] + "'");
664
+ }
665
+ }
666
+ if (opts.key == null) throw new CoseError("cose/no-key", "cose.macVerify0: opts.key is required");
667
+ var key = _bstr(opts.key);
668
+
669
+ var decoded = cbor.decode(_bstr(coseMac0), { allowedTags: [COSE_MAC0_TAG], maxBytes: opts.maxBytes, maxDepth: opts.maxDepth });
670
+ var arr = (decoded instanceof cbor.Tag && decoded.tag === COSE_MAC0_TAG) ? decoded.value : decoded;
671
+ if (!Array.isArray(arr) || arr.length !== 4) {
672
+ throw new CoseError("cose/malformed", "cose.macVerify0: not a COSE_Mac0 (expected a 4-element array)");
673
+ }
674
+ var protectedBstr = arr[0];
675
+ var unprotected = arr[1];
676
+ var payload = arr[2];
677
+ var tag = arr[3];
678
+ if (!Buffer.isBuffer(protectedBstr) || !Buffer.isBuffer(tag)) {
679
+ throw new CoseError("cose/malformed", "cose.macVerify0: protected header and tag must be byte strings");
680
+ }
681
+ if (!(unprotected instanceof Map)) {
682
+ throw new CoseError("cose/malformed", "cose.macVerify0: unprotected header must be a CBOR map");
683
+ }
684
+ if (payload === null || payload === undefined) {
685
+ if (opts.externalPayload == null) {
686
+ throw new CoseError("cose/detached-no-payload", "cose.macVerify0: detached (nil) payload — pass opts.externalPayload");
687
+ }
688
+ payload = _bstr(opts.externalPayload);
689
+ } else if (opts.externalPayload != null) {
690
+ throw new CoseError("cose/payload-ambiguous", "cose.macVerify0: externalPayload supplied but the COSE_Mac0 carries an attached payload");
691
+ } else if (!Buffer.isBuffer(payload)) {
692
+ throw new CoseError("cose/malformed", "cose.macVerify0: payload must be a byte string (bstr)");
693
+ }
694
+
695
+ var protMap = protectedBstr.length === 0 ? new Map()
696
+ : cbor.decode(protectedBstr, { maxBytes: opts.maxBytes, maxDepth: opts.maxDepth });
697
+ if (!(protMap instanceof Map)) {
698
+ throw new CoseError("cose/malformed", "cose.macVerify0: protected header is not a CBOR map");
699
+ }
700
+ // crit-bypass defense (RFC 9052 §3.1) — same as b.cose.verify: every
701
+ // label a crit array names must be one this verifier understands AND
702
+ // be present in the protected header.
703
+ if (protMap.has(HDR_CRIT)) {
704
+ var crit = protMap.get(HDR_CRIT);
705
+ if (!Array.isArray(crit)) {
706
+ throw new CoseError("cose/bad-crit", "cose.macVerify0: crit (label 2) must be an array");
707
+ }
708
+ for (var ci = 0; ci < crit.length; ci++) {
709
+ if (UNDERSTOOD_LABELS.indexOf(crit[ci]) === -1) {
710
+ throw new CoseError("cose/crit-unknown",
711
+ "cose.macVerify0: crit lists header label " + crit[ci] + " which is not understood (RFC 9052 §3.1)");
712
+ }
713
+ if (!protMap.has(crit[ci])) {
714
+ throw new CoseError("cose/crit-absent",
715
+ "cose.macVerify0: crit lists label " + crit[ci] + " not present in the protected header");
716
+ }
717
+ }
718
+ }
719
+ var algId = protMap.get(HDR_ALG);
720
+ var algName = HMAC_ID_TO_NAME[algId];
721
+ if (algName === undefined) {
722
+ throw new CoseError("cose/unknown-alg", "cose.macVerify0: unrecognized protected MAC alg id " + algId);
723
+ }
724
+ if (opts.algorithms.indexOf(algName) === -1) {
725
+ throw new CoseError("cose/alg-not-allowed", "cose.macVerify0: alg '" + algName + "' is not in the allowlist");
726
+ }
727
+
728
+ var externalAad = opts.externalAad == null ? Buffer.alloc(0) : _bstr(opts.externalAad);
729
+ var expected = nodeCrypto.createHmac(_hmacHash(algId), key).update(_macStructure(protectedBstr, externalAad, payload)).digest();
730
+ if (!bCrypto.timingSafeEqual(expected, tag)) {
731
+ throw new CoseError("cose/bad-tag", "cose.macVerify0: MAC tag verification failed");
732
+ }
733
+ return { payload: payload, alg: algName, protectedHeaders: protMap, unprotectedHeaders: unprotected };
734
+ }
735
+
736
+ // ---- COSE_Key (RFC 9052 §7 / RFC 9053 §7) → KeyObject ----
737
+
738
+ // COSE_Key EC2 curve identifiers (RFC 9053 §7.1) → JWK crv names. Only
739
+ // the curves b.cose.verify has an algorithm for are accepted: P-256
740
+ // (ES256), P-384 (ES384), P-521 (ES512). secp256k1 is intentionally
741
+ // absent — there is no ES256K path here, so importing one would let a
742
+ // secp256k1 key be verified under ES256, breaking the COSE alg/curve
743
+ // binding (RFC 9053). Re-add with an explicit ES256K algorithm.
744
+ var COSE_EC2_CRV = { 1: "P-256", 2: "P-384", 3: "P-521" };
745
+ var COSE_KTY_OKP = 1;
746
+ var COSE_KTY_EC2 = 2;
747
+ var COSE_OKP_ED25519 = 6; // allow:raw-byte-literal — COSE OKP Ed25519 crv id (RFC 9053)
748
+
749
+ function _coseKeyBytes(v, what) {
750
+ if (Buffer.isBuffer(v)) return v;
751
+ if (v instanceof Uint8Array) return Buffer.from(v);
752
+ throw new CoseError("cose/bad-cose-key", "cose.importKey: COSE_Key " + what + " must be a byte string");
753
+ }
754
+
755
+ /**
756
+ * @primitive b.cose.importKey
757
+ * @signature b.cose.importKey(coseKey)
758
+ * @since 0.12.45
759
+ * @status stable
760
+ * @related b.cose.verify, b.cbor.decode
761
+ *
762
+ * Import a COSE_Key (RFC 9052 §7) — a CBOR map keyed by integer labels —
763
+ * as a <code>node:crypto</code> public KeyObject for
764
+ * <code>b.cose.verify</code>. Accepts the EC2 (<code>kty</code> 2:
765
+ * P-256 / P-384 / P-521) and OKP (<code>kty</code> 1: Ed25519) key
766
+ * types — the curves <code>b.cose.verify</code> has an algorithm for;
767
+ * the curve is allowlisted, so an unexpected key type (including
768
+ * secp256k1, which has no ES256K path here) is refused rather than
769
+ * imported. The verification key embedded in an mdoc MSO or a COSE_Key
770
+ * header is consumed this way.
771
+ *
772
+ * @example
773
+ * var key = b.cose.importKey(coseKeyMap); // → public KeyObject
774
+ * var out = await b.cose.verify(sign1, { algorithms: ["ES256"], publicKey: key });
775
+ */
776
+ function importKey(coseKey) {
777
+ if (!(coseKey instanceof Map)) {
778
+ if (coseKey && typeof coseKey === "object" && !Array.isArray(coseKey)) {
779
+ var m = new Map();
780
+ Object.keys(coseKey).forEach(function (k) { m.set(Number(k), coseKey[k]); });
781
+ coseKey = m;
782
+ } else {
783
+ throw new CoseError("cose/bad-cose-key", "cose.importKey: expected a COSE_Key map");
784
+ }
785
+ }
786
+ var kty = coseKey.get(1);
787
+ var x = _coseKeyBytes(coseKey.get(-2), "x");
788
+ var jwk;
789
+ if (kty === COSE_KTY_OKP) {
790
+ if (coseKey.get(-1) !== COSE_OKP_ED25519) {
791
+ throw new CoseError("cose/unsupported-key", "cose.importKey: only OKP curve Ed25519 is supported");
792
+ }
793
+ jwk = { kty: "OKP", crv: "Ed25519", x: x.toString("base64url") };
794
+ } else if (kty === COSE_KTY_EC2) {
795
+ var crvName = COSE_EC2_CRV[coseKey.get(-1)];
796
+ if (!crvName) throw new CoseError("cose/unsupported-key", "cose.importKey: unsupported EC2 curve id " + coseKey.get(-1));
797
+ var y = _coseKeyBytes(coseKey.get(-3), "y");
798
+ jwk = { kty: "EC", crv: crvName, x: x.toString("base64url"), y: y.toString("base64url") };
799
+ } else {
800
+ throw new CoseError("cose/unsupported-key", "cose.importKey: kty must be OKP (1) or EC2 (2), got " + kty);
801
+ }
802
+ try { return nodeCrypto.createPublicKey({ key: jwk, format: "jwk" }); }
803
+ catch (e) { throw new CoseError("cose/bad-cose-key", "cose.importKey: could not import COSE_Key: " + ((e && e.message) || e)); }
804
+ }
805
+
537
806
  module.exports = {
538
807
  sign: sign,
539
808
  verify: verify,
540
809
  encrypt0: encrypt0,
541
810
  decrypt0: decrypt0,
811
+ mac0: mac0,
812
+ macVerify0: macVerify0,
813
+ importKey: importKey,
542
814
  ALGORITHMS: ALG_NAME_TO_ID,
815
+ MAC_ALGORITHMS: HMAC_NAME_TO_ID,
816
+ COSE_MAC0_TAG: COSE_MAC0_TAG,
543
817
  AEAD_ALGORITHMS: AEAD_NAME_TO_ID,
544
818
  COSE_SIGN1_TAG: COSE_SIGN1_TAG,
545
819
  COSE_ENCRYPT0_TAG: COSE_ENCRYPT0_TAG,
@@ -62,6 +62,9 @@ var audit = lazyRequire(function () { return require("./audit"); });
62
62
  // loaded because safe-buffer.js itself imports b.crypto for
63
63
  // hex-compare helpers (circular).
64
64
  var safeBuffer = lazyRequire(function () { return require("./safe-buffer"); });
65
+ // pqc-software requires this module (b.crypto) — lazy-load to break the
66
+ // cycle. Only the power-on self-test needs it here.
67
+ var pqcSoftware = lazyRequire(function () { return require("./pqc-software"); });
65
68
 
66
69
  // Streaming-hash algorithm allowlist. Mirrors the framework's PQC-
67
70
  // first crypto policy: SHA3 / SHAKE family is the default surface;
@@ -1873,8 +1876,124 @@ var SUPPORTED_KEM_ALGORITHMS = Object.freeze([
1873
1876
  // var fixtures = require("blamejs/lib/_test/crypto-fixtures");
1874
1877
  // var blob = fixtures.mintLegacyEnvelope0xE1(plaintext, recipient);
1875
1878
 
1879
+ // ---- FIPS 140-3-style power-on self-test ----
1880
+ //
1881
+ // Known-answer tests (KATs) for the deterministic primitives — the
1882
+ // hash / XOF digests are NIST FIPS 202 published vectors, so this
1883
+ // confirms the framework's hashing matches the standard, not merely
1884
+ // itself. The PQC algorithms have no seed-injection API (node generates
1885
+ // the keypair randomness internally), so they get FIPS 140-3 §10.3
1886
+ // pairwise-consistency + negative tests (a fresh keypair must
1887
+ // sign->verify / encaps->decaps consistently, and a tampered signature
1888
+ // must be rejected) rather than a fixed-seed KAT.
1889
+ var KAT_SHA3_512_ABC = "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0";
1890
+ var KAT_SHA3_256_ABC = "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532";
1891
+ var KAT_SHAKE256_ABC_32 = "483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739";
1892
+
1893
+ /**
1894
+ * @primitive b.crypto.selfTest
1895
+ * @signature b.crypto.selfTest(opts?)
1896
+ * @since 0.12.43
1897
+ * @status stable
1898
+ * @compliance soc2, hipaa, pci-dss
1899
+ * @related b.crypto.sha3Hash, b.crypto.encrypt
1900
+ *
1901
+ * Run a power-on self-test over the framework's cryptographic
1902
+ * primitives — the integrity check FIPS 140-3 requires of a validated
1903
+ * module. The hash / XOF checks are known-answer tests against NIST FIPS
1904
+ * 202 vectors (SHA3-256 / SHA3-512 / SHAKE256); the AEAD check
1905
+ * round-trips XChaCha20-Poly1305 and confirms a tampered ciphertext is
1906
+ * rejected; the post-quantum checks run a pairwise-consistency +
1907
+ * negative test for ML-KEM-1024, ML-DSA-87, and SLH-DSA-SHAKE-256f.
1908
+ * Returns a structured report and, by default, throws on any failure so
1909
+ * a broken crypto stack fails closed at boot rather than silently
1910
+ * producing bad output.
1911
+ *
1912
+ * @opts
1913
+ * {
1914
+ * throwOnFailure?: boolean, // default true — throw if any check fails
1915
+ * }
1916
+ *
1917
+ * @example
1918
+ * var report = b.crypto.selfTest();
1919
+ * // -> { ok: true, results: [ { name, ok }, ... ], failures: [], ranAt }
1920
+ */
1921
+ function selfTest(opts) {
1922
+ opts = opts || {};
1923
+ var results = [];
1924
+ function record(name, fn) {
1925
+ try { fn(); results.push({ name: name, ok: true }); }
1926
+ catch (e) { results.push({ name: name, ok: false, detail: (e && e.message) || String(e) }); }
1927
+ }
1928
+ function assert(cond, msg) { if (!cond) throw new Error(msg); }
1929
+
1930
+ record("SHA3-512 KAT (FIPS 202)", function () {
1931
+ assert(sha3Hash("abc") === KAT_SHA3_512_ABC, "SHA3-512(\"abc\") does not match the FIPS 202 vector");
1932
+ });
1933
+ record("SHA3-256 KAT (FIPS 202)", function () {
1934
+ assert(hash("abc", "sha3-256").toString("hex") === KAT_SHA3_256_ABC, "SHA3-256(\"abc\") does not match the FIPS 202 vector");
1935
+ });
1936
+ record("SHAKE256 KAT (FIPS 202)", function () {
1937
+ assert(hash("abc", "shake256", C.BYTES.bytes(32)).toString("hex") === KAT_SHAKE256_ABC_32, "SHAKE256(\"abc\") does not match the FIPS 202 vector");
1938
+ });
1939
+ record("HMAC-SHA3-512 determinism", function () {
1940
+ var k = Buffer.from("self-test-hmac-key", "utf8");
1941
+ assert(timingSafeEqual(hmacSha3(k, "abc"), hmacSha3(k, "abc")), "HMAC-SHA3-512 is not deterministic");
1942
+ assert(!timingSafeEqual(hmacSha3(k, "abc"), hmacSha3(k, "abd")), "HMAC-SHA3-512 collided on distinct inputs");
1943
+ });
1944
+ record("XChaCha20-Poly1305 round-trip + tamper-detect", function () {
1945
+ var key = generateBytes(C.BYTES.bytes(32));
1946
+ var pt = Buffer.from("blamejs crypto self-test plaintext", "utf8");
1947
+ var packed = encryptPacked(pt, key);
1948
+ assert(decryptPacked(packed, key).equals(pt), "AEAD round-trip did not recover the plaintext");
1949
+ var bad = Buffer.from(packed); bad[bad.length - 1] ^= 0xff;
1950
+ var rejected = false;
1951
+ try { decryptPacked(bad, key); } catch (_e) { rejected = true; }
1952
+ assert(rejected, "AEAD accepted a tampered ciphertext");
1953
+ });
1954
+
1955
+ // Post-quantum pairwise-consistency + negative tests. PQC is an
1956
+ // optional vendored dependency — a load failure surfaces as a failed
1957
+ // check rather than a silent skip.
1958
+ var pqc = pqcSoftware();
1959
+ record("ML-KEM-1024 encaps/decaps pairwise consistency", function () {
1960
+ var kp = pqc.ml_kem_1024.keygen();
1961
+ var enc = pqc.ml_kem_1024.encapsulate(kp.publicKey);
1962
+ var ss = pqc.ml_kem_1024.decapsulate(enc.cipherText, kp.secretKey);
1963
+ assert(timingSafeEqual(Buffer.from(ss), Buffer.from(enc.sharedSecret)), "ML-KEM-1024 decapsulated secret does not match");
1964
+ });
1965
+ record("ML-DSA-87 sign/verify + negative", function () {
1966
+ var kp = pqc.ml_dsa_87.keygen();
1967
+ var msg = Buffer.from("blamejs ML-DSA self-test", "utf8");
1968
+ var sig = pqc.ml_dsa_87.sign(msg, kp.secretKey);
1969
+ assert(pqc.ml_dsa_87.verify(sig, msg, kp.publicKey), "ML-DSA-87 rejected a valid signature");
1970
+ var bad = Buffer.from(sig); bad[0] ^= 0xff;
1971
+ assert(!pqc.ml_dsa_87.verify(bad, msg, kp.publicKey), "ML-DSA-87 accepted a tampered signature");
1972
+ });
1973
+ record("SLH-DSA-SHAKE-256f sign/verify + negative", function () {
1974
+ var kp = pqc.slh_dsa_shake_256f.keygen();
1975
+ var msg = Buffer.from("blamejs SLH-DSA self-test", "utf8");
1976
+ var sig = pqc.slh_dsa_shake_256f.sign(msg, kp.secretKey);
1977
+ assert(pqc.slh_dsa_shake_256f.verify(sig, msg, kp.publicKey), "SLH-DSA-SHAKE-256f rejected a valid signature");
1978
+ var bad = Buffer.from(sig); bad[0] ^= 0xff;
1979
+ assert(!pqc.slh_dsa_shake_256f.verify(bad, msg, kp.publicKey), "SLH-DSA-SHAKE-256f accepted a tampered signature");
1980
+ });
1981
+
1982
+ var failures = results.filter(function (r) { return !r.ok; });
1983
+ var report = { ok: failures.length === 0, results: results, failures: failures, ranAt: new Date().toISOString() };
1984
+ if (failures.length && opts.throwOnFailure !== false) {
1985
+ var err = new Error("crypto.selfTest: " + failures.length + " self-test(s) failed: " +
1986
+ failures.map(function (f) { return f.name; }).join("; "));
1987
+ err.code = "crypto/self-test-failed";
1988
+ err.report = report;
1989
+ throw err;
1990
+ }
1991
+ return report;
1992
+ }
1993
+
1876
1994
  module.exports = {
1877
1995
  sri: sri,
1996
+ selfTest: selfTest,
1878
1997
  // Hashing
1879
1998
  sha3Hash: sha3Hash,
1880
1999
  hmacSha3: hmacSha3,