@blamejs/pki 0.3.33 → 0.4.0
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 +18 -1
- package/README.md +1 -1
- package/lib/attrcert-sign.js +1 -1
- package/lib/crl-sign.js +3 -3
- package/lib/key.js +6 -6
- package/lib/pkcs12-build.js +3 -3
- package/lib/schema-c509.js +61 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,24 @@ 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.
|
|
7
|
+
## v0.4.0 — 2026-08-06
|
|
8
|
+
|
|
9
|
+
CRL issuance and verification, PKCS#12 build and open, attribute-certificate issuance, and the key-material lifecycle graduate to stable, and pki.schema.c509 adds the compact policyMappings and policyConstraints value forms -- a C509 certificate's policy mappings and policy constraints now ride their specific draft-20 CBOR shape and interoperate with a conformant C509 implementation rather than only this decoder.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- pki.schema.c509 encodes and decodes the compact value forms for the policyMappings and policyConstraints extensions (draft-ietf-cose-cbor-encoded-cert-20 sec. 3.3): a policy mapping's issuerDomainPolicy and subjectDomainPolicy each ride the sec. 8.9 registry-integer / unwrapped-OID policy space the certificatePolicies extension uses (a policy mapping to or from the special anyPolicy is preserved, matching what a certificate's own decoder accepts), and a policy constraints value rides the fixed two-element [requireExplicitPolicy, inhibitPolicyMapping] array, each field a non-negative skip count or absent. Both directions invert to the DER extnValue byte-for-byte, so a certificate carrying either extension is the specific compact shape a conformant C509 implementation reads rather than an opaque DER byte string. The encoder is guarded: a value the compact form cannot hold falls the whole extension back to the unwrapped-OID byte-string form instead of encoding lossily; a malformed compact value -- an empty or both-absent policy constraints, an odd-length or empty policy-mappings array, a policy mapping member that is not a two-policy pair, or an unregistered policy integer -- fails closed with a typed C509Error.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- pki.crl.sign / verify / isRevoked (RFC 5280 sec. 5), pki.pkcs12.build / open / verifyMac (RFC 7292 / RFC 9579), pki.attrcert.sign (RFC 5755), and the key-material lifecycle pki.key.encrypt / decrypt / export / import / generate / publicFromPrivate (PKCS#8 / RFC 5958, RFC 8018) graduate from experimental to stable. Their governing standards are settled and each is proven against an independent implementation in the integration harness (OpenSSL), or for the attribute-certificate format through the toolkit's own conformance-vector round-trip plus coverage-guided fuzzing. They are now covered by the stability contract: a breaking change to any of them ships only after a prior deprecation cycle, never silently in a minor (the published LTS support window itself takes effect at v1.0).
|
|
18
|
+
- A C509 certificate carrying policyMappings or policyConstraints now encodes to -- and decodes from -- its compact CBOR value shape rather than the unwrapped-OID byte-string form an earlier release emitted; both reconstruct the same DER, but the CBOR bytes differ, so re-encode any C509 produced by an earlier release.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- The C509 encoder now bounds the basicConstraints path length and the inhibitAnyPolicy and policyConstraints skip counts to the same non-negative 31-bit range the toolkit's own certificate decoders enforce. A native C509 carrying one of these counts past that range now fails closed with a typed C509Error rather than reconstructing a DER an X.509 decoder -- this toolkit's included -- would then reject.
|
|
23
|
+
|
|
24
|
+
## v0.3.33 — 2026-08-05
|
|
8
25
|
|
|
9
26
|
pki.schema.c509 encodes and decodes the compact certificatePolicies value form -- a C509 certificate's policy identifiers ride their draft-20 registry integers (or unwrapped OIDs) and their CPS-URI and UserNotice qualifiers ride the specific compact CBOR shape, interoperating with a conformant C509 implementation rather than only this decoder.
|
|
10
27
|
|
package/README.md
CHANGED
|
@@ -204,7 +204,7 @@ is callable today; nothing below is a stub.
|
|
|
204
204
|
| `pki.webcrypto` | A W3C WebCrypto (`SubtleCrypto`) engine over `node:crypto` — `sign`/`verify`/`encrypt`/`decrypt`/`deriveBits`/`digest`/`generateKey`/`importKey`/`exportKey` across RSA, ECDSA, ECDH, Ed25519/Ed448, AES, HMAC, HKDF, PBKDF2, SHA — **and** post-quantum ML-DSA-44/65/87 and SLH-DSA signatures, plus ML-KEM-512/768/1024 key generation and certificate/PKCS#8 import — the RFC 9935 seed / expandedKey / both private-key CHOICE is validated fail-closed, so an OpenSSL-legacy bare-seed or an internally inconsistent key is rejected with a typed error (KEM encapsulation lands with CMS KEM-decrypt). Zero-dependency, OpenSSL-interoperable |
|
|
205
205
|
| `pki.schema` | The schema family — `parse` detects which PKI format DER / PEM encodes and routes to the right parser, `all` enumerates the registered formats, and the engine + per-format members are grouped here |
|
|
206
206
|
| `pki.schema.x509` | Parse DER / PEM certificates into structured, validated fields, with named + partly-decoded extensions — including the RFC 3739 / ETSI EN 319 412-5 qualified-certificate `qcStatements` (EU-qualified declaration, reliance limit, QSCD flag, certificate type, retention, PDS URLs, country of qualification; unknown statements preserved opaque) and the Microsoft Active Directory Certificate Services enrollment extensions (certificate template, CA version, previous-CA-certificate hash, application policies), fail-closed — `parse`, `pemDecode`, `pemEncode` |
|
|
207
|
-
| `pki.schema.c509` | Parse **and encode** C509 CBOR-encoded certificates (draft-ietf-cose-cbor-encoded-cert) — the compact CBOR profile of X.509, decoded fail-closed under deterministic CBOR; an explicit `parse` call (CBOR, not DER, so not auto-routed). `encode(input)` is the byte-exact inverse: a DER X.509 v3 certificate forward-transforms to a compact type-3 C509 whose reconstruction reproduces the original DER byte for byte (so the original signature still verifies), or a `parse` result re-emits its native array — canonical deterministic CBOR with the registry integer shorthands, the C509 compressions, and the compact draft-20 per-extension value forms — the scalar extensions (keyUsage, basicConstraints, extended key usage, subject key identifier, and more), the general-name-bearing extensions (subjectAltName, issuer alt name, name constraints, CRL distribution points, authority/subject information access, and the full authority key identifier) over one shared GeneralNames value codec,
|
|
207
|
+
| `pki.schema.c509` | Parse **and encode** C509 CBOR-encoded certificates (draft-ietf-cose-cbor-encoded-cert) — the compact CBOR profile of X.509, decoded fail-closed under deterministic CBOR; an explicit `parse` call (CBOR, not DER, so not auto-routed). `encode(input)` is the byte-exact inverse: a DER X.509 v3 certificate forward-transforms to a compact type-3 C509 whose reconstruction reproduces the original DER byte for byte (so the original signature still verifies), or a `parse` result re-emits its native array — canonical deterministic CBOR with the registry integer shorthands, the C509 compressions, and the compact draft-20 per-extension value forms — the scalar extensions (keyUsage, basicConstraints, extended key usage, subject key identifier, and more), the general-name-bearing extensions (subjectAltName, issuer alt name, name constraints, CRL distribution points, authority/subject information access, and the full authority key identifier) over one shared GeneralNames value codec, certificate policies (registry-integer or OID policy identifiers with their CPS-URI and UserNotice qualifiers), and policy mappings and policy constraints; a certificate outside the invertible set throws a typed `C509Error` |
|
|
208
208
|
| `pki.schema.crl` | Parse DER / PEM X.509 CRLs per RFC 5280 §5 — revoked serials with real-`Date` revocation times, named + partly-decoded extensions, fail-closed — `parse`, `pemDecode`, `pemEncode` |
|
|
209
209
|
| `pki.schema.csr` | Parse DER / PEM PKCS#10 certification requests per RFC 2986 — subject DN, public key, requested attributes, signature, fail-closed — `parse`, `pemDecode`, `pemEncode` |
|
|
210
210
|
| `pki.schema.pkcs8` | Parse DER / PEM PKCS#8 private keys per RFC 5208 / 5958 — algorithm, raw key bytes, attributes, optional public key, fail-closed; encrypted keys recognized (not decrypted) — `parse`, `parseEncrypted`, `pemDecode`, `pemEncode` |
|
package/lib/attrcert-sign.js
CHANGED
|
@@ -408,7 +408,7 @@ function _buildExtensions(extSpec, aaSpki) {
|
|
|
408
408
|
* @primitive pki.attrcert.sign
|
|
409
409
|
* @signature pki.attrcert.sign(spec, issuer, opts?) -> Promise<Buffer|string>
|
|
410
410
|
* @since 0.3.2
|
|
411
|
-
* @status
|
|
411
|
+
* @status stable
|
|
412
412
|
* @spec RFC 5755
|
|
413
413
|
* @defends forged-attribute-certificate (CWE-347)
|
|
414
414
|
* @related pki.schema.attrcert.parse, pki.x509.sign
|
package/lib/crl-sign.js
CHANGED
|
@@ -454,7 +454,7 @@ function _sign(spec, issuer, opts) {
|
|
|
454
454
|
* @primitive pki.crl.sign
|
|
455
455
|
* @signature pki.crl.sign(spec, issuer, opts?) -> Promise<Buffer|string>
|
|
456
456
|
* @since 0.3.9
|
|
457
|
-
* @status
|
|
457
|
+
* @status stable
|
|
458
458
|
* @spec RFC 5280 sec. 5, RFC 9882, RFC 9814
|
|
459
459
|
* @defends crl-forgery (CWE-347)
|
|
460
460
|
* @related pki.schema.crl.parse, pki.crl.verify, pki.path.crlChecker
|
|
@@ -510,7 +510,7 @@ function _resolveIssuerSpki(issuer) {
|
|
|
510
510
|
* @primitive pki.crl.verify
|
|
511
511
|
* @signature pki.crl.verify(crl, issuer) -> Promise<boolean>
|
|
512
512
|
* @since 0.3.9
|
|
513
|
-
* @status
|
|
513
|
+
* @status stable
|
|
514
514
|
* @spec RFC 5280 sec. 5.1.1.3, RFC 9814
|
|
515
515
|
* @defends crl-signature-bypass (CWE-347)
|
|
516
516
|
* @related pki.crl.sign, pki.path.crlChecker, pki.schema.crl.parse
|
|
@@ -549,7 +549,7 @@ function _serialHexOf(serial) {
|
|
|
549
549
|
* @primitive pki.crl.isRevoked
|
|
550
550
|
* @signature pki.crl.isRevoked(crl, serialNumber) -> entry | null
|
|
551
551
|
* @since 0.3.9
|
|
552
|
-
* @status
|
|
552
|
+
* @status stable
|
|
553
553
|
* @spec RFC 5280 sec. 5.1.2.6
|
|
554
554
|
* @related pki.crl.verify, pki.schema.crl.parse
|
|
555
555
|
*
|
package/lib/key.js
CHANGED
|
@@ -81,7 +81,7 @@ async function _toPrivateKeyDer(input) {
|
|
|
81
81
|
* @primitive pki.key.encrypt
|
|
82
82
|
* @signature pki.key.encrypt(privateKey, password, opts?) -> Promise<Buffer|string>
|
|
83
83
|
* @since 0.3.10
|
|
84
|
-
* @status
|
|
84
|
+
* @status stable
|
|
85
85
|
* @spec RFC 5958 sec. 3, RFC 8018
|
|
86
86
|
* @related pki.key.decrypt, pki.schema.pkcs8.parseEncrypted, pki.cms.encrypt
|
|
87
87
|
*
|
|
@@ -132,7 +132,7 @@ async function encrypt(privateKey, password, opts) {
|
|
|
132
132
|
* @primitive pki.key.decrypt
|
|
133
133
|
* @signature pki.key.decrypt(encrypted, password, opts?) -> Promise<Buffer|string>
|
|
134
134
|
* @since 0.3.10
|
|
135
|
-
* @status
|
|
135
|
+
* @status stable
|
|
136
136
|
* @spec RFC 5958 sec. 3, RFC 8018 sec. 6.2, RFC 8018 sec. 8
|
|
137
137
|
* @defends pbes2-padding-oracle (CWE-208), pbkdf2-work-dos (CWE-400)
|
|
138
138
|
* @related pki.key.encrypt, pki.schema.pkcs8.parse
|
|
@@ -187,7 +187,7 @@ function _decryptPbes2(encAlg, ciphertext, password, opts) {
|
|
|
187
187
|
* @primitive pki.key.export
|
|
188
188
|
* @signature pki.key.export(key, opts?) -> Promise<Buffer|string>
|
|
189
189
|
* @since 0.3.10
|
|
190
|
-
* @status
|
|
190
|
+
* @status stable
|
|
191
191
|
* @spec RFC 5958, RFC 5280 sec. 4.1.2.7, RFC 8410 sec. 3
|
|
192
192
|
* @related pki.key.import, pki.key.publicFromPrivate, pki.schema.pkcs8.parse
|
|
193
193
|
*
|
|
@@ -222,7 +222,7 @@ async function export_(key, opts) {
|
|
|
222
222
|
* @primitive pki.key.import
|
|
223
223
|
* @signature pki.key.import(input, opts?) -> Promise<CryptoKey>
|
|
224
224
|
* @since 0.3.10
|
|
225
|
-
* @status
|
|
225
|
+
* @status stable
|
|
226
226
|
* @spec RFC 5958, RFC 5280 sec. 4.1.2.7, RFC 8018
|
|
227
227
|
* @related pki.key.export, pki.key.decrypt
|
|
228
228
|
*
|
|
@@ -265,7 +265,7 @@ async function import_(input, opts) {
|
|
|
265
265
|
* @primitive pki.key.generate
|
|
266
266
|
* @signature pki.key.generate(algorithm, opts?) -> Promise<{ privateKey, publicKey }>
|
|
267
267
|
* @since 0.3.10
|
|
268
|
-
* @status
|
|
268
|
+
* @status stable
|
|
269
269
|
* @spec W3C WebCrypto, FIPS 203, FIPS 204
|
|
270
270
|
* @related pki.key.export, pki.key.publicFromPrivate
|
|
271
271
|
*
|
|
@@ -296,7 +296,7 @@ async function generate(algorithm, opts) {
|
|
|
296
296
|
* @primitive pki.key.publicFromPrivate
|
|
297
297
|
* @signature pki.key.publicFromPrivate(privateKey, opts?) -> Promise<Buffer|string>
|
|
298
298
|
* @since 0.3.10
|
|
299
|
-
* @status
|
|
299
|
+
* @status stable
|
|
300
300
|
* @spec RFC 5280 sec. 4.1.2.7, RFC 8410 sec. 3
|
|
301
301
|
* @related pki.key.export, pki.key.import
|
|
302
302
|
*
|
package/lib/pkcs12-build.js
CHANGED
|
@@ -403,7 +403,7 @@ function _normalizeSpec(spec, opts) {
|
|
|
403
403
|
* @primitive pki.pkcs12.build
|
|
404
404
|
* @signature pki.pkcs12.build(spec, opts?) -> Promise<Buffer|string>
|
|
405
405
|
* @since 0.3.11
|
|
406
|
-
* @status
|
|
406
|
+
* @status stable
|
|
407
407
|
* @spec RFC 7292, RFC 9579, RFC 8018
|
|
408
408
|
* @related pki.schema.pkcs12.parse, pki.pkcs12.verifyMac
|
|
409
409
|
*
|
|
@@ -468,7 +468,7 @@ async function build(spec, opts) {
|
|
|
468
468
|
* @primitive pki.pkcs12.verifyMac
|
|
469
469
|
* @signature pki.pkcs12.verifyMac(pfx, password, opts?) -> Promise<boolean>
|
|
470
470
|
* @since 0.3.11
|
|
471
|
-
* @status
|
|
471
|
+
* @status stable
|
|
472
472
|
* @spec RFC 7292 sec. 5.1, RFC 9579
|
|
473
473
|
* @defends pkcs12-mac-forgery (CWE-347)
|
|
474
474
|
* @related pki.pkcs12.build, pki.schema.pkcs12.parse
|
|
@@ -533,7 +533,7 @@ function _capWork(iterations, salt, opts, keyLength, hardCap) {
|
|
|
533
533
|
* @primitive pki.pkcs12.open
|
|
534
534
|
* @signature pki.pkcs12.open(pfx, password, opts?) -> Promise<OpenResult>
|
|
535
535
|
* @since 0.3.12
|
|
536
|
-
* @status
|
|
536
|
+
* @status stable
|
|
537
537
|
* @spec RFC 7292 sec. 5.1, RFC 9579, RFC 8018
|
|
538
538
|
* @defends pkcs12-unauthenticated-decrypt (CWE-347), pbes2-padding-oracle (CWE-208)
|
|
539
539
|
* @related pki.pkcs12.build, pki.pkcs12.verifyMac, pki.schema.pkcs12.parse
|
package/lib/schema-c509.js
CHANGED
|
@@ -89,6 +89,8 @@ var EXT_BY_INT = {
|
|
|
89
89
|
9: _name("authorityInfoAccess"),
|
|
90
90
|
25: _name("issuerAltName"),
|
|
91
91
|
26: _name("nameConstraints"),
|
|
92
|
+
27: _name("policyMappings"),
|
|
93
|
+
28: _name("policyConstraints"),
|
|
92
94
|
29: _name("freshestCRL"),
|
|
93
95
|
30: _name("inhibitAnyPolicy"),
|
|
94
96
|
31: _name("subjectInfoAccess"),
|
|
@@ -102,6 +104,7 @@ var EXT_COMPACT = {
|
|
|
102
104
|
extKeyUsage: 1, inhibitAnyPolicy: 1, ocspNoCheck: 1, tlsFeature: 1,
|
|
103
105
|
subjectAltName: 1, issuerAltName: 1, nameConstraints: 1, cRLDistributionPoints: 1,
|
|
104
106
|
freshestCRL: 1, authorityInfoAccess: 1, subjectInfoAccess: 1, certificatePolicies: 1,
|
|
107
|
+
policyMappings: 1, policyConstraints: 1,
|
|
105
108
|
};
|
|
106
109
|
// sec. 8.12 Extended Key Usages registry (C509 int -> registered id-kp purpose name). A KeyPurposeId
|
|
107
110
|
// outside this set encodes as an unwrapped ~oid; a C509 int outside it fails closed on decode.
|
|
@@ -289,6 +292,14 @@ function _cborIntVal(node, label) {
|
|
|
289
292
|
if (node.majorType !== 0 && node.majorType !== 1) throw _err("c509/bad-extensions", "a " + label + " value must be a CBOR integer");
|
|
290
293
|
return cbor.read.int(node);
|
|
291
294
|
}
|
|
295
|
+
// A non-negative reconstruct-side count (a policyConstraints / inhibitAnyPolicy SkipCerts, a basicConstraints
|
|
296
|
+
// pathLenConstraint) narrowed to [0, 2^31-1] via the SAME guard.range.uint31 the toolkit's own DER decoders
|
|
297
|
+
// (schema-pkix) bound these INTEGER (0..MAX) counts through. A native CBOR value past 2^31-1 fails closed here
|
|
298
|
+
// rather than reconstructing a DER the toolkit's own decoder would then reject -- the reconstruct path must
|
|
299
|
+
// produce nothing that decoder rejects. Returns a Number; b.integer narrows it to the minimal DER INTEGER.
|
|
300
|
+
function _boundCount(value, label) {
|
|
301
|
+
return guard.range.uint31(value, _err, "c509/bad-extensions", label + " (0..2^31-1)");
|
|
302
|
+
}
|
|
292
303
|
// A C509 KeyPurposeId (int registry alias OR unwrapped ~oid) -> the dotted extended-key-usage OID.
|
|
293
304
|
function _ekuPurposeOid(node) {
|
|
294
305
|
if (node.majorType === 0 || node.majorType === 1) {
|
|
@@ -686,7 +697,7 @@ function _extValueToDer(name, node) {
|
|
|
686
697
|
var iv = _cborIntVal(node, "basicConstraints");
|
|
687
698
|
if (iv === -2n) return b.sequence([]); // cA false (omitted)
|
|
688
699
|
if (iv === -1n) return b.sequence([b.boolean(true)]); // cA true, no pathLen
|
|
689
|
-
if (iv >= 0n) return b.sequence([b.boolean(true), b.integer(iv)]);
|
|
700
|
+
if (iv >= 0n) return b.sequence([b.boolean(true), b.integer(_boundCount(iv, "a basicConstraints pathLenConstraint"))]); // cA true, pathLen (0..2^31-1, as the DER decoder bounds it)
|
|
690
701
|
throw _err("c509/bad-extensions", "a basicConstraints int " + iv + " is outside the -2/-1/pathLen range");
|
|
691
702
|
}
|
|
692
703
|
case "authorityKeyIdentifier": // keyId-only bytes, or [ keyId, authorityCertIssuer, serial ] (sec. 3.3)
|
|
@@ -710,8 +721,8 @@ function _extValueToDer(name, node) {
|
|
|
710
721
|
}
|
|
711
722
|
return b.sequence(items.map(function (it) { return b.oid(_ekuPurposeOid(it)); }));
|
|
712
723
|
}
|
|
713
|
-
case "inhibitAnyPolicy": // uint -> INTEGER SkipCerts
|
|
714
|
-
return b.integer(_cborUint(node, "inhibitAnyPolicy"));
|
|
724
|
+
case "inhibitAnyPolicy": // uint -> INTEGER SkipCerts (0..2^31-1, as the DER decoder bounds it)
|
|
725
|
+
return b.integer(_boundCount(_cborUint(node, "inhibitAnyPolicy"), "an inhibitAnyPolicy SkipCerts"));
|
|
715
726
|
case "ocspNoCheck": // null -> NULL
|
|
716
727
|
if (node.majorType !== 7 || !(Buffer.isBuffer(node.bytes) && node.bytes.length === 1 && node.bytes[0] === 0xf6)) throw _err("c509/bad-extensions", "an ocspNoCheck value must be the CBOR simple value null");
|
|
717
728
|
return b.nullValue();
|
|
@@ -770,6 +781,27 @@ function _extValueToDer(name, node) {
|
|
|
770
781
|
}
|
|
771
782
|
return b.sequence(polInfos);
|
|
772
783
|
}
|
|
784
|
+
case "policyMappings": { // [ idp, sdp, idp, sdp, ... ] -> SEQUENCE OF SEQUENCE { OID, OID } (sec. 3.3)
|
|
785
|
+
if (node.majorType !== 4 || !node.children) throw _err("c509/bad-extensions", "a policyMappings value must be a CBOR array");
|
|
786
|
+
var pmKids = node.children;
|
|
787
|
+
if (pmKids.length === 0 || pmKids.length % 2 !== 0) throw _err("c509/bad-extensions", "a policyMappings array must be non-empty (issuerDomainPolicy, subjectDomainPolicy) pairs (sec. 3.3)");
|
|
788
|
+
var maps = [];
|
|
789
|
+
// each member is a CertPolicyId -- the same sec. 8.9 int/~oid policy space as certificatePolicies (an int
|
|
790
|
+
// outside sec. 8.9 fails closed in _policyIdToDerOid). anyPolicy is accepted: RFC 5280 sec. 4.2.1.5's
|
|
791
|
+
// "MUST NOT map to/from anyPolicy" is a generation rule the toolkit's own DER decoder does not reject.
|
|
792
|
+
for (var mi = 0; mi + 1 < pmKids.length; mi += 2) maps.push(b.sequence([b.oid(_policyIdToDerOid(pmKids[mi])), b.oid(_policyIdToDerOid(pmKids[mi + 1]))]));
|
|
793
|
+
return b.sequence(maps);
|
|
794
|
+
}
|
|
795
|
+
case "policyConstraints": { // [ requireExplicitPolicy: uint/null, inhibitPolicyMapping: uint/null ] -> SEQUENCE { [0]?, [1]? }
|
|
796
|
+
if (node.majorType !== 4 || !node.children || node.children.length !== 2) throw _err("c509/bad-extensions", "a policyConstraints value must be a 2-element CBOR array [ requireExplicitPolicy, inhibitPolicyMapping ] (sec. 3.3)");
|
|
797
|
+
var pcFields = [], repN = node.children[0], ipmN = node.children[1];
|
|
798
|
+
if (!_isCborNull(repN)) pcFields.push(b.implicit(0, b.integer(_boundCount(_cborUint(repN, "a policyConstraints requireExplicitPolicy"), "a policyConstraints requireExplicitPolicy")))); // requireExplicitPolicy [0] IMPLICIT SkipCerts
|
|
799
|
+
if (!_isCborNull(ipmN)) pcFields.push(b.implicit(1, b.integer(_boundCount(_cborUint(ipmN, "a policyConstraints inhibitPolicyMapping"), "a policyConstraints inhibitPolicyMapping")))); // inhibitPolicyMapping [1] IMPLICIT SkipCerts
|
|
800
|
+
// both-null is the empty-SEQUENCE case RFC 5280 sec. 4.2.1.11 forbids ("either ... MUST be present"); the
|
|
801
|
+
// toolkit's own DER decoder rejects the empty PolicyConstraints, so the reconstruct fails closed too.
|
|
802
|
+
if (pcFields.length === 0) throw _err("c509/bad-extensions", "policyConstraints must contain requireExplicitPolicy or inhibitPolicyMapping (RFC 5280 sec. 4.2.1.11)");
|
|
803
|
+
return b.sequence(pcFields); // positional slot -> tag makes [0] < [1] unique + ascending by construction
|
|
804
|
+
}
|
|
773
805
|
default:
|
|
774
806
|
throw _err("c509/bad-extensions", "extension " + name + " has no compact value decoder");
|
|
775
807
|
}
|
|
@@ -906,6 +938,32 @@ function _extValueFromDer(name, der) {
|
|
|
906
938
|
}
|
|
907
939
|
return cbor.build.array(cpOut);
|
|
908
940
|
}
|
|
941
|
+
case "policyMappings": { // SEQUENCE OF SEQUENCE { OID, OID } -> [ idp, sdp, ... ]
|
|
942
|
+
if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children || node.children.length < 1) return null;
|
|
943
|
+
var pmOut = [];
|
|
944
|
+
for (var pm = 0; pm < node.children.length; pm++) {
|
|
945
|
+
var mp = node.children[pm];
|
|
946
|
+
if (mp.tagClass !== "universal" || mp.tagNumber !== asn1.TAGS.SEQUENCE || !mp.children || mp.children.length !== 2) return null;
|
|
947
|
+
pmOut.push(_policyIdFromDer(asn1.read.oid(mp.children[0])));
|
|
948
|
+
pmOut.push(_policyIdFromDer(asn1.read.oid(mp.children[1])));
|
|
949
|
+
}
|
|
950
|
+
return cbor.build.array(pmOut);
|
|
951
|
+
}
|
|
952
|
+
case "policyConstraints": { // SEQUENCE { [0] rep?, [1] ipm? } -> [ rep/null, ipm/null ]
|
|
953
|
+
if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children || node.children.length < 1 || node.children.length > 2) return null;
|
|
954
|
+
var repC = cbor.build.nullValue(), ipmC = cbor.build.nullValue(), pcLast = -1;
|
|
955
|
+
for (var pci = 0; pci < node.children.length; pci++) {
|
|
956
|
+
var f = node.children[pci];
|
|
957
|
+
if (f.tagClass !== "context" || f.tagNumber <= pcLast) return null; // DER: unique + ascending [0] < [1]
|
|
958
|
+
pcLast = f.tagNumber;
|
|
959
|
+
var sv = asn1.read.integerImplicit(f, f.tagNumber);
|
|
960
|
+
if (sv < 0n) return null; // SkipCerts (0..MAX) -- a negative is non-compact
|
|
961
|
+
if (f.tagNumber === 0) repC = cbor.build.uint(sv);
|
|
962
|
+
else if (f.tagNumber === 1) ipmC = cbor.build.uint(sv);
|
|
963
|
+
else return null;
|
|
964
|
+
}
|
|
965
|
+
return cbor.build.array([repC, ipmC]);
|
|
966
|
+
}
|
|
909
967
|
default:
|
|
910
968
|
return null;
|
|
911
969
|
}
|
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:40b5fc50-7b82-41d2-9e80-f01b6be97878",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-06T23:41:09.454Z",
|
|
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.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.4.0",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.
|
|
25
|
+
"version": "0.4.0",
|
|
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.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.4.0",
|
|
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.
|
|
57
|
+
"ref": "@blamejs/pki@0.4.0",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|