@blamejs/pki 0.6.14 → 0.6.16

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 (112) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/lib/acme.js +59 -815
  3. package/lib/asn1-der.js +7 -245
  4. package/lib/attrcert-sign.js +12 -260
  5. package/lib/attrcert-verify.js +1 -7
  6. package/lib/byte-reader.js +0 -8
  7. package/lib/byte-writer.js +0 -11
  8. package/lib/cbor-det.js +0 -54
  9. package/lib/cmc-build.js +15 -357
  10. package/lib/cmc-verify.js +6 -239
  11. package/lib/cmp-build.js +41 -317
  12. package/lib/cmp-session.js +73 -760
  13. package/lib/cmp-verify.js +32 -488
  14. package/lib/cms-compress.js +3 -32
  15. package/lib/cms-decrypt.js +29 -294
  16. package/lib/cms-encrypt.js +14 -234
  17. package/lib/cms-sign.js +29 -324
  18. package/lib/cms-verify.js +9 -500
  19. package/lib/composite-kem.js +2 -102
  20. package/lib/composite-sig.js +1 -77
  21. package/lib/constants.js +0 -249
  22. package/lib/crl-sign.js +9 -237
  23. package/lib/crl-verify.js +1 -10
  24. package/lib/crmf-sign.js +19 -198
  25. package/lib/crmf-verify.js +1 -12
  26. package/lib/csr-sign.js +3 -34
  27. package/lib/csr-verify.js +1 -12
  28. package/lib/ct.js +28 -253
  29. package/lib/edwards-point.js +10 -44
  30. package/lib/est.js +28 -764
  31. package/lib/framework-error.js +4 -182
  32. package/lib/guard-all.js +1 -58
  33. package/lib/guard-async.js +1 -36
  34. package/lib/guard-bytes.js +6 -650
  35. package/lib/guard-compress.js +5 -102
  36. package/lib/guard-crypto.js +1 -40
  37. package/lib/guard-der.js +2 -93
  38. package/lib/guard-encoding.js +2 -60
  39. package/lib/guard-header.js +1 -36
  40. package/lib/guard-identifier.js +7 -531
  41. package/lib/guard-intrinsic.js +1 -150
  42. package/lib/guard-json.js +10 -73
  43. package/lib/guard-limits.js +1 -77
  44. package/lib/guard-list.js +1 -49
  45. package/lib/guard-name.js +3 -209
  46. package/lib/guard-parsed.js +7 -335
  47. package/lib/guard-range.js +1 -71
  48. package/lib/guard-secret.js +1 -102
  49. package/lib/guard-text.js +1 -64
  50. package/lib/guard-time.js +1 -97
  51. package/lib/hpke.js +3 -105
  52. package/lib/http-digest.js +14 -142
  53. package/lib/http-retry-after.js +1 -47
  54. package/lib/http-transport.js +21 -131
  55. package/lib/inspect.js +30 -284
  56. package/lib/ip-utils.js +6 -47
  57. package/lib/jose.js +7 -142
  58. package/lib/key.js +2 -67
  59. package/lib/lint.js +9 -194
  60. package/lib/merkle.js +1 -27
  61. package/lib/mime.js +9 -82
  62. package/lib/ocsp-request-verify.js +1 -14
  63. package/lib/ocsp-verify.js +1 -82
  64. package/lib/ocsp.js +11 -169
  65. package/lib/oid.js +5 -337
  66. package/lib/path-validate.js +103 -1492
  67. package/lib/pbes2.js +2 -89
  68. package/lib/pkcs12-build.js +31 -379
  69. package/lib/pki-build.js +64 -290
  70. package/lib/rc2.js +2 -18
  71. package/lib/rfc3339.js +4 -27
  72. package/lib/scep.js +140 -286
  73. package/lib/schema-all.js +0 -122
  74. package/lib/schema-attrcert.js +4 -146
  75. package/lib/schema-c509.js +153 -802
  76. package/lib/schema-cmc.js +5 -220
  77. package/lib/schema-cmp.js +2 -223
  78. package/lib/schema-cms.js +13 -427
  79. package/lib/schema-crl.js +5 -68
  80. package/lib/schema-crmf.js +6 -167
  81. package/lib/schema-csr.js +1 -54
  82. package/lib/schema-csrattrs.js +2 -87
  83. package/lib/schema-engine.js +0 -152
  84. package/lib/schema-ocsp.js +0 -117
  85. package/lib/schema-pkcs12.js +2 -93
  86. package/lib/schema-pkcs8.js +3 -35
  87. package/lib/schema-pkix.js +24 -655
  88. package/lib/schema-smime.js +0 -54
  89. package/lib/schema-tsp.js +3 -99
  90. package/lib/schema-x509.js +4 -63
  91. package/lib/shbs.js +11 -95
  92. package/lib/sign-scheme.js +2 -97
  93. package/lib/sigstore.js +9 -169
  94. package/lib/sleep.js +2 -8
  95. package/lib/smime.js +15 -408
  96. package/lib/tls-cert-compress.js +0 -62
  97. package/lib/trust.js +19 -185
  98. package/lib/tsp-sign.js +16 -270
  99. package/lib/validator-all.js +1 -30
  100. package/lib/validator-attcert.js +2 -46
  101. package/lib/validator-cose.js +2 -153
  102. package/lib/validator-keydesc.js +1 -37
  103. package/lib/validator-sig.js +1 -46
  104. package/lib/validator-tls.js +1 -88
  105. package/lib/validator-tpm.js +19 -122
  106. package/lib/verify-seam.js +1 -24
  107. package/lib/webauthn-mds.js +3 -400
  108. package/lib/webauthn.js +12 -833
  109. package/lib/webcrypto.js +5 -485
  110. package/lib/x509-sign.js +4 -108
  111. package/package.json +1 -1
  112. package/sbom.cdx.json +6 -6
package/lib/cmp-build.js CHANGED
@@ -20,16 +20,6 @@
20
20
  * @spec RFC 9810
21
21
  * @card Build a CMP PKIMessage with signature or PBMAC1 protection.
22
22
  */
23
- //
24
- // RFC 9810 Appendix A is DEFINITIONS EXPLICIT TAGS: every context tag in the PKIMessage envelope
25
- // (each PKIHeader [0..8] optional, every PKIBody [n] arm, protection [0], extraCerts [1]) is an EXPLICIT
26
- // wrapper (asn1.build.explicit), the exact inverse of the RFC 4211 CRMF interior (IMPLICIT TAGS). The two
27
- // IMPLICIT islands are foreign imports produced wholesale by pki.crmf.build (the CertReqMessages /
28
- // CertTemplate interior) and embedded verbatim, never re-encoded here. Protection is computed over the
29
- // byte-exact DER of the virtual ProtectedPart ::= SEQUENCE { header, body }, built from the same header
30
- // and body TLVs placed in the envelope. The signature scheme resolves from the sender key through the
31
- // shared sign-scheme registry; the Name / GeneralName / SPKI encoders and the post-sign self-check are the
32
- // shared lib/pki-build primitives, bound to the cmp namespace.
33
23
 
34
24
  var asn1 = require("./asn1-der");
35
25
  var schema = require("./schema-engine");
@@ -38,11 +28,7 @@ var cmp = require("./schema-cmp");
38
28
  var crmf = require("./crmf-sign");
39
29
  var schemaCrmf = require("./schema-crmf");
40
30
  var intrinsic = require("./guard-intrinsic");
41
- // Own-key membership through an operation taken at load: it decides which proof-of-possession
42
- // methods couple the message to a protocol version, and an inherited name is one no RFC defined.
43
31
  var _hasOwn = intrinsic.hasOwn;
44
- // The same load-bound treatment for the operations that shape a revocation request and a support
45
- // message: each decides what reaches the wire, so a replaced built-in must not get a say.
46
32
  var _isArray = intrinsic.isArray;
47
33
  var _isBuffer = intrinsic.isBuffer;
48
34
  var _charAt = intrinsic.uncurry(String.prototype.charAt);
@@ -56,12 +42,10 @@ var _forEach = intrinsic.forEach;
56
42
  var _push = intrinsic.push;
57
43
  var _stringify = intrinsic.stringify;
58
44
  var _bigInt = intrinsic.BigInt;
59
- // The constructor taken at load: re-materializing an instant that is about to be encoded must not
60
- // go through a Date a caller could have replaced after this module was read.
61
45
  var _Date = intrinsic.Date;
62
46
  var csr = require("./schema-csr");
63
- require("./path-validate"); // side-effect: path-validate injects its signature engine into csr-verify at load
64
- var csrVerify = require("./csr-verify"); // verify an embedded p10cr request's proof-of-possession before sending it
47
+ require("./path-validate");
48
+ var csrVerify = require("./csr-verify");
65
49
  var crl = require("./schema-crl");
66
50
  var x509 = require("./schema-x509");
67
51
  var signScheme = require("./sign-scheme");
@@ -92,23 +76,17 @@ var KNOWN_HEADER_KEYS = {
92
76
  transactionID: 1, senderNonce: 1, recipNonce: 1, freeText: 1, generalInfo: 1,
93
77
  };
94
78
  var KNOWN_BODY_KEYS = {
95
- ir: 1, cr: 1, kur: 1, krr: 1, ccr: 1, p10cr: 1, certConf: 1, pollReq: 1, genm: 1, rr: 1, // request-side
96
- ip: 1, cp: 1, kup: 1, ccp: 1, krp: 1, rp: 1, genp: 1, error: 1, pollRep: 1, pkiconf: 1, // CA/responder-side
97
- nested: 1, // an RA wrapper of complete, independently-protected PKIMessages (RFC 9810 sec. 5.1.3.5)
79
+ ir: 1, cr: 1, kur: 1, krr: 1, ccr: 1, p10cr: 1, certConf: 1, pollReq: 1, genm: 1, rr: 1,
80
+ ip: 1, cp: 1, kup: 1, ccp: 1, krp: 1, rp: 1, genp: 1, error: 1, pollRep: 1, pkiconf: 1,
81
+ nested: 1,
98
82
  };
99
- // The PKIBody arm -> its EXPLICIT context tag number (schema-cmp.js BODY_ARMS). rr is [11], not [15].
100
83
  var BODY_TAG = {
101
84
  ir: 0, cr: 2, p10cr: 4, kur: 7, krr: 9, ccr: 13, rr: 11, genm: 21, certConf: 24, pollReq: 25,
102
85
  ip: 1, cp: 3, kup: 8, krp: 10, rp: 12, ccp: 14, pkiconf: 19, genp: 22, error: 23, pollRep: 26,
103
- nested: 20, // NestedMessageContent (schema-cmp.js BODY_ARMS [20])
86
+ nested: 20,
104
87
  };
105
- // arms whose content is a CertReqMessages via pki.crmf.build (RFC 9810 sec. 5.3.7 krr, sec. 5.3.11 ccr).
106
88
  var CRMF_BODY = { ir: 1, cr: 1, kur: 1, krr: 1, ccr: 1 };
107
- var CERT_REP_ARM = { ip: 1, cp: 1, kup: 1, ccp: 1 }; // arms carrying a CertRepMessage
108
- // Protection has two mutually exclusive forms, and the signature parameters are read only by the
109
- // signature one: under PBMAC1 a requested pss or digestAlgorithm selects nothing and the message
110
- // goes out MAC'd under the default PRF, byte-identical to the call that never named them. The union
111
- // stays in force where the form is ambiguous, so _resolveProtection keeps its both/neither verdicts.
89
+ var CERT_REP_ARM = { ip: 1, cp: 1, kup: 1, ccp: 1 };
112
90
  var KNOWN_OPTS_KEYS = { key: 1, cert: 1, mac: 1, extraCerts: 1, pem: 1, pss: 1, digestAlgorithm: 1 };
113
91
  var KNOWN_OPTS_SIG_KEYS = { key: 1, cert: 1, extraCerts: 1, pem: 1, pss: 1, digestAlgorithm: 1 };
114
92
  var KNOWN_OPTS_MAC_KEYS = { mac: 1, extraCerts: 1, pem: 1 };
@@ -117,16 +95,14 @@ var KNOWN_MAC_KEYS = { secret: 1, salt: 1, iterationCount: 1, prf: 1, keyLength:
117
95
 
118
96
  var PBMAC1_DEFAULT_ITER = 100000;
119
97
  var PBMAC1_DEFAULT_SALT_BYTES = 16;
120
- var PBMAC1_MIN_SALT = 8; // octets -- RFC 8018 sec. 4.1 (64-bit) floor; matches the pki.cmp.verify verifier floor
121
- var PBMAC1_DEFAULT_KEYLEN = 32; // bytes -- HMAC-SHA256 key
122
- var PBMAC1_MIN_KEYLEN = 20; // bytes -- RFC 9579 sec. 9 floor (matches the pki.cmp.verify verifier floor)
123
- var PBMAC1_MAX_KEYLEN = 1024; // bytes -- an HMAC key beyond a hash block is pointless; bound the work
124
- var PBMAC1_MIN_ITER = 1000; // RFC 8018 sec. 4.2 recommended minimum -- reject a trivially weak count
98
+ var PBMAC1_MIN_SALT = 8;
99
+ var PBMAC1_DEFAULT_KEYLEN = 32;
100
+ var PBMAC1_MIN_KEYLEN = 20;
101
+ var PBMAC1_MAX_KEYLEN = 1024;
102
+ var PBMAC1_MIN_ITER = 1000;
125
103
  var PBMAC1_PRF = { "SHA-256": "hmacWithSHA256", "SHA-384": "hmacWithSHA384", "SHA-512": "hmacWithSHA512" };
126
104
  var PBMAC1_MAC_OID = { "SHA-256": "hmacWithSHA256", "SHA-384": "hmacWithSHA384", "SHA-512": "hmacWithSHA512" };
127
- var PBMAC1_PRF_HLEN = { "SHA-256": 32, "SHA-384": 48, "SHA-512": 64 }; // PBKDF2 PRF output length (one derived block)
128
- // PKIFailureInfo named bits (RFC 9810 sec. 5.2.3), position = bit index, mirroring schema-cmp's decode list;
129
- // the build -> parse round-trip cross-checks the positions against the parser.
105
+ var PBMAC1_PRF_HLEN = { "SHA-256": 32, "SHA-384": 48, "SHA-512": 64 };
130
106
  var FAIL_INFO_NAMES = ["badAlg", "badMessageCheck", "badRequest", "badTime", "badCertId", "badDataFormat",
131
107
  "wrongAuthority", "incorrectData", "missingTimeStamp", "badPOP", "certRevoked", "certConfirmed",
132
108
  "wrongIntegrity", "badRecipientNonce", "timeNotAvailable", "unacceptedPolicy", "unacceptedExtension",
@@ -134,17 +110,14 @@ var FAIL_INFO_NAMES = ["badAlg", "badMessageCheck", "badRequest", "badTime", "ba
134
110
  "unsupportedVersion", "notAuthorized", "systemUnavail", "systemFailure", "duplicateCertReq"];
135
111
  var FAIL_INFO_INDEX = {};
136
112
  FAIL_INFO_NAMES.forEach(function (n, i) { FAIL_INFO_INDEX[n] = i; });
137
- // CertStatus.hashAlg names the hash used to compute certHash, restricted to hash algorithms and not any OID.
138
113
  var CERT_CONF_HASH_ALGS = { sha1: 1, sha256: 1, sha384: 1, sha512: 1, "sha3-256": 1, "sha3-512": 1 };
139
114
 
140
- // ---- small shared encoders (byte-exact inverses of schema-cmp.js readers) ----
141
115
 
142
116
  function _reqOctets(v, what) {
143
117
  var buf = _b.reqDer(v, what);
144
118
  return b.octetString(buf);
145
119
  }
146
120
 
147
- // PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String: non-empty, every element UTF8String.
148
121
  function _encodePkiFreeText(strings, code, what) {
149
122
  if (!Array.isArray(strings) || !strings.length) throw _err(code, what + " must be a non-empty array of strings");
150
123
  return b.sequence(strings.map(function (s) {
@@ -153,9 +126,6 @@ function _encodePkiFreeText(strings, code, what) {
153
126
  }));
154
127
  }
155
128
 
156
- // A recognized fixed-syntax id-it value (RFC 9810 sec. 5.1.1.1/.2/.4): implicitConfirm -> NULL,
157
- // confirmWaitTime -> GeneralizedTime, certProfile -> non-empty SEQUENCE OF UTF8String. An unknown id-it
158
- // carries a pre-encoded infoValue DER (or none).
159
129
  function _encodeInfoValue(itav) {
160
130
  var name = itav.infoType;
161
131
  if (name === "implicitConfirm") {
@@ -163,7 +133,7 @@ function _encodeInfoValue(itav) {
163
133
  return b.nullValue();
164
134
  }
165
135
  if (name === "confirmWaitTime") {
166
- guard.time.assertValid(itav.infoValue, _err, "cmp/bad-info-value", "confirmWaitTime infoValue"); // reject a non-Date / Invalid Date
136
+ guard.time.assertValid(itav.infoValue, _err, "cmp/bad-info-value", "confirmWaitTime infoValue");
167
137
  return b.generalizedTime(itav.infoValue);
168
138
  }
169
139
  if (name === "certProfile") {
@@ -173,7 +143,6 @@ function _encodeInfoValue(itav) {
173
143
  return b.raw(_b.reqDer(itav.infoValue, "infoValue (a pre-encoded DER value)"));
174
144
  }
175
145
 
176
- // InfoTypeAndValue ::= SEQUENCE { infoType OID, infoValue ANY DEFINED BY infoType OPTIONAL }.
177
146
  function _encodeInfoTypeAndValue(itav) {
178
147
  if (!itav || typeof itav !== "object" || Buffer.isBuffer(itav)) throw _err("cmp/bad-info-value", "an InfoTypeAndValue must be an object { infoType, infoValue? }");
179
148
  if (typeof itav.infoType !== "string" || O(itav.infoType) == null) throw _err("cmp/bad-name", "unknown infoType " + JSON.stringify(itav.infoType));
@@ -183,20 +152,14 @@ function _encodeInfoTypeAndValue(itav) {
183
152
  return b.sequence(children);
184
153
  }
185
154
 
186
- // generalInfo [8] / genm content: SEQUENCE SIZE (1..MAX) OF InfoTypeAndValue.
187
155
  function _encodeGeneralInfo(itavs, code, what) {
188
156
  if (!Array.isArray(itavs) || !itavs.length) throw _err(code, what + " must be a non-empty array of InfoTypeAndValue");
189
157
  return b.sequence(itavs.map(_encodeInfoTypeAndValue));
190
158
  }
191
159
 
192
- // ---- PKIHeader ----
193
160
 
194
- // An AlgorithmIdentifier { algorithm OID, parameters ANY OPTIONAL } for a bare/absent-params digest.
195
161
  function _algIdNoParams(name) { return b.sequence([b.oid(O(name))]); }
196
162
 
197
- // PKIHeader ::= SEQUENCE { pvno INTEGER, sender GeneralName, recipient GeneralName, EXPLICIT [0..8] }.
198
- // protectionAlgDer is the DERIVED AlgorithmIdentifier ([1]), present iff the message is protected. Returns
199
- // the exact headerTLV, built ONCE and reused in both the envelope and the ProtectedPart (RFC 9810 5.1.3).
200
163
  function _encodeHeader(headerSpec, protectionAlgDer, pvno) {
201
164
  if (!headerSpec || typeof headerSpec !== "object" || Buffer.isBuffer(headerSpec)) throw _err("cmp/bad-input", "message.header must be an object");
202
165
  guard.identifier.assertKnownKeys(headerSpec, KNOWN_HEADER_KEYS, _err, "cmp/bad-input", "unknown header field ");
@@ -208,10 +171,9 @@ function _encodeHeader(headerSpec, protectionAlgDer, pvno) {
208
171
  _b.encodeGeneralName(headerSpec.sender),
209
172
  _b.encodeGeneralName(headerSpec.recipient),
210
173
  ];
211
- // EXPLICIT [0..8] optionals, ascending, at most once each.
212
174
  if (headerSpec.messageTime != null) {
213
- guard.time.assertValid(headerSpec.messageTime, _err, "cmp/bad-input", "header.messageTime"); // reject a non-Date / Invalid Date
214
- children.push(b.explicit(0, b.generalizedTime(headerSpec.messageTime))); // GeneralizedTime ONLY (never UTCTime)
175
+ guard.time.assertValid(headerSpec.messageTime, _err, "cmp/bad-input", "header.messageTime");
176
+ children.push(b.explicit(0, b.generalizedTime(headerSpec.messageTime)));
215
177
  }
216
178
  if (protectionAlgDer != null) children.push(b.explicit(1, b.raw(protectionAlgDer)));
217
179
  if (headerSpec.senderKID != null) children.push(b.explicit(2, _reqOctets(headerSpec.senderKID, "header.senderKID")));
@@ -224,13 +186,9 @@ function _encodeHeader(headerSpec, protectionAlgDer, pvno) {
224
186
  return b.sequence(children);
225
187
  }
226
188
 
227
- // ---- PKIBody arm content encoders ----
228
189
 
229
- // A certReqId is an UNBOUNDED INTEGER (RFC 9483 -1 sentinel, no upper bound): accept a safe-integer number
230
- // or a bigint (a large value beyond 2^53), reject a non-integer / other type. Returns a BigInt for b.integer.
231
190
  function _reqIdInt(v, code, what) { return guard.range.authoredInteger(v, _err, code, what); }
232
191
 
233
- // PKIFailureInfo ::= BIT STRING (named bits, RFC 9810 sec. 5.2.3): a minimal NamedBitList from bit names.
234
192
  function _encodeFailInfo(names, code) {
235
193
  if (!Array.isArray(names)) throw _err(code, "statusInfo.failInfo must be an array of PKIFailureInfo bit names");
236
194
  return b.namedBitString(names.map(function (n) {
@@ -239,25 +197,21 @@ function _encodeFailInfo(names, code) {
239
197
  }));
240
198
  }
241
199
 
242
- // PKIStatusInfo ::= SEQUENCE { status PKIStatus INTEGER, statusString PKIFreeText OPTIONAL, failInfo BIT STRING
243
- // OPTIONAL }. `code` is the caller's typed error (default cmp/bad-status-info; certConf passes cmp/bad-cert-status).
244
200
  function _encodePkiStatusInfo(si, code) {
245
201
  code = code || "cmp/bad-status-info";
246
202
  if (!si || typeof si !== "object" || Buffer.isBuffer(si)) throw _err(code, "a PKIStatusInfo must be an object { status, statusString?, failInfo? }");
247
203
  if (typeof si.status !== "number" || !Number.isInteger(si.status) || si.status < 0 || si.status > 6) throw _err(code, "statusInfo.status must be a PKIStatus 0..6 (RFC 9810 sec. 5.2.3)");
248
204
  var children = [b.integer(BigInt(si.status))];
249
205
  if (si.statusString != null) children.push(_encodePkiFreeText(si.statusString, code, "statusInfo.statusString"));
250
- if (si.failInfo != null) children.push(_encodeFailInfo(si.failInfo, code)); // strict order: status -> statusString -> failInfo (both optionals untagged)
206
+ if (si.failInfo != null) children.push(_encodeFailInfo(si.failInfo, code));
251
207
  return b.sequence(children);
252
208
  }
253
209
 
254
- // CertStatus ::= SEQUENCE { certHash OCTET STRING, certReqId INTEGER, statusInfo PKIStatusInfo OPTIONAL,
255
- // hashAlg [0] EXPLICIT AlgorithmIdentifier OPTIONAL }. hashAlg present => cmp2021 (pvno bump).
256
210
  function _encodeCertStatus(cs, state) {
257
211
  if (!cs || typeof cs !== "object" || Buffer.isBuffer(cs)) throw _err("cmp/bad-cert-status", "each CertStatus must be an object");
258
212
  var children = [
259
213
  _reqOctets(cs.certHash, "certConf certHash"),
260
- b.integer(_reqIdInt(cs.certReqId, "cmp/bad-cert-status", "CertStatus certReqId")), // signed, -1 legal, unbounded
214
+ b.integer(_reqIdInt(cs.certReqId, "cmp/bad-cert-status", "CertStatus certReqId")),
261
215
  ];
262
216
  if (cs.statusInfo != null) children.push(_encodePkiStatusInfo(cs.statusInfo, "cmp/bad-cert-status"));
263
217
  if (cs.hashAlg != null) {
@@ -268,13 +222,11 @@ function _encodeCertStatus(cs, state) {
268
222
  return b.sequence(children);
269
223
  }
270
224
 
271
- // CertConfirmContent ::= SEQUENCE OF CertStatus (empty legal, no SIZE floor).
272
225
  function _encodeCertConfirmContent(list, state) {
273
226
  if (!Array.isArray(list)) throw _err("cmp/bad-cert-status", "certConf must be an array of CertStatus");
274
227
  return b.sequence(list.map(function (cs) { return _encodeCertStatus(cs, state); }));
275
228
  }
276
229
 
277
- // PollReqContent ::= SEQUENCE OF SEQUENCE { certReqId INTEGER } (-1 legal).
278
230
  function _encodePollReqContent(list) {
279
231
  if (!Array.isArray(list)) throw _err("cmp/bad-poll-req", "pollReq must be an array of { certReqId }");
280
232
  return b.sequence(list.map(function (pr) {
@@ -283,26 +235,19 @@ function _encodePollReqContent(list) {
283
235
  }));
284
236
  }
285
237
 
286
- // GenMsgContent ::= SEQUENCE OF InfoTypeAndValue (empty legal).
287
238
  function _encodeGenMsgContent(list) {
288
239
  if (!Array.isArray(list)) throw _err("cmp/bad-info-type-and-value", "genm must be an array of InfoTypeAndValue");
289
240
  return b.sequence(list.map(_encodeInfoTypeAndValue));
290
241
  }
291
242
 
292
- // RevReqContent ::= SEQUENCE OF RevDetails { certDetails CertTemplate, crlEntryDetails Extensions OPTIONAL }.
293
243
  function _encodeRevDetails(rd) {
294
244
  if (!rd || typeof rd !== "object" || Buffer.isBuffer(rd)) throw _err("cmp/bad-rev-req", "each RevDetails must be an object { certDetails, crlEntryDetails? }");
295
245
  guard.identifier.assertKnownKeys(rd, KNOWN_REV_DETAILS_KEYS, _err, "cmp/bad-rev-req", "unknown RevDetails field ");
296
246
  if (rd.certDetails == null) throw _err("cmp/bad-rev-req", "RevDetails.certDetails (a CertTemplate) is required");
297
247
  var certDetails;
298
248
  if (guard.bytes.isByteSource(rd.certDetails)) {
299
- // The pre-encoded escape hatch: normalized through the byte guard so every BufferSource the guard
300
- // accepts (Buffer, any typed-array view, DataView, raw ArrayBuffer) is taken, not a narrowed subset
301
- // that would drop a caller's container into the { issuer, serialNumber } branch below.
302
249
  certDetails = b.raw(guard.bytes.snapshotSource(rd.certDetails, CmpError, "cmp/bad-rev-req", "RevDetails.certDetails (a pre-encoded CertTemplate DER)"));
303
250
  } else {
304
- // A revocation must identify the certificate to revoke by issuer + serialNumber (RFC 9810 App. D sec.
305
- // 5.3.2); the pre-encoded-CertTemplate hatch above is the escape for an advanced identification.
306
251
  if (rd.certDetails.issuer == null || rd.certDetails.serialNumber == null) throw _err("cmp/bad-rev-req", "a revocation certDetails must identify the certificate by issuer and serialNumber");
307
252
  certDetails = b.raw(crmf.buildCertTemplate(rd.certDetails));
308
253
  }
@@ -311,39 +256,14 @@ function _encodeRevDetails(rd) {
311
256
  return b.sequence(children);
312
257
  }
313
258
 
314
- // crlEntryDetails ::= Extensions. A revocation request states WHY, so the structured form
315
- // { reason } encodes the single reasonCode extension RFC 9483 sec. 4.2 requires. A caller
316
- // needing another entry extension passes a pre-encoded Extensions DER instead.
317
- //
318
- // The reasonCode is emitted even at unspecified(0). RFC 5280 sec. 5.3.1 omits that value from a
319
- // CRL entry, where absence and unspecified carry the same meaning; sec. 4.2 governs a different
320
- // structure and says the field is REQUIRED and "MUST be 0 (unspecified)" when the reason is
321
- // unknown or is not to be published, so the omit-on-zero rule does not travel here.
322
259
  var KNOWN_REV_DETAILS_KEYS = { certDetails: 1, crlEntryDetails: 1 };
323
260
  var KNOWN_CRL_ENTRY_DETAIL_KEYS = { reason: 1 };
324
261
  var CRL_REASON_BY_NAME = intrinsic.create(null);
325
262
  _forEach(_keys(constants.NAMES.CRL_REASON), function (v) { CRL_REASON_BY_NAME[constants.NAMES.CRL_REASON[v]] = intrinsic.Number(v); });
326
263
  function _encodeCrlEntryDetails(spec) {
327
- // A pre-encoded Extensions DER is classified through the byte guard and normalized through it, so
328
- // every BufferSource form is taken as bytes. A narrowed check would drop a raw ArrayBuffer / DataView
329
- // into the { reason } branch below, where an absent reason silently becomes a generated unspecified(0)
330
- // reasonCode -- a different revocation reason than the caller encoded.
331
264
  if (guard.bytes.isByteSource(spec)) return guard.bytes.snapshotSource(spec, CmpError, "cmp/bad-rev-req", "RevDetails.crlEntryDetails (a pre-encoded Extensions DER)");
332
- // After the byte guard, the only valid structured form is a plain { reason? } record. Anything else --
333
- // an array, a Date/Map/Set or other exotic, a boxed primitive -- carries no `reason`, so the fallback
334
- // below would read undefined and emit a generated unspecified(0) reasonCode: a real revocation reason
335
- // the caller never wrote, from a value outside the documented { reason } | DER union. assertKnownKeys
336
- // does not catch these (an empty array or a keyless exotic has no unknown key to reject), so the value
337
- // is held to being a plain record through guard.identifier.assertPlainRecord rather than a bare
338
- // typeof-object test.
339
265
  guard.identifier.assertPlainRecord(spec, _err, "cmp/bad-rev-req", "RevDetails.crlEntryDetails ({ reason } or a pre-encoded Extensions DER)");
340
266
  guard.identifier.assertKnownKeys(spec, KNOWN_CRL_ENTRY_DETAIL_KEYS, _err, "cmp/bad-rev-req", "unknown crlEntryDetails field ");
341
- // Read `reason` ONCE: a caller getter or Proxy could otherwise answer the validation with one value
342
- // (a known reason) and the encode with another (null -> a generated unspecified(0)), so the request
343
- // would carry a different revocation reason than the one that was checked.
344
- // Read `reason` ONCE, consistent with the module's read-once discipline: build() already deep-copies
345
- // the caller's message at entry, so a getter or Proxy cannot vary between the reads here, but reading
346
- // one local keeps validation and encode on the same value with no dependence on that upstream copy.
347
267
  var reason = spec.reason;
348
268
  if (reason != null && (typeof reason !== "string" || !_hasOwn(CRL_REASON_BY_NAME, reason))) {
349
269
  throw _err("cmp/bad-rev-req", "unknown CRLReason " + _stringify(reason) + " (RFC 5280 sec. 5.3.1)");
@@ -352,74 +272,32 @@ function _encodeCrlEntryDetails(spec) {
352
272
  return b.sequence([_b.ext(O("reasonCode"), false, b.enumerated(_bigInt(code)))]);
353
273
  }
354
274
 
355
- // ---- support-message request values (RFC 9480 sec. 2.17, profiled by RFC 9483 sec. 4.3.4) ----
356
- //
357
- // CRLSource ::= CHOICE { dpn [0] DistributionPointName, issuer [1] GeneralNames }
358
- // CRLStatus ::= SEQUENCE { source CRLSource, thisUpdate Time OPTIONAL }
359
- // CRLStatusListValue ::= SEQUENCE SIZE (1..MAX) OF CRLStatus
360
- //
361
- // The CMP module is EXPLICIT TAGS, so CRLSource's own [0] / [1] wrap their alternative. The
362
- // DistributionPointName inside the dpn arm keeps the IMPLICIT tagging of the module it is
363
- // imported from, which is why its fullName [0] is a bare context-constructed GeneralNames.
364
275
  var KNOWN_CRL_STATUS_KEYS = { dpn: 1, issuer: 1, thisUpdate: 1 };
365
276
  var KNOWN_DPN_KEYS = { fullName: 1 };
366
277
  function buildCrlStatusList(spec) {
367
278
  if (!spec || typeof spec !== "object" || _isBuffer(spec)) throw _err("cmp/bad-input", "a crlUpdate request must be an object { dpn | issuer, thisUpdate? }");
368
279
  guard.identifier.assertKnownKeys(spec, KNOWN_CRL_STATUS_KEYS, _err, "cmp/bad-input", "unknown crlUpdate field ");
369
- // Each caller field is read ONCE, here, and every use below is of the value that was read. A
370
- // second read of the same field can answer differently, and what is encoded then differs from
371
- // what the answer is held to.
372
280
  var dpnSpec = spec.dpn, issuerSpec = spec.issuer;
373
281
  if (dpnSpec == null && issuerSpec == null) {
374
282
  throw _err("cmp/bad-input", "a crlUpdate identifies the requested CRL by a dpn (a distribution point name), an issuer (the CA that issues it), or both (RFC 9483 sec. 4.3.4)");
375
283
  }
376
- // A distribution point name is a pointer the responder resolves internally, so on its own it
377
- // leaves nothing to hold the answer to: a CRL claiming to be its issuer's complete list states no
378
- // scope at all (RFC 5280 sec. 5.2.5 permits that), and the request named no issuer to compare it
379
- // with, so any well-formed CRL from any CA would answer. The issuer is therefore required
380
- // alongside a dpn, naming the CA whose CRL the caller will accept. sec. 4.3.4's own note says
381
- // where a distribution point name comes from, and both sources -- a certificate's
382
- // cRLDistributionPoints, a held CRL's issuingDistributionPoint -- carry the issuer with it.
383
284
  if (dpnSpec != null && issuerSpec == null) {
384
285
  throw _err("cmp/bad-input", "a crlUpdate naming a dpn must also name the issuer whose CRL is acceptable: a distribution point name alone leaves the response unbound, since a complete CRL states no scope to compare it against (RFC 9483 sec. 4.3.4, RFC 5280 sec. 5.2.5). Drive an unbound request with pki.cmp.build + pki.cmp.transfer.");
385
286
  }
386
287
  var source, issuerNameDer = null, dpnNames = null;
387
- // The issuer, when given, is kept whether or not it goes on the wire: a caller who names one is
388
- // saying which CA's CRL they will accept, and pki.cmp.session holds the answer to it. Taking the
389
- // encoded Name from here means the comparison reads the name the caller gave rather than
390
- // re-interpreting the spec at a later boundary.
391
288
  if (issuerSpec != null) issuerNameDer = _b.encodeName(issuerSpec);
392
289
  if (dpnSpec != null) {
393
- // CRLSource is a CHOICE, so exactly one alternative goes on the wire, and sec. 4.3.4 settles
394
- // which: the dpn "if the CRL distribution point name is available", otherwise the issuer. A
395
- // caller giving both is answered by the dpn, the more specific pointer, with the issuer held
396
- // back to bind the response.
397
290
  if (typeof dpnSpec !== "object" || _isBuffer(dpnSpec)) throw _err("cmp/bad-input", "crlUpdate.dpn must be { fullName: [<GeneralName>...] }");
398
291
  guard.identifier.assertKnownKeys(dpnSpec, KNOWN_DPN_KEYS, _err, "cmp/bad-input", "unknown crlUpdate.dpn field ");
399
- // The nameRelativeToCRLIssuer arm of DistributionPointName is not built, matching the same
400
- // decision in pki.crl.sign for issuingDistributionPoint and freshestCRL. Re-open it in all
401
- // three together, since a partial relative-name surface would encode names one verb can
402
- // produce and its sibling cannot.
403
292
  var fullName = dpnSpec.fullName;
404
293
  if (fullName == null) throw _err("cmp/bad-input", "crlUpdate.dpn requires fullName (the nameRelativeToCRLIssuer arm is not built; pki.crl.sign draws the same line for issuingDistributionPoint)");
405
294
  var entries = _isArray(fullName) ? fullName : [fullName];
406
295
  if (!entries.length) throw _err("cmp/bad-input", "crlUpdate.dpn.fullName must carry at least one GeneralName");
407
- // The encoded GeneralNames are kept for the same reason as the issuer Name. RFC 5280 sec. 5.2.5
408
- // pins the comparison to the identical encoding, so what is kept is the bytes that went on the
409
- // wire rather than the spec they were built from.
410
296
  dpnNames = _map(entries, function (e) { return _b.encodeGeneralName(e); });
411
297
  source = b.explicit(0, b.contextConstructed(0, _bufferConcat(dpnNames)));
412
298
  } else {
413
- // sec. 4.3.4: the issuer choice "MUST contain the issuer DN in the directoryName field of a
414
- // GeneralName element". The directoryName [4] EXPLICIT wraps the SAME encoded Name saved above for
415
- // the response binding (issuerNameDer), not a second read of issuerSpec: a caller's issuer can be an
416
- // accessor, and re-encoding it here would put one name on the wire while the answer is held to
417
- // another, so a CRL from the name that was validated could answer a request that named a different one.
418
299
  source = b.explicit(1, b.sequence([b.explicit(4, b.raw(issuerNameDer))]));
419
300
  }
420
- // Read ONCE. A caller's thisUpdate can be an accessor, so encoding from one read and validating
421
- // against a second could judge the answer against a cutoff the request never carried. The instant
422
- // that went on the wire is returned with the bytes, and it is that value the response is held to.
423
301
  var status = [source], askedInstant = null, asked = spec.thisUpdate;
424
302
  if (asked != null) {
425
303
  askedInstant = guard.time.instantOf(asked, _err, "cmp/bad-input", "crlUpdate.thisUpdate");
@@ -433,20 +311,12 @@ function buildCrlStatusList(spec) {
433
311
  };
434
312
  }
435
313
 
436
- // The distributionPoint [0] of a CRL's issuingDistributionPoint extension (RFC 5280 sec. 5.2.5),
437
- // decoded to the DistributionPointName form guard.name.dpnCorresponds compares, or null when the
438
- // extension names no point -- a complete CRL, whose scope is its issuer's whole population rather
439
- // than one point of it. A structural fault THROWS instead of reading as "names no point": a session
440
- // binds a requested distribution point to this value, and an unreadable extension resolving to
441
- // "nothing to compare" would skip the binding it is read for.
442
314
  var IDP_SCHEMA = pkix.issuingDistributionPoint("cmp/bad-info-value");
443
315
  function decodeIdpDistributionPoint(valueBytes) {
444
316
  var m;
445
317
  try { m = schema.walk(IDP_SCHEMA, asn1.decode(valueBytes), NS); }
446
318
  catch (e) { throw _err("cmp/bad-info-value", "the returned CRL carries a malformed issuingDistributionPoint extension (RFC 5280 sec. 5.2.5)", e); }
447
319
  if (!m.fields.distributionPoint.present) return null;
448
- // distributionPoint [0] EXPLICIT-wraps the DistributionPointName CHOICE (a context tag on a
449
- // CHOICE-typed field is always EXPLICIT), so the name is its single child.
450
320
  var wrap = m.fields.distributionPoint.node;
451
321
  if (!wrap.children || wrap.children.length !== 1) {
452
322
  throw _err("cmp/bad-info-value", "an issuingDistributionPoint distributionPoint [0] must wrap exactly one DistributionPointName (RFC 5280 sec. 5.2.5)");
@@ -459,20 +329,16 @@ function _encodeRevReqContent(list) {
459
329
  return b.sequence(list.map(_encodeRevDetails));
460
330
  }
461
331
 
462
- // ---- response-side (CA/responder) PKIBody arm content encoders ----
463
332
 
464
- // CertOrEncCert ::= CHOICE { certificate [0] EXPLICIT CMPCertificate, encryptedCert [1] EXPLICIT EncryptedKey }.
465
333
  function _encodeCertOrEncCert(coec, state) {
466
334
  if (!coec || typeof coec !== "object" || Buffer.isBuffer(coec)) throw _err("cmp/bad-cert-response", "certifiedKeyPair must carry certificate or encryptedCert");
467
335
  if (coec.certificate != null && coec.encryptedCert != null) throw _err("cmp/bad-cert-response", "certOrEncCert is a CHOICE: supply exactly one of certificate or encryptedCert, not both");
468
336
  if (coec.certificate != null) {
469
337
  var certDer = _b.reqDerSequence(coec.certificate, "certifiedKeyPair.certificate (a Certificate DER)");
470
338
  try { x509.parse(certDer); } catch (e) { if (e instanceof CmpError) throw e; throw _err("cmp/bad-cert-response", "certifiedKeyPair.certificate is not a valid X.509 certificate", e); }
471
- return b.explicit(0, b.raw(certDer)); // certificate [0] EXPLICIT
339
+ return b.explicit(0, b.raw(certDer));
472
340
  }
473
341
  if (coec.encryptedCert != null) {
474
- // encryptedCert [1] wraps an EncryptedKey (pre-encoded DER hatch in v1). Like the privateKey, only the
475
- // EnvelopedData [0] form is cmp2021; the deprecated EncryptedValue (a universal SEQUENCE) is cmp2000.
476
342
  var ec = _b.reqDer(coec.encryptedCert, "certifiedKeyPair.encryptedCert (a pre-encoded EncryptedKey DER)");
477
343
  var ecNode;
478
344
  try { ecNode = asn1.decode(ec); } catch (e) { throw _err("cmp/bad-cert-response", "certifiedKeyPair.encryptedCert is not valid DER", e); }
@@ -482,15 +348,12 @@ function _encodeCertOrEncCert(coec, state) {
482
348
  throw _err("cmp/bad-cert-response", "certifiedKeyPair must carry certificate or encryptedCert");
483
349
  }
484
350
 
485
- // CertifiedKeyPair ::= SEQUENCE { certOrEncCert, privateKey [0] EXPLICIT OPTIONAL, publicationInfo [1] EXPLICIT OPTIONAL }.
486
351
  function _encodeCertifiedKeyPair(ckp, state) {
487
352
  if (!ckp || typeof ckp !== "object" || Buffer.isBuffer(ckp)) throw _err("cmp/bad-cert-response", "certifiedKeyPair must be an object");
488
- var children = [_encodeCertOrEncCert(ckp, state)]; // certificate / encryptedCert are direct keys (the parse-result shape)
353
+ var children = [_encodeCertOrEncCert(ckp, state)];
489
354
  if (ckp.privateKey != null) {
490
355
  var pk = _b.reqDer(ckp.privateKey, "certifiedKeyPair.privateKey (a pre-encoded EncryptedKey DER)");
491
356
  children.push(b.explicit(0, b.raw(pk)));
492
- // EncryptedKey ::= CHOICE { encryptedValue EncryptedValue (a universal SEQUENCE, the deprecated cmp2000
493
- // form), envelopedData [0] (a cmp2021 feature). Only the envelopedData form bumps pvno to cmp2021(3).
494
357
  var pkNode;
495
358
  try { pkNode = asn1.decode(pk); } catch (e) { throw _err("cmp/bad-cert-response", "certifiedKeyPair.privateKey is not valid DER", e); }
496
359
  if (pkNode.tagClass === "context" && pkNode.tagNumber === 0) state.usesCmp2021 = true;
@@ -499,13 +362,10 @@ function _encodeCertifiedKeyPair(ckp, state) {
499
362
  return b.sequence(children);
500
363
  }
501
364
 
502
- // CertResponse ::= SEQUENCE { certReqId INTEGER, status PKIStatusInfo, certifiedKeyPair OPTIONAL, rspInfo OCTET STRING OPTIONAL }.
503
365
  function _encodeCertResponse(cr, state) {
504
366
  if (!cr || typeof cr !== "object" || Buffer.isBuffer(cr)) throw _err("cmp/bad-cert-response", "each CertResponse must be an object");
505
367
  if (cr.status == null) throw _err("cmp/bad-cert-response", "CertResponse.status (a PKIStatusInfo) is required");
506
368
  var hasCkp = cr.certifiedKeyPair != null;
507
- // RFC 9810 sec. 5.3.4: failInfo XOR certifiedKeyPair; a certifiedKeyPair is allowed only under a granting
508
- // status (accepted 0 / grantedWithMods 1).
509
369
  if (hasCkp && cr.status.failInfo != null) throw _err("cmp/bad-cert-response", "a CertResponse must not carry both failInfo and certifiedKeyPair");
510
370
  if (hasCkp && cr.status.status !== 0 && cr.status.status !== 1) throw _err("cmp/bad-cert-response", "a CertResponse certifiedKeyPair is allowed only under a granting status (accepted / grantedWithMods)");
511
371
  var children = [b.integer(_reqIdInt(cr.certReqId, "cmp/bad-cert-response", "CertResponse certReqId")), _encodePkiStatusInfo(cr.status, "cmp/bad-cert-response")];
@@ -514,12 +374,11 @@ function _encodeCertResponse(cr, state) {
514
374
  return b.sequence(children);
515
375
  }
516
376
 
517
- // CertRepMessage ::= SEQUENCE { caPubs [1] EXPLICIT SEQUENCE OF CMPCertificate OPTIONAL, response SEQUENCE OF CertResponse }.
518
377
  function _encodeCertRepMessage(spec, state, arm) {
519
378
  if (!spec || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("cmp/bad-cert-rep", arm + " must be an object { caPubs?, response }");
520
379
  if (!Array.isArray(spec.response)) throw _err("cmp/bad-cert-rep", arm + ".response must be an array of CertResponse");
521
380
  var children = [];
522
- if (spec.caPubs != null) { // caPubs [1] PRECEDES response (the optional-first x509-version shape)
381
+ if (spec.caPubs != null) {
523
382
  if (!Array.isArray(spec.caPubs) || !spec.caPubs.length) throw _err("cmp/bad-cert-rep", arm + ".caPubs must be a non-empty array of certificate DERs");
524
383
  children.push(b.explicit(1, b.sequence(spec.caPubs.map(function (c) { return _certRaw(c, "cmp/bad-cert-rep", "a caPubs entry"); }))));
525
384
  }
@@ -527,14 +386,12 @@ function _encodeCertRepMessage(spec, state, arm) {
527
386
  return b.sequence(children);
528
387
  }
529
388
 
530
- // A raw Certificate DER, validated via the real parser before it is embedded.
531
389
  function _certRaw(c, code, what) {
532
390
  var der = _b.reqDerSequence(c, what + " (a Certificate DER)");
533
391
  try { x509.parse(der); } catch (e) { if (e instanceof CmpError) throw e; throw _err(code, what + " is not a valid X.509 certificate", e); }
534
392
  return b.raw(der);
535
393
  }
536
394
 
537
- // RevRepContent ::= SEQUENCE { status SEQ OF PKIStatusInfo (min 1), revCerts [0] EXPLICIT SEQ OF CertId OPTIONAL, crls [1] EXPLICIT SEQ OF CertificateList OPTIONAL }.
538
395
  function _encodeCertIdRr(cid) {
539
396
  if (!cid || typeof cid !== "object" || Buffer.isBuffer(cid) || cid.issuer == null || cid.serialNumber == null) throw _err("cmp/bad-rev-rep", "each revCerts CertId must be { issuer, serialNumber }");
540
397
  return b.sequence([_b.encodeGeneralName(cid.issuer), b.integer(_reqIdInt(cid.serialNumber, "cmp/bad-rev-rep", "CertId serialNumber"))]);
@@ -551,7 +408,7 @@ function _encodeRevRepContent(spec) {
551
408
  if (!Array.isArray(spec.crls) || !spec.crls.length) throw _err("cmp/bad-rev-rep", "rp.crls must be a non-empty array of CertificateList DERs");
552
409
  children.push(b.explicit(1, b.sequence(spec.crls.map(function (c) {
553
410
  var der = _b.reqDerSequence(c, "crls entry (a CertificateList DER)");
554
- try { crl.parse(der); } // each crls entry MUST be a valid X.509 CRL (CertificateList)
411
+ try { crl.parse(der); }
555
412
  catch (e) { if (e instanceof CmpError) throw e; throw _err("cmp/bad-rev-rep", "a crls entry is not a valid CRL (CertificateList)", e); }
556
413
  return b.raw(der);
557
414
  }))));
@@ -559,7 +416,6 @@ function _encodeRevRepContent(spec) {
559
416
  return b.sequence(children);
560
417
  }
561
418
 
562
- // ErrorMsgContent ::= SEQUENCE { pKIStatusInfo, errorCode INTEGER OPTIONAL, errorDetails PKIFreeText OPTIONAL }.
563
419
  function _encodeErrorMsgContent(spec) {
564
420
  if (!spec || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("cmp/bad-error", "error must be an object { pKIStatusInfo, errorCode?, errorDetails? }");
565
421
  if (spec.pKIStatusInfo == null) throw _err("cmp/bad-error", "error.pKIStatusInfo is required");
@@ -569,7 +425,6 @@ function _encodeErrorMsgContent(spec) {
569
425
  return b.sequence(children);
570
426
  }
571
427
 
572
- // PollRepContent ::= SEQUENCE OF SEQUENCE { certReqId INTEGER, checkAfter INTEGER (seconds, >= 0), reason PKIFreeText OPTIONAL }.
573
428
  function _encodePollRepContent(list) {
574
429
  if (!Array.isArray(list) || !list.length) throw _err("cmp/bad-poll-rep", "pollRep must be a non-empty array of { certReqId, checkAfter, reason? }");
575
430
  return b.sequence(list.map(function (pr) {
@@ -581,7 +436,6 @@ function _encodePollRepContent(list) {
581
436
  }));
582
437
  }
583
438
 
584
- // KeyRecRepContent ::= SEQUENCE { status PKIStatusInfo, newSigCert [0] EXPLICIT CMPCertificate OPTIONAL, caCerts [1] EXPLICIT SEQ OF CMPCertificate OPTIONAL, keyPairHist [2] EXPLICIT SEQ OF CertifiedKeyPair OPTIONAL }.
585
439
  function _encodeKeyRecRepContent(spec, state) {
586
440
  if (!spec || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("cmp/bad-key-rec-rep", "krp must be an object { status, newSigCert?, caCerts?, keyPairHist? }");
587
441
  if (spec.status == null) throw _err("cmp/bad-key-rec-rep", "krp.status is required");
@@ -598,17 +452,9 @@ function _encodeKeyRecRepContent(spec, state) {
598
452
  return b.sequence(children);
599
453
  }
600
454
 
601
- // Does a built CertReqMessages carry a proof of possession that RFC 9810 sec. 5.2.8.3 couples to
602
- // cmp2021(3)? Stated over the RFC's pair rather than over the arms this toolkit happens to emit, so
603
- // the coupling still holds if agreeMAC is built later.
604
455
  var CMP2021_POP_METHODS = { agreeMAC: 1, encryptedKey: 1 };
605
456
  function _popNeedsCmp2021(crmfDer) {
606
457
  var messages;
607
- // Re-reading a CertReqMessages this process just produced. A fault here is an internal
608
- // inconsistency, not a caller error, so it is named in this module's namespace: a crmf/* parse code
609
- // surfacing from cmp.build would send the operator looking at a request they wrote correctly. A
610
- // CRMF fault raised while BUILDING that body still passes through untranslated, where it does name
611
- // the caller's own input.
612
458
  try { messages = schemaCrmf.parse(crmfDer).messages; }
613
459
  catch (e) {
614
460
  throw _err("cmp/bad-body", "the certificate request body this message just built did not parse back: " +
@@ -621,37 +467,18 @@ function _popNeedsCmp2021(crmfDer) {
621
467
  return false;
622
468
  }
623
469
 
624
- // nested [20] NestedMessageContent ::= PKIMessages ::= SEQUENCE SIZE (1..MAX) OF PKIMessage (RFC 9810 sec.
625
- // 5.1.3.5 "Multiple Protection"): an RA forwards or batches complete, independently-protected PKIMessages,
626
- // wrapping them UNCHANGED under its OWN protection. Each entry is a full PKIMessage DER, checked by the real
627
- // parser so a non-PKIMessage fails closed here rather than wrapping as opaque bytes. The check is the toolkit's
628
- // PKIMessage parse: it validates the envelope and every arm the parser decodes and, exactly as a parse
629
- // anywhere else does, surfaces the raw arms (p10cr, the announcements, a further nested) without re-validating
630
- // their content -- which sec. 5.1.3.5 leaves to the recipient, since the RA forwards each message unchanged.
631
- // The wrapper's protection covers this SEQUENCE, not the inner messages, and the parser never auto-recurses a
632
- // nested body (the operator re-feeds each inner message).
633
470
  function _encodeNestedContent(list) {
634
471
  if (!intrinsic.isArray(list) || list.length === 0) {
635
472
  throw _err("cmp/bad-input", "nested must be a non-empty array of PKIMessage DER (RFC 9810 sec. 5.1.3.5 PKIMessages is SIZE (1..MAX))");
636
473
  }
637
- // reqDenseArray rejects a sparse hole or a nullish entry up front: Array.prototype.map SKIPS a hole,
638
- // which would otherwise reach Buffer.concat as a native ERR_INVALID_ARG_TYPE and, on a huge sparse
639
- // length, traverse the empty slots. With the array proven dense, the map validates each entry in turn.
640
474
  return b.sequence(intrinsic.map(_b.reqDenseArray(list, "nested"), function (m, i) {
641
- // reqDerSequence requires a DER SEQUENCE up front: cmp.parse also unwraps PEM from a byte source, so a
642
- // PEM-armored entry (a Buffer straight from fs.readFileSync) would otherwise pass the parse yet be
643
- // embedded by b.raw as ASCII armor and rejected as cmp/bad-der by the outer message's own parse, only
644
- // after protection is computed. With DER assured, the bytes cmp.parse checks are the bytes embedded.
645
475
  var der = _b.reqDerSequence(m, "nested[" + i + "] (a PKIMessage DER)");
646
- // A bad entry is a build-time authoring error, so any parse failure is one uniform cmp/bad-input naming
647
- // the offending index (the underlying reason rides along as the cause), never the parser's internal code.
648
- try { cmp.parse(der); } // check each embedded message parses as a PKIMessage (fail-closed on a non-PKIMessage)
476
+ try { cmp.parse(der); }
649
477
  catch (e) { throw _err("cmp/bad-input", "nested[" + i + "] does not parse as a PKIMessage", e); }
650
478
  return b.raw(der);
651
479
  }));
652
480
  }
653
481
 
654
- // Dispatch the single-key body object to its EXPLICIT-tagged arm. Returns { bodyTLV, usesCmp2021 }.
655
482
  function _encodeBody(bodySpec, key, opts) {
656
483
  if (!bodySpec || typeof bodySpec !== "object" || Buffer.isBuffer(bodySpec)) throw _err("cmp/bad-input", "message.body must be a single-key object");
657
484
  var keys = Object.keys(bodySpec);
@@ -661,9 +488,6 @@ function _encodeBody(bodySpec, key, opts) {
661
488
  var tag = BODY_TAG[arm], state = { usesCmp2021: false };
662
489
 
663
490
  if (CRMF_BODY[arm]) {
664
- // The CRMF proof of possession is signed with the REQUESTED key's private half, which is distinct from
665
- // the message-protection key. Take it from a `key` field on the request spec; default to the protection
666
- // key only for a self-request (the client certifying the very key it protects with).
667
491
  var reqSpec = bodySpec[arm], popKey = key;
668
492
  if (reqSpec && typeof reqSpec === "object" && !Buffer.isBuffer(reqSpec) && "key" in reqSpec) {
669
493
  popKey = reqSpec.key;
@@ -671,12 +495,6 @@ function _encodeBody(bodySpec, key, opts) {
671
495
  delete reqSpec.key;
672
496
  }
673
497
  return crmf.build(reqSpec, popKey, {}).then(function (crmfDer) {
674
- // RFC 9810 sec. 5.2.8.3: "When using agreeMAC or encryptedKey choices, the pvno cmp2021(3)
675
- // MUST be used." Read off the message that was actually emitted rather than off the spec that
676
- // asked for it, so the announced version answers for the bytes on the wire. The parser
677
- // enforces the same rule on the way in, and before this the two disagreed: a request carrying
678
- // an encryptedKey proof went out announcing cmp2000(2), and this toolkit's own reader refused
679
- // the message this toolkit had just built.
680
498
  return { bodyTLV: b.explicit(tag, b.raw(crmfDer)), usesCmp2021: _popNeedsCmp2021(crmfDer) };
681
499
  });
682
500
  }
@@ -684,13 +502,8 @@ function _encodeBody(bodySpec, key, opts) {
684
502
  if (arm === "p10cr") {
685
503
  var csrDer = _b.reqDerSequence(bodySpec.p10cr, "p10cr (a CertificationRequest DER)");
686
504
  var parsedCsr;
687
- try { parsedCsr = csr.parse(csrDer); } // validate structure via the real parser AND capture the parse for the PoP check
505
+ try { parsedCsr = csr.parse(csrDer); }
688
506
  catch (e) { if (e instanceof CmpError) throw e; throw _err("cmp/bad-input", "p10cr is not a valid PKCS#10 CertificationRequest", e); }
689
- // Verify the embedded request's proof-of-possession before it is protected and sent: a p10cr carries
690
- // a PKCS#10 CertificationRequest (RFC 9810 sec. 5.3.3, whose structure is RFC 2986), and its
691
- // self-signature under its own subject public key is that request's proof-of-possession. A CSR whose
692
- // self-signature does not verify is one the CA will reject, so refuse it rather than send it -- through
693
- // the shared inbound-PoP seam pki.csr.verify uses.
694
507
  return _p10crBody(csrDer, parsedCsr, tag, state);
695
508
  }
696
509
  if (arm === "certConf") {
@@ -701,11 +514,11 @@ function _encodeBody(bodySpec, key, opts) {
701
514
  inner = _encodeGenMsgContent(bodySpec.genm);
702
515
  } else if (arm === "rr") {
703
516
  inner = _encodeRevReqContent(bodySpec.rr);
704
- } else if (CERT_REP_ARM[arm]) { // ip / cp / kup / ccp -- CertRepMessage
517
+ } else if (CERT_REP_ARM[arm]) {
705
518
  inner = _encodeCertRepMessage(bodySpec[arm], state, arm);
706
519
  } else if (arm === "rp") {
707
520
  inner = _encodeRevRepContent(bodySpec.rp);
708
- } else if (arm === "genp") { // GenRepContent == GenMsgContent
521
+ } else if (arm === "genp") {
709
522
  inner = _encodeGenMsgContent(bodySpec.genp);
710
523
  } else if (arm === "error") {
711
524
  inner = _encodeErrorMsgContent(bodySpec.error);
@@ -715,16 +528,13 @@ function _encodeBody(bodySpec, key, opts) {
715
528
  inner = _encodeKeyRecRepContent(bodySpec.krp, state);
716
529
  } else if (arm === "nested") {
717
530
  inner = _encodeNestedContent(bodySpec.nested);
718
- } else { // pkiconf -- PKIConfirmContent ::= NULL
531
+ } else {
719
532
  if (bodySpec.pkiconf !== null && bodySpec.pkiconf !== true) throw _err("cmp/bad-input", "pkiconf takes null or true (PKIConfirmContent is NULL)");
720
533
  inner = b.nullValue();
721
534
  }
722
535
  return Promise.resolve({ bodyTLV: b.explicit(tag, inner), usesCmp2021: state.usesCmp2021 });
723
536
  }
724
537
 
725
- // `await`, not a `.then` chain: the PoP verdict is read through the native promise job, not the
726
- // replaceable Promise.prototype.then a co-resident could swap to force `ok` true and slip an
727
- // unverified request past. The caller has already validated csrDer's structure synchronously.
728
538
  async function _p10crBody(csrDer, parsedCsr, tag, state) {
729
539
  if (!(await csrVerify.verifyCsrSignature(parsedCsr))) {
730
540
  throw _err("cmp/bad-popo", "the p10cr CertificationRequest failed its proof-of-possession: its PKCS#10 signature does not verify under its own subject public key, so a CA would reject it");
@@ -732,11 +542,8 @@ async function _p10crBody(csrDer, parsedCsr, tag, state) {
732
542
  return { bodyTLV: b.explicit(tag, b.raw(csrDer)), usesCmp2021: state.usesCmp2021 };
733
543
  }
734
544
 
735
- // ---- protection ----
736
545
 
737
546
 
738
- // Resolve the protection selector to { protectionAlgDer, computeBits(protectedPartDer)->Promise<Buffer> },
739
- // senderSpki, senderScheme } before the header is built (protectionAlg is opts-derived, not header-derived).
740
547
  function _resolveProtection(opts) {
741
548
  var hasSig = opts.key != null || opts.cert != null;
742
549
  var hasMac = opts.mac != null;
@@ -764,7 +571,6 @@ function _resolveProtection(opts) {
764
571
  };
765
572
  }
766
573
 
767
- // PBMAC1
768
574
  var m = opts.mac;
769
575
  if (!m || typeof m !== "object" || Buffer.isBuffer(m)) throw _err("cmp/bad-input", "opts.mac must be an object { secret, salt?, iterationCount?, prf?, keyLength? }");
770
576
  guard.identifier.assertKnownKeys(m, KNOWN_MAC_KEYS, _err, "cmp/bad-input", "unknown opts.mac field ");
@@ -774,28 +580,17 @@ function _resolveProtection(opts) {
774
580
  if (!Buffer.isBuffer(secret) || !secret.length) throw _err("cmp/bad-input", "opts.mac.secret must be a non-empty string or Buffer");
775
581
  }
776
582
  var secretBuf = Buffer.isBuffer(secret) ? secret : Buffer.from(secret, "utf8");
777
- // Default only when prf is absent (null/undefined), the same test the iterationCount and keyLength
778
- // siblings below use. `m.prf || "SHA-256"` would default on any falsy value, so a supplied "", false, or
779
- // 0 -- none a valid PRF name -- would silently encode HMAC-SHA-256 instead of being refused.
780
583
  var prf = m.prf != null ? m.prf : "SHA-256";
781
584
  if (!PBMAC1_PRF[prf]) throw _err("cmp/bad-input", "opts.mac.prf must be SHA-256 / SHA-384 / SHA-512");
782
585
  var iterationCount = m.iterationCount != null ? m.iterationCount : PBMAC1_DEFAULT_ITER;
783
586
  if (typeof iterationCount !== "number" || !Number.isInteger(iterationCount) || iterationCount < PBMAC1_MIN_ITER) throw _err("cmp/bad-input", "opts.mac.iterationCount must be at least " + PBMAC1_MIN_ITER + " (RFC 8018 sec. 4.2)");
784
- // Bound the PBKDF2 work factors before deriving: a huge iterationCount or keyLength is self-inflicted work.
785
587
  if (iterationCount > constants.LIMITS.PBKDF2_MAX_ITERATIONS) throw _err("cmp/bad-input", "opts.mac.iterationCount exceeds the PBKDF2 work-factor cap " + constants.LIMITS.PBKDF2_MAX_ITERATIONS);
786
588
  var keyLength = m.keyLength != null ? m.keyLength : PBMAC1_DEFAULT_KEYLEN;
787
- // RFC 9579 sec. 9 floor (>= 20 bytes): produce only messages the verifier (pki.cmp.verify, same floor) can
788
- // accept, so a shorter derived key is refused at production time and never emitted as an unverifiable message.
789
589
  if (typeof keyLength !== "number" || !Number.isInteger(keyLength) || keyLength < PBMAC1_MIN_KEYLEN) throw _err("cmp/bad-input", "opts.mac.keyLength must be an integer >= " + PBMAC1_MIN_KEYLEN + " bytes (RFC 9579 sec. 9)");
790
590
  if (keyLength > PBMAC1_MAX_KEYLEN) throw _err("cmp/bad-input", "opts.mac.keyLength exceeds the cap " + PBMAC1_MAX_KEYLEN + " bytes");
791
- // Bound the COMBINED work: a keyLength spanning multiple PRF blocks costs iterationCount HMACs per block, so
792
- // the product can exceed the per-block iteration cap. Cap it against the same ceiling pki.cmp.verify enforces,
793
- // so build never emits a message its verify-inverse would refuse as over-budget (RFC 8018 sec. 5.2).
794
591
  var blocks = Math.ceil(keyLength / PBMAC1_PRF_HLEN[prf]);
795
592
  if (iterationCount * blocks > constants.LIMITS.PBKDF2_MAX_ITERATIONS) throw _err("cmp/bad-input", "opts.mac combined work (iterationCount " + iterationCount + " x " + blocks + " derived blocks) exceeds the PBKDF2 work-factor cap " + constants.LIMITS.PBKDF2_MAX_ITERATIONS);
796
593
  var salt = m.salt != null ? _b.reqDer(m.salt, "opts.mac.salt") : Buffer.from(webcrypto.webcrypto.getRandomValues(new Uint8Array(PBMAC1_DEFAULT_SALT_BYTES)));
797
- // RFC 8018 sec. 4.1 (64-bit) floor: an empty or short salt loses precomputation resistance, so produce only
798
- // messages pki.cmp.verify (same floor) accepts, refusing a below-minimum salt at construction time.
799
594
  if (salt.length < PBMAC1_MIN_SALT) throw _err("cmp/bad-input", "opts.mac.salt must be at least " + PBMAC1_MIN_SALT + " octets (RFC 8018 sec. 4.1)");
800
595
  if (salt.length > constants.LIMITS.PBKDF2_MAX_SALT) throw _err("cmp/bad-input", "opts.mac.salt exceeds " + constants.LIMITS.PBKDF2_MAX_SALT + " bytes");
801
596
  var macDesc = { salt: salt, iterationCount: iterationCount, keyLength: keyLength, prfName: PBMAC1_PRF[prf], macName: PBMAC1_MAC_OID[prf] };
@@ -811,12 +606,8 @@ function _resolveProtection(opts) {
811
606
  };
812
607
  }
813
608
 
814
- // ---- orchestrator ----
815
609
 
816
610
  function build(message, opts) {
817
- // Every caller-owned argument copied at entry and released when the call settles; see the note
818
- // on the same call in x509-sign. `opts.mac.secret` is why the copy has to be deep: it sits a
819
- // level below the options object and is read by the PBMAC1 derivation after the first turn.
820
611
  return guard.bytes.fixedCall(CmpError, "cmp/bad-input", [
821
612
  [message, "the PKIMessage spec"], [opts, "pki.cmp.build options"],
822
613
  ], _build);
@@ -843,25 +634,22 @@ async function _build(message, opts) {
843
634
  var prot = _resolveProtection(opts);
844
635
  var senderKey = opts.key;
845
636
 
846
- // `await`, not a `.then` chain: an embedded p10cr/crm proof-of-possession is enforced by _encodeBody
847
- // REJECTING, so the verdict must reach here through the native promise job, not a Promise.prototype.then
848
- // a co-resident could swap to fire this callback with a forged body and slip an unverified request past.
849
637
  var bodyResult = await _encodeBody(message.body, senderKey, opts);
850
638
  var bodyTLV = bodyResult.bodyTLV;
851
639
  var pvno = message.header.pvno != null ? message.header.pvno : 2;
852
640
  if (typeof pvno !== "number" || !Number.isInteger(pvno)) throw _err("cmp/bad-input", "header.pvno must be an integer");
853
- if (bodyResult.usesCmp2021 && pvno < 3) pvno = 3; // a certConf hashAlg forces cmp2021(3)
641
+ if (bodyResult.usesCmp2021 && pvno < 3) pvno = 3;
854
642
 
855
- var headerTLV = _encodeHeader(message.header, prot.protectionAlgDer, pvno); // built ONCE
856
- var protectedPartDer = b.sequence([headerTLV, bodyTLV]); // virtual ProtectedPart ::= SEQUENCE { header, body }
643
+ var headerTLV = _encodeHeader(message.header, prot.protectionAlgDer, pvno);
644
+ var protectedPartDer = b.sequence([headerTLV, bodyTLV]);
857
645
 
858
646
  var protectionBits = await prot.computeBits(protectedPartDer);
859
- var msgChildren = [headerTLV, bodyTLV, b.explicit(0, protectionBits)]; // reuse the SAME header/body TLVs
647
+ var msgChildren = [headerTLV, bodyTLV, b.explicit(0, protectionBits)];
860
648
  var extraCertsDers = _collectExtraCerts(opts, prot.certDer);
861
649
  if (extraCertsDers.length) msgChildren.push(b.explicit(1, b.sequence(extraCertsDers)));
862
650
  var der = b.sequence(msgChildren);
863
651
 
864
- cmp.parse(der); // round-trip re-validation: the emitted message MUST parse (protection<=>protectionAlg, pvno, freeText, etc.)
652
+ cmp.parse(der);
865
653
  if (opts.pem != null) {
866
654
  if (opts.pem === true) return cmp.pemEncode(der, "CMP");
867
655
  if (typeof opts.pem !== "string" || !opts.pem) throw _err("cmp/bad-input", "opts.pem must be true or a non-empty PEM label string");
@@ -877,7 +665,7 @@ function _collectExtraCerts(opts, protCertDer) {
877
665
  if (!Array.isArray(opts.extraCerts)) throw _err("cmp/bad-extra-certs", "opts.extraCerts must be an array of certificate DERs");
878
666
  opts.extraCerts.forEach(function (c) {
879
667
  var der = _b.reqDerSequence(c, "extraCerts entry (a Certificate DER)");
880
- try { x509.parse(der); } // each extraCerts entry MUST be a valid X.509 certificate (RFC 9810 sec. 5.1)
668
+ try { x509.parse(der); }
881
669
  catch (e) { if (e instanceof CmpError) throw e; throw _err("cmp/bad-extra-certs", "an extraCerts entry is not a valid X.509 certificate", e); }
882
670
  out.push(b.raw(der));
883
671
  });
@@ -941,66 +729,40 @@ function _collectExtraCerts(opts, protCertDer) {
941
729
  * pki.schema.cmp.parse(der).body.arm; // "p10cr"
942
730
  */
943
731
 
944
- // ---- the thin RFC 9811 HTTP transfer verb: POST a DER PKIMessage over the shared pki.transport ----
945
- //
946
- // RFC 9811 defines exactly ONE client operation -- POST a DER PKIMessage, receive a DER PKIMessage -- so
947
- // every CMP exchange (ir / p10cr / certConf / pollReq / rr / genm ...) rides this single stateless verb;
948
- // the caller builds+protects the message upstream with pki.cmp.build and hands the finished bytes here,
949
- // which are POSTed VERBATIM (the protection covers them). No socket is opened by this codec -- the verb
950
- // composes pki.schema.cmp.parse over pki.transport, exactly as pki.est / pki.acme wire their message layers.
951
732
 
952
733
  var DEFAULT_TRANSFER_TIMEOUT = constants.TIME.seconds(30);
953
734
  var MAX_TRANSFER_TIMEOUT = constants.TIME.seconds(600);
954
735
  var PKIXCMP = "application/pkixcmp";
955
- var PKIXCMP_POLL = "application/pkixcmp-poll"; // the legacy type handled "like application/pkixcmp" (RFC 9811 sec. 4)
736
+ var PKIXCMP_POLL = "application/pkixcmp-poll";
956
737
  var KNOWN_TRANSFER_OPTS = { transport: 1, tls: 1, headers: 1, timeout: 1, maxResponseBytes: 1 };
957
738
 
958
- // The DER of a caller-supplied PKIMessage: a DER Buffer / Uint8Array re-viewed through guard.bytes.view
959
- // (sent VERBATIM -- protection covers these exact bytes), a PEM "CMP" string decoded, else a config-time
960
- // cmp/bad-input before the wire. The re-view fails a DETACHED backing buffer (a transferred / structuredClone'd
961
- // view reads as zero-length) as cmp/bad-input rather than silently POSTing an empty body in its place.
962
739
  function _cmpMessageDer(input) {
963
740
  var der;
964
741
  if (guard.bytes.isByteSource(input)) der = guard.bytes.source(input, CmpError, "cmp/bad-input", "the CMP message DER");
965
742
  else if (typeof input === "string") {
966
- // Normalize a PEM decode failure (no block, wrong armor label, malformed base64) to the stable
967
- // cmp/bad-input at this input boundary, rather than leaking the PEM layer's PemError to the caller.
968
743
  try { der = cmp.pemDecode(input); }
969
744
  catch (e) { throw _err("cmp/bad-input", "the CMP message is not a valid PEM CMP block", e); }
970
745
  } else throw _err("cmp/bad-input", "a CMP message must be a DER Buffer/Uint8Array or a PEM CMP string");
971
- // Validate the bytes ARE a PKIMessage before the wire (transfer accepts a DER PKIMessage), so malformed
972
- // input fails locally as cmp/bad-input rather than crossing the network seam. parse validates only -- the
973
- // ORIGINAL der is returned for the verbatim POST (protection covers the exact bytes; nothing re-serialized).
974
746
  try { cmp.parse(der); }
975
747
  catch (e) { throw _err("cmp/bad-input", "the CMP message is not a valid PKIMessage", e); }
976
748
  return der;
977
749
  }
978
750
 
979
- // Map opts.tls (operator-facing) to the transport request.tls shape. rejectUnauthorized is NOT here --
980
- // the transport forces it on unconditionally; the verb never disables server verification.
981
751
  function _tlsForTransfer(opts) {
982
752
  var t = opts.tls || {};
983
753
  return { anchors: t.anchors, useSystemStore: t.useSystemStore, cert: t.cert, key: t.key, minVersion: t.minVersion, servername: t.servername, checkServerIdentity: t.checkServerIdentity };
984
754
  }
985
755
 
986
- // The response content-type token (before any ;-parameters), lowercased; is it a CMP media type?
987
756
  function _ctypeToken(headers) { return String((headers || {})["content-type"] || "").split(";")[0].trim().toLowerCase(); }
988
757
  function _isPkixcmp(headers) { var t = _ctypeToken(headers); return t === PKIXCMP || t === PKIXCMP_POLL; }
989
758
 
990
- // Classify the CMP HTTP response (RFC 9811 sec. 3.1/3.2/4). 200 + a pkixcmp body -> parse + resolve;
991
- // another 2xx -> reject (a request response MUST be 200, sec. 3.1); 3xx -> not followed (sec. 3.1/5);
992
- // 4xx/5xx WITH a well-formed CMP body -> FORWARD the integrity-protected verdict (sec. 1.2/3.1), else
993
- // cmp/http-error. The HTTP status is surfaced as DATA, never used to set the transaction verdict (sec. 5).
994
- // `body` is always a Buffer (the verb normalizes it before the size cap).
995
759
  function _classifyCmpResponse(status, headers, body, tls) {
996
760
  var ctype = _ctypeToken(headers);
997
761
  if (status === 200) {
998
762
  if (!_isPkixcmp(headers)) throw _err("cmp/bad-content-type", "a 200 CMP response must be application/pkixcmp (RFC 9811 sec. 3.2), got " + JSON.stringify(ctype || null));
999
763
  if (body.length === 0) throw _err("cmp/empty-response", "a 200 CMP response carried an empty body (RFC 9811 sec. 3.3)");
1000
- return { response: cmp.parse(body), responseBytes: body, status: 200, contentType: ctype, tls: tls }; // a malformed body surfaces the parser's fail-closed cmp/* verdict
764
+ return { response: cmp.parse(body), responseBytes: body, status: 200, contentType: ctype, tls: tls };
1001
765
  }
1002
- // Below 200, every branch requires a valid HTTP status INTEGER -- a missing / non-numeric / out-of-range
1003
- // status from an injected transport must not fall through to the forward branch as if it were a 4xx/5xx.
1004
766
  var httpStatus = (typeof status === "number" && Number.isSafeInteger(status) && status >= 100 && status <= 599) ? status : null;
1005
767
  if (httpStatus !== null && httpStatus >= 300 && httpStatus < 400) throw _err("cmp/redirect-not-followed", "a CMP " + httpStatus + " redirect is not followed (RFC 9811 sec. 3.1, sec. 5); reconfigure the endpoint URL");
1006
768
  if (httpStatus !== null && httpStatus >= 400 && httpStatus <= 599) {
@@ -1008,15 +770,11 @@ function _classifyCmpResponse(status, headers, body, tls) {
1008
770
  var forwarded;
1009
771
  try { forwarded = cmp.parse(body); }
1010
772
  catch (e) { throw _err("cmp/http-error", "the CMP server returned HTTP " + httpStatus + " with an undecodable body", e); }
1011
- // Only a CMP `error` message is a coherent verdict to forward on an HTTP failure. A 4xx/5xx carrying a
1012
- // non-error arm (a granting `ip`, a `pkiconf`) is contradictory -- the HTTP layer says failure while the
1013
- // CMP body says success -- so it is NOT accepted as a usable response; the HTTP failure stands.
1014
773
  if (forwarded.body.arm !== "error") throw _err("cmp/http-error", "the CMP server returned HTTP " + httpStatus + " with a non-error '" + forwarded.body.arm + "' body; an HTTP failure forwards only a CMP error message (RFC 9811 sec. 1.2/3.1)");
1015
774
  return { response: forwarded, responseBytes: body, status: httpStatus, contentType: ctype, tls: tls };
1016
775
  }
1017
776
  throw _err("cmp/http-error", "the CMP server returned HTTP " + httpStatus + " with no forwardable CMP error body");
1018
777
  }
1019
- // Any other status -- a non-200 2xx, a 1xx informational, or a non-numeric / out-of-range value.
1020
778
  throw _err("cmp/unexpected-status", "a CMP response must be HTTP 200 (or a 4xx/5xx carrying a CMP error), not " + JSON.stringify(status) + " (RFC 9811 sec. 3.1)");
1021
779
  }
1022
780
 
@@ -1027,9 +785,9 @@ function transfer(url, message, opts) {
1027
785
  function _transfer(url, message, opts) {
1028
786
  opts = opts || {};
1029
787
  guard.identifier.assertKnownKeys(opts, KNOWN_TRANSFER_OPTS, _err, "cmp/bad-input", "unknown opts field ");
1030
- var der = _cmpMessageDer(message); // config gate: before any transport call
788
+ var der = _cmpMessageDer(message);
1031
789
  var parsedUrl;
1032
- try { parsedUrl = new URL(String(url)); } // parse only -- NO client scheme gate (the transport owns socket security)
790
+ try { parsedUrl = new URL(String(url)); }
1033
791
  catch (e) { throw _err("cmp/bad-url", "the CMP URL did not parse: " + String(url), e); }
1034
792
  var transport = opts.transport;
1035
793
  if (!transport) {
@@ -1040,11 +798,6 @@ function _transfer(url, message, opts) {
1040
798
  }
1041
799
  var timeout = guard.limits.cap(opts.timeout, "timeout", DEFAULT_TRANSFER_TIMEOUT, { E: _err, code: "cmp/bad-input", min: 1, max: MAX_TRANSFER_TIMEOUT });
1042
800
  var maxResponseBytes = guard.limits.cap(opts.maxResponseBytes, "maxResponseBytes", constants.LIMITS.HTTP_MAX_RESPONSE_BYTES, { E: _err, code: "cmp/bad-input", min: 1, max: constants.LIMITS.HTTP_MAX_RESPONSE_BYTES });
1043
- // Build the request headers: forward custom opts.headers but STRIP the request-framing headers
1044
- // (content-length, transfer-encoding) and ANY content-type case variant -- the verb sets the content-type
1045
- // and the transport computes Content-Length from the exact body, so a caller cannot desync the request
1046
- // framing (HTTP request smuggling via a short/long content-length) or override the media type through
1047
- // header-name casing (a "Content-Type" that Object.assign would keep alongside the lowercase one).
1048
801
  var headers = {};
1049
802
  Object.keys(opts.headers || {}).forEach(function (k) {
1050
803
  var lk = k.toLowerCase();
@@ -1056,10 +809,6 @@ function _transfer(url, message, opts) {
1056
809
  res = res || {};
1057
810
  var h = {};
1058
811
  Object.keys(res.headers || {}).forEach(function (k) { h[k.toLowerCase()] = res.headers[k]; });
1059
- // Normalize the response body to a Buffer preserving its exact bytes: a binary view (Buffer / Uint8Array
1060
- // from an injected transport) is re-viewed through the byte guard, as the request path does; only a
1061
- // genuine string body falls back to a UTF-8 encode (the width a real socket already counts) so the size
1062
- // cap and the parser see the same bytes -- a typed array is not stringified to "48,130,..." garbage.
1063
812
  var body = guard.bytes.isByteSource(res.body)
1064
813
  ? guard.bytes.source(res.body, CmpError, "cmp/bad-response", "the CMP response body")
1065
814
  : Buffer.from(String(res.body == null ? "" : res.body), "utf8");
@@ -1068,9 +817,6 @@ function _transfer(url, message, opts) {
1068
817
  });
1069
818
  }
1070
819
 
1071
- // A single safe path segment for a well-known URI: no separator, dot-segment, or reserved/space char that
1072
- // would retarget the resource; percent-encoded on the way out. Fails closed (guards never guess a value).
1073
- // /[/?#\s\\]/ : does `s` contain a path separator, query/fragment marker, whitespace, or a backslash?
1074
820
  function _hasUnsafeSegChar(s) {
1075
821
  for (var i = 0; i < s.length; i++) {
1076
822
  var c = _charCodeAt(s, i);
@@ -1081,18 +827,13 @@ function _hasUnsafeSegChar(s) {
1081
827
  function _wellKnownSeg(v, name) {
1082
828
  var s = String(v);
1083
829
  if (s === "" || s === "." || s === ".." || _hasUnsafeSegChar(s)) throw _err("cmp/bad-url", "the CMP " + name + " must be a single safe path segment (no '/', dot-segment, or reserved char): " + JSON.stringify(s));
1084
- // encodeURIComponent throws a raw URIError (no .code) on an unpaired UTF-16 surrogate; normalize it to the
1085
- // helper's cmp/bad-url contract so a malformed-config segment never escapes as an untyped error.
1086
830
  try { return encodeURIComponent(s); }
1087
831
  catch (e) { throw _err("cmp/bad-url", "the CMP " + name + " is not encodable (e.g. an unpaired surrogate): " + JSON.stringify(s), e); }
1088
832
  }
1089
833
 
1090
834
  var KNOWN_WELLKNOWN_OPTS = { label: 1, operation: 1 };
1091
835
 
1092
- // ---- URL raw-path helpers by character scan (no regex) --------------------------------------------
1093
836
  function _isAsciiLetter(ch) { return (ch >= "A" && ch <= "Z") || (ch >= "a" && ch <= "z"); }
1094
- // The "scheme://authority" prefix removed (matching /^[a-z]+:\/\/[^/?#]*/i: a leading letter run,
1095
- // "://", then a run of characters other than "/", "?" or "#"), else the string unchanged.
1096
837
  function _stripSchemeAuthority(s) {
1097
838
  var p = 0, n = s.length;
1098
839
  while (p < n && _isAsciiLetter(_charAt(s, p))) p += 1;
@@ -1101,7 +842,6 @@ function _stripSchemeAuthority(s) {
1101
842
  while (p < n) { var ch = _charAt(s, p); if (ch === "/" || ch === "?" || ch === "#") break; p += 1; }
1102
843
  return _strSlice(s, p);
1103
844
  }
1104
- // Everything before the first "?" or "#" (matching split(/[?#]/)[0]).
1105
845
  function _beforeQueryOrFragment(s) {
1106
846
  for (var i = 0; i < s.length; i++) { var ch = _charAt(s, i); if (ch === "?" || ch === "#") return _strSlice(s, 0, i); }
1107
847
  return s;
@@ -1109,28 +849,15 @@ function _beforeQueryOrFragment(s) {
1109
849
 
1110
850
  function wellKnownUrl(base, opts) {
1111
851
  opts = opts || {};
1112
- // Reject an unknown option key (a typo like { lable } / { operaton }), as build/transfer do -- a silently
1113
- // ignored option would build the DEFAULT endpoint and send the request to a different CA/profile.
1114
852
  guard.identifier.assertKnownKeys(opts, KNOWN_WELLKNOWN_OPTS, _err, "cmp/bad-input", function (k) {
1115
853
  return "unknown wellKnownUrl option " + JSON.stringify(k) + " (expected label / operation)";
1116
854
  });
1117
855
  var u;
1118
856
  try { u = new URL(String(base)); }
1119
857
  catch (e) { throw _err("cmp/bad-url", "the CMP base URL did not parse: " + String(base), e); }
1120
- // Require an http(s) authority: a non-HTTP scheme (file:, data:, a custom scheme) has an opaque origin
1121
- // ("null"), which would build a nonsensical "null/.well-known/cmp". A well-known URI is an HTTP concept
1122
- // (RFC 8615); https vs http is left to the transport (Build decision 6), but the scheme must be one of them.
1123
858
  if (u.protocol !== "https:" && u.protocol !== "http:") throw _err("cmp/bad-url", "the CMP base URL must be http or https (a well-known URI is authority-rooted over HTTP), got " + u.protocol + ": " + JSON.stringify(String(base)));
1124
859
  if (u.search || u.hash) throw _err("cmp/bad-url", "the CMP base URL must not carry a query or fragment component (RFC 9811 sec. 3.4)");
1125
- // A backslash has no place in an http(s) authority: WHATWG rewrites it to a path separator (so
1126
- // `https://ca.example\tenant` is really the path `/tenant`), but the raw-path regex below excludes only
1127
- // `/?#` and would swallow it as authority, slipping a supplied path past the check. Reject it outright.
1128
860
  if (_strIndexOf(String(base), "\\") !== -1) throw _err("cmp/bad-url", "the CMP base URL must not contain a backslash (WHATWG rewrites it to a path separator): " + JSON.stringify(String(base)));
1129
- // An RFC 8615 well-known URI is AUTHORITY-ROOTED (/.well-known/...), so a base carrying ANY path is
1130
- // rejected fail-closed -- an operator whose CMP endpoint sits under a path passes that full operation URL
1131
- // to transfer() directly. Inspect the RAW path in the source string, NOT url.pathname: WHATWG normalizes
1132
- // dot-segments (`/tenant/..`, `/%2e`) to "/" before the parse, which would slip a supplied path past a
1133
- // pathname check and silently target the authority-root profile instead of reporting the misconfiguration.
1134
861
  var rawBasePath = _beforeQueryOrFragment(_stripSchemeAuthority(String(base)));
1135
862
  if (rawBasePath !== "" && rawBasePath !== "/") throw _err("cmp/bad-url", "an RFC 8615 well-known URI is authority-rooted; the CMP base URL must have no path component (pass a full operation URL to transfer instead): " + JSON.stringify(String(base)));
1136
863
  var segs = [".well-known", "cmp"];
@@ -1210,17 +937,14 @@ function wellKnownUrl(base, opts) {
1210
937
  */
1211
938
  module.exports = {
1212
939
  build: build, transfer: transfer, wellKnownUrl: wellKnownUrl,
1213
- buildCrlStatusList: buildCrlStatusList, // @internal -- pki.cmp.session composes it for a crlUpdate support message
1214
- // @internal -- one key, several legal SPKI encodings (a compressed EC point among them), so the
1215
- // session compares root-rollover keys through the same definition the builders already use.
940
+ buildCrlStatusList: buildCrlStatusList, // @internal
941
+ // @internal
1216
942
  samePublicKey: function (a, b) { return _b.samePublicKey(a, b); },
1217
- // @internal -- the shared certificate-extension decoders, so a consumer reads basicConstraints or
1218
- // keyUsage through the same definition every format uses instead of walking the bytes again.
943
+ // @internal
1219
944
  decodeCertExtension: function (dottedOid, valueBytes) {
1220
945
  var dec = EXT_DECODERS[dottedOid];
1221
946
  return dec ? dec(valueBytes) : null;
1222
947
  },
1223
- // @internal -- the CRL-side companion: schema-crl leaves issuingDistributionPoint raw, and the
1224
- // session needs the point it names to bind a crlUpdate answer to the point that was requested.
948
+ // @internal
1225
949
  decodeIdpDistributionPoint: decodeIdpDistributionPoint,
1226
950
  };