@blamejs/pki 0.9.1 → 0.9.4
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/CHANGELOG.md +37 -0
- package/MIGRATING.md +57 -0
- package/README.md +5 -5
- package/lib/alt-sig.js +3 -3
- package/lib/attrcert-sign.js +5 -5
- package/lib/cmc-verify.js +13 -9
- package/lib/cmp-build.js +2 -2
- package/lib/cmp-verify.js +1 -1
- package/lib/cms-sign.js +21 -0
- package/lib/cms-verify.js +5 -3
- package/lib/crl-sign.js +1 -1
- package/lib/crmf-sign.js +1 -1
- package/lib/csr-sign.js +4 -2
- package/lib/est.js +4 -4
- package/lib/guard-identifier.js +110 -0
- package/lib/guard-name.js +91 -4
- package/lib/inspect.js +4 -4
- package/lib/lint.js +55 -8
- package/lib/ocsp-verify.js +1 -1
- package/lib/pki-build.js +14 -0
- package/lib/possession.js +20 -14
- package/lib/related-cert.js +72 -11
- package/lib/schema-attrcert.js +5 -5
- package/lib/schema-c509.js +42 -6
- package/lib/schema-cmp.js +3 -3
- package/lib/schema-pkix.js +62 -3
- package/lib/schema-trustanchor.js +4 -4
- package/lib/sigstore.js +1 -1
- package/lib/smime.js +18 -2
- package/lib/trust.js +1 -1
- package/lib/trustanchor-build.js +1 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cms-sign.js
CHANGED
|
@@ -205,6 +205,19 @@ function _assertAlgorithmProtectionPairs(pairs, digestAlgId, armAlgId, armTag) {
|
|
|
205
205
|
function _resolveSignerContext(signer, opts) {
|
|
206
206
|
var so = signer || intrinsic.create(null);
|
|
207
207
|
var keyOnly = so.cert == null && so.spki != null;
|
|
208
|
+
/** @internal A descriptor names the digest and the padding the signature is made under, so a field
|
|
209
|
+
* this verb does not read is a request that was never carried out: measured, a `digestAlgoritm`
|
|
210
|
+
* typo signed under sha256 while the caller believed they had asked for sha384, and the same
|
|
211
|
+
* misspelling one argument along in `opts` was already a refusal. The two key sets below have
|
|
212
|
+
* described this shape since they were written, and `pkcs12-build` already holds the same
|
|
213
|
+
* descriptors to them; this is the module that owns them doing the same. */
|
|
214
|
+
guard.identifier.assertKnownDescriptorKeys(so, keyOnly ? KNOWN_SIGNER_KEY_ONLY_KEYS : KNOWN_SIGNER_CERT_KEYS,
|
|
215
|
+
_err, "cms/bad-input", function (k) {
|
|
216
|
+
return "unknown signer field " + guard.text.showValue(k) + " for the " + (keyOnly
|
|
217
|
+
? "key-only form; it takes { key, spki, keyIdentifier } plus the signature parameters"
|
|
218
|
+
: "certificate form; it takes { key, cert } plus the signature parameters") +
|
|
219
|
+
" (pss, digestAlgorithm, combinedRsaSig)";
|
|
220
|
+
});
|
|
208
221
|
var soKey = so.key;
|
|
209
222
|
var soSpki = keyOnly ? guard.bytes.snapshotSource(so.spki, CmsError, "cms/bad-input", "a key-only signer's spki") : null;
|
|
210
223
|
var certDer = keyOnly ? null : _normCertDer(so.cert);
|
|
@@ -534,6 +547,14 @@ function _resolveSignerIndices(spec, n) {
|
|
|
534
547
|
|
|
535
548
|
function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
536
549
|
var so = countersigner || intrinsic.create(null);
|
|
550
|
+
/** @internal A countersignature is a SignerInfo (RFC 5652 sec. 11.4) and its descriptor names the
|
|
551
|
+
* same signature parameters, so a field this reader does not read is the same dropped request it
|
|
552
|
+
* is at `sign`. This reader is its own, so the gate at the other one did not cover it. A
|
|
553
|
+
* countersignature is always the certificate form, which is the `cert` read below. */
|
|
554
|
+
guard.identifier.assertKnownDescriptorKeys(so, KNOWN_SIGNER_CERT_KEYS, _err, "cms/bad-input", function (k) {
|
|
555
|
+
return "unknown countersigner field " + guard.text.showValue(k) +
|
|
556
|
+
"; it takes { key, cert } plus the signature parameters (pss, digestAlgorithm, combinedRsaSig)";
|
|
557
|
+
});
|
|
537
558
|
var certDer = _normCertDer(so.cert);
|
|
538
559
|
var cert = x509.parse(certDer);
|
|
539
560
|
var scheme = signScheme.resolveSignScheme(cert, so, opts.signedAttributes === false, _signE);
|
package/lib/cms-verify.js
CHANGED
|
@@ -1170,10 +1170,12 @@ function _snapshotTimestampTrust(opts, allowWeakDigests) {
|
|
|
1170
1170
|
};
|
|
1171
1171
|
}
|
|
1172
1172
|
|
|
1173
|
-
/** @internal
|
|
1174
|
-
*
|
|
1173
|
+
/** @internal The decoder emits the RFC 5280 sec. 4.2.1.3 bit name `nonRepudiation`, never the X.509
|
|
1174
|
+
* spelling `contentCommitment`, so naming the latter here asked for a name nothing defines.
|
|
1175
|
+
* A signature over content is authorized by digitalSignature or nonRepudiation alike; the shared
|
|
1176
|
+
* helper carries the rest of the rule. */
|
|
1175
1177
|
var _permitsSigning = pkix.keyUsagePermits(pkix.makeNS("cms", CmsError, oid),
|
|
1176
|
-
["digitalSignature", "
|
|
1178
|
+
["digitalSignature", "nonRepudiation"]);
|
|
1177
1179
|
|
|
1178
1180
|
function _keyUsagePermitsSigning(parsedCerts, der) {
|
|
1179
1181
|
var entry = guard.list.firstMatch(parsedCerts, function (c) { return intrinsic.bufferEquals(c.der, der); });
|
package/lib/crl-sign.js
CHANGED
|
@@ -682,7 +682,7 @@ function _verify(crl, issuer) {
|
|
|
682
682
|
var parsed = _coerceCrl(crl);
|
|
683
683
|
var resolved = _resolveIssuer(issuer);
|
|
684
684
|
var issuerMaySign = resolved.cert ? _issuerMaySign(parsed, resolved.cert) : true;
|
|
685
|
-
return crlVerify.verifyCrlSignature(parsed, resolved.spki)
|
|
685
|
+
return intrinsic.chain(crlVerify.verifyCrlSignature(parsed, resolved.spki), function (sigOk) {
|
|
686
686
|
var signatureValid = sigOk === true;
|
|
687
687
|
var valid = issuerMaySign && signatureValid;
|
|
688
688
|
return guard.verdict.of(
|
package/lib/crmf-sign.js
CHANGED
|
@@ -1255,7 +1255,7 @@ function _build(spec, key, opts) {
|
|
|
1255
1255
|
* `Buffer`; a malformed template throws a typed `CrmfError`.
|
|
1256
1256
|
*
|
|
1257
1257
|
* @example
|
|
1258
|
-
* var tpl = pki.crmf.buildCertTemplate({ serialNumber: 42n, issuer: "
|
|
1258
|
+
* var tpl = pki.crmf.buildCertTemplate({ serialNumber: 42n, issuer: "CA" });
|
|
1259
1259
|
* pki.asn1.decode(tpl).tagNumber === pki.asn1.TAGS.SEQUENCE; // the CertTemplate SEQUENCE
|
|
1260
1260
|
*/
|
|
1261
1261
|
function buildCertTemplate(template) { return _encodeCertTemplate(template, { revocation: true }).der; }
|
package/lib/csr-sign.js
CHANGED
|
@@ -338,8 +338,10 @@ function _coerceCsr(request) {
|
|
|
338
338
|
* var req = await pki.csr.sign({ subject: "device-42", subjectPublicKey: spki }, { key: pkcs8 });
|
|
339
339
|
* var r = await pki.csr.verify(req);
|
|
340
340
|
* // Issue from r.subject / r.subjectPublicKeyInfo / r.attributes, which are the verified fields.
|
|
341
|
+
* // `r.subject.bytes` is the request's own Name DER, so the issued subject is the requested one
|
|
342
|
+
* // byte for byte. `r.subject.dn` is its text form, which a builder reads as a common name.
|
|
341
343
|
* var issued = r.verified
|
|
342
|
-
* ? await pki.x509.sign({ subject: r.subject.
|
|
344
|
+
* ? await pki.x509.sign({ subject: r.subject.bytes, subjectPublicKey: r.subjectPublicKeyInfo.bytes,
|
|
343
345
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2027-01-01T00:00:00Z") },
|
|
344
346
|
* { key: pkcs8 })
|
|
345
347
|
* : null;
|
|
@@ -347,7 +349,7 @@ function _coerceCsr(request) {
|
|
|
347
349
|
function verify(request) { return guard.async.deferred(function () { return _verify(request); }); }
|
|
348
350
|
function _verify(request) {
|
|
349
351
|
var parsed = _coerceCsr(request);
|
|
350
|
-
return _promiseResolve(_Promise, csrVerify.verifyCsrSignature(parsed))
|
|
352
|
+
return intrinsic.chain(_promiseResolve(_Promise, csrVerify.verifyCsrSignature(parsed)), function (ok) {
|
|
351
353
|
return guard.verdict.of({
|
|
352
354
|
valid: ok === true,
|
|
353
355
|
verified: ok === true,
|
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)
|
|
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)
|
|
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)
|
|
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
|
-
})
|
|
1541
|
+
}), null, function () { return null; });
|
|
1542
1542
|
}
|
|
1543
1543
|
|
|
1544
1544
|
function _isTypedError(e) {
|
package/lib/guard-identifier.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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: "
|
|
1535
|
-
* recipient: { directoryName: "
|
|
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: "
|
|
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: "
|
|
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
|
@@ -3901,6 +3901,43 @@ function _isMlKem(cert) {
|
|
|
3901
3901
|
var spki = cert.subjectPublicKeyInfo;
|
|
3902
3902
|
return !!(spki && spki.algorithm && ML_KEM_EK_LEN[spki.algorithm.name] !== undefined);
|
|
3903
3903
|
}
|
|
3904
|
+
/** @internal RFC 5280 sec. 4.2.1.3 states no per-algorithm bit set of its own: "appropriate values
|
|
3905
|
+
* for keyUsage extensions for particular algorithms are specified in [RFC3279], [RFC4055], and
|
|
3906
|
+
* [RFC4491]". RFC 5480 updates RFC 3279 for elliptic curve, and RFC 8813 sec. 3 adds the
|
|
3907
|
+
* prohibition: for a certificate indicating id-ecPublicKey, keyEncipherment and dataEncipherment
|
|
3908
|
+
* MUST NOT be present. The row runs in the rfc5280 profile because that is the profile whose own
|
|
3909
|
+
* clause delegates the question, and the profile every authoring call is held to.
|
|
3910
|
+
*
|
|
3911
|
+
* Only the id-ecPublicKey arm is here. Sec. 3's other two arms govern id-ecDH and id-ecMQV, which
|
|
3912
|
+
* this registry does not name, so a row keyed on them could never fire; registering them as
|
|
3913
|
+
* subject-key algorithms is adding surface rather than closing this. There is no RSA row: RFC 3279
|
|
3914
|
+
* sec. 2.3.1 states a MAY list and no prohibition, so an error-severity finding there would promote
|
|
3915
|
+
* a MAY-list omission to a MUST NOT and stop the signer emitting a certificate a caller may want. */
|
|
3916
|
+
var EC_FORBIDDEN_KU_BITS = ["keyEncipherment", "dataEncipherment"];
|
|
3917
|
+
var RFC5480_RULES = [
|
|
3918
|
+
{
|
|
3919
|
+
id: "lint/rfc5480/ec-key-usage-forbidden", severity: "error", source: "rfc5480",
|
|
3920
|
+
citation: "RFC 8813 3 (updating RFC 5480 3)",
|
|
3921
|
+
message: "an elliptic curve certificate's keyUsage must not assert keyEncipherment or dataEncipherment",
|
|
3922
|
+
/** @internal The same classifier the CABF key-usage tables select on, so one answer to "is this
|
|
3923
|
+
* an ECC key" serves both and they cannot diverge on a subject key. */
|
|
3924
|
+
appliesTo: _isEccKey,
|
|
3925
|
+
check: function (cert, ctx) {
|
|
3926
|
+
var ku = ctx.decode("keyUsage");
|
|
3927
|
+
if (!ku || !ku.value) return null;
|
|
3928
|
+
var set = [];
|
|
3929
|
+
for (var i = 0; i < EC_FORBIDDEN_KU_BITS.length; i++) {
|
|
3930
|
+
if (ku.value[EC_FORBIDDEN_KU_BITS[i]] === true) _push(set, EC_FORBIDDEN_KU_BITS[i]);
|
|
3931
|
+
}
|
|
3932
|
+
/** @internal One finding naming both bits rather than one each: the fault is the pair the
|
|
3933
|
+
* clause forbids, and an operator clearing them clears them together. The join is the
|
|
3934
|
+
* captured one, since this file is mid-conversion and a live `Array.prototype.join` is a
|
|
3935
|
+
* read a replacement decides. */
|
|
3936
|
+
return set.length ? { context: { bits: intrinsic.join(set, ", ") } } : null;
|
|
3937
|
+
},
|
|
3938
|
+
},
|
|
3939
|
+
];
|
|
3940
|
+
|
|
3904
3941
|
var RFC9935_RULES = [
|
|
3905
3942
|
{
|
|
3906
3943
|
id: "lint/rfc9935/kem-key-usage", severity: "error", source: "rfc9935", citation: "RFC 9935 5",
|
|
@@ -6650,7 +6687,12 @@ var RFC5280_EXTENSION_SYNTAX_RULES = RFC5280_RULES.filter(function (r) {
|
|
|
6650
6687
|
return r.id === "lint/rfc5280/extension-undecodable";
|
|
6651
6688
|
});
|
|
6652
6689
|
var PROFILES = Object.assign(Object.create(null), {
|
|
6653
|
-
|
|
6690
|
+
/** @internal The elliptic curve rows ride with rfc5280 rather than only under their own name,
|
|
6691
|
+
* because sec. 4.2.1.3 of that document delegates the per-algorithm key usage values and RFC 8813
|
|
6692
|
+
* is where the delegate's answer now is. Under their own name as well, so a caller can ask for
|
|
6693
|
+
* them alone and `pki.lint.profiles()` names the document that states the rule. */
|
|
6694
|
+
"rfc5280": intrinsic.concatList(RFC5280_RULES, RFC5480_RULES),
|
|
6695
|
+
"rfc5480": intrinsic.concatList(RFC5480_RULES, RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6654
6696
|
"rfc9881": RFC9881_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6655
6697
|
"rfc9909": RFC9909_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
6656
6698
|
"rfc9935": RFC9935_RULES.concat(RFC5280_EXTENSION_SYNTAX_RULES),
|
|
@@ -6685,6 +6727,11 @@ var ALL_RULES = RFC5280_RULES.concat(RFC9881_RULES).concat(RFC9909_RULES).concat
|
|
|
6685
6727
|
* inert under the default set because each one asks whether the caller named the profile. */
|
|
6686
6728
|
.concat(MOZILLA_RULES).concat(CHROME_RULES).concat(MICROSOFT_RULES).concat(APPLE_RULES)
|
|
6687
6729
|
.concat(NIST_TRANSITION_RULES).concat(CNSA2_RULES);
|
|
6730
|
+
/** @internal Appended through the capture rather than a fourth `.concat`: this file is
|
|
6731
|
+
* mid-conversion and the live `Array.prototype.concat` reads `Symbol.isConcatSpreadable` off each
|
|
6732
|
+
* operand and builds through ArraySpeciesCreate, so a replacement decides which rows the default
|
|
6733
|
+
* set holds. The pre-existing chain above is counted in the conversion budget. */
|
|
6734
|
+
ALL_RULES = intrinsic.concatList(ALL_RULES, RFC5480_RULES);
|
|
6688
6735
|
|
|
6689
6736
|
var CABF_TLS_CSR_RULES = CABF_TLS_RULES.filter(function (r) {
|
|
6690
6737
|
return CABF_TLS_CSR_RULE_IDS.indexOf(r.id) !== -1;
|
|
@@ -7556,10 +7603,10 @@ function _ingestAttrCert(input) {
|
|
|
7556
7603
|
* @example
|
|
7557
7604
|
* var pair = await pki.key.generate("Ed25519");
|
|
7558
7605
|
* var key = await pki.key.export(pair.privateKey);
|
|
7559
|
-
* var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "
|
|
7606
|
+
* var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "Alice" } },
|
|
7560
7607
|
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
7561
7608
|
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
7562
|
-
* { name: "
|
|
7609
|
+
* { name: "Example AA", publicKey: await pki.key.export(pair.publicKey), key: key });
|
|
7563
7610
|
* pki.lint.attrcert(der).findings.length; // -> 0 for a conforming attribute certificate
|
|
7564
7611
|
*/
|
|
7565
7612
|
function attrcert(input, opts) {
|
|
@@ -7701,11 +7748,11 @@ function rules(profile, artifact) {
|
|
|
7701
7748
|
* and `"default"`, are not among them.
|
|
7702
7749
|
* @example
|
|
7703
7750
|
* pki.lint.profiles();
|
|
7704
|
-
* // -> ["rfc5280", "rfc9881", "rfc9909", "rfc9935", "rfc9763", "rfc7633",
|
|
7705
|
-
* // "cabf-tls", "cabf-smime", "cabf-cs", "etsi-qc", "mozilla-root",
|
|
7706
|
-
* // "microsoft-root", "apple-root", "nist-pqc-transition", "cnsa-2.0",
|
|
7707
|
-
* // "rfc9883", "rfc5280-crl", "rfc6960", "rfc5019", "rfc5652", "rfc8933",
|
|
7708
|
-
* // "rfc5755"]
|
|
7751
|
+
* // -> ["rfc5280", "rfc5480", "rfc9881", "rfc9909", "rfc9935", "rfc9763", "rfc7633",
|
|
7752
|
+
* // "x509-altsig", "cabf-tls", "cabf-smime", "cabf-cs", "etsi-qc", "mozilla-root",
|
|
7753
|
+
* // "chrome-root", "microsoft-root", "apple-root", "nist-pqc-transition", "cnsa-2.0",
|
|
7754
|
+
* // "rfc2986", "rfc9883", "rfc5280-crl", "rfc6960", "rfc5019", "rfc5652", "rfc8933",
|
|
7755
|
+
* // "rfc3161", "rfc5755"]
|
|
7709
7756
|
*/
|
|
7710
7757
|
/** @internal `cabf-tls` names the certificate profile in this list and is not repeated for the
|
|
7711
7758
|
* request form: `pki.lint.csr` takes the same name and runs those rows against what a request
|
package/lib/ocsp-verify.js
CHANGED
|
@@ -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)
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
|
526
|
-
|
|
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
|
|
530
|
-
|
|
531
|
-
|
|
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
|
|
554
|
-
|
|
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) {
|