@blamejs/pki 0.8.30 → 0.8.48
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 +411 -0
- package/README.md +67 -16
- package/bin/pki.js +716 -30
- package/examples/issue-a-certificate.js +90 -0
- package/examples/post-quantum.js +66 -0
- package/examples/read-a-certificate.js +56 -0
- package/examples/revoke-and-check.js +86 -0
- package/examples/sign-and-verify-cms.js +63 -0
- package/index.js +18 -5
- package/lib/acme.js +223 -74
- package/lib/alt-sig.js +441 -0
- package/lib/asn1-der.js +48 -25
- package/lib/attrcert-sign.js +27 -19
- package/lib/cbor-det.js +24 -14
- package/lib/cmc-build.js +9 -2
- package/lib/cmc-verify.js +14 -3
- package/lib/cmp-build.js +17 -5
- package/lib/cmp-session.js +24 -11
- package/lib/cmp-verify.js +28 -19
- package/lib/cms-compress.js +3 -1
- package/lib/cms-decrypt.js +102 -7
- package/lib/cms-encrypt.js +59 -7
- package/lib/cms-sign.js +31 -9
- package/lib/cms-verify.js +22 -18
- package/lib/composite-kem.js +30 -18
- package/lib/composite-sig.js +29 -14
- package/lib/constants.js +28 -0
- package/lib/crl-sign.js +111 -19
- package/lib/crmf-sign.js +167 -15
- package/lib/csr-sign.js +117 -6
- package/lib/ct.js +939 -48
- package/lib/est.js +29 -16
- package/lib/framework-error.js +12 -0
- package/lib/guard-async.js +1 -1
- package/lib/guard-bytes.js +103 -20
- package/lib/guard-crypto.js +205 -3
- package/lib/guard-encoding.js +6 -1
- package/lib/guard-identifier.js +55 -8
- package/lib/guard-intrinsic.js +43 -0
- package/lib/guard-json.js +122 -9
- package/lib/guard-limits.js +12 -1
- package/lib/guard-list.js +63 -8
- package/lib/guard-name.js +167 -20
- package/lib/guard-range.js +43 -5
- package/lib/guard-text.js +41 -0
- package/lib/guard-verdict.js +39 -8
- package/lib/hpke.js +688 -42
- package/lib/http-transport.js +156 -4
- package/lib/identity-match.js +4 -4
- package/lib/inspect.js +448 -2
- package/lib/jose.js +93 -25
- package/lib/key.js +18 -13
- package/lib/lint.js +165 -1
- package/lib/merkle.js +233 -27
- package/lib/ocsp.js +27 -14
- package/lib/oid.js +14 -4
- package/lib/path-validate.js +143 -61
- package/lib/pkcs12-build.js +7 -2
- package/lib/pki-build.js +31 -7
- package/lib/possession.js +575 -0
- package/lib/related-cert.js +708 -0
- package/lib/revocation-fetch.js +6 -6
- package/lib/scep.js +15 -5
- package/lib/schema-c509.js +5 -2
- package/lib/schema-cmc.js +1 -1
- package/lib/schema-cmp.js +8 -8
- package/lib/schema-cms.js +2 -15
- package/lib/schema-csr.js +69 -0
- package/lib/schema-engine.js +10 -3
- package/lib/schema-pkix.js +267 -4
- package/lib/schema-tsp.js +7 -6
- package/lib/sign-scheme.js +22 -9
- package/lib/sigstore.js +684 -168
- package/lib/sleep.js +8 -1
- package/lib/smime.js +22 -1
- package/lib/tlog.js +1235 -0
- package/lib/tls-cert-compress.js +25 -8
- package/lib/tsp-sign.js +120 -35
- package/lib/tuf.js +1395 -0
- package/lib/validator-tpm.js +4 -1
- package/lib/verify-seam.js +7 -1
- package/lib/webauthn-mds.js +41 -4
- package/lib/webauthn.js +43 -25
- package/lib/webcrypto.js +48 -46
- package/lib/x509-sign.js +225 -19
- package/package.json +2 -1
- package/sbom.cdx.json +6 -6
package/lib/tls-cert-compress.js
CHANGED
|
@@ -168,11 +168,19 @@ function _resolveAllowed(opts) {
|
|
|
168
168
|
*/
|
|
169
169
|
function decompressCertificate(bytes, opts) {
|
|
170
170
|
opts = opts || {};
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
/** @internal Viewed, CAPPED, then COPIED, and only then are the options read. A view stays a window
|
|
172
|
+
* onto the caller's buffer, and `_resolveCap` reads `opts`, which is caller code when it is an
|
|
173
|
+
* accessor: a `maxOutputBytes` getter that wrote another valid message of the same length over the
|
|
174
|
+
* buffer had this verb decompress the REPLACEMENT, having framed the one it was handed. Measured, a
|
|
175
|
+
* message framing `0141000000` came back as `0142000000`. `compressCertificate` takes its copy before
|
|
176
|
+
* reading `opts.algorithm` for the same reason; this is the other half of that pair. The cap sits
|
|
177
|
+
* ahead of the copy so the copy is not an allocation the size of whatever arrived. */
|
|
178
|
+
var viewed = guard.bytes.source(bytes, TlsError, "tls/bad-input", "the compressed certificate message");
|
|
179
|
+
if (guard.bytes.lengthOf(viewed) > C.LIMITS.TLS_CERT_MSG_MAX_BYTES) {
|
|
180
|
+
throw _err("tls/too-large", "the compressed certificate message is " + guard.bytes.lengthOf(viewed) +
|
|
174
181
|
" bytes, over the " + C.LIMITS.TLS_CERT_MSG_MAX_BYTES + "-byte handshake framing limit");
|
|
175
182
|
}
|
|
183
|
+
var view = guard.bytes.snapshot(viewed, TlsError, "tls/bad-input", "the compressed certificate message");
|
|
176
184
|
var cap = _resolveCap(opts);
|
|
177
185
|
var allowed = _resolveAllowed(opts);
|
|
178
186
|
|
|
@@ -308,7 +316,20 @@ function parseCertificateMessage(bytes, opts) {
|
|
|
308
316
|
*/
|
|
309
317
|
function compressCertificate(certificateMessage, opts) {
|
|
310
318
|
opts = opts || {};
|
|
311
|
-
|
|
319
|
+
/** @internal Copied, not viewed. `opts.algorithm` is read below, and reading an option is caller code
|
|
320
|
+
* when it is an accessor, so over a view it would run between the message being looked at and being
|
|
321
|
+
* compressed and replace what this verb compresses, after it had accepted the message it was handed.
|
|
322
|
+
* A substitute the framing check below accepts goes through unseen, so the copy has to come first. */
|
|
323
|
+
/** @internal Viewed, CAPPED, then copied, in that order, and the option is read after all three. The
|
|
324
|
+
* copy has to come before the option for the reason above; the cap has to come before the copy or the
|
|
325
|
+
* copy is an allocation the size of whatever arrived: measured, a 64 MiB message allocated another
|
|
326
|
+
* 64 MiB before the framing limit below refused it. Viewing allocates nothing, so both hold. */
|
|
327
|
+
var viewed = guard.bytes.source(certificateMessage, TlsError, "tls/bad-input", "the certificate message");
|
|
328
|
+
if (guard.bytes.lengthOf(viewed) > C.LIMITS.TLS_CERT_MSG_MAX_BYTES) {
|
|
329
|
+
throw _err("tls/too-large", "the certificate message is " + guard.bytes.lengthOf(viewed) +
|
|
330
|
+
" bytes, over the " + C.LIMITS.TLS_CERT_MSG_MAX_BYTES + "-byte handshake framing limit");
|
|
331
|
+
}
|
|
332
|
+
var view = guard.bytes.snapshot(viewed, TlsError, "tls/bad-input", "the certificate message");
|
|
312
333
|
var sel = opts.algorithm === undefined ? "zlib" : opts.algorithm;
|
|
313
334
|
var name = (typeof sel === "number") ? ALG_BY_NUMBER[sel] : sel;
|
|
314
335
|
if (typeof name !== "string" || SUPPORTED.indexOf(name) === -1) {
|
|
@@ -317,10 +338,6 @@ function compressCertificate(certificateMessage, opts) {
|
|
|
317
338
|
if (view.length < MIN_CERT_MSG_BYTES) {
|
|
318
339
|
throw _err("tls/bad-input", "a Certificate message is at least " + MIN_CERT_MSG_BYTES + " bytes (RFC 8446 sec. 4.4.2)");
|
|
319
340
|
}
|
|
320
|
-
if (view.length > C.LIMITS.TLS_CERT_MSG_MAX_BYTES) {
|
|
321
|
-
throw _err("tls/too-large", "the certificate message is " + view.length + " bytes, over the " +
|
|
322
|
-
C.LIMITS.TLS_CERT_MSG_MAX_BYTES + "-byte handshake framing limit");
|
|
323
|
-
}
|
|
324
341
|
if (opts.level !== undefined && (typeof opts.level !== "number" || !isFinite(opts.level) || Math.floor(opts.level) !== opts.level)) {
|
|
325
342
|
throw _err("tls/bad-input", "opts.level must be an integer");
|
|
326
343
|
}
|
package/lib/tsp-sign.js
CHANGED
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
* @card Create an RFC 3161 timestamp token (a CMS SignedData over a TSTInfo).
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
var nodeCrypto = require("crypto");
|
|
19
18
|
var asn1 = require("./asn1-der");
|
|
20
19
|
var oid = require("./oid");
|
|
21
20
|
var cmsSign = require("./cms-sign");
|
|
@@ -27,6 +26,11 @@ var schemaTsp = require("./schema-tsp");
|
|
|
27
26
|
var guard = require("./guard-all");
|
|
28
27
|
var intrinsic = require("./guard-intrinsic");
|
|
29
28
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
29
|
+
var _compare = intrinsic.compare;
|
|
30
|
+
var _pop = intrinsic.pop;
|
|
31
|
+
var _reverse = intrinsic.reverse;
|
|
32
|
+
var _arrSlice = intrinsic.arraySlice;
|
|
33
|
+
var _bufferFrom = intrinsic.bufferFrom;
|
|
30
34
|
var frameworkError = require("./framework-error");
|
|
31
35
|
|
|
32
36
|
var pkix = require("./schema-pkix");
|
|
@@ -85,7 +89,7 @@ function _certDer(c) {
|
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
function _signingCertV2(certDer, hashName) {
|
|
88
|
-
var certHash =
|
|
92
|
+
var certHash = guard.crypto.digest(NODE_DIGEST[hashName], certDer);
|
|
89
93
|
var essCertId = hashName === "sha256"
|
|
90
94
|
? b.sequence([b.octetString(certHash)])
|
|
91
95
|
: b.sequence([_hashAlgId(hashName), b.octetString(certHash)]);
|
|
@@ -195,7 +199,10 @@ function _sign(messageImprint, tsa, opts) {
|
|
|
195
199
|
var tstInfo = b.sequence(fields);
|
|
196
200
|
|
|
197
201
|
var signCert = { type: "signingCertificateV2", values: [_signingCertV2(certDer, certHashAlg)] };
|
|
198
|
-
|
|
202
|
+
/** @internal Through the capture: assembled by a live `Array.prototype.concat`, the signed attribute
|
|
203
|
+
* set was that method's to choose, and the first entry is the signing-certificate attribute that
|
|
204
|
+
* binds the token to the certificate it names. A replacement could drop it. */
|
|
205
|
+
var extra = intrinsic.concat([signCert], opts.additionalSignedAttributes || []);
|
|
199
206
|
var signer = { cert: certDer, key: tsa && tsa.key, digestAlgorithm: opts.digestAlgorithm, pss: opts.pss };
|
|
200
207
|
return cmsSign.sign(tstInfo, signer, {
|
|
201
208
|
eContentType: "tSTInfo",
|
|
@@ -428,16 +435,28 @@ function parseResponse(input) { return schemaTsp.parse(input); }
|
|
|
428
435
|
var _VERIFY_DIGEST = intrinsic.assign(intrinsic.create(null), { sha256: "sha256", sha384: "sha384", sha512: "sha512", sha1: "sha1" });
|
|
429
436
|
var _VERIFY_DIGEST_LEN = constants.NAMES.DIGEST_OCTETS;
|
|
430
437
|
var _BIND_DIGEST = intrinsic.assign(intrinsic.create(null), { sha256: "sha256", sha384: "sha384", sha512: "sha512" });
|
|
438
|
+
/** @internal The digests admitted for an imprint that are not collision resistant, so a token under one
|
|
439
|
+
* binds nothing an attacker with a collision cannot move. They verify only under `allowWeakDigests`. */
|
|
440
|
+
var _WEAK_IMPRINT_DIGEST = intrinsic.assign(intrinsic.create(null), { sha1: 1 });
|
|
431
441
|
|
|
432
|
-
function _imprintMatches(mi, data) {
|
|
442
|
+
function _imprintMatches(mi, data, allowWeakDigests) {
|
|
433
443
|
var name = mi.hashAlgorithm && mi.hashAlgorithm.name;
|
|
434
444
|
var nodeAlg = (name && intrinsic.hasOwn(_VERIFY_DIGEST, name)) ? _VERIFY_DIGEST[name] : null;
|
|
435
445
|
if (!nodeAlg) return "tsp/unsupported-algorithm";
|
|
446
|
+
/** @internal The imprint is the ONLY thing binding a token to the data it timestamps, so the digest
|
|
447
|
+
* that computes it carries the whole binding and a collision in it substitutes the data under a
|
|
448
|
+
* genuine token. `allowWeakDigests` used to reach only the token's own signature digest, through
|
|
449
|
+
* `cmsVerify.verify`, so a SHA-1 imprint verified under every setting of it including `false`.
|
|
450
|
+
* NO FETCHED CLAUSE FORBIDS SHA-1 HERE: the one quoted algorithm prohibition is EN 319 122-1 clause
|
|
451
|
+
* 6.2.1 on MD5. Refusing it by default is this toolkit's posture, matching what `pki.cms.verify`
|
|
452
|
+
* already does with a signature digest, and the opt-in is the same option an archived token needs. */
|
|
453
|
+
if (!allowWeakDigests && intrinsic.hasOwn(_WEAK_IMPRINT_DIGEST, name)) return "tsp/weak-digest";
|
|
436
454
|
var wantLen = _VERIFY_DIGEST_LEN[name];
|
|
437
455
|
if (mi.hashedMessage.length !== wantLen) return "tsp/imprint-mismatch";
|
|
438
456
|
var actual;
|
|
439
457
|
if (guard.bytes.isByteSource(data)) {
|
|
440
|
-
|
|
458
|
+
var dataBytes = guard.bytes.source(data, TspError, "tsp/bad-input", "data");
|
|
459
|
+
actual = guard.crypto.digest(nodeAlg, dataBytes);
|
|
441
460
|
} else if (data && typeof data === "object" && guard.bytes.isByteSource(data.hashedMessage)) {
|
|
442
461
|
if (data.hashAlgorithm !== name) return "tsp/imprint-mismatch";
|
|
443
462
|
var dhmLen = guard.bytes.source(data.hashedMessage, TspError, "tsp/bad-input", "precomputed imprint hashedMessage").length;
|
|
@@ -446,43 +465,109 @@ function _imprintMatches(mi, data) {
|
|
|
446
465
|
} else {
|
|
447
466
|
throw _err("tsp/bad-input", "pki.tsp.verify data must be a Buffer or a { hashAlgorithm, hashedMessage } imprint");
|
|
448
467
|
}
|
|
449
|
-
return
|
|
468
|
+
return _compare(actual, _bufferFrom(mi.hashedMessage)) === 0 ? true : "tsp/imprint-mismatch";
|
|
450
469
|
}
|
|
451
470
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
471
|
+
/** @internal The certificate one binding attribute names, and the digest under which it names it.
|
|
472
|
+
* An ESSCertID v1 carries NO hashAlgorithm field: it is SHA-1 by definition (RFC 2634 sec. 5.4.1),
|
|
473
|
+
* and EN 319 122-1 clause 5.2.2.2 says the v1 attribute "shall be used if the SHA-1 hash algorithm is
|
|
474
|
+
* used", so these tokens exist. An absent algorithm therefore means SHA-1 rather than nothing, and
|
|
475
|
+
* reporting `tsp/unsupported-algorithm` for one named the wrong fault: the algorithm is known and the
|
|
476
|
+
* binding is deliberately weak, which is a different refusal from one this build cannot compute.
|
|
477
|
+
* ABSENT IS NOT THE SAME AS UNRECOGNIZED: a v1 ESSCertID omits the field, so absent carries the type's
|
|
478
|
+
* default of SHA-1; an ESSCertIDv2 naming an OID this build does not know carries an algorithm that
|
|
479
|
+
* genuinely cannot be computed, and that one is `tsp/unsupported-algorithm`.
|
|
480
|
+
* Returns a refusal code, or the ESSCertID with the digest resolved. SHA-1 resolves rather than
|
|
481
|
+
* refusing, because whether a weak binding is a refusal depends on what the caller is doing with it:
|
|
482
|
+
* the attribute the verdict rests on is held to it, and an attribute read only for agreement is not. */
|
|
483
|
+
function _readBinding(attr) {
|
|
461
484
|
var decoded;
|
|
462
|
-
try { decoded = smime.decodeAttribute(
|
|
485
|
+
try { decoded = smime.decodeAttribute(attr); }
|
|
463
486
|
catch (_e) { return "tsp/bad-signing-certificate"; }
|
|
464
487
|
var essCert = decoded.certs && decoded.certs[0];
|
|
465
488
|
if (!essCert) return "tsp/bad-signing-certificate";
|
|
466
|
-
var
|
|
467
|
-
if (
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
489
|
+
var name = essCert.hashAlgorithm == null ? "sha1" : essCert.hashAlgorithm.name;
|
|
490
|
+
if (name === "sha1") return { essCert: essCert, nodeDigest: "sha1", weak: true };
|
|
491
|
+
if (!name || !intrinsic.hasOwn(_BIND_DIGEST, name)) return "tsp/unsupported-algorithm";
|
|
492
|
+
return { essCert: essCert, nodeDigest: _BIND_DIGEST[name], weak: false };
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/** @internal Whether one ESSCertID names the certificate that signed the token, by every claim it
|
|
496
|
+
* carries: the hash under its own digest, and the issuer and serial where it states them. Returns
|
|
497
|
+
* true or the refusal code. An ESSCertID states the signer in two independent ways and both are
|
|
498
|
+
* compared, because a hash that agrees beside an issuer and serial that name another certificate is
|
|
499
|
+
* the same contradiction as two attributes disagreeing. Certificate identifiers after the first are
|
|
500
|
+
* not read: RFC 2634 sec. 5.4 makes the first the signing certificate and leaves the rest as the
|
|
501
|
+
* chain, so they say nothing about the signer. */
|
|
502
|
+
function _bindingNamesCert(read, tsaCertDer) {
|
|
503
|
+
/** @internal Through the captured digest: this comparison IS the binding between a token and the
|
|
504
|
+
* certificate its signing-certificate attribute names, so what the digest covers is not the
|
|
505
|
+
* caller's to choose. */
|
|
506
|
+
if (_compare(guard.crypto.digest(read.nodeDigest, tsaCertDer),
|
|
507
|
+
_bufferFrom(read.essCert.certHash)) !== 0) return "tsp/cert-binding-mismatch";
|
|
508
|
+
var issuerSerial = read.essCert.issuerSerial;
|
|
509
|
+
if (!issuerSerial) return true;
|
|
510
|
+
var signerCert;
|
|
511
|
+
try { signerCert = pkiX509.parse(tsaCertDer); }
|
|
512
|
+
catch (_e) { return "tsp/bad-signing-certificate"; }
|
|
513
|
+
if (issuerSerial.serialNumber !== signerCert.serialNumber) return "tsp/cert-binding-mismatch";
|
|
514
|
+
var names = issuerSerial.issuer.names || [];
|
|
515
|
+
var gn = null;
|
|
516
|
+
try { if (names.length === 1) gn = schemaTsp.decodeGeneralName(names[0].bytes); }
|
|
517
|
+
catch (_e2) { return "tsp/cert-binding-mismatch"; }
|
|
518
|
+
if (!gn || gn.tagNumber !== 4 || !gn.value || !guard.name.dnEqual(gn.value.rdns, signerCert.issuer.rdns, _err, "tsp/cert-binding-mismatch", "the ESSCertID issuer")) {
|
|
519
|
+
return "tsp/cert-binding-mismatch";
|
|
482
520
|
}
|
|
483
521
|
return true;
|
|
484
522
|
}
|
|
485
523
|
|
|
524
|
+
function _checkCertBinding(signerInfo, tsaCertDer) {
|
|
525
|
+
var attrs = (signerInfo && signerInfo.signedAttrs) || [];
|
|
526
|
+
var binding = [];
|
|
527
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
528
|
+
if (attrs[i].type === O("signingCertificateV2") || attrs[i].type === O("signingCertificate")) {
|
|
529
|
+
intrinsic.push(binding, attrs[i]);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (!binding.length) return "tsp/missing-signing-certificate";
|
|
533
|
+
/** @internal A v2 binding supersedes a v1 one (RFC 5035), and that is why the verdict rests on the v2
|
|
534
|
+
* where there is one. But the v1 attribute does not stop existing: a token carrying both used to be
|
|
535
|
+
* checked against the v2 alone, so one naming the real signer in v2 and a DIFFERENT certificate in v1
|
|
536
|
+
* verified, and a legacy verifier reading only v1 resolves a certificate this one never checked. Both
|
|
537
|
+
* sit in signedAttrs, so the TSA signed the contradiction. One structure making two incompatible
|
|
538
|
+
* claims about one fact is refused rather than resolved in favor of whichever attribute a reader
|
|
539
|
+
* prefers.
|
|
540
|
+
* EVERY BINDING ATTRIBUTE IS COLLECTED, not the first of each type. signedAttrs is a DER SET OF,
|
|
541
|
+
* sorted by encoded value, so which of the two attributes comes first is a function of their lengths
|
|
542
|
+
* and the producer of a hostile token chooses it: a scan that stopped at the v2 caught the
|
|
543
|
+
* contradiction in one ordering and missed it in the other, and an ordering-dependent check is no
|
|
544
|
+
* check. The same applies to a repeated instance of either type, which `pki.cms.verify` refuses
|
|
545
|
+
* before this runs and which is not relied on here.
|
|
546
|
+
* THE SHA-1 ON A SUPERSEDED ATTRIBUTE PINS NOTHING. It detects a disagreement, while the binding the
|
|
547
|
+
* verdict rests on is the v2 one. A collision would let a disagreeing v1 pass for an agreeing one,
|
|
548
|
+
* which costs an attacker the refusal and gains them nothing: the certificate the token is held to is
|
|
549
|
+
* the one v2 names, under its own digest. */
|
|
550
|
+
var chosen = 0;
|
|
551
|
+
for (var j = 0; j < binding.length; j++) {
|
|
552
|
+
if (binding[j].type === O("signingCertificateV2")) { chosen = j; break; }
|
|
553
|
+
}
|
|
554
|
+
for (var k = 0; k < binding.length; k++) {
|
|
555
|
+
if (k === chosen) continue;
|
|
556
|
+
var alt = _readBinding(binding[k]);
|
|
557
|
+
if (typeof alt === "string") return alt;
|
|
558
|
+
var altCode = _bindingNamesCert(alt, tsaCertDer);
|
|
559
|
+
if (altCode !== true) return altCode;
|
|
560
|
+
}
|
|
561
|
+
var read = _readBinding(binding[chosen]);
|
|
562
|
+
if (typeof read === "string") return read;
|
|
563
|
+
/** @internal The refusal of a weak binding stands whatever `allowWeakDigests` says, because a binding
|
|
564
|
+
* is not the archived-signature case the option exists for. Whether it should follow the imprint and
|
|
565
|
+
* become an opt-in is a posture question, and the refusal was pinned deliberately, so it is left as
|
|
566
|
+
* it was. */
|
|
567
|
+
if (read.weak) return "tsp/weak-digest";
|
|
568
|
+
return _bindingNamesCert(read, tsaCertDer);
|
|
569
|
+
}
|
|
570
|
+
|
|
486
571
|
function _checkTsaCertUsage(tsaCertDer) {
|
|
487
572
|
var cert;
|
|
488
573
|
try { cert = pkiX509.parse(tsaCertDer); }
|
|
@@ -540,12 +625,12 @@ function _buildTsaChains(leaf, pool) {
|
|
|
540
625
|
if (guard.name.dnEqual(pool[i].subject.rdns, current.issuer.rdns, _err, "tsp/bad-tsa-certificate", "the TSA certificate subject")) {
|
|
541
626
|
used[i] = true; guard.list.append(acc, pool[i]);
|
|
542
627
|
dfs(pool[i], acc, used);
|
|
543
|
-
acc
|
|
628
|
+
_pop(acc); used[i] = false;
|
|
544
629
|
}
|
|
545
630
|
}
|
|
546
631
|
}
|
|
547
632
|
dfs(leaf, [leaf], []);
|
|
548
|
-
return chains.map(function (c) { return
|
|
633
|
+
return chains.map(function (c) { return _reverse(_arrSlice(c)); }).sort(function (a, b) { return b.length - a.length; });
|
|
549
634
|
}
|
|
550
635
|
|
|
551
636
|
/**
|
|
@@ -643,7 +728,7 @@ async function verify(token, data, opts) {
|
|
|
643
728
|
if (!cmsRes.valid) return fail("tsp/bad-signature", signer && (signer.code || signer.message));
|
|
644
729
|
var tsaCertDer = signer && signer.cert;
|
|
645
730
|
if (!tsaCertDer) return fail("tsp/bad-signature", "the TSA signer certificate was not found");
|
|
646
|
-
var mi = _imprintMatches(tst.messageImprint, data);
|
|
731
|
+
var mi = _imprintMatches(tst.messageImprint, data, opts.allowWeakDigests === true);
|
|
647
732
|
if (mi !== true) return fail(mi);
|
|
648
733
|
if (opts.nonce != null) {
|
|
649
734
|
var wantNonce = guard.range.authoredInteger(opts.nonce, _err, "tsp/bad-input", "opts.nonce");
|