@blamejs/pki 0.4.15 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -1
- package/MIGRATING.md +2 -2
- package/README.md +142 -137
- package/index.js +4 -0
- package/lib/acme.js +73 -1
- package/lib/asn1-der.js +2 -0
- package/lib/attrcert-sign.js +4 -0
- package/lib/cbor-det.js +32 -16
- package/lib/cmc-build.js +880 -0
- package/lib/cmc-verify.js +657 -0
- package/lib/cmp-build.js +8 -7
- package/lib/cmp-verify.js +11 -1
- package/lib/cms-sign.js +170 -8
- package/lib/cms-verify.js +80 -14
- package/lib/crl-sign.js +22 -0
- package/lib/crmf-sign.js +5 -2
- package/lib/csr-sign.js +3 -0
- package/lib/ct.js +72 -0
- package/lib/est.js +828 -32
- package/lib/framework-error.js +13 -0
- package/lib/guard-bytes.js +37 -1
- package/lib/guard-range.js +23 -1
- package/lib/http-transport.js +9 -3
- package/lib/inspect.js +28 -5
- package/lib/jose.js +64 -6
- package/lib/lint.js +4 -0
- package/lib/merkle.js +5 -5
- package/lib/ocsp.js +139 -11
- package/lib/oid.js +69 -1
- package/lib/path-validate.js +27 -4
- package/lib/pkcs12-build.js +12 -0
- package/lib/schema-all.js +19 -1
- package/lib/schema-attrcert.js +27 -0
- package/lib/schema-c509.js +6 -0
- package/lib/schema-cmc.js +791 -0
- package/lib/schema-cmp.js +25 -0
- package/lib/schema-cms.js +17 -1
- package/lib/schema-crl.js +23 -1
- package/lib/schema-crmf.js +13 -0
- package/lib/schema-csr.js +11 -0
- package/lib/schema-csrattrs.js +6 -0
- package/lib/schema-engine.js +6 -2
- package/lib/schema-ocsp.js +41 -0
- package/lib/schema-pkcs12.js +16 -0
- package/lib/schema-pkcs8.js +8 -0
- package/lib/schema-smime.js +4 -4
- package/lib/schema-tsp.js +32 -1
- package/lib/schema-x509.js +14 -1
- package/lib/shbs.js +12 -4
- package/lib/sigstore.js +62 -6
- package/lib/smime.js +28 -7
- package/lib/tls-cert-compress.js +15 -3
- package/lib/trust.js +27 -4
- package/lib/tsp-sign.js +41 -6
- package/lib/vendor/README.md +19 -19
- package/lib/webauthn.js +895 -26
- package/lib/webcrypto.js +35 -2
- package/lib/x509-sign.js +3 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/sigstore.js
CHANGED
|
@@ -117,6 +117,8 @@ function _b64(s, label) {
|
|
|
117
117
|
* bundle object (structure only -- no cryptographic verification).
|
|
118
118
|
*
|
|
119
119
|
* @example
|
|
120
|
+
* // requires: `bundle` -- a Sigstore bundle as cosign or npm provenance emits it
|
|
121
|
+
* // (the JSON object, a JSON string, or its raw bytes)
|
|
120
122
|
* var b = pki.sigstore.parseBundle(bundle);
|
|
121
123
|
* b.mediaType; // "application/vnd.dev.sigstore.bundle.v0.3+json"
|
|
122
124
|
*/
|
|
@@ -514,14 +516,54 @@ function _fulcioExtValue(ext, leafArc) {
|
|
|
514
516
|
return asn1.read.string(asn1.decode(ext.value));
|
|
515
517
|
}
|
|
516
518
|
|
|
519
|
+
// The fields an identity policy may constrain. Named once so the check, the refusal of an
|
|
520
|
+
// unknown key, and the report of what ran cannot drift apart.
|
|
521
|
+
var IDENTITY_FIELDS = ["san", "issuer", "sourceRepositoryURI"];
|
|
522
|
+
// The guard tests membership with hasOwnProperty, so the permitted set is a lookup object --
|
|
523
|
+
// an array would treat "0"/"1" as the known keys and reject every real field name.
|
|
524
|
+
var IDENTITY_KEYS = { san: 1, issuer: 1, sourceRepositoryURI: 1 };
|
|
525
|
+
|
|
526
|
+
// Returns WHICH fields were actually compared. A bundle verifies its own signature and log
|
|
527
|
+
// inclusion whoever signed it -- Fulcio issues to anyone who completes an OIDC flow -- so
|
|
528
|
+
// `verified: true` without an identity policy says the artifact was signed and logged, not that
|
|
529
|
+
// a trusted party signed it. The caller cannot tell those apart from a bare boolean.
|
|
517
530
|
function _checkIdentity(id, policy) {
|
|
518
|
-
|
|
531
|
+
var ran = { san: false, issuer: false, sourceRepositoryURI: false };
|
|
532
|
+
if (policy === undefined || policy === null) return ran;
|
|
533
|
+
if (typeof policy !== "object" || Array.isArray(policy)) {
|
|
534
|
+
throw _err("sigstore/bad-input", "opts.identity must be an object naming the identity fields to pin (" + IDENTITY_FIELDS.join(", ") + ")");
|
|
535
|
+
}
|
|
536
|
+
// An unknown key is refused, not ignored: cosign spells this `certificateIdentity`, and a
|
|
537
|
+
// swallowed spelling checks nothing under a name the operator believes pins the signer.
|
|
538
|
+
// The guard rejects through a (code, message) FACTORY. Handing it the error CLASS raises
|
|
539
|
+
// "class constructor cannot be invoked without new" -- a raw, untyped throw escaping a public
|
|
540
|
+
// verb, on the branch a valid-input test never takes.
|
|
541
|
+
guard.identifier.assertKnownKeys(policy, IDENTITY_KEYS, _err, "sigstore/bad-input", "opts.identity has an unknown key ");
|
|
542
|
+
// A policy that constrains NOTHING is a configuration mistake, and the most dangerous input on
|
|
543
|
+
// this surface: every guard below is falsy, so it accepts every signer while reading as though
|
|
544
|
+
// an identity policy is in force. Refused at the boundary rather than answered.
|
|
545
|
+
var asked = IDENTITY_FIELDS.filter(function (f) { return policy[f] !== undefined; });
|
|
546
|
+
if (!asked.length) {
|
|
547
|
+
throw _err("sigstore/bad-input", "opts.identity constrains nothing -- name at least one of " + IDENTITY_FIELDS.join(", ") + ", or omit it to state that the signer is not being checked");
|
|
548
|
+
}
|
|
549
|
+
// A named field must carry a value that can actually be compared. The comparisons below are
|
|
550
|
+
// truthiness-guarded, so an empty string or a null would be skipped while the field had been
|
|
551
|
+
// named -- reporting a signer check that never ran, which is the exact confusion this report
|
|
552
|
+
// exists to remove. Deciding "asked" and deciding "compared" must be the SAME test, so a value
|
|
553
|
+
// that cannot be compared is refused here rather than quietly becoming "not asked".
|
|
554
|
+
asked.forEach(function (f) {
|
|
555
|
+
if (typeof policy[f] !== "string" || policy[f] === "") {
|
|
556
|
+
throw _err("sigstore/bad-input", "opts.identity." + f + " must be a non-empty string -- a value that cannot be compared would leave the signer unchecked under a policy that names it");
|
|
557
|
+
}
|
|
558
|
+
ran[f] = true;
|
|
559
|
+
});
|
|
519
560
|
var sanValue = id.san && id.san.value;
|
|
520
561
|
if (policy.san && sanValue !== policy.san) throw _err("sigstore/identity-mismatch", "the certificate SAN " + JSON.stringify(sanValue) + " does not match the expected identity");
|
|
521
562
|
// The OIDC issuer is carried by the current Issuer V2 (.1.8) or, on older certs,
|
|
522
563
|
// only by the deprecated raw-string issuer (.1.1); match against either.
|
|
523
564
|
if (policy.issuer && policy.issuer !== id.extensions.issuer && policy.issuer !== id.extensions.issuerLegacy) throw _err("sigstore/identity-mismatch", "the certificate OIDC issuer does not match the expected issuer");
|
|
524
565
|
if (policy.sourceRepositoryURI && id.extensions.sourceRepositoryURI !== policy.sourceRepositoryURI) throw _err("sigstore/identity-mismatch", "the certificate source-repository URI does not match");
|
|
566
|
+
return ran;
|
|
525
567
|
}
|
|
526
568
|
|
|
527
569
|
// ---- in-toto Statement leg ---------------------------------------------------
|
|
@@ -559,18 +601,29 @@ function _statement(payload, payloadType, expectedPredicate) {
|
|
|
559
601
|
* the log entry bound to this exact signature; and the in-toto SLSA statement.
|
|
560
602
|
* Any leg failing throws a typed `sigstore/*` error. On success returns
|
|
561
603
|
* `{ verified: true, payload, statement, subjects, predicateType, predicate,
|
|
562
|
-
* identity, integratedTime }` -- `payload` is the RAW verified
|
|
563
|
-
* (never a re-serialization), and the caller confirms a
|
|
564
|
-
* matches the published artifact.
|
|
604
|
+
* identity, identityChecked, integratedTime }` -- `payload` is the RAW verified
|
|
605
|
+
* envelope bytes (never a re-serialization), and the caller confirms a
|
|
606
|
+
* `subjects[].digest` matches the published artifact.
|
|
607
|
+
*
|
|
608
|
+
* `verified: true` says the artifact was signed and logged -- not that a party you
|
|
609
|
+
* trust signed it. Fulcio issues a certificate to anyone who completes an OIDC
|
|
610
|
+
* flow, so WHO signed is decided only by `opts.identity`, and `identityChecked`
|
|
611
|
+
* reports which of its fields were compared (`{ san, issuer, sourceRepositoryURI }`,
|
|
612
|
+
* each a boolean). An `identity` naming none of them is refused rather than
|
|
613
|
+
* satisfied, since it would accept every signer while reading as a policy; so is an
|
|
614
|
+
* unrecognized field name, which would otherwise pin nothing under a spelling the
|
|
615
|
+
* operator believes constrains the signer.
|
|
565
616
|
*
|
|
566
617
|
* @opts
|
|
567
618
|
* fulcioRoots: Array, // the Fulcio CA anchors: a DER Buffer or { der, validFor } each
|
|
568
619
|
* rekorKeys: Array, // [{ keyId, spki, validFor? }] the Rekor log public keys
|
|
569
|
-
* identity: object, // optional policy: { san, issuer, sourceRepositoryURI }
|
|
620
|
+
* identity: object, // optional policy: { san, issuer, sourceRepositoryURI }; at least one required when present
|
|
570
621
|
* predicateType: string, // optional: require this in-toto predicateType (e.g. the SLSA URI)
|
|
571
622
|
* time: Date, // optional check-date override (default: the Rekor integratedTime)
|
|
572
623
|
*
|
|
573
624
|
* @example
|
|
625
|
+
* // requires: `bundle` from cosign / npm provenance, and `sigstoreTrust` built from
|
|
626
|
+
* // the public-good trusted_root.json (the Fulcio + Rekor material it pins)
|
|
574
627
|
* var out = await pki.sigstore.verifyBundle(bundle, sigstoreTrust);
|
|
575
628
|
* out.verified; // true
|
|
576
629
|
* out.subjects[0].digest; // { sha512: "..." } -- confirm against your tarball
|
|
@@ -612,7 +665,7 @@ async function verifyBundle(bundle, opts) {
|
|
|
612
665
|
var checkTime = (opts.time instanceof Date) ? opts.time.getTime() : C.TIME.seconds(integratedTime);
|
|
613
666
|
await _verifyChain(leaf, _chainDers(vm), fulcioRoots, checkTime);
|
|
614
667
|
var identity = _identity(leaf);
|
|
615
|
-
_checkIdentity(identity, opts.identity);
|
|
668
|
+
var identityChecked = _checkIdentity(identity, opts.identity);
|
|
616
669
|
|
|
617
670
|
// Leg 4 -- the in-toto SLSA statement + subject binding (with an optional
|
|
618
671
|
// caller-pinned predicateType).
|
|
@@ -626,6 +679,9 @@ async function verifyBundle(bundle, opts) {
|
|
|
626
679
|
predicateType: st.predicateType,
|
|
627
680
|
predicate: st.predicate,
|
|
628
681
|
identity: identity,
|
|
682
|
+
// Which identity fields were actually compared. `verified` says the artifact was signed and
|
|
683
|
+
// logged; only these say a signer the caller named was the one who signed it.
|
|
684
|
+
identityChecked: identityChecked,
|
|
629
685
|
integratedTime: integratedTime,
|
|
630
686
|
};
|
|
631
687
|
}
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
*/
|
package/lib/tls-cert-compress.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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)],
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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"), {});
|
package/lib/vendor/README.md
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
# Vendored dependencies
|
|
2
2
|
|
|
3
|
-
`@blamejs/pki` ships with
|
|
4
|
-
|
|
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
|
|
12
|
-
(Ed25519/Ed448), ECDH
|
|
13
|
-
PBKDF2, and the SHA-1/2/3 family; and
|
|
14
|
-
- the FIPS post-quantum set
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
22
|
+
## When something is vendored
|
|
23
23
|
|
|
24
|
-
A package is added here
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
35
|
-
vendored dependency is a deliberate, justified exception to native-first
|
|
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
|
|
40
|
+
version. Refreshing one recomputes its SHA-256 and updates the bytes and the
|
|
41
41
|
matching `hashes.server` entry in the same change.
|