@blamejs/pki 0.6.14 → 0.6.16
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 +27 -1
- package/lib/acme.js +59 -815
- package/lib/asn1-der.js +7 -245
- package/lib/attrcert-sign.js +12 -260
- package/lib/attrcert-verify.js +1 -7
- package/lib/byte-reader.js +0 -8
- package/lib/byte-writer.js +0 -11
- package/lib/cbor-det.js +0 -54
- package/lib/cmc-build.js +15 -357
- package/lib/cmc-verify.js +6 -239
- package/lib/cmp-build.js +41 -317
- package/lib/cmp-session.js +73 -760
- package/lib/cmp-verify.js +32 -488
- package/lib/cms-compress.js +3 -32
- package/lib/cms-decrypt.js +29 -294
- package/lib/cms-encrypt.js +14 -234
- package/lib/cms-sign.js +29 -324
- package/lib/cms-verify.js +9 -500
- package/lib/composite-kem.js +2 -102
- package/lib/composite-sig.js +1 -77
- package/lib/constants.js +0 -249
- package/lib/crl-sign.js +9 -237
- package/lib/crl-verify.js +1 -10
- package/lib/crmf-sign.js +19 -198
- package/lib/crmf-verify.js +1 -12
- package/lib/csr-sign.js +3 -34
- package/lib/csr-verify.js +1 -12
- package/lib/ct.js +28 -253
- package/lib/edwards-point.js +10 -44
- package/lib/est.js +28 -764
- package/lib/framework-error.js +4 -182
- package/lib/guard-all.js +1 -58
- package/lib/guard-async.js +1 -36
- package/lib/guard-bytes.js +6 -650
- package/lib/guard-compress.js +5 -102
- package/lib/guard-crypto.js +1 -40
- package/lib/guard-der.js +2 -93
- package/lib/guard-encoding.js +2 -60
- package/lib/guard-header.js +1 -36
- package/lib/guard-identifier.js +7 -531
- package/lib/guard-intrinsic.js +1 -150
- package/lib/guard-json.js +10 -73
- package/lib/guard-limits.js +1 -77
- package/lib/guard-list.js +1 -49
- package/lib/guard-name.js +3 -209
- package/lib/guard-parsed.js +7 -335
- package/lib/guard-range.js +1 -71
- package/lib/guard-secret.js +1 -102
- package/lib/guard-text.js +1 -64
- package/lib/guard-time.js +1 -97
- package/lib/hpke.js +3 -105
- package/lib/http-digest.js +14 -142
- package/lib/http-retry-after.js +1 -47
- package/lib/http-transport.js +21 -131
- package/lib/inspect.js +30 -284
- package/lib/ip-utils.js +6 -47
- package/lib/jose.js +7 -142
- package/lib/key.js +2 -67
- package/lib/lint.js +9 -194
- package/lib/merkle.js +1 -27
- package/lib/mime.js +9 -82
- package/lib/ocsp-request-verify.js +1 -14
- package/lib/ocsp-verify.js +1 -82
- package/lib/ocsp.js +11 -169
- package/lib/oid.js +5 -337
- package/lib/path-validate.js +103 -1492
- package/lib/pbes2.js +2 -89
- package/lib/pkcs12-build.js +31 -379
- package/lib/pki-build.js +64 -290
- package/lib/rc2.js +2 -18
- package/lib/rfc3339.js +4 -27
- package/lib/scep.js +140 -286
- package/lib/schema-all.js +0 -122
- package/lib/schema-attrcert.js +4 -146
- package/lib/schema-c509.js +153 -802
- package/lib/schema-cmc.js +5 -220
- package/lib/schema-cmp.js +2 -223
- package/lib/schema-cms.js +13 -427
- package/lib/schema-crl.js +5 -68
- package/lib/schema-crmf.js +6 -167
- package/lib/schema-csr.js +1 -54
- package/lib/schema-csrattrs.js +2 -87
- package/lib/schema-engine.js +0 -152
- package/lib/schema-ocsp.js +0 -117
- package/lib/schema-pkcs12.js +2 -93
- package/lib/schema-pkcs8.js +3 -35
- package/lib/schema-pkix.js +24 -655
- package/lib/schema-smime.js +0 -54
- package/lib/schema-tsp.js +3 -99
- package/lib/schema-x509.js +4 -63
- package/lib/shbs.js +11 -95
- package/lib/sign-scheme.js +2 -97
- package/lib/sigstore.js +9 -169
- package/lib/sleep.js +2 -8
- package/lib/smime.js +15 -408
- package/lib/tls-cert-compress.js +0 -62
- package/lib/trust.js +19 -185
- package/lib/tsp-sign.js +16 -270
- package/lib/validator-all.js +1 -30
- package/lib/validator-attcert.js +2 -46
- package/lib/validator-cose.js +2 -153
- package/lib/validator-keydesc.js +1 -37
- package/lib/validator-sig.js +1 -46
- package/lib/validator-tls.js +1 -88
- package/lib/validator-tpm.js +19 -122
- package/lib/verify-seam.js +1 -24
- package/lib/webauthn-mds.js +3 -400
- package/lib/webauthn.js +12 -833
- package/lib/webcrypto.js +5 -485
- package/lib/x509-sign.js +4 -108
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cms-sign.js
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright (c) blamejs contributors
|
|
3
3
|
"use strict";
|
|
4
|
-
//
|
|
5
|
-
// @internal -- the pki.cms.sign implementation. So the pki.cms namespace has one @module home,
|
|
4
|
+
// @internal
|
|
6
5
|
// the operator-facing @module pki.cms + the @primitive pki.cms.sign documentation block live in
|
|
7
|
-
// cms-verify.js, which re-exports this sign function.
|
|
8
|
-
//
|
|
9
|
-
// CMS SignedData signing (RFC 5652 sec. 5), the producing side of pki.cms.verify: composes the
|
|
10
|
-
// strict asn1 build layer (canonical DER, and build.set SET-OF-sorts the signed attributes for
|
|
11
|
-
// free), the WebCrypto sign surface over node:crypto, and the shared validator.sig.rawToEcdsaDer
|
|
12
|
-
// DER-ECDSA home -- emitting exactly the shapes cms.verify checks (NULL params for RSA, absent
|
|
13
|
-
// for ECDSA/EdDSA, the RSASSA-PSS params SEQUENCE), with the sign->verify round-trip (and OpenSSL
|
|
14
|
-
// cms -verify) as the guard.
|
|
15
6
|
|
|
16
7
|
var asn1 = require("./asn1-der");
|
|
17
8
|
var oid = require("./oid");
|
|
@@ -24,73 +15,48 @@ var webcrypto = require("./webcrypto");
|
|
|
24
15
|
var signScheme = require("./sign-scheme");
|
|
25
16
|
var guard = require("./guard-all");
|
|
26
17
|
var intrinsic = require("./guard-intrinsic");
|
|
27
|
-
var pkiBuild = require("./pki-build");
|
|
18
|
+
var pkiBuild = require("./pki-build");
|
|
28
19
|
var cms = require("./schema-cms");
|
|
29
20
|
|
|
30
21
|
var subtle = webcrypto.webcrypto.subtle;
|
|
31
22
|
var CmsError = frameworkError.CmsError;
|
|
32
23
|
var b = asn1.build;
|
|
33
24
|
function _err(code, message, cause) { return new CmsError(code, message, cause); }
|
|
34
|
-
// The domain error factory the shared sign-scheme resolver/signer throws through (kind ->
|
|
35
|
-
// cms/<kind>), so its faults keep the cms/* codes.
|
|
36
25
|
function _signE(kind, message, cause) { return new CmsError("cms/" + kind, message, cause); }
|
|
37
26
|
function O(name) { return oid.byName(name); }
|
|
38
27
|
|
|
39
|
-
// Every option each verb READS, and nothing else. Derived from the signing paths themselves rather
|
|
40
|
-
// than from what callers happen to pass: an option this list omits is one the verb would ignore in
|
|
41
|
-
// silence, and the ones that matter most are the ones a caller would most expect to work.
|
|
42
|
-
// A misspelled option name is the shape this closes: it does not select the mode the caller meant,
|
|
43
|
-
// and without the gate the message is signed the other way with nothing said about it.
|
|
44
28
|
var KNOWN_SIGN_OPTS = {
|
|
45
29
|
signedAttributes: 1, signingTime: 1, additionalSignedAttributes: 1, unsignedAttributes: 1,
|
|
46
30
|
sid: 1, eContentType: 1, detached: 1, certificates: 1, pem: 1,
|
|
47
31
|
};
|
|
48
|
-
// The SIGNER descriptor has two forms, and every field it reads is named here so a caller that
|
|
49
|
-
// authors one can be held to the form it selects: a certificate signer, and the key-only signer
|
|
50
|
-
// RFC 5272 sec. 3.2 requires for a Full PKI Request, whose identity comes from `spki` +
|
|
51
|
-
// `keyIdentifier` instead. The signature parameters are read by both. Exported for the verbs that
|
|
52
|
-
// take signer descriptors as AUTHORING input and gate them at their own door -- pki.cms.sign takes
|
|
53
|
-
// them straight from its caller, where a bag carrying extra handles is an ordinary shape.
|
|
54
32
|
var KNOWN_SIGNER_CERT_KEYS = { cert: 1, key: 1, pss: 1, digestAlgorithm: 1, combinedRsaSig: 1 };
|
|
55
33
|
var KNOWN_SIGNER_KEY_ONLY_KEYS = { spki: 1, keyIdentifier: 1, key: 1, pss: 1, digestAlgorithm: 1, combinedRsaSig: 1 };
|
|
56
|
-
// Countersign has its own set: it takes signerIndex and countersignatureOf to SELECT which
|
|
57
|
-
// signature it attaches to, and has no content of its own, so eContentType, detached and
|
|
58
|
-
// unsignedAttributes are not among the options it reads.
|
|
59
34
|
var KNOWN_COUNTERSIGN_OPTS = {
|
|
60
35
|
signerIndex: 1, countersignatureOf: 1, signingTime: 1, certificates: 1, pem: 1,
|
|
61
36
|
signedAttributes: 1, additionalSignedAttributes: 1, sid: 1,
|
|
62
37
|
};
|
|
63
|
-
// The shared producing-side helpers, bound to this domain's error class. Only the
|
|
64
|
-
// post-sign self-check is used here (the same one every other signer in the
|
|
65
|
-
// toolkit runs), so a key-only signer's declared public key is bound by evidence
|
|
66
|
-
// and never by the caller's word.
|
|
67
38
|
var NS = pkix.makeNS("cms", CmsError, oid);
|
|
68
39
|
var _b = pkiBuild.makeBuilder({
|
|
69
40
|
ErrorClass: CmsError, prefix: "cms", O: O, NS: NS,
|
|
70
41
|
NAME_SCHEMA: pkix.name(NS), SPKI_SCHEMA: pkix.spki(NS), EXT_DECODERS: {},
|
|
71
42
|
});
|
|
72
43
|
|
|
73
|
-
// A digest-algorithm name -> the engine's hash name. The FIPS 202 extendable-output
|
|
74
|
-
// functions are message digests here at the lengths RFC 8702 sec. 4 fixes for that use,
|
|
75
|
-
// which the engine applies from the name alone.
|
|
76
44
|
var DIGEST_HASH = {
|
|
77
45
|
sha256: "SHA-256", sha384: "SHA-384", sha512: "SHA-512",
|
|
78
46
|
shake128: "SHAKE128", shake256: "SHAKE256",
|
|
79
47
|
};
|
|
80
48
|
|
|
81
49
|
var OID_DATA = O("data");
|
|
82
|
-
var OID_PKI_DATA = O("id-cct-PKIData");
|
|
50
|
+
var OID_PKI_DATA = O("id-cct-PKIData");
|
|
83
51
|
var OID_SIGNED_DATA = O("signedData");
|
|
84
52
|
var OID_SKI = O("subjectKeyIdentifier");
|
|
85
53
|
|
|
86
54
|
|
|
87
|
-
// The message digest of the content under the digest algorithm, resolved to a Buffer.
|
|
88
55
|
function _digest(digestName, content) {
|
|
89
56
|
return subtle.digest(DIGEST_HASH[digestName], content).then(function (d) { return Buffer.from(d); });
|
|
90
57
|
}
|
|
91
58
|
|
|
92
59
|
|
|
93
|
-
// The cert's subjectKeyIdentifier extension value (the raw key id), or throws.
|
|
94
60
|
function _skiValue(cert) {
|
|
95
61
|
var ext = (cert.extensions || []).filter(function (e) { return e.oid === OID_SKI; })[0];
|
|
96
62
|
if (!ext) throw _err("cms/no-ski", "a subjectKeyIdentifier signer identifier requires the signer certificate to carry an SKI extension");
|
|
@@ -99,23 +65,13 @@ function _skiValue(cert) {
|
|
|
99
65
|
}
|
|
100
66
|
|
|
101
67
|
|
|
102
|
-
// The SignerInfo SignerIdentifier + its coupled CMSVersion (RFC 5652 sec. 5.3): a [0] IMPLICIT
|
|
103
|
-
// subjectKeyIdentifier => version 3, else an IssuerAndSerialNumber => version 1. Shared by the
|
|
104
|
-
// top-level signer and the sec. 11.4 countersignature (a countersignature IS a SignerInfo).
|
|
105
68
|
function _buildSid(cert, useSki) {
|
|
106
69
|
var sid = useSki
|
|
107
|
-
? b.contextPrimitive(0, _skiValue(cert))
|
|
108
|
-
: b.sequence([b.raw(pkiBuild.tbsNameField(cert, "issuer")), b.integer(cert.serialNumber)]);
|
|
70
|
+
? b.contextPrimitive(0, _skiValue(cert))
|
|
71
|
+
: b.sequence([b.raw(pkiBuild.tbsNameField(cert, "issuer")), b.integer(cert.serialNumber)]);
|
|
109
72
|
return { sid: sid, version: useSki ? 3 : 1 };
|
|
110
73
|
}
|
|
111
74
|
|
|
112
|
-
// Assemble a SignedAttributes SET from resolved attribute pairs (each { type: <dotted OID>, values:
|
|
113
|
-
// [<build node or DER Buffer>] }), returning { setOf, wire }: `setOf` is the canonical DER SET OF
|
|
114
|
-
// (tag 0x31, build.set SET-OF-sorts) the signature covers (RFC 5652 sec. 5.4); `wire` is the same
|
|
115
|
-
// bytes with the on-wire [0] IMPLICIT tag (0xA0). Each attribute type appears at most once (sec.
|
|
116
|
-
// 5.3), so a duplicate throws. Shared by the top-level signer (content-type + message-digest +
|
|
117
|
-
// signing-time + caller attrs) and the countersignature builder (message-digest over the target
|
|
118
|
-
// signature octets + signing-time, content-type omitted per sec. 11.4).
|
|
119
75
|
function _buildSignedAttrs(pairs) {
|
|
120
76
|
var seenTypes = {};
|
|
121
77
|
var attrs = intrinsic.map(pairs, function (p) {
|
|
@@ -123,14 +79,11 @@ function _buildSignedAttrs(pairs) {
|
|
|
123
79
|
seenTypes[p.type] = 1;
|
|
124
80
|
return b.sequence([b.oid(p.type), b.set(p.values)]);
|
|
125
81
|
});
|
|
126
|
-
var setOf = b.set(attrs);
|
|
127
|
-
var wire = intrinsic.bufferFrom(setOf); wire[0] = 0xA0;
|
|
82
|
+
var setOf = b.set(attrs);
|
|
83
|
+
var wire = intrinsic.bufferFrom(setOf); wire[0] = 0xA0;
|
|
128
84
|
return { setOf: setOf, wire: wire };
|
|
129
85
|
}
|
|
130
86
|
|
|
131
|
-
// Resolve caller-supplied additional signed attributes ({ type: OID name or dotted, values: [DER] })
|
|
132
|
-
// into the pair shape _buildSignedAttrs consumes; each MUST carry >= 1 value (Attribute values is
|
|
133
|
-
// SET SIZE (1..MAX)).
|
|
134
87
|
function _resolveAttrPairs(list, what) {
|
|
135
88
|
return intrinsic.map(list || [], function (a) {
|
|
136
89
|
var vals = intrinsic.map(a.values || [], function (v) { return _toBuf(v, what); });
|
|
@@ -139,17 +92,11 @@ function _resolveAttrPairs(list, what) {
|
|
|
139
92
|
});
|
|
140
93
|
}
|
|
141
94
|
|
|
142
|
-
// content-type / message-digest / signing-time are content-binding signed attributes and MUST NOT
|
|
143
|
-
// appear as unsigned attributes (RFC 5652 sec. 11.1/11.2/11.3, the parser's ATTR_FORBIDDEN_IN).
|
|
144
95
|
var UNSIGNED_FORBIDDEN = {};
|
|
145
96
|
UNSIGNED_FORBIDDEN[O("contentType")] = "content-type";
|
|
146
97
|
UNSIGNED_FORBIDDEN[O("messageDigest")] = "message-digest";
|
|
147
98
|
UNSIGNED_FORBIDDEN[O("signingTime")] = "signing-time";
|
|
148
99
|
|
|
149
|
-
// Build the [1] IMPLICIT unsignedAttrs SET OF (on-wire tag 0xA1) from opts.unsignedAttributes
|
|
150
|
-
// (each { type, values: [DER] }, such as a countersignature or an RFC 3161 timestamp token), or null when
|
|
151
|
-
// absent/empty. Unsigned attributes sit outside the signature; a placement-forbidden type or a
|
|
152
|
-
// duplicate type (RFC 5652 sec. 11 / sec. 5.3) is a config-time cms/bad-input.
|
|
153
100
|
function _buildUnsignedAttrs(list) {
|
|
154
101
|
if (list == null) return null;
|
|
155
102
|
if (!intrinsic.isArray(list)) throw _err("cms/bad-input", "opts.unsignedAttributes must be an array of { type, values }");
|
|
@@ -162,67 +109,30 @@ function _buildUnsignedAttrs(list) {
|
|
|
162
109
|
seen[p.type] = 1;
|
|
163
110
|
});
|
|
164
111
|
var setOf = b.set(intrinsic.map(pairs, function (p) { return b.sequence([b.oid(p.type), b.set(p.values)]); }));
|
|
165
|
-
var wire = intrinsic.bufferFrom(setOf); wire[0] = 0xA1;
|
|
112
|
+
var wire = intrinsic.bufferFrom(setOf); wire[0] = 0xA1;
|
|
166
113
|
return wire;
|
|
167
114
|
}
|
|
168
115
|
|
|
169
|
-
// Resolve, at entry and once, every field an emitted SignerInfo depends on (RFC 5652 sec. 5.3):
|
|
170
|
-
// the SignerIdentifier + version, the signature scheme, the private key reference, the declared
|
|
171
|
-
// public bytes, and the signer certificate DER. Nothing here defers, so the values are captured
|
|
172
|
-
// before any promise turn; the digest, the signature, and the self-check happen a turn later in
|
|
173
|
-
// _finishSignerInfo, off exactly these captured values. Buffered and streamed signing share this.
|
|
174
116
|
function _resolveSignerContext(signer, opts) {
|
|
175
117
|
var so = signer || {};
|
|
176
|
-
// A key-only signer: `{ key, spki, keyIdentifier }` with no certificate. RFC
|
|
177
|
-
// 5272 sec. 3.2 requires exactly this when a Full PKI Request is signed with the
|
|
178
|
-
// key of a certification request it carries: there is no certificate yet, the
|
|
179
|
-
// sid MUST be the subjectKeyIdentifier form, and its value MUST be the SKI the
|
|
180
|
-
// request itself declares. The scheme resolver only ever reads
|
|
181
|
-
// `cert.subjectPublicKeyInfo.algorithm`, so the request's own parsed SPKI stands
|
|
182
|
-
// in for the certificate that does not exist.
|
|
183
118
|
var keyOnly = so.cert == null && so.spki != null;
|
|
184
|
-
// Every field the emitted SignerInfo depends on is read ONCE, HERE, while
|
|
185
|
-
// nothing has deferred yet. The SignerIdentifier is built from the certificate
|
|
186
|
-
// or the key identifier on the lines below; the key signs, and is matched
|
|
187
|
-
// against `spki`, a promise turn later. A descriptor whose `key` and `spki`
|
|
188
|
-
// were swapped in that gap would emit a SignerInfo that names one public key
|
|
189
|
-
// and carries a signature by another -- coherent to the match check, since
|
|
190
|
-
// both halves moved together, and unverifiable to everyone else.
|
|
191
|
-
//
|
|
192
|
-
// The public bytes are COPIED, so an in-place rewrite cannot reach them
|
|
193
|
-
// either. The private key is captured by reference: copying it would put a
|
|
194
|
-
// second copy of key material in this function's hands, a secret it would then
|
|
195
|
-
// own for the rest of the call, and the reference already pins which key
|
|
196
|
-
// signs against the identifier that names it.
|
|
197
119
|
var soKey = so.key;
|
|
198
120
|
var soSpki = keyOnly ? guard.bytes.snapshotSource(so.spki, CmsError, "cms/bad-input", "a key-only signer's spki") : null;
|
|
199
121
|
var certDer = keyOnly ? null : _normCertDer(so.cert);
|
|
200
122
|
var cert = keyOnly ? _keyOnlyCertStandIn(soSpki) : x509.parse(certDer);
|
|
201
123
|
var scheme = signScheme.resolveSignScheme(cert, so, opts.signedAttributes === false, _signE);
|
|
202
124
|
var sidv = keyOnly
|
|
203
|
-
? { sid: b.contextPrimitive(0, _keyOnlyKeyId(so)), version: 3 }
|
|
125
|
+
? { sid: b.contextPrimitive(0, _keyOnlyKeyId(so)), version: 3 }
|
|
204
126
|
: _buildSid(cert, opts.sid === "ski");
|
|
205
127
|
return { keyOnly: keyOnly, soKey: soKey, soSpki: soSpki, certDer: certDer, cert: cert,
|
|
206
128
|
scheme: scheme, sid: sidv.sid, version: sidv.version };
|
|
207
129
|
}
|
|
208
130
|
|
|
209
|
-
// Finish a SignerInfo (RFC 5652 sec. 5.3) from a resolved context: build the SignedAttributes (or
|
|
210
|
-
// sign the content directly), sign them, self-check the signature against the declared key, and
|
|
211
|
-
// assemble the SignerInfo. `md` is the message digest of the content under `rc.scheme.digest`,
|
|
212
|
-
// already computed -- over a buffered content by _digest, or over a streamed content's chunks by the
|
|
213
|
-
// engine's single-pass digest. `content` is read ONLY on the signedAttributes:false path, where the
|
|
214
|
-
// signature covers the content directly and there is no digest attribute.
|
|
215
131
|
async function _finishSignerInfo(rc, md, content, eContentType, opts) {
|
|
216
|
-
// await throughout, never a live Promise.resolve()/.then lookup: on the streamed path this runs after
|
|
217
|
-
// the content's iterator has driven the digest, so a replaced global Promise.resolve or a replaced
|
|
218
|
-
// Promise.prototype.then would otherwise let it disrupt the signing that completes here. await hooks
|
|
219
|
-
// onto a native promise through the internal job, not the caller-replaceable prototype method.
|
|
220
132
|
var toSign;
|
|
221
133
|
if (opts.signedAttributes === false) {
|
|
222
|
-
toSign = content;
|
|
134
|
+
toSign = content;
|
|
223
135
|
} else {
|
|
224
|
-
// Signed attributes (RFC 5652 sec. 5.3): content-type == eContentType, message-digest ==
|
|
225
|
-
// digest(content), and (by default) signing-time, plus any caller-supplied attribute.
|
|
226
136
|
var pairs = [
|
|
227
137
|
{ type: O("contentType"), values: [b.oid(eContentType)] },
|
|
228
138
|
{ type: O("messageDigest"), values: [b.octetString(md)] },
|
|
@@ -231,21 +141,17 @@ async function _finishSignerInfo(rc, md, content, eContentType, opts) {
|
|
|
231
141
|
pairs = intrinsic.concat(pairs, _resolveAttrPairs(opts.additionalSignedAttributes, "a signed attribute value"));
|
|
232
142
|
toSign = _buildSignedAttrs(pairs);
|
|
233
143
|
}
|
|
234
|
-
var signedBytes = toSign.setOf ? toSign.setOf : toSign;
|
|
144
|
+
var signedBytes = toSign.setOf ? toSign.setOf : toSign;
|
|
235
145
|
var sig = await signScheme.signOverTbs(rc.scheme, rc.soKey, signedBytes, _signE);
|
|
236
146
|
await _assertKeyMatchesSpki(rc.keyOnly, rc.soKey, rc.soSpki, rc.scheme, sig, signedBytes, rc.cert);
|
|
237
147
|
var fields = [b.integer(BigInt(rc.version)), rc.sid, rc.scheme.digestAlgId];
|
|
238
|
-
if (toSign.wire) intrinsic.push(fields, toSign.wire);
|
|
148
|
+
if (toSign.wire) intrinsic.push(fields, toSign.wire);
|
|
239
149
|
intrinsic.push(fields, rc.scheme.sigAlgId, b.octetString(sig));
|
|
240
150
|
var ua = _buildUnsignedAttrs(opts.unsignedAttributes);
|
|
241
|
-
if (ua) intrinsic.push(fields, ua);
|
|
242
|
-
// certDer is null for a key-only signer, and the certificates [0] embedding below filters it out --
|
|
243
|
-
// there is nothing to embed, which is the point.
|
|
151
|
+
if (ua) intrinsic.push(fields, ua);
|
|
244
152
|
return { si: b.sequence(fields), digestAlgId: rc.scheme.digestAlgId, version: rc.version, certDer: rc.certDer };
|
|
245
153
|
}
|
|
246
154
|
|
|
247
|
-
// Build one SignerInfo (RFC 5652 sec. 5.3) over a buffered content and sign it: resolve the context,
|
|
248
|
-
// digest the content (unless it is signed directly), then finish.
|
|
249
155
|
function _buildSignerInfo(signer, content, eContentType, opts) {
|
|
250
156
|
var rc = _resolveSignerContext(signer, opts);
|
|
251
157
|
var mdP = opts.signedAttributes === false ? Promise.resolve(null) : _digest(rc.scheme.digest, content);
|
|
@@ -254,35 +160,13 @@ function _buildSignerInfo(signer, content, eContentType, opts) {
|
|
|
254
160
|
|
|
255
161
|
|
|
256
162
|
|
|
257
|
-
// A SignerInfo names a public key -- as `spki` when the signer is key-only, and as
|
|
258
|
-
// the certificate it embeds otherwise -- while the signature comes from `key`. Two
|
|
259
|
-
// different keys make a well-formed SignerInfo nobody can verify: the recipient
|
|
260
|
-
// resolves the identifier to the declared key and checks a signature made by
|
|
261
|
-
// another. Neither form catches that by itself, so both are checked here.
|
|
262
|
-
//
|
|
263
|
-
// Proven from the SIGNATURE, not from the key. Deriving the public half and
|
|
264
|
-
// comparing it works only for key material this process can export, and the keys
|
|
265
|
-
// this signer serves are often exactly the ones that cannot be: a non-extractable
|
|
266
|
-
// CryptoKey, an HSM handle, or a composite `{ mldsa, trad }` pair that is two keys
|
|
267
|
-
// and not one. Every one of those can still be checked the direct way: the
|
|
268
|
-
// signature it just produced either verifies under the declared public key or it
|
|
269
|
-
// does not, so there is no key kind this has to take on trust.
|
|
270
|
-
//
|
|
271
|
-
// This is the same post-sign self-check every other signer in the toolkit runs,
|
|
272
|
-
// through the same shared helper. It costs one verification per SignerInfo, which
|
|
273
|
-
// is what a certificate, a CRL, a CSR and a CMP message have always paid here.
|
|
274
163
|
async function _assertKeyMatchesSpki(keyOnly, soKey, soSpki, scheme, sig, signedBytes, cert) {
|
|
275
164
|
var declared = keyOnly ? soSpki : (cert && cert.subjectPublicKeyInfo && cert.subjectPublicKeyInfo.bytes);
|
|
276
|
-
// A parsed certificate that does not retain its SPKI bytes cannot be checked this
|
|
277
|
-
// way. That is not a shape x509.parse produces, and if it ever were, refusing is
|
|
278
|
-
// the answer, never skipping, since a skip is the fail-open this prevents.
|
|
279
165
|
if (!declared) {
|
|
280
166
|
throw _signE("bad-input",
|
|
281
167
|
"a signer certificate did not surface its subjectPublicKeyInfo, so the signature it produced could " +
|
|
282
168
|
"not be checked against the key the SignerInfo declares");
|
|
283
169
|
}
|
|
284
|
-
// await, never Promise.resolve()/.then: reached from _finishSignerInfo after a streamed content's
|
|
285
|
-
// iterator ran, so a replaced global Promise.resolve would otherwise disrupt this self-check.
|
|
286
170
|
try {
|
|
287
171
|
await _b.assertSignatureVerifies(signedBytes, sig, declared, scheme);
|
|
288
172
|
} catch (e) {
|
|
@@ -293,35 +177,14 @@ async function _assertKeyMatchesSpki(keyOnly, soKey, soSpki, scheme, sig, signed
|
|
|
293
177
|
}
|
|
294
178
|
}
|
|
295
179
|
|
|
296
|
-
// A signing-time Time value: UTCTime before 2050, GeneralizedTime from 2050 (RFC 5652 sec. 11.3 /
|
|
297
|
-
// RFC 5280 sec. 4.1.2.5). A caller Date overrides; false omits the attribute (handled above). The
|
|
298
|
-
// DEFAULT instant is read from the captured Date intrinsic, never the live global: a streamed content's
|
|
299
|
-
// iterator runs during the digest, one turn before this attribute is built, and a replaced global Date
|
|
300
|
-
// would otherwise let it stamp the signature with an attacker-chosen instant (byte-identical, under a
|
|
301
|
-
// deterministic scheme, to signing with that instant explicitly).
|
|
302
180
|
function _timeValue(when) {
|
|
303
181
|
var d = guard.time.isDate(when) ? when : new intrinsic.Date();
|
|
304
182
|
return d.getUTCFullYear() < 2050 ? b.utcTime(d) : b.generalizedTime(d);
|
|
305
183
|
}
|
|
306
184
|
|
|
307
|
-
// Normalize a signer certificate input to its raw DER (DER Buffer / PEM string / Uint8Array).
|
|
308
|
-
// The same bytes drive scheme resolution and the certificates [0] embedding, so a parsed
|
|
309
|
-
// certificate (which does not retain its full DER) is rejected; pass DER or PEM.
|
|
310
|
-
// The stand-in a key-only signer resolves its signature scheme from: the parsed
|
|
311
|
-
// SubjectPublicKeyInfo of the key that will sign. Only `.subjectPublicKeyInfo` is
|
|
312
|
-
// read downstream, so this deliberately carries nothing else; a fuller fake
|
|
313
|
-
// would invite code to start trusting fields no certificate actually backs.
|
|
314
185
|
function _keyOnlyCertStandIn(spkiDer) {
|
|
315
186
|
var alg;
|
|
316
187
|
try {
|
|
317
|
-
// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }
|
|
318
|
-
// The whole structure, not just the field this function goes on to read. For an
|
|
319
|
-
// opaque key handle the derivation check downstream is deliberately skipped, so
|
|
320
|
-
// this is the only thing standing between a caller's bytes and a SignerInfo that
|
|
321
|
-
// declares them: a SEQUENCE carrying an algorithm and nothing else, or a third
|
|
322
|
-
// field, would be emitted as the signer's public key and resolve to nothing for
|
|
323
|
-
// anyone trying to verify. The key value is not interpreted, only that there
|
|
324
|
-
// is one, in the field the structure reserves for it.
|
|
325
188
|
var node = asn1.decode(spkiDer);
|
|
326
189
|
if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children ||
|
|
327
190
|
node.children.length !== 2) {
|
|
@@ -333,44 +196,29 @@ function _keyOnlyCertStandIn(spkiDer) {
|
|
|
333
196
|
throw _err("cms/bad-input",
|
|
334
197
|
"a key-only signer's spki subjectPublicKey must be a BIT STRING (RFC 5280 sec. 4.1.2.7)");
|
|
335
198
|
}
|
|
336
|
-
asn1.read.bitString(keyNode);
|
|
199
|
+
asn1.read.bitString(keyNode);
|
|
337
200
|
var algNode = node.children[0];
|
|
338
201
|
if (algNode.tagClass !== "universal" || algNode.tagNumber !== asn1.TAGS.SEQUENCE ||
|
|
339
202
|
!algNode.children || !algNode.children.length || algNode.children.length > 2) {
|
|
340
203
|
throw _err("cms/bad-input",
|
|
341
204
|
"a key-only signer's spki algorithm is SEQUENCE { algorithm OID, parameters OPTIONAL } (RFC 5280 sec. 4.1.1.2)");
|
|
342
205
|
}
|
|
343
|
-
// The same shape schema-x509 surfaces: the algorithm OID plus its parameters
|
|
344
|
-
// as the raw TLV (or null when absent); the resolver re-decodes those bytes
|
|
345
|
-
// for an EC named curve and for the RSASSA-PSS hash pinning, so handing it a
|
|
346
|
-
// different representation here would break exactly those two algorithms.
|
|
347
206
|
alg = {
|
|
348
207
|
oid: asn1.read.oid(algNode.children[0]),
|
|
349
208
|
parameters: algNode.children[1] ? algNode.children[1].bytes : null,
|
|
350
209
|
};
|
|
351
210
|
} catch (e) {
|
|
352
|
-
// The shape refusals above already name what is wrong; only a decode failure
|
|
353
|
-
// needs the generic message, so a precise verdict is not overwritten by it.
|
|
354
211
|
if (e && typeof e.code === "string" && e.code.indexOf("cms/") === 0) throw e;
|
|
355
212
|
throw _err("cms/bad-input", "a key-only signer's spki is not a SubjectPublicKeyInfo", e);
|
|
356
213
|
}
|
|
357
214
|
return { subjectPublicKeyInfo: { algorithm: alg } };
|
|
358
215
|
}
|
|
359
216
|
|
|
360
|
-
// The key identifier a key-only signer's sid carries. Required: without it there
|
|
361
|
-
// is no way for a verifier to find the key, and RFC 5272 sec. 3.2 makes the value
|
|
362
|
-
// the one the certification request declares.
|
|
363
217
|
function _keyOnlyKeyId(so) {
|
|
364
218
|
if (so.keyIdentifier == null) {
|
|
365
219
|
throw _err("cms/bad-input",
|
|
366
220
|
"a key-only signer requires keyIdentifier -- the subjectKeyIdentifier the certification request declares (RFC 5272 sec. 3.2)");
|
|
367
221
|
}
|
|
368
|
-
// A key identifier is bytes, so the type is checked and never coerced. Buffer.from
|
|
369
|
-
// accepts far more than it should mean here: Buffer.from(20) allocates twenty zero
|
|
370
|
-
// octets and Buffer.from("a1b2") takes the ASCII of the text, not the two
|
|
371
|
-
// octets a reader means by it. Either would emit a structurally valid
|
|
372
|
-
// SignerIdentifier carrying an identifier the caller never asked for, which no
|
|
373
|
-
// verifier can match back to the certification request (RFC 5272 sec. 3.2).
|
|
374
222
|
var id = guard.bytes.view(so.keyIdentifier, CmsError, "cms/bad-input", "a key-only signer's keyIdentifier");
|
|
375
223
|
if (!id.length) throw _err("cms/bad-input", "a key-only signer's keyIdentifier must not be empty");
|
|
376
224
|
return id;
|
|
@@ -380,7 +228,7 @@ function _normCertDer(c) {
|
|
|
380
228
|
if (c == null) throw _err("cms/bad-input", "each signer requires a certificate (cert)");
|
|
381
229
|
if (guard.bytes.isByteSource(c)) {
|
|
382
230
|
c = guard.bytes.snapshotSource(c, CmsError, "cms/bad-input", "a signer certificate");
|
|
383
|
-
return c[0] === 0x30 ? c : _pemToDer(c.toString("latin1"));
|
|
231
|
+
return c[0] === 0x30 ? c : _pemToDer(c.toString("latin1"));
|
|
384
232
|
}
|
|
385
233
|
if (typeof c === "string") return _pemToDer(c);
|
|
386
234
|
throw _err("cms/bad-input", "a signer certificate must be a DER Buffer or a PEM string");
|
|
@@ -392,45 +240,22 @@ function _pemToDer(text) {
|
|
|
392
240
|
}
|
|
393
241
|
|
|
394
242
|
// pki.cms.sign -- documented by the @primitive block in cms-verify.js (the @module pki.cms home).
|
|
395
|
-
// Documented `-> Promise`, so a fault leaves as a REJECTION (guard-async). The stream/buffer routing
|
|
396
|
-
// classifies `content` at the door, which reads `content[Symbol.asyncIterator]` -- a caller accessor
|
|
397
|
-
// that can throw -- so the whole dispatch runs inside the rejection boundary rather than fixedCall's
|
|
398
|
-
// alone, which the classification precedes. The checks the impls run stay synchronous because they
|
|
399
|
-
// read the caller's mutable content and signer list.
|
|
400
243
|
function sign(content, signers, opts) {
|
|
401
244
|
return guard.async.deferred(function () { return _signDispatch(content, signers, opts); });
|
|
402
245
|
}
|
|
403
246
|
|
|
404
247
|
function _signDispatch(content, signers, opts) {
|
|
405
|
-
// The signer list and options are copied FIRST, before the content is classified. Classifying reads
|
|
406
|
-
// content[Symbol.asyncIterator] -- a caller accessor that runs in-realm -- and a probe run before the
|
|
407
|
-
// copy could mutate the still-uncopied signers/opts, or replace the Array.prototype method the copy
|
|
408
|
-
// uses, and steer the emitted signature. With the copies taken first, the probe cannot reach them.
|
|
409
248
|
return guard.bytes.fixedCall(CmsError, "cms/bad-input", [
|
|
410
249
|
[signers, "the signer list"], [opts, "pki.cms.sign options"],
|
|
411
250
|
], function (copiedSigners, copiedOpts) {
|
|
412
|
-
// A streaming content is classified by guard.bytes.asyncStreamOf, which reads Symbol.asyncIterator a
|
|
413
|
-
// single time and returns a LAZY stream (its underlying iterator is acquired only when the digest
|
|
414
|
-
// engine drives it, so a pre-hash rejection never opens a resource to leak). The stream, consumed
|
|
415
|
-
// exactly once, is passed through; it CANNOT be deep-copied itself. A byte source (or any
|
|
416
|
-
// non-iterable) yields null here and takes the buffered path.
|
|
417
251
|
var stream = guard.bytes.asyncStreamOf(content);
|
|
418
252
|
if (stream) return _signStream(stream, copiedSigners, copiedOpts);
|
|
419
|
-
// Buffered: the content byte source is copied too, so the value that decides the attribute-shaped-
|
|
420
|
-
// content refusal and the value that gets signed are the same read.
|
|
421
253
|
return guard.bytes.fixedCall(CmsError, "cms/bad-input", [
|
|
422
254
|
[content, "content"],
|
|
423
255
|
], function (copiedContent) { return _sign(copiedContent, copiedSigners, copiedOpts); });
|
|
424
256
|
});
|
|
425
257
|
}
|
|
426
258
|
|
|
427
|
-
// Sign a streamed content (an async iterable of byte chunks) as a DETACHED SignedData. The payload
|
|
428
|
-
// is hashed once, incrementally, under every distinct SignerInfo digest algorithm (RFC 5652
|
|
429
|
-
// sec. 5.4), so an arbitrarily large content is never held whole and a single-use stream serves
|
|
430
|
-
// every signer in one pass. Detached is the only conformant streamed form: an attached SignedData
|
|
431
|
-
// embeds the content as a definite-length OCTET STRING, which needs the whole content buffered to
|
|
432
|
-
// state its length, and signed attributes are required (a stream cannot be signed directly, and the
|
|
433
|
-
// message-digest attribute is exactly what the streamed hash produces).
|
|
434
259
|
async function _signStream(stream, signers, opts) {
|
|
435
260
|
opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.sign options");
|
|
436
261
|
guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
|
|
@@ -451,32 +276,18 @@ async function _signStream(stream, signers, opts) {
|
|
|
451
276
|
"a key-only signer must be the ONLY SignerInfo in a Full PKI Request (RFC 5272 sec. 3.2)");
|
|
452
277
|
}
|
|
453
278
|
if (opts.signingTime != null && opts.signingTime !== false) guard.time.assertValid(opts.signingTime, _err, "cms/bad-input", "signingTime");
|
|
454
|
-
// Resolve every signer's context first (no stream consumed yet): this yields each SignerInfo's
|
|
455
|
-
// digest algorithm, so the stream can be hashed once under the distinct set.
|
|
456
279
|
var rcs = intrinsic.map(list, function (s) { return _resolveSignerContext(s, opts); });
|
|
457
280
|
var digestNames = [], seenDigest = {};
|
|
458
281
|
intrinsic.forEach(rcs, function (r) { if (!seenDigest[r.scheme.digest]) { seenDigest[r.scheme.digest] = 1; intrinsic.push(digestNames, r.scheme.digest); } });
|
|
459
282
|
var wcNames = intrinsic.map(digestNames, function (n) { return DIGEST_HASH[n]; });
|
|
460
|
-
// `await`, not `.then`: the streamed content's iterator runs during digestStream and could replace
|
|
461
|
-
// Promise.prototype.then (or Promise.all, whose spec-internal element handling dispatches through it)
|
|
462
|
-
// to drop SignerInfos from the emitted DER or bypass the non-empty-signer check. await and a captured
|
|
463
|
-
// await-each aggregation dispatch through no such caller-replaceable method.
|
|
464
283
|
var digests;
|
|
465
284
|
try {
|
|
466
285
|
digests = await subtle.digestStream(wcNames, stream);
|
|
467
286
|
} catch (e) {
|
|
468
|
-
// The engine reports malformed streamed content in its own domain; report it in this verb's via the
|
|
469
|
-
// shared guard, rather than leaking the engine code. The digest algorithm names are this module's own,
|
|
470
|
-
// so a webcrypto/syntax here is never an algorithm fault.
|
|
471
287
|
guard.bytes.translateStreamError(e, _err, "cms/bad-input");
|
|
472
288
|
}
|
|
473
289
|
var byName = {};
|
|
474
290
|
intrinsic.forEach(digestNames, function (n, i) { byName[n] = intrinsic.bufferFrom(digests[i]); });
|
|
475
|
-
// Build each SignerInfo sequentially -- START it only as it is awaited -- rather than starting them
|
|
476
|
-
// all and awaiting each: awaiting is the capture-safe combinator (no Promise.all/.then the streamed
|
|
477
|
-
// content could replace), but starting all first would leave a later signer's rejection unobserved
|
|
478
|
-
// if an earlier one rejects, which Node can escalate to an unhandled-rejection crash. Per-signer work
|
|
479
|
-
// is small, so the sequential cost is negligible.
|
|
480
291
|
var built = [];
|
|
481
292
|
for (var _ip = 0; _ip < rcs.length; _ip++) {
|
|
482
293
|
intrinsic.push(built, await _finishSignerInfo(rcs[_ip], byName[rcs[_ip].scheme.digest], null, eContentType, opts));
|
|
@@ -487,115 +298,64 @@ async function _signStream(stream, signers, opts) {
|
|
|
487
298
|
function _sign(content, signers, opts) {
|
|
488
299
|
opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.sign options");
|
|
489
300
|
guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "cms/bad-input", "unknown opts field ");
|
|
490
|
-
// The arguments were copied at entry (see `sign` above), which is what makes the refusal below
|
|
491
|
-
// hold: flipping signedAttributes from true to false after the call returns would otherwise skip
|
|
492
|
-
// the attribute-shaped-content check while the signer signs that content directly, which is the
|
|
493
|
-
// very signature the stripping attack needs.
|
|
494
301
|
var contentBuf = _toBuf(content, "content");
|
|
495
302
|
var list = Array.isArray(signers) ? _b.reqDenseArray(signers, "the signer list") : [signers];
|
|
496
303
|
if (!list.length) throw _err("cms/bad-input", "pki.cms.sign requires at least one signer");
|
|
497
304
|
var eContentType = opts.eContentType ? O(opts.eContentType) : OID_DATA;
|
|
498
|
-
// RFC 5272 sec. 3.2: "If the request key is used for signing, there MUST be only
|
|
499
|
-
// one SignerInfo in the SignedData." That clause governs a Full PKI Request, so
|
|
500
|
-
// it binds HERE only for that content type. CMS itself is happy with several
|
|
501
|
-
// SignerInfos, and a key-only signer's certificate can reach a verifier by other
|
|
502
|
-
// means -- applying the CMC rule to every content type would refuse ordinary
|
|
503
|
-
// SignedData that nothing objects to. pki.cmc.build enforces the same rule on
|
|
504
|
-
// the message it assembles, which is where the clause is really about.
|
|
505
305
|
if (eContentType === OID_PKI_DATA && list.length > 1 &&
|
|
506
306
|
list.some(function (s) { return s && s.cert == null && s.spki != null; })) {
|
|
507
307
|
throw _err("cms/bad-input",
|
|
508
308
|
"a key-only signer must be the ONLY SignerInfo in a Full PKI Request (RFC 5272 sec. 3.2)");
|
|
509
309
|
}
|
|
510
|
-
// RFC 5652 sec. 5.3: signed attributes MUST be present (carrying a content-type attribute)
|
|
511
|
-
// whenever the encapsulated content type is not id-data, so signedAttributes:false is only
|
|
512
|
-
// valid for id-data content. Refusing it here keeps cms.sign from emitting a non-conformant
|
|
513
|
-
// SignedData (e.g. a timestamp token, id-ct-TSTInfo, with no signed attributes).
|
|
514
310
|
if (opts.signedAttributes === false && eContentType !== OID_DATA) {
|
|
515
311
|
throw _err("cms/bad-input", "signed attributes are required when eContentType is not id-data (RFC 5652 sec. 5.3)");
|
|
516
312
|
}
|
|
517
|
-
// The signer's half of the signed-attribute stripping problem
|
|
518
|
-
// (draft-vangeest-lamps-cms-euf-cma-signeddata, Attack Type 2). Signing attribute-shaped content
|
|
519
|
-
// without attributes produces a signature that can afterwards be promoted into an
|
|
520
|
-
// attributes-present message, because the signature does not commit to which mode was used: the
|
|
521
|
-
// attacker attaches the signed bytes as the SignedAttributes and swaps in whatever content their
|
|
522
|
-
// message-digest attribute names. Refusing to mint the ambiguous signature is the only point at
|
|
523
|
-
// which this direction can be stopped -- by the time it is a message, the damage is done.
|
|
524
313
|
if (opts.signedAttributes === false && cms.looksLikeSignedAttributes(contentBuf)) {
|
|
525
314
|
throw _err("cms/ambiguous-content", "this content is itself an encoded SignedAttributes block, so signing " +
|
|
526
315
|
"it WITHOUT signed attributes would produce a signature that could be re-presented as one over " +
|
|
527
316
|
"attributes (RFC 5652 sec. 5.4); sign it with signed attributes instead");
|
|
528
317
|
}
|
|
529
|
-
// A supplied signing-time MUST be a valid Date (or false to omit the attribute), never a
|
|
530
|
-
// silently-ignored non-Date or an Invalid Date that would encode a garbage Time.
|
|
531
318
|
if (opts.signingTime != null && opts.signingTime !== false) guard.time.assertValid(opts.signingTime, _err, "cms/bad-input", "signingTime");
|
|
532
319
|
|
|
533
320
|
return Promise.all(list.map(function (s) { return _buildSignerInfo(s, contentBuf, eContentType, opts); }))
|
|
534
321
|
.then(function (built) { return _assembleSignedData(built, eContentType, opts, contentBuf); });
|
|
535
322
|
}
|
|
536
323
|
|
|
537
|
-
// Assemble the SignedData ContentInfo (RFC 5652 sec. 5.1) from the built SignerInfos: the deduped
|
|
538
|
-
// digestAlgorithms set, the coupled CMSVersion, the EncapsulatedContentInfo (eContent omitted when
|
|
539
|
-
// detached, the only form a streamed content takes), the deduped certificates [0], and the
|
|
540
|
-
// signerInfos SET OF. `contentBuf` is the content bytes for the attached form, or null when detached.
|
|
541
324
|
function _assembleSignedData(built, eContentType, opts, contentBuf) {
|
|
542
|
-
// digestAlgorithms: the distinct SignerInfo digestAlgorithm AlgorithmIdentifiers, deduped. Captured
|
|
543
|
-
// enumeration throughout: this assembly runs after a streamed content's iterator may have replaced
|
|
544
|
-
// Array.prototype.map / forEach / Buffer.prototype.toString, and a live one could drop SignerInfos
|
|
545
|
-
// from the emitted CMS or mis-dedup the algorithm / certificate sets.
|
|
546
325
|
var seen = {}, digestAlgs = [];
|
|
547
326
|
intrinsic.forEach(built, function (x) { var k = intrinsic.bufToString(x.digestAlgId, "hex"); if (!seen[k]) { seen[k] = 1; intrinsic.push(digestAlgs, x.digestAlgId); } });
|
|
548
|
-
// CMSVersion (RFC 5652 sec. 5.1): 3 if any SignerInfo is v3 (ski) or eContentType != id-data;
|
|
549
|
-
// otherwise 1 (v1 emits only X.509 certificates, so the v4/v5 attribute-certificate cases
|
|
550
|
-
// do not arise).
|
|
551
327
|
var v3 = intrinsic.some(built, function (x) { return x.version === 3; }) || eContentType !== OID_DATA;
|
|
552
328
|
var version = v3 ? 3 : 1;
|
|
553
|
-
// EncapsulatedContentInfo: eContentType + [0] EXPLICIT eContent (omitted when detached).
|
|
554
329
|
var encapFields = [b.oid(eContentType)];
|
|
555
330
|
if (!opts.detached) intrinsic.push(encapFields, b.explicit(0, b.octetString(contentBuf)));
|
|
556
331
|
var encap = b.sequence(encapFields);
|
|
557
|
-
// certificates [0] IMPLICIT SET OF (the signer certs), deduped + SET-OF-ordered, when embedded.
|
|
558
332
|
var sdFields = [b.integer(BigInt(version)), b.set(digestAlgs), encap];
|
|
559
333
|
if (opts.certificates !== false) {
|
|
560
|
-
// A key-only signer contributes no certificate (certDer is null), so the
|
|
561
|
-
// set can end up EMPTY -- and an empty certificates [0] is not the same as
|
|
562
|
-
// an absent one, so the field is omitted entirely in that case.
|
|
563
334
|
var certDers = intrinsic.sort(_dedupe(intrinsic.filter(intrinsic.map(built, function (x) { return x.certDer; }),
|
|
564
|
-
function (d) { return d != null; })), intrinsic.compare);
|
|
565
|
-
if (certDers.length) intrinsic.push(sdFields, b.contextConstructed(0, intrinsic.bufferConcat(certDers)));
|
|
335
|
+
function (d) { return d != null; })), intrinsic.compare);
|
|
336
|
+
if (certDers.length) intrinsic.push(sdFields, b.contextConstructed(0, intrinsic.bufferConcat(certDers)));
|
|
566
337
|
}
|
|
567
|
-
intrinsic.push(sdFields, b.set(intrinsic.map(built, function (x) { return x.si; })));
|
|
338
|
+
intrinsic.push(sdFields, b.set(intrinsic.map(built, function (x) { return x.si; })));
|
|
568
339
|
var signedData = b.sequence(sdFields);
|
|
569
|
-
var contentInfo = b.sequence([b.oid(OID_SIGNED_DATA), b.explicit(0, signedData)]);
|
|
340
|
+
var contentInfo = b.sequence([b.oid(OID_SIGNED_DATA), b.explicit(0, signedData)]);
|
|
570
341
|
return opts.pem ? pkix.pemEncode(contentInfo, "CMS", frameworkError.PemError) : contentInfo;
|
|
571
342
|
}
|
|
572
343
|
|
|
573
|
-
// Dedupe certificate DERs (two signers may share a cert, so embed it once).
|
|
574
344
|
function _dedupe(ders) {
|
|
575
345
|
var seen = {}, out = [];
|
|
576
346
|
intrinsic.forEach(ders, function (d) { var k = intrinsic.bufToString(d, "hex"); if (!seen[k]) { seen[k] = 1; intrinsic.push(out, d); } });
|
|
577
347
|
return out;
|
|
578
348
|
}
|
|
579
349
|
|
|
580
|
-
// ---- pki.cms.certsOnly (RFC 8551 sec. 3.8 / RFC 5652 sec. 5.1) --------------
|
|
581
|
-
// A certificate-management ("certs-only") message: a degenerate SignedData that conveys
|
|
582
350
|
// certificates and/or CRLs and signs nothing. Documented by the @primitive block in cms-verify.js
|
|
583
351
|
// (the @module pki.cms home).
|
|
584
352
|
var KNOWN_CERTS_ONLY_OPTS = { crls: 1, pem: 1 };
|
|
585
353
|
|
|
586
|
-
// A single entity or an array of them -> a dense array (empty when the argument is nullish).
|
|
587
|
-
// reqDenseArray snapshots the array structure and refuses a sparse hole or a nullish entry
|
|
588
|
-
// (M15), so a caller array that mutates under iteration cannot reintroduce a hole.
|
|
589
354
|
function _certsOnlyList(v, what) {
|
|
590
355
|
if (v == null) return [];
|
|
591
356
|
return intrinsic.isArray(v) ? _b.reqDenseArray(v, what) : [v];
|
|
592
357
|
}
|
|
593
358
|
|
|
594
|
-
// Normalize one caller-supplied certificate/CRL to validated raw DER: snapshot the byte source at
|
|
595
|
-
// the door so a later mutation cannot swap the bytes (M15), accept a PEM block, then parse it with
|
|
596
|
-
// `parseFn` so a non-<thing> -- including a tagged CertificateChoices / RevocationInfoChoice
|
|
597
|
-
// alternative, whose first byte is a context tag rather than 0x30 -- is a typed cms/bad-input and is
|
|
598
|
-
// never embedded (M9/M10/M11). `pemLabel` is the PEM armor type.
|
|
599
359
|
function _normEntityDer(v, what, pemLabel, parseFn) {
|
|
600
360
|
var der;
|
|
601
361
|
if (guard.bytes.isByteSource(v)) {
|
|
@@ -624,19 +384,14 @@ function certsOnly(certs, opts) {
|
|
|
624
384
|
guard.identifier.assertKnownKeys(opts, KNOWN_CERTS_ONLY_OPTS, _err, "cms/bad-input", "unknown opts field ");
|
|
625
385
|
var certList = _certsOnlyList(certs, "certificates");
|
|
626
386
|
var crlList = _certsOnlyList(opts.crls, "crls");
|
|
627
|
-
// RFC 8551 sec. 3.8 conveys "certificates and/or CRLs"; a message with neither conveys nothing.
|
|
628
387
|
if (!certList.length && !crlList.length) throw _err("cms/bad-input", "a certs-only message must carry at least one certificate or CRL (RFC 8551 sec. 3.8)");
|
|
629
|
-
// Each entity validated + snapshotted at the door, then DER-sorted + deduped (X.690 sec. 11.6).
|
|
630
388
|
var certDers = intrinsic.sort(_dedupe(intrinsic.map(certList, function (c) { return _normEntityDer(c, "a certificate", "CERTIFICATE", _parseX509); })), intrinsic.compare);
|
|
631
389
|
var crlDers = intrinsic.sort(_dedupe(intrinsic.map(crlList, function (c) { return _normEntityDer(c, "a CRL", "X509 CRL", _parseCrl); })), intrinsic.compare);
|
|
632
|
-
// The degenerate SignedData (RFC 5652 sec. 5.1): version 1 (all choices plain, id-data, no signer),
|
|
633
|
-
// an empty digestAlgorithms + signerInfos, an id-data encapContentInfo with eContent ABSENT, and the
|
|
634
|
-
// caller certificates [0] / crls [1]. The strict re-parse enforces the version<->contents coupling.
|
|
635
390
|
var encap = b.sequence([b.oid(OID_DATA)]);
|
|
636
391
|
var sdFields = [b.integer(1n), b.set([]), encap];
|
|
637
|
-
if (certDers.length) intrinsic.push(sdFields, b.contextConstructed(0, intrinsic.bufferConcat(certDers)));
|
|
638
|
-
if (crlDers.length) intrinsic.push(sdFields, b.contextConstructed(1, intrinsic.bufferConcat(crlDers)));
|
|
639
|
-
intrinsic.push(sdFields, b.set([]));
|
|
392
|
+
if (certDers.length) intrinsic.push(sdFields, b.contextConstructed(0, intrinsic.bufferConcat(certDers)));
|
|
393
|
+
if (crlDers.length) intrinsic.push(sdFields, b.contextConstructed(1, intrinsic.bufferConcat(crlDers)));
|
|
394
|
+
intrinsic.push(sdFields, b.set([]));
|
|
640
395
|
var signedData = b.sequence(sdFields);
|
|
641
396
|
var contentInfo = b.sequence([b.oid(OID_SIGNED_DATA), b.explicit(0, signedData)]);
|
|
642
397
|
return opts.pem ? pkix.pemEncode(contentInfo, "CMS", frameworkError.PemError) : contentInfo;
|
|
@@ -647,15 +402,7 @@ function _toBuf(v, what) {
|
|
|
647
402
|
throw _err("cms/bad-input", what + " must be a Buffer");
|
|
648
403
|
}
|
|
649
404
|
|
|
650
|
-
// ---- pki.cms.countersign (RFC 5652 sec. 11.4) ------------------------------
|
|
651
|
-
// A countersignature is a SignerInfo (Countersignature ::= SignerInfo) over the contents of the
|
|
652
|
-
// countersigned SignerInfo's signature OCTET STRING, never the eContent, attached as the
|
|
653
|
-
// id-countersignature unsigned attribute. It reuses the whole build+sign flow (resolveSignScheme /
|
|
654
|
-
// _buildSid / _buildSignedAttrs / signOverTbs); only the preimage (the target signature octets) and
|
|
655
|
-
// the omitted content-type are the deltas, and the orchestrator splices the [1] unsignedAttrs into
|
|
656
|
-
// an existing SignedData while preserving the targeted SignerInfo's signed bytes BYTE-FOR-BYTE.
|
|
657
405
|
|
|
658
|
-
// Resolve opts.signerIndex (a number, an array of numbers, or "all"; default 0) to primary indices.
|
|
659
406
|
function _resolveSignerIndices(spec, n) {
|
|
660
407
|
if (spec == null) { if (n < 1) throw _err("cms/bad-input", "the SignedData carries no SignerInfo to countersign"); return [0]; }
|
|
661
408
|
if (spec === "all") { var all = []; for (var i = 0; i < n; i++) all.push(i); return all; }
|
|
@@ -665,18 +412,15 @@ function _resolveSignerIndices(spec, n) {
|
|
|
665
412
|
return arr;
|
|
666
413
|
}
|
|
667
414
|
|
|
668
|
-
// Build one countersignature value (RFC 5652 sec. 11.4) over `targetSigOctets`: message-digest bound
|
|
669
|
-
// to digest(targetSigOctets) under the countersignature's own digestAlgorithm, content-type omitted,
|
|
670
|
-
// signed through the same per-algorithm scheme machinery as a top-level signer.
|
|
671
415
|
function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
672
416
|
var so = countersigner || {};
|
|
673
417
|
var certDer = _normCertDer(so.cert);
|
|
674
418
|
var cert = x509.parse(certDer);
|
|
675
419
|
var scheme = signScheme.resolveSignScheme(cert, so, opts.signedAttributes === false, _signE);
|
|
676
420
|
var sidv = _buildSid(cert, opts.sid === "ski");
|
|
677
|
-
var soKey = so.key;
|
|
421
|
+
var soKey = so.key;
|
|
678
422
|
return Promise.resolve().then(function () {
|
|
679
|
-
if (opts.signedAttributes === false) return null;
|
|
423
|
+
if (opts.signedAttributes === false) return null;
|
|
680
424
|
return _digest(scheme.digest, targetSigOctets).then(function (md) {
|
|
681
425
|
var pairs = [{ type: O("messageDigest"), values: [b.octetString(md)] }];
|
|
682
426
|
if (opts.signingTime !== false) pairs.push({ type: O("signingTime"), values: [_timeValue(opts.signingTime)] });
|
|
@@ -687,16 +431,13 @@ function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
|
687
431
|
}).then(function (attrs) {
|
|
688
432
|
return signScheme.signOverTbs(scheme, soKey, attrs ? attrs.setOf : targetSigOctets, _signE).then(function (sig) {
|
|
689
433
|
var fields = [b.integer(BigInt(sidv.version)), sidv.sid, scheme.digestAlgId];
|
|
690
|
-
if (attrs) fields.push(attrs.wire);
|
|
434
|
+
if (attrs) fields.push(attrs.wire);
|
|
691
435
|
fields.push(scheme.sigAlgId, b.octetString(sig));
|
|
692
436
|
return { value: b.sequence(fields), certDer: certDer };
|
|
693
437
|
});
|
|
694
438
|
});
|
|
695
439
|
}
|
|
696
440
|
|
|
697
|
-
// Build the [1] IMPLICIT unsignedAttrs bytes for a SignerInfo, merging `newCsValues` into the single
|
|
698
|
-
// id-countersignature attribute (RFC 5652 sec. 11: one instance per type, multiple values), and
|
|
699
|
-
// keeping every other unsigned attribute and every existing countersignature value verbatim.
|
|
700
441
|
function _mergeCountersig(uaNode, newCsValues) {
|
|
701
442
|
var CS = O("countersignature");
|
|
702
443
|
var others = [], csValues = [];
|
|
@@ -707,13 +448,10 @@ function _mergeCountersig(uaNode, newCsValues) {
|
|
|
707
448
|
newCsValues.forEach(function (v) { csValues.push(v); });
|
|
708
449
|
var csAttr = b.sequence([b.oid(CS), b.set(csValues)]);
|
|
709
450
|
var setOf = b.set(others.concat([csAttr]));
|
|
710
|
-
var wire = Buffer.from(setOf); wire[0] = 0xA1;
|
|
451
|
+
var wire = Buffer.from(setOf); wire[0] = 0xA1;
|
|
711
452
|
return wire;
|
|
712
453
|
}
|
|
713
454
|
|
|
714
|
-
// Append countersignature values to a SignerInfo node, preserving version / sid / digestAlgorithm /
|
|
715
|
-
// signedAttrs / signatureAlgorithm / signature BYTE-FOR-BYTE (the signed preimage), only adding the
|
|
716
|
-
// [1] unsignedAttrs. Returns the new SignerInfo bytes.
|
|
717
455
|
function _appendCountersigs(siNode, newCsValues) {
|
|
718
456
|
var kids = siNode.children;
|
|
719
457
|
var last = kids[kids.length - 1];
|
|
@@ -723,8 +461,6 @@ function _appendCountersigs(siNode, newCsValues) {
|
|
|
723
461
|
return b.sequence(base);
|
|
724
462
|
}
|
|
725
463
|
|
|
726
|
-
// Splice countersignature values into the j-th countersignature VALUE of a SignerInfo node (nested
|
|
727
|
-
// countersignature, RFC 5652 sec. 11.4). Returns the new SignerInfo bytes.
|
|
728
464
|
function _spliceNested(siNode, j, newCsValues) {
|
|
729
465
|
var kids = siNode.children;
|
|
730
466
|
var last = kids[kids.length - 1];
|
|
@@ -745,8 +481,6 @@ function _spliceNested(siNode, j, newCsValues) {
|
|
|
745
481
|
return b.sequence(base);
|
|
746
482
|
}
|
|
747
483
|
|
|
748
|
-
// The signature-value octets of a SignerInfo node: the last universal OCTET STRING, i.e. the last
|
|
749
|
-
// child unless a [1] IMPLICIT unsignedAttrs (a prior countersignature) follows it.
|
|
750
484
|
function _signatureOctets(siNode) {
|
|
751
485
|
var kids = siNode.children;
|
|
752
486
|
var last = kids[kids.length - 1];
|
|
@@ -754,8 +488,6 @@ function _signatureOctets(siNode) {
|
|
|
754
488
|
return asn1.read.octetString(sigNode);
|
|
755
489
|
}
|
|
756
490
|
|
|
757
|
-
// The sec. 11.4 preimage for a target: the primary SignerInfo's signature octets, or (nested) the
|
|
758
|
-
// j-th countersignature value's signature octets.
|
|
759
491
|
function _targetPreimage(siNode, opts) {
|
|
760
492
|
if (opts.countersignatureOf == null) return _signatureOctets(siNode);
|
|
761
493
|
var last = siNode.children[siNode.children.length - 1];
|
|
@@ -770,11 +502,7 @@ function _targetPreimage(siNode, opts) {
|
|
|
770
502
|
}
|
|
771
503
|
|
|
772
504
|
// pki.cms.countersign -- documented by the @primitive block in cms-verify.js (the @module pki.cms home).
|
|
773
|
-
// Documented `-> Promise`, so a fault leaves as a rejection (guard-async).
|
|
774
505
|
function countersign(cmsInput, signers, opts) {
|
|
775
|
-
// Every caller-owned argument copied at entry and released when the call settles; see the note
|
|
776
|
-
// on the same call in x509-sign. `signerIndex` and `countersignatureOf` select which signature is
|
|
777
|
-
// countersigned, so a late read could attach the countersignature to a different one.
|
|
778
506
|
return guard.bytes.fixedCall(CmsError, "cms/bad-input", [
|
|
779
507
|
[cmsInput, "the CMS message"], [signers, "the signer list"], [opts, "pki.cms.countersign options"],
|
|
780
508
|
], _countersign);
|
|
@@ -787,8 +515,6 @@ function _countersign(cmsInput, signers, opts) {
|
|
|
787
515
|
if (!list.length) throw _err("cms/bad-input", "pki.cms.countersign requires at least one countersigner");
|
|
788
516
|
if (opts.signingTime != null && opts.signingTime !== false) guard.time.assertValid(opts.signingTime, _err, "cms/bad-input", "signingTime");
|
|
789
517
|
var der = pkix.coerceToDer(cmsInput, { pemLabel: null, PemError: frameworkError.PemError, ErrorClass: CmsError, prefix: "cms" });
|
|
790
|
-
// cms.parse throws a typed cms/* error on a malformed input (mirroring verify's own parse call); a
|
|
791
|
-
// structurally-valid but non-SignedData CMS (an EnvelopedData) parses without a signerInfos array.
|
|
792
518
|
var parsed = cms.parse(der);
|
|
793
519
|
if (!Array.isArray(parsed.signerInfos)) throw _err("cms/bad-input", "pki.cms.countersign input is not a CMS SignedData");
|
|
794
520
|
var targets = _resolveSignerIndices(opts.signerIndex, parsed.signerInfos.length);
|
|
@@ -797,7 +523,6 @@ function _countersign(cmsInput, signers, opts) {
|
|
|
797
523
|
var sdKids = sd.children;
|
|
798
524
|
var siSet = sdKids[sdKids.length - 1];
|
|
799
525
|
|
|
800
|
-
// Build every countersignature value (target x countersigner), then splice into the node tree.
|
|
801
526
|
var jobs = [];
|
|
802
527
|
targets.forEach(function (t) {
|
|
803
528
|
var preimage = _targetPreimage(siSet.children[t], opts);
|
|
@@ -812,8 +537,6 @@ function _countersign(cmsInput, signers, opts) {
|
|
|
812
537
|
return opts.countersignatureOf == null ? _appendCountersigs(siNode, byTarget[idx]) : _spliceNested(siNode, opts.countersignatureOf, byTarget[idx]);
|
|
813
538
|
}));
|
|
814
539
|
|
|
815
|
-
// Rebuild SignedData: version, digestAlgorithms (unchanged, since a countersignature digest is not a
|
|
816
|
-
// SignedData digestAlgorithm), encapContentInfo, certificates [0]?, crls [1]?, the new signerInfos.
|
|
817
540
|
var certsNode = null, crlsNode = null;
|
|
818
541
|
for (var i = 3; i < sdKids.length - 1; i++) {
|
|
819
542
|
if (sdKids[i].tagClass === "context" && sdKids[i].tagNumber === 0) certsNode = sdKids[i];
|
|
@@ -822,9 +545,9 @@ function _countersign(cmsInput, signers, opts) {
|
|
|
822
545
|
var existing = [];
|
|
823
546
|
if (certsNode) certsNode.children.forEach(function (c) { existing.push(c.bytes); });
|
|
824
547
|
if (opts.certificates !== false) certDers.forEach(function (d) { existing.push(d); });
|
|
825
|
-
var allCerts = _dedupe(existing).sort(Buffer.compare);
|
|
548
|
+
var allCerts = _dedupe(existing).sort(Buffer.compare);
|
|
826
549
|
|
|
827
|
-
var newSdFields = [sdKids[0].bytes, sdKids[1].bytes, sdKids[2].bytes];
|
|
550
|
+
var newSdFields = [sdKids[0].bytes, sdKids[1].bytes, sdKids[2].bytes];
|
|
828
551
|
if (allCerts.length) newSdFields.push(b.contextConstructed(0, Buffer.concat(allCerts)));
|
|
829
552
|
if (crlsNode) newSdFields.push(crlsNode.bytes);
|
|
830
553
|
newSdFields.push(newSiSet);
|
|
@@ -833,27 +556,9 @@ function _countersign(cmsInput, signers, opts) {
|
|
|
833
556
|
});
|
|
834
557
|
}
|
|
835
558
|
|
|
836
|
-
// Coverage residual: three defensive branches are unreachable through the shipped path.
|
|
837
|
-
// * `_skiValue`'s `cert.extensions || []` fallback. x509.parse always surfaces `extensions`
|
|
838
|
-
// as an array (empty when absent), so the `|| []` never fires.
|
|
839
|
-
// * `_assertKeyMatchesScheme`'s `key.algorithm || {}`. A WebCrypto CryptoKey always carries
|
|
840
|
-
// an `algorithm`, so the `|| {}` fallback never fires.
|
|
841
|
-
// * `_assertKeyMatchesScheme`'s `!ka.hash` guard. An `imp.hash` is set only for an RSA
|
|
842
|
-
// scheme, which requires `ka.name` to already equal the RSA name (else the earlier name
|
|
843
|
-
// check throws); an RSA CryptoKey always carries a `hash`, so `!ka.hash` never fires.
|
|
844
|
-
// Countersign-side residuals also unreachable through the shipped path:
|
|
845
|
-
// * `_resolveSignerIndices`'s `n < 1` throw. A parsed SignedData always carries at least one
|
|
846
|
-
// SignerInfo, so the default index [0] is always in range.
|
|
847
|
-
// * `_spliceNested`'s no-countersignature / index-out-of-range / not-found throws. The same node
|
|
848
|
-
// is validated by `_targetPreimage` first (it computes the nested preimage before the build), so
|
|
849
|
-
// by the time `_spliceNested` re-walks it those conditions cannot hold; the checks are
|
|
850
|
-
// belt-and-suspenders against a future caller reordering the two.
|
|
851
|
-
// * the `crls [1]` preservation branches in `countersign`. pki.cms.sign never emits a crls field,
|
|
852
|
-
// so a store this producer countersigns never carries one to preserve.
|
|
853
559
|
module.exports = {
|
|
854
560
|
sign: sign, countersign: countersign, certsOnly: certsOnly,
|
|
855
|
-
// @internal
|
|
856
|
-
// from its own caller's options and gates them at its own door.
|
|
561
|
+
// @internal
|
|
857
562
|
KNOWN_SIGNER_CERT_KEYS: KNOWN_SIGNER_CERT_KEYS,
|
|
858
563
|
KNOWN_SIGNER_KEY_ONLY_KEYS: KNOWN_SIGNER_KEY_ONLY_KEYS,
|
|
859
564
|
};
|