@blamejs/pki 0.9.1 → 0.9.3

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.
package/lib/est.js CHANGED
@@ -1513,7 +1513,7 @@ function _fullcmcResult(res, opts, wanted, sent) {
1513
1513
  bound: { transactionId: false, senderNonce: false, dataReturn: false, bodyPartIDs: false },
1514
1514
  boundToRequest: false });
1515
1515
  }
1516
- return cmcVerify.verify(der, sent).then(function (verdict) {
1516
+ return intrinsic.chain(cmcVerify.verify(der, sent), function (verdict) {
1517
1517
  if (verdict.outcome !== "issued") return verdict;
1518
1518
  if (!wanted.length) { verdict.issuedCertificates = []; return verdict; }
1519
1519
  var issuedCerts = _correlateIssued(verdict.certificates, wanted, "CMC-response");
@@ -1524,12 +1524,12 @@ function _fullcmcResult(res, opts, wanted, sent) {
1524
1524
  }
1525
1525
 
1526
1526
  function _tryDecodeCmcFault(res, sent) {
1527
- return _promiseResolve(_Promise, undefined).then(function () {
1527
+ return intrinsic.chain(intrinsic.chain(_promiseResolve(_Promise, undefined), function () {
1528
1528
  var pt = _partMediaType(_ciHeader(res.headers, "content-type"));
1529
1529
  if (pt.media !== "application/pkcs7-mime") return null;
1530
1530
  if (pt.ambiguous) return null;
1531
1531
  if (String(pt.smimeType || "").toLowerCase() !== "cmc-response") return null;
1532
- return cmcVerify.verify(transferDecode(res.body), sent).then(function (verdict) {
1532
+ return intrinsic.chain(cmcVerify.verify(transferDecode(res.body), sent), function (verdict) {
1533
1533
  if (verdict.outcome !== "rejected") return null;
1534
1534
  var e = E("est/cmc-failed",
1535
1535
  "the EST server rejected the Full PKI Request: " + verdict.outcome +
@@ -1538,7 +1538,7 @@ function _tryDecodeCmcFault(res, sent) {
1538
1538
  e.httpStatus = res.status;
1539
1539
  return e;
1540
1540
  });
1541
- }).then(null, function () { return null; });
1541
+ }), null, function () { return null; });
1542
1542
  }
1543
1543
 
1544
1544
  function _isTypedError(e) {
@@ -31,6 +31,7 @@ var _mapSet = intrinsic.mapSet;
31
31
  var _mapHas = intrinsic.mapHas;
32
32
  var _Set = intrinsic.Set;
33
33
  var _Map = intrinsic.Map;
34
+ var _ObjectProto = intrinsic.ObjectProto;
34
35
  var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
35
36
  var _strSplit = intrinsic.uncurry(String.prototype.split);
36
37
  var _fnToString = intrinsic.uncurry(Function.prototype.toString);
@@ -92,6 +93,54 @@ function assertKnownKeys(obj, known, E, code, message) {
92
93
  });
93
94
  }
94
95
 
96
+ /**
97
+ * @internal
98
+ * Whether `k` is `v`'s function machinery, owned or reached from its prototype chain. A check that
99
+ * walks the chain asks this; a reader that copies one LEVEL at a time asks `isOwnMachineryName`.
100
+ *
101
+ * Which of the two a given name is depends on the function and the engine. A STRICT function, which
102
+ * is every function in an ES module or a `"use strict"` file, owns neither `arguments` nor `caller`,
103
+ * and `Function.prototype` carries both as the restricted accessors, so a chain walk finds them
104
+ * there. On the Node 24.21.0 floor a non-strict function owns both outright. Measured before this
105
+ * covered the inherited side: a plain strict callable descriptor was refused for `arguments` by
106
+ * `pki.cms.sign` and `pki.cms.countersign`, while the minted floor shape passed, which is the shape
107
+ * the vectors were written on.
108
+ */
109
+ // @enforced-by behavioral -- the vectors that drive a PLAIN callable and a floor-shaped one through
110
+ // @enforced-by behavioral -- each door are the guard; the name set has no rename-proof code shape
111
+ function isMachineryName(v, k) {
112
+ if (v === null || v === undefined) return false;
113
+ if (isOwnMachineryName(v, k)) return true;
114
+ if (typeof v !== "function" || !_CALLABLE_MACHINERY.has(k)) return false;
115
+ /** @internal Not owned, so `readableNames` reached it through the prototype chain. A caller's own
116
+ * property of that name is theirs and stays refused by name, which `isOwnMachineryName` above
117
+ * decides; making one takes `defineProperty`, since assigning `fn.arguments` reaches the
118
+ * restricted setter and throws. */
119
+ return _getOwnPropertyDescriptor(v, k) === undefined;
120
+ }
121
+
122
+ /**
123
+ * @internal
124
+ * `assertKnownKeys` for a bag a verb documents as acceptably CALLABLE, where the names a function
125
+ * carries by construction are not fields the caller named. The exemption belongs at the door that
126
+ * refuses, not only at a reader that copies: `pki.smime.sign` snapshots a descriptor before handing
127
+ * it on, so excluding the machinery there left a caller going straight to `pki.cms.sign` with the
128
+ * same callable refused for `length`.
129
+ *
130
+ * It is not `assertKnownKeys`'s own rule, because a bare function handed in where an OPTIONS BAG
131
+ * belongs is refused for exactly those names, which is a contract of its own.
132
+ */
133
+ // @enforced-by behavioral -- the vectors that drive a callable descriptor through each door are the
134
+ // @enforced-by behavioral -- guard; the exemption has no rename-proof code shape
135
+ function assertKnownDescriptorKeys(obj, known, E, code, message) {
136
+ _refuseUnenumerable(obj, E, code, "the options");
137
+ var describe = typeof message === "function" ? message : function (k) { return message + _stringify(k); };
138
+ _forEach(_readableNames(obj), function (k) {
139
+ if (_hasOwn(known, k) || isMachineryName(obj, k)) return;
140
+ throw E(code, describe(typeof k === "symbol" ? _String(k) : k));
141
+ });
142
+ }
143
+
95
144
  // @enforced-by behavioral -- reading a property twice has no rename-proof code shape; the RED
96
145
  // @enforced-by behavioral -- vectors that drive an accessor-backed option are the guard
97
146
  function snapshotOptions(obj, known) {
@@ -320,6 +369,64 @@ function _kindMembers(v) {
320
369
  return [];
321
370
  }
322
371
 
372
+ /** @internal The names a callable carries before a caller assigns a field, asked of the engine
373
+ * rather than listed here: a function's own names, plus `Function.prototype`'s, which is where the
374
+ * restricted `arguments` and `caller` accessors live. Which of them a given function makes OWN
375
+ * depends on the engine. Measured: on Node 24.21.0, the version `engines.node` floors at, every
376
+ * non-strict function carries own `arguments` and `caller`; on 26.9.0 none does, for a declaration
377
+ * and for `new Function` alike. A list written against one of those refuses a legitimate callable
378
+ * descriptor on the other, and a caller's non-strict CommonJS module is where the shape comes
379
+ * from. */
380
+ var _CALLABLE_MACHINERY = (function () {
381
+ var names = _create(null), symbols = new _Set();
382
+ /** @internal One pristine probe per callable KIND, because the kinds do not share a prototype: an
383
+ * async function, a generator and an async generator each sit on their own intrinsic prototype
384
+ * carrying `Symbol.toStringTag`, which an ordinary function's chain never reaches. Measured
385
+ * before the probes were per-kind: `pki.cms.sign` refused all three for
386
+ * `Symbol(Symbol.toStringTag)` while an ordinary function passed. Symbol keys are collected too,
387
+ * since that is the name that was missing. */
388
+ /** @internal Every kind, not the subset that happens to suffice: the four intrinsic prototypes
389
+ * carry the SAME well-known `Symbol.toStringTag` under different values, so dropping the async
390
+ * generator still passes today and would not if a kind ever carried a key of its own. */
391
+ var probes = [function () {}, function* () {}, async function () {}, async function* () {}, class {}];
392
+ _forEach(probes, function (p) {
393
+ for (var o = p; o !== null && o !== _ObjectProto; o = _getPrototypeOf(o)) {
394
+ _forEach(_ownKeys(o), function (k) {
395
+ if (typeof k === "symbol") _setAdd(symbols, k); else names[k] = true;
396
+ });
397
+ }
398
+ });
399
+ return {
400
+ has: function (k) { return typeof k === "symbol" ? _setHas(symbols, k) : names[k] === true; },
401
+ };
402
+ })();
403
+
404
+ /**
405
+ * @internal
406
+ * Whether `k` is part of `v`'s own machinery rather than a field a caller named: a RegExp's
407
+ * `lastIndex`, the non-thenable mask's `then`, and a callable's own function machinery, which is
408
+ * non-enumerable where a field a caller assigned is enumerable.
409
+ *
410
+ * A reader that COPIES a descriptor's own keys asks this, so it copies what the caller wrote and
411
+ * nothing else. The name checks do not: a bare function handed in where an options bag belongs is
412
+ * refused for the names it carries, which is a contract of its own, so the callable rule lives here
413
+ * rather than in `_isOwnStructuralName`.
414
+ */
415
+ // @enforced-by behavioral -- a per-kind name set has no rename-proof code shape; the vectors
416
+ // @enforced-by behavioral -- that drive a callable descriptor through a copying door are the guard
417
+ function isOwnMachineryName(v, k) {
418
+ if (v === null || v === undefined) return false;
419
+ var d = _getOwnPropertyDescriptor(v, k);
420
+ /** @internal A DATA property, because that is what the machinery is: measured on both Node
421
+ * 24.21.0 and 26.9.0, every one of these names a function owns is a non-enumerable data
422
+ * property. An accessor a caller installed under one of those names is therefore theirs, and it
423
+ * reaches the unknown-field refusal by its name rather than being exempted and ignored. Asking
424
+ * the descriptor rather than reading the property leaves the getter uninvoked either way. */
425
+ if (typeof v === "function" && !!d && d.enumerable === false && _hasOwn(d, "value") &&
426
+ _CALLABLE_MACHINERY.has(k)) return true;
427
+ return _isOwnStructuralName(v, k, d, false);
428
+ }
429
+
323
430
  function _isOwnStructuralName(v, k, d, inherited) {
324
431
  if (util.types.isRegExp(v) && k === "lastIndex") return true;
325
432
  /** @internal The non-thenable mask `guard.verdict.shield` leaves on a value the toolkit hands back:
@@ -680,6 +787,9 @@ module.exports = intrinsic.freeze({
680
787
  snapshotOptions: snapshotOptions,
681
788
  snapshotPresentOptions: snapshotPresentOptions,
682
789
  optionNames: optionNames,
790
+ isOwnMachineryName: isOwnMachineryName,
791
+ isMachineryName: isMachineryName,
792
+ assertKnownDescriptorKeys: assertKnownDescriptorKeys,
683
793
  ownOptions: ownOptions,
684
794
  pickSettings: pickSettings,
685
795
  readableNames: readableNames,
package/lib/guard-name.js CHANGED
@@ -65,7 +65,51 @@ function assertPrintableIa5(buf, E, code, label) {
65
65
  }
66
66
 
67
67
  function _isSpace(c) { return c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d; }
68
- function _canonAttrValue(v, E, code, label) {
68
+ /** @internal The attribute types whose EQUALITY rule is not case-ignoring, resolved once to their
69
+ * object identifiers. An equality rule belongs to the attribute, and folding case on one whose rule
70
+ * is case-exact makes two distinct names compare as one name: measured through `pki.x509.parseDn`,
71
+ * `challengePassword=Secret` and `challengePassword=secret` encode differently and compared EQUAL,
72
+ * as did `localKeyId=AB` against `ab` and a `role` URI against its lowercase form.
73
+ *
74
+ * Only the exceptions are listed. caseIgnoreMatch under the RFC 4518 preparation is the correct
75
+ * rule for every attribute RFC 5280 sec. 4.1.2.4 requires support for, so a conforming PKIX name
76
+ * compares as it did and an attribute the registry does not name keeps that default rather than
77
+ * being given a rule nothing states.
78
+ *
79
+ * The identifiers are resolved rather than written, since a dotted-decimal literal is never the
80
+ * identity a module carries. */
81
+ /** @internal RFC 2985 sec. 5.4.1 gives challengePassword caseExactMatch, sec. 5.5.2 gives localKeyId
82
+ * octetStringMatch, and RFC 5755 sec. 4.4.5 makes a role a URI. */
83
+ var _CASE_EXACT_NAMES = ["challengePassword", "localKeyId", "role"];
84
+ var _caseExactOids = null;
85
+ function _isCaseExactType(type) {
86
+ if (typeof type !== "string") return false;
87
+ if (_caseExactOids === null) {
88
+ var oid = require("./oid"); // allow:inline-require -- circular load: oid -> guard-all -> this module
89
+ _caseExactOids = _create(null);
90
+ for (var i = 0; i < _CASE_EXACT_NAMES.length; i++) {
91
+ var o = oid.byName(_CASE_EXACT_NAMES[i]);
92
+ if (typeof o === "string") _caseExactOids[o] = true;
93
+ }
94
+ }
95
+ return _caseExactOids[type] === true;
96
+ }
97
+
98
+ /** @internal The registry's own reading of a dotted-decimal object identifier, taken from there
99
+ * rather than re-derived: `numericoid` is RFC 4512's production and one definition of it decides
100
+ * both what the registry resolves and what opens a distinguished name. */
101
+ var _isDottedOid = null;
102
+ function _opensAsNumericOid(run) {
103
+ if (_isDottedOid === null) {
104
+ var oid = require("./oid"); // allow:inline-require -- circular load: oid -> guard-all -> this module
105
+ _isDottedOid = oid.isDottedDecimal;
106
+ }
107
+ return _isDottedOid(run) === true;
108
+ }
109
+
110
+ /** @internal The RFC 4518 insignificant-space preparation, which every equality rule here applies.
111
+ * The case fold is the caller's to add, because that step is the one the attribute's rule decides. */
112
+ function _prepAttrValue(v, E, code, label) {
69
113
  if (typeof v !== "string") return v;
70
114
  assertNoControlBytes(v, E, code, label);
71
115
  var out = "", lastWasSpace = true;
@@ -75,8 +119,9 @@ function _canonAttrValue(v, E, code, label) {
75
119
  lastWasSpace = false;
76
120
  out += _charAt(v, i);
77
121
  }
78
- return _toLowerCase(out);
122
+ return out;
79
123
  }
124
+
80
125
  /** @internal Both lengths are NARROWED where they are taken, and every test and walk below asks the
81
126
  * local. `Array.isArray` is true for a Proxy whose target is an array and a `get` trap answers `length`
82
127
  * afresh on each read, so the equality test and the walk that follows it could see two different counts:
@@ -114,14 +159,25 @@ function rdnEqual(a, b, E, code, label) {
114
159
  * comparison's one unacceptable answer, since it is what chains a certificate to its issuer. */
115
160
  var ea = a[i];
116
161
  var typeA = ea == null ? undefined : ea.type;
117
- var valueA = _canonAttrValue(ea == null ? undefined : ea.value, E, code, label);
162
+ /** @internal Prepared once without the fold, and folded once, so the attribute's own rule picks
163
+ * between them without reading the element again. */
164
+ var prepA = _prepAttrValue(ea == null ? undefined : ea.value, E, code, label);
165
+ var foldedA = typeof prepA === "string" ? _toLowerCase(prepA) : prepA;
166
+ var exactA = _isCaseExactType(typeA);
118
167
  var found = false;
119
168
  for (var j = 0; j < n.b; j++) {
120
169
  if (used[j]) continue;
121
170
  var eb = b[j];
122
171
  var typeB = eb == null ? undefined : eb.type;
123
172
  if (typeA !== typeB) continue;
124
- if (valueA === _canonAttrValue(eb == null ? undefined : eb.value, E, code, label)) {
173
+ var prepB = _prepAttrValue(eb == null ? undefined : eb.value, E, code, label);
174
+ /** @internal Either side naming a case-exact attribute decides it. The types already matched,
175
+ * so the two can disagree only through an object answering differently per read, and the
176
+ * exact comparison is the one that cannot make two distinct names equal. */
177
+ var same = exactA || _isCaseExactType(typeB)
178
+ ? prepA === prepB
179
+ : foldedA === (typeof prepB === "string" ? _toLowerCase(prepB) : prepB);
180
+ if (same) {
125
181
  used[j] = true; found = true; break;
126
182
  }
127
183
  }
@@ -213,6 +269,36 @@ function isAttributeDescriptor(name) {
213
269
  return true;
214
270
  }
215
271
 
272
+ /**
273
+ * @internal
274
+ * Whether a string opens with the prefix RFC 4514 sec. 3 requires of a distinguished name: an
275
+ * attribute type, a descriptor or a dotted-decimal OID, immediately followed by `=`. It answers one
276
+ * question, whether a caller who passed a bare string meant a distinguished name rather than the
277
+ * single value the shorthand reads it as, and it is deliberately narrower than parsing: a value that
278
+ * CONTAINS an `=` further along, `Example CA (v=2)`, opens with no attribute type and is not
279
+ * a distinguished name.
280
+ *
281
+ * `attributeType` is `descr / numericoid`, and a `numericoid` carries at least one dot, so a numeric
282
+ * run without one is not an attribute type: `1=1` is a value no reader reads as a distinguished
283
+ * name, and refusing it would name a remedy that does not apply. The leading whitespace sec. 3
284
+ * admits nowhere is the same case from the other side: ` CN=Example` opens with no attribute type
285
+ * and is the value it says it is.
286
+ */
287
+ // @enforced-by behavioral -- an opening-prefix test has no rename-proof shape distinct from
288
+ function opensAsDnString(str) {
289
+ if (typeof str !== "string" || str.length === 0) return false;
290
+ var i = 0, n = str.length, first = _charCodeAt(str, 0);
291
+ if (_isDigit(first)) {
292
+ while (i < n && (_isDigit(_charCodeAt(str, i)) || _charCodeAt(str, i) === 0x2e)) i++;
293
+ if (!_opensAsNumericOid(_strSlice(str, 0, i))) return false;
294
+ } else if (_isAttrTypeStart(first)) {
295
+ while (i < n && _isAttrTypeChar(_charCodeAt(str, i))) i++;
296
+ } else {
297
+ return false;
298
+ }
299
+ return i > 0 && i < n && _charCodeAt(str, i) === 0x3d;
300
+ }
301
+
216
302
  // @enforced-by behavioral -- the inverse of escapeDnValue has no rename-proof shape distinct
217
303
  function parseDnString(str, E, code, label) {
218
304
  if (typeof str !== "string") throw E(code, label + " must be a string");
@@ -1058,6 +1144,7 @@ module.exports = intrinsic.freeze({
1058
1144
  assertNoControlBytes: assertNoControlBytes, assertPrintableIa5: assertPrintableIa5,
1059
1145
  dnEqual: dnEqual, rdnEqual: rdnEqual, emailEqual: emailEqual, dpnCorresponds: dpnCorresponds,
1060
1146
  escapeControlBytes: escapeControlBytes, escapeDnValue: escapeDnValue, parseDnString: parseDnString,
1147
+ opensAsDnString: opensAsDnString,
1061
1148
  isAttributeDescriptor: isAttributeDescriptor,
1062
1149
  lowerAscii: lowerAscii, uriSchemeIs: uriSchemeIs,
1063
1150
  stripTrailingDot: stripTrailingDot, isFqdnHost: isFqdnHost, splitMailbox: splitMailbox,
package/lib/inspect.js CHANGED
@@ -1531,8 +1531,8 @@ function crmfReport(input) {
1531
1531
  * var key = await pki.key.export(pair.privateKey);
1532
1532
  * var cert = await pki.x509.sign({ subject: "cmp.example", subjectPublicKey: await pki.key.export(pair.publicKey),
1533
1533
  * notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
1534
- * var der = await pki.cmp.build({ header: { sender: { directoryName: "CN=cmp.example" },
1535
- * recipient: { directoryName: "CN=A CA" } },
1534
+ * var der = await pki.cmp.build({ header: { sender: { directoryName: "cmp.example" },
1535
+ * recipient: { directoryName: "A CA" } },
1536
1536
  * body: { ir: { certTemplate: { subject: [{ commonName: "cmp.example" }], publicKey: await pki.key.export(pair.publicKey) } } } },
1537
1537
  * { cert: cert, key: key });
1538
1538
  * pki.inspect.cmp(der).split("\n")[0]; // "CMP Message:"
@@ -1937,10 +1937,10 @@ function tspReport(input) {
1937
1937
  *
1938
1938
  * @example
1939
1939
  * var pair = await pki.key.generate("Ed25519");
1940
- * var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "CN=Alice" } },
1940
+ * var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "Alice" } },
1941
1941
  * notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
1942
1942
  * attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
1943
- * { name: "CN=An AA", publicKey: await pki.key.export(pair.publicKey), key: await pki.key.export(pair.privateKey) });
1943
+ * { name: "An AA", publicKey: await pki.key.export(pair.publicKey), key: await pki.key.export(pair.privateKey) });
1944
1944
  * pki.inspect.attrcert(der).split("\n")[0]; // "Attribute Certificate:"
1945
1945
  */
1946
1946
  function attrCertReport(input) {
package/lib/lint.js CHANGED
@@ -7556,10 +7556,10 @@ function _ingestAttrCert(input) {
7556
7556
  * @example
7557
7557
  * var pair = await pki.key.generate("Ed25519");
7558
7558
  * var key = await pki.key.export(pair.privateKey);
7559
- * var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "CN=Alice" } },
7559
+ * var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "Alice" } },
7560
7560
  * notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
7561
7561
  * attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
7562
- * { name: "CN=Example AA", publicKey: await pki.key.export(pair.publicKey), key: key });
7562
+ * { name: "Example AA", publicKey: await pki.key.export(pair.publicKey), key: key });
7563
7563
  * pki.lint.attrcert(der).findings.length; // -> 0 for a conforming attribute certificate
7564
7564
  */
7565
7565
  function attrcert(input, opts) {
@@ -25,7 +25,7 @@ var OID_OCSP_NOCHECK = oid.byName("ocspNoCheck");
25
25
  var OID_EKU = oid.byName("extKeyUsage");
26
26
  var OID_KEY_USAGE = oid.byName("keyUsage");
27
27
 
28
- function ocspDigest(alg, buf) { return subtle.digest(alg, buf).then(function (h) { return _bufferFrom(h); }); }
28
+ function ocspDigest(alg, buf) { return intrinsic.chain(subtle.digest(alg, buf), function (h) { return _bufferFrom(h); }); }
29
29
 
30
30
  function ocspKeyValue(spkiDer) {
31
31
  return asn1.read.bitString(asn1.decode(spkiDer).children[1]).bytes;
package/lib/pki-build.js CHANGED
@@ -522,6 +522,20 @@ function makeBuilder(ctx) {
522
522
  }
523
523
  function encodeName(spec) {
524
524
  if (guard.bytes.isByteSource(spec)) { var _nd = guard.bytes.snapshotSource(spec, ErrorClass, ctx.prefix + "/bad-name", "raw Name DER"); assertValidNameDer(_nd); return _nd; }
525
+ /** @internal A bare string is the common-name shorthand, and a caller who writes a distinguished
526
+ * name here gets one commonName holding the whole text: measured, `"CN=Example CA, O=Example"`
527
+ * certified the single RDN `CN=CN=Example CA\, O=Example`, the doubled `CN=` being the caller's
528
+ * own attribute type read as part of the value. Only one of the two readings can be what was
529
+ * meant, so the distinguished-name one is refused rather than certified as a name nobody asked
530
+ * for, and the caller says which by passing `pki.x509.parseDn(s).bytes` or the RDN array. A
531
+ * common name that contains an `=` further along does not open as a distinguished name
532
+ * and is untouched. OpenSSL refuses the same string for the same reason, naming the form it
533
+ * wants; Go has no string form and .NET reads a string as the distinguished name. */
534
+ if (typeof spec === "string" && guard.name.opensAsDnString(spec)) {
535
+ throw E("bad-name", "a bare name string is a common name, and " + guard.text.showValue(spec) +
536
+ " opens as a distinguished name. Pass pki.x509.parseDn(s).bytes for the distinguished name, " +
537
+ "or [{ commonName: s }] to certify it as one common name");
538
+ }
525
539
  if (typeof spec === "string") spec = [{ commonName: spec }];
526
540
  if (!_isArray(spec)) throw E("bad-name", "a name must be a string, an array of RDNs, or raw Name DER");
527
541
  /** @internal Held to the same density every other authoring list is held to. Mapping over a
package/lib/possession.js CHANGED
@@ -160,12 +160,12 @@ function _namesCertificate(signer, certParsed) {
160
160
  * var kemSpki = await pki.key.export(kem.publicKey, { format: "der" });
161
161
  * var sigSpki = await pki.key.export(sig.publicKey, { format: "der" });
162
162
  * var sigCert = await pki.x509.sign({
163
- * subject: "CN=kem.example", subjectPublicKey: sigSpki, serialNumber: 34n,
163
+ * subject: "kem.example", subjectPublicKey: sigSpki, serialNumber: 34n,
164
164
  * notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
165
165
  * }, { key: sig.privateKey });
166
166
  * var parsedCert = pki.schema.x509.parse(sigCert);
167
167
  * var csr = await pki.csr.sign({
168
- * subject: "CN=kem.example", subjectPublicKey: kemSpki,
168
+ * subject: "kem.example", subjectPublicKey: kemSpki,
169
169
  * privateKeyPossessionStatement: {
170
170
  * signer: { issuer: parsedCert.issuer.bytes, serialNumber: parsedCert.serialNumber },
171
171
  * certificate: sigCert,
@@ -354,18 +354,18 @@ var _isSignatureOnlyKey = pkix.signatureOnlyKeyOid(NS);
354
354
  * var ca = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
355
355
  * var caSpki = await pki.key.export(ca.publicKey, { format: "der" });
356
356
  * var caCert = await pki.x509.sign({
357
- * subject: "CN=Possession CA", subjectPublicKey: caSpki, serialNumber: 1n,
357
+ * subject: "Possession CA", subjectPublicKey: caSpki, serialNumber: 1n,
358
358
  * notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
359
359
  * extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"] },
360
360
  * }, { key: ca.privateKey });
361
361
  * var sigCert = await pki.x509.sign({
362
- * subject: "CN=kem.example", subjectPublicKey: sigSpki, serialNumber: 34n,
362
+ * subject: "kem.example", subjectPublicKey: sigSpki, serialNumber: 34n,
363
363
  * notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
364
364
  * extensions: { keyUsage: ["digitalSignature"] },
365
365
  * }, { cert: caCert, key: ca.privateKey });
366
366
  * var p = pki.schema.x509.parse(sigCert);
367
367
  * var csr = await pki.csr.sign({
368
- * subject: "CN=kem.example", subjectPublicKey: kemSpki,
368
+ * subject: "kem.example", subjectPublicKey: kemSpki,
369
369
  * privateKeyPossessionStatement: {
370
370
  * signer: { issuer: p.issuer.bytes, serialNumber: p.serialNumber }, certificate: sigCert,
371
371
  * },
@@ -522,13 +522,15 @@ function _verifyRequest(request, opts) {
522
522
  "authorize a certification request (RFC 5280 sec. 4.2.1.3)");
523
523
  }
524
524
 
525
- return _promiseResolve(_Promise, _validatePath(chain, { trustAnchors: o.trustAnchors, time: at }))
526
- .then(function (pathResult) {
525
+ return intrinsic.chain(
526
+ _promiseResolve(_Promise, _validatePath(chain, { trustAnchors: o.trustAnchors, time: at })),
527
+ function (pathResult) {
527
528
  var pathValidated = pathResult.valid === true;
528
529
  if (!pathValidated) _push(reasons, "the signature certificate's certification path is not valid, and RFC 9883 sec. 4 makes that a MUST");
529
- return _seam.verify(parsed.signatureAlgorithm, parsed.signatureValue,
530
- sigCert.subjectPublicKeyInfo.bytes, parsed.certificationRequestInfoBytes)
531
- .then(function (verified) {
530
+ return intrinsic.chain(
531
+ _seam.verify(parsed.signatureAlgorithm, parsed.signatureValue,
532
+ sigCert.subjectPublicKeyInfo.bytes, parsed.certificationRequestInfoBytes),
533
+ function (verified) {
532
534
  if (verified !== true) _push(reasons, "the request signature does not verify under the signature certificate's public key, and RFC 9883 sec. 4 makes that a MUST");
533
535
  return guard.verdict.of({}, {
534
536
  valid: verified === true && pathValidated && signerMaySign && !requestsSignatureCertificate &&
@@ -543,15 +545,19 @@ function _verifyRequest(request, opts) {
543
545
  requestsSignatureCertificate: requestsSignatureCertificate,
544
546
  reason: reasons.length ? _join(reasons, "; ") : undefined,
545
547
  });
546
- });
547
- });
548
+ }
549
+ );
550
+ }
551
+ );
548
552
  }
549
553
 
550
554
  /** @internal Whether the signature certificate's key may make this signature at all. A certificate confined
551
555
  * to `keyAgreement` does not authorize a signature over a certification request, and accepting one lets a
552
556
  * key never issued for signing authorize issuance. The accepted set is `pki.cms`'s: a signature over
553
- * content is authorized by digitalSignature, contentCommitment or nonRepudiation alike. */
554
- var _maySign = pkix.keyUsagePermits(NS, ["digitalSignature", "contentCommitment", "nonRepudiation"]);
557
+ * content is authorized by digitalSignature or nonRepudiation alike. The decoder emits the
558
+ * RFC 5280 sec. 4.2.1.3 bit name `nonRepudiation`, never the X.509 spelling `contentCommitment`,
559
+ * so naming the latter here asked for a name nothing defines. */
560
+ var _maySign = pkix.keyUsagePermits(NS, ["digitalSignature", "nonRepudiation"]);
555
561
 
556
562
  /** @internal The subjectAltName a request asks for, or null when it asks for none. */
557
563
  function _requestedSan(parsedCsr) {
@@ -99,6 +99,33 @@ function _certIdParts(der) {
99
99
  var DIGEST_LENGTHS = pkix.digestLengths();
100
100
  var SIG_OID_DIGEST = pkix.signatureDigestNames(oid);
101
101
 
102
+ /** @internal Whether the related certificate's key may make this one signature. RFC 5280 sec. 4.2.1.3
103
+ * confines a key to the purposes a present keyUsage asserts, so a certificate issued only to sign
104
+ * certificates or CRLs does not authorize a proof of possession over a certification request.
105
+ *
106
+ * The set is NOT `pki.possession`'s. RFC 9763 sec. 3.1 states that "if the related certificate is a
107
+ * key establishment certificate (e.g., using RSA key transport or Elliptic Curve Cryptography (ECC)
108
+ * key agreement), the private key is used to sign one time for proof of possession (POP)", so
109
+ * keyEncipherment and keyAgreement authorize this signature where they authorize no other, and
110
+ * holding the proof to possession's signing-only set would refuse a request the document admits.
111
+ * What is left to refuse is a certificate confined to keyCertSign, cRLSign, or the two modifier bits
112
+ * that carry no meaning without keyAgreement. An absent keyUsage constrains nothing.
113
+ *
114
+ * Which establishment bit counts comes from the SUBJECT KEY, because the sentence admits a key
115
+ * establishment CERTIFICATE and a bit the key's own family cannot perform does not make one. The
116
+ * union of both bits admitted a certificate that is not an establishment certificate under any
117
+ * reading: measured, an EC certificate asserting only keyEncipherment, which RFC 8813 sec. 3 makes a
118
+ * MUST NOT for id-ecPublicKey, and an RSA certificate asserting only keyAgreement, which RFC 3279
119
+ * sec. 2.3.1 does not list, both reported signerMaySign and a valid proof. A family the shared table
120
+ * places in neither column admits neither bit, so a signature usage is what authorizes it. */
121
+ var SIGNATURE_USAGE_BITS = ["digitalSignature", "nonRepudiation"];
122
+ var _estabBit = pkix.keyEstablishmentBit(NS);
123
+ var _maySign = pkix.keyUsagePermits(NS, function (parsedCert) {
124
+ var spki = parsedCert && parsedCert.subjectPublicKeyInfo;
125
+ var bit = _estabBit(spki && spki.algorithm ? spki.algorithm.oid : null);
126
+ return bit === null ? SIGNATURE_USAGE_BITS : [bit, "digitalSignature", "nonRepudiation"];
127
+ });
128
+
102
129
  /** @internal The digests this module will bind a certificate identity to. sha1 is registered and
103
130
  * decodable, and is absent here: a chosen-prefix collision on a certificate is a demonstrated
104
131
  * attack, so a value naming it is refused before anything is computed. A caller reading a hashValue a
@@ -392,7 +419,7 @@ function _parseCert(certDer, what) {
392
419
  * @example
393
420
  * var kp = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
394
421
  * var spki = await pki.key.export(kp.publicKey, { format: "der" });
395
- * var related = await pki.x509.sign({ subject: "CN=Held", subjectPublicKey: spki, serialNumber: 42n,
422
+ * var related = await pki.x509.sign({ subject: "Held", subjectPublicKey: spki, serialNumber: 42n,
396
423
  * notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z") }, { key: kp.privateKey });
397
424
  * pki.relatedCert.certificateHash(related).hashAlgorithm; // -> "sha256", from this certificate's signature OID
398
425
  * pki.relatedCert.certificateHash(related, "sha512").hashValue.length; // -> 64
@@ -455,9 +482,9 @@ function digestValue(hashAlgorithm, hashValue) {
455
482
  * @example
456
483
  * var kp = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
457
484
  * var spki = await pki.key.export(kp.publicKey, { format: "der" });
458
- * var related = await pki.x509.sign({ subject: "CN=Held", subjectPublicKey: spki, serialNumber: 42n,
485
+ * var related = await pki.x509.sign({ subject: "Held", subjectPublicKey: spki, serialNumber: 42n,
459
486
  * notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z") }, { key: kp.privateKey });
460
- * var leaf = await pki.x509.sign({ subject: "CN=New Key", subjectPublicKey: spki, serialNumber: 43n,
487
+ * var leaf = await pki.x509.sign({ subject: "New Key", subjectPublicKey: spki, serialNumber: 43n,
461
488
  * notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
462
489
  * extensions: { relatedCertificate: { relatedCertificate: related } } }, { key: kp.privateKey });
463
490
  * var ext = pki.schema.x509.parse(leaf).extensions
@@ -563,7 +590,7 @@ function _namedAlgorithm(value) {
563
590
 
564
591
  /**
565
592
  * @primitive pki.relatedCert.verifyRequest
566
- * @signature pki.relatedCert.verifyRequest(requesterCertificate, certificate, opts?) -> Promise<boolean>
593
+ * @signature pki.relatedCert.verifyRequest(requesterCertificate, certificate, opts?) -> Promise<verdict>
567
594
  * @since 0.8.42
568
595
  * @status stable
569
596
  * @spec RFC 9763 sec. 3.1
@@ -572,9 +599,18 @@ function _namedAlgorithm(value) {
572
599
  *
573
600
  * Verify the proof of possession in a `relatedCertRequest` CSR attribute. `requesterCertificate` is
574
601
  * the value `pki.schema.csr.parse` carries on that attribute, and `certificate` is the DER of the
575
- * certificate its `certID` names. Returns `true` when the signature over the bytes
576
- * `pki.relatedCert.requestSignedData` describes verifies under that certificate's subject key, and
577
- * `false` when a signature was checked and did not verify.
602
+ * certificate its `certID` names.
603
+ *
604
+ * Returns `{ valid, verified, signerMaySign, signatureAlgorithm }`, plus `code` and `reason` when it
605
+ * did not pass. `verified` is the signature over the bytes
606
+ * `pki.relatedCert.requestSignedData` describes, checked under that certificate's subject key.
607
+ * `signerMaySign` is whether the certificate's keyUsage authorizes that signature at all, and
608
+ * `valid` is both together. They are separate because a key the certificate never authorized for
609
+ * signing and a signature that does not verify are different facts, and a caller acting on one as
610
+ * if it were the other acts on the wrong one. RFC 5280 sec. 4.2.1.3 confines a key to the purposes a
611
+ * present keyUsage asserts; a certificate carrying none is unconstrained. A key-establishment
612
+ * certificate still passes, because RFC 9763 sec. 3.1 has such a key "sign one time for proof of
613
+ * possession", so keyEncipherment and keyAgreement authorize this signature and no other.
578
614
  *
579
615
  * The certificate handed in must be the one `certID` names: an issuer and serial that do not match
580
616
  * throw `relatedcert/cert-mismatch`, since verifying a proof against a certificate the request did
@@ -614,7 +650,7 @@ function _namedAlgorithm(value) {
614
650
  * @example
615
651
  * var kp = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
616
652
  * var spki = await pki.key.export(kp.publicKey, { format: "der" });
617
- * var held = await pki.x509.sign({ subject: "CN=Held", subjectPublicKey: spki, serialNumber: 42n,
653
+ * var held = await pki.x509.sign({ subject: "Held", subjectPublicKey: spki, serialNumber: 42n,
618
654
  * notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z") }, { key: kp.privateKey });
619
655
  * var parsed = pki.schema.x509.parse(held);
620
656
  * var certID = { issuer: parsed.issuer.bytes, serialNumber: parsed.serialNumber };
@@ -627,7 +663,7 @@ function _namedAlgorithm(value) {
627
663
  * pki.asn1.build.integer(BigInt("0x" + raw.subarray(0, half).toString("hex"))),
628
664
  * pki.asn1.build.integer(BigInt("0x" + raw.subarray(half).toString("hex"))),
629
665
  * ]);
630
- * var csr = await pki.csr.sign({ subject: "CN=New Key", subjectPublicKey: spki,
666
+ * var csr = await pki.csr.sign({ subject: "New Key", subjectPublicKey: spki,
631
667
  * relatedCertRequest: { certID: certID, requestTime: 1800000000,
632
668
  * locationInfo: ["https://certs.example/held.cer"], signature: proof } }, { key: kp.privateKey });
633
669
  * var attr = pki.schema.csr.parse(csr).attributes
@@ -635,7 +671,7 @@ function _namedAlgorithm(value) {
635
671
  * // The window is the certification authority's policy, so it is named: five minutes here, judged
636
672
  * // against the instant the request was made, which is what a CA reading a recorded request does.
637
673
  * await pki.relatedCert.verifyRequest(attr.relatedCertRequest, held,
638
- * { maxAge: 300, at: new Date(1800000060 * 1000) }); // -> true
674
+ * { maxAge: 300, at: new Date(1800000060 * 1000) }); // -> { valid: true, verified: true, ... }
639
675
  */
640
676
  function verifyRequest(requesterCertificate, certificate, opts) {
641
677
  /** @internal Every refusal reaches the caller as a rejection, so a verb that returns a promise never
@@ -680,6 +716,8 @@ function _verifyRequest(requesterCertificate, certificate, opts) {
680
716
  "the certificate given is not the one certID names (RFC 9763 sec. 3.2)");
681
717
  }
682
718
 
719
+ var signerMaySign = _maySign(c.parsed);
720
+
683
721
  var sigAlg = o.signatureAlgorithm !== undefined && o.signatureAlgorithm !== null
684
722
  ? _namedAlgorithm(o.signatureAlgorithm)
685
723
  : _proofAlgorithm(c.parsed, o.digestAlgorithm);
@@ -694,7 +732,30 @@ function _verifyRequest(requesterCertificate, certificate, opts) {
694
732
  }
695
733
  if (sig.bytes.length === 0) throw _err("relatedcert/bad-input", "the proof signature must not be empty");
696
734
 
697
- return _seam.verify(sigAlg, sig, c.parsed.subjectPublicKeyInfo.bytes, preimage);
735
+ /** @internal The two facts are reported apart. A bare boolean could not say which check failed, so
736
+ * a key the certificate never authorized for signing and a signature that simply does not verify
737
+ * read the same, and a caller acting on one as if it were the other acts on the wrong fact. */
738
+ return intrinsic.chain(_seam.verify(sigAlg, sig, c.parsed.subjectPublicKeyInfo.bytes, preimage), function (verified) {
739
+ var ok = verified === true && signerMaySign;
740
+ /** @internal The verdict carries what this verb DETERMINED, not an echo of what it was handed.
741
+ * The caller already holds `certID` and `requestTime` on the request it passed in, and copying
742
+ * the decoded issuer onto the result would invite a byte comparison against a name this verb
743
+ * judged equal under the RFC 5280 sec. 7.1 folding: two names that compare equal here are
744
+ * routinely different bytes, and there is no public comparison a caller could use instead.
745
+ * `signatureAlgorithm` is kept because it is derived rather than supplied, so without it a
746
+ * caller cannot tell which digest the proof was actually checked under. */
747
+ return guard.verdict.of({
748
+ valid: ok,
749
+ verified: verified === true,
750
+ signerMaySign: signerMaySign,
751
+ signatureAlgorithm: sigAlg,
752
+ }, ok ? null : {
753
+ code: !signerMaySign ? "relatedcert/signer-may-not-sign" : "relatedcert/bad-signature",
754
+ reason: !signerMaySign
755
+ ? "the related certificate's keyUsage does not authorize a proof-of-possession signature (RFC 5280 sec. 4.2.1.3)"
756
+ : "the proof signature does not verify under the key of the certificate certID names",
757
+ });
758
+ });
698
759
  }
699
760
 
700
761
  module.exports = {