@blamejs/pki 0.8.27 → 0.8.30

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/lint.js CHANGED
@@ -363,6 +363,7 @@ function _criticalityRule(name, id, severity, citation, message) {
363
363
  var RFC5280_RULES = [
364
364
  {
365
365
  id: "lint/rfc5280/signature-empty", severity: "error", source: "rfc5280", citation: "RFC 5280 4.1.1.3",
366
+ readsSignatureValue: true,
366
367
  /** @internal The strict parser admits an empty BIT STRING, since one is well-formed DER; a
367
368
  * signatureValue that contains no signature is not the field section 4.1.1.3 defines. */
368
369
  message: "the signatureValue contains no signature",
@@ -3949,7 +3950,7 @@ var RFC9881_RULES = [
3949
3950
  },
3950
3951
  {
3951
3952
  id: "lint/rfc9881/mldsa-signature-length", severity: "error", source: "rfc9881",
3952
- citation: "RFC 9881 4 / FIPS 204",
3953
+ citation: "RFC 9881 4 / FIPS 204", readsSignatureValue: true,
3953
3954
  /** @internal The signature is the FIPS 204 value carried directly in the BIT STRING, and FIPS 204
3954
3955
  * fixes its length per parameter set as it fixes the public key's. So a signatureValue of any other
3955
3956
  * length is not a signature of the algorithm the identifier names, whatever a verifier would later
@@ -4218,6 +4219,7 @@ function _criticalityRows(list, table, what) {
4218
4219
  var RFC5280_CRL_RULES = [
4219
4220
  {
4220
4221
  id: "lint/rfc5280-crl/signature-empty", severity: "error", source: "rfc5280-crl", citation: "RFC 5280 5.1.1.3",
4222
+ readsSignatureValue: true,
4221
4223
  message: "the signatureValue contains no signature",
4222
4224
  check: function (crl) { return crl.signatureValue.bytes.length === 0 ? true : null; },
4223
4225
  },
@@ -4702,7 +4704,7 @@ function _cnsaSignatureRow(id, citation, reader) {
4702
4704
  * registry passes returns the length and whether it is octet-aligned. */
4703
4705
  function _cnsaSignatureLengthRow(id, citation, algReader, lengthReader) {
4704
4706
  return {
4705
- id: id, severity: "error", source: "cnsa-2.0", citation: citation,
4707
+ id: id, severity: "error", source: "cnsa-2.0", citation: citation, readsSignatureValue: true,
4706
4708
  message: "the signature is not the length FIPS 204 fixes for its algorithm",
4707
4709
  appliesTo: function (artifact, ctx) {
4708
4710
  var sig = algReader(artifact);
@@ -5260,6 +5262,7 @@ function _placementRow(name, placed, where) {
5260
5262
  var RFC6960_OCSP_RULES = [
5261
5263
  {
5262
5264
  id: "lint/rfc6960/signature-empty", severity: "error", source: "rfc6960", citation: "RFC 6960 2.2",
5265
+ readsSignatureValue: true,
5263
5266
  message: "a definitive response shall be digitally signed; the signature BIT STRING is empty",
5264
5267
  check: function (resp) { return _ocspBasic(resp).signature.length === 0 ? true : null; },
5265
5268
  },
@@ -7270,6 +7273,126 @@ function profiles() {
7270
7273
  return out;
7271
7274
  }
7272
7275
 
7276
+ /** @internal The rules the build-time gate does not refuse on because the SPEC does not place the
7277
+ * requirement on the issuer, keyed by the last segment of the rule id so one entry answers for every
7278
+ * profile carrying the rule: a certificate reports lint/rfc5280/unknown-critical-extension and a
7279
+ * revocation list reports lint/rfc5280-crl/unknown-critical-extension, and the two state one
7280
+ * requirement. The value is the reason, read by the gate's conformance vectors rather than restated.
7281
+ *
7282
+ * The other reason a rule cannot apply, that it reads a signature the gate has not produced yet, is
7283
+ * not a name here: those rules declare `readsSignatureValue` where they are defined, and
7284
+ * SIGNATURE_VALUE_RULES below derives the set from the same registries `rules()` walks. A rule that
7285
+ * starts reading the signature value therefore exempts itself, and a vector measures the declared set
7286
+ * against the rules whose verdict actually moves with the signature. */
7287
+ var BUILD_GATE_EXEMPT = Object.assign(Object.create(null), {
7288
+ "unknown-critical-extension": "RFC 5280 sec. 4.2 places this requirement on \"a certificate-using system\", so the rule reports what a relying party owes rather than what an issuer does. It covers two artifacts an issuer may conform in emitting: one carrying a critical extension the specification does not identify, which the same section permits a conforming CA to support, and one carrying an extension the specification does identify but this toolkit's own path validation does not process, such as a critical policyMappings",
7289
+ });
7290
+
7291
+ /** @internal Every rule id whose verdict reads the signature VALUE, derived at load from the rule rows
7292
+ * themselves rather than listed. The gate runs on an artifact carrying a placeholder signature, so one
7293
+ * of these answers about the placeholder and not about what the caller is asking to emit. */
7294
+ var SIGNATURE_VALUE_RULES = (function () {
7295
+ var out = Object.create(null);
7296
+ Object.keys(BY_ARTIFACT).forEach(function (artifact) {
7297
+ Object.keys(BY_ARTIFACT[artifact]).forEach(function (name) {
7298
+ BY_ARTIFACT[artifact][name].forEach(function (r) {
7299
+ if (r.readsSignatureValue === true) out[r.id] = true;
7300
+ });
7301
+ });
7302
+ });
7303
+ return out;
7304
+ })();
7305
+
7306
+ /** @internal The profile whose rows an artifact is held to whatever the caller names. RFC 5280 states
7307
+ * the contract a certificate and a revocation list are written to and RFC 2986 the one a request is,
7308
+ * so these run on every authoring call and `opts.profile` names rows beside them. */
7309
+ var BUILD_GATE_ALWAYS = Object.assign(Object.create(null), {
7310
+ certificate: "rfc5280", csr: "rfc2986", crl: "rfc5280-crl",
7311
+ });
7312
+
7313
+ var BUILD_GATE_VERB = Object.assign(Object.create(null), {
7314
+ certificate: certificate, csr: csr, crl: crl,
7315
+ });
7316
+
7317
+ /** @internal The profile name that runs no rules. A toolkit that cannot emit a non-conforming artifact
7318
+ * cannot build the corpus its own linter is tested against, and an operator reproducing a real
7319
+ * misissuance needs the same. The escape is a name the caller writes rather than an absence, so a
7320
+ * non-conforming artifact is one somebody asked for: the gate is closed by default and every refusal
7321
+ * names this among the profiles it would accept. */
7322
+ var BUILD_GATE_NONE = "none";
7323
+
7324
+ /** @internal The exemption table, copied so a caller cannot edit the gate's own line. The gate's
7325
+ * vectors read the reasons from here, which is why it is reachable at all. */
7326
+ function buildGateExemptions() {
7327
+ return Object.assign(Object.create(null), BUILD_GATE_EXEMPT);
7328
+ }
7329
+
7330
+ /** @internal The rule ids that declared they read the signature value, for the vector that measures
7331
+ * this set against the rules whose verdict actually moves when the signature does. */
7332
+ function buildGateSignatureRules() {
7333
+ return Object.assign(Object.create(null), SIGNATURE_VALUE_RULES);
7334
+ }
7335
+
7336
+ /** @internal The profile a build-time gate always runs for this artifact, for a vector that derives
7337
+ * the expected rule set from the same name the gate uses. */
7338
+ function buildGateProfile(artifact) {
7339
+ return BUILD_GATE_ALWAYS[artifact] || null;
7340
+ }
7341
+
7342
+ function _buildGateTail(id) {
7343
+ var cut = id.lastIndexOf("/");
7344
+ return cut === -1 ? id : id.slice(cut + 1);
7345
+ }
7346
+
7347
+ /**
7348
+ * @internal Refuse a profile name an authoring verb cannot run, under the CALLER's code, so
7349
+ * `pki.x509.sign` reports an unknown profile in its own domain as it reports every other bad option.
7350
+ * The sentence is the one `pki.lint.rules` gives for the same name, so asking the linter where a
7351
+ * profile lives and asking a builder to hold an artifact to it are answered alike.
7352
+ */
7353
+ function assertKnownBuildProfile(artifact, profile, E, code) {
7354
+ if (profile == null || profile === BUILD_GATE_NONE) return;
7355
+ if (typeof profile !== "string") {
7356
+ throw E(code, "profile must be the name of a lint profile (known: " +
7357
+ Object.keys(BY_ARTIFACT[artifact]).join(", ") + ", " + BUILD_GATE_NONE + ")");
7358
+ }
7359
+ if (!Object.prototype.hasOwnProperty.call(BY_ARTIFACT[artifact], profile)) {
7360
+ throw E(code, _profileRefusal(profile, artifact).message + ", or \"" + BUILD_GATE_NONE +
7361
+ "\" to emit what the spec describes without holding it to a profile");
7362
+ }
7363
+ }
7364
+
7365
+ /**
7366
+ * @internal Refuse to emit an artifact this toolkit's own linter grades `error` or worse. The caller
7367
+ * passes the artifact it is about to sign, carrying a placeholder signature, and the gate runs the
7368
+ * always-on profile for that artifact plus the one the caller named. A finding whose rule id is in
7369
+ * BUILD_GATE_EXEMPT is passed over, and every other one throws under the caller's code naming the
7370
+ * rule and its citation.
7371
+ *
7372
+ * Standards are the contract, so this runs before the signing key is reached: a certificate that was
7373
+ * signed and then discarded is still one a key signed and an audit log recorded. A caller naming the
7374
+ * profile "none" gets no rules and whatever the spec describes.
7375
+ */
7376
+ function assertBuildClean(artifact, der, profile, E, code) {
7377
+ if (profile === BUILD_GATE_NONE) return;
7378
+ var verb = BUILD_GATE_VERB[artifact];
7379
+ var reports = [verb(der, { profile: BUILD_GATE_ALWAYS[artifact] })];
7380
+ if (profile != null && profile !== BUILD_GATE_ALWAYS[artifact]) {
7381
+ reports.push(verb(der, { profile: profile }));
7382
+ }
7383
+ for (var r = 0; r < reports.length; r++) {
7384
+ var found = reports[r].findings;
7385
+ for (var i = 0; i < found.length; i++) {
7386
+ var f = found[i];
7387
+ if (f.severity !== "error" && f.severity !== "fatal") continue;
7388
+ if (SIGNATURE_VALUE_RULES[f.id]) continue;
7389
+ if (BUILD_GATE_EXEMPT[_buildGateTail(f.id)]) continue;
7390
+ throw E(code, "this spec describes " + _article(ARTIFACT_NOUN[artifact]) + " " +
7391
+ ARTIFACT_NOUN[artifact] + " that violates " + f.id + " (" + f.citation + "): " + f.message);
7392
+ }
7393
+ }
7394
+ }
7395
+
7273
7396
  module.exports = {
7274
7397
  certificate: certificate,
7275
7398
  csr: csr,
@@ -7280,4 +7403,9 @@ module.exports = {
7280
7403
  attrcert: attrcert,
7281
7404
  rules: rules,
7282
7405
  profiles: profiles,
7406
+ assertBuildClean: assertBuildClean,
7407
+ assertKnownBuildProfile: assertKnownBuildProfile,
7408
+ buildGateExemptions: buildGateExemptions,
7409
+ buildGateSignatureRules: buildGateSignatureRules,
7410
+ buildGateProfile: buildGateProfile,
7283
7411
  };
package/lib/ocsp.js CHANGED
@@ -197,9 +197,16 @@ function _buildRequest(query, opts) {
197
197
  var issuer = _certOf(q.issuer, "the issuer certificate");
198
198
  return _buildCertID(cert, issuer, hashName).then(function (certID) {
199
199
  var reqChildren = [certID];
200
- if (q.singleRequestExtensions && q.singleRequestExtensions.length) {
201
- if (lightweight) throw _err("ocsp/bad-input", "the lightweight profile permits no singleRequestExtensions (RFC 5019 sec. 2.1.1)");
202
- reqChildren.push(b.explicit(0, b.sequence(q.singleRequestExtensions)));
200
+ if (q.singleRequestExtensions != null) {
201
+ /** @internal Read through the same door the sibling lists in this module use, so a value that is
202
+ * not an array is refused rather than passed over: read as truthy-and-has-length, an object or a
203
+ * number produced a Request carrying none of the extensions the caller asked for, and nothing in
204
+ * the result said so. */
205
+ var singles = pkiBuild.reqDenseArray(q.singleRequestExtensions, "singleRequestExtensions", _err, "ocsp/bad-input");
206
+ if (singles.length) {
207
+ if (lightweight) throw _err("ocsp/bad-input", "the lightweight profile permits no singleRequestExtensions (RFC 5019 sec. 2.1.1)");
208
+ reqChildren.push(b.explicit(0, b.sequence(singles)));
209
+ }
203
210
  }
204
211
  return b.sequence(reqChildren);
205
212
  });
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.27",
3
+ "version": "0.8.30",
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",
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:0dc52d70-7641-471f-98fe-c06068d7838f",
5
+ "serialNumber": "urn:uuid:b9b5ff1a-3017-4ab5-ab52-4eb332fecb31",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-09-28T19:32:41.570Z",
8
+ "timestamp": "2026-09-29T03:10:30.011Z",
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.27",
22
+ "bom-ref": "@blamejs/pki@0.8.30",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.8.27",
25
+ "version": "0.8.30",
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.27",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.8.30",
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.27",
57
+ "ref": "@blamejs/pki@0.8.30",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]