@blamejs/pki 0.6.34 → 0.6.36
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 +31 -0
- package/lib/schema-c509.js +120 -22
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.6.36 — 2026-09-04
|
|
8
|
+
|
|
9
|
+
A natively signed C509 certificate is refused when it spells an algorithm generically instead of using the registry entry that names the same value.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- The subjectPublicKeyAlgorithm on a pki.schema.c509.parse result carries a parameters buffer when the certificate used a C509 registry integer, holding the parameters that entry names: the DER NULL for rsaEncryption and the namedCurve OID for an EC key. The generic OID forms already surfaced this field, so it is now present for every form that has parameters.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- pki.schema.c509.parse refuses a natively signed certificate (c509CertificateType 2) that writes an algorithm as an OID, or as an OID with parameters, when a C509 registry entry names that same value. Draft section 3.7 reserves the generic form for re-encoded certificates, where it stays accepted. An algorithm with no registry entry, and a registry OID whose parameters differ from the entry's, both keep the generic form.
|
|
18
|
+
- pki.schema.c509.parse refuses an rsaEncryption subjectPublicKeyAlgorithm that does not carry the DER NULL parameters RFC 3279 section 2.3.1 requires, including the bare OID form that carries none. The type-3 reconstruction previously emitted the OID and a NULL regardless, so the rebuilt DER described an algorithm identifier the certificate had not encoded.
|
|
19
|
+
- pki.schema.c509.encode writes an algorithm back as the form naming the same value. It previously chose the form by algorithm name alone, so re-encoding a certificate field by field could move an algorithm that carried no parameters onto a registry entry that names some, and could miss the entry for a key identified by its namedCurve parameters rather than by a curve name.
|
|
20
|
+
- pki.schema.c509.encode refuses a serialNumber, RSA modulus or RSA exponent that is not a BigInt. A string was previously read as hexadecimal and truncated to an even number of digits, so a caller passing "abc" received a certificate carrying the serial 0xab, and a number was silently converted. A missing modulus or exponent reached the emitter as an untyped fault rather than a c509 error naming the field.
|
|
21
|
+
- pki.schema.c509.encode reports every field it reads off a result as a typed c509 error naming that field. An extensions entry or an RDN that is not an object, an extension carrying neither an OID nor a registry name, an extension OID that is not a dotted-decimal string, and a keyUsageBits that is not a safe integer previously surfaced as a TypeError, a RangeError, or a fault from the OID or CBOR builders. An RDN value that is not a string is refused rather than converted, so a caller passing a number no longer receives a certificate naming its decimal spelling.
|
|
22
|
+
- pki.schema.c509.encode refuses a sparse extensions or rdns array. Array iteration skips a hole, so such a list emitted fewer entries than it held without reporting anything.
|
|
23
|
+
|
|
24
|
+
## v0.6.35 — 2026-09-04
|
|
25
|
+
|
|
26
|
+
Three redundant C509 spellings are refused, so a certificate has one encoding and an issuer signature cannot cover two different C509 byte strings.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- pki.schema.c509.encode refuses a serialNumberHex that is not an even-length hexadecimal string instead of decoding a prefix of it. An odd-length or partly non-hexadecimal value previously became a different, valid serial number without any error, which the encoder's own re-parse could not catch. The check applies whether the result is re-emitted from its preserved field bytes or built field by field, so a caller is told the value is malformed rather than having it quietly ignored.
|
|
31
|
+
|
|
32
|
+
### Security
|
|
33
|
+
|
|
34
|
+
- pki.schema.c509.parse refuses an unsigned bignum whose first octet is 0x00, including a value that is only that octet, so every number has exactly one C509 encoding (draft sec. 3.1.2). A zero serial previously had two spellings that reconstructed byte-identical DER, letting one issuer signature cover two distinct C509 certificates. The rule covers the certificate serial number, the authorityKeyIdentifier serial, and the RSA modulus and exponent.
|
|
35
|
+
- pki.schema.c509.parse refuses an RSA subjectPublicKey written as [modulus, 65537] instead of the bare modulus. Draft sec. 3.1.9 omits the array and the exponent when the exponent is 65537, so the array spelling of such a key is a second encoding of one certificate, reconstructing byte-identical DER. An RSA key with any other exponent still uses the array form.
|
|
36
|
+
- pki.schema.c509.parse refuses a notAfter written as the epoch 253402300799 rather than the CBOR simple value null. Draft sec. 3.1.5 encodes the no-expiration time 99991231235959Z as null, so the explicit epoch is a second spelling that reconstructs the same GeneralizedTime and therefore the same DER. A notBefore is unaffected, and pki.schema.c509.encode refuses that Date on notAfter instead of emitting a form the decoder rejects.
|
|
37
|
+
|
|
7
38
|
## v0.6.34 — 2026-09-04
|
|
8
39
|
|
|
9
40
|
An injected transport that does not return a promise is reported as a typed option error naming the option, in every client that accepts one.
|
package/lib/schema-c509.js
CHANGED
|
@@ -32,7 +32,22 @@ var b = asn1.build;
|
|
|
32
32
|
var C = constants;
|
|
33
33
|
var C509Error = frameworkError.C509Error;
|
|
34
34
|
function _err(code, message, cause) { return new C509Error(code, message, cause); }
|
|
35
|
+
/** @internal Name a caller-supplied value in a diagnostic without converting it: a Symbol has no
|
|
36
|
+
* string form and a BigInt has no JSON form, so formatting either one would throw from the message. */
|
|
37
|
+
function _describe(v) {
|
|
38
|
+
if (v === null) return "null";
|
|
39
|
+
var t = typeof v;
|
|
40
|
+
if (t === "string") return JSON.stringify(v);
|
|
41
|
+
return t === "number" || t === "boolean" || t === "undefined" ? String(v) : t;
|
|
42
|
+
}
|
|
43
|
+
/** @internal A hole is skipped by forEach, so a sparse list would emit fewer items than it holds. */
|
|
44
|
+
function _assertDense(list, code, label) {
|
|
45
|
+
for (var i = 0; i < list.length; i++) {
|
|
46
|
+
if (!(i in list)) throw _err(code, label + " must not be a sparse array; index " + i + " is a hole");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
35
49
|
var MAX_EPOCH_SECONDS = 8640000000000n;
|
|
50
|
+
var NO_EXPIRY_SECONDS = 253402300799n;
|
|
36
51
|
|
|
37
52
|
function _name(n) { var d = oid.byName(n); if (!d) { throw new Error("schema-c509: unregistered OID name " + JSON.stringify(n)); } return n; }
|
|
38
53
|
|
|
@@ -142,14 +157,29 @@ function _biguint(node, code, label) {
|
|
|
142
157
|
if (!node || node.majorType !== 2) throw _err(code, label + " must be an unwrapped CBOR byte string (~biguint)");
|
|
143
158
|
var b = node.content;
|
|
144
159
|
if (b.length > C.LIMITS.CBOR_MAX_BIGUINT_BYTES) throw _err(code, label + " exceeds the ~biguint byte cap");
|
|
145
|
-
if (b.length >
|
|
160
|
+
if (b.length > 0 && b[0] === 0x00) throw _err("c509/non-minimal-serial", label + " has a redundant leading 0x00 (~biguint omits the sign octet)");
|
|
146
161
|
return b.length ? BigInt("0x" + b.toString("hex")) : 0n;
|
|
147
162
|
}
|
|
148
163
|
|
|
164
|
+
function _hexBytes(s, code, label) {
|
|
165
|
+
if (typeof s !== "string") throw _err(code, label + " must be a hexadecimal string");
|
|
166
|
+
if ((s.length % 2) !== 0) throw _err(code, label + " must have an even number of hexadecimal digits");
|
|
167
|
+
for (var i = 0; i < s.length; i++) {
|
|
168
|
+
var c = s.charCodeAt(i);
|
|
169
|
+
if (!((c >= 48 && c <= 57) || (c >= 97 && c <= 102) || (c >= 65 && c <= 70))) {
|
|
170
|
+
throw _err(code, label + " must be hexadecimal");
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return Buffer.from(s, "hex");
|
|
174
|
+
}
|
|
175
|
+
|
|
149
176
|
function _time(node, allowNull, label) {
|
|
150
177
|
if (allowNull && node.majorType === 7 && node.ai === 22) return null;
|
|
151
178
|
if (node.majorType !== 0) throw _err("c509/bad-validity", label + " must be an unwrapped CBOR epoch integer (~time)");
|
|
152
179
|
var secs = node.argument;
|
|
180
|
+
if (allowNull && secs === NO_EXPIRY_SECONDS) {
|
|
181
|
+
throw _err("c509/bad-validity", label + " of 99991231235959Z is encoded as the CBOR simple value null (draft sec. 3.1.5), not as the epoch " + NO_EXPIRY_SECONDS);
|
|
182
|
+
}
|
|
153
183
|
if (secs > MAX_EPOCH_SECONDS) throw _err("c509/bad-validity", label + " is outside the representable Date range");
|
|
154
184
|
return new Date(C.TIME.seconds(Number(secs)));
|
|
155
185
|
}
|
|
@@ -162,19 +192,58 @@ function _oidName(node, code, label) {
|
|
|
162
192
|
return { oid: dotted, name: oid.name(dotted) || dotted };
|
|
163
193
|
}
|
|
164
194
|
|
|
165
|
-
|
|
195
|
+
/**
|
|
196
|
+
* @internal Draft sec. 8.14 and 8.15 give each registry row a full DER AlgorithmIdentifier, so the
|
|
197
|
+
* row's parameters are part of the value it denotes: a signature row carries none, the rsaEncryption
|
|
198
|
+
* row carries NULL, and an ecPublicKey row carries its namedCurve.
|
|
199
|
+
*/
|
|
200
|
+
function _rowParameters(row) {
|
|
201
|
+
if (typeof row === "string") return null;
|
|
202
|
+
if (row.curve) return asn1.build.oid(oid.byName(row.curve));
|
|
203
|
+
return asn1.build.nullValue();
|
|
204
|
+
}
|
|
205
|
+
function _rowParametersMatch(row, params) {
|
|
206
|
+
if (row === undefined) return false;
|
|
207
|
+
var rowParams = _rowParameters(row);
|
|
208
|
+
return rowParams === null ? params === null : (params !== null && rowParams.equals(params));
|
|
209
|
+
}
|
|
210
|
+
function _specificInt(byInt, dotted, params, curve) {
|
|
211
|
+
var keys = Object.keys(byInt);
|
|
212
|
+
for (var i = 0; i < keys.length; i++) {
|
|
213
|
+
var row = byInt[keys[i]];
|
|
214
|
+
if (oid.byName(typeof row === "string" ? row : row.alg) !== dotted) continue;
|
|
215
|
+
var hit = params === null && curve ? (typeof row !== "string" && row.curve === curve)
|
|
216
|
+
: _rowParametersMatch(row, params);
|
|
217
|
+
if (hit) return Number(keys[i]);
|
|
218
|
+
}
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
function _hasSpecificForm(byInt, dotted, params) {
|
|
222
|
+
return _specificInt(byInt, dotted, params) !== undefined;
|
|
223
|
+
}
|
|
224
|
+
function _assertSpecificForm(byInt, dotted, params, isNative, label) {
|
|
225
|
+
if (!isNative || !_hasSpecificForm(byInt, dotted, params)) return;
|
|
226
|
+
throw _err("c509/non-specific-encoding", label + " has a C509 registry row, and a natively signed certificate uses only the specific CBOR encoding (draft sec. 3.7)");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function _algorithm(node, byInt, code, label, isNative) {
|
|
166
230
|
if (node.majorType === 0 || node.majorType === 1) {
|
|
167
231
|
var i = Number(cbor.read.int(node));
|
|
168
232
|
var mapped = byInt[i];
|
|
169
233
|
if (mapped === undefined) throw _err("c509/unknown-algorithm", label + " integer " + i + " has no C509 registry row");
|
|
170
234
|
if (typeof mapped === "string") return { name: mapped, oid: oid.byName(mapped) };
|
|
171
|
-
return { name: mapped.alg, oid: oid.byName(mapped.alg), curve: mapped.curve || null };
|
|
235
|
+
return { name: mapped.alg, oid: oid.byName(mapped.alg), curve: mapped.curve || null, parameters: _rowParameters(mapped) };
|
|
236
|
+
}
|
|
237
|
+
if (node.majorType === 2) {
|
|
238
|
+
var r = _oidName(node, code, label);
|
|
239
|
+
_assertSpecificForm(byInt, r.oid, null, isNative, label);
|
|
240
|
+
return { name: r.name, oid: r.oid };
|
|
172
241
|
}
|
|
173
|
-
if (node.majorType === 2) { var r = _oidName(node, code, label); return { name: r.name, oid: r.oid }; }
|
|
174
242
|
if (node.majorType === 4 && node.children && node.children.length === 2) {
|
|
175
243
|
var a = _oidName(node.children[0], code, label);
|
|
176
244
|
if (node.children[1].majorType !== 2) throw _err(code, label + " algorithm parameters must be a CBOR byte string");
|
|
177
245
|
if (node.children[1].content.length === 0) throw _err(code, label + " algorithm parameters must not be an empty byte string; omit them with the bare ~oid form (draft sec. 3.1.3)");
|
|
246
|
+
_assertSpecificForm(byInt, a.oid, node.children[1].content, isNative, label);
|
|
178
247
|
return { name: a.name, oid: a.oid, parameters: node.children[1].content };
|
|
179
248
|
}
|
|
180
249
|
throw _err(code, label + " is not a C509 AlgorithmIdentifier (int / ~oid / [~oid, params])");
|
|
@@ -1404,6 +1473,10 @@ function _reconSpki(spkAlg, keyBytes, rsaKey) {
|
|
|
1404
1473
|
return b.sequence([b.sequence([b.oid(oid.byName("ecPublicKey")), b.oid(oid.byName(spkAlg.curve))]), b.bitString(point, 0)]);
|
|
1405
1474
|
}
|
|
1406
1475
|
if (spkAlg.name === "rsaEncryption") {
|
|
1476
|
+
/** @internal RFC 3279 sec. 2.3.1 fixes rsaEncryption parameters to the DER NULL. */
|
|
1477
|
+
if (!spkAlg.parameters || !spkAlg.parameters.equals(b.nullValue())) {
|
|
1478
|
+
throw _err("c509/bad-spki", "rsaEncryption parameters must be the DER NULL (RFC 3279 sec. 2.3.1); the bare ~oid form carries none");
|
|
1479
|
+
}
|
|
1407
1480
|
var rsaPk = b.sequence([b.integer(rsaKey.modulus), b.integer(rsaKey.exponent)]);
|
|
1408
1481
|
return b.sequence([b.sequence([b.oid(oid.byName("rsaEncryption")), b.nullValue()]), b.bitString(rsaPk, 0)]);
|
|
1409
1482
|
}
|
|
@@ -1507,7 +1580,7 @@ function parse(input) {
|
|
|
1507
1580
|
var serial = _biguint(serialBytes, "c509/bad-serial", "certificateSerialNumber");
|
|
1508
1581
|
var sHex = serialBytes.content.toString("hex");
|
|
1509
1582
|
|
|
1510
|
-
var sigAlg = _algorithm(f[2], SIG_ALG_BY_INT, "c509/unknown-algorithm", "issuerSignatureAlgorithm");
|
|
1583
|
+
var sigAlg = _algorithm(f[2], SIG_ALG_BY_INT, "c509/unknown-algorithm", "issuerSignatureAlgorithm", type === 2);
|
|
1511
1584
|
var issuer = _name509(f[3], false, type === 2);
|
|
1512
1585
|
var notBefore = _time(f[4], false, "validityNotBefore");
|
|
1513
1586
|
var notAfter = _time(f[5], true, "validityNotAfter");
|
|
@@ -1515,12 +1588,13 @@ function parse(input) {
|
|
|
1515
1588
|
if (issuer === null && (!subject || !subject.rdns || subject.rdns.length === 0)) {
|
|
1516
1589
|
throw _err("c509/bad-name", "a self-signed C509 (issuer == subject) requires a non-empty subject, since it is also the issuer (RFC 5280 sec. 4.1.2.4)");
|
|
1517
1590
|
}
|
|
1518
|
-
var spkAlg = _algorithm(f[7], PK_ALG_BY_INT, "c509/unknown-algorithm", "subjectPublicKeyAlgorithm");
|
|
1591
|
+
var spkAlg = _algorithm(f[7], PK_ALG_BY_INT, "c509/unknown-algorithm", "subjectPublicKeyAlgorithm", type === 2);
|
|
1519
1592
|
var subjectPublicKey = null, rsaKey = null;
|
|
1520
1593
|
if (spkAlg.name === "rsaEncryption") {
|
|
1521
1594
|
if (f[8].majorType === 2) rsaKey = { modulus: _biguint(f[8], "c509/bad-spki", "RSA modulus"), exponent: 65537n };
|
|
1522
1595
|
else if (f[8].majorType === 4 && f[8].children && f[8].children.length === 2) {
|
|
1523
1596
|
rsaKey = { modulus: _biguint(f[8].children[0], "c509/bad-spki", "RSA modulus"), exponent: _biguint(f[8].children[1], "c509/bad-spki", "RSA exponent") };
|
|
1597
|
+
if (rsaKey.exponent === 65537n) throw _err("c509/bad-spki", "an RSA subjectPublicKey with exponent 65537 omits the array and the exponent (sec. 3.1.9)");
|
|
1524
1598
|
} else throw _err("c509/bad-spki", "an RSA subjectPublicKey must be a ~biguint modulus or [modulus, exponent]");
|
|
1525
1599
|
if (rsaKey.modulus < 1n || rsaKey.exponent < 1n) throw _err("c509/bad-spki", "an RSA modulus and public exponent must be positive");
|
|
1526
1600
|
} else {
|
|
@@ -1561,29 +1635,35 @@ function matches(node) {
|
|
|
1561
1635
|
}
|
|
1562
1636
|
|
|
1563
1637
|
|
|
1564
|
-
var SIG_ALG_TO_INT = Object.assign(Object.create(null), { ecdsaWithSHA256: 0, ecdsaWithSHA384: 1, ecdsaWithSHA512: 2 });
|
|
1565
|
-
var PK_ALG_TO_INT = Object.assign(Object.create(null), { rsaEncryption: 0, "ecPublicKey|prime256v1": 1, "ecPublicKey|secp384r1": 2, "ecPublicKey|secp521r1": 3 });
|
|
1566
1638
|
var ATTR_TO_INT = Object.create(null);
|
|
1567
1639
|
Object.keys(ATTR_BY_INT).forEach(function (k) { ATTR_TO_INT[ATTR_BY_INT[k]] = Number(k); });
|
|
1568
1640
|
var EXT_TO_INT = Object.create(null);
|
|
1569
1641
|
Object.keys(EXT_BY_INT).forEach(function (k) { if (EXT_COMPACT[EXT_BY_INT[k]]) EXT_TO_INT[EXT_BY_INT[k]] = Number(k); });
|
|
1570
1642
|
|
|
1571
1643
|
function _minBytes(n) {
|
|
1644
|
+
if (typeof n !== "bigint") throw _err("c509/bad-serial", "a ~biguint value must be a BigInt, got " + _describe(n));
|
|
1572
1645
|
if (n < 0n) throw _err("c509/bad-serial", "a ~biguint value must be non-negative");
|
|
1573
1646
|
if (n === 0n) return Buffer.alloc(0);
|
|
1574
1647
|
var hex = n.toString(16); if (hex.length % 2) hex = "0" + hex;
|
|
1575
1648
|
return Buffer.from(hex, "hex");
|
|
1576
1649
|
}
|
|
1577
|
-
function _encAlgorithm(alg,
|
|
1578
|
-
var
|
|
1579
|
-
if (
|
|
1580
|
-
var
|
|
1581
|
-
|
|
1650
|
+
function _encAlgorithm(alg, byInt) {
|
|
1651
|
+
var dotted = alg.oid || oid.byName(alg.name);
|
|
1652
|
+
if (!dotted) throw _err("c509/bad-input", "algorithm " + alg.name + " carries no OID and its name is not in the OID registry");
|
|
1653
|
+
var supplied = alg.parameters && alg.parameters.length ? alg.parameters : null;
|
|
1654
|
+
var i = _specificInt(byInt, dotted, supplied, alg.curve);
|
|
1655
|
+
if (i !== undefined) return cbor.build.int(BigInt(i));
|
|
1656
|
+
var oidBytes = cbor.build.byteString(asn1.encodeOidContent(dotted));
|
|
1657
|
+
if (supplied !== null) return cbor.build.array([oidBytes, cbor.build.byteString(supplied)]);
|
|
1582
1658
|
return oidBytes;
|
|
1583
1659
|
}
|
|
1584
1660
|
function _encSpecialText(rdn) {
|
|
1585
|
-
if (rdn.eui64
|
|
1586
|
-
|
|
1661
|
+
if (rdn.eui64 != null) {
|
|
1662
|
+
if (!Buffer.isBuffer(rdn.eui64)) throw _err("c509/bad-name", "an RDN eui64 must be a Buffer, got " + _describe(rdn.eui64));
|
|
1663
|
+
return cbor.build.tag(48, cbor.build.byteString(rdn.eui64));
|
|
1664
|
+
}
|
|
1665
|
+
if (typeof rdn.value !== "string") throw _err("c509/bad-name", "an RDN value must be a string, got " + _describe(rdn.value));
|
|
1666
|
+
var v = rdn.value;
|
|
1587
1667
|
if (v.length >= 2 && _isEvenHexLower(v)) return cbor.build.byteString(Buffer.from(v, "hex"));
|
|
1588
1668
|
return cbor.build.textString(v);
|
|
1589
1669
|
}
|
|
@@ -1593,11 +1673,15 @@ function _encName(name, isSubject) {
|
|
|
1593
1673
|
throw _err("c509/bad-name", "the subject Name is required");
|
|
1594
1674
|
}
|
|
1595
1675
|
var rdns = name.rdns || [];
|
|
1676
|
+
_assertDense(rdns, "c509/bad-name", "the rdns array");
|
|
1677
|
+
rdns.forEach(function (rdn) {
|
|
1678
|
+
if (!rdn || typeof rdn !== "object") throw _err("c509/bad-name", "each RDN must be an object, got " + _describe(rdn));
|
|
1679
|
+
});
|
|
1596
1680
|
if (rdns.length === 1 && rdns[0].type === "commonName" && !rdns[0].printable) return _encSpecialText(rdns[0]);
|
|
1597
1681
|
var items = [];
|
|
1598
1682
|
rdns.forEach(function (rdn) {
|
|
1599
1683
|
var ai = ATTR_TO_INT[rdn.type];
|
|
1600
|
-
if (ai === undefined) throw _err("c509/bad-name", "attribute type " + rdn.type + " has no C509 registry int");
|
|
1684
|
+
if (ai === undefined) throw _err("c509/bad-name", "attribute type " + _describe(rdn.type) + " has no C509 registry int");
|
|
1601
1685
|
items.push(cbor.build.int(BigInt(rdn.printable ? -ai : ai)));
|
|
1602
1686
|
items.push(_encSpecialText(rdn));
|
|
1603
1687
|
});
|
|
@@ -1613,6 +1697,18 @@ function _encSpk(r) {
|
|
|
1613
1697
|
return cbor.build.byteString(r.subjectPublicKey);
|
|
1614
1698
|
}
|
|
1615
1699
|
function _encExtensions(exts) {
|
|
1700
|
+
_assertDense(exts, "c509/bad-input", "the extensions array");
|
|
1701
|
+
exts.forEach(function (ext) {
|
|
1702
|
+
if (!ext || typeof ext !== "object") throw _err("c509/bad-input", "each extension must be an object, got " + _describe(ext));
|
|
1703
|
+
if (ext.oid != null) {
|
|
1704
|
+
if (typeof ext.oid !== "string") throw _err("c509/bad-input", "an extension oid must be a dotted-decimal string, got " + _describe(ext.oid));
|
|
1705
|
+
try { asn1.encodeOidContent(ext.oid); }
|
|
1706
|
+
catch (_oe) { throw _err("c509/bad-input", "an extension oid must be a dotted-decimal string, got " + _describe(ext.oid), _oe); }
|
|
1707
|
+
}
|
|
1708
|
+
if (ext.keyUsageBits != null && (typeof ext.keyUsageBits !== "number" || !Number.isSafeInteger(ext.keyUsageBits))) {
|
|
1709
|
+
throw _err("c509/bad-extensions", "keyUsageBits must be a safe integer, got " + _describe(ext.keyUsageBits));
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1616
1712
|
if (exts.length === 1 && exts[0].name === "keyUsage" && typeof exts[0].keyUsageBits === "number") {
|
|
1617
1713
|
return cbor.build.int(BigInt(exts[0].critical ? -exts[0].keyUsageBits : exts[0].keyUsageBits));
|
|
1618
1714
|
}
|
|
@@ -1624,7 +1720,9 @@ function _encExtensions(exts) {
|
|
|
1624
1720
|
items.push(cbor.build.int(BigInt(ext.critical ? -ei : ei)));
|
|
1625
1721
|
items.push(compact);
|
|
1626
1722
|
} else {
|
|
1627
|
-
|
|
1723
|
+
var extDotted = ext.oid || (typeof ext.name === "string" ? oid.byName(ext.name) : null);
|
|
1724
|
+
if (!extDotted) throw _err("c509/bad-input", "an extension must carry an OID or a name in the OID registry, got " + _describe(ext.name));
|
|
1725
|
+
items.push(cbor.build.byteString(asn1.encodeOidContent(extDotted)));
|
|
1628
1726
|
if (!Buffer.isBuffer(ext.value)) throw _err("c509/non-invertible", "extension " + (ext.oid || ext.name) + " has no byte-string value to encode");
|
|
1629
1727
|
var bs = cbor.build.byteString(ext.value);
|
|
1630
1728
|
items.push(ext.critical ? cbor.build.array([bs]) : bs);
|
|
@@ -1650,21 +1748,21 @@ function _validityUint(date, label) {
|
|
|
1650
1748
|
function _encodeC509Array(r) {
|
|
1651
1749
|
if (Buffer.isBuffer(r._fieldBytes)) {
|
|
1652
1750
|
if (!Buffer.isBuffer(r.signatureValue)) throw _err("c509/bad-input", "a re-emitted certificate must carry a Buffer signatureValue");
|
|
1751
|
+
if (r.serialNumberHex != null) _hexBytes(r.serialNumberHex, "c509/bad-serial", "serialNumberHex");
|
|
1653
1752
|
var out = Buffer.concat([Buffer.from([0x8b]), r._fieldBytes, cbor.build.byteString(r.signatureValue)]);
|
|
1654
1753
|
parse(out);
|
|
1655
1754
|
return out;
|
|
1656
1755
|
}
|
|
1657
1756
|
_requireResultShape(r);
|
|
1658
|
-
var pkKey = r.subjectPublicKeyAlgorithm.curve ? r.subjectPublicKeyAlgorithm.name + "|" + r.subjectPublicKeyAlgorithm.curve : r.subjectPublicKeyAlgorithm.name;
|
|
1659
1757
|
var arr = cbor.build.array([
|
|
1660
1758
|
cbor.build.int(BigInt(r.certificateType)),
|
|
1661
|
-
cbor.build.byteString(r.serialNumberHex != null ?
|
|
1662
|
-
_encAlgorithm(r.signatureAlgorithm,
|
|
1759
|
+
cbor.build.byteString(r.serialNumberHex != null ? _hexBytes(r.serialNumberHex, "c509/bad-serial", "serialNumberHex") : _minBytes(r.serialNumber)),
|
|
1760
|
+
_encAlgorithm(r.signatureAlgorithm, SIG_ALG_BY_INT),
|
|
1663
1761
|
_encName(r.issuer, false),
|
|
1664
1762
|
_validityUint(r.validity.notBefore, "validityNotBefore"),
|
|
1665
1763
|
r.validity.notAfter === null ? cbor.build.nullValue() : _validityUint(r.validity.notAfter, "validityNotAfter"),
|
|
1666
1764
|
_encName(r.subject, true),
|
|
1667
|
-
_encAlgorithm(r.subjectPublicKeyAlgorithm,
|
|
1765
|
+
_encAlgorithm(r.subjectPublicKeyAlgorithm, PK_ALG_BY_INT),
|
|
1668
1766
|
_encSpk(r),
|
|
1669
1767
|
_encExtensions(r.extensions),
|
|
1670
1768
|
cbor.build.byteString(r.signatureValue),
|
|
@@ -1753,7 +1851,7 @@ function _keyUsageBitsFromDer(extnValue) {
|
|
|
1753
1851
|
}
|
|
1754
1852
|
return _namedBitsFromContent(bs.bytes, bs.unusedBits);
|
|
1755
1853
|
}
|
|
1756
|
-
var _NO_EXPIRY =
|
|
1854
|
+
var _NO_EXPIRY = C.TIME.seconds(Number(NO_EXPIRY_SECONDS));
|
|
1757
1855
|
var NODE_TO_WEBCRYPTO = Object.assign(Object.create(null), { "prime256v1": "P-256", "secp384r1": "P-384", "secp521r1": "P-521" });
|
|
1758
1856
|
var WEBCRYPTO_FIELD_BYTES = Object.assign(Object.create(null), { "P-256": 32, "P-384": 48, "P-521": 66 });
|
|
1759
1857
|
var SIG_ALG_TO_CURVE = Object.assign(Object.create(null), { "ecdsaWithSHA256": "P-256", "ecdsaWithSHA384": "P-384", "ecdsaWithSHA512": "P-521" });
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:8d50e6dd-1ddf-46f8-b8db-ed97195096c3",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-05T01:58:33.232Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/pki@0.6.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.6.36",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.6.
|
|
25
|
+
"version": "0.6.36",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/pki@0.6.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.6.36",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/pki@0.6.
|
|
57
|
+
"ref": "@blamejs/pki@0.6.36",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|