@blamejs/pki 0.4.14 → 0.5.0
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 +61 -0
- package/MIGRATING.md +2 -2
- package/README.md +142 -137
- package/index.js +4 -0
- package/lib/acme.js +73 -1
- package/lib/asn1-der.js +2 -0
- package/lib/attrcert-sign.js +4 -0
- package/lib/cbor-det.js +32 -16
- package/lib/cmc-build.js +880 -0
- package/lib/cmc-verify.js +657 -0
- package/lib/cmp-build.js +8 -7
- package/lib/cmp-verify.js +11 -1
- package/lib/cms-sign.js +170 -8
- package/lib/cms-verify.js +80 -14
- package/lib/crl-sign.js +22 -0
- package/lib/crmf-sign.js +5 -2
- package/lib/csr-sign.js +3 -0
- package/lib/ct.js +72 -0
- package/lib/est.js +828 -32
- package/lib/framework-error.js +13 -0
- package/lib/guard-bytes.js +37 -1
- package/lib/guard-range.js +23 -1
- package/lib/http-transport.js +9 -3
- package/lib/inspect.js +28 -5
- package/lib/jose.js +15 -0
- package/lib/lint.js +4 -0
- package/lib/merkle.js +5 -5
- package/lib/ocsp.js +139 -11
- package/lib/oid.js +69 -1
- package/lib/path-validate.js +438 -100
- package/lib/pkcs12-build.js +12 -0
- package/lib/schema-all.js +19 -1
- package/lib/schema-attrcert.js +27 -0
- package/lib/schema-c509.js +6 -0
- package/lib/schema-cmc.js +791 -0
- package/lib/schema-cmp.js +25 -0
- package/lib/schema-cms.js +17 -1
- package/lib/schema-crl.js +23 -1
- package/lib/schema-crmf.js +13 -0
- package/lib/schema-csr.js +11 -0
- package/lib/schema-csrattrs.js +6 -0
- package/lib/schema-engine.js +6 -2
- package/lib/schema-ocsp.js +41 -0
- package/lib/schema-pkcs12.js +16 -0
- package/lib/schema-pkcs8.js +8 -0
- package/lib/schema-pkix.js +6 -0
- package/lib/schema-smime.js +4 -4
- package/lib/schema-tsp.js +32 -1
- package/lib/schema-x509.js +14 -1
- package/lib/shbs.js +12 -4
- package/lib/sigstore.js +4 -0
- package/lib/smime.js +28 -7
- package/lib/tls-cert-compress.js +15 -3
- package/lib/trust.js +27 -4
- package/lib/tsp-sign.js +41 -6
- package/lib/vendor/README.md +19 -19
- package/lib/webauthn.js +895 -26
- package/lib/x509-sign.js +3 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cmp-build.js
CHANGED
|
@@ -191,12 +191,7 @@ function _encodeHeader(headerSpec, protectionAlgDer, pvno) {
|
|
|
191
191
|
|
|
192
192
|
// A certReqId is an UNBOUNDED INTEGER (RFC 9483 -1 sentinel, no upper bound): accept a safe-integer number
|
|
193
193
|
// or a bigint (a large value beyond 2^53), reject a non-integer / other type. Returns a BigInt for b.integer.
|
|
194
|
-
function _reqIdInt(v, code, what) {
|
|
195
|
-
if (typeof v === "bigint") return v;
|
|
196
|
-
// Number.isSafeInteger (not isInteger): a value above 2^53 is imprecise as a Number, so it MUST be a bigint.
|
|
197
|
-
if (typeof v === "number" && Number.isSafeInteger(v)) return BigInt(v);
|
|
198
|
-
throw _err(code, what + " must be an integer (a safe-integer number, or a bigint for a large value)");
|
|
199
|
-
}
|
|
194
|
+
function _reqIdInt(v, code, what) { return guard.range.authoredInteger(v, _err, code, what); }
|
|
200
195
|
|
|
201
196
|
// PKIFailureInfo ::= BIT STRING (named bits, RFC 9810 sec. 5.2.3) -- a minimal NamedBitList from bit names.
|
|
202
197
|
function _encodeFailInfo(names, code) {
|
|
@@ -616,7 +611,7 @@ function _collectExtraCerts(opts, protCertDer) {
|
|
|
616
611
|
* @primitive pki.cmp.build
|
|
617
612
|
* @signature pki.cmp.build(message, opts?) -> Promise<Buffer|string>
|
|
618
613
|
* @since 0.3.5
|
|
619
|
-
* @status
|
|
614
|
+
* @status stable
|
|
620
615
|
* @spec RFC 9810, RFC 9481, RFC 9579
|
|
621
616
|
* @related pki.schema.cmp.parse
|
|
622
617
|
*
|
|
@@ -645,6 +640,12 @@ function _collectExtraCerts(opts, protCertDer) {
|
|
|
645
640
|
* - `pss` (boolean) / `digestAlgorithm` (string) -- signature-protection algorithm options.
|
|
646
641
|
*
|
|
647
642
|
* @example
|
|
643
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
644
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
645
|
+
* var signerCertDer = await pki.x509.sign({ subject: "client", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
646
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: signerKeyPkcs8 });
|
|
647
|
+
* var csrDer = await pki.csr.sign({ subject: "client", subjectPublicKey: await pki.key.export(pair.publicKey) },
|
|
648
|
+
* { key: signerKeyPkcs8 });
|
|
648
649
|
* var der = await pki.cmp.build(
|
|
649
650
|
* { header: { sender: { directoryName: "CN=client" }, recipient: { directoryName: "CN=CA" } },
|
|
650
651
|
* body: { p10cr: csrDer } },
|
package/lib/cmp-verify.js
CHANGED
|
@@ -665,7 +665,7 @@ async function _verify(message, opts) {
|
|
|
665
665
|
* @primitive pki.cmp.verify
|
|
666
666
|
* @signature pki.cmp.verify(message, opts?) -> Promise<verdict>
|
|
667
667
|
* @since 0.3.26
|
|
668
|
-
* @status
|
|
668
|
+
* @status stable
|
|
669
669
|
* @spec RFC 9810, RFC 9481, RFC 9579, RFC 9483
|
|
670
670
|
* @related pki.cmp.build, pki.schema.cmp.parse
|
|
671
671
|
* @defends cmp-unverified-protection (CWE-347), cmp-mac-timing (CWE-208)
|
|
@@ -709,6 +709,16 @@ async function _verify(message, opts) {
|
|
|
709
709
|
* - `maxIterations` (number) -- downward-only override of the PBKDF2 iteration cap.
|
|
710
710
|
*
|
|
711
711
|
* @example
|
|
712
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
713
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
714
|
+
* var signerCertDer = await pki.x509.sign({ subject: "client", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
715
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: signerKeyPkcs8 });
|
|
716
|
+
* var certDer = signerCertDer; // self-signed here, so it is also its own anchor
|
|
717
|
+
* var csrDer = await pki.csr.sign({ subject: "client", subjectPublicKey: await pki.key.export(pair.publicKey) },
|
|
718
|
+
* { key: signerKeyPkcs8 });
|
|
719
|
+
* var cmpDer = await pki.cmp.build(
|
|
720
|
+
* { header: { sender: { directoryName: "CN=client" }, recipient: { directoryName: "CN=CA" } },
|
|
721
|
+
* body: { p10cr: csrDer } }, { key: signerKeyPkcs8, cert: signerCertDer });
|
|
712
722
|
* var v = await pki.cmp.verify(cmpDer, { signerCert: signerCertDer, trustAnchors: [certDer] });
|
|
713
723
|
* if (v.valid && v.trusted) console.log("the response protection is authentic and the signer is trusted");
|
|
714
724
|
*/
|
package/lib/cms-sign.js
CHANGED
|
@@ -22,7 +22,7 @@ var frameworkError = require("./framework-error");
|
|
|
22
22
|
var webcrypto = require("./webcrypto");
|
|
23
23
|
var signScheme = require("./sign-scheme");
|
|
24
24
|
var guard = require("./guard-all");
|
|
25
|
-
var pkiBuild = require("./pki-build");
|
|
25
|
+
var pkiBuild = require("./pki-build"); // the shared post-sign self-check, among other producing helpers
|
|
26
26
|
var cms = require("./schema-cms");
|
|
27
27
|
|
|
28
28
|
var subtle = webcrypto.webcrypto.subtle;
|
|
@@ -33,6 +33,15 @@ function _err(code, message, cause) { return new CmsError(code, message, cause);
|
|
|
33
33
|
// cms/<kind>), so its faults keep the cms/* codes.
|
|
34
34
|
function _signE(kind, message, cause) { return new CmsError("cms/" + kind, message, cause); }
|
|
35
35
|
function O(name) { return oid.byName(name); }
|
|
36
|
+
// The shared producing-side helpers, bound to this domain's error class. Only the
|
|
37
|
+
// post-sign self-check is used here -- the same one every other signer in the
|
|
38
|
+
// toolkit runs -- so a key-only signer's declared public key is bound by evidence
|
|
39
|
+
// rather than by the caller's word.
|
|
40
|
+
var NS = pkix.makeNS("cms", CmsError, oid);
|
|
41
|
+
var _b = pkiBuild.makeBuilder({
|
|
42
|
+
ErrorClass: CmsError, prefix: "cms", O: O, NS: NS,
|
|
43
|
+
NAME_SCHEMA: pkix.name(NS), SPKI_SCHEMA: pkix.spki(NS), EXT_DECODERS: {},
|
|
44
|
+
});
|
|
36
45
|
|
|
37
46
|
// A digest-algorithm name -> the engine's hash name. The FIPS 202 extendable-output
|
|
38
47
|
// functions are message digests here at the lengths RFC 8702 sec. 4 fixes for that use,
|
|
@@ -43,6 +52,7 @@ var DIGEST_HASH = {
|
|
|
43
52
|
};
|
|
44
53
|
|
|
45
54
|
var OID_DATA = O("data");
|
|
55
|
+
var OID_PKI_DATA = O("id-cct-PKIData"); // the content type RFC 5272 sec. 3.2's signer rules govern
|
|
46
56
|
var OID_SIGNED_DATA = O("signedData");
|
|
47
57
|
var OID_SKI = O("subjectKeyIdentifier");
|
|
48
58
|
|
|
@@ -134,10 +144,35 @@ function _buildUnsignedAttrs(list) {
|
|
|
134
144
|
// SignedData digestAlgorithms + certificates sets).
|
|
135
145
|
function _buildSignerInfo(signer, content, eContentType, opts) {
|
|
136
146
|
var so = signer || {};
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
// A KEY-ONLY signer: `{ key, spki, keyIdentifier }` with no certificate. RFC
|
|
148
|
+
// 5272 sec. 3.2 requires exactly this when a Full PKI Request is signed with the
|
|
149
|
+
// key of a certification request it carries -- there is no certificate yet, the
|
|
150
|
+
// sid MUST be the subjectKeyIdentifier form, and its value MUST be the SKI the
|
|
151
|
+
// request itself declares. The scheme resolver only ever reads
|
|
152
|
+
// `cert.subjectPublicKeyInfo.algorithm`, so the request's own parsed SPKI stands
|
|
153
|
+
// in for the certificate that does not exist.
|
|
154
|
+
var keyOnly = so.cert == null && so.spki != null;
|
|
155
|
+
// Every field the emitted SignerInfo depends on is read ONCE, HERE, while
|
|
156
|
+
// nothing has deferred yet. The SignerIdentifier is built from the certificate
|
|
157
|
+
// or the key identifier on the lines below; the key signs, and is matched
|
|
158
|
+
// against `spki`, a promise turn later. A descriptor whose `key` and `spki`
|
|
159
|
+
// were swapped in that gap would emit a SignerInfo that names one public key
|
|
160
|
+
// and carries a signature by another -- coherent to the match check, since
|
|
161
|
+
// both halves moved together, and unverifiable to everyone else.
|
|
162
|
+
//
|
|
163
|
+
// The public bytes are COPIED, so an in-place rewrite cannot reach them
|
|
164
|
+
// either. The private key is captured by reference: copying it would put a
|
|
165
|
+
// second copy of key material in this function's hands, a secret it would then
|
|
166
|
+
// own for the rest of the call, and the reference already pins which key
|
|
167
|
+
// signs against the identifier that names it.
|
|
168
|
+
var soKey = so.key;
|
|
169
|
+
var soSpki = keyOnly ? guard.bytes.snapshotSource(so.spki, CmsError, "cms/bad-input", "a key-only signer's spki") : null;
|
|
170
|
+
var certDer = keyOnly ? null : _normCertDer(so.cert);
|
|
171
|
+
var cert = keyOnly ? _keyOnlyCertStandIn(soSpki) : x509.parse(certDer);
|
|
139
172
|
var scheme = signScheme.resolveSignScheme(cert, so, opts.signedAttributes === false, _signE);
|
|
140
|
-
var sidv =
|
|
173
|
+
var sidv = keyOnly
|
|
174
|
+
? { sid: b.contextPrimitive(0, _keyOnlyKeyId(so)), version: 3 } // [0] IMPLICIT SubjectKeyIdentifier
|
|
175
|
+
: _buildSid(cert, opts.sid === "ski");
|
|
141
176
|
var sid = sidv.sid, version = sidv.version;
|
|
142
177
|
|
|
143
178
|
return Promise.resolve().then(function () {
|
|
@@ -155,19 +190,60 @@ function _buildSignerInfo(signer, content, eContentType, opts) {
|
|
|
155
190
|
});
|
|
156
191
|
}).then(function (toSign) {
|
|
157
192
|
var signedBytes = toSign.setOf ? toSign.setOf : toSign; // SET-OF form for signing (sec. 5.4)
|
|
158
|
-
return signScheme.signOverTbs(scheme,
|
|
193
|
+
return signScheme.signOverTbs(scheme, soKey, signedBytes, _signE).then(function (sig) {
|
|
194
|
+
return _assertKeyMatchesSpki(keyOnly, soKey, soSpki, scheme, sig, signedBytes, cert).then(function () { return sig; });
|
|
195
|
+
}).then(function (sig) {
|
|
159
196
|
var fields = [b.integer(BigInt(version)), sid, scheme.digestAlgId];
|
|
160
197
|
if (toSign.wire) fields.push(toSign.wire); // [0] IMPLICIT signedAttrs
|
|
161
198
|
fields.push(scheme.sigAlgId, b.octetString(sig));
|
|
162
199
|
var ua = _buildUnsignedAttrs(opts.unsignedAttributes);
|
|
163
200
|
if (ua) fields.push(ua); // [1] IMPLICIT unsignedAttrs
|
|
164
201
|
return { si: b.sequence(fields), digestAlgId: scheme.digestAlgId, version: version, certDer: certDer };
|
|
202
|
+
// certDer is null for a key-only signer, and the certificates [0] embedding
|
|
203
|
+
// below filters it out -- there is nothing to embed, which is the point.
|
|
165
204
|
});
|
|
166
205
|
});
|
|
167
206
|
}
|
|
168
207
|
|
|
169
208
|
|
|
170
209
|
|
|
210
|
+
// A SignerInfo names a public key -- as `spki` when the signer is key-only, and as
|
|
211
|
+
// the certificate it embeds otherwise -- while the signature comes from `key`. Two
|
|
212
|
+
// different keys make a well-formed SignerInfo nobody can verify: the recipient
|
|
213
|
+
// resolves the identifier to the declared key and checks a signature made by
|
|
214
|
+
// another. Neither form catches that by itself, so both are checked here.
|
|
215
|
+
//
|
|
216
|
+
// Proven from the SIGNATURE, not from the key. Deriving the public half and
|
|
217
|
+
// comparing it works only for key material this process can export, and the keys
|
|
218
|
+
// this signer serves are often exactly the ones that cannot be: a non-extractable
|
|
219
|
+
// CryptoKey, an HSM handle, or a composite `{ mldsa, trad }` pair that is two keys
|
|
220
|
+
// rather than one. Every one of those can still be checked the direct way -- the
|
|
221
|
+
// signature it just produced either verifies under the declared public key or it
|
|
222
|
+
// does not -- so there is no key kind this has to take on trust.
|
|
223
|
+
//
|
|
224
|
+
// This is the same post-sign self-check every other signer in the toolkit runs,
|
|
225
|
+
// through the same shared helper. It costs one verification per SignerInfo, which
|
|
226
|
+
// is what a certificate, a CRL, a CSR and a CMP message have always paid here.
|
|
227
|
+
function _assertKeyMatchesSpki(keyOnly, soKey, soSpki, scheme, sig, signedBytes, cert) {
|
|
228
|
+
var declared = keyOnly ? soSpki : (cert && cert.subjectPublicKeyInfo && cert.subjectPublicKeyInfo.bytes);
|
|
229
|
+
// A parsed certificate that does not retain its SPKI bytes cannot be checked this
|
|
230
|
+
// way. That is not a shape x509.parse produces -- and if it ever were, refusing is
|
|
231
|
+
// the answer rather than skipping, since a skip is the fail-open this prevents.
|
|
232
|
+
if (!declared) {
|
|
233
|
+
throw _signE("bad-input",
|
|
234
|
+
"a signer certificate did not surface its subjectPublicKeyInfo, so the signature it produced could " +
|
|
235
|
+
"not be checked against the key the SignerInfo declares");
|
|
236
|
+
}
|
|
237
|
+
return Promise.resolve()
|
|
238
|
+
.then(function () { return _b.assertSignatureVerifies(signedBytes, sig, declared, scheme); })
|
|
239
|
+
.then(null, function (e) {
|
|
240
|
+
if (e && typeof e.code === "string" && e.code.indexOf("cms/") === 0) throw e;
|
|
241
|
+
throw _signE("bad-input",
|
|
242
|
+
"a signer's `key` does not match the public key its SignerInfo declares (" +
|
|
243
|
+
(keyOnly ? "`spki`" : "its certificate") + "): the signature it produced does not verify under that key", e);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
171
247
|
// A signing-time Time value: UTCTime before 2050, GeneralizedTime from 2050 (RFC 5652 sec. 11.3 /
|
|
172
248
|
// RFC 5280 sec. 4.1.2.5). A caller Date overrides; false omits the attribute (handled above).
|
|
173
249
|
function _timeValue(when) {
|
|
@@ -178,6 +254,75 @@ function _timeValue(when) {
|
|
|
178
254
|
// Normalize a signer certificate input to its raw DER (DER Buffer / PEM string / Uint8Array).
|
|
179
255
|
// The same bytes drive scheme resolution and the certificates [0] embedding, so a parsed
|
|
180
256
|
// certificate (which does not retain its full DER) is rejected -- pass DER or PEM.
|
|
257
|
+
// The stand-in a key-only signer resolves its signature scheme from: the parsed
|
|
258
|
+
// SubjectPublicKeyInfo of the key that will sign. Only `.subjectPublicKeyInfo` is
|
|
259
|
+
// read downstream, so this deliberately carries nothing else -- a fuller fake
|
|
260
|
+
// would invite code to start trusting fields no certificate actually backs.
|
|
261
|
+
function _keyOnlyCertStandIn(spkiDer) {
|
|
262
|
+
var alg;
|
|
263
|
+
try {
|
|
264
|
+
// SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }
|
|
265
|
+
// The WHOLE structure, not just the field this function goes on to read. For an
|
|
266
|
+
// OPAQUE key handle the derivation check downstream is deliberately skipped, so
|
|
267
|
+
// this is the ONLY thing standing between a caller's bytes and a SignerInfo that
|
|
268
|
+
// declares them: a SEQUENCE carrying an algorithm and nothing else, or a third
|
|
269
|
+
// field, would be emitted as the signer's public key and resolve to nothing for
|
|
270
|
+
// anyone trying to verify. The key VALUE is not interpreted -- only that there
|
|
271
|
+
// is one, in the field the structure reserves for it.
|
|
272
|
+
var node = asn1.decode(spkiDer);
|
|
273
|
+
if (node.tagClass !== "universal" || node.tagNumber !== asn1.TAGS.SEQUENCE || !node.children ||
|
|
274
|
+
node.children.length !== 2) {
|
|
275
|
+
throw _err("cms/bad-input",
|
|
276
|
+
"a key-only signer's spki is SEQUENCE { algorithm, subjectPublicKey BIT STRING } (RFC 5280 sec. 4.1.2.7)");
|
|
277
|
+
}
|
|
278
|
+
var keyNode = node.children[1];
|
|
279
|
+
if (keyNode.tagClass !== "universal" || keyNode.tagNumber !== asn1.TAGS.BIT_STRING) {
|
|
280
|
+
throw _err("cms/bad-input",
|
|
281
|
+
"a key-only signer's spki subjectPublicKey must be a BIT STRING (RFC 5280 sec. 4.1.2.7)");
|
|
282
|
+
}
|
|
283
|
+
asn1.read.bitString(keyNode); // read, not tag-inspected: a malformed unused-bit count is refused here
|
|
284
|
+
var algNode = node.children[0];
|
|
285
|
+
if (algNode.tagClass !== "universal" || algNode.tagNumber !== asn1.TAGS.SEQUENCE ||
|
|
286
|
+
!algNode.children || !algNode.children.length || algNode.children.length > 2) {
|
|
287
|
+
throw _err("cms/bad-input",
|
|
288
|
+
"a key-only signer's spki algorithm is SEQUENCE { algorithm OID, parameters OPTIONAL } (RFC 5280 sec. 4.1.1.2)");
|
|
289
|
+
}
|
|
290
|
+
// The same shape schema-x509 surfaces: the algorithm OID plus its parameters
|
|
291
|
+
// as the RAW TLV (or null when absent) -- the resolver re-decodes those bytes
|
|
292
|
+
// for an EC named curve and for the RSASSA-PSS hash pinning, so handing it a
|
|
293
|
+
// different representation here would break exactly those two algorithms.
|
|
294
|
+
alg = {
|
|
295
|
+
oid: asn1.read.oid(algNode.children[0]),
|
|
296
|
+
parameters: algNode.children[1] ? algNode.children[1].bytes : null,
|
|
297
|
+
};
|
|
298
|
+
} catch (e) {
|
|
299
|
+
// The shape refusals above already name what is wrong; only a decode failure
|
|
300
|
+
// needs the generic message, so a precise verdict is not overwritten by it.
|
|
301
|
+
if (e && typeof e.code === "string" && e.code.indexOf("cms/") === 0) throw e;
|
|
302
|
+
throw _err("cms/bad-input", "a key-only signer's spki is not a SubjectPublicKeyInfo", e);
|
|
303
|
+
}
|
|
304
|
+
return { subjectPublicKeyInfo: { algorithm: alg } };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// The key identifier a key-only signer's sid carries. Required: without it there
|
|
308
|
+
// is no way for a verifier to find the key, and RFC 5272 sec. 3.2 makes the value
|
|
309
|
+
// the one the certification request declares.
|
|
310
|
+
function _keyOnlyKeyId(so) {
|
|
311
|
+
if (so.keyIdentifier == null) {
|
|
312
|
+
throw _err("cms/bad-input",
|
|
313
|
+
"a key-only signer requires keyIdentifier -- the subjectKeyIdentifier the certification request declares (RFC 5272 sec. 3.2)");
|
|
314
|
+
}
|
|
315
|
+
// A key identifier is BYTES, so the type is checked rather than coerced. Buffer.from
|
|
316
|
+
// ACCEPTS far more than it should mean here: Buffer.from(20) allocates twenty zero
|
|
317
|
+
// octets and Buffer.from("a1b2") takes the ASCII of the text rather than the two
|
|
318
|
+
// octets a reader means by it. Either would emit a structurally valid
|
|
319
|
+
// SignerIdentifier carrying an identifier the caller never asked for, which no
|
|
320
|
+
// verifier can match back to the certification request (RFC 5272 sec. 3.2).
|
|
321
|
+
var id = guard.bytes.view(so.keyIdentifier, CmsError, "cms/bad-input", "a key-only signer's keyIdentifier");
|
|
322
|
+
if (!id.length) throw _err("cms/bad-input", "a key-only signer's keyIdentifier must not be empty");
|
|
323
|
+
return id;
|
|
324
|
+
}
|
|
325
|
+
|
|
181
326
|
function _normCertDer(c) {
|
|
182
327
|
if (c == null) throw _err("cms/bad-input", "each signer requires a certificate (cert)");
|
|
183
328
|
if (c instanceof Uint8Array && !Buffer.isBuffer(c)) c = Buffer.from(c); // a Uint8Array -> Buffer (below)
|
|
@@ -199,6 +344,18 @@ function sign(content, signers, opts) {
|
|
|
199
344
|
var list = Array.isArray(signers) ? signers : [signers];
|
|
200
345
|
if (!list.length) throw _err("cms/bad-input", "pki.cms.sign requires at least one signer");
|
|
201
346
|
var eContentType = opts.eContentType ? O(opts.eContentType) : OID_DATA;
|
|
347
|
+
// RFC 5272 sec. 3.2: "If the request key is used for signing, there MUST be only
|
|
348
|
+
// one SignerInfo in the SignedData." That clause governs a Full PKI Request, so
|
|
349
|
+
// it binds HERE only for that content type. CMS itself is happy with several
|
|
350
|
+
// SignerInfos, and a key-only signer's certificate can reach a verifier by other
|
|
351
|
+
// means -- applying the CMC rule to every content type would refuse ordinary
|
|
352
|
+
// SignedData that nothing objects to. pki.cmc.build enforces the same rule on
|
|
353
|
+
// the message it assembles, which is where the clause is really about.
|
|
354
|
+
if (eContentType === OID_PKI_DATA && list.length > 1 &&
|
|
355
|
+
list.some(function (s) { return s && s.cert == null && s.spki != null; })) {
|
|
356
|
+
throw _err("cms/bad-input",
|
|
357
|
+
"a key-only signer must be the ONLY SignerInfo in a Full PKI Request (RFC 5272 sec. 3.2)");
|
|
358
|
+
}
|
|
202
359
|
// RFC 5652 sec. 5.3: signed attributes MUST be present (carrying a content-type attribute)
|
|
203
360
|
// whenever the encapsulated content type is not id-data -- so signedAttributes:false is only
|
|
204
361
|
// valid for id-data content. Refusing it here keeps cms.sign from emitting a non-conformant
|
|
@@ -226,8 +383,12 @@ function sign(content, signers, opts) {
|
|
|
226
383
|
// certificates [0] IMPLICIT SET OF (the signer certs), deduped + SET-OF-ordered, when embedded.
|
|
227
384
|
var sdFields = [b.integer(BigInt(version)), b.set(digestAlgs), encap];
|
|
228
385
|
if (opts.certificates !== false) {
|
|
229
|
-
|
|
230
|
-
|
|
386
|
+
// A key-only signer contributes no certificate (certDer is null), so the
|
|
387
|
+
// set can end up EMPTY -- and an empty certificates [0] is not the same as
|
|
388
|
+
// an absent one, so the field is omitted entirely in that case.
|
|
389
|
+
var certDers = _dedupe(built.map(function (x) { return x.certDer; })
|
|
390
|
+
.filter(function (d) { return d != null; })).sort(Buffer.compare); // X.690 sec. 11.6
|
|
391
|
+
if (certDers.length) sdFields.push(b.contextConstructed(0, Buffer.concat(certDers))); // [0] IMPLICIT SET OF
|
|
231
392
|
}
|
|
232
393
|
sdFields.push(b.set(built.map(function (x) { return x.si; }))); // signerInfos SET OF
|
|
233
394
|
var signedData = b.sequence(sdFields);
|
|
@@ -276,6 +437,7 @@ function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
|
276
437
|
var cert = x509.parse(certDer);
|
|
277
438
|
var scheme = signScheme.resolveSignScheme(cert, so, opts.signedAttributes === false, _signE);
|
|
278
439
|
var sidv = _buildSid(cert, opts.sid === "ski");
|
|
440
|
+
var soKey = so.key; // read once, here -- the SignerIdentifier above is already fixed to `cert`
|
|
279
441
|
return Promise.resolve().then(function () {
|
|
280
442
|
if (opts.signedAttributes === false) return null; // sign the target signature octets directly
|
|
281
443
|
return _digest(scheme.digest, targetSigOctets).then(function (md) {
|
|
@@ -286,7 +448,7 @@ function _buildCountersignature(targetSigOctets, countersigner, opts) {
|
|
|
286
448
|
return _buildSignedAttrs(pairs.concat(extra));
|
|
287
449
|
});
|
|
288
450
|
}).then(function (attrs) {
|
|
289
|
-
return signScheme.signOverTbs(scheme,
|
|
451
|
+
return signScheme.signOverTbs(scheme, soKey, attrs ? attrs.setOf : targetSigOctets, _signE).then(function (sig) {
|
|
290
452
|
var fields = [b.integer(BigInt(sidv.version)), sidv.sid, scheme.digestAlgId];
|
|
291
453
|
if (attrs) fields.push(attrs.wire); // [0] IMPLICIT signedAttrs
|
|
292
454
|
fields.push(scheme.sigAlgId, b.octetString(sig));
|
package/lib/cms-verify.js
CHANGED
|
@@ -572,7 +572,7 @@ function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
|
|
|
572
572
|
* @primitive pki.cms.verify
|
|
573
573
|
* @signature pki.cms.verify(input, opts?) -> Promise<{ valid, signers }>
|
|
574
574
|
* @since 0.2.14
|
|
575
|
-
* @status
|
|
575
|
+
* @status stable
|
|
576
576
|
* @spec RFC 5652
|
|
577
577
|
* @spec RFC 9882
|
|
578
578
|
* @spec RFC 9814
|
|
@@ -594,19 +594,55 @@ function _verifyOneCountersig(vDer, targetSig, parsedCerts) {
|
|
|
594
594
|
* @opts certs Extra signer certificates (an array of DER `Buffer`s) to match against, in
|
|
595
595
|
* addition to the certificates embedded in the SignedData.
|
|
596
596
|
* @example
|
|
597
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
598
|
+
* var key = await pki.key.export(pair.privateKey);
|
|
599
|
+
* var cert = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
600
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
601
|
+
* var detachedBytes = Buffer.from("hello");
|
|
602
|
+
* var p7sDer = await pki.cms.sign(detachedBytes, { cert: cert, key: key, detached: true });
|
|
597
603
|
* var res = await pki.cms.verify(p7sDer, { content: detachedBytes });
|
|
598
604
|
* res.valid; // boolean
|
|
599
605
|
* res.signers[0].ok; // per-signer verdict
|
|
600
606
|
*/
|
|
607
|
+
// A private copy of the input when -- and only when -- it is memory the caller can
|
|
608
|
+
// still write to. Anything else (a PEM string, and whatever else the parser accepts)
|
|
609
|
+
// is returned untouched so this cannot narrow the input contract of the parse it
|
|
610
|
+
// feeds; the parser stays the one place that decides what an acceptable input is.
|
|
611
|
+
function _snapshotIfBytes(input, label) {
|
|
612
|
+
// Every byte form the parser accepts -- an ArrayBuffer or a DataView reaches the
|
|
613
|
+
// decoder as readily as a Buffer, and leaving those aliased would reopen the
|
|
614
|
+
// window for exactly the inputs that came in by the wider door.
|
|
615
|
+
if (ArrayBuffer.isView(input) || input instanceof ArrayBuffer) {
|
|
616
|
+
return guard.bytes.snapshotSource(input, CmsError, "cms/bad-input", label);
|
|
617
|
+
}
|
|
618
|
+
return input;
|
|
619
|
+
}
|
|
620
|
+
|
|
601
621
|
function verify(input, opts) {
|
|
602
622
|
opts = opts || {};
|
|
603
623
|
if (typeof opts !== "object" || Buffer.isBuffer(opts)) throw _err("cms/bad-input", "pki.cms.verify options must be an object");
|
|
604
|
-
|
|
624
|
+
// Handed MUTABLE BYTES, parse a private copy. This function decodes synchronously
|
|
625
|
+
// and checks signatures in a later promise turn, so without the copy every range
|
|
626
|
+
// the parse surfaced -- the signed content above all -- stays a view into the
|
|
627
|
+
// caller's memory across that await, and a buffer rewritten in the gap would leave
|
|
628
|
+
// the result describing one message while the signature was checked over another.
|
|
629
|
+
// Only a Buffer / Uint8Array can change underneath us: a PEM string is immutable
|
|
630
|
+
// and passes through untouched, and an already-parsed object is a lifetime the
|
|
631
|
+
// caller chose. Narrow rather than blanket, so neither input form is refused here
|
|
632
|
+
// that pki.cms.parse itself accepts.
|
|
633
|
+
var parsed = (input && typeof input === "object" && !Buffer.isBuffer(input) && Array.isArray(input.signerInfos))
|
|
634
|
+
? input
|
|
635
|
+
: cms.parse(_snapshotIfBytes(input, "pki.cms.verify"));
|
|
605
636
|
if (!Array.isArray(parsed.signerInfos)) throw _err("cms/bad-input", "input is not a CMS SignedData");
|
|
606
637
|
var content = parsed.encapContentInfo.eContent;
|
|
607
638
|
if (content == null) {
|
|
608
639
|
if (opts.content == null) throw _err("cms/detached-content-required", "this SignedData is detached; opts.content (the external content) is required");
|
|
609
|
-
content
|
|
640
|
+
// Copied, not aliased: the digest over this content is computed in a later promise turn,
|
|
641
|
+
// and a detached signature's content is the whole of what the signature speaks about. A
|
|
642
|
+
// caller who rewrote the buffer in that gap would have the signature checked against the
|
|
643
|
+
// original bytes while holding the replacement, and the verdict would report a valid
|
|
644
|
+
// signature over content that was never signed.
|
|
645
|
+
content = _toBuf(_snapshotIfBytes(opts.content, "opts.content"), "opts.content");
|
|
610
646
|
} else if (opts.content != null) {
|
|
611
647
|
// A caller supplied external content AND the SignedData is attached. A redundant COPY that equals
|
|
612
648
|
// the encapsulated content is fine; content that DIFFERS is a substitution trap -- silently
|
|
@@ -635,10 +671,15 @@ function verify(input, opts) {
|
|
|
635
671
|
}
|
|
636
672
|
// Parse a candidate cert DER and index its SKI; a cert that will not parse is skipped (it
|
|
637
673
|
// simply cannot be a signer match, and a malformed embedded cert must not fail the verify).
|
|
674
|
+
// The certificate bytes are copied BEFORE they are parsed, not after: the parse surfaces the
|
|
675
|
+
// signed portion and the public key as VIEWS into this buffer, and both are read in a later
|
|
676
|
+
// promise turn to check the signer's signature and walk the chain. Copying afterwards would
|
|
677
|
+
// leave those views pointing at bytes the caller can still rewrite -- a signer certificate
|
|
678
|
+
// swapped for another in that gap, with the verdict still naming the first.
|
|
638
679
|
function _addCert(out, der) {
|
|
639
680
|
var buf;
|
|
640
681
|
try {
|
|
641
|
-
buf = _toBuf(der, "a certificate");
|
|
682
|
+
buf = _toBuf(_snapshotIfBytes(der, "a certificate"), "a certificate");
|
|
642
683
|
} catch (_e) { return; }
|
|
643
684
|
var cert;
|
|
644
685
|
try {
|
|
@@ -663,7 +704,7 @@ function _addCert(out, der) {
|
|
|
663
704
|
* @primitive pki.cms.sign
|
|
664
705
|
* @signature pki.cms.sign(content, signers, opts?) -> Promise<Buffer|string>
|
|
665
706
|
* @since 0.2.15
|
|
666
|
-
* @status
|
|
707
|
+
* @status stable
|
|
667
708
|
* @spec RFC 5652
|
|
668
709
|
* @spec RFC 9882
|
|
669
710
|
* @spec RFC 9814
|
|
@@ -701,6 +742,10 @@ function _addCert(out, der) {
|
|
|
701
742
|
* rejected here (RFC 5652 sec. 11). Use `pki.cms.countersign` for a
|
|
702
743
|
* countersignature specifically.
|
|
703
744
|
* @example
|
|
745
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
746
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
747
|
+
* var signerCertDer = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
748
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: signerKeyPkcs8 });
|
|
704
749
|
* var p7 = await pki.cms.sign(Buffer.from("hello"), { cert: signerCertDer, key: signerKeyPkcs8 });
|
|
705
750
|
* var res = await pki.cms.verify(p7); // res.valid === true
|
|
706
751
|
*/
|
|
@@ -710,7 +755,7 @@ var sign = cmsSign.sign;
|
|
|
710
755
|
* @primitive pki.cms.countersign
|
|
711
756
|
* @signature pki.cms.countersign(cms, signers, opts?) -> Promise<Buffer|string>
|
|
712
757
|
* @since 0.3.13
|
|
713
|
-
* @status
|
|
758
|
+
* @status stable
|
|
714
759
|
* @spec RFC 5652
|
|
715
760
|
* @related pki.cms.sign, pki.cms.verify, pki.schema.cms.parse
|
|
716
761
|
*
|
|
@@ -740,6 +785,10 @@ var sign = cmsSign.sign;
|
|
|
740
785
|
* @opts certificates Embed the countersigner certificate(s) in the SignedData. Default true.
|
|
741
786
|
* @opts pem Return a PEM string instead of a DER Buffer.
|
|
742
787
|
* @example
|
|
788
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
789
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
790
|
+
* var signerCertDer = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
791
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: signerKeyPkcs8 });
|
|
743
792
|
* var p7 = await pki.cms.sign(Buffer.from("hi"), { cert: signerCertDer, key: signerKeyPkcs8 });
|
|
744
793
|
* var cs = await pki.cms.countersign(p7, { cert: signerCertDer, key: signerKeyPkcs8 });
|
|
745
794
|
* var res = await pki.cms.verify(cs);
|
|
@@ -751,7 +800,7 @@ var countersign = cmsSign.countersign;
|
|
|
751
800
|
* @primitive pki.cms.encrypt
|
|
752
801
|
* @signature pki.cms.encrypt(content, recipients, opts?) -> Promise<Buffer | string>
|
|
753
802
|
* @since 0.2.23
|
|
754
|
-
* @status
|
|
803
|
+
* @status stable
|
|
755
804
|
* @spec RFC 5652, RFC 5083, RFC 5084, RFC 3560, RFC 5753, RFC 8418, RFC 9629, RFC 9936, RFC 3211, RFC 8018
|
|
756
805
|
* @related pki.cms.decrypt, pki.schema.cms.parse
|
|
757
806
|
*
|
|
@@ -775,6 +824,11 @@ var countersign = cmsSign.countersign;
|
|
|
775
824
|
* @opts authAttrs Authenticated attributes (SET OF Attribute) for AuthEnvelopedData.
|
|
776
825
|
* @opts pem Return a PEM string instead of a DER Buffer.
|
|
777
826
|
* @example
|
|
827
|
+
* var rsa = { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" };
|
|
828
|
+
* var pair = await pki.key.generate(rsa);
|
|
829
|
+
* var recipientKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
830
|
+
* var recipientCertDer = await pki.x509.sign({ subject: "Recipient", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
831
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: recipientKeyPkcs8 });
|
|
778
832
|
* var env = await pki.cms.encrypt(Buffer.from("secret"), [{ cert: recipientCertDer }]);
|
|
779
833
|
*/
|
|
780
834
|
var encrypt = cmsEncrypt.encrypt;
|
|
@@ -783,7 +837,7 @@ var encrypt = cmsEncrypt.encrypt;
|
|
|
783
837
|
* @primitive pki.cms.authenticate
|
|
784
838
|
* @signature pki.cms.authenticate(content, recipients, opts?) -> Promise<Buffer | string>
|
|
785
839
|
* @since 0.3.14
|
|
786
|
-
* @status
|
|
840
|
+
* @status stable
|
|
787
841
|
* @spec RFC 5652, RFC 2104, RFC 4231, RFC 3370
|
|
788
842
|
* @related pki.cms.decrypt, pki.schema.cms.parse
|
|
789
843
|
*
|
|
@@ -809,9 +863,14 @@ var encrypt = cmsEncrypt.encrypt;
|
|
|
809
863
|
* @opts keyIdentifier `"issuerAndSerial"` (default) or `"subjectKeyIdentifier"`.
|
|
810
864
|
* @opts pem Return a PEM string (`-----BEGIN CMS-----`) instead of a DER Buffer.
|
|
811
865
|
* @example
|
|
812
|
-
*
|
|
813
|
-
* var
|
|
814
|
-
* var
|
|
866
|
+
* // authenticate a message to an RSA recipient, then verify the MAC via decrypt
|
|
867
|
+
* var rsa = { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" };
|
|
868
|
+
* var pair = await pki.key.generate(rsa);
|
|
869
|
+
* var recipientKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
870
|
+
* var recipientCertDer = await pki.x509.sign({ subject: "Recipient", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
871
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: recipientKeyPkcs8 });
|
|
872
|
+
* var auth = await pki.cms.authenticate(Buffer.from("hi"), [{ cert: recipientCertDer }]);
|
|
873
|
+
* var out = await pki.cms.decrypt(auth, { key: recipientKeyPkcs8, cert: recipientCertDer });
|
|
815
874
|
* out.authenticated; // true
|
|
816
875
|
*/
|
|
817
876
|
var authenticate = cmsEncrypt.authenticate;
|
|
@@ -820,7 +879,7 @@ var authenticate = cmsEncrypt.authenticate;
|
|
|
820
879
|
* @primitive pki.cms.decrypt
|
|
821
880
|
* @signature pki.cms.decrypt(input, keyMaterial, opts?) -> Promise<{ content, contentType, contentTypeName, recipientType, recipientIndex, contentEncryptionAlgorithm, authenticated }>
|
|
822
881
|
* @since 0.2.23
|
|
823
|
-
* @status
|
|
882
|
+
* @status stable
|
|
824
883
|
* @spec RFC 5652, RFC 5083, RFC 5084, RFC 3560, RFC 5753, RFC 8418, RFC 9629, RFC 9936, RFC 3211, RFC 8018, RFC 3218, RFC 2104
|
|
825
884
|
* @related pki.cms.encrypt, pki.cms.authenticate, pki.schema.cms.parse
|
|
826
885
|
*
|
|
@@ -843,6 +902,12 @@ var authenticate = cmsEncrypt.authenticate;
|
|
|
843
902
|
* @opts recipientIndex Explicitly select the recipient by index (overrides key-material matching).
|
|
844
903
|
* @opts maxIterations Lower the PBKDF2 iteration cap (a DoS bound; downward only).
|
|
845
904
|
* @example
|
|
905
|
+
* var rsa = { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" };
|
|
906
|
+
* var pair = await pki.key.generate(rsa);
|
|
907
|
+
* var recipientKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
908
|
+
* var recipientCertDer = await pki.x509.sign({ subject: "Recipient", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
909
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: recipientKeyPkcs8 });
|
|
910
|
+
* var envDer = await pki.cms.encrypt(Buffer.from("secret"), [{ cert: recipientCertDer }]);
|
|
846
911
|
* var res = await pki.cms.decrypt(envDer, { key: recipientKeyPkcs8, cert: recipientCertDer });
|
|
847
912
|
* res.content; // the recovered plaintext Buffer
|
|
848
913
|
*/
|
|
@@ -852,7 +917,7 @@ var decrypt = cmsDecrypt.decrypt;
|
|
|
852
917
|
* @primitive pki.cms.compress
|
|
853
918
|
* @signature pki.cms.compress(content, opts?) -> Promise<Buffer | string>
|
|
854
919
|
* @since 0.2.27
|
|
855
|
-
* @status
|
|
920
|
+
* @status stable
|
|
856
921
|
* @spec RFC 3274, RFC 1950, RFC 1951
|
|
857
922
|
* @related pki.cms.decompress, pki.schema.cms.parse
|
|
858
923
|
*
|
|
@@ -875,7 +940,7 @@ var compress = cmsCompress.compress;
|
|
|
875
940
|
* @primitive pki.cms.decompress
|
|
876
941
|
* @signature pki.cms.decompress(input, opts?) -> Promise<{ content, contentType, contentTypeName, compressionAlgorithm }>
|
|
877
942
|
* @since 0.2.27
|
|
878
|
-
* @status
|
|
943
|
+
* @status stable
|
|
879
944
|
* @spec RFC 3274, RFC 1950, RFC 1951
|
|
880
945
|
* @related pki.cms.compress, pki.schema.cms.parse
|
|
881
946
|
*
|
|
@@ -890,6 +955,7 @@ var compress = cmsCompress.compress;
|
|
|
890
955
|
*
|
|
891
956
|
* @opts maxOutputBytes Lower the decompressed-output cap (a DoS bound; downward only).
|
|
892
957
|
* @example
|
|
958
|
+
* var compressedDer = await pki.cms.compress(Buffer.from("compress me"));
|
|
893
959
|
* var res = await pki.cms.decompress(compressedDer);
|
|
894
960
|
* res.content; // the recovered plaintext Buffer
|
|
895
961
|
*/
|
package/lib/crl-sign.js
CHANGED
|
@@ -486,6 +486,12 @@ function _sign(spec, issuer, opts) {
|
|
|
486
486
|
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS rather than PKCS#1 v1.5.
|
|
487
487
|
* - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
|
|
488
488
|
* @example
|
|
489
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
490
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
491
|
+
* var signerCertDer = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
492
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
493
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"], subjectKeyIdentifier: true } },
|
|
494
|
+
* { key: signerKeyPkcs8 });
|
|
489
495
|
* var der = await pki.crl.sign({
|
|
490
496
|
* thisUpdate: new Date("2026-01-01T00:00:00Z"), nextUpdate: new Date("2026-02-01T00:00:00Z"),
|
|
491
497
|
* crlNumber: 7n,
|
|
@@ -529,6 +535,14 @@ function _resolveIssuerSpki(issuer) {
|
|
|
529
535
|
* signature only -- issuer authorization, currency, and distribution-point scope are `pki.path.crlChecker`.
|
|
530
536
|
*
|
|
531
537
|
* @example
|
|
538
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
539
|
+
* var signerSpki = await pki.key.export(pair.publicKey);
|
|
540
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
541
|
+
* var signerCertDer = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: signerSpki,
|
|
542
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
543
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } }, { key: signerKeyPkcs8 });
|
|
544
|
+
* var crlDer = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"), crlNumber: 1n, revoked: [] },
|
|
545
|
+
* { cert: signerCertDer, key: signerKeyPkcs8 });
|
|
532
546
|
* var ok = await pki.crl.verify(crlDer, { publicKey: signerSpki }); // true / false
|
|
533
547
|
*/
|
|
534
548
|
function verify(crl, issuer) { return Promise.resolve().then(function () { return _verify(crl, issuer); }); }
|
|
@@ -565,6 +579,14 @@ function _serialHexOf(serial) {
|
|
|
565
579
|
* it does NOT verify the CRL signature or its currency; call `pki.crl.verify` / `pki.path.crlChecker` for that.
|
|
566
580
|
*
|
|
567
581
|
* @example
|
|
582
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
583
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
584
|
+
* var signerCertDer = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
585
|
+
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
|
|
586
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } }, { key: signerKeyPkcs8 });
|
|
587
|
+
* var crlDer = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"), crlNumber: 1n,
|
|
588
|
+
* revoked: [{ serialNumber: 0x1234n, revocationDate: new Date("2026-01-15T00:00:00Z") }] },
|
|
589
|
+
* { cert: signerCertDer, key: signerKeyPkcs8 });
|
|
568
590
|
* pki.crl.isRevoked(crlDer, 0x1234n) ? "revoked" : "not listed";
|
|
569
591
|
*/
|
|
570
592
|
function isRevoked(crl, serialNumber) {
|
package/lib/crmf-sign.js
CHANGED
|
@@ -203,7 +203,7 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
|
|
|
203
203
|
* @primitive pki.crmf.build
|
|
204
204
|
* @signature pki.crmf.build(spec, key?, opts?) -> Promise<Buffer|string>
|
|
205
205
|
* @since 0.3.3
|
|
206
|
-
* @status
|
|
206
|
+
* @status stable
|
|
207
207
|
* @spec RFC 4211
|
|
208
208
|
* @defends forged-certificate-request (CWE-347)
|
|
209
209
|
* @related pki.schema.crmf.parse, pki.csr.sign
|
|
@@ -229,6 +229,9 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
|
|
|
229
229
|
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS rather than PKCS#1 v1.5.
|
|
230
230
|
* - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
|
|
231
231
|
* @example
|
|
232
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
233
|
+
* var signerSpki = await pki.key.export(pair.publicKey);
|
|
234
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
232
235
|
* var msg = await pki.crmf.build(
|
|
233
236
|
* { certReqId: 0, certTemplate: { subject: "device-42", publicKey: signerSpki } },
|
|
234
237
|
* { key: signerKeyPkcs8 });
|
|
@@ -287,7 +290,7 @@ function _build(spec, key, opts) {
|
|
|
287
290
|
* @primitive pki.crmf.buildCertTemplate
|
|
288
291
|
* @signature pki.crmf.buildCertTemplate(template) -> Buffer
|
|
289
292
|
* @since 0.3.5
|
|
290
|
-
* @status
|
|
293
|
+
* @status stable
|
|
291
294
|
* @spec RFC 4211
|
|
292
295
|
* @related pki.crmf.build
|
|
293
296
|
*
|
package/lib/csr-sign.js
CHANGED
|
@@ -91,6 +91,9 @@ function _challengePassword(pw) {
|
|
|
91
91
|
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS rather than PKCS#1 v1.5.
|
|
92
92
|
* - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
|
|
93
93
|
* @example
|
|
94
|
+
* var pair = await pki.key.generate("Ed25519");
|
|
95
|
+
* var signerSpki = await pki.key.export(pair.publicKey);
|
|
96
|
+
* var signerKeyPkcs8 = await pki.key.export(pair.privateKey);
|
|
94
97
|
* var req = await pki.csr.sign(
|
|
95
98
|
* { subject: "req.example.com", subjectPublicKey: signerSpki,
|
|
96
99
|
* extensionRequest: { subjectAltName: [{ dNSName: "req.example.com" }] } },
|