@blamejs/pki 0.8.58 → 0.8.60
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 +50 -0
- package/lib/acme.js +53 -30
- package/lib/alt-sig.js +3 -1
- package/lib/asn1-der.js +98 -39
- 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 +16 -10
- package/lib/cmp-build.js +45 -34
- package/lib/cmp-session.js +19 -9
- package/lib/cms-decrypt.js +28 -10
- package/lib/cms-encrypt.js +23 -17
- package/lib/cms-sign.js +65 -43
- package/lib/cms-verify.js +11 -7
- package/lib/crl-sign.js +27 -20
- package/lib/csr-sign.js +5 -2
- package/lib/ct.js +37 -17
- package/lib/est.js +55 -32
- package/lib/guard-bytes.js +5 -1
- package/lib/guard-der.js +6 -1
- package/lib/guard-identifier.js +1 -1
- package/lib/guard-intrinsic.js +12 -4
- package/lib/guard-list.js +64 -0
- package/lib/guard-name.js +1 -1
- package/lib/hpke.js +10 -3
- package/lib/http-transport.js +12 -3
- package/lib/inspect.js +10 -2
- package/lib/jose.js +3 -1
- package/lib/key.js +11 -7
- package/lib/lint.js +236 -152
- package/lib/mime.js +9 -5
- package/lib/ocsp.js +25 -22
- package/lib/path-validate.js +48 -18
- package/lib/pbes2.js +6 -2
- package/lib/pkcs11-uri.js +3 -1
- package/lib/pkcs12-build.js +14 -11
- package/lib/pki-build.js +18 -14
- package/lib/possession.js +3 -1
- package/lib/rc2.js +6 -2
- package/lib/revocation-fetch.js +78 -19
- package/lib/scep.js +14 -6
- 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 +4 -1
- package/lib/shbs.js +7 -3
- package/lib/sign-scheme.js +4 -4
- package/lib/smime.js +18 -14
- package/lib/tlog.js +17 -11
- package/lib/tuf.js +3 -1
- package/lib/validator-keydesc.js +4 -1
- package/lib/validator-tls.js +7 -2
- package/lib/webcrypto.js +14 -8
- package/lib/x509-sign.js +20 -15
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cmp-session.js
CHANGED
|
@@ -15,6 +15,10 @@ var crmfSign = require("./crmf-sign");
|
|
|
15
15
|
var cmpBuild = require("./cmp-build");
|
|
16
16
|
var csr = require("./schema-csr");
|
|
17
17
|
var guard = require("./guard-all");
|
|
18
|
+
/** @internal The append every list here is built with: `push` stores at the index and a store
|
|
19
|
+
* consults a setter inherited there, and one of these lists is the accumulated CA certificates a
|
|
20
|
+
* later leg is authenticated against. */
|
|
21
|
+
var _push = guard.list.append;
|
|
18
22
|
var compositeSig = require("./composite-sig");
|
|
19
23
|
var constants = require("./constants");
|
|
20
24
|
var webcrypto = require("./webcrypto");
|
|
@@ -34,7 +38,7 @@ function setEngine(engine) { _engine = engine; }
|
|
|
34
38
|
var HASHLESS_SIG_OIDS = Object.create(null);
|
|
35
39
|
(function () {
|
|
36
40
|
var names = ["Ed25519", "Ed448", "id-ml-dsa-44", "id-ml-dsa-65", "id-ml-dsa-87"];
|
|
37
|
-
["sha2", "shake"].forEach(function (h) { ["128s", "128f", "192s", "192f", "256s", "256f"].forEach(function (s) { names
|
|
41
|
+
["sha2", "shake"].forEach(function (h) { ["128s", "128f", "192s", "192f", "256s", "256f"].forEach(function (s) { _push(names,"id-slh-dsa-" + h + "-" + s); }); });
|
|
38
42
|
names.forEach(function (n) { var o = oid.byName(n); if (o) HASHLESS_SIG_OIDS[o] = 1; });
|
|
39
43
|
})();
|
|
40
44
|
var SIG_OID_TO_HASH = Object.create(null);
|
|
@@ -136,11 +140,11 @@ function _asCertList(v) { return v == null ? [] : (Array.isArray(v) ? guard.list
|
|
|
136
140
|
function _boundedPool(base, added) {
|
|
137
141
|
var ceiling = constants.LIMITS.PATH_BUILD_MAX_CANDIDATES;
|
|
138
142
|
var seen = Object.create(null), distinctBase = [];
|
|
139
|
-
base.forEach(function (c) { var k = _certIdentity(c); if (k == null) { distinctBase
|
|
143
|
+
base.forEach(function (c) { var k = _certIdentity(c); if (k == null) { _push(distinctBase,c); return; } if (!seen[k]) { seen[k] = 1; _push(distinctBase,c); } });
|
|
140
144
|
var room = ceiling - distinctBase.length;
|
|
141
145
|
if (room <= 0) return distinctBase;
|
|
142
146
|
var useful = [];
|
|
143
|
-
added.forEach(function (c) { var k = _certIdentity(c); if (k != null && seen[k]) return; if (k != null) seen[k] = 1; useful
|
|
147
|
+
added.forEach(function (c) { var k = _certIdentity(c); if (k != null && seen[k]) return; if (k != null) seen[k] = 1; _push(useful,c); });
|
|
144
148
|
return distinctBase.concat(useful.slice(0, room));
|
|
145
149
|
}
|
|
146
150
|
function _certIdentity(cert) {
|
|
@@ -228,7 +232,7 @@ function _responseExtraCerts(responseBytes) {
|
|
|
228
232
|
seen[key] = true;
|
|
229
233
|
try { x509.parse(c); }
|
|
230
234
|
catch (_e) { continue; }
|
|
231
|
-
out
|
|
235
|
+
_push(out,Buffer.from(c));
|
|
232
236
|
}
|
|
233
237
|
return out;
|
|
234
238
|
}
|
|
@@ -682,7 +686,13 @@ function session(opts) {
|
|
|
682
686
|
if (!Buffer.isBuffer(c)) return;
|
|
683
687
|
var k = c.toString("base64");
|
|
684
688
|
if (caPubsSeen[k]) {
|
|
685
|
-
|
|
689
|
+
/** @internal The entry is read, removed and appended as three steps rather than through
|
|
690
|
+
* `splice`. `splice` RETURNS what it removed, so it builds that result through
|
|
691
|
+
* `ArraySpeciesCreate`, which reads the receiver's `constructor` and then its
|
|
692
|
+
* `Symbol.species`: a getter installed on `Array.prototype.constructor` is handed this
|
|
693
|
+
* accumulator as its `this` while the move is in progress, and the list holds the CA
|
|
694
|
+
* certificates a later leg is authenticated against. */
|
|
695
|
+
if (isGrantLeg) { for (var pi = 0; pi < caPubsAccum.length; pi++) { if (caPubsAccum[pi].toString("base64") === k) { var moved = caPubsAccum[pi]; guard.list.removeAt(caPubsAccum, pi); guard.list.append(caPubsAccum, moved); if (pi < caPubsWaitingCount) caPubsWaitingCount--; break; } } }
|
|
686
696
|
return;
|
|
687
697
|
}
|
|
688
698
|
try { x509.parse(c); } catch (e) { throw _err("cmp/bad-cert-response", "an authenticated caPubs entry is not a valid X.509 certificate", e); }
|
|
@@ -963,10 +973,10 @@ function session(opts) {
|
|
|
963
973
|
async function _validateLeaf(leaf, caPubs) {
|
|
964
974
|
if (_engine == null || opts.trustAnchors == null) return;
|
|
965
975
|
var caPubsList = [];
|
|
966
|
-
if (Array.isArray(caPubs)) caPubs.forEach(function (c) { if (Buffer.isBuffer(c)) caPubsList
|
|
976
|
+
if (Array.isArray(caPubs)) caPubs.forEach(function (c) { if (Buffer.isBuffer(c)) _push(caPubsList,c); });
|
|
967
977
|
var added = _asCertList(opts.intermediates);
|
|
968
|
-
if (Buffer.isBuffer(cachedSignerCert)) added
|
|
969
|
-
cachedChain.forEach(function (c) { if (Buffer.isBuffer(c)) added
|
|
978
|
+
if (Buffer.isBuffer(cachedSignerCert)) _push(added,cachedSignerCert);
|
|
979
|
+
cachedChain.forEach(function (c) { if (Buffer.isBuffer(c)) _push(added,c); });
|
|
970
980
|
var pool = _boundedPool(caPubsList, added);
|
|
971
981
|
var res;
|
|
972
982
|
try { res = await _engine.build(leaf, { trustAnchors: _asCertList(opts.trustAnchors), intermediates: pool, validate: true, time: opts.time != null ? opts.time : new Date() }); }
|
|
@@ -1395,7 +1405,7 @@ function session(opts) {
|
|
|
1395
1405
|
if (limit.total !== null && total > limit.total) {
|
|
1396
1406
|
throw _err("cmp/bad-input", "the resumeToken " + name + " exceeds the " + limit.total + "-byte pool a transaction holds");
|
|
1397
1407
|
}
|
|
1398
|
-
out
|
|
1408
|
+
_push(out,der);
|
|
1399
1409
|
}
|
|
1400
1410
|
return out;
|
|
1401
1411
|
}
|
package/lib/cms-decrypt.js
CHANGED
|
@@ -121,10 +121,10 @@ function _selectCandidates(recips, km, opts) {
|
|
|
121
121
|
for (var k = 0; k < recips.length; k++) {
|
|
122
122
|
var r = recips[k];
|
|
123
123
|
if (want === "asym" && (r.type === "ktri" || r.type === "kari" || r.type === "ori")) {
|
|
124
|
-
if (cert && _ridMatches(r, cert))
|
|
125
|
-
} else if (want === "pwri" && r.type === "pwri") {
|
|
124
|
+
if (cert && _ridMatches(r, cert)) intrinsic.append(out, { ri: r, index: k });
|
|
125
|
+
} else if (want === "pwri" && r.type === "pwri") { intrinsic.append(out, { ri: r, index: k }); }
|
|
126
126
|
else if (want === "kekri" && r.type === "kekri") {
|
|
127
|
-
if (km.kekId == null || _kekIdMatches(r, km.kekId))
|
|
127
|
+
if (km.kekId == null || _kekIdMatches(r, km.kekId)) intrinsic.append(out, { ri: r, index: k });
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
if (!out.length) throw _err("cms/no-matching-recipient", "no recipient matches the supplied key material");
|
|
@@ -563,19 +563,23 @@ async function _aesKwUnwrap(kek, wrapped) {
|
|
|
563
563
|
guard.secret.zeroize(new Uint8Array(rawAb), CmsError, "cms/bad-input", "the unwrapped content key");
|
|
564
564
|
}
|
|
565
565
|
}
|
|
566
|
+
/** @internal Both KDF-input structures APPEND by defining the index. A store at an index the list
|
|
567
|
+
* has no own property at consults a setter inherited from the array prototype, so an accessor there
|
|
568
|
+
* would decide the SharedInfo these bytes become and derive a different KEK from the same shared
|
|
569
|
+
* secret. */
|
|
566
570
|
function _eccSharedInfo(wrapName, ukm, kekBytes) {
|
|
567
571
|
var kids = [b.sequence([b.oid(O(wrapName))])];
|
|
568
|
-
if (ukm)
|
|
572
|
+
if (ukm) intrinsic.append(kids, b.explicit(0, b.octetString(ukm)));
|
|
569
573
|
/** @internal The SuppPubInfo key length is written with the capture, for the same reason the
|
|
570
574
|
* encrypt side does it: this is KDF input, and a replaced write derives a different KEK from the
|
|
571
575
|
* same shared secret. */
|
|
572
576
|
var supp = Buffer.alloc(4); intrinsic.writeUInt32BE(supp, kekBytes * 8, 0);
|
|
573
|
-
|
|
577
|
+
intrinsic.append(kids, b.explicit(2, b.octetString(supp)));
|
|
574
578
|
return b.sequence(kids);
|
|
575
579
|
}
|
|
576
580
|
function _kemOtherInfo(wrapName, kekBytes, ukm) {
|
|
577
581
|
var kids = [b.sequence([b.oid(O(wrapName))]), b.integer(BigInt(kekBytes))];
|
|
578
|
-
if (ukm)
|
|
582
|
+
if (ukm) intrinsic.append(kids, b.explicit(0, b.octetString(ukm)));
|
|
579
583
|
return b.sequence(kids);
|
|
580
584
|
}
|
|
581
585
|
function _pwriUnwrap(kek, wrapped, iv, keyBits) {
|
|
@@ -631,9 +635,13 @@ var HASH_W3C = intrinsic.create(null); HASH_W3C[O("sha1")] = "SHA-1"; HASH_W3C[O
|
|
|
631
635
|
function _hashW3c(o) { if (!HASH_W3C[o]) throw _err("cms/unsupported-algorithm", "unsupported OAEP hash " + o); return HASH_W3C[o]; }
|
|
632
636
|
|
|
633
637
|
var X963_HASH = intrinsic.create(null);
|
|
634
|
-
[["dhSinglePass-stdDH-sha1kdf-scheme", "SHA-1"], ["dhSinglePass-stdDH-sha224kdf-scheme", "SHA-224"], ["dhSinglePass-stdDH-sha256kdf-scheme", "SHA-256"], ["dhSinglePass-stdDH-sha384kdf-scheme", "SHA-384"], ["dhSinglePass-stdDH-sha512kdf-scheme", "SHA-512"],
|
|
638
|
+
var _X963_ROWS = [["dhSinglePass-stdDH-sha1kdf-scheme", "SHA-1"], ["dhSinglePass-stdDH-sha224kdf-scheme", "SHA-224"], ["dhSinglePass-stdDH-sha256kdf-scheme", "SHA-256"], ["dhSinglePass-stdDH-sha384kdf-scheme", "SHA-384"], ["dhSinglePass-stdDH-sha512kdf-scheme", "SHA-512"],
|
|
635
639
|
["dhSinglePass-cofactorDH-sha1kdf-scheme", "SHA-1"], ["dhSinglePass-cofactorDH-sha224kdf-scheme", "SHA-224"], ["dhSinglePass-cofactorDH-sha256kdf-scheme", "SHA-256"], ["dhSinglePass-cofactorDH-sha384kdf-scheme", "SHA-384"], ["dhSinglePass-cofactorDH-sha512kdf-scheme", "SHA-512"]
|
|
636
|
-
]
|
|
640
|
+
];
|
|
641
|
+
/** @internal The registry is built through the captured walk. The method is read off the live
|
|
642
|
+
* prototype at the moment it runs even where the receiver is a literal, and these rows decide which
|
|
643
|
+
* hash a key-agreement recipient's KDF uses. */
|
|
644
|
+
intrinsic.forEach(_X963_ROWS, function (r) { X963_HASH[O(r[0])] = r[1]; });
|
|
637
645
|
function _x963Hash(scheme) { if (!X963_HASH[scheme]) throw _err("cms/unsupported-algorithm", "unsupported kari key-agreement scheme " + scheme); return X963_HASH[scheme]; }
|
|
638
646
|
function _kariWrap(kea) {
|
|
639
647
|
var params = asn1.decode(kea.parameters);
|
|
@@ -644,7 +652,7 @@ function _originatorSpki(originator) {
|
|
|
644
652
|
if (!originator || originator.form !== "originatorKey") throw _err("cms/unsupported-algorithm", "kari requires an originatorKey (ephemeral-static ECDH)");
|
|
645
653
|
var v = originator.value;
|
|
646
654
|
var algKids = [b.oid(v.algorithm.oid)];
|
|
647
|
-
if (v.algorithm.parameters != null)
|
|
655
|
+
if (v.algorithm.parameters != null) intrinsic.append(algKids, b.raw(v.algorithm.parameters));
|
|
648
656
|
return b.sequence([b.sequence(algKids), b.bitString(v.publicKey.bytes, v.publicKey.unusedBits)]);
|
|
649
657
|
}
|
|
650
658
|
var MONT = intrinsic.create(null); MONT[O("X25519")] = { name: "X25519", hkdf: "SHA-256" }; MONT[O("X448")] = { name: "X448", hkdf: "SHA-512" };
|
|
@@ -706,7 +714,17 @@ async function _verifyAuthenticatedData(parsed, km, opts, requireProtection) {
|
|
|
706
714
|
guard.der.assertParams(parsed.digestAlgorithm.parameters, "absentOrNull", _err,
|
|
707
715
|
"cms/unsupported-algorithm", parsed.digestAlgorithm.name + " digestAlgorithm (RFC 5754 sec. 2)");
|
|
708
716
|
preimage = _explicitSetOf(parsed.authAttrsBytes);
|
|
709
|
-
|
|
717
|
+
/** @internal The selection returns the first matching attribute or null. `filter(...)[0]` indexes
|
|
718
|
+
* a result that is EMPTY when the attribute is absent, and index 0 of an empty array answers
|
|
719
|
+
* from the array prototype, so a value installed there would supply the digest this comparison
|
|
720
|
+
* is made against. The refusal restates an invariant the parse already holds: an
|
|
721
|
+
* AuthenticatedData with authAttrs is walked through the content-binding check, which requires
|
|
722
|
+
* the message-digest attribute (`cms/missing-message-digest`), so this is not reachable from a
|
|
723
|
+
* message and is here so the read below cannot be made on something that is not an attribute. */
|
|
724
|
+
var mdAttr = guard.list.firstMatch(parsed.authAttrs || [], function (a) { return a.type === O("messageDigest"); });
|
|
725
|
+
if (!mdAttr || mdAttr.values.length !== 1) {
|
|
726
|
+
throw _err("cms/bad-auth-attrs", "authenticated attributes must carry exactly one message-digest attribute (RFC 5652 sec. 9.2)");
|
|
727
|
+
}
|
|
710
728
|
mdCheck = { hash: dHash, declared: asn1.read.octetString(asn1.decode(mdAttr.values[0])) };
|
|
711
729
|
/** @internal RFC 6211 sec. 3.2 on this content type: the two comparisons are what make the
|
|
712
730
|
* outer `digestAlgorithm` and `macAlgorithm` fields mean anything, since the MAC input is the
|
package/lib/cms-encrypt.js
CHANGED
|
@@ -14,6 +14,10 @@ var webcrypto = require("./webcrypto");
|
|
|
14
14
|
var frameworkError = require("./framework-error");
|
|
15
15
|
var guard = require("./guard-all");
|
|
16
16
|
var intrinsic = require("./guard-intrinsic");
|
|
17
|
+
/** @internal The append every list here is built with: `push` stores at the index and a store
|
|
18
|
+
* consults a setter inherited there. These lists become a recipient info and the key-derivation
|
|
19
|
+
* input beside it. */
|
|
20
|
+
var _push = intrinsic.append;
|
|
17
21
|
var pbes2 = require("./pbes2");
|
|
18
22
|
var compositeKem = require("./composite-kem");
|
|
19
23
|
var b = asn1.build;
|
|
@@ -66,7 +70,7 @@ function _wrapOidForKek(keyBytes) {
|
|
|
66
70
|
|
|
67
71
|
function _gcmParams(nonce, icvLen) {
|
|
68
72
|
var kids = [b.octetString(nonce)];
|
|
69
|
-
if (icvLen !== 12) kids
|
|
73
|
+
if (icvLen !== 12) _push(kids,b.integer(BigInt(icvLen)));
|
|
70
74
|
return b.sequence(kids);
|
|
71
75
|
}
|
|
72
76
|
|
|
@@ -124,12 +128,12 @@ MONT_KA[O("X448")] = { name: "X448", hkdf: "SHA-512", scheme: "dhSinglePass-stdD
|
|
|
124
128
|
|
|
125
129
|
function _eccSharedInfo(wrapName, ukm, kekBytes) {
|
|
126
130
|
var kids = [_algId(wrapName, "absent")];
|
|
127
|
-
if (ukm) kids
|
|
131
|
+
if (ukm) _push(kids,b.explicit(0, b.octetString(ukm)));
|
|
128
132
|
/** @internal The SuppPubInfo key length is written with the capture. It is KDF input, so it is part
|
|
129
133
|
* of what the key-encryption key derives from, and a replaced write derives a different KEK from
|
|
130
134
|
* the same shared secret. */
|
|
131
135
|
var supp = Buffer.alloc(4); intrinsic.writeUInt32BE(supp, kekBytes * 8, 0);
|
|
132
|
-
kids
|
|
136
|
+
_push(kids,b.explicit(2, b.octetString(supp)));
|
|
133
137
|
return b.sequence(kids);
|
|
134
138
|
}
|
|
135
139
|
async function _buildKari(cek, cert, opts) {
|
|
@@ -179,8 +183,9 @@ async function _buildKari(cek, cert, opts) {
|
|
|
179
183
|
var rek = b.sequence([b.sequence([ridNode, b.octetString(encryptedKey)])]);
|
|
180
184
|
var kekAlg = b.sequence([b.oid(O(origKeyAlgId.scheme)), _algId(wrapName, "absent")]);
|
|
181
185
|
var kariKids = [b.integer(3n), b.explicit(0, originatorKey)];
|
|
182
|
-
if (ukm) kariKids
|
|
183
|
-
kariKids
|
|
186
|
+
if (ukm) _push(kariKids,b.explicit(1, b.octetString(ukm)));
|
|
187
|
+
_push(kariKids,kekAlg);
|
|
188
|
+
_push(kariKids,rek);
|
|
184
189
|
return { tag: 1, node: b.sequence(kariKids) };
|
|
185
190
|
} finally {
|
|
186
191
|
guard.secret.zeroizeAll([z, mz, kek], CmsError, "cms/bad-input", "the key-agreement shared secret");
|
|
@@ -227,7 +232,7 @@ var KEM_WC = intrinsic.create(null); KEM_WC[O("id-ml-kem-512")] = "ML-KEM-512";
|
|
|
227
232
|
|
|
228
233
|
function _kemOtherInfo(wrapName, kekBytes, ukm) {
|
|
229
234
|
var kids = [_algId(wrapName, "absent"), b.integer(BigInt(kekBytes))];
|
|
230
|
-
if (ukm) kids
|
|
235
|
+
if (ukm) _push(kids,b.explicit(0, b.octetString(ukm)));
|
|
231
236
|
return b.sequence(kids);
|
|
232
237
|
}
|
|
233
238
|
async function _kemEncapsulate(keyOid, cert) {
|
|
@@ -258,8 +263,9 @@ async function _buildKemri(cek, cert, opts) {
|
|
|
258
263
|
var encryptedKey = await _aesKwWrap(kek, cek);
|
|
259
264
|
var rid = _rid(cert, opts.keyIdentifier);
|
|
260
265
|
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
|
|
266
|
+
if (ukm) _push(kemriKids,b.explicit(0, b.octetString(ukm)));
|
|
267
|
+
_push(kemriKids,_algId(wrapName, "absent"));
|
|
268
|
+
_push(kemriKids,b.octetString(encryptedKey));
|
|
263
269
|
var kemri = b.sequence(kemriKids);
|
|
264
270
|
return { tag: 4, node: b.sequence([b.oid(O("kem")), kemri]) };
|
|
265
271
|
} finally {
|
|
@@ -446,7 +452,7 @@ async function _encryptImpl(content, recipients, opts) {
|
|
|
446
452
|
if (!recipients.length) throw _err("cms/bad-input", "at least one recipient is required (RFC 5652 sec. 6.1)");
|
|
447
453
|
var recips = [];
|
|
448
454
|
var consumed = intrinsic.create(null);
|
|
449
|
-
for (var i = 0; i < recipients.length; i++) recips
|
|
455
|
+
for (var i = 0; i < recipients.length; i++) _push(recips,await _buildRecipient(cek, recipients[i], opts, consumed));
|
|
450
456
|
_assertDefaultsConsumed(opts, consumed);
|
|
451
457
|
var riNodes = recips.map(_taggedRecipient);
|
|
452
458
|
|
|
@@ -535,8 +541,8 @@ async function _authEnvelopedData(contentBytes, contentStream, cek, ca, contentT
|
|
|
535
541
|
}
|
|
536
542
|
var eci = b.sequence([b.oid(O(contentType)), b.sequence([b.oid(O(ca.oid)), _gcmParams(nonce, 16)]), b.contextPrimitive(0, g.ct)]);
|
|
537
543
|
var kids = [b.integer(0n), b.setOf(riNodes), eci];
|
|
538
|
-
if (authAttrsDer) kids
|
|
539
|
-
kids
|
|
544
|
+
if (authAttrsDer) _push(kids,authAttrsDer);
|
|
545
|
+
_push(kids,b.octetString(g.tag));
|
|
540
546
|
void recips;
|
|
541
547
|
return b.sequence(kids);
|
|
542
548
|
}
|
|
@@ -680,7 +686,7 @@ async function _authenticateImpl(content, recipients, opts) {
|
|
|
680
686
|
try {
|
|
681
687
|
var recips = [];
|
|
682
688
|
var consumedAuth = {};
|
|
683
|
-
for (var i = 0; i < recipients.length; i++) recips
|
|
689
|
+
for (var i = 0; i < recipients.length; i++) _push(recips,await _buildRecipient(macKey, recipients[i], opts, consumedAuth));
|
|
684
690
|
_assertDefaultsConsumed(opts, consumedAuth);
|
|
685
691
|
var riNodes = recips.map(_taggedRecipient);
|
|
686
692
|
|
|
@@ -705,7 +711,7 @@ async function _authenticateImpl(content, recipients, opts) {
|
|
|
705
711
|
* MAC algorithm substitution attack, which is difficult because the attacker does not know
|
|
706
712
|
* the authentication key". Built from the same values the two outer fields are built from. */
|
|
707
713
|
if (protectionOid) {
|
|
708
|
-
intrinsic.
|
|
714
|
+
intrinsic.append(pairs, b.sequence([
|
|
709
715
|
b.oid(protectionOid),
|
|
710
716
|
b.setOf([b.sequence([_algId(digestName), b.implicit(2, _algId(mac.oid))])]),
|
|
711
717
|
]));
|
|
@@ -732,10 +738,10 @@ async function _authenticateImpl(content, recipients, opts) {
|
|
|
732
738
|
|
|
733
739
|
var eci = b.sequence([b.oid(O(contentType)), b.explicit(0, b.octetString(contentBytes))]);
|
|
734
740
|
var kids = [b.integer(0n), b.setOf(riNodes), _algId(mac.oid)];
|
|
735
|
-
if (digestAlgTagged) kids
|
|
736
|
-
kids
|
|
737
|
-
if (authAttrsDer) kids
|
|
738
|
-
kids
|
|
741
|
+
if (digestAlgTagged) _push(kids,digestAlgTagged);
|
|
742
|
+
_push(kids,eci);
|
|
743
|
+
if (authAttrsDer) _push(kids,authAttrsDer);
|
|
744
|
+
_push(kids,b.octetString(macValue));
|
|
739
745
|
var ci = b.sequence([b.oid(O("authData")), b.explicit(0, b.sequence(kids))]);
|
|
740
746
|
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
747
|
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
|
@@ -126,9 +126,9 @@ var SIG_SCHEME = intrinsic.assign(intrinsic.create(null), {
|
|
|
126
126
|
ecdsaWithSHA384: { kind: "ec", hash: "SHA-384", params: "absent" },
|
|
127
127
|
ecdsaWithSHA512: { kind: "ec", hash: "SHA-512", params: "absent" },
|
|
128
128
|
});
|
|
129
|
-
["sha2-128s", "sha2-128f", "sha2-192s", "sha2-192f", "sha2-256s", "sha2-256f",
|
|
130
|
-
|
|
131
|
-
]
|
|
129
|
+
intrinsic.forEach(["sha2-128s", "sha2-128f", "sha2-192s", "sha2-192f", "sha2-256s", "sha2-256f",
|
|
130
|
+
"shake-128s", "shake-128f", "shake-192s", "shake-192f", "shake-256s", "shake-256f"
|
|
131
|
+
], function (s) { SIG_SCHEME["id-slh-dsa-" + s] = { kind: "slhdsa", name: "SLH-DSA-" + intrinsic.toUpperCase(s), params: "absent", sameKeyOid: true, digest: SLHDSA_BY_OID[oid.byName("id-slh-dsa-" + s)].digest }; });
|
|
132
132
|
function _isDerNull(p) { return guard.der.isNull(p); }
|
|
133
133
|
function _algParamsOk(shape, p) { return shape === "null" ? _isDerNull(p) : (p === null || p === undefined); }
|
|
134
134
|
var EC_CURVE = intrinsic.create(null);
|
|
@@ -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
|
}
|