@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.
- package/CHANGELOG.md +6 -0
- package/README.md +1 -1
- package/lib/admin.js +275 -9
- package/lib/affiliates.js +4 -3
- package/lib/analytics.js +3 -2
- package/lib/api-keys.js +1 -1
- package/lib/assembly-instructions.js +2 -1
- package/lib/auto-replenish.js +4 -3
- package/lib/backorder.js +2 -1
- package/lib/business-hours.js +8 -1
- package/lib/carrier-accounts.js +1 -1
- package/lib/carrier-rates.js +1 -1
- package/lib/cart-abandonment.js +3 -2
- package/lib/cart-bulk-ops.js +2 -1
- package/lib/cart-recovery.js +5 -4
- package/lib/cart.js +6 -2
- package/lib/catalog-drafts.js +1 -1
- package/lib/click-and-collect.js +3 -2
- package/lib/clickstream.js +4 -3
- package/lib/config.js +2 -1
- package/lib/cookie-consent.js +2 -1
- package/lib/credit-limits.js +2 -1
- package/lib/currency-display.js +2 -1
- package/lib/customer-activity.js +3 -2
- package/lib/customer-impersonation.js +3 -3
- package/lib/customer-merge.js +4 -3
- package/lib/customer-portal.js +4 -4
- package/lib/customer-risk-profile.js +2 -1
- package/lib/customer-segments.js +2 -1
- package/lib/customer-surveys.js +6 -3
- package/lib/delivery-estimate.js +2 -2
- package/lib/demand-forecast.js +2 -1
- package/lib/discount-analytics.js +2 -2
- package/lib/dunning.js +4 -1
- package/lib/email-warmup.js +6 -1
- package/lib/email.js +1 -8
- package/lib/error-log.js +3 -2
- package/lib/event-log.js +3 -2
- package/lib/fraud-screen.js +3 -1
- package/lib/fulfillment-sla.js +3 -1
- package/lib/index.js +11 -3
- package/lib/inventory-allocations.js +3 -0
- package/lib/inventory-snapshots.js +2 -1
- package/lib/invoice-renderer.js +2 -1
- package/lib/line-gift-wrap.js +6 -1
- package/lib/live-chat.js +2 -1
- package/lib/loyalty-redemption.js +2 -1
- package/lib/newsletter.js +6 -1
- package/lib/operator-activity-feed.js +4 -3
- package/lib/operator-sessions.js +7 -7
- package/lib/order-exchanges.js +1 -0
- package/lib/order-timeline.js +2 -1
- package/lib/payment-retries.js +2 -1
- package/lib/payment.js +5 -4
- package/lib/pixel-events.js +6 -5
- package/lib/preorder.js +2 -1
- package/lib/print-queue.js +2 -1
- package/lib/product-compare.js +2 -1
- package/lib/product-qa.js +2 -1
- package/lib/push-notifications.js +6 -5
- package/lib/recently-viewed.js +7 -2
- package/lib/recommendations.js +7 -2
- package/lib/referral-leaderboard.js +2 -1
- package/lib/refund-automation.js +1 -1
- package/lib/refund-policy.js +1 -1
- package/lib/reorder-reminders.js +2 -1
- package/lib/reorder-thresholds.js +2 -1
- package/lib/robots-config.js +1 -0
- package/lib/sales-reports.js +17 -14
- package/lib/sales-tax-filings.js +2 -1
- package/lib/save-for-later.js +2 -1
- package/lib/search-suggestions.js +1 -1
- package/lib/shipping-insurance.js +2 -1
- package/lib/shipping-labels.js +3 -2
- package/lib/shipping-zones.js +1 -0
- package/lib/shrinkage-report.js +9 -8
- package/lib/sms-dispatcher.js +6 -5
- package/lib/stock-alerts.js +1 -1
- package/lib/stock-receipts.js +2 -1
- package/lib/store-credit.js +2 -1
- package/lib/storefront-forms.js +1 -1
- package/lib/storefront.js +93 -112
- package/lib/subscription-analytics.js +7 -2
- package/lib/subscription-controls.js +9 -8
- package/lib/subscription-gifts.js +2 -1
- package/lib/subscriptions.js +2 -0
- package/lib/support-tickets.js +4 -4
- package/lib/tax-cert-renewals.js +2 -1
- package/lib/tax-remittance.js +2 -1
- package/lib/theme-assets.js +1 -1
- package/lib/vendor/MANIFEST.json +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +16 -0
- package/lib/vendor/blamejs/README.md +6 -4
- package/lib/vendor/blamejs/SECURITY.md +2 -0
- package/lib/vendor/blamejs/api-snapshot.json +255 -2
- package/lib/vendor/blamejs/index.js +1 -0
- package/lib/vendor/blamejs/lib/cose.js +284 -10
- package/lib/vendor/blamejs/lib/crypto.js +119 -0
- package/lib/vendor/blamejs/lib/did.js +416 -0
- package/lib/vendor/blamejs/lib/mdoc.js +122 -0
- package/lib/vendor/blamejs/lib/network-dnssec.js +328 -0
- package/lib/vendor/blamejs/lib/network.js +1 -0
- package/lib/vendor/blamejs/lib/vc.js +231 -33
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.12.41.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.42.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.43.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.44.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.45.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.46.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.47.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.12.48.json +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +47 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +101 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-self-test.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/did.test.js +176 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +130 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mdoc.test.js +52 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +63 -0
- package/lib/vendor-invoices.js +1 -1
- package/lib/webhook-receiver.js +8 -2
- package/lib/webhook-subscriptions.js +1 -1
- package/lib/webhooks.js +6 -5
- package/lib/winback-campaigns.js +2 -1
- package/lib/wishlist-alerts.js +2 -1
- package/lib/wishlist-digest.js +2 -1
- 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
|
-
|
|
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
|
-
|
|
283
|
-
"cose
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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,
|