@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
package/lib/csr-sign.js CHANGED
@@ -25,12 +25,22 @@ var pkix = require("./schema-pkix");
25
25
  var pkiBuild = require("./pki-build");
26
26
  var frameworkError = require("./framework-error");
27
27
  var guard = require("./guard-all");
28
+ var intrinsic = require("./guard-intrinsic");
29
+ /** @internal The promise a self-check is awaited through, built from the constructor captured at load.
30
+ * `Promise.resolve` builds from its RECEIVER, so reading the global binding at the call hands the
31
+ * construction to whatever that binding holds, and a constructor settling without awaiting its
32
+ * argument runs the continuation before the check has finished. */
33
+ var _Promise = intrinsic.Promise;
34
+ var _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
28
35
  require("./path-validate");
29
36
  var csrVerify = require("./csr-verify");
37
+ var relatedCert = require("./related-cert");
38
+ var possession = require("./possession");
39
+ var x509Schema = require("./schema-x509");
30
40
  var lint = require("./lint");
31
41
 
32
42
  var CsrError = frameworkError.CsrError;
33
- var KNOWN_SPEC_KEYS = Object.assign(Object.create(null), { subject: 1, subjectPublicKey: 1, extensionRequest: 1, challengePassword: 1, attributes: 1 });
43
+ var KNOWN_SPEC_KEYS = Object.assign(Object.create(null), { subject: 1, subjectPublicKey: 1, extensionRequest: 1, challengePassword: 1, relatedCertRequest: 1, privateKeyPossessionStatement: 1, attributes: 1 });
34
44
  /** @internal The four this verb reads, which are the four `pki.x509.sign` reads. A name outside
35
45
  * them was accepted and dropped, so asking for PEM and misspelling the option returned DER and
36
46
  * reported nothing. */
@@ -53,7 +63,11 @@ var _b = pkiBuild.makeBuilder({
53
63
  });
54
64
 
55
65
  function _implicitSetOf0(members) {
56
- return b.contextConstructed(0, Buffer.concat(members.slice().sort(Buffer.compare)));
66
+ /** @internal The copy, the DER SET-OF ordering AND the comparator go through captured operations.
67
+ * Read off their own homes, `slice`, `sort` and `Buffer.compare` each decide the bytes emitted:
68
+ * measured, replacing any of them made this set encode an attribute other than the one just
69
+ * validated, on bytes this verb then signs. */
70
+ return b.contextConstructed(0, Buffer.concat(guard.list.sortedCopy(members, guard.bytes.compare)));
57
71
  }
58
72
 
59
73
  function _buildRequestedExtensions(extSpec, subjectSpki) { return _b.requestedExtensions(extSpec, subjectSpki); }
@@ -63,6 +77,80 @@ function _challengePassword(pw) {
63
77
  return asn1.isPrintableString(pw) ? b.printable(pw) : b.utf8(pw);
64
78
  }
65
79
 
80
+ var KNOWN_POSSESSION_KEYS = Object.assign(Object.create(null), { signer: 1, certificate: 1, includeCertificate: 1 });
81
+ /** @internal PrivateKeyPossessionStatement ::= SEQUENCE { signer IssuerAndSerialNumber,
82
+ * cert Certificate OPTIONAL } (RFC 9883 sec. 3). The statement declares that this request is signed by
83
+ * a key OTHER than the one being certified, which is what lets a key-establishment key be certified at
84
+ * all: it cannot sign, so it cannot prove possession the PKCS#10 way. A carried certificate must be the
85
+ * one the signer names, since the two would otherwise describe different keys and only one of them
86
+ * signed. */
87
+ function _possessionStatement(spec) {
88
+ if (spec === null || typeof spec !== "object" || Buffer.isBuffer(spec)) {
89
+ throw _err("csr/bad-input", "privateKeyPossessionStatement must be an object { signer, certificate? }");
90
+ }
91
+ guard.identifier.assertKnownKeys(spec, KNOWN_POSSESSION_KEYS, _err, "csr/bad-input",
92
+ "privateKeyPossessionStatement has an unknown field: ");
93
+ var signer = spec.signer;
94
+ if (signer === null || typeof signer !== "object" || Buffer.isBuffer(signer)) {
95
+ throw _err("csr/bad-input", "privateKeyPossessionStatement needs a signer naming the signature certificate's issuer and serial (RFC 9883 sec. 3)");
96
+ }
97
+ var issuerRaw = (signer.issuer !== null && typeof signer.issuer === "object" && !Buffer.isBuffer(signer.issuer) &&
98
+ signer.issuer.bytes !== undefined) ? signer.issuer.bytes : signer.issuer;
99
+ var issuerDer = _b.reqDer(issuerRaw, "privateKeyPossessionStatement signer.issuer (the signature certificate's issuer Name DER)");
100
+ var serial = guard.range.authoredInteger(signer.serialNumber, _err, "csr/bad-input",
101
+ "privateKeyPossessionStatement signer.serialNumber");
102
+ if (serial < 1n) throw _err("csr/bad-input", "privateKeyPossessionStatement signer.serialNumber must be a positive integer (RFC 5280 sec. 4.1.2.2)");
103
+
104
+ /** @internal The certificate is required HERE even though RFC 9883 sec. 3 lets the wire form omit it,
105
+ * because this verb has to know which key signs: it resolves the signature algorithm from that key and
106
+ * checks its own signature against it. The omission the RFC allows is for the CA's benefit, and a
107
+ * requester holding the signing key holds its certificate too, so `includeCertificate: false` emits
108
+ * the compact form while the builder still knows the key. */
109
+ if (spec.certificate == null) {
110
+ throw _err("csr/bad-input",
111
+ "privateKeyPossessionStatement needs the certificate: this verb resolves the signature algorithm from its key and checks the signature against it. Pass includeCertificate: false to leave it off the wire, which RFC 9883 sec. 3 allows");
112
+ }
113
+ var certDer = _b.reqDer(spec.certificate, "privateKeyPossessionStatement certificate");
114
+ var include = spec.includeCertificate === undefined ? true : spec.includeCertificate;
115
+ if (include !== true && include !== false) {
116
+ throw _err("csr/bad-input", "privateKeyPossessionStatement includeCertificate is a flag, so it is true, false or omitted");
117
+ }
118
+ var signerIsn = b.sequence([b.raw(issuerDer), b.integer(serial)]);
119
+ /** @internal Read back through the one statement reader with the certificate ALWAYS present, so the
120
+ * shape rule and the signer-names-the-certificate rule are applied by the same code a consumer
121
+ * applies. The emitted form may then drop it; dropping it cannot make a checked statement wrong. */
122
+ try { possession.parse(b.sequence([signerIsn, b.raw(certDer)])); }
123
+ catch (e) { throw _err("csr/bad-input", "privateKeyPossessionStatement: " + ((e && e.message) || String(e)), e); }
124
+ return { der: b.sequence(include ? [signerIsn, b.raw(certDer)] : [signerIsn]), certificate: certDer };
125
+ }
126
+
127
+ var KNOWN_RELATED_CERT_KEYS = Object.assign(Object.create(null), { certID: 1, requestTime: 1, locationInfo: 1, signature: 1 });
128
+ /** @internal RequesterCertificate ::= SEQUENCE { certID IssuerAndSerialNumber, requestTime BinaryTime,
129
+ * locationInfo UniformResourceIdentifiers, signature BIT STRING } (RFC 9763 sec. 3.1), where
130
+ * UniformResourceIdentifiers ::= SEQUENCE SIZE (1..MAX) OF IA5String. The first two fields are the
131
+ * ones the signature covers, so they are encoded by the module that defines that preimage rather than
132
+ * a second time here: an encoder that disagreed with it would produce a proof no verifier accepts. */
133
+ function _relatedCertRequest(spec) {
134
+ if (spec === null || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("csr/bad-input", "relatedCertRequest must be an object { certID, requestTime, locationInfo, signature }");
135
+ guard.identifier.assertKnownKeys(spec, KNOWN_RELATED_CERT_KEYS, _err, "csr/bad-input", "relatedCertRequest has an unknown field: ");
136
+ var signed;
137
+ try { signed = relatedCert.requestSignedData({ certID: spec.certID, requestTime: spec.requestTime }); }
138
+ catch (e) { throw _err("csr/bad-input", "relatedCertRequest certID and requestTime: " + ((e && e.message) || String(e)), e); }
139
+
140
+ var locations = spec.locationInfo;
141
+ if (!Array.isArray(locations)) throw _err("csr/bad-input", "relatedCertRequest locationInfo must be an array of URI strings (RFC 9763 sec. 3.1)");
142
+ if (locations.length < 1) throw _err("csr/bad-input", "relatedCertRequest locationInfo must name at least one URI (SEQUENCE SIZE (1..MAX))");
143
+ var uris = guard.list.copyMap(locations, function (u, i) {
144
+ if (typeof u !== "string" || u.length === 0) throw _err("csr/bad-input", "relatedCertRequest locationInfo[" + i + "] must be a non-empty URI string");
145
+ if (!guard.encoding.isAsciiText(u)) throw _err("csr/bad-input", "relatedCertRequest locationInfo[" + i + "] must be an IA5String, so every character is ASCII (RFC 9763 sec. 3.1)");
146
+ return b.ia5(u);
147
+ });
148
+
149
+ var sig = _b.reqDer(spec.signature, "relatedCertRequest signature (the proof of possession over pki.relatedCert.requestSignedData)");
150
+ if (sig.length === 0) throw _err("csr/bad-input", "relatedCertRequest signature must not be empty");
151
+ return b.sequence([b.raw(signed), b.sequence(uris), b.bitString(sig, 0)]);
152
+ }
153
+
66
154
  /**
67
155
  * @primitive pki.csr.sign
68
156
  * @signature pki.csr.sign(spec, key, opts?) -> Promise<Buffer|string>
@@ -140,9 +228,22 @@ function _sign(spec, key, opts) {
140
228
  }
141
229
  if (spec.extensionRequest != null) addAttr(O("extensionRequest"), _buildRequestedExtensions(spec.extensionRequest, subjectSpki));
142
230
  if (spec.challengePassword != null) addAttr(O("challengePassword"), _challengePassword(spec.challengePassword));
231
+ if (spec.relatedCertRequest != null) addAttr(O("relatedCertRequest"), _relatedCertRequest(spec.relatedCertRequest));
232
+ /** @internal The statement, and the key its certificate carries: the signature is made by that key, so
233
+ * the scheme below resolves from it. A statement that omits the certificate leaves the signing key
234
+ * unverifiable here, since there is nothing to check it against, so the certificate is required at
235
+ * build time even though RFC 9883 sec. 3 lets the wire form omit it. */
236
+ var possessionCertSpki = null;
237
+ if (spec.privateKeyPossessionStatement != null) {
238
+ var statement = _possessionStatement(spec.privateKeyPossessionStatement);
239
+ addAttr(O("statementOfPossession"), b.raw(statement.der));
240
+ possessionCertSpki = x509Schema.parse(statement.certificate).subjectPublicKeyInfo.bytes;
241
+ }
143
242
  if (spec.attributes != null) {
144
243
  if (!Array.isArray(spec.attributes)) throw _err("csr/bad-input", "spec.attributes must be an array of pre-encoded Attribute DER");
145
- spec.attributes.forEach(function (a, i) {
244
+ /** @internal Through the captured walk, never the caller's own `forEach`: a request's attributes are
245
+ * signed content, and a replaced walk hands the callback a list it never validated. */
246
+ guard.list.copyMap(spec.attributes, function (a, i) {
146
247
  var der = _b.reqDer(a, "attribute [" + i + "]");
147
248
  var n;
148
249
  try { n = asn1.decode(der); }
@@ -153,6 +254,8 @@ function _sign(spec, key, opts) {
153
254
  catch (e) { throw _err("csr/bad-input", "pre-encoded attribute [" + i + "] type is not an OBJECT IDENTIFIER", e); }
154
255
  if (at === O("extensionRequest")) throw _err("csr/bad-input", "pass requested extensions via spec.extensionRequest, not a pre-encoded extensionRequest attribute");
155
256
  if (at === O("challengePassword")) throw _err("csr/bad-input", "pass the challenge password via spec.challengePassword, not a pre-encoded challengePassword attribute");
257
+ if (at === O("relatedCertRequest")) throw _err("csr/bad-input", "pass the related-certificate request via spec.relatedCertRequest, not a pre-encoded relatedCertRequest attribute");
258
+ if (at === O("statementOfPossession")) throw _err("csr/bad-input", "pass the possession statement via spec.privateKeyPossessionStatement, not a pre-encoded statementOfPossession attribute");
156
259
  if (!n.children[1].children || n.children[1].children.length === 0) throw _err("csr/bad-input", "pre-encoded attribute [" + i + "] value SET must contain at least one value (RFC 2986 SET SIZE(1..MAX))");
157
260
  if (seenAttr[at]) throw _err("csr/bad-input", "duplicate " + (oid.name(at) || at) + " attribute");
158
261
  seenAttr[at] = true;
@@ -160,7 +263,15 @@ function _sign(spec, key, opts) {
160
263
  });
161
264
  }
162
265
 
163
- var scheme = signScheme.resolveSignScheme(_b.certLikeFromSpki(subjectSpki), { combinedRsaSig: true, pss: opts.pss, digestAlgorithm: opts.digestAlgorithm }, true, _signE);
266
+ /** @internal Which key signs, and therefore which key the scheme resolves from. Normally the subject's
267
+ * own, because a PKCS#10 signature IS the proof of possession of the key being certified. With an
268
+ * RFC 9883 statement of possession the signature is made by the key of the certificate the statement
269
+ * names, which is the whole mechanism: a key-establishment key cannot sign, so it cannot prove
270
+ * possession that way. The relaxation is scoped to a request that declares the statement, so a
271
+ * request without one is still held to signing with the key it certifies. */
272
+ var provingSpki = subjectSpki;
273
+ if (possessionCertSpki !== null) provingSpki = possessionCertSpki;
274
+ var scheme = signScheme.resolveSignScheme(_b.certLikeFromSpki(provingSpki), { combinedRsaSig: true, pss: opts.pss, digestAlgorithm: opts.digestAlgorithm }, true, _signE);
164
275
 
165
276
  var criDer = b.sequence([b.integer(0n), subjectDer, b.raw(subjectSpki), _implicitSetOf0(attrs)]);
166
277
  lint.assertBuildClean("csr",
@@ -168,7 +279,7 @@ function _sign(spec, key, opts) {
168
279
  opts.profile, _err, "csr/profile-violation");
169
280
 
170
281
  return signScheme.signOverTbs(scheme, signingKey, criDer, _signE).then(function (sig) {
171
- return Promise.resolve(_b.assertSignatureVerifies(criDer, sig, subjectSpki, scheme)).then(function () {
282
+ return _promiseResolve(_Promise, _b.assertSignatureVerifies(criDer, sig, provingSpki, scheme)).then(function () {
172
283
  var der = b.sequence([criDer, scheme.sigAlgId, b.bitString(sig, 0)]);
173
284
  return opts.pem ? csr.pemEncode(der, "CERTIFICATE REQUEST") : der;
174
285
  });
@@ -233,7 +344,7 @@ function _coerceCsr(request) {
233
344
  function verify(request) { return guard.async.deferred(function () { return _verify(request); }); }
234
345
  function _verify(request) {
235
346
  var parsed = _coerceCsr(request);
236
- return Promise.resolve(csrVerify.verifyCsrSignature(parsed)).then(function (ok) {
347
+ return _promiseResolve(_Promise, csrVerify.verifyCsrSignature(parsed)).then(function (ok) {
237
348
  return guard.verdict.of({
238
349
  valid: ok === true,
239
350
  verified: ok === true,