@blamejs/pki 0.9.4 → 0.9.6
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 +60 -0
- package/README.md +7 -6
- package/bin/pki.js +110 -34
- package/lib/acme.js +91 -46
- package/lib/attrcert-sign.js +4 -3
- package/lib/cmp-build.js +16 -6
- package/lib/cmp-session.js +19 -3
- package/lib/cmp-verify.js +25 -2
- package/lib/cms-compress.js +3 -3
- package/lib/cms-digest.js +10 -5
- package/lib/cms-encrypt.js +6 -1
- package/lib/cms-sign.js +16 -0
- package/lib/cms-verify.js +32 -14
- package/lib/constants.js +1 -0
- package/lib/crl-sign.js +3 -3
- package/lib/csr-sign.js +6 -2
- package/lib/ct.js +16 -3
- package/lib/est.js +67 -31
- package/lib/guard-all.js +2 -0
- package/lib/guard-distrust.js +224 -0
- package/lib/guard-identifier.js +27 -1
- package/lib/guard-json.js +1 -1
- package/lib/http-transport.js +299 -36
- package/lib/jose.js +1 -1
- package/lib/lint.js +67 -7
- package/lib/ocsp-verify.js +9 -3
- package/lib/ocsp.js +16 -4
- package/lib/oid.js +4 -0
- package/lib/path-validate.js +119 -18
- package/lib/pkcs12-build.js +6 -1
- package/lib/pki-build.js +38 -6
- package/lib/possession.js +6 -2
- package/lib/related-cert.js +2 -2
- package/lib/revocation-fetch.js +46 -11
- package/lib/scep.js +39 -14
- package/lib/schema-pkix.js +3 -3
- package/lib/sigstore.js +11 -6
- package/lib/smime.js +32 -16
- package/lib/tls-cert-compress.js +6 -5
- package/lib/trust.js +121 -20
- package/lib/trustanchor-build.js +16 -1
- package/lib/tsp-sign.js +15 -2
- package/lib/webauthn-mds.js +29 -8
- package/lib/webauthn.js +46 -14
- package/lib/x509-sign.js +26 -10
- package/package.json +2 -2
- package/sbom.cdx.json +6 -6
package/lib/cms-digest.js
CHANGED
|
@@ -51,7 +51,7 @@ var _DIGEST_OPTS = intrinsic.assign(intrinsic.create(null), {
|
|
|
51
51
|
var _VERIFY_DIGEST_OPTS = intrinsic.assign(intrinsic.create(null), { content: 1 });
|
|
52
52
|
|
|
53
53
|
function _digestName(opts) {
|
|
54
|
-
var name = opts.digestAlgorithm
|
|
54
|
+
var name = opts.digestAlgorithm == null ? "sha256" : opts.digestAlgorithm;
|
|
55
55
|
var key = guard.text.keyOf(name);
|
|
56
56
|
if (!NODE_DIGEST[key]) {
|
|
57
57
|
throw _err("cms/unsupported-algorithm", "unsupported digest algorithm " + guard.text.showValue(name) +
|
|
@@ -78,7 +78,8 @@ function _digestName(opts) {
|
|
|
78
78
|
* @opts
|
|
79
79
|
* - `digestAlgorithm` (string) -- `sha256` (default), `sha384` or `sha512`.
|
|
80
80
|
* - `contentType` (string) -- the encapsulated content type by OID name, default `data`.
|
|
81
|
-
* - `detached` (boolean) -- omit the content, leaving the digest to travel alone.
|
|
81
|
+
* - `detached` (boolean) -- omit the content, leaving the digest to travel alone. A value other
|
|
82
|
+
* than `true`, `false`, `null` or an absent option is refused `cms/bad-input`.
|
|
82
83
|
* - `pem` (boolean) -- return a PEM `CMS` block instead of DER.
|
|
83
84
|
* @example
|
|
84
85
|
* var der = await pki.cms.digest(Buffer.from("hello"));
|
|
@@ -94,14 +95,18 @@ async function _digest(content, opts) {
|
|
|
94
95
|
opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.digest options");
|
|
95
96
|
guard.identifier.assertKnownKeys(opts, _DIGEST_OPTS, _err, "cms/bad-input",
|
|
96
97
|
"pki.cms.digest has an unknown option ");
|
|
98
|
+
/** @internal Whether the content rides in the message is decided below by comparing this option
|
|
99
|
+
* to `true`, so a value of any other type attached a content the caller asked to leave out. */
|
|
100
|
+
var detached = guard.identifier.booleanOption(opts.detached, _err, "cms/bad-input",
|
|
101
|
+
"pki.cms.digest opts.detached");
|
|
97
102
|
var raw = guard.bytes.source(content, CmsError, "cms/bad-input", "content");
|
|
98
103
|
var alg = _digestName(opts);
|
|
99
|
-
var ctName = opts.contentType
|
|
104
|
+
var ctName = opts.contentType == null ? "data" : opts.contentType;
|
|
100
105
|
var ctOid = O(ctName);
|
|
101
106
|
if (!ctOid) throw _err("cms/bad-input", "opts.contentType is not a known OID name: " + guard.text.showValue(ctName));
|
|
102
107
|
|
|
103
108
|
var value = guard.crypto.digest(NODE_DIGEST[alg], raw);
|
|
104
|
-
var eciChildren =
|
|
109
|
+
var eciChildren = detached
|
|
105
110
|
? [b.oid(ctOid)]
|
|
106
111
|
: [b.oid(ctOid), b.explicit(0, b.octetString(raw))];
|
|
107
112
|
var dd = b.sequence([
|
|
@@ -164,7 +169,7 @@ async function _verifyDigest(input, opts) {
|
|
|
164
169
|
guard.der.assertParams(parsed.digestAlgorithm.parameters, "absentOrNull", _err,
|
|
165
170
|
"cms/unsupported-algorithm", parsed.digestAlgorithm.name + " digest algorithm (RFC 5754 sec. 2)");
|
|
166
171
|
var embedded = parsed.encapContentInfo.eContent;
|
|
167
|
-
var supplied = opts.content
|
|
172
|
+
var supplied = opts.content == null ? null
|
|
168
173
|
: guard.bytes.snapshotSource(opts.content, CmsError, "cms/bad-input", "opts.content");
|
|
169
174
|
if (embedded != null && supplied != null) {
|
|
170
175
|
throw _err("cms/bad-input", "the DigestedData carries its own content, so opts.content is redundant");
|
package/lib/cms-encrypt.js
CHANGED
|
@@ -649,7 +649,12 @@ function authenticate(content, recipients, opts) {
|
|
|
649
649
|
|
|
650
650
|
async function _authenticateImpl(content, recipients, opts) {
|
|
651
651
|
opts = opts || intrinsic.create(null);
|
|
652
|
-
|
|
652
|
+
/** @internal Typed before it is used, because this switch picks the option set the door below
|
|
653
|
+
* checks against as well as the message that gets built: with `authenticatedAttributes: false`
|
|
654
|
+
* the MAC covers the content octets directly, so a value of any other type selected a
|
|
655
|
+
* structurally different message than the caller asked for and said nothing. */
|
|
656
|
+
var withAttrs = guard.identifier.booleanOptionDefaultOn(opts.authenticatedAttributes, _err,
|
|
657
|
+
"cms/bad-input", "pki.cms.authenticate opts.authenticatedAttributes");
|
|
653
658
|
guard.identifier.assertKnownKeys(opts, withAttrs ? _AUTHENTICATE_OPTS_ATTRS : _AUTHENTICATE_OPTS_BARE,
|
|
654
659
|
_err, "cms/bad-input", function (k) {
|
|
655
660
|
return "pki.cms.authenticate has an unknown option " + JSON.stringify(k) +
|
package/lib/cms-sign.js
CHANGED
|
@@ -186,6 +186,19 @@ function _algorithmProtectionPair(attrOid, digestAlgId, armAlgId, armTag) {
|
|
|
186
186
|
* The emitter below reads the same option again, which is sound because the options door refuses
|
|
187
187
|
* one supplied through an accessor, own or inherited, before any value is read: two reads of a
|
|
188
188
|
* plain field are one answer. */
|
|
189
|
+
/** @internal The three switches these verbs document, typed where the options arrive. Each is read
|
|
190
|
+
* further down by comparing it to `false`, which answers for a value of any other type as the
|
|
191
|
+
* state the caller did not ask for: measured, `certificates: "false"` embedded the signer
|
|
192
|
+
* certificate the caller asked to withhold, and `detached: "false"` is read as "not detached" by
|
|
193
|
+
* the shape test and as "detached" by the content test, emitting an attached SignedData with no
|
|
194
|
+
* content in it. Typed here, those comparisons read a value already known to be a boolean.
|
|
195
|
+
* `detached` is not an option every one of these verbs accepts; an absent one types as absent. */
|
|
196
|
+
function _assertSignSwitches(opts, verb) {
|
|
197
|
+
guard.identifier.booleanOptionDefaultOn(opts.certificates, _err, "cms/bad-input", verb + " opts.certificates");
|
|
198
|
+
guard.identifier.booleanOptionDefaultOn(opts.signedAttributes, _err, "cms/bad-input", verb + " opts.signedAttributes");
|
|
199
|
+
guard.identifier.booleanOption(opts.detached, _err, "cms/bad-input", verb + " opts.detached");
|
|
200
|
+
}
|
|
201
|
+
|
|
189
202
|
function _assertProtectionPlaceable(opts) {
|
|
190
203
|
var wanted = cms.algorithmProtectionOid(opts.algorithmProtection, _err);
|
|
191
204
|
if (opts.signedAttributes === false && wanted) {
|
|
@@ -373,6 +386,7 @@ function _signDispatch(content, signers, opts) {
|
|
|
373
386
|
async function _signStream(stream, signers, opts) {
|
|
374
387
|
opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.sign options");
|
|
375
388
|
guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
|
|
389
|
+
_assertSignSwitches(opts, "pki.cms.sign");
|
|
376
390
|
_assertProtectionPlaceable(opts);
|
|
377
391
|
if (opts.detached !== true) {
|
|
378
392
|
throw _err("cms/bad-input", "a streaming (async-iterable) content requires opts.detached: true; an attached " +
|
|
@@ -413,6 +427,7 @@ async function _signStream(stream, signers, opts) {
|
|
|
413
427
|
function _sign(content, signers, opts) {
|
|
414
428
|
opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.sign options");
|
|
415
429
|
guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
|
|
430
|
+
_assertSignSwitches(opts, "pki.cms.sign");
|
|
416
431
|
_assertProtectionPlaceable(opts);
|
|
417
432
|
var contentBuf = _toBuf(content, "content");
|
|
418
433
|
var list = Array.isArray(signers) ? _b.reqDenseArray(signers, "the signer list") : [signers];
|
|
@@ -680,6 +695,7 @@ function countersign(cmsInput, signers, opts) {
|
|
|
680
695
|
function _countersign(cmsInput, signers, opts) {
|
|
681
696
|
opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.countersign options");
|
|
682
697
|
guard.identifier.assertKnownKeys(opts, KNOWN_COUNTERSIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
|
|
698
|
+
_assertSignSwitches(opts, "pki.cms.countersign");
|
|
683
699
|
_assertProtectionPlaceable(opts);
|
|
684
700
|
var list = Array.isArray(signers) ? signers : [signers];
|
|
685
701
|
if (!list.length) throw _err("cms/bad-input", "pki.cms.countersign requires at least one countersigner");
|
package/lib/cms-verify.js
CHANGED
|
@@ -761,6 +761,7 @@ async function _verifyOneTimestamp(attr, targetSig, claimedSigningTime, opts) {
|
|
|
761
761
|
trustAnchors: opts.timestampTrustAnchors,
|
|
762
762
|
revocationChecker: opts.timestampRevocationChecker,
|
|
763
763
|
allowWeakDigests: opts.allowWeakDigests === true,
|
|
764
|
+
distrustedKeys: opts.distrustedKeys,
|
|
764
765
|
});
|
|
765
766
|
} catch (e) {
|
|
766
767
|
return guard.verdict.of({ valid: false, trusted: false, genTime: null,
|
|
@@ -975,6 +976,10 @@ async function _verifyOneCountersig(vDer, targetSig, parsedCerts, allowWeakDiges
|
|
|
975
976
|
* trust decision from the signer: an operator may anchor one and not the other.
|
|
976
977
|
* @opts timestampRevocationChecker A revocation checker for the TSA chain, as `pki.path.validate`
|
|
977
978
|
* takes one. Each row reports what it established in `revocationChecked`.
|
|
979
|
+
* @opts distrustedKeys SHA-256 digests of SubjectPublicKeyInfo DER (64 hexadecimal characters or
|
|
980
|
+
* 32 bytes each, see `pki.trust.spkiDigest`), applied to the signer chain and to the
|
|
981
|
+
* TSA chain of every timestamp row. A listed key on a chain, the anchor included,
|
|
982
|
+
* reports that signer or that row as not trusted.
|
|
978
983
|
* @opts requireAlgorithmProtection Fail a signer that carries no RFC 6211
|
|
979
984
|
* `CMSAlgorithmProtection` attribute, with `cms/missing-algorithm-protection`. Off
|
|
980
985
|
* by default: RFC 6211 says nothing about absence and RFC 8933 sec. 4.1 makes
|
|
@@ -1026,7 +1031,7 @@ var _VERIFY_OPTS = intrinsic.assign(intrinsic.create(null), { certs: 1, content:
|
|
|
1026
1031
|
* leaving the signer unanchored, or the reverse, and one `trustAnchors` for both would make those
|
|
1027
1032
|
* two answers one. */
|
|
1028
1033
|
timestampCerts: 1, timestampTrustAnchors: 1, timestampRevocationChecker: 1,
|
|
1029
|
-
requireAlgorithmProtection: 1 });
|
|
1034
|
+
requireAlgorithmProtection: 1, distrustedKeys: 1 });
|
|
1030
1035
|
|
|
1031
1036
|
function verify(input, opts) {
|
|
1032
1037
|
return guard.async.deferred(function () { return _verify(input, opts); });
|
|
@@ -1043,12 +1048,15 @@ async function _verify(input, opts) {
|
|
|
1043
1048
|
var allowWeakDigests = opts.allowWeakDigests === true;
|
|
1044
1049
|
var requireProtection = guard.identifier.booleanOption(opts.requireAlgorithmProtection, _err,
|
|
1045
1050
|
"cms/bad-input", "pki.cms.verify opts.requireAlgorithmProtection");
|
|
1051
|
+
/** @internal Typed here, before any signer is judged: the signer chain is built only for a signer
|
|
1052
|
+
* that verified, and every throw from a timestamp verify becomes a row. One list serves both. */
|
|
1053
|
+
var distrustedKeys = guard.distrust.keys(opts.distrustedKeys, _err, "cms/bad-input", "pki.cms.verify opts.distrustedKeys");
|
|
1046
1054
|
var parsed = guard.parsed.acceptDerived(input, "cms", function (bytes) {
|
|
1047
1055
|
return cms.parse(_snapshotIfBytes(bytes, "pki.cms.verify"));
|
|
1048
1056
|
}, _err, "cms/bad-input", "the SignedData");
|
|
1049
1057
|
if (!Array.isArray(parsed.signerInfos)) throw _err("cms/bad-input", "input is not a CMS SignedData");
|
|
1050
|
-
var trustCfg = _snapshotTrust(opts);
|
|
1051
|
-
var tsTrustCfg = _snapshotTimestampTrust(opts, allowWeakDigests);
|
|
1058
|
+
var trustCfg = _snapshotTrust(opts, distrustedKeys);
|
|
1059
|
+
var tsTrustCfg = _snapshotTimestampTrust(opts, allowWeakDigests, distrustedKeys);
|
|
1052
1060
|
var parsedCerts = [];
|
|
1053
1061
|
(parsed.certificates || []).forEach(function (c) { _addCert(parsedCerts, c && c.bytes ? c.bytes : c); });
|
|
1054
1062
|
(opts.certs || []).forEach(function (c) { _addCert(parsedCerts, c); });
|
|
@@ -1130,7 +1138,7 @@ function _cloneAnchorValue(v, depth) {
|
|
|
1130
1138
|
}
|
|
1131
1139
|
return out;
|
|
1132
1140
|
}
|
|
1133
|
-
function _snapshotTrust(opts) {
|
|
1141
|
+
function _snapshotTrust(opts, distrustedKeys) {
|
|
1134
1142
|
var raw = opts.trustAnchors;
|
|
1135
1143
|
var anchors = null;
|
|
1136
1144
|
if (raw != null) {
|
|
@@ -1140,9 +1148,10 @@ function _snapshotTrust(opts) {
|
|
|
1140
1148
|
}
|
|
1141
1149
|
return {
|
|
1142
1150
|
trustAnchors: anchors,
|
|
1143
|
-
time: guard.time.isDate(opts.time) ? new intrinsic.Date(guard.time.instantOf(opts.time)) : (opts.time
|
|
1151
|
+
time: guard.time.isDate(opts.time) ? new intrinsic.Date(guard.time.instantOf(opts.time)) : (opts.time == null ? new intrinsic.Date() : opts.time),
|
|
1144
1152
|
requiredEku: Array.isArray(opts.requiredEku) ? guard.list.snapshot(opts.requiredEku) : opts.requiredEku,
|
|
1145
1153
|
checkPurpose: opts.checkPurpose,
|
|
1154
|
+
distrustedKeys: distrustedKeys,
|
|
1146
1155
|
};
|
|
1147
1156
|
}
|
|
1148
1157
|
|
|
@@ -1153,7 +1162,7 @@ function _snapshotTrust(opts) {
|
|
|
1153
1162
|
* authorities. The certificates are copied by value, since a caller holding the array could swap an
|
|
1154
1163
|
* entry after the row that read it. The checker is a function and is captured as the one reference
|
|
1155
1164
|
* every row calls. */
|
|
1156
|
-
function _snapshotTimestampTrust(opts, allowWeakDigests) {
|
|
1165
|
+
function _snapshotTimestampTrust(opts, allowWeakDigests, distrustedKeys) {
|
|
1157
1166
|
var certs = opts.timestampCerts;
|
|
1158
1167
|
var anchors = opts.timestampTrustAnchors;
|
|
1159
1168
|
return {
|
|
@@ -1167,6 +1176,7 @@ function _snapshotTimestampTrust(opts, allowWeakDigests) {
|
|
|
1167
1176
|
}),
|
|
1168
1177
|
timestampRevocationChecker: opts.timestampRevocationChecker,
|
|
1169
1178
|
allowWeakDigests: allowWeakDigests,
|
|
1179
|
+
distrustedKeys: distrustedKeys,
|
|
1170
1180
|
};
|
|
1171
1181
|
}
|
|
1172
1182
|
|
|
@@ -1200,7 +1210,7 @@ async function _applyTrust(res, parsedCerts, cfg) {
|
|
|
1200
1210
|
if (!anchors.length) {
|
|
1201
1211
|
throw _err("cms/bad-input", "opts.trustAnchors is empty -- name at least one root, or omit it to state that the signer is not being anchored");
|
|
1202
1212
|
}
|
|
1203
|
-
if (cfg.time
|
|
1213
|
+
if (cfg.time != null) guard.time.assertValid(cfg.time, _err, "cms/bad-input", "opts.time");
|
|
1204
1214
|
var purposes = _engine.resolvePurposeOpts({ requiredEku: cfg.requiredEku, checkPurpose: cfg.checkPurpose });
|
|
1205
1215
|
intrinsic.forEach(anchors, function (a) {
|
|
1206
1216
|
_engine.toAnchor(a);
|
|
@@ -1210,7 +1220,7 @@ async function _applyTrust(res, parsedCerts, cfg) {
|
|
|
1210
1220
|
var s = res.signers[_si];
|
|
1211
1221
|
{
|
|
1212
1222
|
if (s.ok !== true || !s.cert) { s.trusted = false; continue; }
|
|
1213
|
-
var buildOpts = { trustAnchors: anchors, intermediates: pool, validate: true, time: at };
|
|
1223
|
+
var buildOpts = { trustAnchors: anchors, intermediates: pool, validate: true, time: at, distrustedKeys: cfg.distrustedKeys };
|
|
1214
1224
|
if (cfg.requiredEku != null) buildOpts.requiredEku = cfg.requiredEku;
|
|
1215
1225
|
if (cfg.checkPurpose != null) buildOpts.checkPurpose = cfg.checkPurpose;
|
|
1216
1226
|
if (!_keyUsagePermitsSigning(parsedCerts, s.cert)) { s.trusted = false; continue; }
|
|
@@ -1275,16 +1285,20 @@ function _addCert(out, der) {
|
|
|
1275
1285
|
* the same content as the buffered form.
|
|
1276
1286
|
*
|
|
1277
1287
|
* @opts detached Omit the encapsulated content (a detached signature; the verifier
|
|
1278
|
-
* supplies the content). Default false.
|
|
1288
|
+
* supplies the content). Default false. A value other than `true`,
|
|
1289
|
+
* `false`, `null` or an absent option is refused `cms/bad-input`.
|
|
1279
1290
|
* @opts eContentType The encapsulated content type (an OID name). Default `data`.
|
|
1280
1291
|
* @opts signedAttributes Include signed attributes (content-type, message-digest, signing-time);
|
|
1281
|
-
* false signs the content directly. Default true.
|
|
1292
|
+
* false signs the content directly. Default true. A value other than
|
|
1293
|
+
* `true`, `false`, `null` or an absent option is refused `cms/bad-input`.
|
|
1282
1294
|
* @opts additionalSignedAttributes Extra signed attributes to add to every SignerInfo, each
|
|
1283
1295
|
* `{ type, values }` (`type` an OID name or dotted string, `values` an
|
|
1284
1296
|
* array of DER value `Buffer`s). Covered by the signature.
|
|
1285
1297
|
* @opts signingTime A `Date` for the signing-time attribute, or false to omit it.
|
|
1286
1298
|
* @opts sid `"issuerAndSerial"` (default) or `"ski"` (subjectKeyIdentifier).
|
|
1287
|
-
* @opts certificates Embed the signer certificates in the output. Default true.
|
|
1299
|
+
* @opts certificates Embed the signer certificates in the output. Default true. A value other
|
|
1300
|
+
* than `true`, `false`, `null` or an absent option is refused
|
|
1301
|
+
* `cms/bad-input`.
|
|
1288
1302
|
* @opts pem Return a PEM string (`-----BEGIN CMS-----`) instead of a DER Buffer.
|
|
1289
1303
|
* @opts unsignedAttributes Unsigned attributes for every SignerInfo, each `{ type, values }`
|
|
1290
1304
|
* (`type` an OID name or dotted string, `values` an array of DER value
|
|
@@ -1341,12 +1355,15 @@ var sign = cmsSign.sign;
|
|
|
1341
1355
|
* instead of the primary signature (a nested countersignature).
|
|
1342
1356
|
* @opts signedAttributes Include signed attributes (message-digest + signing-time; never a
|
|
1343
1357
|
* content-type). Default true; false signs the target signature octets
|
|
1344
|
-
* directly.
|
|
1358
|
+
* directly. A value other than `true`, `false`, `null` or an absent
|
|
1359
|
+
* option is refused `cms/bad-input`.
|
|
1345
1360
|
* @opts signingTime A `Date` for the countersignature's signing-time, or false to omit it.
|
|
1346
1361
|
* @opts additionalSignedAttributes Extra signed attributes on the countersignature (a content-type
|
|
1347
1362
|
* is rejected, RFC 5652 sec. 11.4).
|
|
1348
1363
|
* @opts sid `"issuerAndSerial"` (default) or `"ski"`.
|
|
1349
|
-
* @opts certificates Embed the countersigner certificate(s) in the SignedData. Default
|
|
1364
|
+
* @opts certificates Embed the countersigner certificate(s) in the SignedData. Default
|
|
1365
|
+
* true. A value other than `true`, `false`, `null` or an absent option
|
|
1366
|
+
* is refused `cms/bad-input`.
|
|
1350
1367
|
* @opts algorithmProtection Emit the RFC 6211 `CMSAlgorithmProtection` attribute on the
|
|
1351
1368
|
* countersignature, copying ITS own digest and signature algorithm
|
|
1352
1369
|
* identifiers rather than the countersigned signature's. Off by
|
|
@@ -1440,7 +1457,8 @@ var encrypt = cmsEncrypt.encrypt;
|
|
|
1440
1457
|
* @opts digestAlgorithm The authenticated-attributes message-digest hash (defaults to the MAC hash).
|
|
1441
1458
|
* @opts authenticatedAttributes Include the content-type + message-digest authenticated attributes
|
|
1442
1459
|
* (the default, and required for a non-`data` content type); false MACs the
|
|
1443
|
-
* content directly.
|
|
1460
|
+
* content directly. A value other than `true`, `false`, `null` or an absent
|
|
1461
|
+
* option is refused `cms/bad-input`.
|
|
1444
1462
|
* @opts authAttrs Extra authenticated attributes appended to the content-type + message-digest pair.
|
|
1445
1463
|
* @opts contentType The encapsulated content type (an OID name). Default `data`.
|
|
1446
1464
|
* @opts keyIdentifier `"issuerAndSerial"` (default) or `"subjectKeyIdentifier"`.
|
package/lib/constants.js
CHANGED
|
@@ -165,6 +165,7 @@ var LIMITS = Object.assign(Object.create(null), {
|
|
|
165
165
|
MERKLE_MAX_PROOF_NODES: 65,
|
|
166
166
|
MERKLE_MAX_LEAVES: 1048576,
|
|
167
167
|
PROXY_MAX_TLS_ANCHORS: 256,
|
|
168
|
+
DISTRUSTED_KEYS_MAX: 4096,
|
|
168
169
|
PATH_MAX_CERTS: 100,
|
|
169
170
|
PATH_MAX_POLICY_NODES: 4096,
|
|
170
171
|
PATH_BUILD_MAX_DEPTH: 20,
|
package/lib/crl-sign.js
CHANGED
|
@@ -770,16 +770,16 @@ function isRevoked(crl, serialNumber, opts) {
|
|
|
770
770
|
return "pki.crl.isRevoked has an unknown option " + JSON.stringify(k) +
|
|
771
771
|
"; it takes `time` and `historicalMode`";
|
|
772
772
|
});
|
|
773
|
-
if (o.historicalMode
|
|
773
|
+
if (o.historicalMode != null && o.time == null) {
|
|
774
774
|
throw _err("crl/bad-input", "pki.crl.isRevoked opts.historicalMode says how to read an entry " +
|
|
775
775
|
"against the instant asked about, so it needs opts.time as well");
|
|
776
776
|
}
|
|
777
|
-
if (o.historicalMode
|
|
777
|
+
if (o.historicalMode != null && typeof o.historicalMode !== "boolean") {
|
|
778
778
|
throw _err("crl/bad-input", "pki.crl.isRevoked opts.historicalMode must be a boolean");
|
|
779
779
|
}
|
|
780
780
|
var parsed = _coerceCrl(crl);
|
|
781
781
|
var hex = _serialHexOf(serialNumber);
|
|
782
|
-
if (o.time
|
|
782
|
+
if (o.time != null) {
|
|
783
783
|
guard.time.assertValid(o.time, _err, "crl/bad-input", "pki.crl.isRevoked opts.time");
|
|
784
784
|
var at = guard.time.instantOf(o.time);
|
|
785
785
|
if (at < guard.time.instantOf(parsed.thisUpdate)) {
|
package/lib/csr-sign.js
CHANGED
|
@@ -114,7 +114,7 @@ function _possessionStatement(spec) {
|
|
|
114
114
|
"privateKeyPossessionStatement needs the certificate: this verb resolves the signature algorithm from its key and checks the signature against it. Pass includeCertificate: false to leave it off the wire, which RFC 9883 sec. 3 allows");
|
|
115
115
|
}
|
|
116
116
|
var certDer = _b.reqDer(spec.certificate, "privateKeyPossessionStatement certificate");
|
|
117
|
-
var include = spec.includeCertificate
|
|
117
|
+
var include = spec.includeCertificate == null ? true : spec.includeCertificate;
|
|
118
118
|
if (include !== true && include !== false) {
|
|
119
119
|
throw _err("csr/bad-input", "privateKeyPossessionStatement includeCertificate is a flag, so it is true, false or omitted");
|
|
120
120
|
}
|
|
@@ -172,7 +172,11 @@ function _relatedCertRequest(spec) {
|
|
|
172
172
|
* subject-owned set `pki.x509.sign` takes, or an array of pre-encoded Extension DER) and
|
|
173
173
|
* `challengePassword`. An extension the issuing CA assigns (authorityKeyIdentifier, the
|
|
174
174
|
* certificate-transparency pair, the CA version and previous-certificate hash, ocspNoCheck) is not a
|
|
175
|
-
* request and is refused by name. `
|
|
175
|
+
* request and is refused by name. The per-extension criticality flags `pki.x509.sign` takes
|
|
176
|
+
* (`keyUsageCritical`, default true, and `extendedKeyUsageCritical`,
|
|
177
|
+
* `certificatePoliciesCritical`, `policyMappingsCritical`, `qcStatementsCritical`,
|
|
178
|
+
* `tlsFeatureCritical`, each default false) apply here too; each is a boolean, and a value other
|
|
179
|
+
* than `true`, `false`, `null` or an absent flag is refused `csr/bad-input`. `key` (or
|
|
176
180
|
* `{ key }`) is the subject's own PKCS#8 private key, WebCrypto CryptoKey, or signer
|
|
177
181
|
* `{ algorithm, publicKey, sign }`, so the request is self-signed
|
|
178
182
|
* to prove possession of the private half of `subjectPublicKey`, and that proof is verified before the
|
package/lib/ct.js
CHANGED
|
@@ -1216,6 +1216,9 @@ function _trustValue(v, field) {
|
|
|
1216
1216
|
return v;
|
|
1217
1217
|
}
|
|
1218
1218
|
|
|
1219
|
+
/** @internal The settings `_tlsForFetch` reads. A setting not on the list is refused at the door. */
|
|
1220
|
+
var _TLS_KNOWN = intrinsic.freeze(guard.identifier.pickSettings(null, ["anchors", "useSystemStore", "cert", "key",
|
|
1221
|
+
"minVersion", "servername", "checkServerIdentity", "distrustedKeys"]));
|
|
1219
1222
|
function _tlsForFetch(opts) {
|
|
1220
1223
|
/** @internal A record with NO prototype where the caller supplied no TLS at all, because the empty
|
|
1221
1224
|
* object a literal gives inherits from `Object.prototype` and every field below is then answerable
|
|
@@ -1232,9 +1235,12 @@ function _tlsForFetch(opts) {
|
|
|
1232
1235
|
anchors = intrinsic.isArray(anchors)
|
|
1233
1236
|
? guard.list.copyMap(anchors, function (a, i) { return _trustValue(a, "opts.tls.anchors[" + i + "]"); })
|
|
1234
1237
|
: _trustValue(anchors, "opts.tls.anchors");
|
|
1238
|
+
/** @internal The distrust list is typed into a frozen copy. `guard.distrust.keys` reads each entry
|
|
1239
|
+
* through its descriptor and refuses an accessor, a hole and a Proxy, so no caller code runs. */
|
|
1240
|
+
var distrustedKeys = guard.distrust.keys(t.distrustedKeys, _ctErr, "ct/bad-input", "opts.tls.distrustedKeys");
|
|
1235
1241
|
return { anchors: anchors, useSystemStore: t.useSystemStore, cert: _trustValue(t.cert, "opts.tls.cert"),
|
|
1236
1242
|
key: _trustValue(t.key, "opts.tls.key"), minVersion: t.minVersion, servername: t.servername,
|
|
1237
|
-
checkServerIdentity: t.checkServerIdentity };
|
|
1243
|
+
checkServerIdentity: t.checkServerIdentity, distrustedKeys: distrustedKeys };
|
|
1238
1244
|
}
|
|
1239
1245
|
|
|
1240
1246
|
function _sameOrigin(a, b) { return _urlOrigin(new _URL(a)) === _urlOrigin(new _URL(b)); }
|
|
@@ -1418,6 +1424,7 @@ function _settleDecisive(o, keyName) {
|
|
|
1418
1424
|
guard.identifier.refuseAccessorFields(o.tls,
|
|
1419
1425
|
guard.identifier.readableNames(o.tls, _ctErr, "ct/bad-input", "opts.tls"),
|
|
1420
1426
|
_ctErr, "ct/bad-input", "opts.tls");
|
|
1427
|
+
guard.identifier.assertKnownKeys(o.tls, _TLS_KNOWN, _ctErr, "ct/bad-input", "unknown opts.tls setting ");
|
|
1421
1428
|
/** @internal And the ELEMENTS of the anchor list, which refusing accessors on `tls` itself does not
|
|
1422
1429
|
* reach. An accessor on `anchors[0]` runs when the list is copied, and one that overwrote
|
|
1423
1430
|
* `sth.rootHash` with the leaf being proven had an empty audit path verify. The refusal has to go
|
|
@@ -1652,11 +1659,17 @@ async function verifySth(sth, logKey) {
|
|
|
1652
1659
|
*
|
|
1653
1660
|
* `treeSize` and `timestamp` come back as `BigInt`, since both are uint64 on the wire.
|
|
1654
1661
|
*
|
|
1662
|
+
* `opts.tls.distrustedKeys` lists SHA-256 SubjectPublicKeyInfo digests (see pki.trust.spkiDigest). A
|
|
1663
|
+
* log server whose chain carries a listed key is refused with `ct/server-key-distrusted`, and a
|
|
1664
|
+
* malformed list is `ct/bad-input`. A `tls` setting other than `anchors`, `useSystemStore`, `cert`,
|
|
1665
|
+
* `key`, `minVersion`, `servername`, `checkServerIdentity` and `distrustedKeys` is refused
|
|
1666
|
+
* `ct/bad-input`. The other sec. 4 messages read it the same way.
|
|
1667
|
+
*
|
|
1655
1668
|
* @opts
|
|
1656
1669
|
* url: string, // the log's base URL, with or without the ct/v1/ prefix
|
|
1657
1670
|
* logKey: BufferSource, // the log's SubjectPublicKeyInfo, pinned by the caller
|
|
1658
1671
|
* transport: function, // optional (request) => Promise<{ status, headers, body }>
|
|
1659
|
-
* tls: object, // optional { anchors, useSystemStore, ... } when no transport is given
|
|
1672
|
+
* tls: object, // optional { anchors, useSystemStore, distrustedKeys, ... } when no transport is given
|
|
1660
1673
|
* headers: object, // optional extra request headers
|
|
1661
1674
|
* timeout: number, // optional per-request timeout in milliseconds
|
|
1662
1675
|
* maxResponseBytes: number, // optional response cap
|
|
@@ -2194,7 +2207,7 @@ function _b64Array(obj, name, code, label, exactLen, maxCount) {
|
|
|
2194
2207
|
* @opts signerKey REQUIRED and caller-pinned -- the distributor SubjectPublicKeyInfo as a DER Buffer; no baked-in key.
|
|
2195
2208
|
* @opts sigUrl OPTIONAL -- the detached `log_list.sig` URL (https, must share the log-list URL's origin); default `url` with `.json` -> `.sig` (a non-.json url requires an explicit sigUrl).
|
|
2196
2209
|
* @opts transport OPTIONAL injectable `transport(request) -> Promise<{status,headers,body}>` (default `pki.transport.https`); the test seam.
|
|
2197
|
-
* @opts tls OPTIONAL `{ anchors, useSystemStore, cert, key, minVersion, servername, checkServerIdentity }` threaded to the default transport (ignored when a transport is injected); `rejectUnauthorized` is always on.
|
|
2210
|
+
* @opts tls OPTIONAL `{ anchors, useSystemStore, cert, key, minVersion, servername, checkServerIdentity, distrustedKeys }` threaded to the default transport (ignored when a transport is injected); `rejectUnauthorized` is always on. `distrustedKeys` lists SHA-256 SubjectPublicKeyInfo digests (see pki.trust.spkiDigest): a distributor whose chain carries a listed key is refused with `ct/server-key-distrusted`, and a malformed list is `ct/bad-input`. A setting not named in that list is refused `ct/bad-input`.
|
|
2198
2211
|
* @opts headers OPTIONAL extra request headers (the request-framing headers are stripped; the verb owns the GET method).
|
|
2199
2212
|
* @opts timeout OPTIONAL ms budget, default 30s (cap-validated).
|
|
2200
2213
|
* @opts maxResponseBytes OPTIONAL per-GET size cap, default 4 MiB, tightenable DOWNWARD only.
|