@blamejs/pki 0.8.26 → 0.8.28

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/lib/rfc3339.js CHANGED
@@ -6,11 +6,18 @@
6
6
  * lib/rfc3339.js: RFC 3339 date-time parsing + validation, a fail-closed engine primitive shared by
7
7
  * pki.acme (a boolean field-validator over server-supplied JSON) and pki.ct (parse a CT log-list
8
8
  * timestamp / temporal-interval bound to a comparable Date). Beyond the grammar it enforces CALENDAR
9
- * validity: a syntactically well-formed but impossible instant (month 13, February 30, hour 25, a
10
- * :60 leap second, a +25:00 offset) is rejected, so a downstream expiry / window comparison never runs
11
- * on a value JS `Date` would silently roll over or `Date.parse` would NaN. Like the byte-reader / guard
12
- * family, `parse` takes the caller's `(code, message) -> error` factory so every consumer keeps its own
13
- * domain fault code.
9
+ * validity: a syntactically well-formed but impossible instant is rejected, so a downstream expiry or
10
+ * window comparison never runs on a value JS `Date` reads as a different instant.
11
+ * `2026-02-30T00:00:00Z` is the case that earns the check its keep: `new Date` reads it as
12
+ * `2026-03-02T00:00:00.000Z`, two days forward, and returns that instant without complaint. The
13
+ * runtime refuses month 13, hour 25 and a `+25:00` offset on its own, so the check agrees with it
14
+ * there and adds nothing.
15
+ * A `:60` second is well-formed: RFC 3339 sec. 5.7 permits `time-second` to be 60 at the end of a
16
+ * month in which a leap second occurs. Refusing it here is a decision, taken because JS cannot
17
+ * represent a leap second, so a caller that accepted one would hold either an invalid Date or an
18
+ * instant one second off; a typed error says so instead. Like the byte-reader / guard family, `parse`
19
+ * takes the caller's `(code, message) -> error` factory so every consumer keeps its own domain fault
20
+ * code.
14
21
  */
15
22
 
16
23
  function _digitsAt(v, start, count) {
package/lib/schema-crl.js CHANGED
@@ -168,7 +168,8 @@ var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
168
168
  * var caCert = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: await pki.key.export(pair.publicKey),
169
169
  * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
170
170
  * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } }, { key: key });
171
- * var der = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"), crlNumber: 1n,
171
+ * var der = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"),
172
+ * nextUpdate: new Date("2026-02-01T00:00:00Z"), crlNumber: 1n,
172
173
  * revoked: [{ serialNumber: 0x0a3fn, revocationDate: new Date("2026-01-15T00:00:00Z") }] },
173
174
  * { cert: caCert, key: key });
174
175
  * var crl = pki.schema.crl.parse(der);
@@ -197,8 +198,9 @@ var parse = pkix.makeRecordingParser({ pemLabel: "X509 CRL", PemError: PemError,
197
198
  * var key = await pki.key.export(pair.privateKey);
198
199
  * var caCert = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: await pki.key.export(pair.publicKey),
199
200
  * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
200
- * extensions: { basicConstraints: { cA: true }, keyUsage: ["cRLSign"] } }, { key: key });
201
- * var pemText = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"), crlNumber: 1n, revoked: [] },
201
+ * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } }, { key: key });
202
+ * var pemText = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"),
203
+ * nextUpdate: new Date("2026-02-01T00:00:00Z"), crlNumber: 1n, revoked: [] },
202
204
  * { cert: caCert, key: key }, { pem: true });
203
205
  * var der = pki.schema.crl.pemDecode(pemText);
204
206
  */
@@ -220,8 +222,9 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || "X509 CRL
220
222
  * var key = await pki.key.export(pair.privateKey);
221
223
  * var caCert = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: await pki.key.export(pair.publicKey),
222
224
  * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
223
- * extensions: { basicConstraints: { cA: true }, keyUsage: ["cRLSign"] } }, { key: key });
224
- * var der = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"), crlNumber: 1n, revoked: [] },
225
+ * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } }, { key: key });
226
+ * var der = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"),
227
+ * nextUpdate: new Date("2026-02-01T00:00:00Z"), crlNumber: 1n, revoked: [] },
225
228
  * { cert: caCert, key: key });
226
229
  * var pem = pki.schema.crl.pemEncode(der);
227
230
  */
@@ -329,7 +332,8 @@ var DOOR = pkix.extensionDoor({
329
332
  * var caCert = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: await pki.key.export(pair.publicKey),
330
333
  * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
331
334
  * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } }, { key: key });
332
- * var crlDer = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"), crlNumber: 7n,
335
+ * var crlDer = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"),
336
+ * nextUpdate: new Date("2026-02-01T00:00:00Z"), crlNumber: 7n,
333
337
  * revoked: [{ serialNumber: 0x0a3fn, revocationDate: new Date("2026-01-15T00:00:00Z"), reason: "keyCompromise" }] },
334
338
  * { cert: caCert, key: key });
335
339
  * var rows = pki.schema.crl.decodeExtensions(crlDer);
@@ -362,7 +366,8 @@ var decodeExtensions = DOOR.decodeExtensions;
362
366
  * var caCert = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: await pki.key.export(pair.publicKey),
363
367
  * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
364
368
  * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } }, { key: key });
365
- * var crlDer = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"), crlNumber: 7n,
369
+ * var crlDer = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"),
370
+ * nextUpdate: new Date("2026-02-01T00:00:00Z"), crlNumber: 7n,
366
371
  * revoked: [{ serialNumber: 0x0a3fn, revocationDate: new Date("2026-01-15T00:00:00Z"), reason: "keyCompromise" }] },
367
372
  * { cert: caCert, key: key });
368
373
  * var e = pki.schema.crl.parse(crlDer).revokedCertificates[0].crlEntryExtensions[0];
package/lib/x509-sign.js CHANGED
@@ -31,6 +31,7 @@ var schema = require("./schema-engine");
31
31
  var pkix = require("./schema-pkix");
32
32
  var pkiBuild = require("./pki-build");
33
33
  var ct = require("./ct");
34
+ var lint = require("./lint");
34
35
 
35
36
  var CertificateError = frameworkError.CertificateError;
36
37
  var NS = pkix.makeNS("x509", CertificateError, oid);
@@ -47,7 +48,12 @@ var KNOWN_SPEC_KEYS = intrinsic.assign(intrinsic.create(null), {
47
48
  });
48
49
  var KNOWN_ISSUER_CERT_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, cert: 1 });
49
50
  var KNOWN_ISSUER_EXPLICIT_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, name: 1, publicKey: 1 });
50
- var KNOWN_SIGN_OPTS = intrinsic.assign(intrinsic.create(null), { digestAlgorithm: 1, pem: 1, pss: 1 });
51
+ var KNOWN_SIGN_OPTS = intrinsic.assign(intrinsic.create(null), { digestAlgorithm: 1, pem: 1, pss: 1, profile: 1 });
52
+
53
+ /** @internal The signature the build-time gate lints against, standing in for one that does not exist
54
+ * until the key is used. It is non-empty so the gate is not answering a rule about its own placeholder,
55
+ * and the rules that read the signature VALUE are exempt in lint.js for the same reason. */
56
+ var GATE_PLACEHOLDER_SIG = intrinsic.bufferAlloc(64, 0xff);
51
57
 
52
58
  var KNOWN_EXT_KEYS = intrinsic.assign(intrinsic.create(null), {
53
59
  subjectKeyIdentifier: 1, authorityKeyIdentifier: 1, keyUsage: 1, keyUsageCritical: 1,
@@ -493,10 +499,19 @@ function _hasCriticalSan(extSpec) {
493
499
  * marks it critical. The two name opposite ends of one exchange, so a spec naming both is refused. A
494
500
  * client recovers the signed entry from the issued certificate with `pki.ct.x509CertEntry`.
495
501
  *
502
+ * Before the key is used, the certificate the spec describes is linted against the RFC 5280 profile
503
+ * and refused with `x509/profile-violation` if any rule grades it `error`, naming the rule and its
504
+ * clause. Two rules are passed over: one reading the not-yet-written signature, and
505
+ * `lint/rfc5280/unknown-critical-extension`, whose requirement RFC 5280 sec. 4.2 places on a
506
+ * certificate-using system rather than on the issuer.
507
+ *
496
508
  * @opts
497
509
  * - `pem` (boolean) -- return a PEM `CERTIFICATE` string instead of DER.
498
510
  * - `pss` (boolean) -- sign an RSA key with RSASSA-PSS instead of PKCS#1 v1.5.
499
511
  * - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
512
+ * - `profile` (string) -- a second lint profile to hold the certificate to beside RFC 5280, named
513
+ * from `pki.lint.profiles()`. `"none"` runs no rules at all and emits what the spec describes,
514
+ * which is how a deliberately non-conforming certificate is produced.
500
515
  * @example
501
516
  * var pair = await pki.key.generate("Ed25519");
502
517
  * var signerSpki = await pki.key.export(pair.publicKey);
@@ -530,6 +545,7 @@ function _sign(spec, issuer, opts) {
530
545
  : "explicit form; the issuer takes { key, name, publicKey }, or { key, cert } to take both from a certificate");
531
546
  });
532
547
  guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "x509/bad-input", "pki.x509.sign has an unknown option ");
548
+ lint.assertKnownBuildProfile("certificate", opts.profile, _err, "x509/bad-input");
533
549
  if (issuer.key == null) throw _err("x509/bad-input", "a signing key (issuer.key, a WebCrypto CryptoKey or a PKCS#8 private key DER/PEM) is required");
534
550
 
535
551
  var spki = _reqDer(spec.subjectPublicKey, "spec.subjectPublicKey (the SPKI DER of the certified key)");
@@ -596,6 +612,9 @@ function _sign(spec, issuer, opts) {
596
612
  tbsChildren.push(b.raw(spki));
597
613
  if (exts.length) tbsChildren.push(b.explicit(3, b.sequence(exts)));
598
614
  var tbsDer = b.sequence(tbsChildren);
615
+ lint.assertBuildClean("certificate",
616
+ b.sequence([tbsDer, scheme.sigAlgId, b.bitString(GATE_PLACEHOLDER_SIG, 0)]),
617
+ opts.profile, _err, "x509/profile-violation");
599
618
 
600
619
  return signScheme.signOverTbs(scheme, issuer.key, tbsDer, _signE).then(function (sig) {
601
620
  return Promise.resolve(_assertCertVerifies(tbsDer, sig, issuerSpki, scheme)).then(function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.8.26",
3
+ "version": "0.8.28",
4
4
  "description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
@@ -86,6 +86,6 @@
86
86
  "devDependencies": {
87
87
  "c8": "12.0.0",
88
88
  "esbuild": "0.28.2",
89
- "eslint": "10.10.0"
89
+ "eslint": "10.11.0"
90
90
  }
91
91
  }
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:cbd1318b-e7fc-430e-9a9c-ec7c99161ac6",
5
+ "serialNumber": "urn:uuid:3eb9063d-93f7-4a44-bdff-52a992c9a1e7",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-09-28T05:44:25.737Z",
8
+ "timestamp": "2026-09-28T21:19:37.849Z",
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.8.26",
22
+ "bom-ref": "@blamejs/pki@0.8.28",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.8.26",
25
+ "version": "0.8.28",
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.8.26",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.8.28",
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.8.26",
57
+ "ref": "@blamejs/pki@0.8.28",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]