@blamejs/pki 0.8.59 → 0.8.61
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 +57 -0
- package/lib/acme.js +15 -7
- package/lib/alt-sig.js +3 -1
- package/lib/asn1-der.js +82 -34
- package/lib/attrcert-sign.js +19 -16
- package/lib/byte-writer.js +63 -13
- package/lib/cbor-det.js +9 -3
- package/lib/cmc-build.js +26 -18
- package/lib/cmp-build.js +35 -33
- package/lib/cmp-session.js +19 -9
- package/lib/cms-decrypt.js +37 -23
- package/lib/cms-digest.js +2 -3
- package/lib/cms-encrypt.js +38 -33
- package/lib/cms-sign.js +65 -43
- package/lib/cms-verify.js +8 -4
- package/lib/composite-kem.js +28 -29
- package/lib/crl-sign.js +24 -17
- package/lib/crmf-sign.js +11 -17
- package/lib/csr-sign.js +5 -2
- package/lib/ct.js +38 -25
- package/lib/est.js +12 -9
- package/lib/guard-bytes.js +5 -1
- package/lib/guard-crypto.js +506 -14
- package/lib/guard-der.js +6 -1
- package/lib/guard-intrinsic.js +12 -4
- package/lib/guard-list.js +64 -0
- package/lib/guard-name.js +1 -1
- package/lib/guard-secret.js +3 -2
- package/lib/hpke.js +34 -27
- package/lib/http-digest.js +5 -7
- package/lib/http-transport.js +12 -3
- package/lib/inspect.js +10 -2
- package/lib/jose.js +3 -1
- package/lib/key.js +41 -29
- package/lib/lint.js +236 -152
- package/lib/mime.js +9 -5
- package/lib/ocsp.js +26 -24
- package/lib/path-validate.js +43 -13
- package/lib/pbes2.js +12 -9
- package/lib/pkcs11-uri.js +3 -1
- package/lib/pkcs12-build.js +19 -17
- package/lib/pki-build.js +17 -13
- package/lib/possession.js +3 -1
- package/lib/rc2.js +6 -2
- package/lib/revocation-fetch.js +78 -19
- package/lib/scep.js +21 -14
- package/lib/schema-attrcert.js +4 -2
- package/lib/schema-c509.js +94 -78
- package/lib/schema-cmc.js +5 -2
- package/lib/schema-cmp.js +6 -3
- package/lib/schema-cms.js +7 -4
- package/lib/schema-crl.js +1 -1
- package/lib/schema-crmf.js +1 -1
- package/lib/schema-csr.js +3 -3
- package/lib/schema-engine.js +36 -18
- package/lib/schema-pkcs1.js +7 -3
- package/lib/schema-pkcs12.js +9 -6
- package/lib/schema-pkix.js +19 -3
- package/lib/schema-sec1.js +4 -1
- package/lib/schema-tsp.js +7 -4
- package/lib/shbs.js +10 -9
- package/lib/sign-scheme.js +2 -2
- package/lib/sigstore.js +9 -9
- package/lib/smime.js +19 -16
- package/lib/tlog.js +20 -13
- package/lib/tuf.js +7 -4
- package/lib/validator-cose.js +2 -2
- package/lib/validator-keydesc.js +4 -1
- package/lib/validator-tls.js +7 -2
- package/lib/webauthn-mds.js +3 -7
- package/lib/webauthn.js +4 -11
- package/lib/webcrypto.js +70 -49
- package/lib/x509-sign.js +20 -15
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cms-encrypt.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// @internal
|
|
5
5
|
// @primitive pki.cms.encrypt block live in cms-verify.js, which re-exports this function (the
|
|
6
6
|
|
|
7
|
-
var nodeCrypto = require("crypto");
|
|
8
7
|
var asn1 = require("./asn1-der");
|
|
9
8
|
var pkiBuild = require("./pki-build");
|
|
10
9
|
var oid = require("./oid");
|
|
@@ -14,6 +13,10 @@ var webcrypto = require("./webcrypto");
|
|
|
14
13
|
var frameworkError = require("./framework-error");
|
|
15
14
|
var guard = require("./guard-all");
|
|
16
15
|
var intrinsic = require("./guard-intrinsic");
|
|
16
|
+
/** @internal The append every list here is built with: `push` stores at the index and a store
|
|
17
|
+
* consults a setter inherited there. These lists become a recipient info and the key-derivation
|
|
18
|
+
* input beside it. */
|
|
19
|
+
var _push = intrinsic.append;
|
|
17
20
|
var pbes2 = require("./pbes2");
|
|
18
21
|
var compositeKem = require("./composite-kem");
|
|
19
22
|
var b = asn1.build;
|
|
@@ -66,7 +69,7 @@ function _wrapOidForKek(keyBytes) {
|
|
|
66
69
|
|
|
67
70
|
function _gcmParams(nonce, icvLen) {
|
|
68
71
|
var kids = [b.octetString(nonce)];
|
|
69
|
-
if (icvLen !== 12) kids
|
|
72
|
+
if (icvLen !== 12) _push(kids,b.integer(BigInt(icvLen)));
|
|
70
73
|
return b.sequence(kids);
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -124,12 +127,12 @@ MONT_KA[O("X448")] = { name: "X448", hkdf: "SHA-512", scheme: "dhSinglePass-stdD
|
|
|
124
127
|
|
|
125
128
|
function _eccSharedInfo(wrapName, ukm, kekBytes) {
|
|
126
129
|
var kids = [_algId(wrapName, "absent")];
|
|
127
|
-
if (ukm) kids
|
|
130
|
+
if (ukm) _push(kids,b.explicit(0, b.octetString(ukm)));
|
|
128
131
|
/** @internal The SuppPubInfo key length is written with the capture. It is KDF input, so it is part
|
|
129
132
|
* of what the key-encryption key derives from, and a replaced write derives a different KEK from
|
|
130
133
|
* the same shared secret. */
|
|
131
134
|
var supp = Buffer.alloc(4); intrinsic.writeUInt32BE(supp, kekBytes * 8, 0);
|
|
132
|
-
kids
|
|
135
|
+
_push(kids,b.explicit(2, b.octetString(supp)));
|
|
133
136
|
return b.sequence(kids);
|
|
134
137
|
}
|
|
135
138
|
async function _buildKari(cek, cert, opts) {
|
|
@@ -179,8 +182,9 @@ async function _buildKari(cek, cert, opts) {
|
|
|
179
182
|
var rek = b.sequence([b.sequence([ridNode, b.octetString(encryptedKey)])]);
|
|
180
183
|
var kekAlg = b.sequence([b.oid(O(origKeyAlgId.scheme)), _algId(wrapName, "absent")]);
|
|
181
184
|
var kariKids = [b.integer(3n), b.explicit(0, originatorKey)];
|
|
182
|
-
if (ukm) kariKids
|
|
183
|
-
kariKids
|
|
185
|
+
if (ukm) _push(kariKids,b.explicit(1, b.octetString(ukm)));
|
|
186
|
+
_push(kariKids,kekAlg);
|
|
187
|
+
_push(kariKids,rek);
|
|
184
188
|
return { tag: 1, node: b.sequence(kariKids) };
|
|
185
189
|
} finally {
|
|
186
190
|
guard.secret.zeroizeAll([z, mz, kek], CmsError, "cms/bad-input", "the key-agreement shared secret");
|
|
@@ -198,7 +202,7 @@ async function _buildKekri(cek, desc) {
|
|
|
198
202
|
|
|
199
203
|
async function _buildPwri(cek, desc) {
|
|
200
204
|
var iterations = pbes2.assertIterations(desc.iterations == null ? 600000 : desc.iterations, _err, "cms");
|
|
201
|
-
var salt = desc.salt ? pbes2.assertSalt(guard.bytes.view(desc.salt, CmsError, "cms/bad-input", "salt"), _err, "cms") :
|
|
205
|
+
var salt = desc.salt ? pbes2.assertSalt(guard.bytes.view(desc.salt, CmsError, "cms/bad-input", "salt"), _err, "cms") : guard.crypto.randomBytes(16);
|
|
202
206
|
var prf = desc.prf || "hmacWithSHA256";
|
|
203
207
|
_prfHash(prf);
|
|
204
208
|
var innerKeyBytes = 32;
|
|
@@ -208,7 +212,7 @@ async function _buildPwri(cek, desc) {
|
|
|
208
212
|
var kek = Buffer.from(await subtle.deriveBits({ name: "PBKDF2", hash: _prfHash(prf), salt: salt, iterations: iterations }, kekKey, innerKeyBytes * 8));
|
|
209
213
|
if (pwOwn.owned) guard.secret.zeroize(password, CmsError, "cms/bad-input", "the password encoding");
|
|
210
214
|
try {
|
|
211
|
-
var iv =
|
|
215
|
+
var iv = guard.crypto.randomBytes(16);
|
|
212
216
|
var encryptedKey = _pwriWrapIv(kek, cek, iv);
|
|
213
217
|
var kdfParams = pbes2.pbkdf2ParamsSeq(salt, iterations, prf);
|
|
214
218
|
var kdfAlg = b.contextConstructed(0, Buffer.concat([b.oid(O("pbkdf2")), kdfParams]));
|
|
@@ -227,7 +231,7 @@ var KEM_WC = intrinsic.create(null); KEM_WC[O("id-ml-kem-512")] = "ML-KEM-512";
|
|
|
227
231
|
|
|
228
232
|
function _kemOtherInfo(wrapName, kekBytes, ukm) {
|
|
229
233
|
var kids = [_algId(wrapName, "absent"), b.integer(BigInt(kekBytes))];
|
|
230
|
-
if (ukm) kids
|
|
234
|
+
if (ukm) _push(kids,b.explicit(0, b.octetString(ukm)));
|
|
231
235
|
return b.sequence(kids);
|
|
232
236
|
}
|
|
233
237
|
async function _kemEncapsulate(keyOid, cert) {
|
|
@@ -258,8 +262,9 @@ async function _buildKemri(cek, cert, opts) {
|
|
|
258
262
|
var encryptedKey = await _aesKwWrap(kek, cek);
|
|
259
263
|
var rid = _rid(cert, opts.keyIdentifier);
|
|
260
264
|
var kemriKids = [b.integer(0n), rid.node, _algId(oid.name(keyOid), "absent"), b.octetString(kemct), _algId("hkdfWithSha256", "absent"), b.integer(BigInt(kekBytes))];
|
|
261
|
-
if (ukm) kemriKids
|
|
262
|
-
kemriKids
|
|
265
|
+
if (ukm) _push(kemriKids,b.explicit(0, b.octetString(ukm)));
|
|
266
|
+
_push(kemriKids,_algId(wrapName, "absent"));
|
|
267
|
+
_push(kemriKids,b.octetString(encryptedKey));
|
|
263
268
|
var kemri = b.sequence(kemriKids);
|
|
264
269
|
return { tag: 4, node: b.sequence([b.oid(O("kem")), kemri]) };
|
|
265
270
|
} finally {
|
|
@@ -282,17 +287,17 @@ function _pwriFormat(cek) {
|
|
|
282
287
|
var blk = 16;
|
|
283
288
|
var padLen = body.length % blk === 0 ? 0 : blk - (body.length % blk);
|
|
284
289
|
if (body.length + padLen < 2 * blk) padLen += (2 * blk - (body.length + padLen));
|
|
285
|
-
var wrapped = Buffer.concat([body,
|
|
290
|
+
var wrapped = Buffer.concat([body, guard.crypto.randomBytes(padLen)]);
|
|
286
291
|
guard.secret.zeroizeAll([check, body], CmsError, "cms/bad-input", "a PWRI formatting intermediate");
|
|
287
292
|
return wrapped;
|
|
288
293
|
}
|
|
289
294
|
function _pwriWrapIv(kek, cek, iv) {
|
|
290
295
|
var wk = _pwriFormat(cek);
|
|
291
296
|
try {
|
|
292
|
-
var c1 =
|
|
297
|
+
var c1 = guard.crypto.cipher("aes-256-cbc", kek, iv); guard.crypto.transformNoPadding(c1);
|
|
293
298
|
var pass1 = guard.secret.cipherFinish(c1, wk, CmsError, "cms/bad-input", "the PWRI first-pass ciphertext");
|
|
294
299
|
var iv2 = pass1.subarray(pass1.length - 16);
|
|
295
|
-
var c2 =
|
|
300
|
+
var c2 = guard.crypto.cipher("aes-256-cbc", kek, iv2); guard.crypto.transformNoPadding(c2);
|
|
296
301
|
return guard.secret.cipherFinish(c2, pass1, CmsError, "cms/bad-input", "the PWRI wrapped key");
|
|
297
302
|
} finally {
|
|
298
303
|
guard.secret.zeroize(wk, CmsError, "cms/bad-input", "the PWRI plaintext block");
|
|
@@ -438,7 +443,7 @@ async function _encryptImpl(content, recipients, opts) {
|
|
|
438
443
|
* them. */
|
|
439
444
|
var contentBytes = contentStream ? null : guard.bytes.view(content, CmsError, "cms/bad-input", "content");
|
|
440
445
|
var contentType = opts.contentType || "data";
|
|
441
|
-
var cek =
|
|
446
|
+
var cek = guard.crypto.randomBytes(ca.keyBits / 8);
|
|
442
447
|
|
|
443
448
|
try {
|
|
444
449
|
if (!Array.isArray(recipients)) return await _encryptedData(contentBytes, contentStream, recipients, ca, contentType, opts, cek);
|
|
@@ -446,7 +451,7 @@ async function _encryptImpl(content, recipients, opts) {
|
|
|
446
451
|
if (!recipients.length) throw _err("cms/bad-input", "at least one recipient is required (RFC 5652 sec. 6.1)");
|
|
447
452
|
var recips = [];
|
|
448
453
|
var consumed = intrinsic.create(null);
|
|
449
|
-
for (var i = 0; i < recipients.length; i++) recips
|
|
454
|
+
for (var i = 0; i < recipients.length; i++) _push(recips,await _buildRecipient(cek, recipients[i], opts, consumed));
|
|
450
455
|
_assertDefaultsConsumed(opts, consumed);
|
|
451
456
|
var riNodes = recips.map(_taggedRecipient);
|
|
452
457
|
|
|
@@ -471,7 +476,7 @@ async function _encStream(spec, stream) {
|
|
|
471
476
|
}
|
|
472
477
|
|
|
473
478
|
async function _envelopedData(contentBytes, contentStream, cek, ca, contentType, riNodes, recips) {
|
|
474
|
-
var iv =
|
|
479
|
+
var iv = guard.crypto.randomBytes(16);
|
|
475
480
|
var enc = contentStream
|
|
476
481
|
? (await _encStream({ algNode: "aes-" + ca.keyBits + "-cbc", key: cek, iv: iv }, contentStream)).ciphertext
|
|
477
482
|
: pbes2.cbcEncrypt(cek, iv, contentBytes, ca.keyBits, CmsError, "cms/bad-input");
|
|
@@ -516,7 +521,7 @@ var AUTH_ENVELOPED_FORBIDDEN = intrinsic.assign(intrinsic.create(null), {
|
|
|
516
521
|
});
|
|
517
522
|
|
|
518
523
|
async function _authEnvelopedData(contentBytes, contentStream, cek, ca, contentType, opts, riNodes, recips) {
|
|
519
|
-
var nonce =
|
|
524
|
+
var nonce = guard.crypto.randomBytes(12);
|
|
520
525
|
var authAttrsDer = null, aad = Buffer.alloc(0);
|
|
521
526
|
if (contentType !== "data" && !(opts.authAttrs && opts.authAttrs.length)) {
|
|
522
527
|
throw _err("cms/bad-input", "AuthEnvelopedData with a non-data contentType requires authAttrs (RFC 5083 sec. 2.1)");
|
|
@@ -535,8 +540,8 @@ async function _authEnvelopedData(contentBytes, contentStream, cek, ca, contentT
|
|
|
535
540
|
}
|
|
536
541
|
var eci = b.sequence([b.oid(O(contentType)), b.sequence([b.oid(O(ca.oid)), _gcmParams(nonce, 16)]), b.contextPrimitive(0, g.ct)]);
|
|
537
542
|
var kids = [b.integer(0n), b.setOf(riNodes), eci];
|
|
538
|
-
if (authAttrsDer) kids
|
|
539
|
-
kids
|
|
543
|
+
if (authAttrsDer) _push(kids,authAttrsDer);
|
|
544
|
+
_push(kids,b.octetString(g.tag));
|
|
540
545
|
void recips;
|
|
541
546
|
return b.sequence(kids);
|
|
542
547
|
}
|
|
@@ -555,7 +560,7 @@ async function _encryptedData(contentBytes, contentStream, desc, ca, contentType
|
|
|
555
560
|
" EncryptedData descriptor; that field is not read for this arm";
|
|
556
561
|
});
|
|
557
562
|
}
|
|
558
|
-
var iv =
|
|
563
|
+
var iv = guard.crypto.randomBytes(16);
|
|
559
564
|
var contentAlgNode, encKey;
|
|
560
565
|
if (desc && desc.cek != null) {
|
|
561
566
|
encKey = guard.bytes.view(desc.cek, CmsError, "cms/bad-input", "cek");
|
|
@@ -578,9 +583,9 @@ async function _encryptedDataPbes2(contentBytes, contentStream, desc, ca, conten
|
|
|
578
583
|
var pwOwn2 = pbes2.passwordBytesOwned(desc.password, _err, "cms");
|
|
579
584
|
var password = pwOwn2.bytes;
|
|
580
585
|
var iterations = pbes2.assertIterations(desc.iterations == null ? 600000 : desc.iterations, _err, "cms");
|
|
581
|
-
var salt = desc.salt ? pbes2.assertSalt(guard.bytes.view(desc.salt, CmsError, "cms/bad-input", "salt"), _err, "cms") :
|
|
586
|
+
var salt = desc.salt ? pbes2.assertSalt(guard.bytes.view(desc.salt, CmsError, "cms/bad-input", "salt"), _err, "cms") : guard.crypto.randomBytes(16);
|
|
582
587
|
var prf = desc.prf || "hmacWithSHA256";
|
|
583
|
-
var key =
|
|
588
|
+
var key = guard.crypto.pbkdf2(password, salt, iterations, ca.keyBits / 8, pbes2.prfNodeByName(prf, _err, "cms"));
|
|
584
589
|
if (pwOwn2.owned) guard.secret.zeroize(password, CmsError, "cms/bad-input", "the password encoding");
|
|
585
590
|
try {
|
|
586
591
|
var enc = contentStream
|
|
@@ -596,10 +601,10 @@ async function _encryptedDataPbes2(contentBytes, contentStream, desc, ca, conten
|
|
|
596
601
|
}
|
|
597
602
|
|
|
598
603
|
function _gcmEncrypt(key, nonce, plaintext, aad, keyBits, tagLen) {
|
|
599
|
-
var c =
|
|
600
|
-
if (aad && aad.length)
|
|
604
|
+
var c = guard.crypto.cipher("aes-" + keyBits + "-gcm", key, nonce, { authTagLength: tagLen });
|
|
605
|
+
if (aad && aad.length) guard.crypto.transformSetAAD(c, aad);
|
|
601
606
|
var ct = guard.secret.cipherFinish(c, plaintext, CmsError, "cms/bad-input", "a content-encryption intermediate");
|
|
602
|
-
return { ct: ct, tag:
|
|
607
|
+
return { ct: ct, tag: guard.crypto.cipherAuthTag(c) };
|
|
603
608
|
}
|
|
604
609
|
|
|
605
610
|
function _tlvHeaderLen(der) {
|
|
@@ -676,11 +681,11 @@ async function _authenticateImpl(content, recipients, opts) {
|
|
|
676
681
|
var contentType = opts.contentType || "data";
|
|
677
682
|
if (contentType !== "data" && !withAttrs) throw _err("cms/bad-input", "AuthenticatedData with a non-data contentType requires authenticated attributes (RFC 5652 sec. 9.1)");
|
|
678
683
|
|
|
679
|
-
var macKey =
|
|
684
|
+
var macKey = guard.crypto.randomBytes(MAC_KEY_OCTETS);
|
|
680
685
|
try {
|
|
681
686
|
var recips = [];
|
|
682
687
|
var consumedAuth = {};
|
|
683
|
-
for (var i = 0; i < recipients.length; i++) recips
|
|
688
|
+
for (var i = 0; i < recipients.length; i++) _push(recips,await _buildRecipient(macKey, recipients[i], opts, consumedAuth));
|
|
684
689
|
_assertDefaultsConsumed(opts, consumedAuth);
|
|
685
690
|
var riNodes = recips.map(_taggedRecipient);
|
|
686
691
|
|
|
@@ -705,7 +710,7 @@ async function _authenticateImpl(content, recipients, opts) {
|
|
|
705
710
|
* MAC algorithm substitution attack, which is difficult because the attacker does not know
|
|
706
711
|
* the authentication key". Built from the same values the two outer fields are built from. */
|
|
707
712
|
if (protectionOid) {
|
|
708
|
-
intrinsic.
|
|
713
|
+
intrinsic.append(pairs, b.sequence([
|
|
709
714
|
b.oid(protectionOid),
|
|
710
715
|
b.setOf([b.sequence([_algId(digestName), b.implicit(2, _algId(mac.oid))])]),
|
|
711
716
|
]));
|
|
@@ -732,10 +737,10 @@ async function _authenticateImpl(content, recipients, opts) {
|
|
|
732
737
|
|
|
733
738
|
var eci = b.sequence([b.oid(O(contentType)), b.explicit(0, b.octetString(contentBytes))]);
|
|
734
739
|
var kids = [b.integer(0n), b.setOf(riNodes), _algId(mac.oid)];
|
|
735
|
-
if (digestAlgTagged) kids
|
|
736
|
-
kids
|
|
737
|
-
if (authAttrsDer) kids
|
|
738
|
-
kids
|
|
740
|
+
if (digestAlgTagged) _push(kids,digestAlgTagged);
|
|
741
|
+
_push(kids,eci);
|
|
742
|
+
if (authAttrsDer) _push(kids,authAttrsDer);
|
|
743
|
+
_push(kids,b.octetString(macValue));
|
|
739
744
|
var ci = b.sequence([b.oid(O("authData")), b.explicit(0, b.sequence(kids))]);
|
|
740
745
|
try { schemaCms.parse(ci); } catch (e) { throw (e instanceof CmsError) ? e : _err("cms/bad-input", "the supplied authenticated attributes produced an invalid AuthenticatedData", e); }
|
|
741
746
|
return opts.pem ? schemaCms.pemEncode(ci, "CMS") : ci;
|
package/lib/cms-sign.js
CHANGED
|
@@ -15,6 +15,12 @@ var webcrypto = require("./webcrypto");
|
|
|
15
15
|
var signScheme = require("./sign-scheme");
|
|
16
16
|
var guard = require("./guard-all");
|
|
17
17
|
var intrinsic = require("./guard-intrinsic");
|
|
18
|
+
/** @internal `append`, not a captured `push`: appending with `push` STORES at the index, and a store
|
|
19
|
+
* walks the prototype chain for a setter, so an accessor installed at an array index receives each
|
|
20
|
+
* element as it is appended and can define something else in its place. The lists built here become
|
|
21
|
+
* a message's signed attributes, its certificate set and its signer infos. `append` defines the
|
|
22
|
+
* index on the array itself. */
|
|
23
|
+
var _push = intrinsic.append;
|
|
18
24
|
/** @internal `resolve` and `all` build their promise from the RECEIVER, so reading the global binding
|
|
19
25
|
* at the call hands the construction to whatever that binding holds. Each signer's digest and
|
|
20
26
|
* signature are awaited through one of these, and a replacement that settles without awaiting emits a
|
|
@@ -73,7 +79,11 @@ function _digest(digestName, content) {
|
|
|
73
79
|
|
|
74
80
|
|
|
75
81
|
function _skiValue(cert) {
|
|
76
|
-
|
|
82
|
+
/** @internal The selection returns the first matching row or null, because `filter(...)[0]` indexes
|
|
83
|
+
* a result that is EMPTY when the extension is absent, and index 0 of an empty array answers from
|
|
84
|
+
* the array prototype: the refusal below would then not be reached for a certificate that carries
|
|
85
|
+
* no SKI. */
|
|
86
|
+
var ext = guard.list.firstMatch(cert.extensions || [], function (e) { return e.oid === OID_SKI; });
|
|
77
87
|
if (!ext) throw _err("cms/no-ski", "a subjectKeyIdentifier signer identifier requires the signer certificate to carry an SKI extension");
|
|
78
88
|
try { return asn1.read.octetString(asn1.decode(ext.value)); }
|
|
79
89
|
catch (e) { throw _err("cms/no-ski", "the signer certificate's subjectKeyIdentifier extension value is not an OCTET STRING", e); }
|
|
@@ -216,10 +226,10 @@ async function _finishSignerInfo(rc, md, content, eContentType, opts) {
|
|
|
216
226
|
{ type: O("contentType"), values: [b.oid(eContentType)] },
|
|
217
227
|
{ type: O("messageDigest"), values: [b.octetString(md)] },
|
|
218
228
|
];
|
|
219
|
-
if (opts.signingTime !== false)
|
|
229
|
+
if (opts.signingTime !== false) _push(pairs, { type: O("signingTime"), values: [_timeValue(opts.signingTime)] });
|
|
220
230
|
var apOid = cms.algorithmProtectionOid(opts.algorithmProtection, _err);
|
|
221
231
|
if (apOid) {
|
|
222
|
-
|
|
232
|
+
_push(pairs, _algorithmProtectionPair(apOid,
|
|
223
233
|
rc.scheme.digestAlgId, rc.scheme.sigAlgId, 1));
|
|
224
234
|
}
|
|
225
235
|
pairs = intrinsic.concat(pairs, _resolveAttrPairs(opts.additionalSignedAttributes, "a signed attribute value"));
|
|
@@ -230,10 +240,13 @@ async function _finishSignerInfo(rc, md, content, eContentType, opts) {
|
|
|
230
240
|
var sig = await signScheme.signOverTbs(rc.scheme, rc.soKey, signedBytes, _signE);
|
|
231
241
|
await _assertKeyMatchesSpki(rc.keyOnly, rc.soKey, rc.soSpki, rc.scheme, sig, signedBytes, rc.cert);
|
|
232
242
|
var fields = [b.integer(BigInt(rc.version)), rc.sid, rc.scheme.digestAlgId];
|
|
233
|
-
if (toSign.wire)
|
|
234
|
-
|
|
243
|
+
if (toSign.wire) _push(fields, toSign.wire);
|
|
244
|
+
/** @internal Two appends, because `append` takes one value where `push` took many: the algorithm
|
|
245
|
+
* identifier and the signature it describes are adjacent fields of the same SignerInfo. */
|
|
246
|
+
_push(fields, rc.scheme.sigAlgId);
|
|
247
|
+
_push(fields, b.octetString(sig));
|
|
235
248
|
var ua = _buildUnsignedAttrs(opts.unsignedAttributes);
|
|
236
|
-
if (ua)
|
|
249
|
+
if (ua) _push(fields, ua);
|
|
237
250
|
return { si: b.sequence(fields), digestAlgId: rc.scheme.digestAlgId, version: rc.version, certDer: rc.certDer };
|
|
238
251
|
}
|
|
239
252
|
|
|
@@ -367,7 +380,7 @@ async function _signStream(stream, signers, opts) {
|
|
|
367
380
|
if (opts.signingTime != null && opts.signingTime !== false) guard.time.assertEncodable(opts.signingTime, _err, "cms/bad-input", "signingTime");
|
|
368
381
|
var rcs = intrinsic.map(list, function (s) { return _resolveSignerContext(s, opts); });
|
|
369
382
|
var digestNames = [], seenDigest = intrinsic.create(null);
|
|
370
|
-
intrinsic.forEach(rcs, function (r) { if (!seenDigest[r.scheme.digest]) { seenDigest[r.scheme.digest] = 1;
|
|
383
|
+
intrinsic.forEach(rcs, function (r) { if (!seenDigest[r.scheme.digest]) { seenDigest[r.scheme.digest] = 1; _push(digestNames, r.scheme.digest); } });
|
|
371
384
|
var wcNames = intrinsic.map(digestNames, function (n) { return DIGEST_HASH[n]; });
|
|
372
385
|
var digests;
|
|
373
386
|
try {
|
|
@@ -379,7 +392,7 @@ async function _signStream(stream, signers, opts) {
|
|
|
379
392
|
intrinsic.forEach(digestNames, function (n, i) { byName[n] = intrinsic.bufferFrom(digests[i]); });
|
|
380
393
|
var built = [];
|
|
381
394
|
for (var _ip = 0; _ip < rcs.length; _ip++) {
|
|
382
|
-
|
|
395
|
+
_push(built, await _finishSignerInfo(rcs[_ip], byName[rcs[_ip].scheme.digest], null, eContentType, opts));
|
|
383
396
|
}
|
|
384
397
|
return _assembleSignedData(built, eContentType, opts, null);
|
|
385
398
|
}
|
|
@@ -420,19 +433,19 @@ function _sign(content, signers, opts) {
|
|
|
420
433
|
|
|
421
434
|
function _assembleSignedData(built, eContentType, opts, contentBuf) {
|
|
422
435
|
var seen = intrinsic.create(null), digestAlgs = [];
|
|
423
|
-
intrinsic.forEach(built, function (x) { var k = intrinsic.bufToString(x.digestAlgId, "hex"); if (!seen[k]) { seen[k] = 1;
|
|
436
|
+
intrinsic.forEach(built, function (x) { var k = intrinsic.bufToString(x.digestAlgId, "hex"); if (!seen[k]) { seen[k] = 1; _push(digestAlgs, x.digestAlgId); } });
|
|
424
437
|
var v3 = intrinsic.some(built, function (x) { return x.version === 3; }) || eContentType !== OID_DATA;
|
|
425
438
|
var version = v3 ? 3 : 1;
|
|
426
439
|
var encapFields = [b.oid(eContentType)];
|
|
427
|
-
if (!opts.detached)
|
|
440
|
+
if (!opts.detached) _push(encapFields, b.explicit(0, b.octetString(contentBuf)));
|
|
428
441
|
var encap = b.sequence(encapFields);
|
|
429
442
|
var sdFields = [b.integer(BigInt(version)), b.set(digestAlgs), encap];
|
|
430
443
|
if (opts.certificates !== false) {
|
|
431
444
|
var certDers = intrinsic.sort(_dedupe(intrinsic.filter(intrinsic.map(built, function (x) { return x.certDer; }),
|
|
432
445
|
function (d) { return d != null; })), intrinsic.compare);
|
|
433
|
-
if (certDers.length)
|
|
446
|
+
if (certDers.length) _push(sdFields, b.contextConstructed(0, intrinsic.bufferConcat(certDers)));
|
|
434
447
|
}
|
|
435
|
-
|
|
448
|
+
_push(sdFields, b.set(intrinsic.map(built, function (x) { return x.si; })));
|
|
436
449
|
var signedData = b.sequence(sdFields);
|
|
437
450
|
var contentInfo = b.sequence([b.oid(OID_SIGNED_DATA), b.explicit(0, signedData)]);
|
|
438
451
|
return opts.pem ? pkix.pemEncode(contentInfo, "CMS", frameworkError.PemError) : contentInfo;
|
|
@@ -450,7 +463,7 @@ function _digestAlgIdentifier(der) {
|
|
|
450
463
|
|
|
451
464
|
function _dedupe(ders) {
|
|
452
465
|
var seen = intrinsic.create(null), out = [];
|
|
453
|
-
intrinsic.forEach(ders, function (d) { var k = intrinsic.bufToString(d, "hex"); if (!seen[k]) { seen[k] = 1;
|
|
466
|
+
intrinsic.forEach(ders, function (d) { var k = intrinsic.bufToString(d, "hex"); if (!seen[k]) { seen[k] = 1; _push(out, d); } });
|
|
454
467
|
return out;
|
|
455
468
|
}
|
|
456
469
|
|
|
@@ -496,9 +509,9 @@ function certsOnly(certs, opts) {
|
|
|
496
509
|
var crlDers = intrinsic.sort(_dedupe(intrinsic.map(crlList, function (c) { return _normEntityDer(c, "a CRL", "X509 CRL", _parseCrl); })), intrinsic.compare);
|
|
497
510
|
var encap = b.sequence([b.oid(OID_DATA)]);
|
|
498
511
|
var sdFields = [b.integer(1n), b.set([]), encap];
|
|
499
|
-
if (certDers.length)
|
|
500
|
-
if (crlDers.length)
|
|
501
|
-
|
|
512
|
+
if (certDers.length) _push(sdFields, b.contextConstructed(0, intrinsic.bufferConcat(certDers)));
|
|
513
|
+
if (crlDers.length) _push(sdFields, b.contextConstructed(1, intrinsic.bufferConcat(crlDers)));
|
|
514
|
+
_push(sdFields, b.set([]));
|
|
502
515
|
var signedData = b.sequence(sdFields);
|
|
503
516
|
var contentInfo = b.sequence([b.oid(OID_SIGNED_DATA), b.explicit(0, signedData)]);
|
|
504
517
|
return opts.pem ? pkix.pemEncode(contentInfo, "CMS", frameworkError.PemError) : contentInfo;
|
|
@@ -512,7 +525,7 @@ function _toBuf(v, what) {
|
|
|
512
525
|
|
|
513
526
|
function _resolveSignerIndices(spec, n) {
|
|
514
527
|
if (spec == null) { if (n < 1) throw _err("cms/bad-input", "the SignedData carries no SignerInfo to countersign"); return [0]; }
|
|
515
|
-
if (spec === "all") { var all = []; for (var i = 0; i < n; i++) all
|
|
528
|
+
if (spec === "all") { var all = []; for (var i = 0; i < n; i++) _push(all, i); return all; }
|
|
516
529
|
var arr = Array.isArray(spec) ? spec : [spec];
|
|
517
530
|
if (!arr.length) throw _err("cms/bad-input", "signerIndex must select at least one signer");
|
|
518
531
|
arr.forEach(function (i) { if (typeof i !== "number" || !Number.isInteger(i) || i < 0 || i >= n) throw _err("cms/bad-input", "signerIndex out of range: " + i); });
|
|
@@ -534,10 +547,10 @@ function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
|
534
547
|
if (opts.signedAttributes === false) return null;
|
|
535
548
|
return intrinsic.chain(_digest(scheme.digest, targetSigOctets), function (md) {
|
|
536
549
|
var pairs = [{ type: O("messageDigest"), values: [b.octetString(md)] }];
|
|
537
|
-
if (opts.signingTime !== false) pairs
|
|
550
|
+
if (opts.signingTime !== false) _push(pairs, { type: O("signingTime"), values: [_timeValue(opts.signingTime)] });
|
|
538
551
|
var csApOid = cms.algorithmProtectionOid(opts.algorithmProtection, _err);
|
|
539
552
|
if (csApOid) {
|
|
540
|
-
|
|
553
|
+
_push(pairs, _algorithmProtectionPair(csApOid, scheme.digestAlgId, scheme.sigAlgId, 1));
|
|
541
554
|
}
|
|
542
555
|
var extra = _resolveAttrPairs(opts.additionalSignedAttributes, "a countersignature signed attribute value");
|
|
543
556
|
extra.forEach(function (p) { if (p.type === O("contentType")) throw _err("cms/bad-input", "a countersignature must not carry a content-type attribute (RFC 5652 sec. 11.4)"); });
|
|
@@ -556,8 +569,9 @@ function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
|
556
569
|
});
|
|
557
570
|
return intrinsic.chain(selfChecked, function () {
|
|
558
571
|
var fields = [b.integer(BigInt(sidv.version)), sidv.sid, scheme.digestAlgId];
|
|
559
|
-
if (attrs) fields
|
|
560
|
-
fields
|
|
572
|
+
if (attrs) _push(fields, attrs.wire);
|
|
573
|
+
_push(fields, scheme.sigAlgId);
|
|
574
|
+
_push(fields, b.octetString(sig));
|
|
561
575
|
return { value: b.sequence(fields), certDer: certDer, digestAlgId: scheme.digestAlgId };
|
|
562
576
|
});
|
|
563
577
|
});
|
|
@@ -568,13 +582,17 @@ function _mergeCountersig(uaNode, newCsValues) {
|
|
|
568
582
|
var CS = O("countersignature");
|
|
569
583
|
var others = [], csValues = [];
|
|
570
584
|
if (uaNode) uaNode.children.forEach(function (attr) {
|
|
571
|
-
if (asn1.read.oid(attr.children[0]) === CS) attr.children[1].children
|
|
572
|
-
else others
|
|
585
|
+
if (asn1.read.oid(attr.children[0]) === CS) intrinsic.forEach(attr.children[1].children, function (v) { _push(csValues, v.bytes); });
|
|
586
|
+
else _push(others, attr.bytes);
|
|
573
587
|
});
|
|
574
|
-
|
|
588
|
+
intrinsic.forEach(newCsValues, function (v) { _push(csValues, v); });
|
|
575
589
|
var csAttr = b.sequence([b.oid(CS), b.set(csValues)]);
|
|
576
|
-
|
|
577
|
-
|
|
590
|
+
/** @internal `concatList`, not `concat`: `Array.prototype.concat` builds its result through the
|
|
591
|
+
* species constructor, so a species answering with a Proxy intercepted the countersignature
|
|
592
|
+
* attribute as it was inserted and the merged value went out without it. `concatList` copies own
|
|
593
|
+
* indexes into an array it made itself. */
|
|
594
|
+
var setOf = b.set(intrinsic.concatList(others, [csAttr]));
|
|
595
|
+
var wire = intrinsic.bufferFrom(setOf); wire[0] = 0xA1;
|
|
578
596
|
return wire;
|
|
579
597
|
}
|
|
580
598
|
|
|
@@ -583,7 +601,7 @@ function _appendCountersigs(siNode, newCsValues) {
|
|
|
583
601
|
var last = kids[kids.length - 1];
|
|
584
602
|
var hasUa = last.tagClass === "context" && last.tagNumber === 1;
|
|
585
603
|
var base = (hasUa ? kids.slice(0, kids.length - 1) : kids).map(function (k) { return k.bytes; });
|
|
586
|
-
base
|
|
604
|
+
_push(base, _mergeCountersig(hasUa ? last : null, newCsValues));
|
|
587
605
|
return b.sequence(base);
|
|
588
606
|
}
|
|
589
607
|
|
|
@@ -603,7 +621,7 @@ function _spliceNested(siNode, j, newCsValues) {
|
|
|
603
621
|
if (!found) throw _err("cms/bad-input", "the target signer carries no countersignature to countersign");
|
|
604
622
|
var setOf = b.set(attrs); var wire = Buffer.from(setOf); wire[0] = 0xA1;
|
|
605
623
|
var base = kids.slice(0, kids.length - 1).map(function (k) { return k.bytes; });
|
|
606
|
-
base
|
|
624
|
+
_push(base, wire);
|
|
607
625
|
return b.sequence(base);
|
|
608
626
|
}
|
|
609
627
|
|
|
@@ -619,7 +637,11 @@ function _targetPreimage(siNode, opts) {
|
|
|
619
637
|
var last = siNode.children[siNode.children.length - 1];
|
|
620
638
|
var CS = O("countersignature");
|
|
621
639
|
if (!last || last.tagClass !== "context" || last.tagNumber !== 1) throw _err("cms/bad-input", "the target signer carries no countersignature to countersign");
|
|
622
|
-
|
|
640
|
+
/** @internal The selection returns the first matching attribute or null, because `filter(...)[0]`
|
|
641
|
+
* indexes a result that is EMPTY when the signer carries no countersignature attribute, and index
|
|
642
|
+
* 0 of an empty array answers from the array prototype: the refusal below would then not be
|
|
643
|
+
* reached and the signature bytes would be taken from whatever the accessor answered with. */
|
|
644
|
+
var attr = guard.list.firstMatch(last.children, function (a) { return asn1.read.oid(a.children[0]) === CS; });
|
|
623
645
|
if (!attr) throw _err("cms/bad-input", "the target signer carries no countersignature to countersign");
|
|
624
646
|
var values = attr.children[1].children;
|
|
625
647
|
var j = opts.countersignatureOf;
|
|
@@ -653,15 +675,15 @@ function _countersign(cmsInput, signers, opts) {
|
|
|
653
675
|
var jobs = [];
|
|
654
676
|
targets.forEach(function (t) {
|
|
655
677
|
var preimage = _targetPreimage(siSet.children[t], opts);
|
|
656
|
-
|
|
678
|
+
intrinsic.forEach(list, function (cs) { _push(jobs, { t: t, p: _buildCountersignature(preimage, cs, opts) }); });
|
|
657
679
|
});
|
|
658
680
|
return intrinsic.chain(intrinsic.allOf(intrinsic.mapList(jobs, function (j) { return j.p; })), function (built) {
|
|
659
681
|
var byTarget = intrinsic.create(null), certDers = [], csDigestAlgs = [];
|
|
660
682
|
intrinsic.forEach(built, function (res, i) {
|
|
661
683
|
var slot = byTarget[jobs[i].t] || (byTarget[jobs[i].t] = []);
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
684
|
+
_push(slot, res.value);
|
|
685
|
+
_push(certDers, res.certDer);
|
|
686
|
+
_push(csDigestAlgs, res.digestAlgId);
|
|
665
687
|
});
|
|
666
688
|
|
|
667
689
|
var newSiSet = b.set(siSet.children.map(function (siNode, idx) {
|
|
@@ -675,8 +697,8 @@ function _countersign(cmsInput, signers, opts) {
|
|
|
675
697
|
else if (sdKids[i].tagClass === "context" && sdKids[i].tagNumber === 1) crlsNode = sdKids[i];
|
|
676
698
|
}
|
|
677
699
|
var existing = [];
|
|
678
|
-
if (certsNode) certsNode.children
|
|
679
|
-
if (opts.certificates !== false)
|
|
700
|
+
if (certsNode) intrinsic.forEach(certsNode.children, function (c) { _push(existing, c.bytes); });
|
|
701
|
+
if (opts.certificates !== false) intrinsic.forEach(certDers, function (d) { _push(existing, d); });
|
|
680
702
|
/** @internal The DER SET ordering through the captured copy, sort AND comparator: read off its own
|
|
681
703
|
* home, any of the three decides which certificate lands where in the set this message carries. */
|
|
682
704
|
var allCerts = guard.list.sortedCopy(_dedupe(existing), intrinsic.compare);
|
|
@@ -693,20 +715,20 @@ function _countersign(cmsInput, signers, opts) {
|
|
|
693
715
|
* comparison would list it a second time. */
|
|
694
716
|
var listed = [];
|
|
695
717
|
intrinsic.forEach(intrinsic.isArray(parsed.digestAlgorithms) ? parsed.digestAlgorithms : [],
|
|
696
|
-
function (a) {
|
|
718
|
+
function (a) { _push(listed, a); });
|
|
697
719
|
var digestAlgs = intrinsic.map(sdKids[1].children, function (d) { return d.bytes; });
|
|
698
720
|
intrinsic.forEach(csDigestAlgs, function (der) {
|
|
699
721
|
var info = _digestAlgIdentifier(der);
|
|
700
722
|
var already = false;
|
|
701
723
|
intrinsic.forEach(listed, function (alg) { if (cms.sameDigestAlgorithm(alg, info)) already = true; });
|
|
702
724
|
if (already) return;
|
|
703
|
-
|
|
704
|
-
|
|
725
|
+
_push(listed, info);
|
|
726
|
+
_push(digestAlgs, der);
|
|
705
727
|
});
|
|
706
728
|
var newSdFields = [sdKids[0].bytes, b.set(digestAlgs), sdKids[2].bytes];
|
|
707
|
-
if (allCerts.length) newSdFields
|
|
708
|
-
if (crlsNode) newSdFields
|
|
709
|
-
newSdFields
|
|
729
|
+
if (allCerts.length) _push(newSdFields, b.contextConstructed(0, intrinsic.bufferConcat(allCerts)));
|
|
730
|
+
if (crlsNode) _push(newSdFields, crlsNode.bytes);
|
|
731
|
+
_push(newSdFields, newSiSet);
|
|
710
732
|
var newCi = b.sequence([root.children[0].bytes, b.explicit(0, b.sequence(newSdFields))]);
|
|
711
733
|
return opts.pem ? pkix.pemEncode(newCi, "CMS", frameworkError.PemError) : newCi;
|
|
712
734
|
});
|
|
@@ -732,7 +754,7 @@ function _appendUnsignedAttr(siNode, attrDer) {
|
|
|
732
754
|
var existing = hasUa ? intrinsic.map(last.children, function (a) { return a.bytes; }) : [];
|
|
733
755
|
var setOf = b.set(intrinsic.concat(existing, [attrDer]));
|
|
734
756
|
var wire = intrinsic.bufferFrom(setOf); wire[0] = 0xA1;
|
|
735
|
-
|
|
757
|
+
_push(base, wire);
|
|
736
758
|
return b.sequence(base);
|
|
737
759
|
}
|
|
738
760
|
|
|
@@ -955,7 +977,7 @@ function _attachTimestamp(input, token, opts) {
|
|
|
955
977
|
}));
|
|
956
978
|
var sdKids = t.sd.children;
|
|
957
979
|
var newSdFields = intrinsic.map(intrinsic.arraySlice(sdKids, 0, sdKids.length - 1), function (k) { return k.bytes; });
|
|
958
|
-
|
|
980
|
+
_push(newSdFields, newSiSet);
|
|
959
981
|
var out = b.sequence([t.root.children[0].bytes, b.explicit(0, b.sequence(newSdFields))]);
|
|
960
982
|
return opts.pem ? pkix.pemEncode(out, "CMS", frameworkError.PemError) : out;
|
|
961
983
|
}
|
package/lib/cms-verify.js
CHANGED
|
@@ -203,7 +203,11 @@ function _preferBoundCandidate(si, candidates) {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
function _certSki(cert) {
|
|
206
|
-
|
|
206
|
+
/** @internal The selection returns the first matching row or null. `filter(...)[0]` indexes a
|
|
207
|
+
* result that is EMPTY when the extension is absent, and index 0 of an empty array answers from
|
|
208
|
+
* the array prototype, so a value installed there reads at the test below as an extension the
|
|
209
|
+
* certificate does not carry. */
|
|
210
|
+
var ext = guard.list.firstMatch(cert.extensions || [], function (e) { return e.oid === oid.byName("subjectKeyIdentifier"); });
|
|
207
211
|
if (!ext) return null;
|
|
208
212
|
try {
|
|
209
213
|
return asn1.read.octetString(asn1.decode(ext.value));
|
|
@@ -359,7 +363,7 @@ async function _computeCountersigBytes(si, csTarget) {
|
|
|
359
363
|
attrs = _decodeSignedAttrs(reTagged);
|
|
360
364
|
} catch (e) { if (e instanceof CmsError) throw e; throw _err("cms/bad-signed-attrs", "the countersignature signedAttrs is not a valid SET OF Attribute", e); }
|
|
361
365
|
if (intrinsic.filter(attrs, function (a) { return a.type === OID_CONTENT_TYPE; }).length) throw _err("cms/misplaced-attr", "a countersignature must not carry a content-type attribute (RFC 5652 sec. 11.4)");
|
|
362
|
-
var mdAttr =
|
|
366
|
+
var mdAttr = guard.list.firstMatch(attrs, function (a) { return a.type === OID_MESSAGE_DIGEST; });
|
|
363
367
|
if (!mdAttr || mdAttr.values.length !== 1) throw _err("cms/bad-signed-attrs", "a countersignature's signedAttrs must carry exactly one message-digest attribute (RFC 5652 sec. 11.4)");
|
|
364
368
|
var declared;
|
|
365
369
|
try {
|
|
@@ -402,7 +406,7 @@ async function _computeSignedBytes(si, content, eContentType, digestByName) {
|
|
|
402
406
|
ctOid = asn1.read.oid(ctAttr[0].values[0]);
|
|
403
407
|
} catch (e) { throw _err("cms/bad-signed-attrs", "the content-type attribute value is not an OBJECT IDENTIFIER", e); }
|
|
404
408
|
if (ctOid !== eContentType) return { mismatch: { code: "cms/content-type-mismatch", message: "the content-type signed attribute does not match the SignedData eContentType (RFC 5652 sec. 5.3)" } };
|
|
405
|
-
var mdAttr =
|
|
409
|
+
var mdAttr = guard.list.firstMatch(attrs, function (a) { return a.type === OID_MESSAGE_DIGEST; });
|
|
406
410
|
if (!mdAttr || mdAttr.values.length !== 1) throw _err("cms/bad-signed-attrs", "signedAttrs must carry exactly one message-digest attribute (RFC 5652 sec. 5.4)");
|
|
407
411
|
var declared;
|
|
408
412
|
try {
|
|
@@ -1172,7 +1176,7 @@ var _permitsSigning = pkix.keyUsagePermits(pkix.makeNS("cms", CmsError, oid),
|
|
|
1172
1176
|
["digitalSignature", "contentCommitment", "nonRepudiation"]);
|
|
1173
1177
|
|
|
1174
1178
|
function _keyUsagePermitsSigning(parsedCerts, der) {
|
|
1175
|
-
var entry =
|
|
1179
|
+
var entry = guard.list.firstMatch(parsedCerts, function (c) { return intrinsic.bufferEquals(c.der, der); });
|
|
1176
1180
|
if (!entry) return true;
|
|
1177
1181
|
return _permitsSigning(entry.cert);
|
|
1178
1182
|
}
|