@blamejs/pki 0.6.14 → 0.6.15

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 +14 -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 +8 -252
  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/cmc-verify.js CHANGED
@@ -22,12 +22,10 @@
22
22
  var asn1 = require("./asn1-der");
23
23
  var oid = require("./oid");
24
24
  var cmc = require("./schema-cmc");
25
- var cmsVerify = require("./cms-verify"); // PD14: the carrier's signature MUST be verified
26
- var cmsDecrypt = require("./cms-decrypt"); // ... and an AuthenticatedData carrier by its MAC
25
+ var cmsVerify = require("./cms-verify");
26
+ var cmsDecrypt = require("./cms-decrypt");
27
27
  var guard = require("./guard-all");
28
28
  var intrinsic = require("./guard-intrinsic");
29
- // Own-key membership through an operation taken at load. Written out, the question reads two
30
- // replaceable properties to answer one thing, and here it decides which controls a response carries.
31
29
  var _hasOwn = intrinsic.hasOwn;
32
30
  var frameworkError = require("./framework-error");
33
31
 
@@ -42,10 +40,6 @@ var OID_DATA_RETURN = O("id-cmc-dataReturn");
42
40
  var OID_TRUSTED_ANCHORS = O("id-cmc-trustedAnchors");
43
41
  var OID_CONFIRM_CERT_ACCEPTANCE = O("id-cmc-confirmCertAcceptance");
44
42
 
45
- // CMCStatus -> the terminal outcome a caller acts on. RFC 5272 sec. 6.1.3 gives
46
- // seven statuses; four of them mean "this exchange did not produce a certificate
47
- // and will not without another message", so they collapse to `rejected` while the
48
- // status list keeps the detail.
49
43
  var OUTCOME_BY_STATUS = {
50
44
  success: "issued",
51
45
  pending: "pending",
@@ -56,26 +50,14 @@ var OUTCOME_BY_STATUS = {
56
50
  noSupport: "rejected",
57
51
  };
58
52
 
59
- // Which outcome governs when a response carries SEVERAL status controls (sec.
60
- // 6.1 requires a client to cope with that). Ranked worst-first so the verdict can
61
- // never be improved by the order the controls happen to appear in: reporting
62
- // the first one seen would let a server bury a failure behind a success.
63
53
  var OUTCOME_SEVERITY = { rejected: 4, "pop-required": 3, "confirm-required": 2, pending: 1, issued: 0 };
64
54
 
65
- // The single AttributeValue of a control that carries exactly one.
66
55
  function _singleValue(control, what) {
67
56
  if (control.values.length !== 1) {
68
57
  throw E("cmc/bad-control", "the " + what + " control carries exactly one value, got " + control.values.length);
69
58
  }
70
59
  return control.values[0];
71
60
  }
72
- // The single instance of a control, or null when absent.
73
- //
74
- // Body-part identity is unique per element, so a responder can legally carry the
75
- // same control type twice under different bodyPartIDs. Taking the first match
76
- // would let an attacker pair one correct echo with a contradictory one and still
77
- // satisfy the binding, so a duplicate is refused as ambiguous instead of
78
- // resolved. The exchange either has one answer or it has none.
79
61
  function _findControl(controls, attrType, what) {
80
62
  var found = null;
81
63
  for (var i = 0; i < controls.length; i++) {
@@ -92,12 +74,6 @@ function _octets(control, what) {
92
74
  return asn1.read.octetString(asn1.decode(_singleValue(control, what)));
93
75
  }
94
76
 
95
- // The certificate / CRL bag, from whichever carrier holds it. A SignedData keeps
96
- // them at the top level; an AuthenticatedData keeps them under `originatorInfo`
97
- // (RFC 5652 sec. 9.1). Both carriers are accepted for a Full PKI Response, so
98
- // reading only the SignedData shape would silently return an empty bag, and
99
- // "no certificates were issued" is exactly the wrong thing to report when some
100
- // were.
101
77
  function _bagOf(parsedCms, originatorKey, topKey) {
102
78
  if (!parsedCms) return [];
103
79
  var list = parsedCms[topKey];
@@ -105,29 +81,13 @@ function _bagOf(parsedCms, originatorKey, topKey) {
105
81
  return (list || []).map(function (c) { return c.bytes; });
106
82
  }
107
83
 
108
- /**
109
- * The transaction binding -- RFC 5272 sec. 6.6, RFC 5273 sec. 6.
110
- *
111
- * Both halves are CONDITIONAL on what the client sent, which is the subtlety: a
112
- * client that sent no transactionId cannot demand one back, and a client that
113
- * sent no senderNonce has nothing to compare a recipientNonce against. What is
114
- * not conditional is the converse: having sent one, an absent or differing echo
115
- * is a refusal, because that is exactly the replay the nonce exists to stop.
116
- *
117
- * Returns which halves ran, because "conditional" and "checked" are different
118
- * facts and the verdict has to be able to tell them apart.
119
- */
84
+
120
85
  function _assertBound(body, sent) {
121
- // A zero-length value binds nothing. An empty senderNonce echoes equal in every
122
- // exchange that also used one, so counting its presence would report boundToRequest
123
- // true for a response captured from any of them -- the exact replay this gate exists
124
- // to refuse, passing because the comparison it ran was vacuous. The same holds for an
125
- // empty transactionId or dataReturn: a binding has to carry entropy to be a binding.
126
86
  function _carries(v) {
127
87
  if (v == null) return false;
128
88
  if (typeof v === "string") return v.length > 0;
129
89
  if (Buffer.isBuffer(v) || v instanceof Uint8Array) return v.length > 0; // allow:byte-source-narrow -- sent.senderNonce / dataReturn are already normalized to a Buffer by _snapshotSent; this is a length test on a settled value, not an input door
130
- return true; // an integer or other scalar identifier is its own value
90
+ return true;
131
91
  }
132
92
  var bound = {
133
93
  transactionId: _carries(sent.transactionId),
@@ -142,9 +102,6 @@ function _assertBound(body, sent) {
142
102
  "the request carried a Transaction Identifier control, so the response MUST include the same one (RFC 5272 sec. 6.6)");
143
103
  }
144
104
  var got = asn1.read.integer(asn1.decode(_singleValue(txControl, "Transaction Identifier")));
145
- // Compared against the value the authoring guard validated, never a fresh
146
- // conversion of the caller's input -- a number too large to hold an integer
147
- // exactly has already lost the digits that distinguish it from its neighbor.
148
105
  if (got !== sent.transactionIdValue) {
149
106
  throw E("cmc/transaction-mismatch",
150
107
  "the response Transaction Identifier " + got + " does not match the request's " + sent.transactionId);
@@ -157,17 +114,11 @@ function _assertBound(body, sent) {
157
114
  throw E("cmc/nonce-mismatch",
158
115
  "the request carried a Sender Nonce, so the response MUST reflect it back as a Recipient Nonce control (RFC 5272 sec. 6.6)");
159
116
  }
160
- // Constant-time and by full value: guard.crypto.constantTimeEqual gates the
161
- // length first, so a truncation or a prefix is an honest false and not a
162
- // match on the bytes that happen to line up.
163
117
  if (!guard.crypto.constantTimeEqual(_octets(rn, "Recipient Nonce"), Buffer.from(sent.senderNonce))) {
164
118
  throw E("cmc/nonce-mismatch", "the response Recipient Nonce does not match the Sender Nonce the request sent");
165
119
  }
166
120
  }
167
121
 
168
- // RFC 5272 sec. 6.4: "If the Data Return control appears in a Full PKI Request,
169
- // the server MUST return it as part of the PKI Response." The data is opaque to
170
- // the server, so the check is that the same bytes came back.
171
122
  if (sent.dataReturn != null) {
172
123
  var dr = _findControl(body.controls, OID_DATA_RETURN, "Data Return");
173
124
  if (!dr) {
@@ -179,15 +130,6 @@ function _assertBound(body, sent) {
179
130
  }
180
131
  }
181
132
 
182
- // A status names, in its bodyList, the body parts it is ABOUT (RFC 5272
183
- // sec. 6.1.1). A status about a body part the request never carried is not an
184
- // answer to this request -- and the binding controls do not catch it, because a
185
- // server can echo the transaction and nonce correctly while reporting on
186
- // something else entirely. Held to the same asymmetry as every other half of the
187
- // binding: checked only when the client kept the set, and once kept, a reference
188
- // outside it is a refusal.
189
- // Element-by-element, and the lengths must agree: a retained `[a, b]` must not admit a
190
- // reported `[a, b, c]`, which names a part one level deeper than anything sent.
191
133
  function _pathRetained(retained, want) {
192
134
  if (!Array.isArray(retained)) return false;
193
135
  return retained.some(function (have) {
@@ -199,18 +141,10 @@ function _assertBound(body, sent) {
199
141
  if (Array.isArray(sent.bodyPartIDs)) {
200
142
  var known = Object.create(null);
201
143
  sent.bodyPartIDs.forEach(function (id) { known[String(id)] = true; });
202
- // 0 is reserved as the reference to the enclosing PKIData itself (sec. 3.2.1),
203
- // so a status about the request as a whole is in the set by definition.
204
144
  known["0"] = true;
205
145
  body.statuses.forEach(function (s) {
206
146
  (s.bodyList || []).forEach(function (ref) {
207
147
  var path = ref.bodyPartPath;
208
- // A bodyPartPath descends INTO a nested message. Checking only its head would
209
- // accept `[a part we sent, 999]`, a status about something arbitrary wearing a
210
- // reference that passes, so the whole path is matched against the paths the
211
- // request actually composed. Those are retained by reading each nested message
212
- // back; a nested message that could not be read back contributes none, so a path
213
- // into it finds no match and is refused. Either way the tail is never waved through.
214
148
  if (path && path.length > 1) {
215
149
  if (_pathRetained(sent.bodyPartPaths, path)) return;
216
150
  throw E("cmc/body-part-unknown",
@@ -227,24 +161,6 @@ function _assertBound(body, sent) {
227
161
  });
228
162
  }
229
163
 
230
- // Every check above is conditional on the caller having retained something, so a
231
- // caller who retained nothing runs none of them and still gets a full verdict
232
- // (`issued`, signature verified, certificates surfaced) off a Full PKI Response
233
- // captured from any earlier exchange with the same CA. That is the CWE-294 this
234
- // module names as its defense, and leaving it opt-in leaves the defense off for
235
- // whoever did not know to ask.
236
- //
237
- // The posture here is the one _assertAuthentic already takes for the carrier
238
- // signature, applied to the other question: check it when the caller supplies
239
- // what it takes, and refuse when nothing does, unless the caller names that. The
240
- // two opt-outs stay separate because they answer separate questions. A replayed
241
- // response is authentic, so `allowUnverified` covering it would let "I could not
242
- // check the signature" stand in for "I did not check which exchange this
243
- // answers".
244
- //
245
- // bodyPartIDs does not count toward it. Identifiers are allocated per message
246
- // from a small range (sec. 3.2.2), so the same set recurs across requests and a
247
- // status naming one says nothing about which request it answers.
248
164
  bound.boundToRequest = bound.transactionId || bound.senderNonce || bound.dataReturn;
249
165
  if (!bound.boundToRequest && sent.allowUnbound !== true) {
250
166
  throw E("cmc/unbound-response",
@@ -256,9 +172,6 @@ function _assertBound(body, sent) {
256
172
  return bound;
257
173
  }
258
174
 
259
- // The worst outcome across every status control, with success assumed when there
260
- // is none at all (RFC 5272 sec. 6.1.2: "If no status exists for a Simple or Full
261
- // PKI Request, then the value of success is assumed").
262
175
  function _reduceOutcome(statuses) {
263
176
  var outcome = "issued";
264
177
  for (var i = 0; i < statuses.length; i++) {
@@ -268,9 +181,6 @@ function _reduceOutcome(statuses) {
268
181
  return outcome;
269
182
  }
270
183
 
271
- // The pendInfo / failInfo detail belonging to the status that GOVERNED, so a
272
- // caller reading `pendToken` gets the one attached to the pending verdict rather
273
- // than whichever status happened to carry a token.
274
184
  function _governingStatus(statuses, outcome) {
275
185
  for (var i = 0; i < statuses.length; i++) {
276
186
  if (OUTCOME_BY_STATUS[statuses[i].status] === outcome) return statuses[i];
@@ -373,17 +283,12 @@ function _governingStatus(statuses, outcome) {
373
283
  * v.boundToRequest; // false: nothing ties this response to a request either
374
284
  */
375
285
  function verify(response, sent) {
376
- // Both snapshots are taken SYNCHRONOUSLY, here, before anything is deferred.
377
- // Taking them inside the async body would be too late by one microtask: a caller
378
- // that mutates the buffer or the options object on the line after this call has
379
- // already changed them before that body ever runs, which is the easiest version
380
- // of the race to hit by accident.
381
286
  var frozenResponse, frozenSent;
382
287
  try {
383
288
  frozenSent = _snapshotSent(_assertOpts(sent));
384
289
  frozenResponse = _snapshotIfBytes(response);
385
290
  } catch (e) {
386
- return Promise.reject(e); // the surface stays promise-rejecting, never throwing
291
+ return Promise.reject(e);
387
292
  }
388
293
  return guard.async.deferred(function () { return _verify(frozenResponse, frozenSent); });
389
294
  }
@@ -396,35 +301,13 @@ function _assertOpts(sent) {
396
301
  return sent;
397
302
  }
398
303
 
399
- // A private copy of the input when -- and only when -- it is memory the caller can
400
- // still write to. Anything else is returned untouched so this cannot narrow the
401
- // input contract of the parse it feeds; pki.schema.cmc.parse stays the one place
402
- // that decides what an acceptable input is, and keeps raising its own typed error.
403
304
  function _snapshotIfBytes(input) {
404
- // EVERY byte form the parser accepts, not just the two most common. It takes a
405
- // BufferSource, so an ArrayBuffer or a DataView reaches the decoder too -- and
406
- // leaving those aliased would reopen the window for exactly the inputs that came
407
- // in by the wider door.
408
305
  if (guard.bytes.isByteSource(input)) {
409
306
  return guard.bytes.snapshotSource(input, CmcError, "cmc/bad-input", "pki.cmc.verify");
410
307
  }
411
308
  return input;
412
309
  }
413
310
 
414
- // A private copy of the exchange state the binding checks compare against. Scalars
415
- // are copied by value and every byte buffer is copied, so nothing the caller still
416
- // holds a reference to can change what "what we sent" means once verification has
417
- // begun. Unknown fields are carried through untouched -- this fixes the values the
418
- // checks below read, it does not narrow what the options object may contain.
419
- // EVERY byte form, not just the two most common: a DataView or a bare ArrayBuffer arrives by the
420
- // same door as a Buffer, and copying only Buffer and Uint8Array would leave those aliased across
421
- // the very gap this closes -- reopening the window for exactly the inputs that came in by the
422
- // wider one. A DataView is copied over its OWN window, not the whole buffer it happens to sit in.
423
- // One call, because guard.bytes decides what a byte form is and where its bytes are. The three
424
- // hand-rolled branches this replaces read `v.buffer`, `v.byteOffset` and `v.byteLength` off the
425
- // value, and all three are accessors a caller's subclass can answer: one that lied copied a
426
- // different store than the array held, so the bytes checked here and the bytes signed later were
427
- // two different reads.
428
311
  function _copyAnyBytes(v) {
429
312
  if (!guard.bytes.isByteSource(v)) return v;
430
313
  return guard.bytes.snapshotSource(v, CmcError, "cmc/bad-input", "a byte field of the request");
@@ -433,29 +316,12 @@ function _copyAnyBytes(v) {
433
316
  function _snapshotSent(sent) {
434
317
  var out = {}, k;
435
318
  for (k in sent) { if (_hasOwn(sent, k)) out[k] = sent[k]; }
436
- // Normalized through the SAME authoring guard pki.cmc.build puts an authored
437
- // integer through. A Transaction Identifier is an unbounded INTEGER on the wire,
438
- // so one above Number.MAX_SAFE_INTEGER has already been rounded by the time it
439
- // arrives as a `number` -- 9007199254740993 is 9007199254740992 before this code
440
- // sees it, and a response echoing that NEIGHBORING identifier would compare
441
- // equal. Held here rather than converted, so a value too large to be a number is
442
- // refused with the shape to use instead (a bigint) rather than silently bound to
443
- // something adjacent.
444
- // Validated, not replaced: the verdict echoes `transactionId` as the caller gave
445
- // it, so the check is added without changing what comes back.
446
- // Through ONE helper so nothing here can copy "the two common byte forms" and leave the rest
447
- // aliased -- the narrowing that reopens this window for whatever came in by the wider door.
448
319
  if (out.transactionId != null) {
449
320
  out.transactionIdValue = guard.range.authoredInteger(out.transactionId, E, "cmc/bad-input", "sent.transactionId");
450
321
  }
451
322
  out.senderNonce = _copyAnyBytes(out.senderNonce);
452
323
  out.dataReturn = _copyAnyBytes(out.dataReturn);
453
- // Copied for the same reason the nonce is: the check runs after an await, and an
454
- // array the caller still holds could have body parts appended to it in the gap,
455
- // widening what the response is allowed to report on.
456
324
  if (Array.isArray(out.bodyPartIDs)) out.bodyPartIDs = out.bodyPartIDs.slice();
457
- // Both levels: the outer array AND each path, since appending a segment to a retained path
458
- // in the gap would widen what the response may report on exactly as appending an id does.
459
325
  if (Array.isArray(out.bodyPartPaths)) {
460
326
  out.bodyPartPaths = out.bodyPartPaths.map(function (p) { return Array.isArray(p) ? p.slice() : p; });
461
327
  }
@@ -468,18 +334,11 @@ function _snapshotSent(sent) {
468
334
  out.recipient = r;
469
335
  }
470
336
  if (Array.isArray(out.certs)) out.certs = out.certs.map(_copyAnyBytes);
471
- // Both opt-outs normalized to a strict boolean off the ORIGINAL object, so a
472
- // truthy-but-not-true value cannot switch a check off by accident.
473
337
  out.allowUnverified = sent.allowUnverified === true;
474
338
  out.allowUnbound = sent.allowUnbound === true;
475
339
  return out;
476
340
  }
477
341
 
478
- // `response` and `sent` arrive already frozen by verify() above -- BOTH sides of
479
- // every comparison, not just the response bytes. The checks below run across an
480
- // await and compare what the CA returned against what the caller says it sent, so
481
- // freezing one side while reading the other live would leave half of each
482
- // comparison mutable.
483
342
  function _verify(response, sent) {
484
343
  var body = cmc.parse(response);
485
344
  if (body.kind !== "pkiResponse") {
@@ -492,73 +351,25 @@ function _verify(response, sent) {
492
351
  });
493
352
  }
494
353
 
495
- /**
496
- * PD14 -- RFC 5272 sec. 3.2.1.3.4: "As part of processing a PKI Request/Response,
497
- * the signature(s) MUST be verified."
498
- *
499
- * A Full PKI Response is a SignedData (sec. 4.2), so a carrier with no SignerInfo
500
- * at all is not one -- that is the Simple response's shape, and reading an
501
- * `issued` verdict off it would report an enrollment result nobody signed.
502
- *
503
- * Verification needs the signer's certificate, which this layer cannot invent.
504
- * So the posture is fail-closed with a NAMED opt-out rather than a silent one:
505
- * supply `certs` and the signature is checked; supply `allowUnverified: true` and
506
- * the verdict says `signatureVerified: false` so nothing downstream can mistake
507
- * it for an authenticated answer. Doing neither is refused, because that is the
508
- * case where a caller would otherwise believe a check happened.
509
- */
354
+
510
355
  function _assertAuthentic(body, sent, responseBytes) {
511
356
  return Promise.resolve().then(function () {
512
- // Authentication is over BYTES, and that is true of BOTH carriers -- a MAC and
513
- // a signature alike. The already-parsed input form hands this layer an object
514
- // the CALLER still owns: the verdict is read from it synchronously, the check
515
- // runs a microtask later, and a caller that swaps the encapsulated content,
516
- // the signers and the certificate bag in the gap would get one message's
517
- // verdict reported beside another message's verified signature. There are no
518
- // bytes to pin, so the honest answer is that nothing was authenticated.
519
357
  var haveBytes = typeof responseBytes === "string" ||
520
358
  Buffer.isBuffer(responseBytes) || responseBytes instanceof Uint8Array; // allow:byte-source-narrow -- responseBytes is _snapshotIfBytes's return: any BufferSource is already a Buffer here, so this is Buffer-or-string by construction
521
- // The two accepted carriers are authenticated DIFFERENTLY, so the rule has to
522
- // be written for both: a SignedData by its SignerInfo signatures, an
523
- // AuthenticatedData by a MAC over a key this layer never holds. Checking only
524
- // the SignedData shape would reject every conforming AuthenticatedData
525
- // response for having no signer -- which is not a defect in the message.
526
359
  if (body.cms && body.cms.contentTypeName === "authData") {
527
- // The MAC needs key material this layer does not invent -- but a caller who
528
- // HAS it should not be forced through the unauthenticated opt-out, which
529
- // would report signatureVerified:false for a response whose MAC checked out.
530
- // pki.cms.decrypt owns AuthenticatedData; `recipient` is handed to it rather
531
- // than the MAC being recomputed here.
532
360
  if (sent.recipient != null) {
533
- // pki.cms.decrypt deliberately refuses a pre-parsed object too, so nothing
534
- // can hand it a parse result that skipped DER validation. When the caller
535
- // used the already-parsed input form there are no bytes to check, and
536
- // saying exactly that is the honest answer -- reporting "did not
537
- // authenticate" would blame the message for what is a missing input.
538
361
  if (!haveBytes) {
539
362
  throw E("cmc/bad-input",
540
363
  "authenticating an AuthenticatedData response needs the response as DER bytes or PEM, " +
541
364
  "because its MAC is computed over them -- pass the encoded response rather than a parsed one");
542
365
  }
543
- // It re-parses, and the content check below then ties its result back to
544
- // what was interpreted here.
545
366
  return cmsDecrypt.decrypt(responseBytes, sent.recipient).then(null, function (e) {
546
- // The CMS layer's code does not escape this surface. cms/decrypt-failed is
547
- // deliberately oracle-free -- every secret-dependent failure collapses into
548
- // it -- and that property is preserved by re-throwing it as this domain's
549
- // own verdict with the cause chained, rather than adding any detail.
550
367
  throw E("cmc/unverified-response",
551
368
  "the AuthenticatedData response did not authenticate under the supplied recipient key", e);
552
369
  }).then(function (res) {
553
370
  if (!res || res.authenticated !== true) {
554
371
  throw E("cmc/unverified-response", "the AuthenticatedData response did not authenticate under the supplied recipient key");
555
372
  }
556
- // The MAC covers BYTES; bind them to the ones that were interpreted.
557
- // Verifying a MAC over content and then reporting a verdict read from
558
- // somewhere else is the same defect as checking a signature over the
559
- // wrong region -- authentic, and about a different message.
560
- // Both are already Buffers -- pki.cms.decrypt returns one, and the parse
561
- // surfaces one -- so they are compared directly rather than re-wrapped.
562
373
  var authed = res.content;
563
374
  var eContent = body.cms.encapContentInfo && body.cms.encapContentInfo.eContent;
564
375
  if (!eContent || !Buffer.isBuffer(authed) || !guard.crypto.constantTimeEqual(authed, eContent)) {
@@ -577,12 +388,6 @@ function _assertAuthentic(body, sent, responseBytes) {
577
388
  throw E("cmc/unsigned-response",
578
389
  "a Full PKI Response is a SignedData carrying at least one SignerInfo; this carrier has none, so there is no signature to verify (RFC 5272 sec. 4.2 / 3.2.1.3.4)");
579
390
  }
580
- // Same rule as the MAC arm above, applied where a signature is equally over
581
- // bytes. The carrier's SHAPE is judged first -- an unsigned one is refused
582
- // whatever form it arrived in -- and only the act of authenticating needs the
583
- // bytes. A caller who has nothing but a parsed message can still interpret it
584
- // through the named opt-out, which reports signatureVerified:false; what is
585
- // refused is the combination that would claim a check nothing pinned.
586
391
  if (!haveBytes) {
587
392
  if (sent.allowUnverified === true) return false;
588
393
  throw E("cmc/bad-input",
@@ -590,34 +395,14 @@ function _assertAuthentic(body, sent, responseBytes) {
590
395
  "signature is over them -- pass the encoded response rather than a parsed one, or " +
591
396
  "`allowUnverified: true` to interpret it unauthenticated");
592
397
  }
593
- // Verified over the SAME immutable snapshot the verdict was parsed from, not
594
- // over the parse result: the two cannot describe different messages when both
595
- // come from one private copy of the bytes.
596
- //
597
- // TRY first, and only report "cannot verify" when the signer certificate is
598
- // genuinely nowhere to be found. A conforming SignedData carries its own signer
599
- // certificate, which is the shape pki.cmc.build emits and the one a CA sends --
600
- // demanding that the caller ALSO pass it would make the ordinary
601
- // build-then-verify flow fail for a message that already contains everything
602
- // needed. `certs` supplements the embedded bag; it was never meant to be the
603
- // only source. What does not change is the posture: a signature that is present
604
- // and does not verify is a refusal, and an unverifiable one still needs the
605
- // named opt-out rather than passing quietly.
606
398
  return cmsVerify.verify(responseBytes, sent.certs && sent.certs.length ? { certs: sent.certs } : {})
607
399
  .then(function (res) {
608
400
  if (res.valid) return true;
609
- // EVERY failing signer, not just the first. A response may carry several,
610
- // and the opt-out may only be honored when none of them was actually
611
- // checked and rejected: one signer whose certificate is missing must not
612
- // let a DIFFERENT signer's failed signature through beside it.
613
401
  var failed = (res.signers || []).filter(function (s) { return !s.ok; });
614
402
  var code = failed.length ? failed[0].code : null;
615
403
  var onlyMissing = failed.length > 0 && failed.every(function (s) {
616
404
  return s.code === "cms/signer-cert-not-found";
617
405
  });
618
- // No signer certificate anywhere is "could not check", which the opt-out
619
- // covers. A signature that failed against a certificate we DID find is a
620
- // real failure, and no opt-out excuses it.
621
406
  if (onlyMissing && sent.allowUnverified === true) return false;
622
407
  throw E("cmc/unverified-response",
623
408
  "the CMC response signature did not verify" + (code ? " (" + code + ")" : "") +
@@ -637,7 +422,6 @@ function _shape(body, sent, signatureVerified, bound) {
637
422
  return {
638
423
  outcome: outcome,
639
424
  statuses: body.statuses,
640
- // The detail belonging to the governing verdict, flattened for the common read.
641
425
  failInfo: governing ? governing.failInfoName : null,
642
426
  failInfoValue: governing ? governing.failInfo : null,
643
427
  extendedFailInfo: governing ? governing.extendedFailInfo : null,
@@ -645,29 +429,14 @@ function _shape(body, sent, signatureVerified, bound) {
645
429
  pendTime: governing && governing.pendInfo ? governing.pendInfo.pendTime : null,
646
430
  statusString: governing ? governing.statusString : null,
647
431
  transactionId: sent.transactionId != null ? sent.transactionId : null,
648
- // The responder's OWN nonce, retained by the caller for the next leg of the
649
- // same transaction (RFC 5272 sec. 6.6).
650
432
  senderNonce: serverNonce ? _octets(serverNonce, "Sender Nonce") : null,
651
- // Surfaced, never acted on -- see the primitive's note on trust.
652
433
  certificates: _bagOf(body.cms, "certs", "certificates"),
653
434
  crls: _bagOf(body.cms, "crls", "crls"),
654
- // The response's OWN two sequences, raw. For a request whose only arm was the
655
- // other-message form there is no certificate to return, and RFC 5272 sec. 4.1
656
- // puts the answer here instead -- so leaving them out would give such an
657
- // exchange a successful verdict with its result unreachable, and send the
658
- // caller back to the encapsulated bytes to re-parse what was already decoded.
659
435
  cmsSequence: (body.cmsSequence || []).slice(),
660
436
  otherMsgs: (body.otherMsgs || []).slice(),
661
437
  publishTrustAnchors: anchors ? anchors.values.slice() : null,
662
438
  confirmCertId: confirm ? _singleValue(confirm, "Confirm Certificate Acceptance") : null,
663
- // Whether the CARRIER's signature was checked. False only via the explicit
664
- // allowUnverified opt-out -- there is no path that leaves it false silently.
665
439
  signatureVerified: signatureVerified,
666
- // Which halves of the exchange binding ran (RFC 5272 sec. 6.6 / 6.4). Each is
667
- // conditional on the caller having retained the value, so a bare `outcome`
668
- // cannot say whether this response answers this request or an earlier one.
669
- // These can. `boundToRequest` is false only via the explicit allowUnbound
670
- // opt-out; nothing leaves it false silently.
671
440
  bound: {
672
441
  transactionId: bound.transactionId,
673
442
  senderNonce: bound.senderNonce,
@@ -675,8 +444,6 @@ function _shape(body, sent, signatureVerified, bound) {
675
444
  bodyPartIDs: bound.bodyPartIDs,
676
445
  },
677
446
  boundToRequest: bound.boundToRequest,
678
- // Whether anything in here was TRUSTED, which is never: the certificate bag
679
- // and any Publish Trust Anchors control are the caller's to path-validate.
680
447
  trusted: false,
681
448
  controls: body.controls,
682
449
  unhandled: body.unhandled,