@blamejs/pki 0.5.10 → 0.5.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 +49 -0
- package/lib/acme.js +4 -4
- package/lib/asn1-der.js +1 -1
- package/lib/attrcert-sign.js +1 -1
- package/lib/cbor-det.js +4 -4
- package/lib/cmc-verify.js +7 -4
- package/lib/cms-sign.js +3 -5
- package/lib/cms-verify.js +7 -4
- package/lib/crl-sign.js +1 -1
- package/lib/crmf-sign.js +1 -1
- package/lib/ct.js +8 -5
- package/lib/est.js +17 -11
- package/lib/guard-bytes.js +564 -78
- package/lib/guard-identifier.js +691 -3
- package/lib/guard-parsed.js +9 -5
- package/lib/guard-time.js +65 -4
- package/lib/inspect.js +8 -3
- package/lib/key.js +48 -6
- package/lib/lint.js +14 -7
- package/lib/ocsp-verify.js +10 -4
- package/lib/ocsp.js +34 -5
- package/lib/path-validate.js +94 -17
- package/lib/schema-c509.js +3 -3
- package/lib/sigstore.js +6 -2
- package/lib/trust.js +2 -2
- package/lib/tsp-sign.js +2 -2
- package/lib/validator-attcert.js +3 -3
- package/lib/webauthn-mds.js +5 -5
- package/lib/webauthn.js +1 -1
- package/lib/webcrypto.js +15 -6
- package/lib/x509-sign.js +1 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-c509.js
CHANGED
|
@@ -2205,14 +2205,14 @@ function _requireResultShape(r) {
|
|
|
2205
2205
|
if (r.serialNumber == null && r.serialNumberHex == null) throw _err("c509/bad-input", "a C509 result must carry serialNumber or serialNumberHex");
|
|
2206
2206
|
if (!r.signatureAlgorithm || typeof r.signatureAlgorithm.name !== "string") throw _err("c509/bad-input", "a C509 result must carry signatureAlgorithm.name");
|
|
2207
2207
|
if (!r.subjectPublicKeyAlgorithm || typeof r.subjectPublicKeyAlgorithm.name !== "string") throw _err("c509/bad-input", "a C509 result must carry subjectPublicKeyAlgorithm.name");
|
|
2208
|
-
if (!r.validity || !(r.validity.notBefore
|
|
2208
|
+
if (!r.validity || !guard.time.isDate(r.validity.notBefore) || (r.validity.notAfter !== null && !guard.time.isDate(r.validity.notAfter))) throw _err("c509/bad-input", "a C509 result must carry validity.notBefore (Date) and notAfter (Date or null)");
|
|
2209
2209
|
if (!Array.isArray(r.extensions)) throw _err("c509/bad-input", "a C509 result must carry an extensions array");
|
|
2210
2210
|
if (!Buffer.isBuffer(r.signatureValue)) throw _err("c509/bad-input", "a C509 result must carry a Buffer signatureValue");
|
|
2211
2211
|
}
|
|
2212
2212
|
// A validity Date -> its C509 ~time (a non-negative CBOR epoch uint). A pre-epoch date cannot be
|
|
2213
2213
|
// represented (the parser accepts only an unwrapped major-type-0 integer) and fails closed here.
|
|
2214
2214
|
function _validityUint(date, label) {
|
|
2215
|
-
var secs = Math.floor(
|
|
2215
|
+
var secs = Math.floor(guard.time.instantOf(date) / 1000);
|
|
2216
2216
|
if (!isFinite(secs) || secs < 0) throw _err("c509/bad-validity", label + " is before the Unix epoch or not a valid date; C509 ~time is a non-negative CBOR epoch");
|
|
2217
2217
|
return cbor.build.uint(BigInt(secs));
|
|
2218
2218
|
}
|
|
@@ -2433,7 +2433,7 @@ _derToType3 = function (input, opts) {
|
|
|
2433
2433
|
// against a UTF8String of the same characters) would rebuild different bytes and break the
|
|
2434
2434
|
// signature that covers them. Byte-identical is exactly the condition under which that is safe.
|
|
2435
2435
|
issuer: c.issuer.bytes.equals(c.subject.bytes) ? null : _c509NameFromDer(c.issuer.bytes),
|
|
2436
|
-
validity: { notBefore: c.validity.notBefore, notAfter: c.validity.notAfter
|
|
2436
|
+
validity: { notBefore: c.validity.notBefore, notAfter: guard.time.instantOf(c.validity.notAfter) === _NO_EXPIRY ? null : c.validity.notAfter },
|
|
2437
2437
|
subject: _c509NameFromDer(c.subject.bytes),
|
|
2438
2438
|
subjectPublicKeyAlgorithm: { name: "ecPublicKey", oid: c.subjectPublicKeyInfo.algorithm.oid, curve: curve },
|
|
2439
2439
|
subjectPublicKey: _compressEcPoint(asn1.read.bitString(spkiNode.children[1]).bytes, coordLen), // 0x04||X||Y -> C509 compressed marker
|
package/lib/sigstore.js
CHANGED
|
@@ -228,7 +228,7 @@ function _toMs(x) {
|
|
|
228
228
|
// Coverage residual -- _inWindow gates every _toMs call behind a != null check; the null guard is dead defensive code.
|
|
229
229
|
if (x == null) return null;
|
|
230
230
|
if (typeof x === "number") return Number.isFinite(x) ? x : null;
|
|
231
|
-
if (x
|
|
231
|
+
if (guard.time.isDate(x)) { var d = guard.time.instantOf(x); return isNaN(d) ? null : d; }
|
|
232
232
|
if (typeof x === "string") { var t = Date.parse(x); return isNaN(t) ? null : t; }
|
|
233
233
|
return null;
|
|
234
234
|
}
|
|
@@ -671,7 +671,11 @@ async function verifyBundle(bundle, opts) {
|
|
|
671
671
|
if (integratedTime === null) throw lastErr;
|
|
672
672
|
|
|
673
673
|
// Leg 3 -- Fulcio chain, validated as of the (trusted) log time; then identity.
|
|
674
|
-
|
|
674
|
+
// The instant comes through the time guard's intrinsic read. `getTime` is an ordinary method a
|
|
675
|
+
// subclass answers, and the slot predicate above accepts a subclass from any realm, so reading
|
|
676
|
+
// the method off the value let a caller name the moment the Fulcio chain is validated at --
|
|
677
|
+
// epoch zero, say, where every certificate in it is still unissued.
|
|
678
|
+
var checkTime = guard.time.isDate(opts.time) ? guard.time.instantOf(opts.time) : C.TIME.seconds(integratedTime);
|
|
675
679
|
await _verifyChain(leaf, _chainDers(vm), fulcioRoots, checkTime);
|
|
676
680
|
var identity = _identity(leaf);
|
|
677
681
|
var identityChecked = _checkIdentity(identity, opts.identity);
|
package/lib/trust.js
CHANGED
|
@@ -347,7 +347,7 @@ function _copyDistrustAfter(src) {
|
|
|
347
347
|
var out = {};
|
|
348
348
|
if (!src || typeof src !== "object") return out;
|
|
349
349
|
Object.keys(src).forEach(function (k) {
|
|
350
|
-
out[k] = src[k]
|
|
350
|
+
out[k] = guard.time.isDate(src[k]) ? new Date(guard.time.instantOf(src[k])) : src[k];
|
|
351
351
|
});
|
|
352
352
|
return out;
|
|
353
353
|
}
|
|
@@ -375,7 +375,7 @@ function _copyName(name) {
|
|
|
375
375
|
function _datesEqual(x, y) {
|
|
376
376
|
var kx = Object.keys(x).sort(), ky = Object.keys(y).sort();
|
|
377
377
|
if (kx.join(",") !== ky.join(",")) return false;
|
|
378
|
-
return kx.every(function (k) { return x[k]
|
|
378
|
+
return kx.every(function (k) { return guard.time.instantOf(x[k]) === guard.time.instantOf(y[k]); });
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
function _purposesEqual(x, y) {
|
package/lib/tsp-sign.js
CHANGED
|
@@ -161,7 +161,7 @@ function _sign(messageImprint, tsa, opts) {
|
|
|
161
161
|
// genTime defaults to now; a supplied value MUST be a valid Date (never a silently-ignored
|
|
162
162
|
// non-Date or an Invalid Date that would encode a garbage GeneralizedTime).
|
|
163
163
|
if (opts.genTime != null) guard.time.assertValid(opts.genTime, _err, "tsp/bad-input", "genTime");
|
|
164
|
-
var genTime = opts.genTime
|
|
164
|
+
var genTime = guard.time.isDate(opts.genTime) ? opts.genTime : new Date();
|
|
165
165
|
// A caller-authored INTEGER is coerced through the shared guard, so a value that is not one is a
|
|
166
166
|
// typed tsp/bad-input rather than a raw SyntaxError or RangeError out of BigInt() -- an untyped
|
|
167
167
|
// fault escaping a public verb, which a caller cannot catch by code.
|
|
@@ -761,7 +761,7 @@ async function verify(token, data, opts) {
|
|
|
761
761
|
// notBefore is rejected at the floor (ceil >= notBefore but floor < notBefore). Certificate
|
|
762
762
|
// validity is seconds-precise (RFC 5280 sec. 4.1.2.5), so this never over-rejects a valid token.
|
|
763
763
|
var floorT = tst.genTime;
|
|
764
|
-
var ceilT = (tst.genTimeFraction != null && /[1-9]/.test(tst.genTimeFraction.slice(3))) ? new Date(
|
|
764
|
+
var ceilT = (tst.genTimeFraction != null && /[1-9]/.test(tst.genTimeFraction.slice(3))) ? new Date(guard.time.instantOf(floorT) + 1) : floorT;
|
|
765
765
|
try {
|
|
766
766
|
// parseToken surfaces each embedded cert as { bytes, tagClass, ... }; take the X.509 certs
|
|
767
767
|
// (universal SEQUENCE), skipping any attribute-certificate choices, and parse their raw bytes.
|
package/lib/validator-attcert.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
//
|
|
8
8
|
// validator-attcert -- the SINGLE home for "does this X.509 leaf meet the WebAuthn
|
|
9
9
|
// attestation-certificate profile" (WebAuthn sec. 8.2.1 packed, sec. 8.3.1 TPM AIK).
|
|
10
|
-
// Sibling to the guard family: a validator owns a decoded
|
|
11
|
-
// set once, so the packed and AIK profiles cannot drift apart or lose a gate
|
|
12
|
-
// check
|
|
10
|
+
// Sibling to the guard family: a validator owns a decoded type's complete conformance rule
|
|
11
|
+
// set once, so the packed and AIK profiles cannot drift apart or lose a gate. The version
|
|
12
|
+
// check belongs to the profile, not to whichever verifier reaches it first.
|
|
13
13
|
//
|
|
14
14
|
// The caller supplies its parsed certificate + an extension-accessor object `exts`
|
|
15
15
|
// { find(cert, oidName) -> rawExt|null, decode(cert, oidName) -> {critical, value}|null }
|
package/lib/webauthn-mds.js
CHANGED
|
@@ -502,7 +502,7 @@ function _chainToAnchor(chain, anchors, at, what, code) {
|
|
|
502
502
|
function _staleAfter(nextUpdate) {
|
|
503
503
|
var d = rfc3339.parseDate(nextUpdate, function (c, m) { return _err("webauthn/bad-metadata-blob", m); },
|
|
504
504
|
"webauthn/bad-metadata-blob", "the metadata BLOB nextUpdate");
|
|
505
|
-
return
|
|
505
|
+
return guard.time.instantOf(d) + constants.TIME.days(1);
|
|
506
506
|
}
|
|
507
507
|
|
|
508
508
|
// A verified result is a plain object the caller may hold for as long as it likes, so its freshness
|
|
@@ -513,7 +513,7 @@ function _staleAfter(nextUpdate) {
|
|
|
513
513
|
// here, so opting out stays opted out and never silently reappears at the point of use.
|
|
514
514
|
function assertFresh(metadata, at, label) {
|
|
515
515
|
if (!metadata || metadata.allowStale === true || typeof metadata.nextUpdate !== "string") return;
|
|
516
|
-
var atMs = at
|
|
516
|
+
var atMs = guard.time.isDate(at) ? guard.time.instantOf(at) : NaN;
|
|
517
517
|
var limit = _staleAfter(metadata.nextUpdate);
|
|
518
518
|
if (!isFinite(atMs) || !isFinite(limit)) return;
|
|
519
519
|
if (atMs >= limit) {
|
|
@@ -547,7 +547,7 @@ function _parsePayload(seg, at, opts) {
|
|
|
547
547
|
throw _err("webauthn/metadata-rollback", "the metadata BLOB no " + payload.no + " does not exceed the previously held " + opts.previousNo);
|
|
548
548
|
}
|
|
549
549
|
var staleAfter = _staleAfter(payload.nextUpdate);
|
|
550
|
-
var atMs =
|
|
550
|
+
var atMs = guard.time.instantOf(at);
|
|
551
551
|
// An unusable comparison instant must not read as "fresh": without this, a NaN date makes every
|
|
552
552
|
// comparison false and the staleness check silently passes.
|
|
553
553
|
//
|
|
@@ -791,7 +791,7 @@ function _reportInForceAt(report, atMs) {
|
|
|
791
791
|
// parseDate throws and never returns an Invalid Date, so d is finite; atMs was isFinite-
|
|
792
792
|
// checked by the caller before this runs.
|
|
793
793
|
// allow:nan-date-comparison-unguarded -- both operands are source-validated, as described above.
|
|
794
|
-
return
|
|
794
|
+
return guard.time.instantOf(d) <= atMs;
|
|
795
795
|
}
|
|
796
796
|
|
|
797
797
|
function statusDenied(entry, metadata, leaf, at) {
|
|
@@ -817,7 +817,7 @@ function statusDenied(entry, metadata, leaf, at) {
|
|
|
817
817
|
// after the time it asks about. When every dated report is still in the future, the answer is
|
|
818
818
|
// that none of them is in force, not that all of them are.
|
|
819
819
|
var isDated = function (r) { return r && typeof r.effectiveDate === "string" && rfc3339.isValidDate(r.effectiveDate); };
|
|
820
|
-
var atMs = (at
|
|
820
|
+
var atMs = (guard.time.isDate(at) && isFinite(guard.time.instantOf(at))) ? guard.time.instantOf(at) : null;
|
|
821
821
|
if (atMs !== null) {
|
|
822
822
|
reports = reports.filter(function (r) { return !isDated(r) || _reportInForceAt(r, atMs); });
|
|
823
823
|
}
|
package/lib/webauthn.js
CHANGED
|
@@ -533,7 +533,7 @@ function _cloneParsed(v, depth) {
|
|
|
533
533
|
if (depth > 64) throw _err("webauthn/bad-input", "opts.rootCertificates[] is nested too deeply to be a parsed certificate");
|
|
534
534
|
if (Buffer.isBuffer(v) || v instanceof Uint8Array) return guard.bytes.snapshot(v, _err, "webauthn/bad-input", "a byte field of opts.rootCertificates[]");
|
|
535
535
|
if (Array.isArray(v)) return v.map(function (x) { return _cloneParsed(x, depth + 1); });
|
|
536
|
-
if (v
|
|
536
|
+
if (guard.time.isDate(v)) return new Date(guard.time.instantOf(v));
|
|
537
537
|
if (v && typeof v === "object") {
|
|
538
538
|
var out = {};
|
|
539
539
|
Object.keys(v).forEach(function (k) { out[k] = _cloneParsed(v[k], depth + 1); });
|
package/lib/webcrypto.js
CHANGED
|
@@ -1275,14 +1275,23 @@ Crypto.prototype.getRandomValues = function getRandomValues(typedArray) {
|
|
|
1275
1275
|
if (!ArrayBuffer.isView(typedArray) || typedArray instanceof Float32Array || typedArray instanceof Float64Array || typedArray instanceof DataView) {
|
|
1276
1276
|
throw new WebCryptoError("webcrypto/data", "getRandomValues: expected an integer TypedArray");
|
|
1277
1277
|
}
|
|
1278
|
-
|
|
1278
|
+
// Re-view the (already integer-TypedArray-validated) output buffer through the OUTPUT door --
|
|
1279
|
+
// any TypedArray, not just Uint8Array -- so a detached backing store fails closed here; the
|
|
1280
|
+
// returned Buffer aliases typedArray, so randomFillSync writes through to the caller's array.
|
|
1281
|
+
//
|
|
1282
|
+
// The output door, because the input one refuses shared memory: bytes another thread can rewrite
|
|
1283
|
+
// after they have been checked cannot be checked. That answers a question about input. Here the
|
|
1284
|
+
// caller is asking for random bytes in memory they chose, and a `SharedArrayBuffer`-backed view
|
|
1285
|
+
// is a supported W3C argument that the platform fills; refusing it made a working call an error.
|
|
1286
|
+
var out = guard.bytes.outputView(typedArray, WebCryptoError, "webcrypto/data", "getRandomValues");
|
|
1287
|
+
// The ceiling is measured on that view, and never on `typedArray.byteLength`. `byteLength` is an
|
|
1288
|
+
// accessor on the shared typed-array prototype, so a caller's subclass answers it: one reporting
|
|
1289
|
+
// 0 passed the ceiling while the fill below wrote the array's real length, which is the bound
|
|
1290
|
+
// being bypassed rather than enforced. The view is built from the internal slot.
|
|
1291
|
+
if (out.length > MAX_RANDOM_BYTES) {
|
|
1279
1292
|
throw new WebCryptoError("webcrypto/data", "getRandomValues: byteLength exceeds " + MAX_RANDOM_BYTES);
|
|
1280
1293
|
}
|
|
1281
|
-
|
|
1282
|
-
// guard.bytes.source -- any TypedArray, not just Uint8Array -- so a detached
|
|
1283
|
-
// backing store fails closed here; the returned Buffer aliases typedArray, so
|
|
1284
|
-
// randomFillSync writes through to the caller's array.
|
|
1285
|
-
nodeCrypto.randomFillSync(guard.bytes.source(typedArray, WebCryptoError, "webcrypto/data", "getRandomValues"));
|
|
1294
|
+
nodeCrypto.randomFillSync(out);
|
|
1286
1295
|
return typedArray;
|
|
1287
1296
|
};
|
|
1288
1297
|
|
package/lib/x509-sign.js
CHANGED
|
@@ -321,7 +321,7 @@ function _sign(spec, issuer, opts) {
|
|
|
321
321
|
guard.time.assertValid(spec.notBefore, _err, "x509/bad-input", "notBefore");
|
|
322
322
|
guard.time.assertValid(spec.notAfter, _err, "x509/bad-input", "notAfter");
|
|
323
323
|
// allow:nan-date-comparison-unguarded -- both operands are guard.time.assertValid'd on the two lines above (an Invalid Date throws before this comparison).
|
|
324
|
-
if (spec.notBefore
|
|
324
|
+
if (guard.time.instantOf(spec.notBefore) > guard.time.instantOf(spec.notAfter)) throw _err("x509/bad-input", "notBefore must not be after notAfter (RFC 5280 sec. 4.1.2.5)");
|
|
325
325
|
var validityDer = b.sequence([_timeDer(spec.notBefore, "notBefore"), _timeDer(spec.notAfter, "notAfter")]);
|
|
326
326
|
|
|
327
327
|
var exts = _buildExtensions(spec.extensions, { spki: spki, issuerSpki: issuerSpki, issuerCert: issuerCert, subjectEmpty: subjectEmpty });
|
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:70c8692d-688f-45e6-a4af-49916d0b01c7",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-19T06:52:36.445Z",
|
|
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.5.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.5.11",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.5.
|
|
25
|
+
"version": "0.5.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.5.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.5.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.5.
|
|
57
|
+
"ref": "@blamejs/pki@0.5.11",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|