@blamejs/pki 0.4.15 → 0.5.0

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +37 -1
  2. package/MIGRATING.md +2 -2
  3. package/README.md +142 -137
  4. package/index.js +4 -0
  5. package/lib/acme.js +73 -1
  6. package/lib/asn1-der.js +2 -0
  7. package/lib/attrcert-sign.js +4 -0
  8. package/lib/cbor-det.js +32 -16
  9. package/lib/cmc-build.js +880 -0
  10. package/lib/cmc-verify.js +657 -0
  11. package/lib/cmp-build.js +8 -7
  12. package/lib/cmp-verify.js +11 -1
  13. package/lib/cms-sign.js +170 -8
  14. package/lib/cms-verify.js +80 -14
  15. package/lib/crl-sign.js +22 -0
  16. package/lib/crmf-sign.js +5 -2
  17. package/lib/csr-sign.js +3 -0
  18. package/lib/ct.js +72 -0
  19. package/lib/est.js +828 -32
  20. package/lib/framework-error.js +13 -0
  21. package/lib/guard-bytes.js +37 -1
  22. package/lib/guard-range.js +23 -1
  23. package/lib/http-transport.js +9 -3
  24. package/lib/inspect.js +28 -5
  25. package/lib/jose.js +15 -0
  26. package/lib/lint.js +4 -0
  27. package/lib/merkle.js +5 -5
  28. package/lib/ocsp.js +139 -11
  29. package/lib/oid.js +69 -1
  30. package/lib/path-validate.js +27 -4
  31. package/lib/pkcs12-build.js +12 -0
  32. package/lib/schema-all.js +19 -1
  33. package/lib/schema-attrcert.js +27 -0
  34. package/lib/schema-c509.js +6 -0
  35. package/lib/schema-cmc.js +791 -0
  36. package/lib/schema-cmp.js +25 -0
  37. package/lib/schema-cms.js +17 -1
  38. package/lib/schema-crl.js +23 -1
  39. package/lib/schema-crmf.js +13 -0
  40. package/lib/schema-csr.js +11 -0
  41. package/lib/schema-csrattrs.js +6 -0
  42. package/lib/schema-engine.js +6 -2
  43. package/lib/schema-ocsp.js +41 -0
  44. package/lib/schema-pkcs12.js +16 -0
  45. package/lib/schema-pkcs8.js +8 -0
  46. package/lib/schema-smime.js +4 -4
  47. package/lib/schema-tsp.js +32 -1
  48. package/lib/schema-x509.js +14 -1
  49. package/lib/shbs.js +12 -4
  50. package/lib/sigstore.js +4 -0
  51. package/lib/smime.js +28 -7
  52. package/lib/tls-cert-compress.js +15 -3
  53. package/lib/trust.js +27 -4
  54. package/lib/tsp-sign.js +41 -6
  55. package/lib/vendor/README.md +19 -19
  56. package/lib/webauthn.js +895 -26
  57. package/lib/x509-sign.js +3 -0
  58. package/package.json +1 -1
  59. package/sbom.cdx.json +6 -6
package/lib/smime.js CHANGED
@@ -495,7 +495,7 @@ function _base64Body(der) {
495
495
  * @primitive pki.smime.sign
496
496
  * @signature pki.smime.sign(content, signers, opts?) -> Promise<Buffer>
497
497
  * @since 0.2.25
498
- * @status experimental
498
+ * @status stable
499
499
  * @spec RFC 8551, RFC 5652
500
500
  * @related pki.smime.verify, pki.cms.sign
501
501
  *
@@ -521,6 +521,10 @@ function _base64Body(der) {
521
521
  * @opts protectHeaders enable RFC 9788 header protection (`hp="clear"`) -- inline `opts.headers` on the signed payload + the outer display headers.
522
522
  * @opts headers the Non-Structural fields to protect + display: an object `{ Name: value }` or an array `[{ name, value }]` (Subject / From / To / Date / ...); used with `protectHeaders`.
523
523
  * @example
524
+ * var pair = await pki.key.generate("Ed25519");
525
+ * var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
526
+ * var signerCertDer = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
527
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: signerKeyPkcs8 });
524
528
  * var msg = await pki.smime.sign(Buffer.from("hello"), [{ cert: signerCertDer, key: signerKeyPkcs8 }]);
525
529
  */
526
530
  async function sign(content, signers, opts) {
@@ -565,7 +569,7 @@ function _capped(msg) {
565
569
  * @primitive pki.smime.verify
566
570
  * @signature pki.smime.verify(message, opts?) -> Promise<{ valid, signers, form, content, micalg, protectedHeaders, headerProtection }>
567
571
  * @since 0.2.25
568
- * @status experimental
572
+ * @status stable
569
573
  * @spec RFC 8551, RFC 5652, RFC 9788
570
574
  * @related pki.smime.sign, pki.cms.verify, pki.path.validate
571
575
  *
@@ -595,6 +599,11 @@ function _capped(msg) {
595
599
  * @opts strictMicalg reject a `multipart/signed` whose `micalg` disagrees with the SignerInfo digest.
596
600
  * @opts legacyHeaderProtection opt in to detecting a LEGACY RFC 8551 header-protected message (RFC 9788 sec. 4.10): a Cryptographic Payload that is a bare `message/rfc822` wrap with no `hp=` parameter. When set, a precisely-identified legacy message surfaces the inner message's headers under `headerProtection.legacy = { headers, mode, fromMismatch, confidential }` -- `headers` an ordered `[{ name, value }]` array (retaining legally-repeated fields such as `Received`), the mode inferred from the envelope (`clear` here) -- NOT under `protectedHeaders`, and `present` stays `false`. Consuming `headerProtection.legacy.headers` is an explicit choice: a legacy message is structurally indistinguishable from an ordinary forwarded `message/rfc822`, so this is a heuristic (RFC 9788 sec. 4.10.2: "not based on any strong end-to-end guarantees") -- cross-check `legacy.fromMismatch`. Anything not precisely identified (a nested crypto layer, an `hp=` on the inner message, a non-`message/rfc822` payload, a duplicate of a singleton field, or a duplicate Content-Type) reports `legacy: null`. Off by default. The signed-and-encrypted form (RFC 9788 Appendix C.3.17) is a documented gap (`legacy: null` at `decrypt`; surfaces as `clear` only via the caller's re-`verify` step) -- the non-recursive layered API exposes no single seam holding both the inner signature verdict and the outer header section.
597
601
  * @example
602
+ * var pair = await pki.key.generate("Ed25519");
603
+ * var key = await pki.key.export(pair.privateKey);
604
+ * var cert = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
605
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
606
+ * var smimeMessageBytes = await pki.smime.sign(Buffer.from("hello"), [{ cert: cert, key: key }]);
598
607
  * var res = await pki.smime.verify(smimeMessageBytes);
599
608
  * if (res.valid) { res.content; res.signers[0].sid; }
600
609
  */
@@ -680,7 +689,7 @@ function _cmsEncryptOpts(opts) {
680
689
  * @primitive pki.smime.encrypt
681
690
  * @signature pki.smime.encrypt(content, recipients, opts?) -> Promise<Buffer>
682
691
  * @since 0.2.26
683
- * @status experimental
692
+ * @status stable
684
693
  * @spec RFC 8551, RFC 5652, RFC 5083
685
694
  * @related pki.smime.decrypt, pki.cms.encrypt
686
695
  *
@@ -709,6 +718,11 @@ function _cmsEncryptOpts(opts) {
709
718
  * @opts keyIdentifier forwarded: `"issuerAndSerial"` (default) or `"subjectKeyIdentifier"`.
710
719
  * @opts ukm forwarded: user keying material for kari / kemri recipients.
711
720
  * @example
721
+ * var rsa = { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" };
722
+ * var pair = await pki.key.generate(rsa);
723
+ * var recipientCertDer = await pki.x509.sign({ subject: "Recipient", subjectPublicKey: await pki.key.export(pair.publicKey),
724
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
725
+ * { key: await pki.key.export(pair.privateKey) });
712
726
  * var enc = await pki.smime.encrypt(Buffer.from("secret"), [{ cert: recipientCertDer }]);
713
727
  */
714
728
  async function encrypt(content, recipients, opts) {
@@ -735,7 +749,7 @@ async function encrypt(content, recipients, opts) {
735
749
  * @primitive pki.smime.decrypt
736
750
  * @signature pki.smime.decrypt(message, keyMaterial, opts?) -> Promise<{ content, smimeType, authenticated, recipientType, recipientIndex, contentEncryptionAlgorithm, protectedHeaders, headerProtection }>
737
751
  * @since 0.2.26
738
- * @status experimental
752
+ * @status stable
739
753
  * @spec RFC 8551, RFC 5652, RFC 5083, RFC 9788
740
754
  * @related pki.smime.encrypt, pki.cms.decrypt, pki.smime.verify
741
755
  *
@@ -763,9 +777,15 @@ async function encrypt(content, recipients, opts) {
763
777
  * @opts strictSmimeType reject a header `smime-type` that disagrees with the CMS body (`smime/smime-type-mismatch`).
764
778
  * @opts legacyHeaderProtection opt in to detecting a LEGACY RFC 8551 header-protected message (RFC 9788 sec. 4.10): an encrypted Cryptographic Payload that is a bare `message/rfc822` wrap with no `hp=` parameter. When set, a precisely-identified legacy message surfaces the inner headers under `headerProtection.legacy = { headers, mode: "cipher", fromMismatch, confidential }` -- `headers` an ordered `[{ name, value }]` array (retaining repeated fields), the `confidential` set derived from the actual visible outer Header Section -- NOT under `protectedHeaders`, and `present` stays `false`, since a legacy message is structurally indistinguishable from a forwarded `message/rfc822` (a heuristic; cross-check `legacy.fromMismatch`). Anything not precisely identified reports `legacy: null`. Off by default.
765
779
  * @example
780
+ * var rsa = { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" };
781
+ * var pair = await pki.key.generate(rsa);
782
+ * var recipientKeyPkcs8 = await pki.key.export(pair.privateKey);
783
+ * var recipientCertDer = await pki.x509.sign({ subject: "Recipient", subjectPublicKey: await pki.key.export(pair.publicKey),
784
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: recipientKeyPkcs8 });
785
+ * var smimeMessageBytes = await pki.smime.encrypt(Buffer.from("secret"), [{ cert: recipientCertDer }]);
766
786
  * var res = await pki.smime.decrypt(smimeMessageBytes, { key: recipientKeyPkcs8, cert: recipientCertDer });
767
787
  * res.content; // the recovered inner MIME entity
768
- * res.authenticated; // false for an enveloped-only (CBC) message -- no integrity
788
+ * res.authenticated; // true here (an AEAD content cipher); false for an enveloped-only CBC message, which carries no integrity
769
789
  */
770
790
  async function decrypt(message, keyMaterial, opts) {
771
791
  opts = opts || {};
@@ -795,7 +815,7 @@ async function decrypt(message, keyMaterial, opts) {
795
815
  * @primitive pki.smime.compress
796
816
  * @signature pki.smime.compress(content, opts?) -> Promise<Buffer>
797
817
  * @since 0.2.27
798
- * @status experimental
818
+ * @status stable
799
819
  * @spec RFC 8551, RFC 3274
800
820
  * @related pki.smime.decompress, pki.cms.compress
801
821
  *
@@ -827,7 +847,7 @@ async function compress(content, opts) {
827
847
  * @primitive pki.smime.decompress
828
848
  * @signature pki.smime.decompress(message, opts?) -> Promise<{ content, contentType, contentTypeName, compressionAlgorithm }>
829
849
  * @since 0.2.27
830
- * @status experimental
850
+ * @status stable
831
851
  * @spec RFC 8551, RFC 3274
832
852
  * @related pki.smime.compress, pki.cms.decompress, pki.smime.verify, pki.smime.decrypt
833
853
  *
@@ -842,6 +862,7 @@ async function compress(content, opts) {
842
862
  *
843
863
  * @opts maxOutputBytes forwarded to cms.decompress: lower the decompressed-output cap (a DoS bound; downward only).
844
864
  * @example
865
+ * var compressedSmimeBytes = await pki.smime.compress(Buffer.from("compress me"));
845
866
  * var res = await pki.smime.decompress(compressedSmimeBytes);
846
867
  * res.content; // the recovered inner MIME entity
847
868
  */
@@ -141,7 +141,7 @@ function _resolveAllowed(opts) {
141
141
  * @primitive pki.tls.decompressCertificate
142
142
  * @signature pki.tls.decompressCertificate(bytes, opts?) -> { algorithm, algorithmName, uncompressedLength, certificateMessage, certificate }
143
143
  * @since 0.4.3
144
- * @status experimental
144
+ * @status stable
145
145
  * @spec RFC 8879, RFC 8446
146
146
  * @related pki.tls.compressCertificate, pki.tls.parseCertificateMessage, pki.schema.x509.parse
147
147
  *
@@ -179,6 +179,10 @@ function _resolveAllowed(opts) {
179
179
  * @example
180
180
  * // The Certificate message this codec carries: an empty request context, then one
181
181
  * // entry -- the certificate DER followed by its (here empty) extensions vector.
182
+ * var pair = await pki.key.generate("Ed25519");
183
+ * var certDer = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
184
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
185
+ * { key: await pki.key.export(pair.privateKey) });
182
186
  * var u24 = function (n) { var b = Buffer.alloc(3); b.writeUIntBE(n, 0, 3); return b; };
183
187
  * var entry = Buffer.concat([u24(certDer.length), certDer, Buffer.from([0, 0])]);
184
188
  * var message = Buffer.concat([Buffer.from([0]), u24(entry.length), entry]);
@@ -252,7 +256,7 @@ function decompressCertificate(bytes, opts) {
252
256
  * @primitive pki.tls.parseCertificateMessage
253
257
  * @signature pki.tls.parseCertificateMessage(bytes, opts?) -> { certificateRequestContext, entries }
254
258
  * @since 0.4.3
255
- * @status experimental
259
+ * @status stable
256
260
  * @spec RFC 8446, RFC 7250
257
261
  * @related pki.tls.decompressCertificate, pki.schema.x509.parse
258
262
  *
@@ -277,6 +281,10 @@ function decompressCertificate(bytes, opts) {
277
281
  * certificateType - "X509" (default) or "RawPublicKey" (RFC 7250).
278
282
  *
279
283
  * @example
284
+ * var pair = await pki.key.generate("Ed25519");
285
+ * var certDer = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
286
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
287
+ * { key: await pki.key.export(pair.privateKey) });
280
288
  * var u24 = function (n) { var b = Buffer.alloc(3); b.writeUIntBE(n, 0, 3); return b; };
281
289
  * var entry = Buffer.concat([u24(certDer.length), certDer, Buffer.from([0, 0])]);
282
290
  * var message = Buffer.concat([Buffer.from([0]), u24(entry.length), entry]);
@@ -307,7 +315,7 @@ function parseCertificateMessage(bytes, opts) {
307
315
  * @primitive pki.tls.compressCertificate
308
316
  * @signature pki.tls.compressCertificate(certificateMessage, opts?) -> Buffer
309
317
  * @since 0.4.3
310
- * @status experimental
318
+ * @status stable
311
319
  * @spec RFC 8879
312
320
  * @related pki.tls.decompressCertificate
313
321
  *
@@ -330,6 +338,10 @@ function parseCertificateMessage(bytes, opts) {
330
338
  * level - the codec's compression level, passed through unchanged where it applies.
331
339
  *
332
340
  * @example
341
+ * var pair = await pki.key.generate("Ed25519");
342
+ * var certDer = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
343
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
344
+ * { key: await pki.key.export(pair.privateKey) });
333
345
  * var u24 = function (n) { var b = Buffer.alloc(3); b.writeUIntBE(n, 0, 3); return b; };
334
346
  * var entry = Buffer.concat([u24(certDer.length), certDer, Buffer.from([0, 0])]);
335
347
  * var message = Buffer.concat([Buffer.from([0]), u24(entry.length), entry]);
package/lib/trust.js CHANGED
@@ -353,7 +353,7 @@ function _dedupAnchors(anchors) {
353
353
  * @primitive pki.trust.parseCertdata
354
354
  * @signature pki.trust.parseCertdata(text) -> { anchors }
355
355
  * @since 0.2.0
356
- * @status experimental
356
+ * @status stable
357
357
  * @spec RFC 5280 (NSS certdata.txt object stream)
358
358
  * @defends trust-metadata-misattribution (CWE-345), trust-store-parser-DoS (CWE-770)
359
359
  * @related pki.trust.parseCcadbCsv, pki.trust.anchor, pki.path.validate
@@ -378,6 +378,11 @@ function _dedupAnchors(anchors) {
378
378
  * @example
379
379
  * // Real input is the NSS certdata.txt read from disk; a one-root stream is
380
380
  * // synthesized here from a DER certificate to show the object shape.
381
+ * var pair = await pki.key.generate("Ed25519");
382
+ * var der = await pki.x509.sign({ subject: "Example Root", subjectPublicKey: await pki.key.export(pair.publicKey),
383
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
384
+ * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"] } },
385
+ * { key: await pki.key.export(pair.privateKey) });
381
386
  * var cert = pki.schema.x509.parse(der);
382
387
  * var oct = function (buf) { return Array.prototype.map.call(buf, function (b) { return "\\" + ("000" + b.toString(8)).slice(-3); }).join(""); };
383
388
  * var blk = function (n, v) { return n + " MULTILINE_OCTAL\n" + oct(v) + "\nEND\n"; };
@@ -573,7 +578,7 @@ function _pemCell(cell) {
573
578
  * @primitive pki.trust.parseCcadbCsv
574
579
  * @signature pki.trust.parseCcadbCsv(text) -> { anchors }
575
580
  * @since 0.2.0
576
- * @status experimental
581
+ * @status stable
577
582
  * @spec RFC 4180, RFC 5280 (CCADB certificate-records CSV)
578
583
  * @defends trust-metadata-misattribution (CWE-345), trust-store-parser-DoS (CWE-770)
579
584
  * @related pki.trust.parseCertdata, pki.trust.anchor, pki.path.validate
@@ -592,6 +597,11 @@ function _pemCell(cell) {
592
597
  * encoding, through the same strict time reader.
593
598
  *
594
599
  * @example
600
+ * var pair = await pki.key.generate("Ed25519");
601
+ * var pemText = await pki.x509.sign({ subject: "Example Root", subjectPublicKey: await pki.key.export(pair.publicKey),
602
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
603
+ * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } },
604
+ * { key: await pki.key.export(pair.privateKey) }, { pem: true });
595
605
  * var csv = "Common Name or Certificate Name,Trust Bits," +
596
606
  * "Distrust for TLS After Date,Distrust for S/MIME After Date,PEM Info\n" +
597
607
  * 'Example Root,"Websites; Email",2027.06.01,,"' + pemText + '"';
@@ -651,7 +661,7 @@ function parseCcadbCsv(text) {
651
661
  * @primitive pki.trust.anchor
652
662
  * @signature pki.trust.anchor(entry, opts?) -> trustAnchor
653
663
  * @since 0.2.0
654
- * @status experimental
664
+ * @status stable
655
665
  * @spec RFC 5280 sec. 6.1.1 (NSS trust-bit semantics)
656
666
  * @related pki.trust.parseCertdata, pki.trust.parseCcadbCsv, pki.path.validate
657
667
  *
@@ -669,12 +679,25 @@ function parseCcadbCsv(text) {
669
679
  * purpose: string // "serverAuth" | "emailProtection" | "codeSigning" -- fail-fast purpose check
670
680
  *
671
681
  * @example
682
+ * var ca = await pki.key.generate("Ed25519");
683
+ * var caKey = await pki.key.export(ca.privateKey);
684
+ * var caDer = await pki.x509.sign({ subject: "Example Root", subjectPublicKey: await pki.key.export(ca.publicKey),
685
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
686
+ * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"], subjectKeyIdentifier: true } },
687
+ * { key: caKey });
688
+ * var pemText = pki.schema.x509.pemEncode(caDer, "CERTIFICATE");
689
+ * var leaf = await pki.key.generate("Ed25519");
690
+ * var der = await pki.x509.sign({ subject: "leaf.example", subjectPublicKey: await pki.key.export(leaf.publicKey),
691
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
692
+ * extensions: { keyUsage: ["digitalSignature"], extendedKeyUsage: ["serverAuth"], authorityKeyIdentifier: true } },
693
+ * { cert: caDer, key: caKey });
672
694
  * var csv = "Common Name or Certificate Name,Trust Bits," +
673
695
  * "Distrust for TLS After Date,Distrust for S/MIME After Date,PEM Info\n" +
674
696
  * 'Example Root,Websites,,,"' + pemText + '"';
675
697
  * var entry = pki.trust.parseCcadbCsv(csv).anchors[0];
676
698
  * var anchor = pki.trust.anchor(entry, { purpose: "serverAuth" });
677
- * await pki.path.validate([pki.schema.x509.parse(der)], { time: new Date(), trustAnchor: anchor, checkPurpose: "serverAuth" });
699
+ * await pki.path.validate([pki.schema.x509.parse(der)],
700
+ * { time: new Date("2026-06-01T00:00:00Z"), trustAnchor: anchor, checkPurpose: "serverAuth" });
678
701
  */
679
702
  function anchor(entry, opts) {
680
703
  if (!entry || typeof entry !== "object" || !Buffer.isBuffer(entry.publicKey) ||
package/lib/tsp-sign.js CHANGED
@@ -76,7 +76,7 @@ function _signingCertV2(certDer, hashName) {
76
76
  * @primitive pki.tsp.sign
77
77
  * @signature pki.tsp.sign(messageImprint, tsa, opts) -> Promise<Buffer|string>
78
78
  * @since 0.2.15
79
- * @status experimental
79
+ * @status stable
80
80
  * @spec RFC 3161
81
81
  * @related pki.schema.tsp.parseToken, pki.cms.sign
82
82
  *
@@ -96,6 +96,14 @@ function _signingCertV2(certDer, hashName) {
96
96
  * @opts certHashAlgorithm The ESSCertIDv2 hash algorithm name. Default `sha256`.
97
97
  * @opts sid / pem Passed through to `pki.cms.sign` (signer identifier, PEM output).
98
98
  * @example
99
+ * var pair = await pki.key.generate("Ed25519");
100
+ * var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
101
+ * var signerCertDer = await pki.x509.sign({ subject: "Example TSA", subjectPublicKey: await pki.key.export(pair.publicKey),
102
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
103
+ * // RFC 3161 sec. 2.3: a TSA certificate's extendedKeyUsage MUST be critical
104
+ * extensions: { keyUsage: ["digitalSignature"], extendedKeyUsage: ["timeStamping"], extendedKeyUsageCritical: true } },
105
+ * { key: signerKeyPkcs8 });
106
+ * var sha256Digest = Buffer.from(await pki.webcrypto.subtle.digest("SHA-256", Buffer.from("hello")));
99
107
  * var imprint = { hashAlgorithm: "sha256", hashedMessage: sha256Digest };
100
108
  * var token = await pki.tsp.sign(imprint, { cert: signerCertDer, key: signerKeyPkcs8 }, { policy: "1.3.6.1.4.1.1", serialNumber: 1 });
101
109
  * (await pki.cms.verify(token)).valid; // true
@@ -200,7 +208,7 @@ function _assertImprint(mi) {
200
208
  * @primitive pki.tsp.request
201
209
  * @signature pki.tsp.request(messageImprint, opts) -> Buffer|string
202
210
  * @since 0.2.19
203
- * @status experimental
211
+ * @status stable
204
212
  * @spec RFC 3161
205
213
  * @related pki.tsp.parseRequest, pki.tsp.sign
206
214
  *
@@ -215,6 +223,7 @@ function _assertImprint(mi) {
215
223
  * @opts extensions An array of encoded Extension DER buffers ([0] IMPLICIT Extensions).
216
224
  * @opts pem Return a PEM "TIMESTAMP REQUEST" string instead of DER (boolean).
217
225
  * @example
226
+ * var sha256Digest = Buffer.from(await pki.webcrypto.subtle.digest("SHA-256", Buffer.from("hello")));
218
227
  * var req = pki.tsp.request({ hashAlgorithm: "sha256", hashedMessage: sha256Digest }, { nonce: 0x0102030405060708n, certReq: true });
219
228
  */
220
229
  function request(messageImprint, opts) {
@@ -242,7 +251,7 @@ function request(messageImprint, opts) {
242
251
  * @primitive pki.tsp.parseRequest
243
252
  * @signature pki.tsp.parseRequest(input) -> timeStampReq
244
253
  * @since 0.2.19
245
- * @status experimental
254
+ * @status stable
246
255
  * @spec RFC 3161
247
256
  * @related pki.tsp.request, pki.schema.tsp.parseRequest
248
257
  *
@@ -251,6 +260,8 @@ function request(messageImprint, opts) {
251
260
  * nonceHex, certReq, extensions }`; a malformed structure throws a typed `TspError`.
252
261
  *
253
262
  * @example
263
+ * var der = pki.tsp.request({ hashAlgorithm: "sha256", hashedMessage: Buffer.from(await pki.webcrypto.subtle.digest("SHA-256", Buffer.from("hello"))) },
264
+ * { certReq: true });
254
265
  * var req = pki.tsp.parseRequest(der);
255
266
  * req.certReq; // -> boolean
256
267
  */
@@ -260,7 +271,7 @@ function parseRequest(input) { return schemaTsp.parseRequest(input); }
260
271
  * @primitive pki.tsp.response
261
272
  * @signature pki.tsp.response(token, opts) -> Buffer|string
262
273
  * @since 0.2.19
263
- * @status experimental
274
+ * @status stable
264
275
  * @spec RFC 3161
265
276
  * @related pki.tsp.parseResponse, pki.tsp.sign
266
277
  *
@@ -275,6 +286,14 @@ function parseRequest(input) { return schemaTsp.parseRequest(input); }
275
286
  * @opts statusString Human-readable PKIFreeText (string or array of strings).
276
287
  * @opts pem Return a PEM "TIMESTAMP RESPONSE" string instead of DER (boolean).
277
288
  * @example
289
+ * var pair = await pki.key.generate("Ed25519");
290
+ * var key = await pki.key.export(pair.privateKey);
291
+ * var cert = await pki.x509.sign({ subject: "Example TSA", subjectPublicKey: await pki.key.export(pair.publicKey),
292
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
293
+ * extensions: { keyUsage: ["digitalSignature"], extendedKeyUsage: ["timeStamping"], extendedKeyUsageCritical: true } },
294
+ * { key: key });
295
+ * var token = await pki.tsp.sign({ hashAlgorithm: "sha256", hashedMessage: Buffer.from(await pki.webcrypto.subtle.digest("SHA-256", Buffer.from("hello"))) },
296
+ * { cert: cert, key: key }, { policy: "1.3.6.1.4.1.1", serialNumber: 1 });
278
297
  * var resp = pki.tsp.response(token, {}); // granted
279
298
  * var rej = pki.tsp.response(null, { status: 2, failInfo: ["badAlg"] }); // rejection
280
299
  */
@@ -308,7 +327,7 @@ function response(token, opts) {
308
327
  * @primitive pki.tsp.parseResponse
309
328
  * @signature pki.tsp.parseResponse(input) -> timeStampResp
310
329
  * @since 0.2.19
311
- * @status experimental
330
+ * @status stable
312
331
  * @spec RFC 3161
313
332
  * @related pki.tsp.response, pki.schema.tsp.parse
314
333
  *
@@ -317,6 +336,15 @@ function response(token, opts) {
317
336
  * status-to-token coupling enforced; a granted response's token is decoded via `parseToken`.
318
337
  *
319
338
  * @example
339
+ * var pair = await pki.key.generate("Ed25519");
340
+ * var key = await pki.key.export(pair.privateKey);
341
+ * var cert = await pki.x509.sign({ subject: "Example TSA", subjectPublicKey: await pki.key.export(pair.publicKey),
342
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
343
+ * extensions: { keyUsage: ["digitalSignature"], extendedKeyUsage: ["timeStamping"], extendedKeyUsageCritical: true } },
344
+ * { key: key });
345
+ * var token = await pki.tsp.sign({ hashAlgorithm: "sha256", hashedMessage: Buffer.from(await pki.webcrypto.subtle.digest("SHA-256", Buffer.from("hello"))) },
346
+ * { cert: cert, key: key }, { policy: "1.3.6.1.4.1.1", serialNumber: 1 });
347
+ * var der = pki.tsp.response(token, {});
320
348
  * var resp = pki.tsp.parseResponse(der);
321
349
  * resp.timeStampToken.tstInfo.genTime; // -> Date (on a granted response)
322
350
  */
@@ -534,7 +562,7 @@ function _buildTsaChains(leaf, pool) {
534
562
  * @primitive pki.tsp.verify
535
563
  * @signature pki.tsp.verify(token, data, opts) -> Promise<result>
536
564
  * @since 0.2.19
537
- * @status experimental
565
+ * @status stable
538
566
  * @spec RFC 3161, RFC 5816
539
567
  * @related pki.tsp.sign, pki.cms.verify, pki.path.validate
540
568
  *
@@ -563,6 +591,13 @@ function _buildTsaChains(leaf, pool) {
563
591
  * token can still verify and chain.
564
592
  * @opts revocationChecker Passed through to `pki.path.validate`.
565
593
  * @example
594
+ * var pair = await pki.key.generate("Ed25519");
595
+ * var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
596
+ * var signerCertDer = await pki.x509.sign({ subject: "Example TSA", subjectPublicKey: await pki.key.export(pair.publicKey),
597
+ * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
598
+ * extensions: { keyUsage: ["digitalSignature"], extendedKeyUsage: ["timeStamping"], extendedKeyUsageCritical: true } },
599
+ * { key: signerKeyPkcs8 });
600
+ * var sha256Digest = Buffer.from(await pki.webcrypto.subtle.digest("SHA-256", Buffer.from("hello")));
566
601
  * var imprint = { hashAlgorithm: "sha256", hashedMessage: sha256Digest };
567
602
  * var token = await pki.tsp.sign(imprint, { cert: signerCertDer, key: signerKeyPkcs8 }, { policy: "1.3.6.1.4.1.1", serialNumber: 1 });
568
603
  * var res = await pki.tsp.verify(token, Buffer.from("hello"), {});
@@ -1,41 +1,41 @@
1
1
  # Vendored dependencies
2
2
 
3
- `@blamejs/pki` ships with **zero npm runtime dependencies**, and it currently
4
- vendors **nothing** this directory holds only the manifest.
3
+ `@blamejs/pki` ships with zero npm runtime dependencies and currently vendors
4
+ nothing. This directory holds only the manifest.
5
5
 
6
6
  ## Native-first crypto
7
7
 
8
8
  The toolkit's cryptography runs entirely on Node's built-in `node:crypto`. The
9
9
  engine floor (Node `>=24.19`) links OpenSSL 3.5, which provides:
10
10
 
11
- - the full classical set RSA (PKCS#1 v1.5, PSS, OAEP), ECDSA, EdDSA
12
- (Ed25519/Ed448), ECDH (incl. X25519/X448), AES (GCM/CBC/CTR/KW), HMAC, HKDF,
13
- PBKDF2, and the SHA-1/2/3 family; and
14
- - the FIPS post-quantum set **ML-KEM** (FIPS 203), **ML-DSA** (FIPS 204), and
11
+ - the full classical set: RSA (PKCS#1 v1.5, PSS, OAEP), ECDSA, EdDSA
12
+ (Ed25519/Ed448), ECDH including X25519 and X448, AES (GCM/CBC/CTR/KW), HMAC,
13
+ HKDF, PBKDF2, and the SHA-1/2/3 family; and
14
+ - the FIPS post-quantum set: **ML-KEM** (FIPS 203), **ML-DSA** (FIPS 204), and
15
15
  **SLH-DSA** (FIPS 205).
16
16
 
17
- A platform built-in ships **zero bytes**, has **nothing to hash-pin or keep
18
- current**, and is **OpenSSL/NSS-interoperable by construction** which is what
19
- the toolkit's interoperability gate needs. So there is no reason to vendor a
20
- crypto library when the runtime already provides the primitive.
17
+ A platform built-in ships zero bytes, has nothing to hash-pin or keep current,
18
+ and is OpenSSL- and NSS-interoperable by construction, which is what the
19
+ toolkit's interoperability gate needs. There is no reason to vendor a crypto
20
+ library when the runtime already provides the primitive.
21
21
 
22
- ## When something IS vendored
22
+ ## When something is vendored
23
23
 
24
- A package is added here **only** when a specific operation is confirmed missing
25
- from the engine floor (for example, a pure-JS fallback for a post-quantum
26
- operation whose JS binding is absent on a supported Node version, or a
27
- cross-check reference used only in tests). When that happens:
24
+ A package is added here only when a specific operation is confirmed missing from
25
+ the engine floor: for example a pure-JS fallback for a post-quantum operation
26
+ whose JS binding is absent on a supported Node version, or a cross-check
27
+ reference used only in tests. When that happens:
28
28
 
29
29
  - `MANIFEST.json` records the package's version, SPDX license, upstream author,
30
30
  source URL, exported surface, bundler invocation, CPE, and pinned SHA-256, so
31
31
  any tampering is detectable and `scripts/check-vendor-currency.js` can gate
32
32
  version drift.
33
33
  - The top-level `NOTICE` gains the component's attribution.
34
- - The reason and the re-open condition are recorded alongside the entry — a
35
- vendored dependency is a deliberate, justified exception to native-first, not
36
- a default.
34
+ - The reason and the re-open condition are recorded alongside the entry. A
35
+ vendored dependency is a deliberate, justified exception to native-first
36
+ rather than a default.
37
37
 
38
38
  Bundles, when present, are produced with
39
39
  `esbuild --format=cjs --minify --platform=node` against the pinned upstream
40
- version; refreshing one recomputes its SHA-256 and updates the bytes and the
40
+ version. Refreshing one recomputes its SHA-256 and updates the bytes and the
41
41
  matching `hashes.server` entry in the same change.