@blamejs/pki 0.5.7 → 0.5.8
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 +405 -386
- package/MIGRATING.md +43 -0
- package/README.md +12 -12
- package/lib/acme.js +31 -31
- package/lib/asn1-der.js +10 -10
- package/lib/attrcert-sign.js +19 -19
- package/lib/byte-reader.js +6 -6
- package/lib/byte-writer.js +5 -5
- package/lib/cbor-det.js +27 -24
- package/lib/cmc-build.js +97 -92
- package/lib/cmc-verify.js +106 -30
- package/lib/cmp-build.js +25 -25
- package/lib/cmp-session.js +70 -70
- package/lib/cmp-verify.js +71 -57
- package/lib/cms-compress.js +7 -7
- package/lib/cms-decrypt.js +90 -72
- package/lib/cms-encrypt.js +32 -32
- package/lib/cms-sign.js +74 -55
- package/lib/cms-verify.js +97 -75
- package/lib/composite-sig.js +13 -13
- package/lib/constants.js +4 -4
- package/lib/crl-sign.js +22 -22
- package/lib/crl-verify.js +7 -6
- package/lib/crmf-sign.js +14 -14
- package/lib/csr-sign.js +8 -8
- package/lib/ct.js +37 -37
- package/lib/edwards-point.js +7 -7
- package/lib/est.js +98 -55
- package/lib/framework-error.js +5 -5
- package/lib/guard-all.js +3 -3
- package/lib/guard-async.js +4 -4
- package/lib/guard-bytes.js +79 -79
- package/lib/guard-compress.js +17 -17
- package/lib/guard-crypto.js +1 -1
- package/lib/guard-encoding.js +15 -15
- package/lib/guard-header.js +3 -3
- package/lib/guard-identifier.js +16 -16
- package/lib/guard-json.js +15 -15
- package/lib/guard-limits.js +7 -7
- package/lib/guard-name.js +81 -16
- package/lib/guard-parsed.js +80 -80
- package/lib/guard-range.js +19 -19
- package/lib/guard-secret.js +11 -10
- package/lib/guard-text.js +6 -6
- package/lib/guard-time.js +10 -10
- package/lib/hpke.js +18 -17
- package/lib/http-digest.js +35 -35
- package/lib/http-retry-after.js +13 -13
- package/lib/http-transport.js +20 -19
- package/lib/inspect.js +53 -53
- package/lib/ip-utils.js +2 -2
- package/lib/jose.js +13 -13
- package/lib/key.js +16 -16
- package/lib/lint.js +51 -51
- package/lib/merkle.js +51 -36
- package/lib/mime.js +18 -18
- package/lib/ocsp-verify.js +10 -10
- package/lib/ocsp.js +13 -13
- package/lib/oid.js +29 -29
- package/lib/path-validate.js +114 -113
- package/lib/pbes2.js +16 -16
- package/lib/pkcs12-build.js +53 -53
- package/lib/pki-build.js +21 -19
- package/lib/rc2.js +1 -1
- package/lib/rfc3339.js +5 -5
- package/lib/schema-all.js +31 -31
- package/lib/schema-attrcert.js +12 -12
- package/lib/schema-c509.js +144 -142
- package/lib/schema-cmc.js +58 -58
- package/lib/schema-cmp.js +43 -43
- package/lib/schema-cms.js +45 -45
- package/lib/schema-crl.js +7 -7
- package/lib/schema-crmf.js +28 -28
- package/lib/schema-csr.js +12 -12
- package/lib/schema-csrattrs.js +16 -16
- package/lib/schema-engine.js +18 -18
- package/lib/schema-ocsp.js +15 -15
- package/lib/schema-pkcs12.js +20 -20
- package/lib/schema-pkcs8.js +2 -2
- package/lib/schema-pkix.js +131 -126
- package/lib/schema-smime.js +19 -19
- package/lib/schema-tsp.js +12 -12
- package/lib/schema-x509.js +3 -3
- package/lib/shbs.js +18 -18
- package/lib/sign-scheme.js +13 -13
- package/lib/sigstore.js +10 -11
- package/lib/sleep.js +1 -1
- package/lib/smime.js +308 -96
- package/lib/tls-cert-compress.js +18 -18
- package/lib/trust.js +27 -27
- package/lib/tsp-sign.js +17 -17
- package/lib/validator-all.js +1 -1
- package/lib/validator-attcert.js +1 -1
- package/lib/validator-cose.js +43 -44
- package/lib/validator-keydesc.js +3 -3
- package/lib/validator-sig.js +13 -13
- package/lib/validator-tls.js +11 -11
- package/lib/validator-tpm.js +20 -19
- package/lib/webauthn-mds.js +66 -66
- package/lib/webauthn.js +33 -33
- package/lib/webcrypto.js +15 -15
- package/lib/x509-sign.js +13 -13
- package/package.json +3 -2
- package/sbom.cdx.json +6 -6
package/lib/crl-sign.js
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
// The signature matrix comes from the shared sign-scheme resolver (the same registry pki.x509.sign /
|
|
20
20
|
// pki.cms.sign drive), so a new algorithm is a registry row, never a branch here. The TBSCertList DER is
|
|
21
21
|
// assembled through the canonical asn1.build.* layer + the shared pki-build producing primitives; the
|
|
22
|
-
// strict schema-crl decoder round-trips it and OpenSSL cross-checks it.
|
|
23
|
-
// path-validate.verifyCrlSignature engine pki.path.crlChecker uses
|
|
22
|
+
// strict schema-crl decoder round-trips it and OpenSSL cross-checks it. Verify composes the single
|
|
23
|
+
// path-validate.verifyCrlSignature engine pki.path.crlChecker uses; there is no second, weaker verifier.
|
|
24
24
|
|
|
25
25
|
var asn1 = require("./asn1-der");
|
|
26
26
|
var oid = require("./oid");
|
|
@@ -51,7 +51,7 @@ function _signE(kind, message, cause) { return new CrlError("crl/" + kind, messa
|
|
|
51
51
|
function O(n) { return oid.byName(n); }
|
|
52
52
|
|
|
53
53
|
// The shared PKIX producing primitives (lib/pki-build.js), bound to the crl namespace so they keep the
|
|
54
|
-
// frozen crl/* codes (Hard rule #5
|
|
54
|
+
// frozen crl/* codes (Hard rule #5: one encode definition, no per-format hand-roll).
|
|
55
55
|
var _b = pkiBuild.makeBuilder({ ErrorClass: CrlError, prefix: "crl", O: O, NS: NS, NAME_SCHEMA: NAME_SCHEMA, SPKI_SCHEMA: SPKI_SCHEMA, EXT_DECODERS: {} });
|
|
56
56
|
var _encodeName = _b.encodeName, _isEmptyName = _b.isEmptyName, _reqDer = _b.reqDer,
|
|
57
57
|
_assertValidSpki = _b.assertValidSpki, _assertValidExtension = _b.assertValidExtension,
|
|
@@ -88,16 +88,16 @@ var REQUIRED_ENTRY_CRITICALITY = {};
|
|
|
88
88
|
|
|
89
89
|
// certificateIssuer (indirect CRLs) is deferred: pki.path.crlChecker skips a CRL carrying any critical entry
|
|
90
90
|
// extension other than reasonCode (it does not track the per-entry issuer of an indirect CRL), so a CRL
|
|
91
|
-
// signed with certificateIssuer would verify but never be authoritative for revocation. Reject it
|
|
92
|
-
// the object form and the pre-encoded hatch
|
|
91
|
+
// signed with certificateIssuer would verify but never be authoritative for revocation. Reject it, on both
|
|
92
|
+
// the object form and the pre-encoded hatch, until the checker processes indirect CRLs.
|
|
93
93
|
var _CERT_ISSUER_DEFERRED = "certificateIssuer (indirect CRLs) is not yet supported -- pki.path.crlChecker skips a CRL carrying any critical entry extension other than reasonCode, so an indirect CRL would never be authoritative for revocation. Re-enabled once the checker processes certificateIssuer / indirect CRLs.";
|
|
94
|
-
// The IDP indirectCRL flag marks the whole CRL as indirect, which crlChecker skips outright
|
|
94
|
+
// The IDP indirectCRL flag marks the whole CRL as indirect, which crlChecker skips outright. Deferred for
|
|
95
95
|
// the same reason as certificateIssuer, on both the object form and the pre-encoded hatch.
|
|
96
96
|
var _INDIRECT_IDP_DEFERRED = "issuingDistributionPoint indirectCRL (indirect CRLs) is not yet supported -- pki.path.crlChecker skips a CRL whose IDP marks it indirect, so it would never be authoritative for revocation. Re-enabled once the checker processes indirect CRLs.";
|
|
97
97
|
|
|
98
98
|
// ---- shared numeric / reason / key-id helpers ------------------------------
|
|
99
99
|
|
|
100
|
-
// A non-negative INTEGER (0..MAX) of at most 20 content octets
|
|
100
|
+
// A non-negative INTEGER (0..MAX) of at most 20 content octets: cRLNumber (sec. 5.2.3) and
|
|
101
101
|
// deltaCRLIndicator baseCRLNumber (sec. 5.2.4). b.integer has no size cap of its own (unlike serialInteger).
|
|
102
102
|
function _boundedInteger(v, label) {
|
|
103
103
|
var n;
|
|
@@ -113,7 +113,7 @@ function _boundedInteger(v, label) {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// Decode a pre-encoded Extension's extnValue (the trailing OCTET STRING's content) to one well-formed DER
|
|
116
|
-
// node, or reject
|
|
116
|
+
// node, or reject, so a recognized extension's value is validated on the escape hatch, not emitted opaque.
|
|
117
117
|
function _extInner(node, label) {
|
|
118
118
|
try { return asn1.decode(asn1.read.octetString(node.children[node.children.length - 1])); }
|
|
119
119
|
catch (e) { throw _err("crl/bad-input", "pre-encoded " + label + " extension value is not valid DER", e); }
|
|
@@ -193,7 +193,7 @@ function _validatePreEncodedIdp(inner) {
|
|
|
193
193
|
if (!inner.children || !inner.children.length) throw _err("crl/bad-idp", "pre-encoded issuingDistributionPoint MUST NOT be empty (RFC 5280 sec. 5.2.5)");
|
|
194
194
|
var f = schema.walk(_PRE_ENCODED_IDP_SCHEMA, inner, NS).fields;
|
|
195
195
|
// A present DEFAULT-FALSE flag encodes a default DER omits (X.690 sec. 11.5), so it is rejected
|
|
196
|
-
// whichever value it carries
|
|
196
|
+
// whichever value it carries, never counted as the FALSE it spells.
|
|
197
197
|
["onlyContainsUserCerts", "onlyContainsCACerts", "onlyContainsAttributeCerts"].forEach(function (name) {
|
|
198
198
|
if (f[name].present && f[name].value !== true) throw _err("crl/bad-idp", "pre-encoded issuingDistributionPoint " + name + " encodes its DEFAULT FALSE, which DER omits (X.690 sec. 11.5)");
|
|
199
199
|
});
|
|
@@ -305,7 +305,7 @@ function _buildCrlExtensions(spec, ctx) {
|
|
|
305
305
|
|
|
306
306
|
// The revokedCertificates entries (sec. 5.1.2.6 / 5.3). Each entry: serial + revocationDate + optional
|
|
307
307
|
// crlEntryExtensions (reasonCode ENUMERATED, invalidityDate GeneralizedTime-only, certificateIssuer, or a
|
|
308
|
-
// pre-encoded escape hatch). Returns { entries, anyExt }
|
|
308
|
+
// pre-encoded escape hatch). Returns { entries, anyExt }, where anyExt forces v2 (sec. 5.1.2.1).
|
|
309
309
|
function _buildRevoked(entryList, isDelta) {
|
|
310
310
|
if (!Array.isArray(entryList)) throw _err("crl/bad-input", "revoked must be an array of revoked-certificate entries");
|
|
311
311
|
var anyExt = false, seenSerials = {};
|
|
@@ -332,7 +332,7 @@ function _buildRevoked(entryList, isDelta) {
|
|
|
332
332
|
}
|
|
333
333
|
if (e.invalidityDate != null) {
|
|
334
334
|
guard.time.assertValid(e.invalidityDate, _err, "crl/bad-input", "invalidityDate");
|
|
335
|
-
// sec. 5.3.2
|
|
335
|
+
// sec. 5.3.2: always GeneralizedTime (never the UTCTime cutover), no fractional seconds.
|
|
336
336
|
pushE("invalidityDate", false, b.generalizedTime(e.invalidityDate));
|
|
337
337
|
}
|
|
338
338
|
if (e.certificateIssuer != null) throw _err("crl/bad-input", _CERT_ISSUER_DEFERRED);
|
|
@@ -482,15 +482,15 @@ function _sign(spec, issuer, opts) {
|
|
|
482
482
|
*
|
|
483
483
|
* The version is derived from the field set (v2 when any CRL or entry extension is present, else v1). The
|
|
484
484
|
* outer `signatureAlgorithm` is emitted from the same source as `tbsCertList.signature` (sec. 5.1.1.2); an
|
|
485
|
-
* empty revocation list omits `revokedCertificates`
|
|
485
|
+
* empty revocation list omits `revokedCertificates` instead of emitting an empty SEQUENCE (sec. 5.1.2.6);
|
|
486
486
|
* `reasonCode` is an ENUMERATED and `invalidityDate` is always GeneralizedTime (sec. 5.3.1/5.3.2);
|
|
487
487
|
* per-extension criticality is fixed by the RFC; and the produced signature is verified under the issuer
|
|
488
|
-
* key before return. A violation throws a typed `CrlError`; where the spec carries raw DER
|
|
489
|
-
* `Name` Buffer or a pre-encoded `Extension`
|
|
488
|
+
* key before return. A violation throws a typed `CrlError`; where the spec carries raw DER (an issuer
|
|
489
|
+
* `Name` Buffer or a pre-encoded `Extension`), a malformed leaf inside those bytes throws `Asn1Error`.
|
|
490
490
|
*
|
|
491
491
|
* @opts
|
|
492
492
|
* - `pem` (boolean) -- return a PEM `X509 CRL` string instead of DER.
|
|
493
|
-
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS
|
|
493
|
+
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS instead of PKCS#1 v1.5.
|
|
494
494
|
* - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
|
|
495
495
|
* @example
|
|
496
496
|
* var pair = await pki.key.generate("Ed25519");
|
|
@@ -584,8 +584,8 @@ function _issuerMaySign(parsed, cert) {
|
|
|
584
584
|
* EdDSA low-order-point gates -- so there is no second, weaker CRL verifier. It fails closed to `false` on
|
|
585
585
|
* any resolution, import, or verification fault; malformed input throws a typed `CrlError`.
|
|
586
586
|
*
|
|
587
|
-
* Given a
|
|
588
|
-
* the certificate is the issuer this CRL names, and that its keyUsage
|
|
587
|
+
* Given a certificate in place of a bare key, it also asks what only a certificate can answer: that
|
|
588
|
+
* the certificate is the issuer this CRL names, and that its keyUsage, when it carries one, asserts
|
|
589
589
|
* `cRLSign` (RFC 5280 sec. 4.2.1.3, the same rule this module's signing side already enforces). Either
|
|
590
590
|
* failing is `false`: a statement about the CRL, not about the caller's input, so trying each candidate
|
|
591
591
|
* issuer in turn still works. A signature verifying says only that SOME key signed these bytes; without
|
|
@@ -639,17 +639,17 @@ function _serialHexOf(serial) {
|
|
|
639
639
|
* PEM string, or a parsed CRL; `serialNumber` is a `BigInt`, a safe integer, a decimal / `0x`-hex string,
|
|
640
640
|
* or a magnitude `Buffer`. Returns the matching revoked-certificate entry (`{ serialNumber, serialNumberHex,
|
|
641
641
|
* revocationDate, crlEntryExtensions }`) or `null` when the serial is not listed. A structural lookup only --
|
|
642
|
-
* it does
|
|
642
|
+
* it does not verify the CRL signature or its currency; call `pki.crl.verify` / `pki.path.crlChecker` for that.
|
|
643
643
|
*
|
|
644
|
-
* It does check
|
|
645
|
-
* certificates a CRL speaks for, and this verb is given a serial and nothing else
|
|
646
|
-
* speaks for part of its issuer's certificates is refused
|
|
644
|
+
* It does check scope first, because a serial number means something only within the set of
|
|
645
|
+
* certificates a CRL speaks for, and this verb is given a serial and nothing else. So a CRL that
|
|
646
|
+
* speaks for part of its issuer's certificates is refused, never answered from:
|
|
647
647
|
*
|
|
648
648
|
* - A DELTA CRL lists changes since a base, so a serial in it may be there to say the certificate
|
|
649
649
|
* was RELEASED; read alone, the entry meaning "no longer revoked" reads as "revoked"
|
|
650
650
|
* (`crl/delta-not-authoritative`). Merge it with its base through `pki.path.crlChecker`.
|
|
651
651
|
* - An INDIRECT CRL carries entries for other issuers, whose serials are unrelated to yours
|
|
652
|
-
* (`crl/indirect-not-supported`)
|
|
652
|
+
* (`crl/indirect-not-supported`), as does any CRL carrying `certificateIssuer` on an entry
|
|
653
653
|
* while not declaring itself indirect, a contradiction about whose certificates it lists.
|
|
654
654
|
* - Any other `issuingDistributionPoint` narrows the CRL to one distribution point, one kind of
|
|
655
655
|
* certificate, or a subset of revocation reasons (`crl/scope-not-authoritative`). Which part
|
package/lib/crl-verify.js
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// Copyright (c) blamejs contributors
|
|
3
3
|
"use strict";
|
|
4
4
|
//
|
|
5
|
-
// @internal -- the
|
|
6
|
-
// route through
|
|
7
|
-
// second, weaker CRL verifier can drift. path-validate owns that engine
|
|
8
|
-
// here at its module load (setEngine).
|
|
9
|
-
// path-validate's module.exports
|
|
10
|
-
// SubjectPublicKeyInfo bytes, not a documented issuer shape
|
|
5
|
+
// @internal -- the single CRL signature-verify seam, shared by pki.path.crlChecker and pki.crl.verify
|
|
6
|
+
// so both route through one signature engine (algorithm-confusion + EdDSA low-order + composite
|
|
7
|
+
// gates) and no second, weaker CRL verifier can drift. path-validate owns that engine
|
|
8
|
+
// (_verifyWithSpki) and injects it here at its module load (setEngine). The seam lives in this
|
|
9
|
+
// internal module, not on path-validate's module.exports, which keeps it off the public pki.path
|
|
10
|
+
// surface: it takes the path-internal SubjectPublicKeyInfo bytes, not a documented issuer shape.
|
|
11
|
+
// It is not wired into index.js and is reached only by require.
|
|
11
12
|
|
|
12
13
|
var guard = require("./guard-all");
|
|
13
14
|
|
package/lib/crmf-sign.js
CHANGED
|
@@ -50,8 +50,8 @@ var KNOWN_SPEC_KEYS = { certReqId: 1, certTemplate: 1, controls: 1, regInfo: 1,
|
|
|
50
50
|
var KNOWN_BATCH_KEYS = { messages: 1 };
|
|
51
51
|
var KNOWN_TEMPLATE_KEYS = { version: 1, subject: 1, publicKey: 1, validity: 1, extensions: 1, issuer: 1 };
|
|
52
52
|
var REVOCATION_TEMPLATE_KEYS = { version: 1, subject: 1, publicKey: 1, validity: 1, extensions: 1, issuer: 1, serialNumber: 1 };
|
|
53
|
-
// The controls (RFC 4211 sec. 6) and regInfo (sec. 7) are
|
|
54
|
-
// regToken/authenticator/oldCertID/protocolEncrKey are controls
|
|
53
|
+
// The controls (RFC 4211 sec. 6) and regInfo (sec. 7) are disjoint AttributeTypeAndValue namespaces:
|
|
54
|
+
// regToken/authenticator/oldCertID/protocolEncrKey are controls and utf8Pairs is regInfo, so the object
|
|
55
55
|
// form validates each key against its own field's registry (a control name is not a valid regInfo, and
|
|
56
56
|
// vice versa). The key name equals the registered OID name; complex values (pkiPublicationInfo, certReq)
|
|
57
57
|
// ride the pre-encoded escape hatch. Each entry is the value encoder for that OID.
|
|
@@ -69,7 +69,7 @@ function _encodeOptionalValidity(validity) {
|
|
|
69
69
|
var nb = validity.notBefore, na = validity.notAfter;
|
|
70
70
|
if (nb == null && na == null) throw _err("crmf/bad-validity", "validity must contain notBefore or notAfter (RFC 4211 sec. 5)");
|
|
71
71
|
var parts = [];
|
|
72
|
-
// timeDer validates each instant (guard.time.assertValid throws on an Invalid Date)
|
|
72
|
+
// timeDer validates each instant (guard.time.assertValid throws on an Invalid Date) before the
|
|
73
73
|
// inverted-window comparison, so getTime() below cannot be NaN.
|
|
74
74
|
if (nb != null) parts.push(b.explicit(0, _b.timeDer(nb, "validity notBefore")));
|
|
75
75
|
if (na != null) parts.push(b.explicit(1, _b.timeDer(na, "validity notAfter")));
|
|
@@ -77,13 +77,13 @@ function _encodeOptionalValidity(validity) {
|
|
|
77
77
|
if (nb != null && na != null && nb.getTime() > na.getTime()) throw _err("crmf/bad-validity", "notBefore must not be after notAfter");
|
|
78
78
|
return b.implicit(4, b.sequence(parts));
|
|
79
79
|
}
|
|
80
|
-
// CertTemplate ::= SEQUENCE { [0..9] all IMPLICIT OPTIONAL }. A
|
|
81
|
-
// [2] / issuerUID [7] / subjectUID [8] (CA-assigned or deprecated, RFC 4211 sec. 5)
|
|
80
|
+
// CertTemplate ::= SEQUENCE { [0..9] all IMPLICIT OPTIONAL }. A request omits serialNumber [1] / signingAlg
|
|
81
|
+
// [2] / issuerUID [7] / subjectUID [8] (CA-assigned or deprecated, RFC 4211 sec. 5), and the builder never
|
|
82
82
|
// emits them. Fields are emitted in ascending tag order.
|
|
83
83
|
function _encodeCertTemplate(tpl, opts) {
|
|
84
84
|
if (!tpl || typeof tpl !== "object" || Buffer.isBuffer(tpl)) throw _err("crmf/bad-cert-template", "certTemplate must be an object");
|
|
85
|
-
// A
|
|
86
|
-
// RFC 9810 sec. 5.3.9) carries serialNumber [1] to name the certificate to revoke
|
|
85
|
+
// A request template omits serialNumber (CA-assigned, RFC 4211 sec. 5); a revocation template (CMP rr,
|
|
86
|
+
// RFC 9810 sec. 5.3.9) carries serialNumber [1] to name the certificate to revoke, allowed only then.
|
|
87
87
|
var allowed = (opts && opts.revocation) ? REVOCATION_TEMPLATE_KEYS : KNOWN_TEMPLATE_KEYS;
|
|
88
88
|
guard.identifier.assertKnownKeys(tpl, allowed, _err, "crmf/bad-input", "unknown certTemplate field ");
|
|
89
89
|
var fields = [];
|
|
@@ -158,8 +158,8 @@ function _encodeCertId(id) {
|
|
|
158
158
|
return b.sequence([_b.encodeGeneralName(id.issuer), _b.serialInteger(id.serialNumber)]);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
// ProofOfPossession. The signature arm (the default when a key is given): when the template carries
|
|
162
|
-
// subject and publicKey (complete), sign the CertRequest DER and
|
|
161
|
+
// ProofOfPossession. The signature arm (the default when a key is given): when the template carries both
|
|
162
|
+
// subject and publicKey (complete), sign the CertRequest DER and omit poposkInput; otherwise build a
|
|
163
163
|
// POPOSigningKeyInput (authInfo sender [0] GeneralName + the requested publicKey), sign its SEQUENCE, and
|
|
164
164
|
// carry it as poposkInput [0] (RFC 4211 sec. 4.1). raVerified is emitted only on an explicit opt-in.
|
|
165
165
|
function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
|
|
@@ -209,8 +209,8 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
|
|
|
209
209
|
* @related pki.schema.crmf.parse, pki.csr.sign
|
|
210
210
|
*
|
|
211
211
|
* Build and DER-encode an RFC 4211 `CertReqMessages`. `spec` describes one certificate request message (or
|
|
212
|
-
* pass `spec.messages
|
|
213
|
-
* 9483 `-1` sentinel is allowed), `certTemplate` (the requested certificate fields
|
|
212
|
+
* pass `spec.messages`, an array of specs, for a batch): `certReqId` (an integer, default 0; the RFC
|
|
213
|
+
* 9483 `-1` sentinel is allowed), `certTemplate` (the requested certificate fields: `subject`, `publicKey`
|
|
214
214
|
* (the SPKI DER of the key being certified), `validity` ({ notBefore, notAfter } Dates), `extensions` (an
|
|
215
215
|
* object of subjectAltName / keyUsage / extendedKeyUsage / basicConstraints / certificatePolicies /
|
|
216
216
|
* subjectKeyIdentifier, or pre-encoded Extension DER), and an optional `version` (2)), optional `controls`
|
|
@@ -219,14 +219,14 @@ function _buildProofOfPossession(pop, certReqDer, template, signingKey, opts) {
|
|
|
219
219
|
* REQUESTER's private key -- the private half of `certTemplate.publicKey`; the message carries a
|
|
220
220
|
* `POPOSigningKey` proof of possession signed with it (verified before the message is returned), exactly
|
|
221
221
|
* as a PKCS#10 CSR proves possession. The signature algorithm is resolved from the requested public key
|
|
222
|
-
* (RSA PKCS#1 v1.5 / PSS, ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm). `key` is optional
|
|
222
|
+
* (RSA PKCS#1 v1.5 / PSS, ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm). `key` is optional: omit it
|
|
223
223
|
* for a `raVerified` proof (opt in with `pop: { type: 'raVerified', raVerified: true }`). Returns DER, or a
|
|
224
224
|
* PEM block with `opts.pem` (the label is required). Malformed input throws a typed `CrmfError`.
|
|
225
225
|
* Certificate-request-message parsing is `pki.schema.crmf.parse`.
|
|
226
226
|
*
|
|
227
227
|
* @opts
|
|
228
228
|
* - `pem` (string) -- return a PEM block with this label instead of DER (e.g. "CERTIFICATE REQUEST MESSAGE").
|
|
229
|
-
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS
|
|
229
|
+
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS instead of PKCS#1 v1.5.
|
|
230
230
|
* - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
|
|
231
231
|
* @example
|
|
232
232
|
* var pair = await pki.key.generate("Ed25519");
|
|
@@ -298,7 +298,7 @@ function _build(spec, key, opts) {
|
|
|
298
298
|
* @spec RFC 4211
|
|
299
299
|
* @related pki.crmf.build
|
|
300
300
|
*
|
|
301
|
-
* Encode a bare RFC 4211 `CertTemplate` (the requested-certificate fields
|
|
301
|
+
* Encode a bare RFC 4211 `CertTemplate` (the requested-certificate fields: `subject`, `publicKey`,
|
|
302
302
|
* `validity`, requested `extensions`, an optional `version` 2, `issuer`) to canonical DER. This is the
|
|
303
303
|
* `certTemplate` interior of `pki.crmf.build`, exposed for the RFC 9810 CMP `rr` (revocation request) body,
|
|
304
304
|
* whose `RevDetails.certDetails` carries a `CertTemplate` naming the certificate to revoke. Returns the DER
|
package/lib/csr-sign.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* @nav Signing
|
|
8
8
|
* @title Certification requests
|
|
9
9
|
* @intro The PKCS#10 certification-request producing side. `pki.csr.sign` builds a
|
|
10
|
-
* `CertificationRequestInfo`, signs it with the
|
|
11
|
-
* CSR has no issuer), and emits a `CertificationRequest` (RFC 2986) that `pki.schema.csr.parse`,
|
|
10
|
+
* `CertificationRequestInfo`, signs it with the subject's own private key (proof of possession, since
|
|
11
|
+
* a CSR has no issuer), and emits a `CertificationRequest` (RFC 2986) that `pki.schema.csr.parse`,
|
|
12
12
|
* OpenSSL, and a CA enrollment pipeline all accept. Requested v3 extensions ride in a PKCS#9
|
|
13
13
|
* `extensionRequest` attribute (RFC 2985) a CA copies into the issued certificate. Parsing lives at
|
|
14
14
|
* `pki.schema.csr.parse`.
|
|
@@ -73,22 +73,22 @@ function _challengePassword(pw) {
|
|
|
73
73
|
* @defends forged-certification-request (CWE-347)
|
|
74
74
|
* @related pki.schema.csr.parse, pki.x509.sign
|
|
75
75
|
*
|
|
76
|
-
* Build, sign, and DER-encode a PKCS#10 certification request. `spec` describes the request
|
|
76
|
+
* Build, sign, and DER-encode a PKCS#10 certification request. `spec` describes the request: `subject`
|
|
77
77
|
* (a common-name string, an array of RDNs, or raw Name DER; MAY be empty), `subjectPublicKey` (the SPKI
|
|
78
|
-
* DER of the key being certified), and optional `extensionRequest` (requested v3 extensions
|
|
78
|
+
* DER of the key being certified), and optional `extensionRequest` (requested v3 extensions, as an object
|
|
79
79
|
* of subjectAltName / keyUsage / extendedKeyUsage / basicConstraints / certificatePolicies /
|
|
80
80
|
* subjectKeyIdentifier, or an array of pre-encoded Extension DER) and `challengePassword`. `key` (or
|
|
81
|
-
* `{ key }`) is the
|
|
81
|
+
* `{ key }`) is the subject's own PKCS#8 private key / WebCrypto CryptoKey, so the request is self-signed
|
|
82
82
|
* to prove possession of the private half of `subjectPublicKey`, and that proof is verified before the
|
|
83
83
|
* request is returned. The signature algorithm is resolved from the subject key (RSA PKCS#1 v1.5 or PSS,
|
|
84
84
|
* ECDSA, EdDSA, ML-DSA, SLH-DSA, or a composite arm). Returns DER, or a PEM `CERTIFICATE REQUEST` with
|
|
85
|
-
* `opts.pem`. Malformed input throws a typed `CsrError`; where the spec carries raw DER
|
|
86
|
-
* Buffer, a pre-encoded requested `Extension` or `Attribute`
|
|
85
|
+
* `opts.pem`. Malformed input throws a typed `CsrError`; where the spec carries raw DER (a `Name`
|
|
86
|
+
* Buffer, a pre-encoded requested `Extension` or `Attribute`) a malformed leaf inside those bytes
|
|
87
87
|
* throws `Asn1Error` instead. Certificate-request parsing is `pki.schema.csr.parse`.
|
|
88
88
|
*
|
|
89
89
|
* @opts
|
|
90
90
|
* - `pem` (boolean) -- return a PEM `CERTIFICATE REQUEST` string instead of DER.
|
|
91
|
-
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS
|
|
91
|
+
* - `pss` (boolean) -- sign an RSA key with RSASSA-PSS instead of PKCS#1 v1.5.
|
|
92
92
|
* - `digestAlgorithm` (string) -- override the message digest where the algorithm permits a choice.
|
|
93
93
|
* @example
|
|
94
94
|
* var pair = await pki.key.generate("Ed25519");
|
package/lib/ct.js
CHANGED
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
* certificate timestamps.
|
|
16
16
|
*
|
|
17
17
|
* The SCT payload is encoded in the TLS presentation language (RFC 8446 sec. 3 /
|
|
18
|
-
* RFC 5246 sec. 4 conventions)
|
|
19
|
-
* integers and length-prefixed opaque vectors
|
|
20
|
-
* owns a bounded big-endian TLS-struct reader
|
|
18
|
+
* RFC 5246 sec. 4 conventions): positional, tag-less, fixed-width big-endian
|
|
19
|
+
* integers and length-prefixed opaque vectors, never ASN.1/DER. So this module
|
|
20
|
+
* owns a bounded big-endian TLS-struct reader instead of composing the DER
|
|
21
21
|
* schema engine; the only ASN.1 surface is the sec. 3.3 double wrap (the
|
|
22
22
|
* extension value is a DER OCTET STRING whose content is another DER OCTET
|
|
23
|
-
* STRING whose content is the TLS list
|
|
23
|
+
* STRING whose content is the TLS list; the certificate/OCSP layer peels the
|
|
24
24
|
* outer, this module peels the inner).
|
|
25
25
|
*
|
|
26
|
-
* Structure is decoded, crypto is surfaced
|
|
27
|
-
* (32 raw bytes
|
|
26
|
+
* Structure is decoded, crypto is surfaced raw: each SCT surfaces its `logId`
|
|
27
|
+
* (32 raw bytes, the SHA-256 of the log's SPKI, never recomputed), the exact
|
|
28
28
|
* `timestamp` as a BigInt, the raw `extensions`, the named-but-not-interpreted
|
|
29
|
-
* `hashAlg`/`sigAlg` code points, and the raw `signature`. The parser
|
|
30
|
-
* verifies a signature, recomputes a LogID, or trusts a log
|
|
29
|
+
* `hashAlg`/`sigAlg` code points, and the raw `signature`. The parser never
|
|
30
|
+
* verifies a signature, recomputes a LogID, or trusts a log. A verifier
|
|
31
31
|
* composes `webcrypto` over `reconstructSignedData(...)`, the exact
|
|
32
32
|
* `digitally-signed` preimage. DER-only carrier, fail-closed.
|
|
33
33
|
*
|
|
@@ -58,10 +58,10 @@ var C = constants;
|
|
|
58
58
|
|
|
59
59
|
// (code, message[, cause]) -> CtError, the factory the composed guards + the shared transport throw
|
|
60
60
|
// through so a malformed value keeps the ct/* typed verdict (the optional cause carries the underlying
|
|
61
|
-
// transport / evaluation error; existing 2-arg callers pass undefined
|
|
61
|
+
// transport / evaluation error; existing 2-arg callers pass undefined, so the change preserves behavior).
|
|
62
62
|
function _ctErr(c, m, cause) { return new CtError(c, m, cause); }
|
|
63
63
|
|
|
64
|
-
// RFC 5246 sec. 7.4.1.4.1 code points
|
|
64
|
+
// RFC 5246 sec. 7.4.1.4.1 code points: 1-byte values, never OIDs. Surfaced named; an
|
|
65
65
|
// unknown code surfaces as its numeric byte with a null name (never rejected --
|
|
66
66
|
// off-profile-pair rejection is a verifier-tier log-conformance concern).
|
|
67
67
|
var HASH_ALGORITHMS = {
|
|
@@ -75,7 +75,7 @@ var SCT_MIN_BODY = 47;
|
|
|
75
75
|
var LOGID_BYTES = 32;
|
|
76
76
|
var MAX_SAFE = 9007199254740991n; // 2^53 - 1; above this a Number loses precision
|
|
77
77
|
|
|
78
|
-
// ---- TlsReader
|
|
78
|
+
// ---- TlsReader: the RFC 6962 TLS-vector cursor, the shared bounded big-endian
|
|
79
79
|
// ByteReader engine primitive bound to the ct/* fault domain. A lying inner length
|
|
80
80
|
// can overrun only the current sub-reader's `end`, never the parent buffer, so
|
|
81
81
|
// bounds-before-slice is structural (see lib/byte-reader.js).
|
|
@@ -102,7 +102,7 @@ function _toBuffer(v, field) {
|
|
|
102
102
|
// version this parser does not define is preserved OPAQUE, not rejected: RFC 6962
|
|
103
103
|
// sec. 3.3 gives every SerializedSCT its own length prefix precisely so a client "can
|
|
104
104
|
// still parse old SCTs while skipping over new SCTs whose versions they don't
|
|
105
|
-
// understand"
|
|
105
|
+
// understand", so an unknown version yields { unknown, version, rawSct } and the
|
|
106
106
|
// v1-specific field decode (and the 47-byte floor) is skipped.
|
|
107
107
|
function _parseSct(r, sctLen) {
|
|
108
108
|
var bodyStart = r.pos;
|
|
@@ -156,8 +156,8 @@ function _parseSct(r, sctLen) {
|
|
|
156
156
|
* `extensions` (raw Buffer), `hashAlg` / `sigAlg` (1-byte code points) + a named
|
|
157
157
|
* `signatureAlgorithm`, the raw `signature` Buffer, and `rawSct` (the full
|
|
158
158
|
* SerializedSCT body). A SerializedSCT whose version this parser does not define
|
|
159
|
-
* is preserved
|
|
160
|
-
*
|
|
159
|
+
* is preserved opaque in `unknownScts` as `{ version, rawSct }` and does not fail
|
|
160
|
+
* the list: RFC 6962 sec. 3.3 frames each SerializedSCT with its own length
|
|
161
161
|
* so unknown versions are skippable (forward compatibility). `all` lists every
|
|
162
162
|
* SerializedSCT (known and unknown) in the exact wire order, so
|
|
163
163
|
* `encodeSctList(all)` reproduces the list byte-identically even when the two
|
|
@@ -166,7 +166,7 @@ function _parseSct(r, sctLen) {
|
|
|
166
166
|
* The extension value is a DER `OCTET STRING` wrapping the TLS-encoded list
|
|
167
167
|
* (RFC 6962 sec. 3.3 double wrap); everything below that peel is TLS presentation
|
|
168
168
|
* language, decoded with a bounded cursor. Structure is decoded, crypto is
|
|
169
|
-
* surfaced
|
|
169
|
+
* surfaced raw: the signature is never verified and the LogID never recomputed.
|
|
170
170
|
*
|
|
171
171
|
* Throws `CtError` with a stable `ct/*` code on any malformed input (a bad inner
|
|
172
172
|
* DER wrap is `ct/bad-der` with the `asn1/*` fault as `.cause`), never a raw
|
|
@@ -249,7 +249,7 @@ function _u24Bytes(n) {
|
|
|
249
249
|
* Rebuild the exact `digitally-signed` preimage bytes an external verifier
|
|
250
250
|
* hashes to check an SCT's signature (RFC 6962 sec. 3.2), for a parsed `sct`.
|
|
251
251
|
* `entry` selects the log-entry arm:
|
|
252
|
-
* - `{ entryType: 0, leafCert: <DER Buffer> }
|
|
252
|
+
* - `{ entryType: 0, leafCert: <DER Buffer> }`, an SCT delivered over TLS /
|
|
253
253
|
* OCSP, signed over `x509_entry(0)` with the leaf certificate.
|
|
254
254
|
* - `{ entryType: 1, tbsCertificate: <DER Buffer>, issuerKeyHash: <32B> }` --
|
|
255
255
|
* an SCT EMBEDDED in a certificate, signed over `precert_entry(1)` with the
|
|
@@ -378,12 +378,12 @@ function _spkiAlg(spki) {
|
|
|
378
378
|
* (`{ entryType: 0, leafCert }` or `{ entryType: 1, tbsCertificate, issuerKeyHash }`,
|
|
379
379
|
* as for `reconstructSignedData`), `sct` a decoded v1 SCT from `parseSctList().scts[]`,
|
|
380
380
|
* and `logPublicKey` the log's SubjectPublicKeyInfo (DER `Buffer`). Reconstructs the exact
|
|
381
|
-
* signed data, imports the log key, and verifies the SCT signature
|
|
381
|
+
* signed data, imports the log key, and verifies the SCT signature. An ECDSA signature is
|
|
382
382
|
* routed through the strict DER ECDSA-Sig-Value conformance gate before conversion to the
|
|
383
383
|
* raw r||s WebCrypto expects, an RSA signature verifies directly.
|
|
384
384
|
*
|
|
385
385
|
* Resolves `true` on a valid signature and `false` on a cryptographic mismatch (a false
|
|
386
|
-
* verdict is a verdict). Throws a typed `CtError` on structural failure
|
|
386
|
+
* verdict is a verdict). Throws a typed `CtError` on structural failure: a malformed
|
|
387
387
|
* entry/SCT, an unusable log key, or an unsupported hash/signature algorithm.
|
|
388
388
|
*
|
|
389
389
|
* @example
|
|
@@ -492,12 +492,12 @@ function _encodeSctBody(sct) {
|
|
|
492
492
|
* @spec RFC 6962, RFC 5246
|
|
493
493
|
* @related pki.ct.parseSctList, pki.ct.signSct
|
|
494
494
|
*
|
|
495
|
-
* Build the value of an RFC 6962 SCT-list extension from an array of SCTs
|
|
495
|
+
* Build the value of an RFC 6962 SCT-list extension from an array of SCTs: the exact
|
|
496
496
|
* inverse of `parseSctList`, such that `parseSctList(encodeSctList(list.all))` round-trips to
|
|
497
497
|
* identical bytes. Each element is either a decoded v1 SCT (the shape `parseSctList().scts[]`
|
|
498
498
|
* or `signSct` returns: `version` 0, 32-byte `logId`, `timestamp` BigInt, raw `extensions`,
|
|
499
|
-
* `hashAlg` / `sigAlg` code points, raw `signature`)
|
|
500
|
-
* 6962 sec. 3.2 field order
|
|
499
|
+
* `hashAlg` / `sigAlg` code points, raw `signature`), rebuilt from its fields in the RFC
|
|
500
|
+
* 6962 sec. 3.2 field order, or an opaque non-v1 entry (`{ version, rawSct }`) whose
|
|
501
501
|
* `rawSct` is re-emitted verbatim (forward compatibility, sec. 3.3). Pass `parseSctList().all`
|
|
502
502
|
* (not `.scts`) to preserve the exact wire order and every unknown-version entry.
|
|
503
503
|
*
|
|
@@ -568,7 +568,7 @@ function _logKeyMaterial(logKey) {
|
|
|
568
568
|
* @related pki.ct.verifySct, pki.ct.reconstructSignedData, pki.ct.encodeSctList
|
|
569
569
|
*
|
|
570
570
|
* Perform a Certificate Transparency log's signing step (RFC 6962 sec. 3.2): rebuild the exact
|
|
571
|
-
* `digitally-signed` preimage over `entry` (via `reconstructSignedData`, the
|
|
571
|
+
* `digitally-signed` preimage over `entry` (via `reconstructSignedData`, the same builder the
|
|
572
572
|
* verifier hashes), sign it with the log's private key, and return a fully-formed v1 SCT that
|
|
573
573
|
* `verifySct` accepts against the log's public key. `entry` is the log entry the SCT covers
|
|
574
574
|
* (`{ entryType: 0, leafCert }` or `{ entryType: 1, tbsCertificate, issuerKeyHash }`, as for
|
|
@@ -576,7 +576,7 @@ function _logKeyMaterial(logKey) {
|
|
|
576
576
|
* or a node `KeyObject`).
|
|
577
577
|
*
|
|
578
578
|
* The log-key profile is RFC 6962 sec. 2.1.4: ECDSA NIST P-256 (`sigAlg` 3) or RSA >= 2048
|
|
579
|
-
* (`sigAlg` 1), SHA-256 only
|
|
579
|
+
* (`sigAlg` 1), SHA-256 only; an unsupported key fails closed `ct/unsupported-algorithm`. The
|
|
580
580
|
* `logId` is derived as SHA-256 of the log SPKI (sec. 3.4); a supplied `opts.logId` must match.
|
|
581
581
|
* The returned SCT is the parseSctList/verifySct shape and composes with `encodeSctList`.
|
|
582
582
|
*
|
|
@@ -715,17 +715,17 @@ function _logsAgree(a, b) {
|
|
|
715
715
|
* @related pki.ct.verifySctWithLogList, pki.ct.verifySct
|
|
716
716
|
*
|
|
717
717
|
* Ingest a Certificate Transparency log-list JSON document (the `log_list.json` browsers consume) into a
|
|
718
|
-
* set of constraint-carrying trusted logs, keyed by log-id. `json` is a Buffer or string
|
|
718
|
+
* set of constraint-carrying trusted logs, keyed by log-id. `json` is a Buffer or string; the caller
|
|
719
719
|
* supplies the already-fetched, already-authenticated bytes (offline, no network fetch). Parsing routes
|
|
720
720
|
* through the bounded, duplicate-member-rejecting JSON reader; for each log it base64-decodes the `key`
|
|
721
721
|
* to its DER SubjectPublicKeyInfo, validates it as a well-formed on-profile key, **recomputes**
|
|
722
|
-
* `SHA-256(SPKI)` and fail-closed **requires** it equal the stated `log_id` (RFC 6962 sec. 3.2
|
|
722
|
+
* `SHA-256(SPKI)` and fail-closed **requires** it equal the stated `log_id` (RFC 6962 sec. 3.2; a log
|
|
723
723
|
* whose stated id disagrees with its key is refused as `ct/log-id-mismatch`), and decodes the `state`
|
|
724
724
|
* (exactly one of pending/qualified/usable/readonly/retired/rejected) and `temporal_interval`. Returns
|
|
725
725
|
* `{ logs, byLogId, version, timestamp }` where each log is `{ logId, logIdHex, key, description, url, mmd,
|
|
726
726
|
* operator, state: { name, since, trusted, conditional }, temporalInterval, trusted }`, `byLogId` is a
|
|
727
727
|
* null-proto `{ logIdHex: log }` map, `version` is the document's version string (or null), and `timestamp`
|
|
728
|
-
* is the parsed `log_list_timestamp` `Date` (or null when absent/unparseable
|
|
728
|
+
* is the parsed `log_list_timestamp` `Date` (or null when absent/unparseable; the staleness surface, read
|
|
729
729
|
* leniently, never a throw). Every malformed / oversized / mis-bound input is a typed `CtError`.
|
|
730
730
|
*
|
|
731
731
|
* @example
|
|
@@ -810,8 +810,8 @@ function _resolveNotAfter(entry, opts) {
|
|
|
810
810
|
* the log is resolved by `sct.logIdHex` (an unknown log is `ct/log-not-found`). The log's **state** gates
|
|
811
811
|
* trust (usable/qualified/readonly proceed; a retired log proceeds only for an SCT timestamped before its
|
|
812
812
|
* retirement instant; pending/rejected are `ct/log-untrusted`); its **temporal_interval** gates the
|
|
813
|
-
* covered certificate
|
|
814
|
-
* `opts.certNotAfter
|
|
813
|
+
* covered certificate: the cert's `notAfter`, from `entry.leafCert` when `entryType` is 0 or from
|
|
814
|
+
* `opts.certNotAfter`, must fall in `[start_inclusive, end_exclusive)`, and a windowed log with no
|
|
815
815
|
* resolvable notAfter is `ct/temporal-interval`, never silently skipped). Then the crypto is delegated to
|
|
816
816
|
* the shipped `verifySct(entry, sct, log.key)` (which independently re-checks `logId == SHA-256(key)`).
|
|
817
817
|
* Resolves `true` for a valid signature from a trusted, in-window log; `false` on a cryptographic
|
|
@@ -870,9 +870,9 @@ async function verifySctWithLogList(entry, sct, logList, opts) {
|
|
|
870
870
|
* @related pki.ct.parseLogList, pki.ct.verifySct
|
|
871
871
|
*
|
|
872
872
|
* Verify the detached signature published alongside the Certificate Transparency log list (the
|
|
873
|
-
* `log_list.sig` over `log_list.json`). `json` is the
|
|
874
|
-
* as a string
|
|
875
|
-
* `publicKey` is the caller-
|
|
873
|
+
* `log_list.sig` over `log_list.json`). `json` is the raw log-list bytes (a Buffer, or the fetched text
|
|
874
|
+
* as a string, verified byte-for-byte and never re-serialized), `signature` is the detached signature, and
|
|
875
|
+
* `publicKey` is the caller-pinned signer SubjectPublicKeyInfo (DER; there is no baked-in key). The scheme
|
|
876
876
|
* is RSASSA-PKCS1-v1.5 with SHA-256 over an RSA key (the deployed scheme; an EC P-256 / ECDSA-SHA-256 arm
|
|
877
877
|
* is accepted for future-proofing). Resolves `true` for a valid signature, `false` on a cryptographic
|
|
878
878
|
* mismatch (a verdict). Fail-closed forgery defenses throw before any verify: an RSA public exponent below
|
|
@@ -1010,27 +1010,27 @@ function _fetchBody(transport, url, req, label) {
|
|
|
1010
1010
|
* @spec RFC 6962
|
|
1011
1011
|
* @related pki.ct.parseLogList, pki.ct.verifyLogListSignature, pki.ct.verifySctWithLogList
|
|
1012
1012
|
*
|
|
1013
|
-
* Fetch the Certificate Transparency log list live and return the trusted-log set
|
|
1013
|
+
* Fetch the Certificate Transparency log list live and return the trusted-log set only after the detached
|
|
1014
1014
|
* signature verifies against the caller-pinned distributor key. It GETs `opts.url` (the `log_list.json`)
|
|
1015
1015
|
* and the detached `opts.sigUrl` (the `log_list.sig`, by default `opts.url` with a `.json` path suffix
|
|
1016
1016
|
* rewritten to `.sig`) over the shared, fail-closed `pki.transport` (or an injected `opts.transport`), then
|
|
1017
|
-
* verifies the detached signature over the
|
|
1018
|
-
* strict `true` verdict ingests
|
|
1017
|
+
* verifies the detached signature over the raw fetched JSON bytes against `opts.signerKey` and only on a
|
|
1018
|
+
* strict `true` verdict ingests those same bytes through `parseLogList`, so the client never parses, reads,
|
|
1019
1019
|
* caches, or surfaces any field of an unverified document (verify-before-parse). No baked-in vendor URL and
|
|
1020
|
-
* no baked-in key: the caller pins both out-of-band. Trust is
|
|
1020
|
+
* no baked-in key: the caller pins both out-of-band. Trust is explicit: an `opts.tls.anchors` set or an
|
|
1021
1021
|
* `opts.tls.useSystemStore` opt-in, `rejectUnauthorized` always on. The returned `timestamp` is surfaced
|
|
1022
1022
|
* (never policed) so the caller enforces its own freshness policy; chaining a resolved log to an SCT is the
|
|
1023
1023
|
* caller's `verifySctWithLogList` step. Every fetch / verify / parse failure is a typed `CtError`.
|
|
1024
1024
|
*
|
|
1025
1025
|
* @opts url REQUIRED -- the `log_list.json` URL; must be https (no baked-in vendor URL).
|
|
1026
|
-
* @opts signerKey REQUIRED
|
|
1026
|
+
* @opts signerKey REQUIRED and caller-pinned -- the distributor SubjectPublicKeyInfo as a DER Buffer; no baked-in key.
|
|
1027
1027
|
* @opts sigUrl OPTIONAL -- the detached `log_list.sig` URL (https, must share the log-list URL's origin); default `url` with `.json` -> `.sig` (a non-.json url requires an explicit sigUrl).
|
|
1028
1028
|
* @opts transport OPTIONAL injectable `transport(request) -> Promise<{status,headers,body}>` (default `pki.transport.https`); the test seam.
|
|
1029
1029
|
* @opts tls OPTIONAL `{ anchors, useSystemStore, cert, key, minVersion, servername, checkServerIdentity }` threaded to the default transport (ignored when a transport is injected); `rejectUnauthorized` is always on.
|
|
1030
1030
|
* @opts headers OPTIONAL extra request headers (the request-framing headers are stripped; the verb owns the GET method).
|
|
1031
1031
|
* @opts timeout OPTIONAL ms budget, default 30s (cap-validated).
|
|
1032
1032
|
* @opts maxResponseBytes OPTIONAL per-GET size cap, default 4 MiB, tightenable DOWNWARD only.
|
|
1033
|
-
* @opts requireJsonContentType OPTIONAL boolean
|
|
1033
|
+
* @opts requireJsonContentType OPTIONAL boolean (default false) -- opt in to a strict `ct/bad-content-type` gate on the JSON GET.
|
|
1034
1034
|
* @example
|
|
1035
1035
|
* // a live distributor uses the default pki.transport.https; here an injected transport returns the pair
|
|
1036
1036
|
* var r = await pki.ct.fetchLogList({ url: "https://ct.example/log_list.json", signerKey: googleSignerSpki,
|
package/lib/edwards-point.js
CHANGED
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
// OKP (Ed25519 / Ed448) credential public-key point.
|
|
7
7
|
//
|
|
8
8
|
// edwards-point -- point validation for the Edwards curves EdDSA uses. node:crypto imports
|
|
9
|
-
// an RFC 8410 OKP SubjectPublicKeyInfo
|
|
9
|
+
// an RFC 8410 OKP SubjectPublicKeyInfo without checking the point is a valid, full-order
|
|
10
10
|
// Edwards point (an all-zeroes Ed25519/Ed448 key parses, and an all-zeroes key with an
|
|
11
|
-
// all-zeroes signature even verifies
|
|
11
|
+
// all-zeroes signature even verifies true, a trivial forgery), so an OKP credential key
|
|
12
12
|
// needs an explicit check. This decodes the compressed point per RFC 8032 sec. 5.1.3
|
|
13
|
-
// (Ed25519) / sec. 5.2.3 (Ed448)
|
|
14
|
-
// encoding
|
|
15
|
-
// all-zeroes key is a low-order point
|
|
13
|
+
// (Ed25519) / sec. 5.2.3 (Ed448); the decode fails closed on an off-curve or non-canonical
|
|
14
|
+
// encoding. It then rejects a low-order point via the cofactor check [8]P == identity (an
|
|
15
|
+
// all-zeroes key is a low-order point on the curve, so the decode alone does not catch it).
|
|
16
16
|
// A conformant authenticator never emits a low-order credential key.
|
|
17
17
|
//
|
|
18
18
|
// Pure BigInt modular arithmetic (constant-time is not a goal here: the input is a public
|
|
@@ -93,8 +93,8 @@ function validate(raw, crv) {
|
|
|
93
93
|
// validateSpki(spkiBytes, crv, E, code) -> throws new E(code, ...) unless the
|
|
94
94
|
// SubjectPublicKeyInfo's subjectPublicKey (its BIT STRING body, past the unused-bits octet) is a
|
|
95
95
|
// canonical, on-curve, full-order Edwards point for the OKP curve (6 = Ed25519, 7 = Ed448). This
|
|
96
|
-
// is the one home every EdDSA verify path routes an SPKI through before importKey/verify
|
|
97
|
-
// imports a low-order (e.g. all-zeroes) OKP SPKI without complaint and such a key verifies a
|
|
96
|
+
// is the one home every EdDSA verify path routes an SPKI through before importKey/verify, because
|
|
97
|
+
// node imports a low-order (e.g. all-zeroes) OKP SPKI without complaint and such a key verifies a
|
|
98
98
|
// forged signature. Error-parameterized like the guard family: the caller passes its own typed
|
|
99
99
|
// error CONSTRUCTOR `E` and domain `code`, so the shared gate keeps no error domain of its own.
|
|
100
100
|
function validateSpki(spkiBytes, crv, E, code) {
|