@blamejs/pki 0.8.59 → 0.8.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/lib/acme.js +15 -7
- package/lib/alt-sig.js +3 -1
- package/lib/asn1-der.js +82 -34
- package/lib/attrcert-sign.js +19 -16
- package/lib/byte-writer.js +63 -13
- package/lib/cbor-det.js +9 -3
- package/lib/cmc-build.js +26 -18
- package/lib/cmp-build.js +35 -33
- package/lib/cmp-session.js +19 -9
- package/lib/cms-decrypt.js +37 -23
- package/lib/cms-digest.js +2 -3
- package/lib/cms-encrypt.js +38 -33
- package/lib/cms-sign.js +65 -43
- package/lib/cms-verify.js +8 -4
- package/lib/composite-kem.js +28 -29
- package/lib/crl-sign.js +24 -17
- package/lib/crmf-sign.js +11 -17
- package/lib/csr-sign.js +5 -2
- package/lib/ct.js +38 -25
- package/lib/est.js +12 -9
- package/lib/guard-bytes.js +5 -1
- package/lib/guard-crypto.js +506 -14
- package/lib/guard-der.js +6 -1
- package/lib/guard-intrinsic.js +12 -4
- package/lib/guard-list.js +64 -0
- package/lib/guard-name.js +1 -1
- package/lib/guard-secret.js +3 -2
- package/lib/hpke.js +34 -27
- package/lib/http-digest.js +5 -7
- package/lib/http-transport.js +12 -3
- package/lib/inspect.js +10 -2
- package/lib/jose.js +3 -1
- package/lib/key.js +41 -29
- package/lib/lint.js +236 -152
- package/lib/mime.js +9 -5
- package/lib/ocsp.js +26 -24
- package/lib/path-validate.js +43 -13
- package/lib/pbes2.js +12 -9
- package/lib/pkcs11-uri.js +3 -1
- package/lib/pkcs12-build.js +19 -17
- package/lib/pki-build.js +17 -13
- package/lib/possession.js +3 -1
- package/lib/rc2.js +6 -2
- package/lib/revocation-fetch.js +78 -19
- package/lib/scep.js +21 -14
- package/lib/schema-attrcert.js +4 -2
- package/lib/schema-c509.js +94 -78
- package/lib/schema-cmc.js +5 -2
- package/lib/schema-cmp.js +6 -3
- package/lib/schema-cms.js +7 -4
- package/lib/schema-crl.js +1 -1
- package/lib/schema-crmf.js +1 -1
- package/lib/schema-csr.js +3 -3
- package/lib/schema-engine.js +36 -18
- package/lib/schema-pkcs1.js +7 -3
- package/lib/schema-pkcs12.js +9 -6
- package/lib/schema-pkix.js +19 -3
- package/lib/schema-sec1.js +4 -1
- package/lib/schema-tsp.js +7 -4
- package/lib/shbs.js +10 -9
- package/lib/sign-scheme.js +2 -2
- package/lib/sigstore.js +9 -9
- package/lib/smime.js +19 -16
- package/lib/tlog.js +20 -13
- package/lib/tuf.js +7 -4
- package/lib/validator-cose.js +2 -2
- package/lib/validator-keydesc.js +4 -1
- package/lib/validator-tls.js +7 -2
- package/lib/webauthn-mds.js +3 -7
- package/lib/webauthn.js +4 -11
- package/lib/webcrypto.js +70 -49
- package/lib/x509-sign.js +20 -15
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/composite-kem.js
CHANGED
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
var asn1 = require("./asn1-der");
|
|
24
24
|
var oid = require("./oid");
|
|
25
25
|
var webcrypto = require("./webcrypto");
|
|
26
|
-
var nodeCrypto = require("node:crypto");
|
|
27
26
|
var frameworkError = require("./framework-error");
|
|
28
27
|
var guard = require("./guard-all");
|
|
29
28
|
var intrinsic = require("./guard-intrinsic");
|
|
@@ -115,11 +114,11 @@ function _mlkemDecaps(d, seed, mlkemCT) {
|
|
|
115
114
|
function _ecPubFromPoint(d, point) {
|
|
116
115
|
if (point.length < 1 || point[0] !== 0x04) throw _err("kem/bad-key", "the EC component point is not in uncompressed form");
|
|
117
116
|
var spki = _b.sequence([_b.sequence([_b.oid(oid.byName("ecPublicKey")), _b.oid(oid.byName(d.trad.curve))]), _b.bitString(point, 0)]);
|
|
118
|
-
return
|
|
117
|
+
return guard.crypto.publicKey({ key: spki, format: "der", type: "spki" });
|
|
119
118
|
}
|
|
120
119
|
function _okpPubFromRaw(d, raw) {
|
|
121
120
|
var spki = _b.sequence([_b.sequence([_b.oid(oid.byName(d.trad.curve))]), _b.bitString(raw, 0)]);
|
|
122
|
-
return
|
|
121
|
+
return guard.crypto.publicKey({ key: spki, format: "der", type: "spki" });
|
|
123
122
|
}
|
|
124
123
|
/** @internal The encoding is this helper's own allocation and holds the traditional private scalar, so
|
|
125
124
|
* it is cleared here. A caller's own cleanup cannot reach a buffer it never sees, and both the
|
|
@@ -127,13 +126,13 @@ function _okpPubFromRaw(d, raw) {
|
|
|
127
126
|
function _okpPrivFromRaw(d, raw) {
|
|
128
127
|
var p8 = _b.sequence([_b.integer(0n), _b.sequence([_b.oid(oid.byName(d.trad.curve))]), _b.octetString(_b.octetString(raw))]);
|
|
129
128
|
try {
|
|
130
|
-
return
|
|
129
|
+
return guard.crypto.privateKey({ key: p8, format: "der", type: "pkcs8" });
|
|
131
130
|
} finally {
|
|
132
131
|
guard.secret.zeroize(p8, KemError, "kem/bad-input", "the " + d.trad.curve + " component key encoding");
|
|
133
132
|
}
|
|
134
133
|
}
|
|
135
134
|
function _rawPubOf(keyObject) {
|
|
136
|
-
var spki =
|
|
135
|
+
var spki = guard.crypto.exportKey(keyObject, { format: "der", type: "spki" });
|
|
137
136
|
return Buffer.from(asn1.read.bitString(asn1.decode(spki).children[1]).bytes);
|
|
138
137
|
}
|
|
139
138
|
|
|
@@ -143,12 +142,12 @@ function _tradEncaps(d, tradPK) {
|
|
|
143
142
|
var pub;
|
|
144
143
|
try {
|
|
145
144
|
asn1.decode(tradPK);
|
|
146
|
-
pub =
|
|
145
|
+
pub = guard.crypto.publicKey({ key: tradPK, format: "der", type: "pkcs1" });
|
|
147
146
|
} catch (e) { return _promiseReject(_Promise, _err("kem/bad-key", "the RSA component public key is not a valid RSAPublicKey", e)); }
|
|
148
|
-
if (pub.
|
|
149
|
-
var ss = Buffer.from(
|
|
147
|
+
if (guard.crypto.keyDetails(pub).modulusLength !== trad.bits) return _promiseReject(_Promise, _err("kem/bad-key", "the RSA component modulus is not " + trad.bits + " bits (draft sec. 6)"));
|
|
148
|
+
var ss = Buffer.from(guard.crypto.randomBytes(RSA_SS));
|
|
150
149
|
var ct;
|
|
151
|
-
try { ct = Buffer.from(
|
|
150
|
+
try { ct = Buffer.from(guard.crypto.publicEncrypt({ key: pub, padding: guard.crypto.padding.RSA_PKCS1_OAEP, oaepHash: "sha256" }, ss)); }
|
|
152
151
|
catch (e) {
|
|
153
152
|
guard.secret.zeroize(ss, KemError, "kem/bad-input", "the RSA-OAEP component secret");
|
|
154
153
|
return _promiseReject(_Promise, _err("kem/bad-key", "the RSA-OAEP component encryption failed", e));
|
|
@@ -158,8 +157,8 @@ function _tradEncaps(d, tradPK) {
|
|
|
158
157
|
var dh, dhCt;
|
|
159
158
|
try {
|
|
160
159
|
var recip = trad.kind === "ec" ? _ecPubFromPoint(d, tradPK) : _okpPubFromRaw(d, tradPK);
|
|
161
|
-
var eph = trad.kind === "ec" ?
|
|
162
|
-
dh = Buffer.from(
|
|
160
|
+
var eph = trad.kind === "ec" ? guard.crypto.generateKeyPair("ec", { namedCurve: trad.node }) : guard.crypto.generateKeyPair(trad.curve.toLowerCase());
|
|
161
|
+
dh = Buffer.from(guard.crypto.diffieHellman({ privateKey: eph.privateKey, publicKey: recip }));
|
|
163
162
|
dhCt = _rawPubOf(eph.publicKey);
|
|
164
163
|
} catch (e) { return _promiseReject(_Promise, _err("kem/bad-key", "the " + trad.curve + " public key is invalid", e)); }
|
|
165
164
|
return _promiseResolve(_Promise, { ss: dh, ct: dhCt });
|
|
@@ -171,37 +170,38 @@ function _tradDecaps(d, tradSK, tradCT) {
|
|
|
171
170
|
var priv;
|
|
172
171
|
try {
|
|
173
172
|
asn1.decode(tradSK);
|
|
174
|
-
priv =
|
|
173
|
+
priv = guard.crypto.privateKey({ key: tradSK, format: "der", type: "pkcs1" });
|
|
175
174
|
} catch (e) { return _promiseReject(_Promise, _err("kem/bad-key", "the RSA component private key is not a valid RSAPrivateKey", e)); }
|
|
176
|
-
if (priv.
|
|
175
|
+
if (guard.crypto.keyDetails(priv).modulusLength !== trad.bits) return _promiseReject(_Promise, _err("kem/bad-key", "the RSA component modulus is not " + trad.bits + " bits (draft sec. 6)"));
|
|
177
176
|
var ss;
|
|
178
|
-
try { ss = Buffer.from(
|
|
177
|
+
try { ss = Buffer.from(guard.crypto.privateDecrypt({ key: priv, padding: guard.crypto.padding.RSA_PKCS1_OAEP, oaepHash: "sha256" }, tradCT)); }
|
|
179
178
|
catch (_e) { return _promiseReject(_Promise, _err("kem/decapsulation-failed", "the RSA-OAEP component decryption failed")); }
|
|
180
179
|
if (ss.length !== RSA_SS) {
|
|
181
180
|
guard.secret.zeroize(ss, KemError, "kem/bad-input", "the RSA-OAEP component secret");
|
|
182
181
|
return _promiseReject(_Promise, _err("kem/decapsulation-failed", "the RSA-OAEP component decryption failed"));
|
|
183
182
|
}
|
|
184
|
-
return _promiseResolve(_Promise, { ss: ss,
|
|
183
|
+
return _promiseResolve(_Promise, { ss: ss,
|
|
184
|
+
tradPK: Buffer.from(guard.crypto.exportKey(guard.crypto.publicKey(priv), { format: "der", type: "pkcs1" })) });
|
|
185
185
|
}
|
|
186
186
|
var priv2, eph;
|
|
187
187
|
try {
|
|
188
188
|
if (trad.kind === "ec") {
|
|
189
189
|
asn1.decode(tradSK);
|
|
190
|
-
priv2 =
|
|
190
|
+
priv2 = guard.crypto.privateKey({ key: tradSK, format: "der", type: "sec1" });
|
|
191
191
|
} else {
|
|
192
192
|
priv2 = _okpPrivFromRaw(d, tradSK);
|
|
193
193
|
}
|
|
194
194
|
eph = trad.kind === "ec" ? _ecPubFromPoint(d, tradCT) : _okpPubFromRaw(d, tradCT);
|
|
195
195
|
} catch (e) { return _promiseReject(_Promise, _err("kem/bad-key", "the " + trad.curve + " key material is invalid", e)); }
|
|
196
196
|
var dh;
|
|
197
|
-
try { dh = Buffer.from(
|
|
197
|
+
try { dh = Buffer.from(guard.crypto.diffieHellman({ privateKey: priv2, publicKey: eph })); }
|
|
198
198
|
catch (e) { return _promiseReject(_Promise, _err("kem/decapsulation-failed", "the " + trad.curve + " Diffie-Hellman failed", e)); }
|
|
199
199
|
/** @internal The same canonical point the derivation states, read from the one function that spells
|
|
200
200
|
* it. The combiner hashes these bytes, so a stored spelling here and a generated one there would
|
|
201
201
|
* reach different secrets from the two sides of one key. */
|
|
202
202
|
var tradPK;
|
|
203
203
|
try {
|
|
204
|
-
tradPK = trad.kind === "ec" ? _ecPointOf(d, tradSK) : _rawPubOf(
|
|
204
|
+
tradPK = trad.kind === "ec" ? _ecPointOf(d, tradSK) : _rawPubOf(guard.crypto.publicKey(priv2));
|
|
205
205
|
} catch (e) {
|
|
206
206
|
/** @internal The agreement already produced a secret, and this rejection happens after it, so the
|
|
207
207
|
* secret is wiped here: the caller's settle-or-wipe covers a value it was handed, and a rejected
|
|
@@ -371,9 +371,7 @@ function _ecPointOf(d, tradSK) {
|
|
|
371
371
|
var scalar = _ecScalarOf(d, tradSK);
|
|
372
372
|
var point;
|
|
373
373
|
try {
|
|
374
|
-
|
|
375
|
-
ecdh.setPrivateKey(scalar);
|
|
376
|
-
point = ecdh.getPublicKey(null, "uncompressed");
|
|
374
|
+
point = guard.crypto.ecPointFromScalar(d.trad.node, scalar);
|
|
377
375
|
} catch (e) {
|
|
378
376
|
if (e instanceof KemError) throw e;
|
|
379
377
|
throw _err("kem/bad-key", "the EC component scalar does not generate a point on " + d.trad.node, e);
|
|
@@ -418,18 +416,18 @@ function _tradPubOf(d, tradSK) {
|
|
|
418
416
|
var rp;
|
|
419
417
|
try {
|
|
420
418
|
asn1.decode(tradSK);
|
|
421
|
-
rp =
|
|
419
|
+
rp = guard.crypto.privateKey({ key: tradSK, format: "der", type: "pkcs1" });
|
|
422
420
|
} catch (e) { throw _err("kem/bad-key", "the RSA component private key is not a valid RSAPrivateKey", e); }
|
|
423
|
-
if (rp.
|
|
421
|
+
if (guard.crypto.keyDetails(rp).modulusLength !== trad.bits) {
|
|
424
422
|
throw _err("kem/bad-key", "the RSA component modulus is not " + trad.bits + " bits (draft sec. 6)");
|
|
425
423
|
}
|
|
426
|
-
return Buffer.from(
|
|
424
|
+
return Buffer.from(guard.crypto.exportKey(guard.crypto.publicKey(rp), { format: "der", type: "pkcs1" }));
|
|
427
425
|
}
|
|
428
426
|
var priv;
|
|
429
427
|
try {
|
|
430
428
|
if (trad.kind === "ec") {
|
|
431
429
|
asn1.decode(tradSK);
|
|
432
|
-
priv =
|
|
430
|
+
priv = guard.crypto.privateKey({ key: tradSK, format: "der", type: "sec1" });
|
|
433
431
|
} else {
|
|
434
432
|
priv = _okpPrivFromRaw(d, tradSK);
|
|
435
433
|
}
|
|
@@ -439,7 +437,8 @@ function _tradPubOf(d, tradSK) {
|
|
|
439
437
|
* public key nothing can use, so the two are held to each other here. Decapsulation reaches the same
|
|
440
438
|
* conclusion by failing the agreement; a derivation performs no operation that would notice. */
|
|
441
439
|
if (trad.kind === "ec") {
|
|
442
|
-
var
|
|
440
|
+
var privDetails = guard.crypto.keyDetails(priv);
|
|
441
|
+
var named = privDetails && privDetails.namedCurve;
|
|
443
442
|
if (named !== trad.node) {
|
|
444
443
|
throw _err("kem/bad-key", "the EC component is on " + guard.text.showValue(named) + ", and " + d.name + " names " + trad.node);
|
|
445
444
|
}
|
|
@@ -450,7 +449,7 @@ function _tradPubOf(d, tradSK) {
|
|
|
450
449
|
* point stored beside it cannot stand in for one it does not generate. */
|
|
451
450
|
return _ecPointOf(d, tradSK);
|
|
452
451
|
}
|
|
453
|
-
return _rawPubOf(
|
|
452
|
+
return _rawPubOf(guard.crypto.publicKey(priv));
|
|
454
453
|
}
|
|
455
454
|
|
|
456
455
|
/** @internal The composite `SubjectPublicKeyInfo` for a composite ML-KEM private key. The runtime key
|
|
@@ -488,8 +487,8 @@ async function _publicFromPrivate(privateKey) {
|
|
|
488
487
|
* copy of the seed and is registered as one. */
|
|
489
488
|
var seedPkcs8 = _mlkemSeedPkcs8(d, seed);
|
|
490
489
|
guard.list.append(secrets, seedPkcs8);
|
|
491
|
-
var mlPriv =
|
|
492
|
-
ek = _rawPubOf(
|
|
490
|
+
var mlPriv = guard.crypto.privateKey({ key: seedPkcs8, format: "der", type: "pkcs8" });
|
|
491
|
+
ek = _rawPubOf(guard.crypto.publicKey(mlPriv));
|
|
493
492
|
} catch (e) {
|
|
494
493
|
if (e instanceof KemError) throw e;
|
|
495
494
|
throw _err("kem/bad-key", "the ML-KEM component private key is invalid", e);
|
package/lib/crl-sign.js
CHANGED
|
@@ -24,6 +24,9 @@ var x509Schema = require("./schema-x509");
|
|
|
24
24
|
var signScheme = require("./sign-scheme");
|
|
25
25
|
var guard = require("./guard-all");
|
|
26
26
|
var intrinsic = require("./guard-intrinsic");
|
|
27
|
+
/** @internal The append every list here is built with: `push` stores at the index and a store
|
|
28
|
+
* consults a setter inherited there, and these lists become the bytes that get signed. */
|
|
29
|
+
var _push = intrinsic.append;
|
|
27
30
|
var _hasOwn = intrinsic.hasOwn, _bufferEquals = intrinsic.bufferEquals;
|
|
28
31
|
var _arrSlice = intrinsic.arraySlice, _promiseResolve = intrinsic.uncurry(intrinsic.promiseResolve);
|
|
29
32
|
/** @internal The RECEIVER for the captured `Promise.resolve`, captured too. `resolve` builds through
|
|
@@ -184,11 +187,11 @@ function _idpValue(idp) {
|
|
|
184
187
|
if (idp.fullName != null) {
|
|
185
188
|
var entries = Array.isArray(idp.fullName) ? pkiBuild.reqDenseArray(idp.fullName, "issuingDistributionPoint.fullName", _err, "crl/bad-idp") : [idp.fullName];
|
|
186
189
|
if (!entries.length) throw _err("crl/bad-idp", "issuingDistributionPoint fullName must carry at least one GeneralName");
|
|
187
|
-
children
|
|
190
|
+
_push(children,b.contextConstructed(0, b.contextConstructed(0, Buffer.concat(entries.map(_encodeGeneralName)))));
|
|
188
191
|
}
|
|
189
192
|
var scopeTrue = 0;
|
|
190
|
-
if (idp.onlyContainsUserCerts === true) { children
|
|
191
|
-
if (idp.onlyContainsCACerts === true) { children
|
|
193
|
+
if (idp.onlyContainsUserCerts === true) { _push(children,b.contextPrimitive(1, Buffer.from([0xff]))); scopeTrue++; }
|
|
194
|
+
if (idp.onlyContainsCACerts === true) { _push(children,b.contextPrimitive(2, Buffer.from([0xff]))); scopeTrue++; }
|
|
192
195
|
if (idp.indirectCRL === true) throw _err("crl/bad-idp", _INDIRECT_IDP_DEFERRED);
|
|
193
196
|
if (scopeTrue > 1) throw _err("crl/bad-idp", "at most one of onlyContainsUserCerts / onlyContainsCACerts may be TRUE (RFC 5280 sec. 5.2.5)");
|
|
194
197
|
if (!children.length) throw _err("crl/bad-idp", "issuingDistributionPoint MUST NOT be empty (RFC 5280 sec. 5.2.5)");
|
|
@@ -232,7 +235,7 @@ function _buildCrlExtensions(spec, ctx) {
|
|
|
232
235
|
var id = O(oidName);
|
|
233
236
|
if (seen[id]) throw _err("crl/bad-input", "duplicate CRL extension " + oidName + " (RFC 5280 sec. 5.2 -- at most one instance)");
|
|
234
237
|
seen[id] = true;
|
|
235
|
-
out
|
|
238
|
+
_push(out,_ext(id, critical, valueDer));
|
|
236
239
|
}
|
|
237
240
|
var crlNumberVal = null;
|
|
238
241
|
if (spec.crlNumber != null) { var cn = _boundedInteger(spec.crlNumber, "cRLNumber"); crlNumberVal = cn.value; push("cRLNumber", false, cn.tlv); }
|
|
@@ -264,7 +267,7 @@ function _buildCrlExtensions(spec, ctx) {
|
|
|
264
267
|
}
|
|
265
268
|
_assertExtensionValueDecodes(node, extnId, "extension");
|
|
266
269
|
if (extnId === O("deltaCRLIndicator")) isDelta = true;
|
|
267
|
-
out
|
|
270
|
+
_push(out,b.raw(der));
|
|
268
271
|
});
|
|
269
272
|
if (isDelta) {
|
|
270
273
|
if (seen[O("freshestCRL")]) throw _err("crl/bad-input", "freshestCRL MUST NOT appear in a delta CRL (RFC 5280 sec. 5.2.6)");
|
|
@@ -323,7 +326,7 @@ function _buildRevoked(entryList, isDelta) {
|
|
|
323
326
|
var id = O(oidName);
|
|
324
327
|
if (seen[id]) throw _err("crl/bad-input", "duplicate entry extension " + oidName + " in revoked entry [" + idx + "]");
|
|
325
328
|
seen[id] = true;
|
|
326
|
-
entryExts
|
|
329
|
+
_push(entryExts,_ext(id, critical, valueDer));
|
|
327
330
|
}
|
|
328
331
|
if (e.reason != null) {
|
|
329
332
|
var codeNum = _resolveReason(e.reason, isDelta);
|
|
@@ -360,10 +363,10 @@ function _buildRevoked(entryList, isDelta) {
|
|
|
360
363
|
}
|
|
361
364
|
}
|
|
362
365
|
_assertExtensionValueDecodes(xnode, extnId, "entry extension");
|
|
363
|
-
entryExts
|
|
366
|
+
_push(entryExts,b.raw(der));
|
|
364
367
|
});
|
|
365
368
|
}
|
|
366
|
-
if (entryExts.length) { anyExt = true; children
|
|
369
|
+
if (entryExts.length) { anyExt = true; _push(children,b.sequence(entryExts)); }
|
|
367
370
|
return b.sequence(children);
|
|
368
371
|
});
|
|
369
372
|
return { entries: entries, anyExt: anyExt };
|
|
@@ -375,7 +378,11 @@ function _parseIssuerCert(cert) {
|
|
|
375
378
|
}
|
|
376
379
|
|
|
377
380
|
function _assertIssuerCanSignCrl(issuerCert) {
|
|
378
|
-
|
|
381
|
+
/** @internal The selection returns the first matching row or null. `filter(...)[0]` indexes a
|
|
382
|
+
* result that is EMPTY when the certificate carries no keyUsage, and index 0 of an empty array
|
|
383
|
+
* answers from the array prototype, so a value installed there would be decoded as the issuer's
|
|
384
|
+
* key usage and decide whether it may sign this CRL. */
|
|
385
|
+
var kuExt = guard.list.firstMatch(issuerCert.extensions || [], function (e) { return e.oid === O("keyUsage"); });
|
|
379
386
|
if (!kuExt) return;
|
|
380
387
|
var ku;
|
|
381
388
|
try { ku = EXT_DECODERS[O("keyUsage")](kuExt.value); }
|
|
@@ -453,13 +460,13 @@ function _sign(spec, issuer, opts) {
|
|
|
453
460
|
* into the PreTBSCertList the alternative signature covers. */
|
|
454
461
|
function tbsOver(extList, omitSignatureAlg) {
|
|
455
462
|
var tbsChildren = [];
|
|
456
|
-
if (version === 2) tbsChildren
|
|
457
|
-
if (omitSignatureAlg !== true) tbsChildren
|
|
458
|
-
tbsChildren
|
|
459
|
-
tbsChildren
|
|
460
|
-
if (nextU) tbsChildren
|
|
461
|
-
if (revoked.entries.length) tbsChildren
|
|
462
|
-
if (extList.length) tbsChildren
|
|
463
|
+
if (version === 2) _push(tbsChildren,b.integer(1n));
|
|
464
|
+
if (omitSignatureAlg !== true) _push(tbsChildren,scheme.sigAlgId);
|
|
465
|
+
_push(tbsChildren,issuerDer);
|
|
466
|
+
_push(tbsChildren,thisU);
|
|
467
|
+
if (nextU) _push(tbsChildren,nextU);
|
|
468
|
+
if (revoked.entries.length) _push(tbsChildren,b.sequence(revoked.entries));
|
|
469
|
+
if (extList.length) _push(tbsChildren,b.explicit(0, b.sequence(extList)));
|
|
463
470
|
return b.sequence(tbsChildren);
|
|
464
471
|
}
|
|
465
472
|
|
|
@@ -813,7 +820,7 @@ function isRevoked(crl, serialNumber, opts) {
|
|
|
813
820
|
|
|
814
821
|
function _findExtOid(list, name) {
|
|
815
822
|
var want = O(name);
|
|
816
|
-
return (list || []
|
|
823
|
+
return guard.list.firstMatch(list || [], function (e) { return e.oid === want; });
|
|
817
824
|
}
|
|
818
825
|
|
|
819
826
|
var _IDP_SCHEMA = pkix.issuingDistributionPoint("crl/scope-not-authoritative");
|
package/lib/crmf-sign.js
CHANGED
|
@@ -17,12 +17,6 @@
|
|
|
17
17
|
* @card Build a CRMF CertReqMessages with a signature proof of possession.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
var nodeCrypto = require("node:crypto");
|
|
21
|
-
/** @internal Bound at module load, like every other capture here: the proof below decides what it
|
|
22
|
-
* agrees and what it hashes, so a replacement swapped in afterwards must not be able to steer it. */
|
|
23
|
-
var _diffieHellman = nodeCrypto.diffieHellman;
|
|
24
|
-
var _createPublicKey = nodeCrypto.createPublicKey;
|
|
25
|
-
var _createPrivateKey = nodeCrypto.createPrivateKey;
|
|
26
20
|
var asn1 = require("./asn1-der");
|
|
27
21
|
var oid = require("./oid");
|
|
28
22
|
var x509 = require("./schema-x509");
|
|
@@ -345,7 +339,7 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
|
|
|
345
339
|
_assertAgreementExponent(pkcs3, "the authority certificate's key");
|
|
346
340
|
}
|
|
347
341
|
}
|
|
348
|
-
try { caPub =
|
|
342
|
+
try { caPub = guard.crypto.publicKey({ key: caSpkiBytes, format: "der", type: "spki" }); }
|
|
349
343
|
catch (e) { throw _err("crmf/bad-popo", "the authority certificate does not carry a key this runtime can agree with", e); }
|
|
350
344
|
/** @internal The snapshot of the caller's key is this function's own copy of private material, so it
|
|
351
345
|
* is registered for the wipe below rather than left to the argument boundary, which clears what it
|
|
@@ -357,7 +351,7 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
|
|
|
357
351
|
/** @internal Inside the cleanup scope, because the import can throw AFTER the snapshot exists: a
|
|
358
352
|
* catch that returned from out here would leave that copy of private material behind. */
|
|
359
353
|
try {
|
|
360
|
-
if (
|
|
354
|
+
if (guard.crypto.keyTypeOf(pop.key) != null) {
|
|
361
355
|
eePriv = pop.key;
|
|
362
356
|
} else if (pop.key && typeof pop.key === "object" && pop.key.type === "private" &&
|
|
363
357
|
typeof pop.key.export === "function") {
|
|
@@ -366,10 +360,10 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
|
|
|
366
360
|
* applies here: supporting the encoding for a caller holding bytes and refusing the caller
|
|
367
361
|
* holding the key those bytes import to would be the same key admitted one way. The export
|
|
368
362
|
* is this function's own copy of private material and is cleared with the rest. */
|
|
369
|
-
keySnapshot = pop.key
|
|
363
|
+
keySnapshot = guard.crypto.exportKey(pop.key, { format: "der", type: "pkcs8" });
|
|
370
364
|
translated = _x942PrivToPkcs3(keySnapshot);
|
|
371
365
|
eePriv = translated !== null
|
|
372
|
-
?
|
|
366
|
+
? guard.crypto.privateKey({ key: translated, format: "der", type: "pkcs8" })
|
|
373
367
|
: pop.key;
|
|
374
368
|
} else if (guard.bytes.isByteSource(pop.key)) {
|
|
375
369
|
keySnapshot = guard.bytes.snapshotSource(pop.key, CrmfError, "crmf/bad-popo", "the requester's private key");
|
|
@@ -377,7 +371,7 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
|
|
|
377
371
|
* copy of the private value, and an import that throws after it exists must not leave it
|
|
378
372
|
* behind. The wipe below clears whatever was made, on every way out. */
|
|
379
373
|
translated = _x942PrivToPkcs3(keySnapshot);
|
|
380
|
-
eePriv =
|
|
374
|
+
eePriv = guard.crypto.privateKey({ key: translated || keySnapshot, format: "der", type: "pkcs8" });
|
|
381
375
|
} else {
|
|
382
376
|
/** @internal A PEM key reaches the same conversion: it is the same key in the same encoding
|
|
383
377
|
* with an armor around it, so admitting one form and not the other would support X9.42 for a
|
|
@@ -386,9 +380,9 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
|
|
|
386
380
|
if (pemDer !== null) {
|
|
387
381
|
keySnapshot = pemDer;
|
|
388
382
|
translated = _x942PrivToPkcs3(keySnapshot);
|
|
389
|
-
eePriv =
|
|
383
|
+
eePriv = guard.crypto.privateKey({ key: translated || keySnapshot, format: "der", type: "pkcs8" });
|
|
390
384
|
} else {
|
|
391
|
-
eePriv =
|
|
385
|
+
eePriv = guard.crypto.privateKey(pop.key);
|
|
392
386
|
}
|
|
393
387
|
}
|
|
394
388
|
} catch (e) {
|
|
@@ -402,7 +396,7 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
|
|
|
402
396
|
* something this request never asked to have certified. The encryptedKey arm holds its enclosed
|
|
403
397
|
* key to the same rule. */
|
|
404
398
|
var eeSpki;
|
|
405
|
-
try { eeSpki =
|
|
399
|
+
try { eeSpki = guard.crypto.exportKey(guard.crypto.publicKey(eePriv), { format: "der", type: "spki" }); }
|
|
406
400
|
catch (e) { throw _err("crmf/bad-popo", "the public half of pop.key could not be derived to check it against the requested key", e); }
|
|
407
401
|
/** @internal Compared as keys, not as bytes. A PKCS#3 DHParameter carries an optional
|
|
408
402
|
* privateValueLength, so one finite-field key has more than one valid SubjectPublicKeyInfo and a
|
|
@@ -449,7 +443,7 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
|
|
|
449
443
|
* with parameters g and p, agreeing `g^xy mod p`. An elliptic-curve or montgomery pair agrees a
|
|
450
444
|
* secret through the same call and would be emitted under the same OID, which names an operation
|
|
451
445
|
* the recipient would not be performing. */
|
|
452
|
-
if (eePriv
|
|
446
|
+
if (guard.crypto.keyType(eePriv) !== "dh" || guard.crypto.keyType(caPub) !== "dh") {
|
|
453
447
|
/** @internal A key this runtime imported but did not classify reports no asymmetricKeyType, which
|
|
454
448
|
* is what an X9.42 dhpublicnumber SubjectPublicKeyInfo does here where the PKCS#3 dhKeyAgreement
|
|
455
449
|
* form is classified. The agreement refuses such a key on its own, so naming the unrecognized
|
|
@@ -459,7 +453,7 @@ function _buildAgreeMac(pop, outerTag, certReqDer, template) {
|
|
|
459
453
|
}
|
|
460
454
|
/** @internal The agreement's own Buffer, not a copy of it: wrapping it would leave the original
|
|
461
455
|
* holding the secret with nothing to clear it. */
|
|
462
|
-
try { zz =
|
|
456
|
+
try { zz = guard.crypto.diffieHellman({ privateKey: eePriv, publicKey: caPub }); }
|
|
463
457
|
catch (e) { throw _err("crmf/bad-popo", "the requester's key and the authority's key did not agree a secret; this proof needs a key on the authority's own parameters (RFC 4211 sec. 4.3)", e); }
|
|
464
458
|
/** @internal K = SHA1(LeadingInfo | ZZ | TrailingInfo) (RFC 2875 sec. 3, step 3c), under the
|
|
465
459
|
* digest the proof's own algorithm identifier names. */
|
|
@@ -762,7 +756,7 @@ function _x942ToPkcs3(spkiBytes, what) {
|
|
|
762
756
|
/** @internal How a key reads in the refusal above. A key the runtime imported without classifying
|
|
763
757
|
* carries no asymmetricKeyType, and reporting that as a type name says nothing an operator can act on. */
|
|
764
758
|
function _keyTypeLabel(what, key) {
|
|
765
|
-
var type = key
|
|
759
|
+
var type = guard.crypto.keyTypeOf(key);
|
|
766
760
|
if (typeof type !== "string" || type === "") {
|
|
767
761
|
return what + " carries an algorithm this runtime does not recognize as a key it can agree with";
|
|
768
762
|
}
|
package/lib/csr-sign.js
CHANGED
|
@@ -26,6 +26,9 @@ var pkiBuild = require("./pki-build");
|
|
|
26
26
|
var frameworkError = require("./framework-error");
|
|
27
27
|
var guard = require("./guard-all");
|
|
28
28
|
var intrinsic = require("./guard-intrinsic");
|
|
29
|
+
/** @internal The append every list here is built with: `push` stores at the index and a store
|
|
30
|
+
* consults a setter inherited there, and these lists become the bytes that get signed. */
|
|
31
|
+
var _push = intrinsic.append;
|
|
29
32
|
/** @internal The promise a self-check is awaited through, built from the constructor captured at load.
|
|
30
33
|
* `Promise.resolve` builds from its RECEIVER, so reading the global binding at the call hands the
|
|
31
34
|
* construction to whatever that binding holds, and a constructor settling without awaiting its
|
|
@@ -224,7 +227,7 @@ function _sign(spec, key, opts) {
|
|
|
224
227
|
function addAttr(attrType, valueTlv) {
|
|
225
228
|
if (seenAttr[attrType]) throw _err("csr/bad-input", "duplicate " + (oid.name(attrType) || attrType) + " attribute");
|
|
226
229
|
seenAttr[attrType] = true;
|
|
227
|
-
attrs
|
|
230
|
+
_push(attrs,b.sequence([b.oid(attrType), b.set([valueTlv])]));
|
|
228
231
|
}
|
|
229
232
|
if (spec.extensionRequest != null) addAttr(O("extensionRequest"), _buildRequestedExtensions(spec.extensionRequest, subjectSpki));
|
|
230
233
|
if (spec.challengePassword != null) addAttr(O("challengePassword"), _challengePassword(spec.challengePassword));
|
|
@@ -259,7 +262,7 @@ function _sign(spec, key, opts) {
|
|
|
259
262
|
if (!n.children[1].children || n.children[1].children.length === 0) throw _err("csr/bad-input", "pre-encoded attribute [" + i + "] value SET must contain at least one value (RFC 2986 SET SIZE(1..MAX))");
|
|
260
263
|
if (seenAttr[at]) throw _err("csr/bad-input", "duplicate " + (oid.name(at) || at) + " attribute");
|
|
261
264
|
seenAttr[at] = true;
|
|
262
|
-
attrs
|
|
265
|
+
_push(attrs,b.raw(der));
|
|
263
266
|
});
|
|
264
267
|
}
|
|
265
268
|
|
package/lib/ct.js
CHANGED
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
* bounded TLS-struct decode, fail-closed.
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
|
-
var nodeCrypto = require("crypto");
|
|
43
42
|
var asn1 = require("./asn1-der.js");
|
|
44
43
|
var constants = require("./constants.js");
|
|
45
44
|
var frameworkError = require("./framework-error.js");
|
|
@@ -64,6 +63,7 @@ var _bufferCompare = intrinsic.compare;
|
|
|
64
63
|
var _subarray = intrinsic.subarray;
|
|
65
64
|
var _toLowerCase = intrinsic.toLowerCase;
|
|
66
65
|
var _ctTrim = intrinsic.uncurry(String.prototype.trim);
|
|
66
|
+
var _writeBigUInt64BE = intrinsic.uncurry(Buffer.prototype.writeBigUInt64BE);
|
|
67
67
|
var _bufCopy = intrinsic.uncurry(Buffer.prototype.copy);
|
|
68
68
|
var _writeU64BE = intrinsic.uncurry(Buffer.prototype.writeBigUInt64BE);
|
|
69
69
|
var _searchSet = intrinsic.uncurry(URLSearchParams.prototype.set);
|
|
@@ -293,8 +293,11 @@ function parseSctList(extValue) {
|
|
|
293
293
|
}
|
|
294
294
|
sctCount.tick();
|
|
295
295
|
var one = _parseSct(outer.subReader(sctLen, "ct/list-trailing-bytes"), sctLen);
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
/** @internal The parsed timestamps are appended by defining their indexes: the lists a caller
|
|
297
|
+
* reads its verdict from, and the `all` list a policy counts, are what a numeric setter on the
|
|
298
|
+
* array prototype would otherwise take an entry out of. */
|
|
299
|
+
if (one.unknown) { var u = { version: one.version, rawSct: one.rawSct }; intrinsic.append(unknownScts, u); intrinsic.append(all, u); }
|
|
300
|
+
else { intrinsic.append(scts, one); intrinsic.append(all, one); }
|
|
298
301
|
}
|
|
299
302
|
return guard.verdict.of({ scts: scts, unknownScts: unknownScts, all: all });
|
|
300
303
|
}
|
|
@@ -366,29 +369,36 @@ function reconstructSignedData(entry, sct) {
|
|
|
366
369
|
throw new CtError("ct/bad-input", "reconstructSignedData expects a decoded v1 SCT from parseSctList().scts[]");
|
|
367
370
|
}
|
|
368
371
|
var tsVal = guard.range.uint64(sct.timestamp, _ctErr, "ct/bad-input", "sct.timestamp (RFC 6962 3.2)");
|
|
372
|
+
/** @internal EVERY component of this preimage is appended by defining its index, and the
|
|
373
|
+
* concatenation is the captured one. These bytes are what a log's signature is verified over, so a
|
|
374
|
+
* numeric setter inherited from the array prototype replacing any one of them, the version, the
|
|
375
|
+
* signature type, the timestamp, the entry type, the certificate or the extensions, puts the
|
|
376
|
+
* verification on bytes the log never signed. Converting only the timestamp left the rest open,
|
|
377
|
+
* which is the whole class: the rule belongs to the function, not to the line it was found on. */
|
|
369
378
|
var parts = [];
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
var ts = _bufferAlloc(8); ts
|
|
373
|
-
|
|
379
|
+
intrinsic.append(parts, _bufferFrom([sct.version & 0xff]));
|
|
380
|
+
intrinsic.append(parts, _bufferFrom([0]));
|
|
381
|
+
var ts = _bufferAlloc(8); _writeBigUInt64BE(ts, tsVal); intrinsic.append(parts, ts);
|
|
382
|
+
intrinsic.append(parts, _bufferFrom([(entryType >> 8) & 0xff, entryType & 0xff]));
|
|
374
383
|
if (entryType === 0) {
|
|
375
384
|
var cert = _toBuffer(entry.leafCert, "leafCert", undefined, undefined, 0xffffff, "ct/bad-tbs-length");
|
|
376
|
-
|
|
385
|
+
intrinsic.append(parts, _u24Bytes(cert.length)); intrinsic.append(parts, cert);
|
|
377
386
|
} else {
|
|
378
387
|
var ikh = _toBuffer(entry.issuerKeyHash, "issuerKeyHash");
|
|
379
388
|
if (ikh.length !== 32) {
|
|
380
389
|
throw new CtError("ct/bad-issuer-key-hash", "issuer_key_hash must be exactly 32 bytes (SHA-256 of the issuer SPKI), got " + ikh.length + " (RFC 6962 sec. 3.2)");
|
|
381
390
|
}
|
|
382
391
|
var tbs = _toBuffer(entry.tbsCertificate, "tbsCertificate", undefined, undefined, 0xffffff, "ct/bad-tbs-length");
|
|
383
|
-
|
|
384
|
-
|
|
392
|
+
intrinsic.append(parts, ikh);
|
|
393
|
+
intrinsic.append(parts, _u24Bytes(tbs.length)); intrinsic.append(parts, tbs);
|
|
385
394
|
}
|
|
386
395
|
var ext = _toBuffer(sct.extensions, "sct.extensions", undefined, undefined, 0xffff, "ct/bad-extensions");
|
|
387
396
|
if (ext.length > 0xffff) {
|
|
388
397
|
throw new CtError("ct/bad-extensions", "CtExtensions must be 0..65535 bytes, got " + ext.length + " (RFC 6962 3.2)");
|
|
389
398
|
}
|
|
390
|
-
|
|
391
|
-
|
|
399
|
+
intrinsic.append(parts, _bufferFrom([(ext.length >> 8) & 0xff, ext.length & 0xff]));
|
|
400
|
+
intrinsic.append(parts, ext);
|
|
401
|
+
return intrinsic.bufferConcat(parts);
|
|
392
402
|
}
|
|
393
403
|
|
|
394
404
|
var CT_HASH = intrinsic.assign(intrinsic.create(null), { sha256: "SHA-256" });
|
|
@@ -584,7 +594,7 @@ function encodeSctList(scts) {
|
|
|
584
594
|
var el = ew.build();
|
|
585
595
|
total += el.length;
|
|
586
596
|
if (total > 0xffff) throw new CtError("ct/too-large", "the SCT list body exceeds the 65535-byte maximum (RFC 6962 sec. 3.3)");
|
|
587
|
-
|
|
597
|
+
intrinsic.append(elements, el);
|
|
588
598
|
}
|
|
589
599
|
var lw = new TlsWriter();
|
|
590
600
|
lw.vector(2, 1, 0xffff, Buffer.concat(elements, total), "ct/too-large");
|
|
@@ -594,11 +604,14 @@ function encodeSctList(scts) {
|
|
|
594
604
|
function _logKeyMaterial(logKey) {
|
|
595
605
|
try {
|
|
596
606
|
var keyObj;
|
|
597
|
-
if (logKey
|
|
598
|
-
else if (Buffer.isBuffer(logKey)) keyObj =
|
|
599
|
-
else keyObj =
|
|
600
|
-
if (keyObj
|
|
601
|
-
return {
|
|
607
|
+
if (guard.crypto.keyTypeOf(logKey)) keyObj = logKey;
|
|
608
|
+
else if (Buffer.isBuffer(logKey)) keyObj = guard.crypto.privateKey({ key: logKey, format: "der", type: "pkcs8" });
|
|
609
|
+
else keyObj = guard.crypto.privateKey(logKey);
|
|
610
|
+
if (guard.crypto.keyKind(keyObj) !== "private") throw new CtError("ct/bad-input", "signSct requires the CT log PRIVATE key");
|
|
611
|
+
return {
|
|
612
|
+
pkcs8: guard.crypto.exportKey(keyObj, { type: "pkcs8", format: "der" }),
|
|
613
|
+
spki: guard.crypto.exportKey(guard.crypto.publicKey(keyObj), { type: "spki", format: "der" }),
|
|
614
|
+
};
|
|
602
615
|
} catch (e) {
|
|
603
616
|
if (e instanceof CtError) throw e;
|
|
604
617
|
throw new CtError("ct/bad-input", "the CT log private key could not be loaded", e);
|
|
@@ -714,7 +727,7 @@ function _parseLog(log, operatorName) {
|
|
|
714
727
|
var statedId = guard.encoding.base64(log.log_id, 64, _ctErr, "ct/bad-log-list", "the CT log id");
|
|
715
728
|
if (statedId.length !== 32) throw _ctErr("ct/bad-log-list", "a CT log_id must be 32 bytes (SHA-256), got " + statedId.length);
|
|
716
729
|
_spkiAlg(spki);
|
|
717
|
-
var logId =
|
|
730
|
+
var logId = guard.crypto.digest("sha256", spki);
|
|
718
731
|
if (!logId.equals(statedId)) throw _ctErr("ct/log-id-mismatch", "the CT log_id does not match SHA-256 of the log key (RFC 6962 sec. 3.2)");
|
|
719
732
|
return guard.verdict.of({
|
|
720
733
|
logId: logId, logIdHex: logId.toString("hex"), key: spki,
|
|
@@ -803,7 +816,7 @@ function parseLogList(json, opts) {
|
|
|
803
816
|
continue;
|
|
804
817
|
}
|
|
805
818
|
byLogId[rec.logIdHex] = rec;
|
|
806
|
-
|
|
819
|
+
intrinsic.append(logs, rec);
|
|
807
820
|
}
|
|
808
821
|
}
|
|
809
822
|
}
|
|
@@ -1092,15 +1105,15 @@ function x509CertEntry(cert, issuer) {
|
|
|
1092
1105
|
for (var j = 0; j < extsSeq.children.length; j++) {
|
|
1093
1106
|
var ext = extsSeq.children[j];
|
|
1094
1107
|
if (ext.children[0] && intrinsic.bufferEquals(ext.children[0].bytes, sctOid)) { removed++; continue; }
|
|
1095
|
-
intrinsic.
|
|
1108
|
+
intrinsic.append(kept, ext.bytes);
|
|
1096
1109
|
}
|
|
1097
1110
|
if (removed === 0) throw _ctErr("ct/no-sct-extension", "the certificate carries no signedCertificateTimestampList extension (RFC 6962 sec. 3.2)");
|
|
1098
1111
|
var tbsChildren = [];
|
|
1099
1112
|
for (var k = 0; k < tbs.children.length; k++) {
|
|
1100
|
-
if (k !== wrapIdx) { intrinsic.
|
|
1101
|
-
if (kept.length > 0) intrinsic.
|
|
1113
|
+
if (k !== wrapIdx) { intrinsic.append(tbsChildren, tbs.children[k].bytes); continue; }
|
|
1114
|
+
if (kept.length > 0) intrinsic.append(tbsChildren, asn1.build.explicit(3, asn1.build.sequence(kept)));
|
|
1102
1115
|
}
|
|
1103
|
-
var issuerKeyHash =
|
|
1116
|
+
var issuerKeyHash = guard.crypto.digest("sha256", issSpki);
|
|
1104
1117
|
return { entryType: 1, tbsCertificate: asn1.build.sequence(tbsChildren), issuerKeyHash: issuerKeyHash };
|
|
1105
1118
|
}
|
|
1106
1119
|
|
|
@@ -2017,7 +2030,7 @@ async function _addChain(opts, message, entryType) {
|
|
|
2017
2030
|
? _bufferAlloc(0) : _b64Field(doc, "extensions", "ct/bad-sct", "sct"),
|
|
2018
2031
|
signature: _b64Field(doc, "signature", "ct/bad-sct", "sct"),
|
|
2019
2032
|
};
|
|
2020
|
-
var pinnedId =
|
|
2033
|
+
var pinnedId = guard.crypto.digest("sha256", pinnedKey);
|
|
2021
2034
|
if (_bufferCompare(sct.logId, pinnedId) !== 0) {
|
|
2022
2035
|
throw _ctErr("ct/sct-untrusted", "the SCT names log id " + _bufToString(sct.logId, "hex") +
|
|
2023
2036
|
" where the pinned key's id is " + _bufToString(pinnedId, "hex") + " -- it is not this log's receipt");
|