@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
@@ -0,0 +1,416 @@
1
+ "use strict";
2
+ /**
3
+ * @module b.did
4
+ * @nav Crypto
5
+ * @title Decentralized Identifiers (DID)
6
+ *
7
+ * @intro
8
+ * Resolve W3C Decentralized Identifiers (DID Core 1.0, a W3C
9
+ * Recommendation) to verification keys — the missing link that lets a
10
+ * credential's issuer be named by a DID rather than a raw key. Resolve
11
+ * the issuer DID of a <code>b.vc</code> / <code>b.mdoc</code> /
12
+ * <code>b.scitt</code> credential to a <code>node:crypto</code>
13
+ * KeyObject, then hand that key to the verifier.
14
+ *
15
+ * Three methods are supported. <strong>did:key</strong> encodes a
16
+ * public key directly in the identifier (multicodec + base58btc
17
+ * multibase) and <strong>did:jwk</strong> encodes it as a base64url
18
+ * public JWK — both resolve deterministically and offline (Ed25519,
19
+ * P-256, P-384, and secp256k1 round-trip). <strong>did:web</strong>
20
+ * places the DID document at an HTTPS URL derived from the identifier;
21
+ * the network fetch is the operator's to make (the same
22
+ * operator-supplied-input stance as the rest of the framework), and
23
+ * <code>resolve</code> takes the fetched document and extracts its
24
+ * verification methods.
25
+ *
26
+ * <code>b.did.keyToDid(publicKey)</code> produces a did:key from a
27
+ * KeyObject (an issuer naming itself); <code>b.did.parse(did)</code>
28
+ * splits the identifier (and, for did:web, returns the HTTPS URL to
29
+ * fetch); <code>b.did.resolve(did, opts)</code> returns the DID
30
+ * document and its verification methods as KeyObjects. Verification
31
+ * methods expressed as <code>publicKeyMultibase</code> or
32
+ * <code>publicKeyJwk</code> are both understood.
33
+ *
34
+ * <strong>Maturity.</strong> DID Core 1.0 is a Recommendation, but the
35
+ * method specs are deployed-stable rather than Recommendations:
36
+ * did:key is a W3C CCG report and did:web is a registered DID method
37
+ * (mandated by the EU Digital Identity Wallet). They are widely
38
+ * deployed and interoperable today; pin the dependency deliberately.
39
+ *
40
+ * @card
41
+ * W3C DID resolution (did:key + did:jwk + did:web) → verification
42
+ * KeyObjects for the credential verifiers. did:key + did:jwk are
43
+ * deterministic + offline (Ed25519 / P-256 / P-384 / secp256k1);
44
+ * did:web parses an operator-fetched DID document. Composes
45
+ * node:crypto; no new dep.
46
+ */
47
+
48
+ var nodeCrypto = require("node:crypto");
49
+ var safeJson = require("./safe-json");
50
+ var validateOpts = require("./validate-opts");
51
+ var { defineClass } = require("./framework-error");
52
+
53
+ var DidError = defineClass("DidError", { alwaysPermanent: true });
54
+
55
+ var B58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
56
+ var B58_MAP = (function () {
57
+ var m = {};
58
+ for (var i = 0; i < B58_ALPHABET.length; i += 1) m[B58_ALPHABET[i]] = i;
59
+ return m;
60
+ })();
61
+ var MAX_MULTIBASE_CHARS = 1024; // allow:raw-byte-literal — bounded did:key multibase length (DoS cap)
62
+ var MAX_JWK_B64_CHARS = 8192; // allow:raw-byte-literal — bounded did:jwk encoded-JWK length (DoS cap)
63
+
64
+ // multicodec public-key codes (unsigned-varint) → curve descriptor.
65
+ // keyLen is the multicodec payload: Ed25519 raw 32; EC compressed point.
66
+ var MULTICODEC = {
67
+ 0xed: { name: "Ed25519", kind: "okp" }, // ed25519-pub
68
+ 0x1200: { name: "P-256", kind: "ec", curveOid: "1.2.840.10045.3.1.7" }, // allow:raw-byte-literal allow:raw-time-literal — p256-pub multicodec code + OID dotted-form
69
+ 0x1201: { name: "P-384", kind: "ec", curveOid: "1.3.132.0.34" }, // allow:raw-byte-literal — p384-pub multicodec code
70
+ 0xe7: { name: "secp256k1", kind: "ec", curveOid: "1.3.132.0.10" }, // secp256k1-pub
71
+ };
72
+ var NAME_TO_CODE = {};
73
+ Object.keys(MULTICODEC).forEach(function (c) { NAME_TO_CODE[MULTICODEC[c].name] = Number(c); });
74
+
75
+ // ---- base58btc (bounded) ----
76
+
77
+ function _b58decode(str) {
78
+ if (str.length > MAX_MULTIBASE_CHARS) {
79
+ throw new DidError("did/too-long", "did: multibase value exceeds the " + MAX_MULTIBASE_CHARS + "-char cap");
80
+ }
81
+ var bytes = [0];
82
+ for (var i = 0; i < str.length; i += 1) {
83
+ var v = B58_MAP[str[i]];
84
+ if (v === undefined) throw new DidError("did/bad-base58", "did: invalid base58btc character '" + str[i] + "'");
85
+ var carry = v;
86
+ for (var j = 0; j < bytes.length; j += 1) {
87
+ carry += bytes[j] * 58;
88
+ bytes[j] = carry & 0xff;
89
+ carry >>= 8; // allow:raw-byte-literal — base-256 carry
90
+ }
91
+ while (carry > 0) { bytes.push(carry & 0xff); carry >>= 8; } // allow:raw-byte-literal — base-256 carry
92
+ }
93
+ // Leading '1's are leading zero bytes.
94
+ for (var k = 0; k < str.length && str[k] === "1"; k += 1) bytes.push(0);
95
+ return Buffer.from(bytes.reverse());
96
+ }
97
+
98
+ function _b58encode(buf) {
99
+ var digits = [0];
100
+ for (var i = 0; i < buf.length; i += 1) {
101
+ var carry = buf[i];
102
+ for (var j = 0; j < digits.length; j += 1) {
103
+ carry += digits[j] << 8; // allow:raw-byte-literal — base-256 shift
104
+ digits[j] = carry % 58;
105
+ carry = (carry / 58) | 0;
106
+ }
107
+ while (carry > 0) { digits.push(carry % 58); carry = (carry / 58) | 0; }
108
+ }
109
+ var out = "";
110
+ for (var z = 0; z < buf.length && buf[z] === 0; z += 1) out += "1";
111
+ for (var d = digits.length - 1; d >= 0; d -= 1) out += B58_ALPHABET[digits[d]];
112
+ return out;
113
+ }
114
+
115
+ // Read an unsigned LEB128 varint (multicodec code). Bounded to 4 bytes.
116
+ function _readVarint(buf) {
117
+ var value = 0, shift = 0, len = 0;
118
+ for (var i = 0; i < buf.length && i < 4; i += 1) { // allow:raw-byte-literal — multicodec varint ≤ 4 bytes
119
+ var b = buf[i];
120
+ value |= (b & 0x7f) << shift;
121
+ len += 1;
122
+ if ((b & 0x80) === 0) return { value: value >>> 0, length: len };
123
+ shift += 7; // allow:raw-byte-literal — 7 bits per varint byte
124
+ }
125
+ throw new DidError("did/bad-multicodec", "did: multicodec varint did not terminate");
126
+ }
127
+ function _encodeVarint(code) {
128
+ var out = [];
129
+ var n = code;
130
+ do { var b = n & 0x7f; n >>>= 7; if (n > 0) b |= 0x80; out.push(b); } while (n > 0); // allow:raw-byte-literal — LEB128 7-bit groups
131
+ return Buffer.from(out);
132
+ }
133
+
134
+ // ---- key <-> bytes ----
135
+
136
+ var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex"); // RFC 8410 Ed25519 SubjectPublicKeyInfo header
137
+
138
+ function _keyObjectFromMulticodec(code, keyBytes) {
139
+ var desc = MULTICODEC[code];
140
+ if (!desc) throw new DidError("did/unsupported-key", "did: unsupported multicodec key code 0x" + code.toString(16)); // allow:raw-byte-literal — hex radix
141
+ if (desc.kind === "okp") {
142
+ if (keyBytes.length !== 32) { // allow:raw-byte-literal — Ed25519 public key is 32 bytes
143
+ throw new DidError("did/bad-key", "did: Ed25519 key must be 32 bytes (got " + keyBytes.length + ")");
144
+ }
145
+ return nodeCrypto.createPublicKey({ key: Buffer.concat([ED25519_SPKI_PREFIX, keyBytes]), format: "der", type: "spki" });
146
+ }
147
+ // EC: keyBytes is a compressed point (0x02/0x03 + X). Build an SPKI and
148
+ // let node decompress.
149
+ if (keyBytes.length < 2 || (keyBytes[0] !== 0x02 && keyBytes[0] !== 0x03)) {
150
+ throw new DidError("did/bad-key", "did: EC key must be a compressed point (0x02/0x03 prefix)");
151
+ }
152
+ var algid = _ecAlgId(desc.curveOid);
153
+ var bitstr = Buffer.concat([Buffer.from([0x03, keyBytes.length + 1, 0x00]), keyBytes]);
154
+ var body = Buffer.concat([algid, bitstr]);
155
+ var spki = Buffer.concat([Buffer.from([0x30, body.length]), body]); // allow:raw-byte-literal — SEQUENCE tag; single-byte DER length holds for these curves
156
+ try { return nodeCrypto.createPublicKey({ key: spki, format: "der", type: "spki" }); }
157
+ catch (e) { throw new DidError("did/bad-key", "did: could not import EC key: " + ((e && e.message) || e)); }
158
+ }
159
+
160
+ // AlgorithmIdentifier SEQUENCE { id-ecPublicKey, namedCurve OID }.
161
+ function _ecAlgId(curveOid) {
162
+ var idEcPublicKey = Buffer.from("06072a8648ce3d0201", "hex"); // allow:raw-byte-literal allow:raw-time-literal — DER OID for id-ecPublicKey
163
+ var curve = _oidDer(curveOid);
164
+ var inner = Buffer.concat([idEcPublicKey, curve]);
165
+ return Buffer.concat([Buffer.from([0x30, inner.length]), inner]);
166
+ }
167
+ function _oidDer(dotted) {
168
+ var parts = dotted.split(".").map(Number);
169
+ var bytes = [parts[0] * 40 + parts[1]]; // allow:raw-byte-literal — X.690 first-arc encoding
170
+ for (var i = 2; i < parts.length; i += 1) {
171
+ var arc = parts[i], stack = [];
172
+ do { stack.unshift(arc & 0x7f); arc >>>= 7; } while (arc > 0); // allow:raw-byte-literal — base-128 OID arc
173
+ for (var j = 0; j < stack.length - 1; j += 1) stack[j] |= 0x80; // allow:raw-byte-literal — continuation bit
174
+ bytes = bytes.concat(stack);
175
+ }
176
+ return Buffer.concat([Buffer.from([0x06, bytes.length]), Buffer.from(bytes)]);
177
+ }
178
+
179
+ // Compressed point + curve name from an EC KeyObject's JWK.
180
+ function _compressedPoint(jwk) {
181
+ var x = Buffer.from(jwk.x, "base64url");
182
+ var y = Buffer.from(jwk.y, "base64url");
183
+ return Buffer.concat([Buffer.from([(y[y.length - 1] & 1) ? 0x03 : 0x02]), x]);
184
+ }
185
+
186
+ /**
187
+ * @primitive b.did.parse
188
+ * @signature b.did.parse(did)
189
+ * @since 0.12.41
190
+ * @status experimental
191
+ * @related b.did.resolve, b.did.keyToDid
192
+ *
193
+ * Split a DID string into its method and method-specific id. For
194
+ * <code>did:web</code> the HTTPS URL of the DID document is also
195
+ * returned (host[:port][:path] → <code>https://host/path/did.json</code>,
196
+ * or <code>/.well-known/did.json</code> with no path).
197
+ *
198
+ * @example
199
+ * b.did.parse("did:web:example.com:issuers:42");
200
+ * // → { method: "web", id: "example.com:issuers:42", url: "https://example.com/issuers/42/did.json" }
201
+ */
202
+ function parse(did) {
203
+ if (typeof did !== "string" || did.indexOf("did:") !== 0) {
204
+ throw new DidError("did/bad-did", "did.parse: not a DID (must start with 'did:')");
205
+ }
206
+ var rest = did.slice(4);
207
+ var colon = rest.indexOf(":");
208
+ if (colon <= 0) throw new DidError("did/bad-did", "did.parse: DID is missing a method-specific id");
209
+ var method = rest.slice(0, colon);
210
+ var id = rest.slice(colon + 1);
211
+ var out = { method: method, id: id };
212
+ if (method === "web") out.url = _didWebUrl(id);
213
+ return out;
214
+ }
215
+
216
+ function _didWebUrl(id) {
217
+ // did-method-web §read: ':' separates segments (→ '/'); only the host
218
+ // may carry a percent-encoded port (%3A → ':'). Path segments are kept
219
+ // verbatim — NOT percent-decoded — so an escaped reserved char (e.g.
220
+ // %3F) stays path data rather than becoming URL control syntax, and a
221
+ // malformed escape never throws a raw URIError.
222
+ var segs = id.split(":");
223
+ var host = segs[0].replace(/%3[Aa]/g, ":");
224
+ if (!host) throw new DidError("did/bad-did", "did:web: missing host");
225
+ var path = segs.slice(1);
226
+ var base = "https://" + host;
227
+ return path.length ? base + "/" + path.join("/") + "/did.json" : base + "/.well-known/did.json";
228
+ }
229
+
230
+ /**
231
+ * @primitive b.did.keyToDid
232
+ * @signature b.did.keyToDid(publicKey, opts?)
233
+ * @since 0.12.41
234
+ * @status experimental
235
+ * @related b.did.resolve
236
+ *
237
+ * Encode a public key (a <code>node:crypto</code> KeyObject or PEM) as a
238
+ * DID — the inverse of resolution, for an issuer that names itself by
239
+ * its key. Defaults to <code>did:key</code> (multicodec + base58btc);
240
+ * pass <code>opts.method = "jwk"</code> for <code>did:jwk</code>
241
+ * (base64url-encoded public JWK). Ed25519, P-256, P-384, and secp256k1
242
+ * are supported.
243
+ *
244
+ * @opts
245
+ * {
246
+ * method: string, // "key" (default) | "jwk"
247
+ * }
248
+ *
249
+ * @example
250
+ * var did = b.did.keyToDid(issuerPublicKey); // → "did:key:z6Mk…"
251
+ * var dj = b.did.keyToDid(issuerPublicKey, { method: "jwk" }); // → "did:jwk:eyJr…"
252
+ */
253
+ function keyToDid(publicKey, opts) {
254
+ var key = (publicKey && typeof publicKey === "object" && publicKey.asymmetricKeyType)
255
+ ? publicKey : nodeCrypto.createPublicKey(publicKey);
256
+ var jwk = key.export({ format: "jwk" });
257
+ if (opts && opts.method === "jwk") {
258
+ // did:jwk — base64url(UTF-8(JSON of the PUBLIC JWK)). Strip any
259
+ // private member defensively (a public KeyObject has none, but a
260
+ // caller could pass a private key by mistake).
261
+ var pub = {};
262
+ Object.keys(jwk).forEach(function (k) { if (k !== "d") pub[k] = jwk[k]; });
263
+ // Gate on the same kty/crv allowlist resolution enforces, so a
264
+ // produced did:jwk always round-trips (no generate-succeeds /
265
+ // resolve-fails RSA-style identifiers).
266
+ _jwkToKey(pub);
267
+ return "did:jwk:" + Buffer.from(JSON.stringify(pub), "utf8").toString("base64url");
268
+ }
269
+ var code, payload;
270
+ if (jwk.kty === "OKP" && jwk.crv === "Ed25519") {
271
+ code = NAME_TO_CODE["Ed25519"];
272
+ payload = Buffer.from(jwk.x, "base64url");
273
+ } else if (jwk.kty === "EC") {
274
+ var name = jwk.crv === "P-256" ? "P-256" : jwk.crv === "P-384" ? "P-384" : jwk.crv === "secp256k1" ? "secp256k1" : null;
275
+ if (!name) throw new DidError("did/unsupported-key", "did.keyToDid: unsupported EC curve '" + jwk.crv + "'");
276
+ code = NAME_TO_CODE[name];
277
+ payload = _compressedPoint(jwk);
278
+ } else {
279
+ throw new DidError("did/unsupported-key", "did.keyToDid: unsupported key type '" + jwk.kty + "/" + jwk.crv + "'");
280
+ }
281
+ return "did:key:z" + _b58encode(Buffer.concat([_encodeVarint(code), payload]));
282
+ }
283
+
284
+ /**
285
+ * @primitive b.did.resolve
286
+ * @signature b.did.resolve(did, opts?)
287
+ * @since 0.12.41
288
+ * @status experimental
289
+ * @compliance soc2
290
+ * @related b.did.parse, b.vc.verify, b.mdoc.verifyIssuerSigned
291
+ *
292
+ * Resolve a DID to its document and verification methods (each with a
293
+ * <code>node:crypto</code> public KeyObject ready for a verifier).
294
+ * <code>did:key</code> and <code>did:jwk</code> resolve deterministically
295
+ * and offline. <code>did:web</code> requires the operator to supply the fetched DID
296
+ * document as <code>opts.document</code> (the network fetch is the
297
+ * operator's; the URL to fetch is on <code>b.did.parse(did).url</code>).
298
+ *
299
+ * @opts
300
+ * {
301
+ * document: object, // did:web — the fetched did.json (required for did:web)
302
+ * }
303
+ *
304
+ * @example
305
+ * var r = b.did.resolve("did:key:z6Mk…");
306
+ * var key = r.verificationMethods[0].publicKey; // → KeyObject for b.vc.verify / b.mdoc / b.scitt
307
+ */
308
+ function resolve(did, opts) {
309
+ opts = opts || {};
310
+ validateOpts.requireObject(opts, "did.resolve", DidError);
311
+ validateOpts(opts, ["document"], "did.resolve");
312
+ var parsed = parse(did);
313
+
314
+ if (parsed.method === "key") {
315
+ if (parsed.id[0] !== "z") {
316
+ throw new DidError("did/bad-did", "did:key: method-specific id must be base58btc multibase (start with 'z')");
317
+ }
318
+ var raw = _b58decode(parsed.id.slice(1));
319
+ var vh = _readVarint(raw);
320
+ var key = _keyObjectFromMulticodec(vh.value, raw.slice(vh.length));
321
+ var vmId = did + "#" + parsed.id;
322
+ var vm = { id: vmId, controller: did, type: MULTICODEC[vh.value].name, publicKey: key };
323
+ var doc = {
324
+ "@context": ["https://www.w3.org/ns/did/v1"],
325
+ id: did,
326
+ verificationMethod: [{ id: vmId, controller: did, type: "Multikey", publicKeyMultibase: parsed.id }],
327
+ assertionMethod: [vmId], authentication: [vmId],
328
+ };
329
+ return { didDocument: doc, verificationMethods: [vm] };
330
+ }
331
+
332
+ if (parsed.method === "jwk") {
333
+ if (parsed.id.length > MAX_JWK_B64_CHARS) {
334
+ throw new DidError("did/too-long", "did:jwk: encoded JWK exceeds the " + MAX_JWK_B64_CHARS + "-char cap");
335
+ }
336
+ var jwkJson = Buffer.from(parsed.id, "base64url").toString("utf8");
337
+ var jwk;
338
+ try { jwk = safeJson.parse(jwkJson, { maxBytes: MAX_JWK_B64_CHARS }); } catch (_e) {
339
+ throw new DidError("did/bad-jwk", "did:jwk: method-specific id is not base64url-encoded JSON");
340
+ }
341
+ if (!jwk || typeof jwk !== "object" || Array.isArray(jwk)) {
342
+ throw new DidError("did/bad-jwk", "did:jwk: decoded value is not a JWK object");
343
+ }
344
+ var jwkKey = _jwkToKey(jwk); // kty/crv allowlisted
345
+ var jwkVmId = did + "#0";
346
+ var jwkVm = { id: jwkVmId, controller: did, type: "JsonWebKey2020", publicKey: jwkKey };
347
+ var jwkDoc = {
348
+ "@context": ["https://www.w3.org/ns/did/v1"],
349
+ id: did,
350
+ verificationMethod: [{ id: jwkVmId, controller: did, type: "JsonWebKey2020", publicKeyJwk: jwk }],
351
+ assertionMethod: [jwkVmId], authentication: [jwkVmId],
352
+ };
353
+ return { didDocument: jwkDoc, verificationMethods: [jwkVm] };
354
+ }
355
+
356
+ if (parsed.method === "web") {
357
+ if (!opts.document || typeof opts.document !== "object") {
358
+ throw new DidError("did/document-required",
359
+ "did:web: the DID document must be fetched by the operator and passed as opts.document (GET " + parsed.url + ")");
360
+ }
361
+ var docW = opts.document;
362
+ if (docW.id !== did) {
363
+ throw new DidError("did/document-mismatch", "did:web: document id '" + docW.id + "' does not match the requested DID");
364
+ }
365
+ return { didDocument: docW, verificationMethods: _extractVerificationMethods(docW) };
366
+ }
367
+
368
+ throw new DidError("did/unsupported-method", "did.resolve: unsupported DID method '" + parsed.method + "' (did:key and did:web only)");
369
+ }
370
+
371
+ // Import a publicKeyJwk after allowlisting its kty/crv — a DID document
372
+ // is untrusted input, so an unexpected key type (RSA / oct / unknown
373
+ // curve) is refused before it reaches node:crypto rather than blindly
374
+ // imported (the DID-context equivalent of the JWT alg/kty cross-check;
375
+ // there is no single verification `alg` in a DID document).
376
+ function _jwkToKey(jwk) {
377
+ var ok = (jwk.kty === "OKP" && jwk.crv === "Ed25519") ||
378
+ (jwk.kty === "EC" && (jwk.crv === "P-256" || jwk.crv === "P-384" || jwk.crv === "secp256k1"));
379
+ if (!ok) {
380
+ throw new DidError("did/unsupported-key",
381
+ "did: verificationMethod publicKeyJwk has unsupported kty/crv (" + jwk.kty + "/" + jwk.crv + ")");
382
+ }
383
+ try { return nodeCrypto.createPublicKey({ key: jwk, format: "jwk" }); }
384
+ catch (e) { throw new DidError("did/bad-key", "did: verificationMethod publicKeyJwk is invalid: " + ((e && e.message) || e)); }
385
+ }
386
+
387
+ // Extract verification methods from a DID document → KeyObjects.
388
+ function _extractVerificationMethods(doc) {
389
+ var vms = Array.isArray(doc.verificationMethod) ? doc.verificationMethod : [];
390
+ var out = [];
391
+ for (var i = 0; i < vms.length; i += 1) {
392
+ var vm = vms[i];
393
+ if (!vm || typeof vm !== "object") continue;
394
+ var key = null;
395
+ if (typeof vm.publicKeyMultibase === "string" && vm.publicKeyMultibase[0] === "z") {
396
+ var raw = _b58decode(vm.publicKeyMultibase.slice(1));
397
+ var vh = _readVarint(raw);
398
+ key = _keyObjectFromMulticodec(vh.value, raw.slice(vh.length));
399
+ } else if (vm.publicKeyJwk && typeof vm.publicKeyJwk === "object") {
400
+ key = _jwkToKey(vm.publicKeyJwk);
401
+ } else {
402
+ continue; // unknown key encoding — skip rather than guess
403
+ }
404
+ out.push({ id: vm.id, controller: vm.controller, type: vm.type, publicKey: key });
405
+ }
406
+ if (!out.length) throw new DidError("did/no-keys", "did: document has no resolvable verification methods");
407
+ return out;
408
+ }
409
+
410
+ module.exports = {
411
+ parse: parse,
412
+ keyToDid: keyToDid,
413
+ resolve: resolve,
414
+ MULTICODEC: MULTICODEC,
415
+ DidError: DidError,
416
+ };
@@ -251,17 +251,138 @@ async function verifyIssuerSigned(issuerSigned, opts) {
251
251
  _verifyChain(chain, anchors, at);
252
252
  }
253
253
 
254
+ // The device key (MSO deviceKeyInfo.deviceKey, a COSE_Key) binds the
255
+ // holder — surfaced for b.mdoc.verifyDeviceAuth.
256
+ var deviceKeyInfo = _mapGet(mso, "deviceKeyInfo");
257
+ var deviceKey = deviceKeyInfo ? _mapGet(deviceKeyInfo, "deviceKey") : undefined;
258
+
254
259
  return {
255
260
  docType: docType,
256
261
  version: _mapGet(mso, "version"),
257
262
  digestAlgorithm: digestAlgName,
258
263
  validityInfo: { validFrom: new Date(validFromMs), validUntil: new Date(validUntilMs) },
259
264
  namespaces: out,
265
+ deviceKey: deviceKey,
260
266
  signerCert: signerCert.toString(),
261
267
  alg: verified.alg,
262
268
  };
263
269
  }
264
270
 
271
+ /**
272
+ * @primitive b.mdoc.verifyDeviceAuth
273
+ * @signature b.mdoc.verifyDeviceAuth(opts)
274
+ * @since 0.12.46
275
+ * @status experimental
276
+ * @compliance gdpr, soc2
277
+ * @related b.mdoc.verifyIssuerSigned, b.cose.verify
278
+ *
279
+ * Verify the device-authentication half of an ISO 18013-5 mdoc (§9.1.3,
280
+ * signature variant) — the proof that the holder controls the device key
281
+ * the issuer bound into the MSO, which stops a captured issuer-signed
282
+ * document from being replayed by anyone else. The device's COSE_Sign1
283
+ * (<code>deviceSigned.deviceAuth.deviceSignature</code>) is verified over
284
+ * the detached DeviceAuthentication structure
285
+ * (<code>["DeviceAuthentication", SessionTranscript, DocType,
286
+ * DeviceNameSpacesBytes]</code>) with the device key from the issuer-signed
287
+ * MSO (<code>verifyIssuerSigned(...).deviceKey</code>). The
288
+ * <code>sessionTranscript</code> binds the proof to this exact exchange
289
+ * and is supplied by the operator (the presentation protocol — e.g.
290
+ * OpenID4VP — defines it). The MAC variant (<code>deviceMac</code> /
291
+ * COSE_Mac0, used in proximity flows with a reader ephemeral key) is not
292
+ * yet supported and is refused with <code>mdoc/device-mac-unsupported</code>.
293
+ *
294
+ * @opts
295
+ * {
296
+ * deviceKey: object, // COSE_Key (from verifyIssuerSigned().deviceKey) or a KeyObject / PEM
297
+ * deviceSigned: object, // the DeviceSigned structure (CBOR bytes or decoded)
298
+ * docType: string, // the document type (must match the issuer-signed docType)
299
+ * sessionTranscript: any, // the SessionTranscript (CBOR bytes or decoded) bound by the protocol
300
+ * algorithms: string[], // required — accepted COSE alg names (ES256/384/512, EdDSA)
301
+ * maxBytes: number, // forwarded to b.cbor.decode
302
+ * maxDepth: number,
303
+ * }
304
+ *
305
+ * @example
306
+ * var issuer = await b.mdoc.verifyIssuerSigned(issuerSignedBytes, { algorithms: ["ES256"] });
307
+ * var dev = await b.mdoc.verifyDeviceAuth({ deviceKey: issuer.deviceKey, deviceSigned: deviceSignedBytes, docType: issuer.docType, sessionTranscript: transcript, algorithms: ["ES256"] });
308
+ * // → { docType, alg, deviceNamespaces }
309
+ */
310
+ async function verifyDeviceAuth(opts) {
311
+ validateOpts.requireObject(opts, "mdoc.verifyDeviceAuth", MdocError);
312
+ validateOpts(opts, ["deviceKey", "deviceSigned", "docType", "sessionTranscript", "algorithms", "maxBytes", "maxDepth"], "mdoc.verifyDeviceAuth");
313
+ if (!Array.isArray(opts.algorithms) || opts.algorithms.length === 0) {
314
+ throw new MdocError("mdoc/algorithms-required", "mdoc.verifyDeviceAuth: opts.algorithms is required");
315
+ }
316
+ if (typeof opts.docType !== "string" || !opts.docType) {
317
+ throw new MdocError("mdoc/bad-input", "mdoc.verifyDeviceAuth: opts.docType is required");
318
+ }
319
+ if (opts.sessionTranscript === undefined || opts.sessionTranscript === null) {
320
+ throw new MdocError("mdoc/no-session-transcript", "mdoc.verifyDeviceAuth: opts.sessionTranscript is required (the protocol-bound transcript)");
321
+ }
322
+ var decodeOpts = { allowedTags: ALLOWED_TAGS, maxBytes: opts.maxBytes, maxDepth: opts.maxDepth };
323
+
324
+ // Device key → KeyObject. Accept a COSE_Key (Map/object) via importKey,
325
+ // or an already-loaded KeyObject / PEM.
326
+ var deviceKeyObj;
327
+ if (opts.deviceKey && typeof opts.deviceKey === "object" && typeof opts.deviceKey.asymmetricKeyType === "string") {
328
+ deviceKeyObj = opts.deviceKey;
329
+ } else if (opts.deviceKey instanceof Map || (opts.deviceKey && typeof opts.deviceKey === "object")) {
330
+ deviceKeyObj = cose.importKey(opts.deviceKey);
331
+ } else if (typeof opts.deviceKey === "string") {
332
+ deviceKeyObj = opts.deviceKey; // PEM, resolved by b.cose
333
+ } else {
334
+ throw new MdocError("mdoc/no-device-key", "mdoc.verifyDeviceAuth: opts.deviceKey is required (a COSE_Key or KeyObject)");
335
+ }
336
+
337
+ var ds = (Buffer.isBuffer(opts.deviceSigned) || opts.deviceSigned instanceof Uint8Array)
338
+ ? cbor.decode(_bytes(opts.deviceSigned, "deviceSigned"), decodeOpts) : opts.deviceSigned;
339
+ var deviceNameSpaces = _mapGet(ds, "nameSpaces");
340
+ var deviceAuth = _mapGet(ds, "deviceAuth");
341
+ if (!deviceNameSpaces || !deviceAuth) {
342
+ throw new MdocError("mdoc/malformed", "mdoc.verifyDeviceAuth: deviceSigned must have nameSpaces + deviceAuth");
343
+ }
344
+ if (!(deviceNameSpaces instanceof cbor.Tag) || deviceNameSpaces.tag !== TAG_ENCODED_CBOR) {
345
+ throw new MdocError("mdoc/malformed", "mdoc.verifyDeviceAuth: deviceSigned.nameSpaces must be a Tag-24 DeviceNameSpacesBytes");
346
+ }
347
+ var deviceSignature = _mapGet(deviceAuth, "deviceSignature");
348
+ if (!deviceSignature) {
349
+ if (_mapGet(deviceAuth, "deviceMac") !== undefined) {
350
+ throw new MdocError("mdoc/device-mac-unsupported",
351
+ "mdoc.verifyDeviceAuth: the MAC variant (deviceMac / COSE_Mac0) is not supported — only deviceSignature");
352
+ }
353
+ throw new MdocError("mdoc/no-device-signature", "mdoc.verifyDeviceAuth: deviceAuth has no deviceSignature");
354
+ }
355
+
356
+ var st = (Buffer.isBuffer(opts.sessionTranscript) || opts.sessionTranscript instanceof Uint8Array)
357
+ ? cbor.decode(_bytes(opts.sessionTranscript, "sessionTranscript"), decodeOpts) : opts.sessionTranscript;
358
+
359
+ // DeviceAuthentication (ISO 18013-5 §9.1.3.4); the detached payload is
360
+ // its Tag-24-wrapped CBOR.
361
+ var deviceAuthentication = ["DeviceAuthentication", st, opts.docType, deviceNameSpaces];
362
+ var deviceAuthBytes = cbor.encode(new cbor.Tag(TAG_ENCODED_CBOR, cbor.encode(deviceAuthentication)));
363
+
364
+ var coseBytes = Array.isArray(deviceSignature) ? cbor.encode(deviceSignature) : _bytes(deviceSignature, "deviceSignature");
365
+ var out = await cose.verify(coseBytes, {
366
+ algorithms: opts.algorithms,
367
+ keyResolver: function () { return deviceKeyObj; },
368
+ externalPayload: deviceAuthBytes,
369
+ maxBytes: opts.maxBytes,
370
+ maxDepth: opts.maxDepth,
371
+ });
372
+
373
+ var deviceNamespaces = {};
374
+ try {
375
+ var dns = cbor.decode(deviceNameSpaces.value, decodeOpts);
376
+ if (dns instanceof Map) {
377
+ dns.forEach(function (items, ns) {
378
+ deviceNamespaces[ns] = items instanceof Map ? Object.fromEntries(items) : items;
379
+ });
380
+ }
381
+ } catch (_e) { /* device-released namespaces are optional + advisory */ }
382
+
383
+ return { docType: opts.docType, alg: out.alg, deviceNamespaces: deviceNamespaces };
384
+ }
385
+
265
386
  // Verify the leaf (chain[0]) chains to a supplied anchor and every cert
266
387
  // is valid at `at`. Intermediates in the x5chain are consulted.
267
388
  function _verifyChain(chainDer, anchorsPem, at) {
@@ -300,6 +421,7 @@ function _assertValidAt(cert, atMs) {
300
421
 
301
422
  module.exports = {
302
423
  verifyIssuerSigned: verifyIssuerSigned,
424
+ verifyDeviceAuth: verifyDeviceAuth,
303
425
  DIGEST_ALGS: DIGEST_ALGS,
304
426
  MdocError: MdocError,
305
427
  };