@blamejs/pki 0.7.9 → 0.7.11
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 +24 -0
- package/lib/ocsp.js +25 -2
- package/lib/schema-pkix.js +7 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ 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.11 — 2026-09-10
|
|
8
|
+
|
|
9
|
+
A name constraint whose base is a zero-length name is read rather than rejected.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- pki.path.validate still refuses a caller-supplied subtree seed whose base is empty, through opts.initialPermittedSubtrees, opts.initialExcludedSubtrees or a trust anchor's nameConstraints. A seed that permits everything is a restriction the caller believes they applied, so that door is unchanged and is separate from decoding a certificate's own extension.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- A zero-length subtree base in a certificate's nameConstraints decodes. RFC 5280 section 4.2.1.6 states the prohibition for subjectAltName, that an extension with a dNSName of "" must not be used, and section 4.2.1.10 places no length floor on a constraint base. A zero-length dNSName in a subjectAltName is still refused, which is where the rule belongs.
|
|
18
|
+
- For dNSName the empty base names the root of the namespace, the same as the bare-dot base already did: section 4.2.1.10 satisfies the constraint by adding zero or more labels to its left-hand side, and every name is that base plus labels. An excluded empty base therefore excludes every dNSName, and a permitted one permits every dNSName. A leaf naming no dNSName is untouched by either.
|
|
19
|
+
- For rfc822Name and uniformResourceIdentifier the base decodes but the comparison reports path/name-constraint-unsupported and refuses. Section 4.2.1.10 gives a mail constraint a mailbox, a host or a leading-period domain, and a URI constraint a fully qualified domain name, so an empty base is none of the shapes either names and is not read as covering everything. The verdict now says the constraint could not be evaluated instead of saying the extension could not be parsed.
|
|
20
|
+
|
|
21
|
+
## v0.7.10 — 2026-09-10
|
|
22
|
+
|
|
23
|
+
An OCSP response is no longer signed with a status window that runs backwards.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- pki.ocsp.sign raises ocsp/bad-input for a response entry whose thisUpdate is after its nextUpdate, before anything is signed. RFC 6960 section 4.2.2.1 makes the pair a validity interval corresponding to the {thisUpdate, nextUpdate} interval in CRLs, and pki.crl.sign, pki.x509.sign, pki.attrcert.sign and pki.crmf.build each already refuse their own reversed window. Every entry in a batch is measured, not the first alone, and a nextUpdate that came from the default is held to the rule a supplied one is.
|
|
28
|
+
- An entry whose thisUpdate equals its nextUpdate still signs, as the equivalent CRL does: a zero-length window is not forbidden. A response entry that names no certificate still reports that first. The times are compared as given, so a thisUpdate later than its nextUpdate is refused even where the two fall inside one second and would encode to the same GeneralizedTime, which is how pki.x509.sign, pki.crl.sign and pki.crmf.build read their own.
|
|
29
|
+
- A pre-produced response still signs. RFC 6960 section 2.5 lets a responder sign a response ahead of the window it certifies, where producedAt is when the response was signed and thisUpdate is when the status was known correct, so a producedAt earlier than thisUpdate is conforming and is left alone.
|
|
30
|
+
|
|
7
31
|
## v0.7.9 — 2026-09-10
|
|
8
32
|
|
|
9
33
|
Certificate revocation lists can now be linted against the RFC 5280 profile.
|
package/lib/ocsp.js
CHANGED
|
@@ -326,10 +326,33 @@ function _buildSingleResponse(r, opts) {
|
|
|
326
326
|
if (r.certID != null) certIdP = Promise.resolve(b.raw(guard.bytes.view(r.certID, OcspError, "ocsp/bad-input", "a response entry's certID")));
|
|
327
327
|
else if (r.cert != null && r.issuer != null) certIdP = _buildCertID(_certOf(r.cert, "a response certificate"), _certOf(r.issuer, "a response issuer"), r.hashAlgorithm || "sha1");
|
|
328
328
|
else return Promise.reject(_err("ocsp/bad-input", "each response entry needs { certID } or { cert, issuer }"));
|
|
329
|
+
/** @internal Every per-entry refusal stays INSIDE this chain. These entries are built through a
|
|
330
|
+
* map into Promise.all, so a synchronous throw here would abort the map before Promise.all
|
|
331
|
+
* attached handlers, and an earlier entry's rejection would be orphaned: the caller catches
|
|
332
|
+
* sign()'s failure and the process still sees an unhandled rejection. */
|
|
329
333
|
return certIdP.then(function (certID) {
|
|
334
|
+
var thisUpdate = _asDate(r.thisUpdate) || new Date();
|
|
335
|
+
var nextUpdate = r.nextUpdate === null ? null : (_asDate(r.nextUpdate) || _defaultNextUpdate());
|
|
336
|
+
/** @internal RFC 6960 sec. 4.2.2.1 makes thisUpdate and nextUpdate a validity interval, and says
|
|
337
|
+
* it corresponds to the {thisUpdate, nextUpdate} interval in CRLs. An interval whose end
|
|
338
|
+
* precedes its start states a status window that closed before it opened, and pki.ocsp.verify
|
|
339
|
+
* reports such a response unknown, so signing one emits what nothing can act on. pki.x509.sign,
|
|
340
|
+
* pki.crl.sign, pki.attrcert.sign and pki.crmf.build each refuse their own reversed window; this
|
|
341
|
+
* is that rule in the one place it was missing. The RESOLVED values are compared rather than the
|
|
342
|
+
* caller's, since a defaulted nextUpdate reaches a relying party the same way a supplied one
|
|
343
|
+
* does. An equal pair is left alone, as it is for a CRL: a zero-length window is not forbidden.
|
|
344
|
+
*
|
|
345
|
+
* The CALLER's instants are compared, at their full precision, which is what the four sibling
|
|
346
|
+
* verbs do. Two instants inside one second encode to the same whole-second GeneralizedTime, so
|
|
347
|
+
* comparing the encoded values instead would accept a descending pair and quietly emit the
|
|
348
|
+
* zero-length window it truncates to. A caller naming a later thisUpdate than nextUpdate has
|
|
349
|
+
* stated a reversed one whatever the seconds round to, and hears so here. */
|
|
350
|
+
if (nextUpdate !== null && guard.time.instantOf(thisUpdate) > guard.time.instantOf(nextUpdate)) {
|
|
351
|
+
throw _err("ocsp/bad-input", "thisUpdate must not be after nextUpdate (RFC 6960 sec. 4.2.2.1)");
|
|
352
|
+
}
|
|
330
353
|
var statusNode = _certStatusNode(r.status, opts);
|
|
331
|
-
var srChildren = [certID, statusNode, b.generalizedTime(
|
|
332
|
-
if (
|
|
354
|
+
var srChildren = [certID, statusNode, b.generalizedTime(thisUpdate)];
|
|
355
|
+
if (nextUpdate !== null) srChildren.push(b.explicit(0, b.generalizedTime(nextUpdate)));
|
|
333
356
|
if (r.singleExtensions && r.singleExtensions.length) srChildren.push(b.explicit(1, b.sequence(r.singleExtensions)));
|
|
334
357
|
return b.sequence(srChildren);
|
|
335
358
|
});
|
package/lib/schema-pkix.js
CHANGED
|
@@ -531,7 +531,13 @@ function generalName(ns, opts) {
|
|
|
531
531
|
} else {
|
|
532
532
|
if (constructed) throw ctx.E(code, "GeneralName [" + t + "] must be primitive (X.690 sec. 10.2)");
|
|
533
533
|
if (GN_IA5[t]) {
|
|
534
|
-
|
|
534
|
+
/** @internal RFC 5280 sec. 4.2.1.6 forbids a zero-length IA5String where a GeneralName names
|
|
535
|
+
* something: "subjectAltName extensions with a dNSName of ' ' MUST NOT be used". Sec.
|
|
536
|
+
* 4.2.1.10 places no length floor on a subtree BASE, where an empty one names every name of
|
|
537
|
+
* that form, which is how a technically-constrained sub-CA excludes a whole form. Scoped on
|
|
538
|
+
* subtreeBase the same way the iPAddress rule below it is, so the prohibition keeps holding
|
|
539
|
+
* everywhere a GeneralName is a name. */
|
|
540
|
+
if (!subtreeBase && n.content.length === 0) throw ctx.E(code, "GeneralName [" + t + "] must be a non-empty IA5String");
|
|
535
541
|
guard.name.assertPrintableIa5(n.content, ctx.E, code, "GeneralName [" + t + "]");
|
|
536
542
|
if (decodeValue) value = _bufToString(n.content, "latin1");
|
|
537
543
|
} else if (t === 7) {
|
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:a94d16a8-ad1e-416e-8ea1-35dc679039b0",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-10T22:54:50.067Z",
|
|
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.7.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.7.11",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.7.
|
|
25
|
+
"version": "0.7.11",
|
|
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.7.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.7.11",
|
|
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.7.
|
|
57
|
+
"ref": "@blamejs/pki@0.7.11",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|