@blamejs/pki 0.6.51 → 0.6.53

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/key.js CHANGED
@@ -26,6 +26,7 @@ var pbes2 = require("./pbes2");
26
26
  var pkcs8 = require("./schema-pkcs8");
27
27
  var pkix = require("./schema-pkix");
28
28
  var webcrypto = require("./webcrypto");
29
+ var compositeKem = require("./composite-kem");
29
30
  var frameworkError = require("./framework-error");
30
31
  var guard = require("./guard-all");
31
32
 
@@ -311,7 +312,7 @@ async function generate(algorithm, opts) {
311
312
  * @signature pki.key.publicFromPrivate(privateKey, opts?) -> Promise<Buffer|string>
312
313
  * @since 0.3.10
313
314
  * @status stable
314
- * @spec RFC 5280 sec. 4.1.2.7, RFC 8410 sec. 3
315
+ * @spec RFC 5280 sec. 4.1.2.7, RFC 8410 sec. 3, draft-ietf-lamps-pq-composite-kem sec. 4.1
315
316
  * @related pki.key.export, pki.key.import
316
317
  *
317
318
  * Derive the SubjectPublicKeyInfo (SPKI) public key from a PKCS#8 private key (DER `Buffer`, `PRIVATE KEY`
@@ -319,6 +320,13 @@ async function generate(algorithm, opts) {
319
320
  * the algorithm from the key structure, so no `AlgorithmIdentifier` is re-encoded: Ed25519 stays
320
321
  * parameters-absent, RSA keeps its NULL, EC keeps its namedCurve.
321
322
  *
323
+ * A composite ML-KEM key is derived from its two components instead, since the node key engine does not
324
+ * read a composite algorithm. The private key is `seed || tradSK` and the public key is
325
+ * `mlkemEK || tradPK` under the same OID: the seed states the ML-KEM encapsulation key it generates, the
326
+ * traditional half states its own public key, and the two are concatenated. An AlgorithmIdentifier
327
+ * carrying parameters, a key no longer than the ML-KEM seed, and an unregistered composite OID are each
328
+ * refused with a typed `KemError`.
329
+ *
322
330
  * @opts
323
331
  * - `pem` (boolean) -- return a `PUBLIC KEY` PEM string instead of DER.
324
332
  * @example
@@ -327,12 +335,40 @@ async function generate(algorithm, opts) {
327
335
  */
328
336
  var _PUBLIC_FROM_PRIVATE_OPTS = Object.assign(Object.create(null), { pem: 1 });
329
337
 
338
+ /** @internal The composite ML-KEM OID a private key names, or null when it names anything else. Read
339
+ * from the AlgorithmIdentifier alone: a key that is not well formed enough to state one is left to the
340
+ * decoder below, which reports it. */
341
+ function _compositeKemOidOf(der) {
342
+ var algOid;
343
+ try {
344
+ /** @internal The AlgorithmIdentifier OID only, read without the strict OneAsymmetricKey rules. A
345
+ * composite key that breaks one of those rules is still a composite key, and the module that owns
346
+ * the algorithm is the one that should say so: parsing strictly here would send a composite key
347
+ * carrying parameters to the runtime decoder, which reports that it cannot read the key rather
348
+ * than that the parameters are not allowed (draft sec. 5.3). */
349
+ var node = asn1.decode(der);
350
+ if (!node.children || node.children.length < 3) return null;
351
+ var alg = node.children[1];
352
+ if (!alg.children || alg.children.length < 1) return null;
353
+ algOid = asn1.read.oid(alg.children[0]);
354
+ } catch (_e) { return null; }
355
+ return typeof algOid === "string" && compositeKem.isCompositeKem(algOid) ? algOid : null;
356
+ }
357
+
330
358
  async function publicFromPrivate(privateKey, opts) {
331
359
  opts = guard.identifier.optionsObject(opts, _err, "key/bad-input", "pki.key.publicFromPrivate options");
332
360
  guard.identifier.assertKnownKeys(opts, _PUBLIC_FROM_PRIVATE_OPTS, _err, "key/bad-input",
333
361
  "pki.key.publicFromPrivate has an unknown option: ");
334
362
  var der = await _toPrivateKeyDer(privateKey);
335
363
  var spki;
364
+ /** @internal A composite ML-KEM key is a toolkit-defined algorithm the runtime decoder cannot read,
365
+ * so it is routed before the decoder rather than after it fails. Its own module owns the split and
366
+ * the wipe, and reports its own typed reasons. */
367
+ var compositeOid = _compositeKemOidOf(der);
368
+ if (compositeOid !== null) {
369
+ spki = await compositeKem.publicFromPrivate(der);
370
+ return opts.pem ? pkix.pemEncode(spki, "PUBLIC KEY", PemError) : spki;
371
+ }
336
372
  try {
337
373
  var priv = nodeCrypto.createPrivateKey({ key: der, format: "der", type: "pkcs8" });
338
374
  spki = nodeCrypto.createPublicKey(priv).export({ format: "der", type: "spki" });
@@ -340,6 +376,164 @@ async function publicFromPrivate(privateKey, opts) {
340
376
  return opts.pem ? pkix.pemEncode(spki, "PUBLIC KEY", PemError) : spki;
341
377
  }
342
378
 
379
+ /**
380
+ * @internal Whether a private key and a SubjectPublicKeyInfo are the two halves of one key pair, for a
381
+ * caller deciding whether a private key it was handed goes with a certificate it was handed.
382
+ *
383
+ * Deriving the public key and comparing does NOT answer this. A key structure states its own public
384
+ * half and the key engine reads what it is told: RFC 5958 sec. 2 gives a OneAsymmetricKey an optional
385
+ * `publicKey`, an EC key's RFC 5915 `ECPrivateKey` carries its own public point, and an RSA private
386
+ * key carries the modulus and public exponent outright. A structure whose private components were
387
+ * replaced while its stored public ones were left alone therefore derives to exactly the public key
388
+ * that was planted in it, and the comparison accepts a private key that cannot use the certificate.
389
+ *
390
+ * So the pair is proven by USING it, on whichever operation the key type can perform: a signature the
391
+ * public half verifies, a Diffie-Hellman agreement that reaches the same secret from both sides, or a
392
+ * key encapsulation the private half decapsulates to the same secret. Each fails for a key that merely
393
+ * claims the public half.
394
+ *
395
+ * What a true answer means is exactly that: the private key WORKS with this public key, on the
396
+ * operation its type performs. It is not a statement that every field of the private structure is
397
+ * internally consistent, which no operation reveals. A component the algorithm ignores, such as a bit
398
+ * an X25519 scalar clamps away or an ML-KEM implicit-rejection secret, is not exercised by a
399
+ * correspondence that succeeds, and does not stop the key from being usable with the certificate.
400
+ * A caller needing structural consistency has a different question, and this is not it.
401
+ */
402
+ var _PAIR_DIGEST = Object.assign(Object.create(null), { rsa: "sha256", ec: "sha256" });
403
+ var _PAIR_AGREE = Object.assign(Object.create(null), { x25519: 1, x448: 1 });
404
+ var _PAIR_KEM = Object.assign(Object.create(null),
405
+ { "ml-kem-512": 1, "ml-kem-768": 1, "ml-kem-1024": 1 });
406
+ /** @internal RFC 4055 sec. 1.2: ONE RSA key pair may be identified either by `rsaEncryption` or by
407
+ * `id-RSASSA-PSS`, and a delivered private key and the certificate that certifies it need not have
408
+ * chosen the same one. The key engine reports those two encodings as different types, so comparing
409
+ * types alone would call a usable pair no pair at all. They name one algorithm and are exercised
410
+ * together, under PSS, which is the padding the restricted encoding permits. */
411
+ var _PAIR_RSA = Object.assign(Object.create(null), { rsa: 1, "rsa-pss": 1 });
412
+ var _PAIR_PROBE = Buffer.from("pki.js key pair correspondence", "utf8");
413
+
414
+ function _sameKeyFamily(priv, pub) {
415
+ if (priv.asymmetricKeyType === pub.asymmetricKeyType) return true;
416
+ return !!(_PAIR_RSA[guard.text.keyOf(priv.asymmetricKeyType)] &&
417
+ _PAIR_RSA[guard.text.keyOf(pub.asymmetricKeyType)]);
418
+ }
419
+
420
+ /** @internal How to exercise a signing pair: the digest, and any padding the encoding requires. An
421
+ * `id-RSASSA-PSS` key may pin the hash and salt length it is usable with, and the engine refuses any
422
+ * other, so a pinned value from either half is what proves the pair. */
423
+ function _pairSignOptions(priv, pub) {
424
+ var privPss = priv.asymmetricKeyType === "rsa-pss", pubPss = pub.asymmetricKeyType === "rsa-pss";
425
+ if (!privPss && !pubPss) {
426
+ return { digest: _PAIR_DIGEST[guard.text.keyOf(priv.asymmetricKeyType)] || null, options: null };
427
+ }
428
+ var privDetails = _pssRestrictionOf(priv, privPss);
429
+ var pubDetails = _pssRestrictionOf(pub, pubPss);
430
+ /** @internal A half that pins nothing constrains nothing, so only two halves that BOTH pin can
431
+ * disagree. Both encodings pinning incompatible parameters describes no operation this pair can
432
+ * perform together, which is not the same as being different keys: it is reported as unexercisable
433
+ * rather than as a false "not a pair". Where one pins and the other does not, the pinned half
434
+ * supplies the parameters below and the engine enforces it against its own key. */
435
+ if (_pssConflict(privDetails, pubDetails)) return null;
436
+ var digest = privDetails.hashAlgorithm || pubDetails.hashAlgorithm || "sha256";
437
+ /** @internal The engine derives MGF1 from the digest and takes no separate option for it, so a half
438
+ * that pins a mask generator other than its own hash names an operation this pair cannot perform
439
+ * together. That is the same "cannot be decided" verdict two conflicting halves get, not a false
440
+ * report that the halves are different keys. */
441
+ var mgf1 = privDetails.mgf1HashAlgorithm || pubDetails.mgf1HashAlgorithm;
442
+ if (mgf1 != null && mgf1 !== digest) return null;
443
+ var salt = _pssSalt(privDetails.saltLength, pubDetails.saltLength);
444
+ /** @internal Null-prototype, because the engine READS this object by name. A plain object would
445
+ * let an inherited `saltLength` reach the signature as a restriction neither half stated. */
446
+ var options = Object.create(null);
447
+ options.padding = nodeCrypto.constants.RSA_PKCS1_PSS_PADDING;
448
+ if (salt != null) options.saltLength = salt;
449
+ return { digest: digest, options: options };
450
+ }
451
+
452
+ var _PSS_PINS = ["hashAlgorithm", "mgf1HashAlgorithm", "saltLength"];
453
+
454
+ /** @internal What a half pins, as a null-prototype record carrying only what the key itself states.
455
+ * A half pins nothing when it is not an id-RSASSA-PSS key at all, and equally when it is one whose
456
+ * parameters are absent, which RFC 4055 sec. 3.1 leaves optional. Reading either through a plain
457
+ * object would let an inherited `saltLength` or `hashAlgorithm` answer as a restriction the key never
458
+ * carried, and refuse a genuine pair. */
459
+ function _pssRestrictionOf(key, isPss) {
460
+ var out = Object.create(null);
461
+ var details = isPss ? key.asymmetricKeyDetails : null;
462
+ if (details == null) return out;
463
+ for (var i = 0; i < _PSS_PINS.length; i++) {
464
+ var name = _PSS_PINS[i];
465
+ if (Object.prototype.hasOwnProperty.call(details, name) && details[name] != null) out[name] = details[name];
466
+ }
467
+ return out;
468
+ }
469
+
470
+ /** @internal Whether two halves state a restriction that cannot be satisfied at once. RFC 4055
471
+ * sec. 3.3 requires the signature's hash and mask generator to match the key's, so two halves naming
472
+ * different ones describe no shared operation. It treats the salt length differently: "The saltLength
473
+ * field in the signature parameters MUST be greater or equal to that in the key parameters field",
474
+ * and sec. 3.1 says the field "does not need to be fixed for a given RSA key pair". Two halves naming
475
+ * different salt lengths are therefore both satisfied by the larger, and do not conflict. A field
476
+ * only one half states constrains only that half, so it cannot disagree with anything. */
477
+ function _pssConflict(a, b) {
478
+ if (a.hashAlgorithm != null && b.hashAlgorithm != null && a.hashAlgorithm !== b.hashAlgorithm) return true;
479
+ if (a.mgf1HashAlgorithm != null && b.mgf1HashAlgorithm != null && a.mgf1HashAlgorithm !== b.mgf1HashAlgorithm) return true;
480
+ return false;
481
+ }
482
+
483
+ /** @internal The smallest salt length both halves accept, which is the larger of the two floors. */
484
+ function _pssSalt(a, b) {
485
+ if (a == null) return b;
486
+ if (b == null) return a;
487
+ return a > b ? a : b;
488
+ }
489
+
490
+ async function correspondsTo(privateKey, spkiDer) {
491
+ var privDer = await _toPrivateKeyDer(privateKey);
492
+ var priv, pub;
493
+ try { priv = nodeCrypto.createPrivateKey({ key: privDer, format: "der", type: "pkcs8" }); }
494
+ catch (e) { throw _err("key/bad-input", "the private key could not be read", e); }
495
+ try { pub = nodeCrypto.createPublicKey({ key: Buffer.from(spkiDer), format: "der", type: "spki" }); }
496
+ catch (e) { throw _err("key/bad-input", "the public key could not be read", e); }
497
+ if (!_sameKeyFamily(priv, pub)) return false;
498
+ var kind = guard.text.keyOf(priv.asymmetricKeyType);
499
+ try {
500
+ if (_PAIR_KEM[kind]) {
501
+ var sealed = nodeCrypto.encapsulate(pub);
502
+ return guard.crypto.constantTimeEqual(nodeCrypto.decapsulate(priv, sealed.ciphertext), sealed.sharedKey);
503
+ }
504
+ if (_PAIR_AGREE[kind]) {
505
+ var ephemeral = nodeCrypto.generateKeyPairSync(priv.asymmetricKeyType);
506
+ if (!guard.crypto.constantTimeEqual(
507
+ nodeCrypto.diffieHellman({ privateKey: priv, publicKey: ephemeral.publicKey }),
508
+ nodeCrypto.diffieHellman({ privateKey: ephemeral.privateKey, publicKey: pub }))) return false;
509
+ /** @internal The agreement proves the SCALAR, not the encoding it was handed. RFC 7748 masks the
510
+ * top bit of an X25519 u-coordinate before the ladder, so a public value differing from the real
511
+ * one only in a masked bit reaches the same secret from both sides. The certificate would then
512
+ * carry bytes this key does not produce, and its subjectKeyIdentifier and any pinning of it
513
+ * would name a key the holder does not have. These types store no public copy in the private
514
+ * structure for an attacker to plant, so what the scalar generates IS the canonical answer and
515
+ * comparing against it adds a bound the agreement alone does not give. */
516
+ return guard.crypto.constantTimeEqual(
517
+ nodeCrypto.createPublicKey(priv).export({ format: "der", type: "spki" }),
518
+ pub.export({ format: "der", type: "spki" }));
519
+ }
520
+ var how = _pairSignOptions(priv, pub);
521
+ if (how === null) {
522
+ throw _err("key/unsupported-algorithm", "the two halves pin incompatible RSASSA-PSS parameters, so no operation exercises them together and whether they are a pair cannot be decided");
523
+ }
524
+ var signKey = how.options ? Object.assign(Object.create(null), { key: priv }, how.options) : priv;
525
+ var verifyKey = how.options ? Object.assign(Object.create(null), { key: pub }, how.options) : pub;
526
+ return nodeCrypto.verify(how.digest, _PAIR_PROBE, verifyKey,
527
+ nodeCrypto.sign(how.digest, _PAIR_PROBE, signKey)) === true;
528
+ } catch (e) {
529
+ /** @internal The incompatible-parameters verdict is decided rather than caught, so it is passed
530
+ * through with the reason it was raised for. Rewrapping it would tell an operator the pair could
531
+ * not be exercised without saying that the two encodings pin conflicting parameters. */
532
+ if (e && e.code === "key/unsupported-algorithm" && e.isPkiError === true) throw e;
533
+ throw _err("key/unsupported-algorithm", "the key pair could not be exercised to decide whether its two halves correspond", e);
534
+ }
535
+ }
536
+
343
537
 
344
538
  function _detectKeyInput(input) {
345
539
  var der = pkix.coerceToDer(input, { pemLabel: null, PemError: PemError, ErrorClass: KeyError, prefix: "key" });
@@ -397,4 +591,5 @@ module.exports = {
397
591
  import: import_,
398
592
  generate: generate,
399
593
  publicFromPrivate: publicFromPrivate,
594
+ correspondsTo: correspondsTo, // @internal
400
595
  };
package/lib/oid.js CHANGED
@@ -174,6 +174,8 @@ var FAMILIES = Object.assign(Object.create(null), {
174
174
  countersignature: 6, challengePassword: 7, extensionRequest: 14,
175
175
  smimeCapabilities: 15, friendlyName: 20, localKeyId: 21 } },
176
176
 
177
+ keyPackageContentTypes: { base: [2, 16, 840, 1, 101, 2, 1, 2, 78], of: { aKeyPackage: 5 } },
178
+
177
179
  pkcs9CertTypes: { base: [1, 2, 840, 113549, 1, 9, 22], of: { x509Certificate: 1, sdsiCertificate: 2 } },
178
180
  pkcs9CrlTypes: { base: [1, 2, 840, 113549, 1, 9, 23], of: { x509CRL: 1 } },
179
181
 
@@ -486,8 +486,16 @@ function ipMatch(constraint, addr) {
486
486
  return true;
487
487
  }
488
488
 
489
+ /** @internal The one place a constraint base meets a name, so the base's form is read here and
490
+ * nowhere else. A caller's base met `guard.name.constraintBaseRefusal` at the door; a base a
491
+ * certificate carries in its own nameConstraints extension met no door, and the same rule decides it
492
+ * here. Answering a base outside its tag's form with no match rather than no verdict would make an
493
+ * excluded subtree that covers nothing exclude nothing. */
489
494
  function nameMatchesConstraint(gnTag, gnValue, base) {
490
495
  if (base.tagNumber !== gnTag) return null;
496
+ if (gnTag === 1 || gnTag === 2 || gnTag === 6) {
497
+ if (guard.name.constraintBaseUnreadable(gnTag, base.value)) return "unsupported";
498
+ }
491
499
  switch (gnTag) {
492
500
  case 1: return emailMatch(base.value, gnValue);
493
501
  case 2: return hostConstraintMatch(base.value, gnValue);
package/lib/schema-cmp.js CHANGED
@@ -359,7 +359,7 @@ var ENCRYPTED_KEY = schema.choice([
359
359
  if (ct === null || ct.length === 0) {
360
360
  throw ctx.E("cmp/bad-cert-response", "an EncryptedKey envelopedData must carry non-empty attached ciphertext -- its encryptedContent is the certificate/key payload (RFC 9810 sec. 5.2.2)");
361
361
  }
362
- return { envelopedData: env };
362
+ return { envelopedData: env, bytes: asn1.sequenceTlv(n) };
363
363
  }) },
364
364
  ], { code: "cmp/bad-cert-response", what: "EncryptedKey" });
365
365
 
@@ -539,6 +539,44 @@ function keyUsageOf(ns, cert, E, code, label) {
539
539
  return null;
540
540
  }
541
541
 
542
+ /** @internal The extendedKeyUsage purposes a certificate asserts, or null when it carries no such
543
+ * extension. A malformed extension throws through the caller's ErrorClass; it never reads as an
544
+ * empty set. */
545
+ var _EKU_DECODER = new _WeakMapI();
546
+ function extKeyUsageOf(ns, cert, E, code, label) {
547
+ var exts = (cert && cert.extensions) || [];
548
+ var want = ns.oid.byName("extKeyUsage");
549
+ for (var i = 0; i < exts.length; i++) {
550
+ if (exts[i].oid !== want || exts[i].value == null) continue;
551
+ var dec = _weakGet(_EKU_DECODER, ns);
552
+ if (!dec) { dec = certExtensionDecoders(ns).byOid[want]; _weakSet(_EKU_DECODER, ns, dec); }
553
+ try { return dec(exts[i].value); }
554
+ catch (e) { throw E(code, "the " + label + " extendedKeyUsage extension is malformed", e); }
555
+ }
556
+ return null;
557
+ }
558
+
559
+ /**
560
+ * @internal Whether a certificate ASSERTS a key purpose, for a rule that requires the assertion to be
561
+ * present. RFC 5280 sec. 4.2.1.12 constrains a certificate only when the extension is there, so an
562
+ * absent extendedKeyUsage permits every purpose and satisfies `pki.path.validate`'s `requiredEku`. A
563
+ * profile that reads the purpose as an authorization statement (RFC 6960's id-kp-OCSPSigning
564
+ * responder, RFC 9483 sec. 4.1.6's id-kp-cmKGA key generation authority) needs the opposite reading:
565
+ * the certificate must say so. This returns false for an absent, empty, malformed, or
566
+ * differently-purposed extension, and true only when the named purpose is listed.
567
+ *
568
+ * @enforced-by behavioral -- an absent extension answering an authorization question has no
569
+ * rename-proof code shape; each consumer's RED conformance vector drives the shipped verb on a
570
+ * certificate with no extendedKeyUsage and asserts the refusal.
571
+ */
572
+ function assertsPurpose(ns, cert, purposeOid) {
573
+ var purposes;
574
+ try { purposes = extKeyUsageOf(ns, cert, ns.E, ns.prefix + "/bad-extension-value", "certificate"); }
575
+ catch (_e) { return false; }
576
+ if (!purposes) return false;
577
+ return guard.list.contains(purposes, purposeOid);
578
+ }
579
+
542
580
  function issuingDistributionPoint(code) {
543
581
  return schema.seq([
544
582
  schema.trailing([
@@ -676,7 +714,7 @@ function certExtensionDecoders(ns) {
676
714
  for (var z = 0; z < bs.bytes.length; z++) { if (bs.bytes[z] !== 0) { anyBit = true; break; } }
677
715
  if (!anyBit) throw ns.E(C, "KeyUsage must assert at least one bit (RFC 5280 sec. 4.2.1.3)");
678
716
  schema.assertMinimalNamedBits(bs.unusedBits, bs.bytes, function (m) { throw ns.E(C, m); });
679
- // allow:registry-table-inherits-object-prototype -- the decoded KeyUsage record, whose keys are the fixed KU_BITS names the schema declares rather than anything the certificate supplies
717
+ // allow:registry-table-inherits-object-prototype -- the decoded KeyUsage record, whose keys are the fixed KU_BITS names the schema declares; the certificate supplies none of them
680
718
  var out = {};
681
719
  _forEach(KU_BITS, function (nm, bit) {
682
720
  var byte = bit >> 3, mask = 0x80 >> (bit & 7);
@@ -822,7 +860,7 @@ function certExtensionDecoders(ns) {
822
860
  function authorityKeyIdentifier(buf) {
823
861
  var C = ns.prefix + "/bad-extension-value";
824
862
  var kids = seqChildren(buf, C, "AuthorityKeyIdentifier");
825
- // allow:registry-table-inherits-object-prototype -- the decoded AuthorityKeyIdentifier record, read by the three field names the schema declares rather than by anything the certificate supplies
863
+ // allow:registry-table-inherits-object-prototype -- the decoded AuthorityKeyIdentifier record, read by the three field names the schema declares; the certificate supplies none of them
826
864
  var out = { keyIdentifier: null, authorityCertIssuer: null, authorityCertSerialNumber: null };
827
865
  var lastTag = -1;
828
866
  _forEach(kids, function (k) {
@@ -869,7 +907,7 @@ function certExtensionDecoders(ns) {
869
907
  if (dp.tagClass !== "universal" || dp.tagNumber !== _T.SEQUENCE || !dp.children) {
870
908
  throw ns.E(C, "DistributionPoint must be a SEQUENCE { distributionPoint?, reasons?, cRLIssuer? }");
871
909
  }
872
- // allow:registry-table-inherits-object-prototype -- the decoded DistributionPoint record, read by the three field names the schema declares rather than by anything the certificate supplies
910
+ // allow:registry-table-inherits-object-prototype -- the decoded DistributionPoint record, read by the three field names the schema declares; the certificate supplies none of them
873
911
  var out = { distributionPoint: null, reasons: null, cRLIssuer: null };
874
912
  var lastTag = -1;
875
913
  _forEach(dp.children, function (f) {
@@ -1247,6 +1285,8 @@ module.exports = {
1247
1285
  distributionPointName: distributionPointName,
1248
1286
  issuingDistributionPoint: issuingDistributionPoint,
1249
1287
  keyUsageOf: keyUsageOf,
1288
+ extKeyUsageOf: extKeyUsageOf,
1289
+ assertsPurpose: assertsPurpose,
1250
1290
  generalizedTime: generalizedTime,
1251
1291
  utf8Text: utf8Text,
1252
1292
  rawNonEmptySequence: rawNonEmptySequence,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/pki",
3
- "version": "0.6.51",
3
+ "version": "0.6.53",
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:d9015ac6-f8a1-409f-9e1f-5ae1fc7491a1",
5
+ "serialNumber": "urn:uuid:258eb315-4dba-4e0f-8600-313c47a1a144",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-09-07T06:27:34.397Z",
8
+ "timestamp": "2026-09-07T16:58:48.291Z",
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.6.51",
22
+ "bom-ref": "@blamejs/pki@0.6.53",
23
23
  "type": "application",
24
24
  "name": "pki",
25
- "version": "0.6.51",
25
+ "version": "0.6.53",
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.6.51",
29
+ "purl": "pkg:npm/%40blamejs/pki@0.6.53",
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.6.51",
57
+ "ref": "@blamejs/pki@0.6.53",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]