@blamejs/pki 0.6.38 → 0.6.40
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/lib/attrcert-sign.js +2 -2
- package/lib/cmc-verify.js +10 -4
- package/lib/cmp-session.js +11 -12
- package/lib/cmp-verify.js +4 -8
- package/lib/cms-verify.js +45 -35
- package/lib/composite-sig.js +1 -1
- package/lib/crl-sign.js +8 -8
- package/lib/crmf-sign.js +6 -3
- package/lib/csr-sign.js +2 -2
- package/lib/ct.js +14 -14
- package/lib/guard-all.js +2 -0
- package/lib/guard-bytes.js +1 -1
- package/lib/guard-identifier.js +1 -1
- package/lib/guard-json.js +1 -1
- package/lib/guard-list.js +16 -0
- package/lib/guard-parsed.js +1 -1
- package/lib/guard-verdict.js +47 -0
- package/lib/ip-utils.js +1 -1
- package/lib/ocsp-verify.js +20 -6
- package/lib/ocsp.js +21 -12
- package/lib/path-validate.js +105 -103
- package/lib/pkcs12-build.js +11 -9
- package/lib/sigstore.js +16 -7
- package/lib/smime.js +6 -6
- package/lib/tsp-sign.js +19 -19
- package/lib/webauthn.js +46 -42
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/pkcs12-build.js
CHANGED
|
@@ -556,12 +556,12 @@ async function verifyMac(pfx, password, opts) {
|
|
|
556
556
|
var m = _storeFromBytes(pfx);
|
|
557
557
|
var valid = await _verifyMacOfStore(m, password, opts);
|
|
558
558
|
var isHmac = m.mac.kind === "hmac";
|
|
559
|
-
return {
|
|
559
|
+
return guard.verdict.of({
|
|
560
560
|
valid: valid,
|
|
561
561
|
macAlgorithm: m.mac.kind,
|
|
562
562
|
macAlgorithmName: isHmac ? _toLower(m.mac.hashName || "") : m.mac.pbmac1.schemeName,
|
|
563
563
|
iterationCount: isHmac ? m.mac.iterations : m.mac.pbmac1.kdf.iterationCount,
|
|
564
|
-
};
|
|
564
|
+
});
|
|
565
565
|
}
|
|
566
566
|
|
|
567
567
|
async function _verifyMacOfStore(m, password, opts) {
|
|
@@ -669,13 +669,15 @@ async function open(pfx, password, opts) {
|
|
|
669
669
|
} else if (!opts.allowUnauthenticated) {
|
|
670
670
|
throw _err("pkcs12/no-integrity", "the store carries no integrity MAC (integrityMode " + m.integrityMode + "); set opts.allowUnauthenticated to open it anyway");
|
|
671
671
|
}
|
|
672
|
-
var out = { valid: macVerified || m.integrityMode === "public-key", integrityMode: m.integrityMode, macVerified: macVerified, signers: signers, keys: [], certs: [], crls: [], secrets: [] };
|
|
672
|
+
var out = guard.verdict.of({ valid: macVerified || m.integrityMode === "public-key", integrityMode: m.integrityMode, macVerified: macVerified, signers: signers, keys: [], certs: [], crls: [], secrets: [] });
|
|
673
673
|
var i;
|
|
674
674
|
var kdfBudget = { rounds: KDF_MAX_ROUNDS };
|
|
675
675
|
for (i = 0; i < m.safeBags.length; i++) _openBag(m.safeBags[i], password, opts, out, 0, kdfBudget);
|
|
676
676
|
for (i = 0; i < m.encryptedSafes.length; i++) await _openEncryptedSafe(m.encryptedSafes[i], password, opts, out, 0, kdfBudget);
|
|
677
677
|
if (opts.keys === "crypto") {
|
|
678
|
-
for (i = 0; i < out.keys.length; i++)
|
|
678
|
+
for (i = 0; i < out.keys.length; i++) {
|
|
679
|
+
guard.verdict.set(out.keys[i], "key", await key.import(out.keys[i].pkcs8, { algorithm: opts.importAlgorithm }));
|
|
680
|
+
}
|
|
679
681
|
}
|
|
680
682
|
return out;
|
|
681
683
|
}
|
|
@@ -685,19 +687,19 @@ function _openBag(bag, password, opts, out, depth, budget) {
|
|
|
685
687
|
case "keyBag":
|
|
686
688
|
try { pkcs8.parse(bag.keyDer); }
|
|
687
689
|
catch (e) { throw _err("pkcs12/bad-der", "a plaintext keyBag PrivateKeyInfo is not canonical DER", e); }
|
|
688
|
-
out.keys
|
|
690
|
+
guard.list.append(out.keys, { pkcs8: bag.keyDer, encrypted: false, friendlyName: bag.friendlyName, localKeyId: bag.localKeyId });
|
|
689
691
|
break;
|
|
690
692
|
case "pkcs8ShroudedKeyBag":
|
|
691
|
-
out.keys
|
|
693
|
+
guard.list.append(out.keys, { pkcs8: _decryptShroudedKey(bag.encrypted, password, opts, budget), encrypted: true, friendlyName: bag.friendlyName, localKeyId: bag.localKeyId });
|
|
692
694
|
break;
|
|
693
695
|
case "certBag":
|
|
694
|
-
out.certs
|
|
696
|
+
guard.list.append(out.certs, { cert: bag.certValue, certType: bag.certType, friendlyName: bag.friendlyName, localKeyId: bag.localKeyId });
|
|
695
697
|
break;
|
|
696
698
|
case "crlBag":
|
|
697
|
-
out.crls
|
|
699
|
+
guard.list.append(out.crls, { crl: bag.crlValue, crlType: bag.crlType, friendlyName: bag.friendlyName, localKeyId: bag.localKeyId });
|
|
698
700
|
break;
|
|
699
701
|
case "secretBag":
|
|
700
|
-
out.secrets
|
|
702
|
+
guard.list.append(out.secrets, { secretTypeId: bag.secretTypeId, secretTypeName: bag.secretTypeName, secretValue: bag.secretValue, friendlyName: bag.friendlyName, localKeyId: bag.localKeyId });
|
|
701
703
|
break;
|
|
702
704
|
case "safeContentsBag":
|
|
703
705
|
if (depth + 1 > C.LIMITS.PKCS12_MAX_BAG_DEPTH) throw _err("pkcs12/too-deep", "safeContentsBag nesting exceeds the depth cap " + C.LIMITS.PKCS12_MAX_BAG_DEPTH);
|
package/lib/sigstore.js
CHANGED
|
@@ -360,10 +360,19 @@ function _normRoots(fulcioRoots) {
|
|
|
360
360
|
|
|
361
361
|
function _pathToAnchor(leaf, anchor, intBySubject) {
|
|
362
362
|
var anchorSubj = _dn(anchor, "subject");
|
|
363
|
-
|
|
363
|
+
/** @internal Issuers are collected leaf-first and the path is assembled once, so no element is
|
|
364
|
+
* ever moved to a new index: an indexed setter has nothing to intercept and cannot replace the
|
|
365
|
+
* leaf the preceding signature checks authenticated. */
|
|
366
|
+
var above = [], cur = leaf, seen = {}, guardN = 0;
|
|
367
|
+
function assemble() {
|
|
368
|
+
var path = [];
|
|
369
|
+
for (var k = above.length - 1; k >= 0; k--) guard.list.append(path, above[k]);
|
|
370
|
+
guard.list.append(path, leaf);
|
|
371
|
+
return path;
|
|
372
|
+
}
|
|
364
373
|
while (guardN++ < 16) {
|
|
365
374
|
var issuerDn = _dn(cur, "issuer");
|
|
366
|
-
if (issuerDn === anchorSubj) return
|
|
375
|
+
if (issuerDn === anchorSubj) return assemble();
|
|
367
376
|
var cands = intBySubject[issuerDn] || [];
|
|
368
377
|
var next = null;
|
|
369
378
|
for (var j = 0; j < cands.length; j++) {
|
|
@@ -372,7 +381,7 @@ function _pathToAnchor(leaf, anchor, intBySubject) {
|
|
|
372
381
|
}
|
|
373
382
|
if (!next) return null;
|
|
374
383
|
seen[_dn(next, "subject")] = 1;
|
|
375
|
-
|
|
384
|
+
guard.list.append(above, next);
|
|
376
385
|
cur = next;
|
|
377
386
|
}
|
|
378
387
|
return null;
|
|
@@ -382,7 +391,7 @@ async function _verifyChain(leaf, chainDers, fulcioRoots, timeMs) {
|
|
|
382
391
|
var roots = _normRoots(fulcioRoots);
|
|
383
392
|
var bundleLinks = chainDers.slice(1).map(function (d, i) { return _parseCert(d, "bundle intermediate [" + i + "]"); });
|
|
384
393
|
var intBySubject = {};
|
|
385
|
-
function addInt(c) { (intBySubject[_dn(c, "subject")] = intBySubject[_dn(c, "subject")] || []
|
|
394
|
+
function addInt(c) { guard.list.append(intBySubject[_dn(c, "subject")] = intBySubject[_dn(c, "subject")] || [], c); }
|
|
386
395
|
roots.forEach(function (r) { addInt(r.cert); });
|
|
387
396
|
bundleLinks.forEach(addInt);
|
|
388
397
|
var candidates = roots.filter(function (r) { return _inWindow(timeMs, r.validFor); });
|
|
@@ -446,7 +455,7 @@ function _sanValue(ext) {
|
|
|
446
455
|
}
|
|
447
456
|
val = { type: "otherName", oid: asn1.read.oid(n.children[0]), value: v };
|
|
448
457
|
}
|
|
449
|
-
if (val)
|
|
458
|
+
if (val) guard.list.append(names, val);
|
|
450
459
|
}
|
|
451
460
|
if (names.length > 1) throw _err("sigstore/bad-certificate", "the Fulcio certificate carries multiple SAN identities");
|
|
452
461
|
return names[0] || null;
|
|
@@ -582,7 +591,7 @@ async function verifyBundle(bundle, opts) {
|
|
|
582
591
|
|
|
583
592
|
var st = _statement(payload, env.payloadType, opts.predicateType);
|
|
584
593
|
|
|
585
|
-
return {
|
|
594
|
+
return guard.verdict.of({
|
|
586
595
|
valid: true,
|
|
587
596
|
verified: true,
|
|
588
597
|
payload: payload,
|
|
@@ -596,7 +605,7 @@ async function verifyBundle(bundle, opts) {
|
|
|
596
605
|
integratedTime: integratedTime,
|
|
597
606
|
logIndex: rekor.logIndex,
|
|
598
607
|
logId: rekor.logId,
|
|
599
|
-
};
|
|
608
|
+
});
|
|
600
609
|
}
|
|
601
610
|
|
|
602
611
|
module.exports = {
|
package/lib/smime.js
CHANGED
|
@@ -334,7 +334,7 @@ function _signerEmails(certDer) {
|
|
|
334
334
|
catch (sanFailed) { out.unreadable++; out.reason = sanFailed.code || "bad-san"; }
|
|
335
335
|
if (dec === null) return;
|
|
336
336
|
(dec.names || []).forEach(function (n) {
|
|
337
|
-
if (n.tagNumber === 1) { out.addresses
|
|
337
|
+
if (n.tagNumber === 1) { guard.list.append(out.addresses, String(n.value)); return; }
|
|
338
338
|
if (n.tagNumber !== 0) return;
|
|
339
339
|
var typeId = n.value && typeof n.value === "object" ? n.value.typeId : undefined;
|
|
340
340
|
if (typeId === undefined) { out.unreadable++; return; }
|
|
@@ -346,7 +346,7 @@ function _signerEmails(certDer) {
|
|
|
346
346
|
(rdns || []).forEach(function (rdn) {
|
|
347
347
|
(rdn || []).forEach(function (attr) {
|
|
348
348
|
if (!attr || attr.type !== EMAIL_ADDRESS_ATTR || typeof attr.value !== "string") return;
|
|
349
|
-
if (out.addresses.indexOf(attr.value) === -1) out.addresses
|
|
349
|
+
if (out.addresses.indexOf(attr.value) === -1) guard.list.append(out.addresses, attr.value);
|
|
350
350
|
});
|
|
351
351
|
});
|
|
352
352
|
return out;
|
|
@@ -362,7 +362,7 @@ function _senderSurface(signers, outerEnt, expectedSender) {
|
|
|
362
362
|
}
|
|
363
363
|
else {
|
|
364
364
|
var froms = [];
|
|
365
|
-
outerEnt.headers.forEach(function (h) { if (h.lname === "from")
|
|
365
|
+
outerEnt.headers.forEach(function (h) { if (h.lname === "from") guard.list.append(froms, h.value.trim()); });
|
|
366
366
|
if (froms.length === 1) { expected = _addrSpec(froms[0]); source = expected === null ? null : "from"; }
|
|
367
367
|
}
|
|
368
368
|
var identities = [], undecidable = false, sawIdentity = false;
|
|
@@ -371,7 +371,7 @@ function _senderSurface(signers, outerEnt, expectedSender) {
|
|
|
371
371
|
var e = s.cert ? _signerEmails(s.cert) : null;
|
|
372
372
|
if (e === null) { undecidable = true; return; }
|
|
373
373
|
if (e.unreadable > 0) { undecidable = true; sawIdentity = true; }
|
|
374
|
-
e.addresses.forEach(function (a) { sawIdentity = true; if (identities.indexOf(a) === -1)
|
|
374
|
+
e.addresses.forEach(function (a) { sawIdentity = true; if (identities.indexOf(a) === -1) guard.list.append(identities, a); });
|
|
375
375
|
});
|
|
376
376
|
if (expected === null) return { checked: false, expected: null, source: null, identities: identities, match: null };
|
|
377
377
|
if (!sawIdentity) return { checked: true, expected: expected, source: source, identities: identities, match: null };
|
|
@@ -630,7 +630,7 @@ async function verify(message, opts) {
|
|
|
630
630
|
var inner;
|
|
631
631
|
try { inner = _toBuf(schemaCms.parse(p7m).encapContentInfo.eContent); }
|
|
632
632
|
catch (e) { throw _err("smime/bad-mime", "the pkcs7-mime SignedData has no encapsulated content", e); }
|
|
633
|
-
return
|
|
633
|
+
return guard.verdict.of({ valid: res.valid, trusted: res.trusted, signers: res.signers, form: "pkcs7-mime", content: inner, micalg: null, sender: _senderSurface(res.signers, ent, opts.expectedSender) }, _hpSurface(inner, ent, "clear", res.valid, opts.legacyHeaderProtection === true));
|
|
634
634
|
}
|
|
635
635
|
if (ct.type === "multipart/signed") {
|
|
636
636
|
if (ct.params.protocol && !_isPkcs7(ct.params.protocol, "signature")) throw _err("smime/bad-multipart", "multipart/signed protocol must be application/pkcs7-signature");
|
|
@@ -651,7 +651,7 @@ async function verify(message, opts) {
|
|
|
651
651
|
if (opts.strictMicalg && micalg && _micalgSet(micalg) !== (_micalgOf(p7s) || "")) {
|
|
652
652
|
throw _err("smime/micalg-mismatch", "the multipart/signed micalg " + JSON.stringify(micalg) + " disagrees with the SignerInfo digests");
|
|
653
653
|
}
|
|
654
|
-
return
|
|
654
|
+
return guard.verdict.of({ valid: res2.valid, trusted: res2.trusted, signers: res2.signers, form: "multipart/signed", content: parts[0], micalg: micalg, sender: _senderSurface(res2.signers, ent, opts.expectedSender) }, _hpSurface(parts[0], ent, "clear", res2.valid, opts.legacyHeaderProtection === true));
|
|
655
655
|
}
|
|
656
656
|
throw _err("smime/unsupported-type", "not a signed S/MIME message (Content-Type " + JSON.stringify(ct.type) + ")");
|
|
657
657
|
}
|
package/lib/tsp-sign.js
CHANGED
|
@@ -171,9 +171,9 @@ function _sign(messageImprint, tsa, opts) {
|
|
|
171
171
|
var genTime = guard.time.isDate(opts.genTime) ? opts.genTime : new Date();
|
|
172
172
|
var serial = guard.range.authoredInteger(opts.serialNumber, _err, "tsp/bad-input", "serialNumber");
|
|
173
173
|
var fields = [b.integer(1n), _policy(opts.policy), imprint, b.integer(serial), b.generalizedTime(genTime)];
|
|
174
|
-
if (opts.accuracy)
|
|
175
|
-
if (opts.ordering === true)
|
|
176
|
-
if (opts.nonce != null)
|
|
174
|
+
if (opts.accuracy) guard.list.append(fields,_accuracy(opts.accuracy));
|
|
175
|
+
if (opts.ordering === true) guard.list.append(fields,b.boolean(true));
|
|
176
|
+
if (opts.nonce != null) guard.list.append(fields,b.integer(guard.range.authoredInteger(opts.nonce, _err, "tsp/bad-input", "nonce")));
|
|
177
177
|
var tstInfo = b.sequence(fields);
|
|
178
178
|
|
|
179
179
|
var signCert = { type: "signingCertificateV2", values: [_signingCertV2(certDer, certHashAlg)] };
|
|
@@ -199,10 +199,10 @@ function _accuracy(a) {
|
|
|
199
199
|
if (typeof sec !== "number" && typeof sec !== "bigint") throw _err("tsp/bad-input", "Accuracy seconds must be a non-negative integer, got " + guard.text.showValue(sec));
|
|
200
200
|
var s = intrinsic.Number(sec);
|
|
201
201
|
if (!Number.isInteger(s) || s < 0 || s > 0x7fffffff) throw _err("tsp/bad-input", "Accuracy seconds must be a non-negative integer");
|
|
202
|
-
|
|
202
|
+
guard.list.append(f,b.integer(BigInt(s)));
|
|
203
203
|
}
|
|
204
|
-
if (mil != null)
|
|
205
|
-
if (mic != null)
|
|
204
|
+
if (mil != null) guard.list.append(f,b.contextPrimitive(0, _subMilliBytes(mil, "millis")));
|
|
205
|
+
if (mic != null) guard.list.append(f,b.contextPrimitive(1, _subMilliBytes(mic, "micros")));
|
|
206
206
|
return b.sequence(f);
|
|
207
207
|
}
|
|
208
208
|
function _subMilliBytes(n, label) {
|
|
@@ -276,10 +276,10 @@ function request(messageImprint, opts) {
|
|
|
276
276
|
_assertImprint(mi);
|
|
277
277
|
var imprint = b.sequence([_hashAlgId(mi.hashAlgorithm), b.octetString(guard.bytes.source(mi.hashedMessage, TspError, "tsp/bad-input", "messageImprint.hashedMessage"))]);
|
|
278
278
|
var fields = [b.integer(1n), imprint];
|
|
279
|
-
if (opts.reqPolicy != null)
|
|
280
|
-
if (opts.nonce != null)
|
|
279
|
+
if (opts.reqPolicy != null) guard.list.append(fields,_policy(opts.reqPolicy));
|
|
280
|
+
if (opts.nonce != null) guard.list.append(fields,b.integer(guard.range.authoredInteger(opts.nonce, _err, "tsp/bad-input", "nonce")));
|
|
281
281
|
if (opts.certReq != null && typeof opts.certReq !== "boolean") throw _err("tsp/bad-input", "certReq must be a boolean");
|
|
282
|
-
if (opts.certReq === true)
|
|
282
|
+
if (opts.certReq === true) guard.list.append(fields,b.boolean(true));
|
|
283
283
|
if (opts.extensions != null) {
|
|
284
284
|
if (!Array.isArray(opts.extensions) || !guard.list.allMatch(opts.extensions, function (e) { return guard.bytes.isByteSource(e); })) throw _err("tsp/bad-input", "extensions must be an array of encoded Extension DER buffers");
|
|
285
285
|
var seenExt = {};
|
|
@@ -291,7 +291,7 @@ function request(messageImprint, opts) {
|
|
|
291
291
|
seenExt[extnId] = true;
|
|
292
292
|
return der;
|
|
293
293
|
});
|
|
294
|
-
if (encoded.length)
|
|
294
|
+
if (encoded.length) guard.list.append(fields,b.contextConstructed(0, Buffer.concat(encoded)));
|
|
295
295
|
}
|
|
296
296
|
var der = b.sequence(fields);
|
|
297
297
|
return opts.pem ? schemaTsp.pemEncode(der, "TIMESTAMP REQUEST") : der;
|
|
@@ -364,14 +364,14 @@ function response(token, opts) {
|
|
|
364
364
|
if (opts.statusString != null) {
|
|
365
365
|
var texts = Array.isArray(opts.statusString) ? opts.statusString : [opts.statusString];
|
|
366
366
|
if (!texts.length) throw _err("tsp/bad-input", "statusString must carry at least one PKIFreeText element (RFC 4210 SEQUENCE SIZE 1..MAX)");
|
|
367
|
-
|
|
367
|
+
guard.list.append(siFields,b.sequence(texts.map(function (t) { return b.utf8(String(t)); })));
|
|
368
368
|
}
|
|
369
369
|
if (opts.failInfo != null) {
|
|
370
370
|
if (status !== 2) throw _err("tsp/unexpected-failinfo", "failInfo is present only when the status is rejection(2) (RFC 3161 sec. 2.4.2)");
|
|
371
|
-
|
|
371
|
+
guard.list.append(siFields,_failInfoBits(opts.failInfo));
|
|
372
372
|
}
|
|
373
373
|
var respFields = [b.sequence(siFields)];
|
|
374
|
-
if (token != null)
|
|
374
|
+
if (token != null) guard.list.append(respFields, b.raw(_tokenDer(token)));
|
|
375
375
|
var der = b.sequence(respFields);
|
|
376
376
|
return opts.pem ? schemaTsp.pemEncode(der, "TIMESTAMP RESPONSE") : der;
|
|
377
377
|
}
|
|
@@ -508,12 +508,12 @@ function _buildTsaChains(leaf, pool) {
|
|
|
508
508
|
var chains = [];
|
|
509
509
|
function dfs(current, acc, used) {
|
|
510
510
|
if (chains.length >= MAX_TSA_CHAINS) return;
|
|
511
|
-
|
|
511
|
+
guard.list.append(chains, acc.slice());
|
|
512
512
|
for (var i = 0; i < pool.length && chains.length < MAX_TSA_CHAINS; i++) {
|
|
513
513
|
if (used[i]) continue;
|
|
514
514
|
if (pool[i].serialNumberHex === current.serialNumberHex && guard.name.dnEqual(pool[i].issuer.rdns, current.issuer.rdns, _err, "tsp/bad-tsa-certificate", "the TSA certificate issuer")) continue;
|
|
515
515
|
if (guard.name.dnEqual(pool[i].subject.rdns, current.issuer.rdns, _err, "tsp/bad-tsa-certificate", "the TSA certificate subject")) {
|
|
516
|
-
used[i] = true;
|
|
516
|
+
used[i] = true; guard.list.append(acc, pool[i]);
|
|
517
517
|
dfs(pool[i], acc, used);
|
|
518
518
|
acc.pop(); used[i] = false;
|
|
519
519
|
}
|
|
@@ -605,7 +605,7 @@ async function verify(token, data, opts) {
|
|
|
605
605
|
var revocationChecked = false;
|
|
606
606
|
var anchorConstraints = null;
|
|
607
607
|
function fail(code, reason) {
|
|
608
|
-
return { valid: false, trusted: false, revocationChecked: revocationChecked, anchorConstraints: anchorConstraints, code: code, reason: reason || null, genTime: tst.genTime, accuracy: tst.accuracy, serialNumber: tst.serialNumber, serialNumberHex: tst.serialNumberHex, policy: tst.policy, policyName: tst.policyName, nonce: tst.nonce, tsa: tst.tsa, tstInfo: tst, signer: null };
|
|
608
|
+
return guard.verdict.of({ valid: false, trusted: false, revocationChecked: revocationChecked, anchorConstraints: anchorConstraints, code: code, reason: reason || null, genTime: tst.genTime, accuracy: tst.accuracy, serialNumber: tst.serialNumber, serialNumberHex: tst.serialNumberHex, policy: tst.policy, policyName: tst.policyName, nonce: tst.nonce, tsa: tst.tsa, tstInfo: tst, signer: null });
|
|
609
609
|
}
|
|
610
610
|
var cmsRes = await cmsVerify.verify(tokenDer, { certs: opts.certs });
|
|
611
611
|
var signer = cmsRes.signers[0];
|
|
@@ -642,7 +642,7 @@ async function verify(token, data, opts) {
|
|
|
642
642
|
var ceilT = (tst.genTimeFraction != null && _hasNonZeroDigit(tst.genTimeFraction.slice(3))) ? new Date(guard.time.instantOf(floorT) + 1) : floorT;
|
|
643
643
|
try {
|
|
644
644
|
var pool = (parsed.certificates || []).filter(function (c) { return c.tagClass === "universal"; }).map(function (c) { return pkiX509.parse(c.bytes); });
|
|
645
|
-
(opts.certs || []).forEach(function (c) {
|
|
645
|
+
(opts.certs || []).forEach(function (c) { guard.list.append(pool, pkiX509.parse(c)); });
|
|
646
646
|
async function validateAt(chain, when) {
|
|
647
647
|
var res = await pathValidate.validate(chain, {
|
|
648
648
|
time: when, trustAnchors: opts.trustAnchors, requiredEku: ["timeStamping"], checkPurpose: "timeStamping", revocationChecker: opts.revocationChecker,
|
|
@@ -662,13 +662,13 @@ async function verify(token, data, opts) {
|
|
|
662
662
|
anchorConstraints = pathRes.anchorConstraints;
|
|
663
663
|
trusted = true;
|
|
664
664
|
}
|
|
665
|
-
return {
|
|
665
|
+
return guard.verdict.of({
|
|
666
666
|
valid: true, trusted: trusted, revocationChecked: revocationChecked, anchorConstraints: anchorConstraints,
|
|
667
667
|
genTime: tst.genTime, accuracy: tst.accuracy,
|
|
668
668
|
serialNumber: tst.serialNumber, serialNumberHex: tst.serialNumberHex,
|
|
669
669
|
policy: tst.policy, policyName: tst.policyName, nonce: tst.nonce, tsa: tst.tsa,
|
|
670
670
|
tstInfo: tst, signer: { cert: tsaCertDer, sid: signer.sid },
|
|
671
|
-
};
|
|
671
|
+
});
|
|
672
672
|
}
|
|
673
673
|
|
|
674
674
|
module.exports = { sign: sign, request: request, parseRequest: parseRequest, response: response, parseResponse: parseResponse, verify: verify };
|
package/lib/webauthn.js
CHANGED
|
@@ -395,8 +395,8 @@ function _applyCallerRoots(res, supplied, vopts, onlyPaths) {
|
|
|
395
395
|
.map(function (el) { return { tp: el.trustPath, at: el.chainValidatedAt }; })
|
|
396
396
|
: (res.trustPath && res.trustPath.length ? [{ tp: res.trustPath, at: res.chainValidatedAt }] : []));
|
|
397
397
|
if (!onlyPaths) {
|
|
398
|
-
res
|
|
399
|
-
anchored: paths.length };
|
|
398
|
+
res = guard.verdict.of(res, { anchoredElements: { total: (res.fmt === "compound" && Array.isArray(res.compound)) ? res.compound.length : 1,
|
|
399
|
+
anchored: paths.length } });
|
|
400
400
|
}
|
|
401
401
|
if (!paths.length) {
|
|
402
402
|
throw _err("webauthn/anchor-not-applicable",
|
|
@@ -410,8 +410,7 @@ function _applyCallerRoots(res, supplied, vopts, onlyPaths) {
|
|
|
410
410
|
"attestation trust path (against the roots supplied as opts.rootCertificates)");
|
|
411
411
|
});
|
|
412
412
|
}, Promise.resolve()).then(function () {
|
|
413
|
-
res
|
|
414
|
-
return res;
|
|
413
|
+
return guard.verdict.of(res, { anchoredTo: _anchoredRoutes(res, "rootCertificates") });
|
|
415
414
|
});
|
|
416
415
|
}
|
|
417
416
|
|
|
@@ -425,7 +424,7 @@ function _safetyNetAnchored(res) {
|
|
|
425
424
|
|
|
426
425
|
function _anchoredRoutes(res, base) {
|
|
427
426
|
var routes = base ? base.split("+") : [];
|
|
428
|
-
if (_safetyNetAnchored(res) && routes.indexOf("safetyNetRoots") < 0)
|
|
427
|
+
if (_safetyNetAnchored(res) && routes.indexOf("safetyNetRoots") < 0) guard.list.append(routes, "safetyNetRoots");
|
|
429
428
|
return routes.length ? routes.join("+") : null;
|
|
430
429
|
}
|
|
431
430
|
|
|
@@ -604,9 +603,8 @@ var VERIFIERS = {
|
|
|
604
603
|
_checkAikCert(aik);
|
|
605
604
|
_checkAaguidExt(aik, att.authData.aaguid);
|
|
606
605
|
validator.tpm.assertObjectAttributePolicy(pub, tpmPolicy, WebauthnError, "webauthn/tpm-policy", "webauthn/bad-tpm");
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
return tpmRes;
|
|
606
|
+
return guard.verdict.of(_result("tpm", "AttCA", chain, att),
|
|
607
|
+
{ tpm: { objectAttributes: pub.objectAttributes, attributes: pub.attributes, authPolicy: pub.authPolicy } });
|
|
610
608
|
});
|
|
611
609
|
},
|
|
612
610
|
|
|
@@ -680,9 +678,8 @@ var VERIFIERS = {
|
|
|
680
678
|
? new Date(payload.timestampMs) : undefined);
|
|
681
679
|
return _safetyNetChainTrusted(chain, roots, chainAt);
|
|
682
680
|
}).then(function () {
|
|
683
|
-
var res = _result("android-safetynet", "Basic", chain, att);
|
|
684
|
-
res
|
|
685
|
-
if (chainAt !== undefined) res.chainValidatedAt = chainAt;
|
|
681
|
+
var res = guard.verdict.of(_result("android-safetynet", "Basic", chain, att), { safetyNet: signals });
|
|
682
|
+
if (chainAt !== undefined) res = guard.verdict.of(res, { chainValidatedAt: chainAt });
|
|
686
683
|
return res;
|
|
687
684
|
});
|
|
688
685
|
},
|
|
@@ -710,14 +707,12 @@ var VERIFIERS = {
|
|
|
710
707
|
return elements.reduce(function (p, e) {
|
|
711
708
|
return p.then(function () {
|
|
712
709
|
return Promise.resolve().then(function () { return VERIFIERS[e.fmt](e.att, clientDataHash, opts); })
|
|
713
|
-
.then(function (r) {
|
|
710
|
+
.then(function (r) { guard.list.append(out, r); }, function (err) {
|
|
714
711
|
throw _err("webauthn/compound-element-failed", "compound element " + e.index + " (format '" + e.fmt + "') did not verify", err);
|
|
715
712
|
});
|
|
716
713
|
});
|
|
717
714
|
}, Promise.resolve()).then(function () {
|
|
718
|
-
|
|
719
|
-
res.compound = out;
|
|
720
|
-
return res;
|
|
715
|
+
return guard.verdict.of(_result("compound", "Compound", [], att), { compound: out });
|
|
721
716
|
});
|
|
722
717
|
},
|
|
723
718
|
|
|
@@ -761,7 +756,7 @@ function _safetyNetChainTrusted(chain, roots, time) {
|
|
|
761
756
|
}
|
|
762
757
|
|
|
763
758
|
function _result(fmt, attestationType, chain, att) {
|
|
764
|
-
return {
|
|
759
|
+
return guard.verdict.of({
|
|
765
760
|
valid: true, attestationVerified: true, fmt: fmt, attestationType: attestationType,
|
|
766
761
|
trustPath: (chain || []).slice().reverse(),
|
|
767
762
|
aaguid: att.authData.aaguid,
|
|
@@ -772,7 +767,7 @@ function _result(fmt, attestationType, chain, att) {
|
|
|
772
767
|
flags: att.authData.flags,
|
|
773
768
|
rpIdHash: att.authData.rpIdHash,
|
|
774
769
|
extensions: att.authData.extensions,
|
|
775
|
-
};
|
|
770
|
+
});
|
|
776
771
|
}
|
|
777
772
|
|
|
778
773
|
function _appleNonce(extValue) {
|
|
@@ -969,37 +964,41 @@ function verify(attestationObject, clientDataHash, opts) {
|
|
|
969
964
|
} catch (e) { return Promise.reject(e); }
|
|
970
965
|
return Promise.resolve().then(function () { return verifier(att, clientDataHash, vopts); })
|
|
971
966
|
.then(function (res) {
|
|
972
|
-
res
|
|
973
|
-
return res;
|
|
967
|
+
return guard.verdict.of(res, { clientData: clientData });
|
|
974
968
|
})
|
|
975
969
|
.then(function (res) {
|
|
976
970
|
if (vopts.metadata === undefined) {
|
|
977
971
|
if (vopts.rootCertificates !== undefined) return _applyCallerRoots(res, vopts.rootCertificates, vopts);
|
|
978
|
-
res
|
|
979
|
-
var sn = _safetyNetAnchored(
|
|
972
|
+
var anchored = guard.verdict.of(res, { anchoredTo: _anchoredRoutes(res, null) });
|
|
973
|
+
var sn = _safetyNetAnchored(anchored);
|
|
980
974
|
if (sn) {
|
|
981
|
-
|
|
982
|
-
anchored:
|
|
975
|
+
anchored = guard.verdict.of(anchored, { anchoredElements: {
|
|
976
|
+
total: (anchored.fmt === "compound" && Array.isArray(anchored.compound)) ? anchored.compound.length : 1,
|
|
977
|
+
anchored: sn } });
|
|
983
978
|
}
|
|
984
|
-
return
|
|
979
|
+
return anchored;
|
|
985
980
|
}
|
|
986
981
|
return Promise.resolve().then(function () { return _applyMetadata(res, att, vopts); })
|
|
987
|
-
.then(function (out) { out
|
|
982
|
+
.then(function (out) { return guard.verdict.of(out, { anchoredTo: _anchoredRoutes(out, "metadata") }); }, function (e) {
|
|
988
983
|
if (!e || e.code !== "webauthn/metadata-not-found" || vopts.rootCertificates === undefined) throw e;
|
|
989
984
|
return Promise.resolve(_applyCallerRoots(res, vopts.rootCertificates, vopts, e.missedPaths))
|
|
990
|
-
.then(function (
|
|
985
|
+
.then(function (withRoots) {
|
|
986
|
+
/** @internal The element coverage the metadata attempt measured is the whole
|
|
987
|
+
* statement's, so it travels on the error rather than being recomputed from the
|
|
988
|
+
* fallback's own paths. */
|
|
989
|
+
var out = e.anchoredElements ? guard.verdict.of(withRoots, { anchoredElements: e.anchoredElements }) : withRoots;
|
|
991
990
|
var applied = e.appliedEntries || [];
|
|
992
991
|
if (!applied.length) return out;
|
|
993
992
|
var primary = applied[0];
|
|
994
|
-
|
|
993
|
+
var withMd = guard.verdict.of(out, { metadata: { aaguid: primary.entry.aaguid,
|
|
994
|
+
keyIdentifiers: primary.entry.keyIdentifiers || [],
|
|
995
995
|
entry: primary.entry, entries: applied.map(function (a) { return a.entry; }),
|
|
996
|
-
anchors: primary.anchors.length };
|
|
997
|
-
|
|
998
|
-
return out;
|
|
996
|
+
anchors: primary.anchors.length } });
|
|
997
|
+
return guard.verdict.of(withMd, { anchoredTo: _anchoredRoutes(withMd, "metadata+rootCertificates") });
|
|
999
998
|
});
|
|
1000
999
|
});
|
|
1001
1000
|
})
|
|
1002
|
-
.then(function (res) { res
|
|
1001
|
+
.then(function (res) { return guard.verdict.of(res, { bindingChecked: bindingChecked }); });
|
|
1003
1002
|
}
|
|
1004
1003
|
|
|
1005
1004
|
function _applyMetadata(res, att, opts) {
|
|
@@ -1011,8 +1010,9 @@ function _applyMetadata(res, att, opts) {
|
|
|
1011
1010
|
? res.compound.filter(function (el) { return el.trustPath && el.trustPath.length; })
|
|
1012
1011
|
.map(function (el) { return { tp: el.trustPath, fmt: el.fmt, at: el.chainValidatedAt }; })
|
|
1013
1012
|
: (res.trustPath && res.trustPath.length ? [{ tp: res.trustPath, fmt: res.fmt, at: res.chainValidatedAt }] : []);
|
|
1014
|
-
res
|
|
1015
|
-
|
|
1013
|
+
res = guard.verdict.of(res, { anchoredElements: {
|
|
1014
|
+
total: (res.fmt === "compound" && Array.isArray(res.compound)) ? res.compound.length : 1,
|
|
1015
|
+
anchored: paths.length } });
|
|
1016
1016
|
if (!paths.length) {
|
|
1017
1017
|
throw _err("webauthn/metadata-not-applicable", "opts.metadata was supplied, but this attestation carries no trust path to anchor (format '" + res.fmt + "')");
|
|
1018
1018
|
}
|
|
@@ -1036,7 +1036,7 @@ function _applyMetadata(res, att, opts) {
|
|
|
1036
1036
|
}
|
|
1037
1037
|
var anchors = mds.metadataAnchors(entry, { metadata: md, time: at, certificate: tp[tp.length - 1] });
|
|
1038
1038
|
if (!anchors.length) throw _err("webauthn/metadata-no-anchor", "the metadata entry for " + identifier + " supplies no attestation root certificate");
|
|
1039
|
-
|
|
1039
|
+
guard.list.append(applied, { entry: entry, anchors: anchors, identifier: identifier });
|
|
1040
1040
|
return { anchors: anchors, identifier: identifier };
|
|
1041
1041
|
}
|
|
1042
1042
|
var at = opts.time !== undefined ? opts.time : new Date();
|
|
@@ -1046,13 +1046,17 @@ function _applyMetadata(res, att, opts) {
|
|
|
1046
1046
|
var missedPaths = [];
|
|
1047
1047
|
for (var gi = 0; gi < paths.length; gi++) {
|
|
1048
1048
|
try {
|
|
1049
|
-
|
|
1049
|
+
guard.list.append(governed, { info: paths[gi], g: govern(paths[gi]) });
|
|
1050
1050
|
} catch (ge) {
|
|
1051
|
-
if (ge && ge.code === "webauthn/metadata-not-found") { missed = missed || ge;
|
|
1051
|
+
if (ge && ge.code === "webauthn/metadata-not-found") { missed = missed || ge; guard.list.append(missedPaths, paths[gi]); continue; }
|
|
1052
1052
|
throw ge;
|
|
1053
1053
|
}
|
|
1054
1054
|
}
|
|
1055
|
-
if (missed) {
|
|
1055
|
+
if (missed) {
|
|
1056
|
+
guard.verdict.set(missed, "missedPaths", missedPaths);
|
|
1057
|
+
guard.verdict.set(missed, "appliedEntries", applied);
|
|
1058
|
+
guard.verdict.set(missed, "anchoredElements", res.anchoredElements);
|
|
1059
|
+
}
|
|
1056
1060
|
return governed.reduce(function (p, item) {
|
|
1057
1061
|
return p.then(function () {
|
|
1058
1062
|
var g = item.g, info = item.info;
|
|
@@ -1064,10 +1068,10 @@ function _applyMetadata(res, att, opts) {
|
|
|
1064
1068
|
.then(function () {
|
|
1065
1069
|
if (missed) throw missed;
|
|
1066
1070
|
var primary = applied[0];
|
|
1067
|
-
res
|
|
1071
|
+
return guard.verdict.of(res, { metadata: { aaguid: primary.entry.aaguid,
|
|
1072
|
+
keyIdentifiers: primary.entry.keyIdentifiers || [],
|
|
1068
1073
|
entry: primary.entry, entries: applied.map(function (a) { return a.entry; }),
|
|
1069
|
-
anchors: primary.anchors.length };
|
|
1070
|
-
return res;
|
|
1074
|
+
anchors: primary.anchors.length } });
|
|
1071
1075
|
});
|
|
1072
1076
|
}
|
|
1073
1077
|
|
|
@@ -1558,7 +1562,7 @@ function verifyAssertion(input) {
|
|
|
1558
1562
|
signCountChecked = true;
|
|
1559
1563
|
}
|
|
1560
1564
|
}
|
|
1561
|
-
return {
|
|
1565
|
+
return guard.verdict.of({
|
|
1562
1566
|
valid: true,
|
|
1563
1567
|
signatureVerified: true,
|
|
1564
1568
|
signCount: authData.signCount,
|
|
@@ -1568,7 +1572,7 @@ function verifyAssertion(input) {
|
|
|
1568
1572
|
extensions: authData.extensions,
|
|
1569
1573
|
clientData: clientData,
|
|
1570
1574
|
bindingChecked: bindingChecked,
|
|
1571
|
-
};
|
|
1575
|
+
});
|
|
1572
1576
|
});
|
|
1573
1577
|
});
|
|
1574
1578
|
}
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:2af62d9e-82a4-4f11-8c8c-7b2c8b1ae891",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-05T15:01:32.918Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/pki@0.6.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.6.40",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.6.
|
|
25
|
+
"version": "0.6.40",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/pki@0.6.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.6.40",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/pki@0.6.
|
|
57
|
+
"ref": "@blamejs/pki@0.6.40",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|