@blamejs/blamejs-shop 0.1.31 → 0.1.33
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 +4 -0
- package/README.md +4 -0
- package/lib/admin.js +18 -10
- package/lib/asset-manifest.json +17 -5
- package/lib/bundles.js +28 -0
- package/lib/checkout.js +48 -2
- package/lib/storefront.js +792 -45
- package/lib/vendor/MANIFEST.json +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +8 -0
- package/lib/vendor/blamejs/README.md +3 -1
- package/lib/vendor/blamejs/api-snapshot.json +99 -2
- package/lib/vendor/blamejs/index.js +3 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +110 -0
- package/lib/vendor/blamejs/lib/network-tsig.js +404 -0
- package/lib/vendor/blamejs/lib/network.js +1 -0
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +44 -9
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +19 -0
- package/lib/vendor/blamejs/lib/worm.js +246 -0
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.12.x.json +1844 -0
- package/lib/vendor/blamejs/release-notes/v0.13.0.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.13.1.json +18 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +11 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tsig.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/testing.test.js +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/worm.test.js +126 -0
- package/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.12.0.json +0 -64
- package/lib/vendor/blamejs/release-notes/v0.12.1.json +0 -32
- package/lib/vendor/blamejs/release-notes/v0.12.10.json +0 -65
- package/lib/vendor/blamejs/release-notes/v0.12.11.json +0 -39
- package/lib/vendor/blamejs/release-notes/v0.12.12.json +0 -48
- package/lib/vendor/blamejs/release-notes/v0.12.13.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.12.14.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.15.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.16.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.17.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.18.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.19.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.2.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.12.20.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.21.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.22.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.23.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.24.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.25.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.26.json +0 -30
- package/lib/vendor/blamejs/release-notes/v0.12.27.json +0 -26
- package/lib/vendor/blamejs/release-notes/v0.12.28.json +0 -26
- package/lib/vendor/blamejs/release-notes/v0.12.29.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.12.3.json +0 -23
- package/lib/vendor/blamejs/release-notes/v0.12.30.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.31.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.32.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.33.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.12.34.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.35.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.36.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.37.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.38.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.39.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.4.json +0 -19
- package/lib/vendor/blamejs/release-notes/v0.12.40.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.41.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.42.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.43.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.44.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.45.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.46.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.47.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.48.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.49.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.12.5.json +0 -40
- package/lib/vendor/blamejs/release-notes/v0.12.50.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.51.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.52.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.53.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.54.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.55.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.56.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.57.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.58.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.6.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.12.60.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.61.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.62.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.63.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.64.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.65.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.66.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.68.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.69.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.7.json +0 -86
- package/lib/vendor/blamejs/release-notes/v0.12.8.json +0 -81
- package/lib/vendor/blamejs/release-notes/v0.12.9.json +0 -61
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module b.network.dns.tsig
|
|
4
|
+
* @nav Network
|
|
5
|
+
* @title DNS TSIG
|
|
6
|
+
*
|
|
7
|
+
* @intro
|
|
8
|
+
* Sign and verify DNS messages with <a
|
|
9
|
+
* href="https://www.rfc-editor.org/rfc/rfc8945">RFC 8945</a> TSIG
|
|
10
|
+
* (Transaction SIGnature) — the shared-key HMAC that authenticates the
|
|
11
|
+
* transaction between a resolver and a server (zone transfers, dynamic
|
|
12
|
+
* updates, and any query/response pair) and proves it was not tampered
|
|
13
|
+
* with in flight. TSIG complements the existing DNSSEC and DANE
|
|
14
|
+
* primitives: DNSSEC authenticates zone <em>data</em> end-to-end, while
|
|
15
|
+
* TSIG authenticates a single hop's <em>transaction</em> with a
|
|
16
|
+
* pre-shared key.
|
|
17
|
+
*
|
|
18
|
+
* <code>sign(message, opts)</code> appends a TSIG resource record to a
|
|
19
|
+
* DNS message and returns the signed wire bytes;
|
|
20
|
+
* <code>verify(message, opts)</code> locates the TSIG record, recomputes
|
|
21
|
+
* the HMAC over the RFC 8945 §4.3.3 digest, compares it in constant time,
|
|
22
|
+
* and checks the time window (the signature is only valid within
|
|
23
|
+
* <code>fudge</code> seconds of <code>timeSigned</code>). The default MAC
|
|
24
|
+
* algorithm is HMAC-SHA-256; SHA-384 / SHA-512 are available, and the
|
|
25
|
+
* broken HMAC-MD5 / HMAC-SHA-1 algorithms are refused unless
|
|
26
|
+
* <code>allowLegacy</code> is set. Signing a response chains the
|
|
27
|
+
* request's MAC into the digest (<code>requestMac</code>) per §5.4.1.
|
|
28
|
+
*
|
|
29
|
+
* @card
|
|
30
|
+
* RFC 8945 DNS TSIG — shared-key HMAC transaction authentication for DNS
|
|
31
|
+
* messages (sign / verify, constant-time MAC compare, time-window check,
|
|
32
|
+
* HMAC-SHA-256 default). The transaction-level companion to DNSSEC + DANE.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
var nodeCrypto = require("node:crypto");
|
|
36
|
+
var validateOpts = require("./validate-opts");
|
|
37
|
+
var { timingSafeEqual } = require("./crypto");
|
|
38
|
+
var { defineClass } = require("./framework-error");
|
|
39
|
+
|
|
40
|
+
var TsigError = defineClass("TsigError", { alwaysPermanent: true });
|
|
41
|
+
|
|
42
|
+
var TYPE_TSIG = 250; // allow:raw-byte-literal — IANA RR type TSIG
|
|
43
|
+
var CLASS_ANY = 255; // allow:raw-byte-literal — TSIG RRs use CLASS ANY
|
|
44
|
+
var DEFAULT_FUDGE = 300; // allow:raw-time-literal — RFC 8945 recommended fudge window (seconds)
|
|
45
|
+
|
|
46
|
+
// Algorithm name → Node hash. The strong HMAC-SHA-2 family is the safe set;
|
|
47
|
+
// HMAC-MD5 and HMAC-SHA-1 are refused unless allowLegacy (kept only for
|
|
48
|
+
// interop with legacy nameservers).
|
|
49
|
+
var ALGORITHMS = {
|
|
50
|
+
"hmac-sha256": "sha256",
|
|
51
|
+
"hmac-sha384": "sha384",
|
|
52
|
+
"hmac-sha512": "sha512",
|
|
53
|
+
"hmac-sha224": "sha224",
|
|
54
|
+
};
|
|
55
|
+
var LEGACY_ALGORITHMS = {
|
|
56
|
+
"hmac-sha1": "sha1",
|
|
57
|
+
"hmac-md5": "md5",
|
|
58
|
+
};
|
|
59
|
+
// RFC 8945 §5.2.2.1 — TSIG error RCODEs.
|
|
60
|
+
var ERROR = { NOERROR: 0, BADSIG: 16, BADKEY: 17, BADTIME: 18, BADTRUNC: 22 }; // allow:raw-byte-literal — RFC 8945 extended-RCODE values
|
|
61
|
+
|
|
62
|
+
function _normAlg(name, allowLegacy) {
|
|
63
|
+
var key = String(name || "hmac-sha256").toLowerCase().replace(/\.$/, "");
|
|
64
|
+
if (ALGORITHMS[key]) return { name: key, hash: ALGORITHMS[key] };
|
|
65
|
+
if (LEGACY_ALGORITHMS[key]) {
|
|
66
|
+
if (!allowLegacy) throw new TsigError("tsig/legacy-algorithm", "tsig: algorithm '" + key + "' is broken; pass allowLegacy:true to permit it for legacy interop");
|
|
67
|
+
return { name: key, hash: LEGACY_ALGORITHMS[key] };
|
|
68
|
+
}
|
|
69
|
+
throw new TsigError("tsig/bad-algorithm", "tsig: unknown algorithm '" + key + "'");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function _secretBuf(secret) {
|
|
73
|
+
if (Buffer.isBuffer(secret)) return secret;
|
|
74
|
+
if (typeof secret === "string") {
|
|
75
|
+
// TSIG keys are conventionally transported as base64.
|
|
76
|
+
var b = Buffer.from(secret, "base64");
|
|
77
|
+
if (b.length === 0 && secret.length > 0) throw new TsigError("tsig/bad-secret", "tsig: secret must be base64 or a Buffer");
|
|
78
|
+
return b;
|
|
79
|
+
}
|
|
80
|
+
throw new TsigError("tsig/bad-secret", "tsig: secret must be a base64 string or Buffer");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Encode a domain name to uncompressed wire form (labels), lower-casing is
|
|
84
|
+
// NOT applied — TSIG uses the names as presented (key names are
|
|
85
|
+
// conventionally lower-case already; algorithm names are canonical).
|
|
86
|
+
function _encodeName(name) {
|
|
87
|
+
var n = String(name).replace(/\.$/, "");
|
|
88
|
+
if (n === "") return Buffer.from([0]);
|
|
89
|
+
var parts = n.split(".");
|
|
90
|
+
var out = [];
|
|
91
|
+
for (var i = 0; i < parts.length; i++) {
|
|
92
|
+
var lab = Buffer.from(parts[i], "ascii");
|
|
93
|
+
if (lab.length === 0 || lab.length > 63) throw new TsigError("tsig/bad-name", "tsig: invalid label in name '" + name + "'"); // allow:raw-byte-literal — RFC 1035 max label length
|
|
94
|
+
out.push(Buffer.from([lab.length]), lab);
|
|
95
|
+
}
|
|
96
|
+
out.push(Buffer.from([0]));
|
|
97
|
+
return Buffer.concat(out);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Read a domain name starting at off, returning { name, end }. Handles a
|
|
101
|
+
// compression pointer as a terminal jump (TSIG only needs the END offset to
|
|
102
|
+
// keep walking; the pointed-at labels are resolved for the name string).
|
|
103
|
+
function _readName(buf, off) {
|
|
104
|
+
var labels = [];
|
|
105
|
+
var i = off;
|
|
106
|
+
var end = -1;
|
|
107
|
+
var jumps = 0;
|
|
108
|
+
for (;;) {
|
|
109
|
+
if (i >= buf.length) throw new TsigError("tsig/truncated", "tsig: truncated name in message");
|
|
110
|
+
var len = buf[i];
|
|
111
|
+
if (len === 0) { if (end === -1) end = i + 1; break; }
|
|
112
|
+
if ((len & 0xc0) === 0xc0) { // allow:raw-byte-literal — RFC 1035 §4.1.4 compression-pointer flag
|
|
113
|
+
if (i + 1 >= buf.length) throw new TsigError("tsig/truncated", "tsig: truncated compression pointer");
|
|
114
|
+
if (end === -1) end = i + 2;
|
|
115
|
+
var ptr = ((len & 0x3f) << 8) | buf[i + 1]; // allow:raw-byte-literal — 14-bit pointer offset
|
|
116
|
+
if (++jumps > 128) throw new TsigError("tsig/bad-name", "tsig: compression-pointer loop"); // allow:raw-byte-literal — pointer-chase cap
|
|
117
|
+
i = ptr;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if ((len & 0xc0) !== 0) throw new TsigError("tsig/bad-name", "tsig: reserved label-length bits set"); // allow:raw-byte-literal — RFC 1035 label top-bits
|
|
121
|
+
i++;
|
|
122
|
+
labels.push(buf.slice(i, i + len).toString("ascii"));
|
|
123
|
+
i += len;
|
|
124
|
+
}
|
|
125
|
+
return { name: labels.length ? labels.join(".") + "." : ".", end: end };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Skip a name, returning the offset after it (compression-pointer aware).
|
|
129
|
+
function _skipName(buf, off) {
|
|
130
|
+
var i = off;
|
|
131
|
+
for (;;) {
|
|
132
|
+
if (i >= buf.length) throw new TsigError("tsig/truncated", "tsig: truncated name");
|
|
133
|
+
var len = buf[i];
|
|
134
|
+
if (len === 0) return i + 1;
|
|
135
|
+
if ((len & 0xc0) === 0xc0) return i + 2; // allow:raw-byte-literal — compression pointer is terminal
|
|
136
|
+
if ((len & 0xc0) !== 0) throw new TsigError("tsig/bad-name", "tsig: reserved label-length bits set"); // allow:raw-byte-literal — RFC 1035 label top-bits
|
|
137
|
+
i += 1 + len;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Walk the message to the start of the LAST resource record, which a
|
|
142
|
+
// TSIG-bearing message requires to be the TSIG RR (RFC 8945 §5.1).
|
|
143
|
+
function _findTsigRr(buf) {
|
|
144
|
+
if (buf.length < 12) throw new TsigError("tsig/truncated", "tsig: message shorter than the 12-byte header"); // allow:raw-byte-literal — DNS header length
|
|
145
|
+
var qd = buf.readUInt16BE(4), an = buf.readUInt16BE(6), ns = buf.readUInt16BE(8), ar = buf.readUInt16BE(10);
|
|
146
|
+
if (ar < 1) throw new TsigError("tsig/no-tsig", "tsig: message has no additional records (no TSIG)");
|
|
147
|
+
var off = 12; // allow:raw-byte-literal — past the DNS header
|
|
148
|
+
var q;
|
|
149
|
+
for (q = 0; q < qd; q++) { off = _skipName(buf, off); off += 4; } // allow:raw-byte-literal — QTYPE + QCLASS
|
|
150
|
+
var total = an + ns + ar;
|
|
151
|
+
var rrStart = -1;
|
|
152
|
+
for (var r = 0; r < total; r++) {
|
|
153
|
+
rrStart = off;
|
|
154
|
+
off = _skipName(buf, off);
|
|
155
|
+
if (off + 10 > buf.length) throw new TsigError("tsig/truncated", "tsig: truncated RR header"); // allow:raw-byte-literal — type+class+ttl+rdlength
|
|
156
|
+
var rdlen = buf.readUInt16BE(off + 8); // allow:raw-byte-literal — rdlength offset within RR header
|
|
157
|
+
off += 10 + rdlen; // allow:raw-byte-literal — RR fixed header before RDATA
|
|
158
|
+
}
|
|
159
|
+
if (off !== buf.length) throw new TsigError("tsig/trailing-bytes", "tsig: trailing bytes after the final record");
|
|
160
|
+
return rrStart;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Build the TSIG-variables byte block (RFC 8945 §4.3.3).
|
|
164
|
+
function _tsigVariables(keyName, algName, timeSigned, fudge, error, otherData) {
|
|
165
|
+
var time = Buffer.alloc(6); // allow:raw-byte-literal — 48-bit time-signed field
|
|
166
|
+
time.writeUIntBE(timeSigned, 0, 6); // allow:raw-byte-literal — 48-bit big-endian
|
|
167
|
+
var head = Buffer.alloc(6); // allow:raw-byte-literal — CLASS(2) + TTL(4)
|
|
168
|
+
head.writeUInt16BE(CLASS_ANY, 0);
|
|
169
|
+
head.writeUInt32BE(0, 2); // TTL is always 0 (4 bytes)
|
|
170
|
+
var tail = Buffer.alloc(6); // allow:raw-byte-literal — fudge(2)+error(2)+otherlen(2)
|
|
171
|
+
tail.writeUInt16BE(fudge, 0);
|
|
172
|
+
tail.writeUInt16BE(error, 2);
|
|
173
|
+
tail.writeUInt16BE(otherData.length, 4);
|
|
174
|
+
// DNS names are case-insensitive and the TSIG digest uses their canonical
|
|
175
|
+
// (lower-cased) form (RFC 8945 §4.3.3 / RFC 4034 §6.2) — the on-the-wire
|
|
176
|
+
// RR may carry any case, but both signer and verifier digest the
|
|
177
|
+
// lower-cased name, so the MAC is stable across case differences.
|
|
178
|
+
return Buffer.concat([_encodeName(String(keyName).toLowerCase()), head, _encodeName(String(algName).toLowerCase()), time, tail, otherData]);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function _requestMacPrefix(requestMac) {
|
|
182
|
+
if (!requestMac) return Buffer.alloc(0);
|
|
183
|
+
var len = Buffer.alloc(2);
|
|
184
|
+
len.writeUInt16BE(requestMac.length, 0);
|
|
185
|
+
return Buffer.concat([len, requestMac]);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* @primitive b.network.dns.tsig.sign
|
|
190
|
+
* @signature b.network.dns.tsig.sign(message, opts)
|
|
191
|
+
* @since 0.12.70
|
|
192
|
+
* @status stable
|
|
193
|
+
* @related b.network.dns.tsig.verify
|
|
194
|
+
*
|
|
195
|
+
* Append a TSIG resource record to a DNS message (a Buffer of wire bytes)
|
|
196
|
+
* and return the signed wire Buffer. The MAC is the HMAC over the message
|
|
197
|
+
* plus the RFC 8945 §4.3.3 TSIG variables. Returns
|
|
198
|
+
* <code>{ wire, mac }</code> — <code>wire</code> is the message with the
|
|
199
|
+
* TSIG RR appended and ARCOUNT incremented, and <code>mac</code> is the raw
|
|
200
|
+
* HMAC (keep it to verify the matching response).
|
|
201
|
+
*
|
|
202
|
+
* @opts
|
|
203
|
+
* keyName: string, // REQUIRED — the shared-key name
|
|
204
|
+
* secret: string | Buffer, // REQUIRED — base64 string or raw bytes
|
|
205
|
+
* algorithm: string, // default: "hmac-sha256"
|
|
206
|
+
* fudge: number, // default: 300 (seconds)
|
|
207
|
+
* time: number, // default: now (Unix seconds)
|
|
208
|
+
* originalId: number, // default: the message's own ID
|
|
209
|
+
* requestMac: Buffer, // when signing a response (§5.4.1)
|
|
210
|
+
* error: number, // default: 0 (NOERROR)
|
|
211
|
+
* otherData: Buffer, // default: empty
|
|
212
|
+
* allowLegacy: boolean, // permit HMAC-MD5 / HMAC-SHA-1
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* var signed = b.network.dns.tsig.sign(queryWire, {
|
|
216
|
+
* keyName: "update.key.", secret: "<base64-secret>",
|
|
217
|
+
* });
|
|
218
|
+
* socket.send(signed.wire);
|
|
219
|
+
*/
|
|
220
|
+
function sign(message, opts) {
|
|
221
|
+
opts = opts || {};
|
|
222
|
+
if (!Buffer.isBuffer(message) || message.length < 12) throw new TsigError("tsig/bad-message", "tsig.sign: message must be a DNS wire Buffer");
|
|
223
|
+
validateOpts.requireNonEmptyString(opts.keyName, "tsig.sign: keyName", TsigError, "tsig/bad-opt");
|
|
224
|
+
var alg = _normAlg(opts.algorithm, opts.allowLegacy === true);
|
|
225
|
+
var secret = _secretBuf(opts.secret);
|
|
226
|
+
var fudge = opts.fudge == null ? DEFAULT_FUDGE : opts.fudge;
|
|
227
|
+
if (typeof fudge !== "number" || !isFinite(fudge) || fudge < 0 || fudge > 0xffff) throw new TsigError("tsig/bad-opt", "tsig.sign: fudge must be 0..65535 seconds"); // allow:raw-byte-literal — 16-bit fudge field
|
|
228
|
+
var time = opts.time == null ? Math.floor(Date.now() / 1000) : opts.time; // allow:raw-time-literal — ms→s
|
|
229
|
+
if (typeof time !== "number" || !isFinite(time) || time < 0) throw new TsigError("tsig/bad-opt", "tsig.sign: time must be a non-negative Unix-seconds number");
|
|
230
|
+
var error = opts.error == null ? 0 : opts.error;
|
|
231
|
+
var otherData = Buffer.isBuffer(opts.otherData) ? opts.otherData : Buffer.alloc(0);
|
|
232
|
+
var originalId = opts.originalId == null ? message.readUInt16BE(0) : opts.originalId;
|
|
233
|
+
var algName = alg.name + ".";
|
|
234
|
+
|
|
235
|
+
var digest = Buffer.concat([
|
|
236
|
+
_requestMacPrefix(opts.requestMac),
|
|
237
|
+
message,
|
|
238
|
+
_tsigVariables(opts.keyName, algName, time, fudge, error, otherData),
|
|
239
|
+
]);
|
|
240
|
+
var mac = nodeCrypto.createHmac(alg.hash, secret).update(digest).digest();
|
|
241
|
+
|
|
242
|
+
// TSIG RDATA: algorithm name, time signed, fudge, MAC size + MAC,
|
|
243
|
+
// original ID, error, other len + other data.
|
|
244
|
+
var rtime = Buffer.alloc(6); rtime.writeUIntBE(time, 0, 6); // allow:raw-byte-literal — 48-bit time-signed
|
|
245
|
+
var fixed = Buffer.alloc(4); // allow:raw-byte-literal — fudge(2)+macsize(2)
|
|
246
|
+
fixed.writeUInt16BE(fudge, 0);
|
|
247
|
+
fixed.writeUInt16BE(mac.length, 2);
|
|
248
|
+
var trailer = Buffer.alloc(6); // allow:raw-byte-literal — origid(2)+error(2)+otherlen(2)
|
|
249
|
+
trailer.writeUInt16BE(originalId, 0);
|
|
250
|
+
trailer.writeUInt16BE(error, 2);
|
|
251
|
+
trailer.writeUInt16BE(otherData.length, 4);
|
|
252
|
+
var rdata = Buffer.concat([_encodeName(algName), rtime, fixed, mac, trailer, otherData]);
|
|
253
|
+
|
|
254
|
+
var rrHead = Buffer.alloc(10); // allow:raw-byte-literal — type+class+ttl+rdlength
|
|
255
|
+
rrHead.writeUInt16BE(TYPE_TSIG, 0);
|
|
256
|
+
rrHead.writeUInt16BE(CLASS_ANY, 2);
|
|
257
|
+
rrHead.writeUInt32BE(0, 4); // TTL 0
|
|
258
|
+
rrHead.writeUInt16BE(rdata.length, 8); // allow:raw-byte-literal — rdlength offset within the 10-byte RR header
|
|
259
|
+
var tsigRr = Buffer.concat([_encodeName(opts.keyName), rrHead, rdata]);
|
|
260
|
+
|
|
261
|
+
var out = Buffer.from(message); // copy so we can bump ARCOUNT
|
|
262
|
+
out.writeUInt16BE(out.readUInt16BE(10) + 1, 10); // allow:raw-byte-literal — ARCOUNT offset
|
|
263
|
+
return { wire: Buffer.concat([out, tsigRr]), mac: mac };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function _parseTsigRr(buf, rrStart) {
|
|
267
|
+
var n = _readName(buf, rrStart);
|
|
268
|
+
var off = n.end;
|
|
269
|
+
var type = buf.readUInt16BE(off);
|
|
270
|
+
if (type !== TYPE_TSIG) throw new TsigError("tsig/not-tsig", "tsig: the final record is not a TSIG RR (type " + type + ")");
|
|
271
|
+
// The MAC digest hard-codes CLASS ANY / TTL 0, so the on-wire CLASS and
|
|
272
|
+
// TTL are outside the signed data — they MUST be validated explicitly or
|
|
273
|
+
// an attacker could flip them in transit and still verify (RFC 8945 §4.2:
|
|
274
|
+
// CLASS = ANY, TTL = 0).
|
|
275
|
+
var rrClass = buf.readUInt16BE(off + 2); // allow:raw-byte-literal — CLASS offset within RR header
|
|
276
|
+
var rrTtl = buf.readUInt32BE(off + 4); // allow:raw-byte-literal — TTL offset within RR header
|
|
277
|
+
if (rrClass !== CLASS_ANY) throw new TsigError("tsig/bad-rr", "tsig: TSIG RR CLASS must be ANY (255), got " + rrClass);
|
|
278
|
+
if (rrTtl !== 0) throw new TsigError("tsig/bad-rr", "tsig: TSIG RR TTL must be 0, got " + rrTtl);
|
|
279
|
+
off += 8; // allow:raw-byte-literal — type(2)+class(2)+ttl(4)
|
|
280
|
+
var rdlen = buf.readUInt16BE(off); off += 2;
|
|
281
|
+
var rdStart = off;
|
|
282
|
+
var alg = _readName(buf, off); off = alg.end;
|
|
283
|
+
var timeSigned = buf.readUIntBE(off, 6); off += 6; // allow:raw-byte-literal — 48-bit time-signed
|
|
284
|
+
var fudge = buf.readUInt16BE(off); off += 2;
|
|
285
|
+
var macSize = buf.readUInt16BE(off); off += 2;
|
|
286
|
+
var mac = buf.slice(off, off + macSize); off += macSize;
|
|
287
|
+
var originalId = buf.readUInt16BE(off); off += 2;
|
|
288
|
+
var error = buf.readUInt16BE(off); off += 2;
|
|
289
|
+
var otherLen = buf.readUInt16BE(off); off += 2;
|
|
290
|
+
var otherData = buf.slice(off, off + otherLen); off += otherLen;
|
|
291
|
+
if (off !== rdStart + rdlen) throw new TsigError("tsig/bad-rdata", "tsig: RDATA length mismatch");
|
|
292
|
+
return {
|
|
293
|
+
keyName: n.name, algName: alg.name.replace(/\.$/, ""), timeSigned: timeSigned, fudge: fudge,
|
|
294
|
+
mac: mac, originalId: originalId, error: error, otherData: otherData, rrStart: rrStart,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* @primitive b.network.dns.tsig.verify
|
|
300
|
+
* @signature b.network.dns.tsig.verify(message, opts)
|
|
301
|
+
* @since 0.12.70
|
|
302
|
+
* @status stable
|
|
303
|
+
* @related b.network.dns.tsig.sign
|
|
304
|
+
*
|
|
305
|
+
* Verify the TSIG record on a DNS message: locate the trailing TSIG RR,
|
|
306
|
+
* recompute the HMAC over the RFC 8945 §4.3.3 digest, compare it in
|
|
307
|
+
* constant time, and check that <code>now</code> is within
|
|
308
|
+
* <code>fudge</code> seconds of <code>timeSigned</code>. Returns
|
|
309
|
+
* <code>{ valid, keyName, algorithm, timeSigned, fudge, error, macValid,
|
|
310
|
+
* timeValid, reason }</code>; <code>valid</code> is true only when the MAC
|
|
311
|
+
* matches, the time window holds, and the embedded error is NOERROR. Never
|
|
312
|
+
* throws for an authentication failure — only for a malformed message or
|
|
313
|
+
* unknown key shape.
|
|
314
|
+
*
|
|
315
|
+
* @opts
|
|
316
|
+
* keys: object, // { "<keyName>": { secret, algorithm } }
|
|
317
|
+
* keyName: string, // single-key form (with secret)
|
|
318
|
+
* secret: string | Buffer, // single-key form
|
|
319
|
+
* algorithm: string, // expected algorithm (single-key form)
|
|
320
|
+
* now: number, // default: now (Unix seconds)
|
|
321
|
+
* requestMac: Buffer, // when verifying a response (§5.4.1)
|
|
322
|
+
* allowLegacy: boolean,
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* var r = b.network.dns.tsig.verify(received, {
|
|
326
|
+
* keys: { "update.key.": { secret: "<base64>" } },
|
|
327
|
+
* });
|
|
328
|
+
* if (!r.valid) refuse(r.reason);
|
|
329
|
+
*/
|
|
330
|
+
function verify(message, opts) {
|
|
331
|
+
opts = opts || {};
|
|
332
|
+
if (!Buffer.isBuffer(message) || message.length < 12) throw new TsigError("tsig/bad-message", "tsig.verify: message must be a DNS wire Buffer");
|
|
333
|
+
var rrStart = _findTsigRr(message);
|
|
334
|
+
var rr = _parseTsigRr(message, rrStart);
|
|
335
|
+
|
|
336
|
+
// Resolve the key for this RR's owner name. DNS names are
|
|
337
|
+
// case-insensitive, so match the lower-cased, dot-trimmed forms.
|
|
338
|
+
function _normName(s) { return String(s).toLowerCase().replace(/\.$/, ""); }
|
|
339
|
+
var rrKeyNorm = _normName(rr.keyName);
|
|
340
|
+
var keyEntry = null;
|
|
341
|
+
if (opts.keys && typeof opts.keys === "object") {
|
|
342
|
+
var ks = Object.keys(opts.keys);
|
|
343
|
+
for (var ki = 0; ki < ks.length; ki++) {
|
|
344
|
+
if (_normName(ks[ki]) === rrKeyNorm) { keyEntry = opts.keys[ks[ki]]; break; }
|
|
345
|
+
}
|
|
346
|
+
} else if (opts.keyName != null && _normName(opts.keyName) === rrKeyNorm) {
|
|
347
|
+
keyEntry = { secret: opts.secret, algorithm: opts.algorithm };
|
|
348
|
+
}
|
|
349
|
+
if (!keyEntry) {
|
|
350
|
+
return { valid: false, keyName: rr.keyName, algorithm: rr.algName, timeSigned: rr.timeSigned, fudge: rr.fudge, error: ERROR.BADKEY, macValid: false, timeValid: false, reason: "unknown key '" + rr.keyName + "'" };
|
|
351
|
+
}
|
|
352
|
+
var alg = _normAlg(keyEntry.algorithm || rr.algName, opts.allowLegacy === true);
|
|
353
|
+
// The RR's algorithm must match the key's expected algorithm
|
|
354
|
+
// (case-insensitive — _normAlg already lower-cases alg.name).
|
|
355
|
+
if (alg.name !== rr.algName.toLowerCase()) {
|
|
356
|
+
return { valid: false, keyName: rr.keyName, algorithm: rr.algName, timeSigned: rr.timeSigned, fudge: rr.fudge, error: ERROR.BADKEY, macValid: false, timeValid: false, reason: "algorithm mismatch (key expects " + alg.name + ", message used " + rr.algName + ")" };
|
|
357
|
+
}
|
|
358
|
+
var secret = _secretBuf(keyEntry.secret);
|
|
359
|
+
|
|
360
|
+
// Reconstruct the digested message: bytes before the TSIG RR, with
|
|
361
|
+
// ARCOUNT decremented and the ID restored to the original ID.
|
|
362
|
+
var digestMsg = Buffer.from(message.slice(0, rrStart));
|
|
363
|
+
digestMsg.writeUInt16BE(rr.originalId, 0);
|
|
364
|
+
digestMsg.writeUInt16BE(digestMsg.readUInt16BE(10) - 1, 10); // allow:raw-byte-literal — ARCOUNT offset
|
|
365
|
+
|
|
366
|
+
var digest = Buffer.concat([
|
|
367
|
+
_requestMacPrefix(opts.requestMac),
|
|
368
|
+
digestMsg,
|
|
369
|
+
_tsigVariables(rr.keyName, rr.algName + ".", rr.timeSigned, rr.fudge, rr.error, rr.otherData),
|
|
370
|
+
]);
|
|
371
|
+
var expected = nodeCrypto.createHmac(alg.hash, secret).update(digest).digest();
|
|
372
|
+
|
|
373
|
+
// Constant-time compare. A truncated MAC (macSize < full) is only valid
|
|
374
|
+
// down to the RFC 8945 §5.2.2.1 floor of max(10, fullLen/2) octets.
|
|
375
|
+
var macValid = false;
|
|
376
|
+
if (rr.mac.length === expected.length) {
|
|
377
|
+
macValid = timingSafeEqual(rr.mac, expected);
|
|
378
|
+
} else if (rr.mac.length >= Math.max(10, expected.length / 2) && rr.mac.length < expected.length) { // allow:raw-byte-literal — RFC 8945 §5.2.2.1 minimum truncated-MAC length
|
|
379
|
+
macValid = timingSafeEqual(rr.mac, expected.slice(0, rr.mac.length));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
var now = opts.now == null ? Math.floor(Date.now() / 1000) : opts.now; // allow:raw-time-literal — ms→s
|
|
383
|
+
var timeValid = Math.abs(now - rr.timeSigned) <= rr.fudge;
|
|
384
|
+
|
|
385
|
+
var reason = null;
|
|
386
|
+
if (!macValid) reason = "MAC mismatch";
|
|
387
|
+
else if (!timeValid) reason = "time outside fudge window";
|
|
388
|
+
else if (rr.error !== ERROR.NOERROR) reason = "TSIG error code " + rr.error;
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
valid: macValid && timeValid && rr.error === ERROR.NOERROR,
|
|
392
|
+
keyName: rr.keyName, algorithm: rr.algName, timeSigned: rr.timeSigned, fudge: rr.fudge,
|
|
393
|
+
error: macValid ? (timeValid ? rr.error : ERROR.BADTIME) : ERROR.BADSIG,
|
|
394
|
+
macValid: macValid, timeValid: timeValid, reason: reason,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
module.exports = {
|
|
399
|
+
sign: sign,
|
|
400
|
+
verify: verify,
|
|
401
|
+
ALGORITHMS: ALGORITHMS,
|
|
402
|
+
ERROR: ERROR,
|
|
403
|
+
TsigError: TsigError,
|
|
404
|
+
};
|
|
@@ -37,6 +37,7 @@ var networkDns = require("./network-dns");
|
|
|
37
37
|
networkDns.resolver = require("./network-dns-resolver");
|
|
38
38
|
networkDns.dnssec = require("./network-dnssec");
|
|
39
39
|
networkDns.dane = require("./network-dane");
|
|
40
|
+
networkDns.tsig = require("./network-tsig");
|
|
40
41
|
var networkProxy = require("./network-proxy");
|
|
41
42
|
var networkTls = require("./network-tls");
|
|
42
43
|
var heartbeat = require("./network-heartbeat");
|
|
@@ -17,7 +17,30 @@
|
|
|
17
17
|
"cpe": "cpe:2.3:a:paulmillr:noble-ciphers:2.2.0:*:*:*:*:node.js:*:*",
|
|
18
18
|
"hashes": {
|
|
19
19
|
"server": "sha256:5d539dfc9ef47121d4c09bd7256d76448a1f5ac47ee09ac44c78ff6a062af9ab"
|
|
20
|
-
}
|
|
20
|
+
},
|
|
21
|
+
"refreshedAt": "2026-05-26T19:53:53.034Z"
|
|
22
|
+
},
|
|
23
|
+
"@noble/curves": {
|
|
24
|
+
"version": "2.2.0",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"author": "Paul Miller",
|
|
27
|
+
"source": "https://github.com/paulmillr/noble-curves",
|
|
28
|
+
"exports": [
|
|
29
|
+
"ristretto255_oprf",
|
|
30
|
+
"p256_oprf",
|
|
31
|
+
"p384_oprf",
|
|
32
|
+
"p521_oprf"
|
|
33
|
+
],
|
|
34
|
+
"files": {
|
|
35
|
+
"server": "lib/vendor/noble-curves.cjs"
|
|
36
|
+
},
|
|
37
|
+
"bundler": "esbuild --format=cjs --minify --platform=node",
|
|
38
|
+
"bundledAt": "2026-05-26T00:00:00Z",
|
|
39
|
+
"cpe": "cpe:2.3:a:paulmillr:noble-curves:0.0.0:*:*:*:*:node.js:*:*",
|
|
40
|
+
"hashes": {
|
|
41
|
+
"server": "sha256:ebf254d5eb56aef8705a1c4af9603f47987b4870a9bb5e657e06907b701e2731"
|
|
42
|
+
},
|
|
43
|
+
"refreshedAt": "2026-05-26T19:53:53.034Z"
|
|
21
44
|
},
|
|
22
45
|
"@noble/post-quantum": {
|
|
23
46
|
"version": "0.6.1",
|
|
@@ -47,7 +70,8 @@
|
|
|
47
70
|
"cpe": "cpe:2.3:a:paulmillr:noble-post-quantum:0.6.1:*:*:*:*:node.js:*:*",
|
|
48
71
|
"hashes": {
|
|
49
72
|
"server": "sha256:f9190309daadca4c2e2cc2b76beaa6b96e463429cc3c390bd9f0ceaf7b588c68"
|
|
50
|
-
}
|
|
73
|
+
},
|
|
74
|
+
"refreshedAt": "2026-05-26T19:53:53.034Z"
|
|
51
75
|
},
|
|
52
76
|
"@simplewebauthn/server": {
|
|
53
77
|
"version": "13.3.0",
|
|
@@ -69,7 +93,8 @@
|
|
|
69
93
|
"cpe": "cpe:2.3:a:simplewebauthn:server:13.3.0:*:*:*:*:node.js:*:*",
|
|
70
94
|
"hashes": {
|
|
71
95
|
"server": "sha256:a9777dca582095d67f17ca24e19a0791de29928555b6b779c2233429175eb3f0"
|
|
72
|
-
}
|
|
96
|
+
},
|
|
97
|
+
"refreshedAt": "2026-05-26T19:53:53.034Z"
|
|
73
98
|
},
|
|
74
99
|
"SecLists-common-passwords-top-10000": {
|
|
75
100
|
"version": "10k-most-common (master)",
|
|
@@ -88,7 +113,8 @@
|
|
|
88
113
|
"data_js": "sha256:87b223beca89f33d2c2c32a2cfda0bc187e58061de40e7127bb5ffc4258c6e2a"
|
|
89
114
|
},
|
|
90
115
|
"runtime_artifact": "lib/vendor/common-passwords-top-10000.data.js",
|
|
91
|
-
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)"
|
|
116
|
+
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
117
|
+
"refreshedAt": "2026-05-26T19:53:53.034Z"
|
|
92
118
|
},
|
|
93
119
|
"bimi-trust-anchors": {
|
|
94
120
|
"version": "operator-managed",
|
|
@@ -112,7 +138,8 @@
|
|
|
112
138
|
"data_js": "sha256:aa7a4d33b65a68422a2a2c1670177689f66fdcaa08bd2514d78798b827bd1608"
|
|
113
139
|
},
|
|
114
140
|
"runtime_artifact": "lib/vendor/bimi-trust-anchors.data.js",
|
|
115
|
-
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)"
|
|
141
|
+
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
142
|
+
"refreshedAt": "2026-05-26T19:53:53.034Z"
|
|
116
143
|
},
|
|
117
144
|
"publicsuffix-list": {
|
|
118
145
|
"version": "master",
|
|
@@ -131,7 +158,8 @@
|
|
|
131
158
|
"data_js": "sha256:b4b6ae76fdacbfe07683c4ea62761326f42894c2ccf4359f253bbcab9826ed04"
|
|
132
159
|
},
|
|
133
160
|
"runtime_artifact": "lib/vendor/public-suffix-list.data.js",
|
|
134
|
-
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)"
|
|
161
|
+
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
162
|
+
"refreshedAt": "2026-05-26T19:53:53.034Z"
|
|
135
163
|
},
|
|
136
164
|
"peculiar-pki": {
|
|
137
165
|
"version": "2.0.0+pkijs-3.4.0",
|
|
@@ -140,8 +168,14 @@
|
|
|
140
168
|
"source": "https://github.com/PeculiarVentures",
|
|
141
169
|
"_about": "Meta-bundle of @peculiar/x509 + pkijs + reflect-metadata + every transitive ASN.1 schema package. Used by lib/mtls-engine-default.js as the pure-JS CA + PKCS#12 engine wired into b.mtlsCa.",
|
|
142
170
|
"components": {
|
|
143
|
-
"@peculiar/x509": {
|
|
144
|
-
|
|
171
|
+
"@peculiar/x509": {
|
|
172
|
+
"url": "https://github.com/PeculiarVentures/x509",
|
|
173
|
+
"version": "1.13.0"
|
|
174
|
+
},
|
|
175
|
+
"pkijs": {
|
|
176
|
+
"url": "https://github.com/PeculiarVentures/PKI.js",
|
|
177
|
+
"version": "3.4.0"
|
|
178
|
+
}
|
|
145
179
|
},
|
|
146
180
|
"exports": [
|
|
147
181
|
"x509",
|
|
@@ -155,7 +189,8 @@
|
|
|
155
189
|
"bundledAt": "2026-04-29T00:00:00Z",
|
|
156
190
|
"hashes": {
|
|
157
191
|
"server": "sha256:9bbc191afaaa2b1e5757f00480457c08134cdc2c55d541df18d9155bba9cbf77"
|
|
158
|
-
}
|
|
192
|
+
},
|
|
193
|
+
"refreshedAt": "2026-05-26T19:53:53.034Z"
|
|
159
194
|
}
|
|
160
195
|
}
|
|
161
196
|
}
|