@blamejs/pki 0.8.30 → 0.8.48

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 (87) hide show
  1. package/CHANGELOG.md +411 -0
  2. package/README.md +67 -16
  3. package/bin/pki.js +716 -30
  4. package/examples/issue-a-certificate.js +90 -0
  5. package/examples/post-quantum.js +66 -0
  6. package/examples/read-a-certificate.js +56 -0
  7. package/examples/revoke-and-check.js +86 -0
  8. package/examples/sign-and-verify-cms.js +63 -0
  9. package/index.js +18 -5
  10. package/lib/acme.js +223 -74
  11. package/lib/alt-sig.js +441 -0
  12. package/lib/asn1-der.js +48 -25
  13. package/lib/attrcert-sign.js +27 -19
  14. package/lib/cbor-det.js +24 -14
  15. package/lib/cmc-build.js +9 -2
  16. package/lib/cmc-verify.js +14 -3
  17. package/lib/cmp-build.js +17 -5
  18. package/lib/cmp-session.js +24 -11
  19. package/lib/cmp-verify.js +28 -19
  20. package/lib/cms-compress.js +3 -1
  21. package/lib/cms-decrypt.js +102 -7
  22. package/lib/cms-encrypt.js +59 -7
  23. package/lib/cms-sign.js +31 -9
  24. package/lib/cms-verify.js +22 -18
  25. package/lib/composite-kem.js +30 -18
  26. package/lib/composite-sig.js +29 -14
  27. package/lib/constants.js +28 -0
  28. package/lib/crl-sign.js +111 -19
  29. package/lib/crmf-sign.js +167 -15
  30. package/lib/csr-sign.js +117 -6
  31. package/lib/ct.js +939 -48
  32. package/lib/est.js +29 -16
  33. package/lib/framework-error.js +12 -0
  34. package/lib/guard-async.js +1 -1
  35. package/lib/guard-bytes.js +103 -20
  36. package/lib/guard-crypto.js +205 -3
  37. package/lib/guard-encoding.js +6 -1
  38. package/lib/guard-identifier.js +55 -8
  39. package/lib/guard-intrinsic.js +43 -0
  40. package/lib/guard-json.js +122 -9
  41. package/lib/guard-limits.js +12 -1
  42. package/lib/guard-list.js +63 -8
  43. package/lib/guard-name.js +167 -20
  44. package/lib/guard-range.js +43 -5
  45. package/lib/guard-text.js +41 -0
  46. package/lib/guard-verdict.js +39 -8
  47. package/lib/hpke.js +688 -42
  48. package/lib/http-transport.js +156 -4
  49. package/lib/identity-match.js +4 -4
  50. package/lib/inspect.js +448 -2
  51. package/lib/jose.js +93 -25
  52. package/lib/key.js +18 -13
  53. package/lib/lint.js +165 -1
  54. package/lib/merkle.js +233 -27
  55. package/lib/ocsp.js +27 -14
  56. package/lib/oid.js +14 -4
  57. package/lib/path-validate.js +143 -61
  58. package/lib/pkcs12-build.js +7 -2
  59. package/lib/pki-build.js +31 -7
  60. package/lib/possession.js +575 -0
  61. package/lib/related-cert.js +708 -0
  62. package/lib/revocation-fetch.js +6 -6
  63. package/lib/scep.js +15 -5
  64. package/lib/schema-c509.js +5 -2
  65. package/lib/schema-cmc.js +1 -1
  66. package/lib/schema-cmp.js +8 -8
  67. package/lib/schema-cms.js +2 -15
  68. package/lib/schema-csr.js +69 -0
  69. package/lib/schema-engine.js +10 -3
  70. package/lib/schema-pkix.js +267 -4
  71. package/lib/schema-tsp.js +7 -6
  72. package/lib/sign-scheme.js +22 -9
  73. package/lib/sigstore.js +684 -168
  74. package/lib/sleep.js +8 -1
  75. package/lib/smime.js +22 -1
  76. package/lib/tlog.js +1235 -0
  77. package/lib/tls-cert-compress.js +25 -8
  78. package/lib/tsp-sign.js +120 -35
  79. package/lib/tuf.js +1395 -0
  80. package/lib/validator-tpm.js +4 -1
  81. package/lib/verify-seam.js +7 -1
  82. package/lib/webauthn-mds.js +41 -4
  83. package/lib/webauthn.js +43 -25
  84. package/lib/webcrypto.js +48 -46
  85. package/lib/x509-sign.js +225 -19
  86. package/package.json +2 -1
  87. package/sbom.cdx.json +6 -6
@@ -0,0 +1,90 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * Issue a certificate from your own CA, then validate the path.
6
+ *
7
+ * node examples/issue-a-certificate.js
8
+ *
9
+ * Generates a CA key and a self-signed CA certificate, generates a leaf key, issues a leaf
10
+ * certificate from the CA, and validates leaf-against-CA through RFC 5280 path validation.
11
+ * Nothing is written to disk and no network is used.
12
+ */
13
+
14
+ var pki = require("@blamejs/pki");
15
+
16
+ // A subject is a distinguished name, and `pki.x509.parseDn(s).bytes` is how a DN string becomes one
17
+ // the builders take. Handing them the string directly is not an error and not the same thing: a bare
18
+ // string is read as a COMMON NAME, so "CN=Example CA, O=Example" would certify one commonName whose
19
+ // value is that whole text.
20
+ function dn(s) { return pki.x509.parseDn(s).bytes; }
21
+
22
+ async function main() {
23
+ // A CA key and the self-signed certificate that names it. basicConstraints cA marks it as a CA
24
+ // and keyCertSign is what lets it sign other certificates; a CA missing either is refused as an
25
+ // issuer during path validation.
26
+ var caKeys = await pki.key.generate("ML-DSA-65");
27
+ var caSpki = await pki.key.export(caKeys.publicKey);
28
+ var caKey = await pki.key.export(caKeys.privateKey);
29
+ var now = new Date();
30
+ var caDer = await pki.x509.sign({
31
+ subject: dn("CN=Example CA, O=Example"),
32
+ subjectPublicKey: caSpki,
33
+ notBefore: now,
34
+ notAfter: new Date(now.getTime() + pki.C.TIME.days(3650)),
35
+ extensions: {
36
+ basicConstraints: { cA: true },
37
+ keyUsage: ["keyCertSign", "cRLSign"],
38
+ subjectKeyIdentifier: true,
39
+ },
40
+ }, { key: caKey });
41
+ console.log("ca subject: " + pki.schema.x509.parse(caDer).subject.dn);
42
+
43
+ // The leaf. Its key is its own; the CA only signs over its public half.
44
+ var leafKeys = await pki.key.generate("ML-DSA-65");
45
+ var leafDer = await pki.x509.sign({
46
+ subject: dn("CN=host.example"),
47
+ subjectPublicKey: await pki.key.export(leafKeys.publicKey),
48
+ notBefore: now,
49
+ notAfter: new Date(now.getTime() + pki.C.TIME.days(90)),
50
+ extensions: {
51
+ subjectAltName: ["host.example", "www.host.example"],
52
+ keyUsage: ["digitalSignature"],
53
+ extendedKeyUsage: ["serverAuth"],
54
+ },
55
+ }, { key: caKey, cert: caDer });
56
+ var leaf = pki.schema.x509.parse(leafDer);
57
+ console.log("leaf subject: " + leaf.subject.dn);
58
+ console.log("leaf issuer: " + leaf.issuer.dn);
59
+ console.log("leaf serial: " + leaf.serialNumberHex.length / 2 + " bytes of entropy");
60
+
61
+ // Path validation is the verb that decides whether the leaf is trusted, and it is separate from
62
+ // having issued it: the anchor is supplied as a name and a key, never as bytes to be trusted.
63
+ var ca = pki.schema.x509.parse(caDer);
64
+ var result = await pki.path.validate([leafDer], {
65
+ time: now,
66
+ trustAnchors: {
67
+ name: ca.subject,
68
+ publicKey: ca.subjectPublicKeyInfo.bytes,
69
+ algorithm: ca.subjectPublicKeyInfo.algorithm.oid,
70
+ },
71
+ });
72
+ console.log("path valid: " + result.valid);
73
+
74
+ // The same validation one day after the leaf expires. A certificate outside its validity window
75
+ // is not trusted, which is the check an expiry outage is.
76
+ var expired = await pki.path.validate([leafDer], {
77
+ time: new Date(now.getTime() + pki.C.TIME.days(91)),
78
+ trustAnchors: {
79
+ name: ca.subject,
80
+ publicKey: ca.subjectPublicKeyInfo.bytes,
81
+ algorithm: ca.subjectPublicKeyInfo.algorithm.oid,
82
+ },
83
+ });
84
+ console.log("after expiry: " + expired.valid);
85
+ }
86
+
87
+ main().then(null, function (e) {
88
+ console.error(e && (e.code ? e.code + ": " + e.message : e.message));
89
+ process.exitCode = 1;
90
+ });
@@ -0,0 +1,66 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * Post-quantum signatures and key establishment, through the same verbs as everything else.
6
+ *
7
+ * node examples/post-quantum.js
8
+ *
9
+ * ML-DSA (FIPS 204) and SLH-DSA (FIPS 205) sign certificates here the way ECDSA and Ed25519 do:
10
+ * an algorithm is a registry entry, so nothing about issuing, verifying or validating a path
11
+ * changes with the suite. ML-KEM (FIPS 203) does key establishment. No network.
12
+ */
13
+
14
+ var pki = require("@blamejs/pki");
15
+
16
+ async function certificateFor(alg) {
17
+ var keys = await pki.key.generate(alg);
18
+ var now = new Date();
19
+ var der = await pki.x509.sign({
20
+ subject: pki.x509.parseDn("CN=" + alg.toLowerCase() + ".example").bytes,
21
+ subjectPublicKey: await pki.key.export(keys.publicKey),
22
+ notBefore: now,
23
+ notAfter: new Date(now.getTime() + pki.C.TIME.days(365)),
24
+ extensions: { keyUsage: ["digitalSignature"] },
25
+ }, { key: await pki.key.export(keys.privateKey) });
26
+ return { der: der, cert: pki.schema.x509.parse(der) };
27
+ }
28
+
29
+ async function main() {
30
+ // The same call shape across three signature families. The name the certificate carries comes
31
+ // from the OID registry, so a reader sees the algorithm rather than a dotted string.
32
+ for (var alg of ["ML-DSA-65", "SLH-DSA-SHA2-128s", "Ed25519"]) {
33
+ var made = await certificateFor(alg);
34
+ console.log(alg.padEnd(18) + " certificate " + String(made.der.length).padStart(6) +
35
+ " bytes, signed with " + made.cert.signatureAlgorithm.name);
36
+ }
37
+
38
+ // A post-quantum certificate validates through the same path validator, with no suite-specific
39
+ // argument: the anchor names its own key algorithm and the verifier resolves the rest.
40
+ var ca = await certificateFor("ML-DSA-65");
41
+ var result = await pki.path.validate([ca.der], {
42
+ time: new Date(),
43
+ trustAnchors: {
44
+ name: ca.cert.subject,
45
+ publicKey: ca.cert.subjectPublicKeyInfo.bytes,
46
+ algorithm: ca.cert.subjectPublicKeyInfo.algorithm.oid,
47
+ },
48
+ });
49
+ console.log("path validates with a post-quantum anchor: " + result.valid);
50
+
51
+ // ML-KEM is key establishment rather than signing: the sender encapsulates to the recipient's
52
+ // public key and both sides end up with the same secret, which neither transmitted. This is the
53
+ // pair the CMS KEMRecipientInfo arm rides on.
54
+ var kemAlg = { name: "ML-KEM-768" };
55
+ var kem = await pki.key.generate("ML-KEM-768");
56
+ var sent = await pki.webcrypto.subtle.encapsulateBits(kemAlg, kem.publicKey);
57
+ var received = await pki.webcrypto.subtle.decapsulateBits(kemAlg, kem.privateKey, sent.ciphertext);
58
+ var agreed = Buffer.compare(Buffer.from(sent.sharedKey), Buffer.from(received)) === 0;
59
+ console.log("ml-kem-768 ciphertext " + sent.ciphertext.byteLength + " bytes, shared secret " +
60
+ sent.sharedKey.byteLength + " bytes, both sides agree: " + agreed);
61
+ }
62
+
63
+ main().then(null, function (e) {
64
+ console.error(e && (e.code ? e.code + ": " + e.message : e.message));
65
+ process.exitCode = 1;
66
+ });
@@ -0,0 +1,56 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * Read a certificate: parse it, render it, lint it, dump its bytes.
6
+ *
7
+ * node examples/read-a-certificate.js
8
+ *
9
+ * The four ways this toolkit reads one certificate, from the structured fields a program uses to
10
+ * the byte offsets a debugger needs. No network, nothing written to disk.
11
+ */
12
+
13
+ var pki = require("@blamejs/pki");
14
+
15
+ async function main() {
16
+ // A certificate to read. Any DER or PEM certificate works here; this one is generated so the
17
+ // example needs no fixture.
18
+ var keys = await pki.key.generate("Ed25519");
19
+ var now = new Date();
20
+ var der = await pki.x509.sign({
21
+ subject: pki.x509.parseDn("CN=host.example, O=Example").bytes,
22
+ subjectPublicKey: await pki.key.export(keys.publicKey),
23
+ notBefore: now,
24
+ notAfter: new Date(now.getTime() + pki.C.TIME.days(90)),
25
+ extensions: { subjectAltName: ["host.example"], keyUsage: ["digitalSignature"] },
26
+ }, { key: await pki.key.export(keys.privateKey) });
27
+
28
+ // 1. parse -- the structured fields, validated. This is what a program reads.
29
+ var cert = pki.schema.x509.parse(der);
30
+ console.log("subject: " + cert.subject.dn);
31
+ console.log("algorithm: " + cert.signatureAlgorithm.name);
32
+ console.log("extensions: " + cert.extensions.map(function (e) { return e.name || e.oid; }).join(", "));
33
+
34
+ // 2. inspect -- the report a person reads, the pure-JS equivalent of `openssl x509 -text`.
35
+ // pki.inspect.any detects the format, so the same call reads a CRL or a CMS message.
36
+ var report = pki.inspect.any(der);
37
+ console.log("report lines: " + report.split("\n").length);
38
+
39
+ // 3. lint -- graded conformance findings. The data path never throws: malformed bytes become a
40
+ // fatal finding rather than an exception, so a survey over a corpus cannot be stopped by one
41
+ // bad file. `worst` is the highest severity present, and "pass" means nothing was found.
42
+ var lint = pki.lint.certificate(der);
43
+ console.log("lint worst: " + (lint.worst || "pass"));
44
+ console.log("lint findings: " + lint.findings.length);
45
+
46
+ // 4. asn1 -- the structural dump, in the shape `openssl asn1parse` prints. This one renders every
47
+ // value in the file, so it is the verb to reach for on a file that is wrong rather than one
48
+ // holding a key.
49
+ var dump = pki.inspect.asn1(der);
50
+ console.log("first node: " + dump.split("\n")[1].trim());
51
+ }
52
+
53
+ main().then(null, function (e) {
54
+ console.error(e && (e.code ? e.code + ": " + e.message : e.message));
55
+ process.exitCode = 1;
56
+ });
@@ -0,0 +1,86 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * Revoke a certificate and see a path stop trusting it (CRL, RFC 5280 section 5).
6
+ *
7
+ * node examples/revoke-and-check.js
8
+ *
9
+ * Issues a leaf, validates it, publishes a CRL that revokes it, and validates again with that
10
+ * CRL supplied. The second answer is the one revocation exists to produce. No network.
11
+ */
12
+
13
+ var pki = require("@blamejs/pki");
14
+
15
+ function dn(s) { return pki.x509.parseDn(s).bytes; }
16
+
17
+ async function main() {
18
+ var now = new Date();
19
+ var caKeys = await pki.key.generate("Ed25519");
20
+ var caKey = await pki.key.export(caKeys.privateKey);
21
+ var caDer = await pki.x509.sign({
22
+ subject: dn("CN=Revocation CA"),
23
+ subjectPublicKey: await pki.key.export(caKeys.publicKey),
24
+ notBefore: now,
25
+ notAfter: new Date(now.getTime() + pki.C.TIME.days(3650)),
26
+ extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"], subjectKeyIdentifier: true },
27
+ }, { key: caKey });
28
+ var ca = pki.schema.x509.parse(caDer);
29
+
30
+ var leafKeys = await pki.key.generate("Ed25519");
31
+ var leafDer = await pki.x509.sign({
32
+ subject: dn("CN=doomed.example"),
33
+ subjectPublicKey: await pki.key.export(leafKeys.publicKey),
34
+ notBefore: now,
35
+ notAfter: new Date(now.getTime() + pki.C.TIME.days(90)),
36
+ extensions: { keyUsage: ["digitalSignature"] },
37
+ }, { key: caKey, cert: caDer });
38
+ var leaf = pki.schema.x509.parse(leafDer);
39
+ console.log("leaf serial: " + leaf.serialNumberHex.slice(0, 16) + "...");
40
+
41
+ var anchor = {
42
+ name: ca.subject,
43
+ publicKey: ca.subjectPublicKeyInfo.bytes,
44
+ algorithm: ca.subjectPublicKeyInfo.algorithm.oid,
45
+ };
46
+ var before = await pki.path.validate([leafDer], { time: now, trustAnchors: anchor });
47
+ console.log("before crl: " + before.valid);
48
+
49
+ // The CA publishes a CRL naming that serial. nextUpdate is required by RFC 5280 section 5.1.2.5
50
+ // and is what tells a relying party how long the list may be believed.
51
+ // The issuer argument names the CA through its certificate, so the spec does not repeat it: the
52
+ // name a CRL is signed under is the one in that certificate.
53
+ var crlDer = await pki.crl.sign({
54
+ thisUpdate: now,
55
+ nextUpdate: new Date(now.getTime() + pki.C.TIME.days(7)),
56
+ crlNumber: 1n,
57
+ revoked: [{
58
+ serialNumber: BigInt("0x" + leaf.serialNumberHex),
59
+ revocationDate: now,
60
+ reason: "keyCompromise",
61
+ }],
62
+ }, { key: caKey, cert: caDer });
63
+ var crl = pki.schema.crl.parse(crlDer);
64
+ console.log("crl entries: " + crl.revokedCertificates.length +
65
+ ", next update in " + Math.round((crl.nextUpdate - crl.thisUpdate) / pki.C.TIME.days(1)) + " days");
66
+
67
+ // The same path, with the CRL supplied. Revocation is a pluggable hook rather than a built-in
68
+ // fetch: pki.path.crlChecker answers from the CRLs you hand it, and a validator given none
69
+ // cannot know a certificate was revoked. Supplying it is what changes the answer.
70
+ var after = await pki.path.validate([leafDer], {
71
+ time: now,
72
+ trustAnchors: anchor,
73
+ revocationChecker: pki.path.crlChecker([crlDer]),
74
+ });
75
+ console.log("after crl: " + after.valid);
76
+ var reasons = [];
77
+ (after.results || []).forEach(function (r) {
78
+ (r.checks || []).forEach(function (c) { if (c.ok === false) reasons.push(c.code); });
79
+ });
80
+ console.log("refused for: " + (reasons.join(", ") || "(nothing)"));
81
+ }
82
+
83
+ main().then(null, function (e) {
84
+ console.error(e && (e.code ? e.code + ": " + e.message : e.message));
85
+ process.exitCode = 1;
86
+ });
@@ -0,0 +1,63 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * Sign a document and verify the signature (CMS SignedData, RFC 5652).
6
+ *
7
+ * node examples/sign-and-verify-cms.js
8
+ *
9
+ * Signs bytes into a SignedData, verifies it, and shows what changes when the content is
10
+ * detached and when the signature is over content that has since been altered. No network,
11
+ * nothing written to disk.
12
+ */
13
+
14
+ var pki = require("@blamejs/pki");
15
+
16
+ async function main() {
17
+ // A signer needs a key and a certificate naming it: a signature that no certificate binds to an
18
+ // identity proves only that someone held a key.
19
+ var keys = await pki.key.generate("ML-DSA-65");
20
+ var key = await pki.key.export(keys.privateKey);
21
+ var now = new Date();
22
+ var cert = await pki.x509.sign({
23
+ subject: pki.x509.parseDn("CN=signer.example").bytes,
24
+ subjectPublicKey: await pki.key.export(keys.publicKey),
25
+ notBefore: now,
26
+ notAfter: new Date(now.getTime() + pki.C.TIME.days(365)),
27
+ extensions: { keyUsage: ["digitalSignature"] },
28
+ }, { key: key });
29
+
30
+ var content = Buffer.from("the bytes under signature", "utf8");
31
+ var signed = await pki.cms.sign(content, { cert: cert, key: key });
32
+ console.log("signeddata: " + signed.length + " bytes");
33
+
34
+ // Verifying against the signer certificate. `verify` answers about the signature; whether that
35
+ // certificate is trusted is what pki.path.validate answers, and they are separate questions.
36
+ var ok = await pki.cms.verify(signed, { certs: [cert] });
37
+ console.log("verified: " + ok.valid);
38
+ console.log("signers: " + ok.signers.length);
39
+
40
+ // Detached: the signature travels without the content, so the verifier is given the content it
41
+ // already has. This is how a signature over a large artifact is shipped.
42
+ var detached = await pki.cms.sign(content, { cert: cert, key: key }, { detached: true });
43
+ var okDetached = await pki.cms.verify(detached, { certs: [cert], content: content });
44
+ console.log("detached: " + detached.length + " bytes, verified " + okDetached.valid);
45
+
46
+ // The same detached signature against content that changed by one byte. A signature is over
47
+ // bytes, so this is the answer that makes it worth having.
48
+ var altered = Buffer.from(content);
49
+ altered[0] ^= 0x01;
50
+ var verdict;
51
+ try {
52
+ var bad = await pki.cms.verify(detached, { certs: [cert], content: altered });
53
+ verdict = bad.valid ? "accepted (this would be a defect)" : "rejected";
54
+ } catch (e) {
55
+ verdict = "rejected (" + (e.code || "threw") + ")";
56
+ }
57
+ console.log("altered: " + verdict);
58
+ }
59
+
60
+ main().then(null, function (e) {
61
+ console.error(e && (e.code ? e.code + ": " + e.message : e.message));
62
+ process.exitCode = 1;
63
+ });
package/index.js CHANGED
@@ -37,6 +37,10 @@ var schema = require("./lib/schema-all");
37
37
  var path = require("./lib/path-validate");
38
38
  var identity = require("./lib/identity-match");
39
39
  var ct = require("./lib/ct");
40
+ var tuf = require("./lib/tuf");
41
+ var relatedCert = require("./lib/related-cert");
42
+ var altSig = require("./lib/alt-sig");
43
+ var possession = require("./lib/possession");
40
44
  var tls = require("./lib/tls-cert-compress");
41
45
  var cms = require("./lib/cms-verify");
42
46
  var smime = require("./lib/smime");
@@ -53,6 +57,7 @@ var key = require("./lib/key");
53
57
  var pkcs12 = require("./lib/pkcs12-build");
54
58
  var pkcs11 = require("./lib/pkcs11-uri");
55
59
  var merkle = require("./lib/merkle");
60
+ var tlog = require("./lib/tlog");
56
61
  var shbs = require("./lib/shbs");
57
62
  var hpke = require("./lib/hpke");
58
63
  var compositeKem = require("./lib/composite-kem");
@@ -97,6 +102,10 @@ module.exports = {
97
102
  // SCT-list extension a certificate / OCSP response carries; the signature is
98
103
  // surfaced raw for external verification (pki.ct.reconstructSignedData).
99
104
  ct: ct,
105
+ tuf: tuf,
106
+ relatedCert: relatedCert,
107
+ altSig: altSig,
108
+ possession: possession,
100
109
  // 'tls' is TLS handshake structures that carry certificates -- RFC 8879 compressed
101
110
  // certificate messages (zlib / brotli / zstd) and the RFC 8446 Certificate message
102
111
  // inside them, decoded to per-entry certificate DER. Structure only; no handshake.
@@ -166,15 +175,19 @@ module.exports = {
166
175
  // verifyConsistency fold an audit / consistency proof and constant-time-
167
176
  // compare to a checkpoint root. Pure sync hashing, fail-closed, transport-free.
168
177
  merkle: merkle,
178
+ // `tlog` is the C2SP transparency-log envelope over those hashes: a signed
179
+ // note, the checkpoint body a log signs into one, and the tile paths a static
180
+ // log serves them beside. Verifying only; nothing signs and nothing fetches.
181
+ tlog: tlog,
169
182
  // `shbs` verifies stateful hash-based signatures -- HSS/LMS (RFC 8554),
170
183
  // carried by RFC 9802 (X.509) and RFC 9708 (CMS), profiled by NIST SP 800-208.
171
184
  // VERIFY ONLY by design: stateful signing requires atomic one-time-key index
172
185
  // state that belongs in an HSM, so this module never mints a signature.
173
186
  shbs: shbs,
174
- // `hpke` is RFC 9180 Hybrid Public Key Encryption -- the KEM + HKDF key
175
- // schedule + AEAD context construction behind TLS ECH / MLS / OHTTP. Pure
176
- // composition over node:crypto; the four DHKEM suites and the three ML-KEM
177
- // suites, all four modes.
187
+ // `hpke` is RFC 9180 Hybrid Public Key Encryption -- the KEM + key schedule +
188
+ // AEAD context construction behind TLS ECH / MLS / OHTTP. Pure composition over
189
+ // node:crypto; five DHKEM suites, three ML-KEM suites and three PQ/T hybrids,
190
+ // over the two-stage HKDF and single-stage SHAKE key schedules, all four modes.
178
191
  hpke: hpke,
179
192
  // `kem` is composite ML-KEM key establishment (draft-ietf-lamps-pq-composite-kem):
180
193
  // pki.kem.encapsulate / decapsulate over a post-quantum ML-KEM hybridized with a
@@ -197,7 +210,7 @@ module.exports = {
197
210
  // rejectUnauthorized always on, a TLS floor, a streaming response cap, and a timeout.
198
211
  // Curated to the public `https` factory; the module's `isBlockedIp` classifier is an
199
212
  // internal helper pki.path.build reuses (require the module), not a public surface.
200
- transport: { https: transport.https },
213
+ transport: { https: transport.https, peerChain: transport.peerChain },
201
214
  // `jose` is the RFC 7515 Flattened JWS + RFC 7638 JWK-thumbprint layer: a strict
202
215
  // base64url codec, a bounded duplicate-key-rejecting JSON reader, profiled
203
216
  // sign/verify (ACME-outer / EAB-inner / keyChange-inner), and an alg registry