@blamejs/pki 0.8.31 → 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 +393 -0
  2. package/README.md +66 -15
  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 +26 -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 +33 -6
  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 +228 -2
  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
@@ -10,6 +10,21 @@ var validator = require("./validator-all");
10
10
  var edwardsPoint = require("./edwards-point");
11
11
  var _verdictOf = require("./guard-verdict").of;
12
12
  var subtle = webcrypto.webcrypto.subtle;
13
+ /** @internal The verify operation, captured at load. A composite signature is verified in two halves
14
+ * and each must hold, so a replacement answering true for either accepts a signature that half of the
15
+ * pair never made, which is the guarantee a composite exists to give. */
16
+ var _subtleVerify = require("./guard-intrinsic").uncurry(subtle.verify);
17
+ /** @internal Every refusal this module returns is a RESOLVED promise carrying a negative verdict, and
18
+ * `Promise.resolve` builds from its RECEIVER. Read from the global binding at the call, the refusal is
19
+ * constructed by whatever that binding holds, so a replacement settling truthy turns each of these
20
+ * into an acceptance. The constructor and the operation are both taken at load. */
21
+ var _intrinsic = require("./guard-intrinsic");
22
+ var _Promise = _intrinsic.Promise;
23
+ var _promiseResolve = _intrinsic.uncurry(_intrinsic.promiseResolve);
24
+ /** @internal The aggregator is the same defect one step out: the composite verdict is `ok` only when
25
+ * BOTH component verifications resolve true, so a replaced `all` that resolves `[true, true]` without
26
+ * awaiting either half reports a forged signature as verified. */
27
+ var _promiseAll = _intrinsic.uncurry(_intrinsic.promiseAll);
13
28
  var _b = asn1.build;
14
29
 
15
30
  function _codeOf(e, fallback) {
@@ -69,10 +84,10 @@ function compositeMprime(d, phBuf, ctx) {
69
84
 
70
85
  function _verifyTradComponent(trad, tradPK, tradSig, mprime, E, badSig) {
71
86
  if (trad.ec) {
72
- if (tradPK.length < 1 || tradPK[0] !== 0x04) return Promise.resolve(false);
87
+ if (tradPK.length < 1 || tradPK[0] !== 0x04) return _promiseResolve(_Promise, false);
73
88
  return subtle.importKey("spki", _ecSpki(trad.ec, tradPK), { name: "ECDSA" }, false, ["verify"]).then(function (k) {
74
89
  var p1363 = validator.sig.ecdsaDerToP1363(tradSig, trad.ec, E, badSig);
75
- return subtle.verify({ name: "ECDSA", hash: trad.hash }, k, p1363, mprime);
90
+ return _subtleVerify(subtle, { name: "ECDSA", hash: trad.hash }, k, p1363, mprime);
76
91
  });
77
92
  }
78
93
  if (trad.eddsa) {
@@ -80,21 +95,21 @@ function _verifyTradComponent(trad, tradPK, tradSig, mprime, E, badSig) {
80
95
  throw new E(badSig, "the composite EdDSA component public key is not a valid, full-order Edwards point");
81
96
  }
82
97
  return subtle.importKey("spki", _edSpki(trad.eddsa, tradPK), { name: trad.eddsa }, false, ["verify"])
83
- .then(function (k) { return subtle.verify({ name: trad.eddsa }, k, tradSig, mprime); });
98
+ .then(function (k) { return _subtleVerify(subtle, { name: trad.eddsa }, k, tradSig, mprime); });
84
99
  }
85
100
  if (trad.rsaPss || trad.rsaPkcs1) {
86
101
  var bits;
87
102
  try { bits = _rsaModulusBits(tradPK); }
88
- catch (_e) { return Promise.resolve(false); }
89
- if (bits !== trad.rsaBits) return Promise.resolve(false);
103
+ catch (_e) { return _promiseResolve(_Promise, false); }
104
+ if (bits !== trad.rsaBits) return _promiseResolve(_Promise, false);
90
105
  if (trad.rsaPss) {
91
106
  return subtle.importKey("spki", _rsaSpki(tradPK), { name: "RSA-PSS", hash: trad.hash }, false, ["verify"])
92
- .then(function (k) { return subtle.verify({ name: "RSA-PSS", saltLength: trad.salt }, k, tradSig, mprime); });
107
+ .then(function (k) { return _subtleVerify(subtle, { name: "RSA-PSS", saltLength: trad.salt }, k, tradSig, mprime); });
93
108
  }
94
109
  return subtle.importKey("spki", _rsaSpki(tradPK), { name: "RSASSA-PKCS1-v1_5", hash: trad.hash }, false, ["verify"])
95
- .then(function (k) { return subtle.verify({ name: "RSASSA-PKCS1-v1_5" }, k, tradSig, mprime); });
110
+ .then(function (k) { return _subtleVerify(subtle, { name: "RSASSA-PKCS1-v1_5" }, k, tradSig, mprime); });
96
111
  }
97
- return Promise.resolve(false);
112
+ return _promiseResolve(_Promise, false);
98
113
  }
99
114
 
100
115
  /** @internal Every verdict this returns carries its own `then`: resolving a promise reads that property
@@ -102,7 +117,7 @@ function _verifyTradComponent(trad, tradPK, tradSig, mprime, E, badSig) {
102
117
  * caller a different object than the one the comparison produced (see guard-verdict). */
103
118
  function compositeVerify(spkiBytes, sigBytes, message, d, E, unsupported, badSig) {
104
119
  if (d.trad.unsupported) {
105
- return Promise.resolve(_verdictOf({ ok: false, code: unsupported,
120
+ return _promiseResolve(_Promise, _verdictOf({ ok: false, code: unsupported,
106
121
  error: new E(unsupported, "composite " + d.name + ": " + d.trad.unsupported) }));
107
122
  }
108
123
  var rawKey;
@@ -110,9 +125,9 @@ function compositeVerify(spkiBytes, sigBytes, message, d, E, unsupported, badSig
110
125
  var bs = asn1.read.bitString(asn1.decode(spkiBytes).children[1]);
111
126
  if (bs.unusedBits !== 0) throw new E(badSig, "composite subjectPublicKey has unused bits");
112
127
  rawKey = bs.bytes;
113
- } catch (e) { return Promise.resolve(_verdictOf({ ok: false, code: _codeOf(e, badSig), error: e })); }
128
+ } catch (e) { return _promiseResolve(_Promise, _verdictOf({ ok: false, code: _codeOf(e, badSig), error: e })); }
114
129
  if (rawKey.length <= d.mldsaPk || sigBytes.length <= d.mldsaSig) {
115
- return Promise.resolve(_verdictOf({ ok: false, code: badSig,
130
+ return _promiseResolve(_Promise, _verdictOf({ ok: false, code: badSig,
116
131
  error: new E(badSig, "composite key/signature shorter than the fixed ML-DSA component") }));
117
132
  }
118
133
  var mldsaPK = rawKey.subarray(0, d.mldsaPk), tradPK = rawKey.subarray(d.mldsaPk);
@@ -120,9 +135,9 @@ function compositeVerify(spkiBytes, sigBytes, message, d, E, unsupported, badSig
120
135
  return subtle.digest({ name: d.ph }, message).then(function (phBuf) {
121
136
  var mprime = compositeMprime(d, phBuf);
122
137
  var mldsaP = subtle.importKey("spki", _mldsaSpki(d.mldsaOid, mldsaPK), { name: d.mldsa }, false, ["verify"])
123
- .then(function (mk) { return subtle.verify({ name: d.mldsa, context: d.label }, mk, mldsaSig, mprime); });
138
+ .then(function (mk) { return _subtleVerify(subtle, { name: d.mldsa, context: d.label }, mk, mldsaSig, mprime); });
124
139
  var tradP = _verifyTradComponent(d.trad, tradPK, tradSig, mprime, E, badSig);
125
- return Promise.all([mldsaP, tradP]).then(function (r) { return _verdictOf({ ok: r[0] === true && r[1] === true }); });
140
+ return _promiseAll(_Promise, [mldsaP, tradP]).then(function (r) { return _verdictOf({ ok: r[0] === true && r[1] === true }); });
126
141
  }).catch(function (e) { return _verdictOf({ ok: false, code: _codeOf(e, badSig), error: e }); });
127
142
  }
128
143
 
@@ -141,7 +156,7 @@ function compositeSign(d, keys, message, ctx) {
141
156
  var mldsaP = subtle.importKey("pkcs8", keys.mldsa, { name: d.mldsa }, false, ["sign"])
142
157
  .then(function (k) { return subtle.sign({ name: d.mldsa, context: d.label }, k, mprime); });
143
158
  var tradP = _signTradComponent(d.trad, keys.trad, mprime);
144
- return Promise.all([mldsaP, tradP]).then(function (s) { return Buffer.concat([Buffer.from(s[0]), Buffer.from(s[1])]); });
159
+ return _promiseAll(_Promise, [mldsaP, tradP]).then(function (s) { return Buffer.concat([Buffer.from(s[0]), Buffer.from(s[1])]); });
145
160
  });
146
161
  }
147
162
  function _signTradComponent(trad, pkcs8, mprime) {
package/lib/constants.js CHANGED
@@ -114,6 +114,20 @@ var LIMITS = Object.assign(Object.create(null), {
114
114
  DER_MAX_ITEMS: 4000000,
115
115
  DUMP_MAX_NODES: 65536,
116
116
  DUMP_MAX_VALUE_BYTES: 256,
117
+ /** @internal The values one deep copy of a caller's object may visit. A copy visits every reference
118
+ * independently, so a value holding the same subobject in two places costs twice, and depth alone
119
+ * bounds nothing: nested 19 levels with each level sharing its child expands to half a million values.
120
+ * The figure bounds the WORST case as well as admitting the real one, which is why it is not larger.
121
+ * MEASURED: a 1000-record trust-anchor list is about 9000 values and copies in 54 ms, 10000 records in
122
+ * 482 ms, and this bound is reached in about 400 ms, so a hostile value costs that much once instead of
123
+ * growing without limit. An options bag or a key is tens of values. */
124
+ DEEP_COPY_MAX_NODES: 65536,
125
+ /** @internal The certificates a CT log's accepted-roots response may carry. A configured log is trusted
126
+ * for its own signatures and not for its resource use, and the response body cap bounds the BYTES while
127
+ * saying nothing about the element count: a body well under it carries about a million empty strings,
128
+ * each of which becomes its own Buffer. The figure is far above any real log, the public ones listing a
129
+ * few hundred roots. */
130
+ CT_MAX_ROOTS: 4096,
117
131
  NAMED_SCAN_MAX_ELEMENTS: 16777216,
118
132
  MAX_DECODE_DEPTH_CEILING: 256,
119
133
  PEM_MAX_BYTES: BYTES.mib(16),
@@ -149,6 +163,7 @@ var LIMITS = Object.assign(Object.create(null), {
149
163
  CMP_KEM_MAX_KEY_BYTES: 1024,
150
164
  CMP_KEM_MIN_KEY_BYTES: 20,
151
165
  MERKLE_MAX_PROOF_NODES: 65,
166
+ MERKLE_MAX_LEAVES: 1048576,
152
167
  PROXY_MAX_TLS_ANCHORS: 256,
153
168
  PATH_MAX_CERTS: 100,
154
169
  PATH_MAX_POLICY_NODES: 4096,
@@ -168,6 +183,8 @@ var LIMITS = Object.assign(Object.create(null), {
168
183
  JSON_MAX_BYTES: BYTES.mib(1),
169
184
  JSON_MAX_DEPTH: 32,
170
185
  CT_LOG_LIST_MAX_BYTES: BYTES.mib(4),
186
+ CT_MAX_ENTRIES_PER_REQUEST: 1024,
187
+ TUF_MAX_ROOT_CHAIN: 1024,
171
188
  PBKDF2_MAX_ITERATIONS: 10000000,
172
189
  PBKDF2_MAX_SALT: 1024,
173
190
  MIME_MAX_BYTES: BYTES.mib(16),
@@ -175,6 +192,15 @@ var LIMITS = Object.assign(Object.create(null), {
175
192
  COMPRESS_MAX_BYTES: BYTES.mib(16),
176
193
  TLS_CERT_MSG_MAX_BYTES: BYTES.mib(16) - 1,
177
194
  TLS_CERT_MAX_ENTRIES: 100,
195
+ TLS_MAX_PEER_CHAIN: 16,
196
+ TLOG_MAX_NOTE_BYTES: BYTES.mib(1),
197
+ TLOG_MAX_SIGNATURES: 64,
198
+ TLOG_MAX_EXTENSION_LINES: 64,
199
+ TLOG_MAX_ENTRY_BUNDLE_BYTES: BYTES.mib(16),
200
+ /** @internal A tile is 256 wide, so an entry bundle covers at most that many entries (C2SP tlog-tiles).
201
+ * The byte cap alone does not bound the COUNT: a megabyte of zero bytes is half a million zero-length
202
+ * entries, each one an object, so the permitted input allocates hundreds of megabytes. */
203
+ TLOG_MAX_ENTRY_BUNDLE_ENTRIES: 256,
178
204
  MDS_BLOB_MAX_BYTES: BYTES.mib(32),
179
205
  MDS_BLOB_HEADER_MAX_BYTES: BYTES.kib(256),
180
206
  MDS_BLOB_SIG_MAX_BYTES: BYTES.kib(8),
package/lib/crl-sign.js CHANGED
@@ -25,6 +25,11 @@ var signScheme = require("./sign-scheme");
25
25
  var guard = require("./guard-all");
26
26
  var intrinsic = require("./guard-intrinsic");
27
27
  var _hasOwn = intrinsic.hasOwn, _bufferEquals = intrinsic.bufferEquals;
28
+ var _arrSlice = intrinsic.arraySlice, _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
29
+ /** @internal The RECEIVER for the captured `Promise.resolve`, captured too. `resolve` builds through
30
+ * its receiver, so handing it the live `globalThis.Promise` put the capture back under a constructor a
31
+ * replacement could take over and decide what the awaited value became. */
32
+ var _Promise = intrinsic.Promise;
28
33
  var frameworkError = require("./framework-error");
29
34
  var pkix = require("./schema-pkix");
30
35
  var schema = require("./schema-engine");
@@ -53,7 +58,8 @@ var _encodeName = _b.encodeName, _isEmptyName = _b.isEmptyName, _reqDer = _b.req
53
58
  _assertValidSpki = _b.assertValidSpki, _assertValidExtension = _b.assertValidExtension,
54
59
  _timeDer = _b.timeDer, _ext = _b.ext, _extAki = _b.extAki, _spkiKeyId = _b.spkiKeyId,
55
60
  _serialInteger = _b.serialInteger, _encodeGeneralName = _b.encodeGeneralName,
56
- _certLikeFromSpki = _b.certLikeFromSpki, _assertSignatureVerifies = _b.assertSignatureVerifies;
61
+ _certLikeFromSpki = _b.certLikeFromSpki, _assertSignatureVerifies = _b.assertSignatureVerifies,
62
+ _altPublicKeyValueOf = _b.altPublicKeyValueOf;
57
63
  var EXT_DECODERS = pkix.certExtensionDecoders(NS).byOid;
58
64
 
59
65
  /** @internal A pre-encoded extension is admitted on the shape of its Extension SEQUENCE, which says
@@ -84,8 +90,11 @@ var KNOWN_SPEC_KEYS = intrinsic.assign(intrinsic.create(null), {
84
90
  var KNOWN_SPEC_KEYS_ISSUER_NAMED = intrinsic.assign(intrinsic.create(null), {
85
91
  thisUpdate: 1, nextUpdate: 1, revoked: 1, crlNumber: 1, extensions: 1,
86
92
  });
87
- var KNOWN_ISSUER_CERT_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, cert: 1 });
88
- var KNOWN_ISSUER_EXPLICIT_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, name: 1, publicKey: 1 });
93
+ /** @internal `altKey` and `altPublicKey` are the issuer's ALTERNATIVE signing key and its public half.
94
+ * ITU-T X.509 (2019) clause 7.10.3 gives a CRL the same alternative-signature procedure a certificate
95
+ * gets, with the same two extensions. */
96
+ var KNOWN_ISSUER_CERT_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, cert: 1, altKey: 1, altPublicKey: 1 });
97
+ var KNOWN_ISSUER_EXPLICIT_KEYS = intrinsic.assign(intrinsic.create(null), { key: 1, name: 1, publicKey: 1, altKey: 1, altPublicKey: 1 });
89
98
  var KNOWN_SIGN_OPTS = intrinsic.assign(intrinsic.create(null), { digestAlgorithm: 1, pem: 1, pss: 1, profile: 1 });
90
99
 
91
100
  /** @internal The signature the build-time gate lints against, standing in for one that does not exist
@@ -214,7 +223,7 @@ function _freshestValue(spec) {
214
223
  function _aiaValue(spec) {
215
224
  if (!Array.isArray(spec) || !spec.length) throw _err("crl/bad-input", "authorityInfoAccess must be a non-empty array of caIssuers GeneralNames");
216
225
  var caIssuers = O("caIssuers");
217
- return b.sequence(spec.map(function (gn) { return b.sequence([b.oid(caIssuers), _encodeGeneralName(gn)]); }));
226
+ return b.sequence(guard.list.copyMap(spec, function (gn) { return b.sequence([b.oid(caIssuers), _encodeGeneralName(gn)]); }));
218
227
  }
219
228
 
220
229
  function _buildCrlExtensions(spec, ctx) {
@@ -231,7 +240,10 @@ function _buildCrlExtensions(spec, ctx) {
231
240
  if (ext == null) ext = {};
232
241
  if (Array.isArray(ext)) {
233
242
  var arrBase = null, arrCrlNum = null;
234
- ext.forEach(function (e, i) {
243
+ /** @internal The walk goes through `guard.list.copyMap`, never the caller's own `forEach`. Reached
244
+ * off the array a caller supplied, it is that caller's to replace, and one handing the callback a
245
+ * different list bypasses every per-element rule below at once on content this verb then signs. */
246
+ guard.list.copyMap(ext, function (e, i) {
235
247
  var der = _reqDer(e, "extension");
236
248
  _assertValidExtension(der, i, EXT_DECODERS);
237
249
  var node = asn1.decode(der);
@@ -324,7 +336,9 @@ function _buildRevoked(entryList, isDelta) {
324
336
  if (e.certificateIssuer != null) throw _err("crl/bad-input", _CERT_ISSUER_DEFERRED);
325
337
  if (e.extensions != null) {
326
338
  if (!Array.isArray(e.extensions)) throw _err("crl/bad-input", "revoked entry [" + idx + "] extensions must be an array of pre-encoded Extension DER");
327
- e.extensions.forEach(function (x, j) {
339
+ /** @internal Through the captured walk for the reason the extension list above gives: a revoked
340
+ * entry's extensions are signed content too. */
341
+ guard.list.copyMap(e.extensions, function (x, j) {
328
342
  var der = _reqDer(x, "entry extension");
329
343
  _assertValidExtension(der, j, EXT_DECODERS);
330
344
  var xnode = asn1.decode(der);
@@ -423,21 +437,65 @@ function _sign(spec, issuer, opts) {
423
437
 
424
438
  var scheme = signScheme.resolveSignScheme(_certLikeFromSpki(issuerSpki), { combinedRsaSig: true, pss: opts.pss, digestAlgorithm: opts.digestAlgorithm }, true, _signE);
425
439
 
426
- var tbsChildren = [];
427
- if (version === 2) tbsChildren.push(b.integer(1n));
428
- tbsChildren.push(scheme.sigAlgId);
429
- tbsChildren.push(issuerDer);
430
- tbsChildren.push(thisU);
431
- if (nextU) tbsChildren.push(nextU);
432
- if (revoked.entries.length) tbsChildren.push(b.sequence(revoked.entries));
433
- if (crlExts.length) tbsChildren.push(b.explicit(0, b.sequence(crlExts)));
434
- var tbsDer = b.sequence(tbsChildren);
435
- lint.assertBuildClean("crl",
436
- b.sequence([tbsDer, scheme.sigAlgId, b.bitString(GATE_PLACEHOLDER_SIG, 0)]),
437
- opts.profile, _err, "crl/profile-violation");
440
+ /** @internal ITU-T X.509 (2019) clause 7.10.3. The alternative signature covers the PreTBSCertList,
441
+ * which is the tbsCertList without its `signature` AlgorithmIdentifier and without its own
442
+ * altSignatureValue extension (draft-truskovsky-lamps-pq-hybrid-x509 sec. 5), so the extension list
443
+ * is built twice from one function and the alternative signature is generated first.
444
+ * subjectAltPublicKeyInfo is not emitted on a CRL: the NOTE in clause 7.2.2 states it is a
445
+ * certificate extension only. */
446
+ var altCrl = _resolveCrlAltSigning(issuer, issuerCert);
447
+ if (altCrl !== null) {
448
+ guard.list.append(crlExts, _ext(O("altSignatureAlgorithm"), false, b.raw(altCrl.altSigAlgId)));
449
+ version = 2;
450
+ }
451
+
452
+ /** @internal `omitSignatureAlg` drops the `signature` AlgorithmIdentifier, turning the tbsCertList
453
+ * into the PreTBSCertList the alternative signature covers. */
454
+ function tbsOver(extList, omitSignatureAlg) {
455
+ var tbsChildren = [];
456
+ if (version === 2) tbsChildren.push(b.integer(1n));
457
+ if (omitSignatureAlg !== true) tbsChildren.push(scheme.sigAlgId);
458
+ tbsChildren.push(issuerDer);
459
+ tbsChildren.push(thisU);
460
+ if (nextU) tbsChildren.push(nextU);
461
+ if (revoked.entries.length) tbsChildren.push(b.sequence(revoked.entries));
462
+ if (extList.length) tbsChildren.push(b.explicit(0, b.sequence(extList)));
463
+ return b.sequence(tbsChildren);
464
+ }
465
+
466
+ /** @internal Self-checked against issuer.altPublicKey before it is embedded, the same gate the native
467
+ * pass applies below. An altKey and an altPublicKey that are different keys of one algorithm resolve
468
+ * to the same scheme, so without this the CRL ships with an alternative signature that does not
469
+ * verify under the alternative key its own issuer names. */
470
+ var altFirst;
471
+ if (altCrl === null) {
472
+ altFirst = _promiseResolve(_Promise, null);
473
+ } else {
474
+ var altPreimage = tbsOver(crlExts, true);
475
+ altFirst = signScheme.signOverTbs(altCrl.scheme, issuer.altKey, altPreimage, _signE)
476
+ .then(function (altSigBytes) {
477
+ return _promiseResolve(_Promise,
478
+ _assertSignatureVerifies(altPreimage, altSigBytes, altCrl.issuerAltSpki, altCrl.scheme)
479
+ ).then(function () { return altSigBytes; });
480
+ });
481
+ }
482
+
483
+ return altFirst.then(function (altSigBytes) {
484
+ var finalExts = crlExts;
485
+ if (altCrl !== null) {
486
+ finalExts = _arrSlice(crlExts);
487
+ guard.list.append(finalExts, _ext(O("altSignatureValue"), false, b.bitString(altSigBytes, 0)));
488
+ }
489
+ var tbsDer = tbsOver(finalExts);
490
+ lint.assertBuildClean("crl",
491
+ b.sequence([tbsDer, scheme.sigAlgId, b.bitString(GATE_PLACEHOLDER_SIG, 0)]),
492
+ opts.profile, _err, "crl/profile-violation");
493
+ return _signNativeCrl(tbsDer);
494
+ });
438
495
 
496
+ function _signNativeCrl(tbsDer) {
439
497
  return signScheme.signOverTbs(scheme, issuer.key, tbsDer, _signE).then(function (sig) {
440
- return Promise.resolve(_assertSignatureVerifies(tbsDer, sig, issuerSpki, scheme)).then(function () {
498
+ return _promiseResolve(_Promise, _assertSignatureVerifies(tbsDer, sig, issuerSpki, scheme)).then(function () {
441
499
  var crlDer = b.sequence([tbsDer, scheme.sigAlgId, b.bitString(sig, 0)]);
442
500
  return opts.pem ? crlSchema.pemEncode(crlDer, "X509 CRL") : crlDer;
443
501
  });
@@ -445,6 +503,40 @@ function _sign(spec, issuer, opts) {
445
503
  if (e instanceof CrlError) throw e;
446
504
  throw _err("crl/bad-input", "signing the CRL failed -- the signing key does not match the resolved algorithm or is invalid", e);
447
505
  });
506
+ }
507
+ }
508
+
509
+ /** @internal The alternative signing side for a CRL. A CRL carries no subjectAltPublicKeyInfo, so the
510
+ * issuer's alternative key alone turns the mechanism on and there is nothing for the spec to name. */
511
+ function _resolveCrlAltSigning(issuer, issuerCert) {
512
+ if (issuer.altKey == null) {
513
+ if (issuer.altPublicKey != null) {
514
+ throw _err("crl/bad-input", "issuer.altPublicKey needs issuer.altKey to sign the alternative signature with (ITU-T X.509 (2019) clause 7.10.3)");
515
+ }
516
+ return null;
517
+ }
518
+ if (issuer.altPublicKey == null) {
519
+ throw _err("crl/bad-input", "an issuer altKey needs issuer.altPublicKey, the SPKI of its public half, to resolve the alternative signature algorithm");
520
+ }
521
+ var issuerAltSpki = _reqDer(issuer.altPublicKey, "issuer.altPublicKey (the issuer's alternative SPKI DER)");
522
+ _assertValidSpki(issuerAltSpki, "issuer.altPublicKey");
523
+ /** @internal Bound to the issuer's certificate, as the native key already is: that one is read OUT of
524
+ * issuer.cert while this one arrives beside it. A relying party checks a CRL's alternative signature
525
+ * with the alternative key the CRL issuer's certificate publishes (clause 9.8.4), so a CRL signed with
526
+ * any other key carries a signature nobody can check. */
527
+ if (issuerCert !== null && issuerCert !== undefined) {
528
+ var published = _altPublicKeyValueOf(issuerCert);
529
+ if (published === null) {
530
+ throw _err("crl/bad-input",
531
+ "issuer.cert publishes no subjectAltPublicKeyInfo, so it names no alternative key for a relying party to check this CRL's alternative signature with (ITU-T X.509 (2019) clause 9.8.4)");
532
+ }
533
+ if (!_bufferEquals(published, issuerAltSpki)) {
534
+ throw _err("crl/bad-input",
535
+ "issuer.altPublicKey is not the alternative key issuer.cert publishes in its subjectAltPublicKeyInfo, so the alternative signature would verify under no key a relying party can find (ITU-T X.509 (2019) clause 9.8.4)");
536
+ }
537
+ }
538
+ var altScheme = signScheme.resolveSignScheme(_certLikeFromSpki(issuerAltSpki), { combinedRsaSig: true }, true, _signE);
539
+ return { scheme: altScheme, altSigAlgId: altScheme.sigAlgId, issuerAltSpki: issuerAltSpki };
448
540
  }
449
541
 
450
542
  /**
package/lib/crmf-sign.js CHANGED
@@ -20,8 +20,6 @@
20
20
  var nodeCrypto = require("node:crypto");
21
21
  /** @internal Bound at module load, like every other capture here: the proof below decides what it
22
22
  * agrees and what it hashes, so a replacement swapped in afterwards must not be able to steer it. */
23
- var _createHash = nodeCrypto.createHash;
24
- var _createHmac = nodeCrypto.createHmac;
25
23
  var _diffieHellman = nodeCrypto.diffieHellman;
26
24
  var _createPublicKey = nodeCrypto.createPublicKey;
27
25
  var _createPrivateKey = nodeCrypto.createPrivateKey;
@@ -37,10 +35,21 @@ var frameworkError = require("./framework-error");
37
35
  var guard = require("./guard-all");
38
36
  var constants = require("./constants");
39
37
  var intrinsic = require("./guard-intrinsic");
38
+ /** @internal The promise a signature verdict is awaited through, built from the constructor captured at
39
+ * load. `Promise.resolve` builds from its RECEIVER, so reading the global binding at the call hands the
40
+ * construction to whatever that binding holds: one settling without awaiting its argument drops the
41
+ * self-check's rejection, and one settling truthy answers the verdict the caller reads. */
42
+ var _Promise = intrinsic.Promise;
43
+ var _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
44
+ /** @internal `verifyPop`'s verdict is the aggregate of the per-message results, so a replaced `all`
45
+ * that resolves a fabricated array reports an unverified proof of possession as verified. */
46
+ var _promiseAll = intrinsic.uncurry(intrinsic.promiseAll);
40
47
  var _hasOwn = intrinsic.hasOwn;
41
48
  var _isBuffer = intrinsic.isBuffer;
49
+ var _push = intrinsic.push, _String = intrinsic.String;
42
50
  var _stringify = intrinsic.stringify;
43
51
  var cms = require("./schema-cms");
52
+ var possession = require("./possession");
44
53
  var cmsEncrypt = require("./cms-encrypt");
45
54
  var key = require("./key");
46
55
  require("./path-validate");
@@ -77,7 +86,89 @@ var KNOWN_TEMPLATE_KEYS = intrinsic.assign(intrinsic.create(null), { version: 1,
77
86
  var REVOCATION_TEMPLATE_KEYS = intrinsic.assign(intrinsic.create(null), { version: 1, subject: 1, publicKey: 1, validity: 1, extensions: 1, issuer: 1, serialNumber: 1 });
78
87
  function _ctlUtf8(v) { return b.utf8(String(v)); }
79
88
  function _ctlSpki(v) { var k = _b.reqDer(v, "protocolEncrKey (an SPKI DER)"); _b.assertValidSpki(k, "protocolEncrKey"); return b.raw(k); }
80
- var CONTROL_VALUE = intrinsic.assign(intrinsic.create(null), { regToken: _ctlUtf8, authenticator: _ctlUtf8, oldCertID: function (v) { return _encodeCertId(v); }, protocolEncrKey: _ctlSpki });
89
+ /** @internal RFC 9883 sec. 5 gives its registration control the value of its PKCS#10 attribute,
90
+ * `regCtrl-privateKeyPossessionStatement ATTRIBUTE ::= privateKeyPossessionStatement` under
91
+ * `id-regCtrl-statementOfPossession ::= id-at-statementOfPossession`, so the control and the attribute
92
+ * are one OID carrying one structure. The structure itself is sec. 3. The value is built and then read
93
+ * back through the one statement reader, which is what applies the signer-names-the-certificate rule
94
+ * here rather than a second copy of it. */
95
+ function _ctlPossession(v) {
96
+ if (v === null || typeof v !== "object" || _isBuffer(v)) {
97
+ throw _err("crmf/bad-controls", "statementOfPossession must be an object { signer, certificate? }");
98
+ }
99
+ guard.identifier.assertKnownKeys(v, KNOWN_POSSESSION_CONTROL_KEYS, _err, "crmf/bad-controls",
100
+ "statementOfPossession has an unknown field: ");
101
+ var signer = v.signer;
102
+ if (signer === null || typeof signer !== "object" || _isBuffer(signer)) {
103
+ throw _err("crmf/bad-controls", "statementOfPossession needs a signer naming the signature certificate's issuer and serial (RFC 9883 sec. 3)");
104
+ }
105
+ var issuerRaw = (signer.issuer !== null && typeof signer.issuer === "object" && !_isBuffer(signer.issuer) &&
106
+ signer.issuer.bytes !== undefined) ? signer.issuer.bytes : signer.issuer;
107
+ var fields = [b.sequence([
108
+ b.raw(_b.reqDer(issuerRaw, "statementOfPossession signer.issuer (the signature certificate's issuer Name DER)")),
109
+ b.integer(guard.range.authoredInteger(signer.serialNumber, _err, "crmf/bad-controls", "statementOfPossession signer.serialNumber")),
110
+ ])];
111
+ if (v.certificate != null) _push(fields, b.raw(_b.reqDer(v.certificate, "statementOfPossession certificate")));
112
+ var der = b.sequence(fields);
113
+ try { possession.parse(der); }
114
+ catch (e) { throw _err("crmf/bad-controls", "statementOfPossession: " + ((e && e.message) || _String(e)), e); }
115
+ return b.raw(der);
116
+ }
117
+ var KNOWN_POSSESSION_CONTROL_KEYS = intrinsic.assign(intrinsic.create(null), { signer: 1, certificate: 1 });
118
+ /** @internal The RFC 9883 statement a parsed certReq carries, or null. The control value is raw DER, so
119
+ * it goes through the one statement reader; a value that reader refuses is reported as absent here and
120
+ * the verdict below says so, since a control this verifier cannot read is not a proof it can use. */
121
+ function _possessionControlOf(certReq) {
122
+ var controls = (certReq && certReq.controls) || [];
123
+ /** @internal A request carrying MORE THAN ONE statement is refused before any of it is read, the way
124
+ * `pki.possession.verifyRequest` refuses the PKCS#10 attribute. Taking the first and returning left
125
+ * every later statement unchecked: a request whose first statement is sound and whose second names a
126
+ * different certificate verified on the strength of the first. The controls are a sequence nothing
127
+ * orders for this purpose, and which one a CA should honor is stated nowhere, so naming the ambiguity
128
+ * is the only reading that does not check one statement and certify against another. */
129
+ var found = null, count = 0;
130
+ for (var i = 0; i < controls.length; i++) {
131
+ if (controls[i].type !== possession.OID_SOP) continue;
132
+ count += 1;
133
+ if (found === null) found = controls[i].value;
134
+ }
135
+ if (count > 1) {
136
+ throw _err("crmf/bad-controls", "this request carries " + count + " statementOfPossession controls, " +
137
+ "and which of them a CA should honor is not stated anywhere, so the RFC 9883 checks cannot be made " +
138
+ "against one of them alone");
139
+ }
140
+ if (found === null) return null;
141
+ try { return possession.parse(found); }
142
+ catch (_e) { return { signer: null, certificate: null }; }
143
+ }
144
+ /** @internal The signature certificate's key from an ALREADY-ENCODED control list, so the array form of
145
+ * `spec.controls` resolves the proof-of-possession algorithm the same way the object form does. The
146
+ * entries arrive as `_buildAttrTypeAndValues` decoded them, which is the point: that walk already held
147
+ * each one to being a `SEQUENCE { type OID, value }` with no duplicate type, so this one neither
148
+ * re-decodes nor re-validates. A second validator over the same bytes is the shape that put a
149
+ * fail-open skip here, where an entry that did not decode was passed over and the request was then
150
+ * reported as carrying no statement, leaving the algorithm resolved from the key being certified while
151
+ * the request traveled carrying the statement that was skipped. Returns null when the list carries
152
+ * none. */
153
+ function _encodedPossessionSpki(entries) {
154
+ for (var i = 0; i < entries.length; i++) {
155
+ if (entries[i].type !== possession.OID_SOP) continue;
156
+ var statement;
157
+ try { statement = possession.parse(entries[i].node.children[1].bytes); }
158
+ catch (e) {
159
+ throw _err("crmf/bad-controls",
160
+ "the pre-encoded statementOfPossession control does not decode as a PrivateKeyPossessionStatement", e);
161
+ }
162
+ if (statement.certificate == null) {
163
+ throw _err("crmf/bad-controls",
164
+ "statementOfPossession needs the certificate: this verb resolves the proof-of-possession algorithm from its key and checks the signature against it");
165
+ }
166
+ return x509.parse(statement.certificate).subjectPublicKeyInfo.bytes;
167
+ }
168
+ return null;
169
+ }
170
+
171
+ var CONTROL_VALUE = intrinsic.assign(intrinsic.create(null), { regToken: _ctlUtf8, authenticator: _ctlUtf8, oldCertID: function (v) { return _encodeCertId(v); }, protocolEncrKey: _ctlSpki, statementOfPossession: _ctlPossession });
81
172
  var REGINFO_VALUE = intrinsic.assign(intrinsic.create(null), { utf8Pairs: _ctlUtf8 });
82
173
 
83
174
 
@@ -115,7 +206,11 @@ function _encodeCertTemplate(tpl, opts) {
115
206
  return { der: b.sequence(fields), spki: spki, complete: tpl.subject != null && tpl.publicKey != null };
116
207
  }
117
208
 
118
- function _buildAttrTypeAndValues(spec, code, label, valueMap) {
209
+ /** @internal `collect`, when given, receives one `{ type, node }` per entry of the ARRAY form, in order.
210
+ * That is how a caller that needs an entry back reads it: this walk already decoded each one and held
211
+ * it to the shape, so a second walk would be a second validator of the same bytes, weaker than this one
212
+ * wherever the two drifted apart. */
213
+ function _buildAttrTypeAndValues(spec, code, label, valueMap, collect) {
119
214
  if (Array.isArray(spec)) {
120
215
  if (!spec.length) throw _err(code, label + " must carry at least one entry");
121
216
  var seenA = intrinsic.create(null);
@@ -128,6 +223,7 @@ function _buildAttrTypeAndValues(spec, code, label, valueMap) {
128
223
  try { t = asn1.read.oid(n.children[0]); } catch (err) { throw _err("crmf/bad-input", "pre-encoded " + label + " [" + i + "] type is not an OBJECT IDENTIFIER", err); }
129
224
  if (seenA[t]) throw _err(code, "duplicate " + label + " type " + (oid.name(t) || t));
130
225
  seenA[t] = true;
226
+ if (collect) guard.list.append(collect, { type: t, node: n });
131
227
  return b.raw(der);
132
228
  }));
133
229
  }
@@ -367,11 +463,11 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
367
463
  * digest the proof's own algorithm identifier names. */
368
464
  var popAlgOid = O("id-dhPop-static-HMAC-SHA1");
369
465
  var digest = DH_POP_DIGEST[popAlgOid];
370
- k = _createHash(digest).update(leading).update(zz).update(trailing).digest();
466
+ k = guard.crypto.digestParts(digest, [leading, zz, trailing]);
371
467
  /** @internal Ordinary HMAC-SHA1. RFC 2875 sec. 3(d) writes the construction correctly and then
372
468
  * labels its pads the wrong way round, calling 0x36 the outer pad and 0x5C the inner one, which is
373
469
  * the reverse of RFC 2104; the same sentence says the MAC is computed "as per [RFC2104]". */
374
- var mac = _createHmac(digest, k).update(certReqDer).digest();
470
+ var mac = guard.crypto.hmac(digest, k, certReqDer);
375
471
  /** @internal The BIT STRING carries the DhPopStatic SEQUENCE, not the raw MAC. RFC 2875 sec. 3
376
472
  * says so after the computation: "DhPopStatic is encoded as a BIT STRING and is the signature
377
473
  * value (i.e. encodes the above sequence instead of the raw output from 3d)". `issuerAndSerial`
@@ -868,7 +964,7 @@ function _assertEnclosedKeyMatches(privateKeyDer, templateSpki) {
868
964
  * cannot use it, so those are proven by USING the key against the template. */
869
965
  var enclosedAlg = key.algorithmOidAt(privateKeyDer, 1);
870
966
  if (enclosedAlg !== null && !key.isDhOid(enclosedAlg)) return _assertEnclosedKeyCorresponds(privateKeyDer, templateSpki);
871
- return Promise.resolve()
967
+ return _promiseResolve(_Promise, undefined)
872
968
  .then(function () { return key.publicFromPrivate(privateKeyDer); })
873
969
  .then(function (derivedSpki) {
874
970
  var derived = guard.bytes.view(derivedSpki, CrmfError, "crmf/bad-input", "the derived public key");
@@ -945,7 +1041,7 @@ function _assertPopArmKeys(pop, mode) {
945
1041
  });
946
1042
  }
947
1043
 
948
- function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
1044
+ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts, possessionSpki) {
949
1045
  if (pop != null && (typeof pop !== "object" || _isBuffer(pop))) throw _err("crmf/bad-input", "spec.pop must be an object (e.g. { type: 'signature' } or { type: 'raVerified', raVerified: true })");
950
1046
  var mode = (pop && pop.type) || (signingKey != null ? "signature" : null);
951
1047
  if (pop != null) _assertPopArmKeys(pop, mode);
@@ -961,7 +1057,13 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
961
1057
  }
962
1058
  if (signingKey == null) throw _err("crmf/bad-input", "a signature proof of possession requires the requester's private key");
963
1059
  if (template.spki == null) throw _err("crmf/bad-input", "a signature proof of possession requires certTemplate.publicKey");
964
- var scheme = signScheme.resolveSignScheme(_b.certLikeFromSpki(template.spki), { combinedRsaSig: true, pss: opts.pss, digestAlgorithm: opts.digestAlgorithm }, true, _signE);
1060
+ /** @internal Normally the requested key signs, which is what makes a signature POP a proof of
1061
+ * possession OF that key. With an RFC 9883 statement of possession the signature is made by the key
1062
+ * of the certificate the statement names, because the requested key is a key-establishment key and
1063
+ * cannot sign. The relaxation is scoped to a request carrying the control, so a request without one
1064
+ * is still held to signing with the key it asks to have certified. */
1065
+ var provingSpki = possessionSpki == null ? template.spki : possessionSpki;
1066
+ var scheme = signScheme.resolveSignScheme(_b.certLikeFromSpki(provingSpki), { combinedRsaSig: true, pss: opts.pss, digestAlgorithm: opts.digestAlgorithm }, true, _signE);
965
1067
  var signedRegion, poposkInputField = null;
966
1068
  if (template.complete) {
967
1069
  signedRegion = certReqDer;
@@ -973,7 +1075,7 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
973
1075
  poposkInputField = b.implicit(0, poposkSeq);
974
1076
  }
975
1077
  return signScheme.signOverTbs(scheme, signingKey, signedRegion, _signE).then(function (sig) {
976
- return Promise.resolve(_b.assertSignatureVerifies(signedRegion, sig, template.spki, scheme)).then(function () {
1078
+ return _promiseResolve(_Promise, _b.assertSignatureVerifies(signedRegion, sig, provingSpki, scheme)).then(function () {
977
1079
  var popoChildren = [];
978
1080
  if (poposkInputField) popoChildren.push(poposkInputField);
979
1081
  popoChildren.push(scheme.sigAlgId);
@@ -1068,10 +1170,33 @@ function _buildCertReqMsg(spec, key, opts) {
1068
1170
  var signingKey = (key && typeof key === "object" && !_isBuffer(key) && !(key instanceof Uint8Array) && key.type == null && "key" in key) ? key.key : key;
1069
1171
  var template = _encodeCertTemplate(spec.certTemplate);
1070
1172
  var certReqChildren = [b.integer(_certReqId(spec.certReqId)), template.der];
1071
- if (spec.controls != null) certReqChildren.push(_buildAttrTypeAndValues(spec.controls, "crmf/bad-controls", "controls", CONTROL_VALUE));
1173
+ var encodedControls = [];
1174
+ if (spec.controls != null) certReqChildren.push(_buildAttrTypeAndValues(spec.controls, "crmf/bad-controls", "controls", CONTROL_VALUE, encodedControls));
1072
1175
  var certReqDer = b.sequence(certReqChildren);
1073
1176
 
1074
- return Promise.resolve(_buildProofOfPossession(spec.pop, certReqDer, template, signingKey, opts)).then(function (popoDer) {
1177
+ /** @internal The key the RFC 9883 control names, when the request carries one: that key makes the
1178
+ * proof of possession rather than the requested key. The certificate is required in the control for
1179
+ * the same reason it is in the PKCS#10 spec field -- this verb has to know which key signs. */
1180
+ var possessionSpki = null;
1181
+ if (spec.controls != null && !intrinsic.isArray(spec.controls) && spec.controls.statementOfPossession != null) {
1182
+ var sopCert = spec.controls.statementOfPossession.certificate;
1183
+ if (sopCert == null) {
1184
+ throw _err("crmf/bad-controls",
1185
+ "statementOfPossession needs the certificate: this verb resolves the proof-of-possession algorithm from its key and checks the signature against it");
1186
+ }
1187
+ possessionSpki = x509.parse(_b.reqDer(sopCert, "statementOfPossession certificate")).subjectPublicKeyInfo.bytes;
1188
+ } else if (spec.controls != null && intrinsic.isArray(spec.controls)) {
1189
+ /** @internal The ARRAY form carries controls already encoded, and it reaches the same resolution. Read
1190
+ * only through the object form, a pre-encoded statement was invisible here and the signing algorithm
1191
+ * was resolved from the REQUESTED key instead of from the signature certificate: an X25519 request
1192
+ * carrying a perfectly good encoded statement was refused `crmf/unsupported-algorithm`, while the
1193
+ * object form of the same request succeeded. One control, two spellings, and the rule has to hold for
1194
+ * both. The entries are the ones the builder above decoded, so the statement is found without a
1195
+ * second walk over the same bytes, and read back through the one statement reader. */
1196
+ possessionSpki = _encodedPossessionSpki(encodedControls);
1197
+ }
1198
+
1199
+ return _promiseResolve(_Promise, _buildProofOfPossession(spec.pop, certReqDer, template, signingKey, opts, possessionSpki)).then(function (popoDer) {
1075
1200
  var msgChildren = [certReqDer];
1076
1201
  if (popoDer) msgChildren.push(popoDer);
1077
1202
  if (spec.regInfo != null) msgChildren.push(_buildAttrTypeAndValues(spec.regInfo, "crmf/bad-reg-info", "regInfo", REGINFO_VALUE));
@@ -1093,7 +1218,7 @@ function _build(spec, key, opts) {
1093
1218
  specs = [spec];
1094
1219
  }
1095
1220
  if (!specs.length) throw _err("crmf/bad-input", "at least one certificate request message is required (RFC 4211 sec. 3)");
1096
- return Promise.all(specs.map(function (s) { return _buildCertReqMsg(s, key, opts); })).then(function (msgs) {
1221
+ return _promiseAll(_Promise, specs.map(function (s) { return _buildCertReqMsg(s, key, opts); })).then(function (msgs) {
1097
1222
  var der = b.sequence(msgs);
1098
1223
  if (opts.pem != null) {
1099
1224
  if (typeof opts.pem !== "string" || !opts.pem) throw _err("crmf/bad-input", "opts.pem must be a non-empty PEM label string");
@@ -1137,7 +1262,7 @@ function _verifyOne(msg) {
1137
1262
  };
1138
1263
  function settle(extra) {
1139
1264
  var v = guard.verdict.of(base, extra);
1140
- return Promise.resolve(guard.verdict.of(v, { valid: v.verified === true }));
1265
+ return _promiseResolve(_Promise, guard.verdict.of(v, { valid: v.verified === true }));
1141
1266
  }
1142
1267
 
1143
1268
  var popo = msg.popo;
@@ -1158,6 +1283,33 @@ function _verifyOne(msg) {
1158
1283
  var preimage = pin ? pin.signedBytes : msg.certReq.certReqBytes;
1159
1284
  var spki = pin ? pin.publicKey : base.publicKey;
1160
1285
  var bound = !pin;
1286
+
1287
+ /** @internal RFC 9883 sec. 5, which states the CRMF conventions: a request carrying a statement of
1288
+ * possession has "the signature over the poposkInput MUST validate with the public key in the
1289
+ * signature certificate", so it is signed by the key of the
1290
+ * certificate the statement names, not by the key being requested, so the proof is checked against
1291
+ * that key. It therefore proves possession of the SIGNATURE key and not of the requested one, which
1292
+ * is why `subjectBound` is false here however complete the template is: the requested
1293
+ * key-establishment key cannot sign, and nothing in this message demonstrates possession of it. What
1294
+ * the statement buys is a signature the CA can attribute to the same entity, and the remaining
1295
+ * checks RFC 9883 states are `pki.possession.verifyRequest`'s, over a PKCS#10 request. */
1296
+ var sop = _possessionControlOf(msg.certReq);
1297
+ if (sop !== null) {
1298
+ if (sop.certificate === null) {
1299
+ return settle({ verified: false, method: "signature", cryptographicallyVerified: false,
1300
+ subjectBound: false,
1301
+ reason: "the statementOfPossession control omits its certificate, so the key that signed this request is not in the message (RFC 9883 sec. 3)" });
1302
+ }
1303
+ var sopSpki;
1304
+ try { sopSpki = x509.parse(sop.certificate).subjectPublicKeyInfo.bytes; }
1305
+ catch (_e) {
1306
+ return settle({ verified: false, method: "signature", cryptographicallyVerified: false,
1307
+ subjectBound: false,
1308
+ reason: "the statementOfPossession control's certificate is not readable, so the key that signed this request cannot be recovered" });
1309
+ }
1310
+ spki = sopSpki;
1311
+ bound = false;
1312
+ }
1161
1313
  return crmfVerify.verifyPopSignature(popo, spki, preimage).then(function (ok) {
1162
1314
  return guard.verdict.of(base, {
1163
1315
  valid: ok === true, verified: ok === true, method: "signature", cryptographicallyVerified: ok === true,
@@ -1216,7 +1368,7 @@ function _verifyOne(msg) {
1216
1368
  function verifyPop(messages) { return guard.async.deferred(function () { return _verifyPop(messages); }); }
1217
1369
  function _verifyPop(messages) {
1218
1370
  var parsed = _coerceMessages(messages);
1219
- return Promise.all(parsed.messages.map(_verifyOne)).then(function (out) {
1371
+ return _promiseAll(_Promise, parsed.messages.map(_verifyOne)).then(function (out) {
1220
1372
  var allVerified = out.length > 0 && out.every(function (m) { return m.verified === true; });
1221
1373
  return guard.verdict.of({ valid: allVerified, verified: allVerified, messages: out });
1222
1374
  });