@blamejs/pki 0.6.14 → 0.6.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -1
- package/lib/acme.js +59 -815
- package/lib/asn1-der.js +7 -245
- package/lib/attrcert-sign.js +12 -260
- package/lib/attrcert-verify.js +1 -7
- package/lib/byte-reader.js +0 -8
- package/lib/byte-writer.js +0 -11
- package/lib/cbor-det.js +0 -54
- package/lib/cmc-build.js +15 -357
- package/lib/cmc-verify.js +6 -239
- package/lib/cmp-build.js +41 -317
- package/lib/cmp-session.js +73 -760
- package/lib/cmp-verify.js +32 -488
- package/lib/cms-compress.js +3 -32
- package/lib/cms-decrypt.js +29 -294
- package/lib/cms-encrypt.js +14 -234
- package/lib/cms-sign.js +29 -324
- package/lib/cms-verify.js +9 -500
- package/lib/composite-kem.js +2 -102
- package/lib/composite-sig.js +1 -77
- package/lib/constants.js +0 -249
- package/lib/crl-sign.js +9 -237
- package/lib/crl-verify.js +1 -10
- package/lib/crmf-sign.js +19 -198
- package/lib/crmf-verify.js +1 -12
- package/lib/csr-sign.js +3 -34
- package/lib/csr-verify.js +1 -12
- package/lib/ct.js +28 -253
- package/lib/edwards-point.js +10 -44
- package/lib/est.js +28 -764
- package/lib/framework-error.js +4 -182
- package/lib/guard-all.js +1 -58
- package/lib/guard-async.js +1 -36
- package/lib/guard-bytes.js +6 -650
- package/lib/guard-compress.js +5 -102
- package/lib/guard-crypto.js +1 -40
- package/lib/guard-der.js +2 -93
- package/lib/guard-encoding.js +2 -60
- package/lib/guard-header.js +1 -36
- package/lib/guard-identifier.js +7 -531
- package/lib/guard-intrinsic.js +1 -150
- package/lib/guard-json.js +10 -73
- package/lib/guard-limits.js +1 -77
- package/lib/guard-list.js +1 -49
- package/lib/guard-name.js +3 -209
- package/lib/guard-parsed.js +7 -335
- package/lib/guard-range.js +1 -71
- package/lib/guard-secret.js +1 -102
- package/lib/guard-text.js +1 -64
- package/lib/guard-time.js +1 -97
- package/lib/hpke.js +3 -105
- package/lib/http-digest.js +14 -142
- package/lib/http-retry-after.js +1 -47
- package/lib/http-transport.js +21 -131
- package/lib/inspect.js +30 -284
- package/lib/ip-utils.js +6 -47
- package/lib/jose.js +7 -142
- package/lib/key.js +2 -67
- package/lib/lint.js +9 -194
- package/lib/merkle.js +1 -27
- package/lib/mime.js +9 -82
- package/lib/ocsp-request-verify.js +1 -14
- package/lib/ocsp-verify.js +1 -82
- package/lib/ocsp.js +11 -169
- package/lib/oid.js +5 -337
- package/lib/path-validate.js +103 -1492
- package/lib/pbes2.js +2 -89
- package/lib/pkcs12-build.js +31 -379
- package/lib/pki-build.js +64 -290
- package/lib/rc2.js +2 -18
- package/lib/rfc3339.js +4 -27
- package/lib/scep.js +140 -286
- package/lib/schema-all.js +0 -122
- package/lib/schema-attrcert.js +4 -146
- package/lib/schema-c509.js +153 -802
- package/lib/schema-cmc.js +5 -220
- package/lib/schema-cmp.js +2 -223
- package/lib/schema-cms.js +13 -427
- package/lib/schema-crl.js +5 -68
- package/lib/schema-crmf.js +6 -167
- package/lib/schema-csr.js +1 -54
- package/lib/schema-csrattrs.js +2 -87
- package/lib/schema-engine.js +0 -152
- package/lib/schema-ocsp.js +0 -117
- package/lib/schema-pkcs12.js +2 -93
- package/lib/schema-pkcs8.js +3 -35
- package/lib/schema-pkix.js +24 -655
- package/lib/schema-smime.js +0 -54
- package/lib/schema-tsp.js +3 -99
- package/lib/schema-x509.js +4 -63
- package/lib/shbs.js +11 -95
- package/lib/sign-scheme.js +2 -97
- package/lib/sigstore.js +9 -169
- package/lib/sleep.js +2 -8
- package/lib/smime.js +15 -408
- package/lib/tls-cert-compress.js +0 -62
- package/lib/trust.js +19 -185
- package/lib/tsp-sign.js +16 -270
- package/lib/validator-all.js +1 -30
- package/lib/validator-attcert.js +2 -46
- package/lib/validator-cose.js +2 -153
- package/lib/validator-keydesc.js +1 -37
- package/lib/validator-sig.js +1 -46
- package/lib/validator-tls.js +1 -88
- package/lib/validator-tpm.js +19 -122
- package/lib/verify-seam.js +1 -24
- package/lib/webauthn-mds.js +3 -400
- package/lib/webauthn.js +12 -833
- package/lib/webcrypto.js +5 -485
- package/lib/x509-sign.js +4 -108
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/crl-sign.js
CHANGED
|
@@ -16,12 +16,6 @@
|
|
|
16
16
|
* @spec RFC 5280
|
|
17
17
|
* @card Build, sign, and verify an X.509 CRL (RFC 5280 sec. 5) over any registry algorithm.
|
|
18
18
|
*/
|
|
19
|
-
//
|
|
20
|
-
// The signature matrix comes from the shared sign-scheme resolver (the same registry pki.x509.sign /
|
|
21
|
-
// pki.cms.sign drive), so a new algorithm is a registry row, never a branch here. The TBSCertList DER is
|
|
22
|
-
// assembled through the canonical asn1.build.* layer + the shared pki-build producing primitives; the
|
|
23
|
-
// strict schema-crl decoder round-trips it and OpenSSL cross-checks it. Verify composes the single
|
|
24
|
-
// path-validate.verifyCrlSignature engine pki.path.crlChecker uses; there is no second, weaker verifier.
|
|
25
19
|
|
|
26
20
|
var asn1 = require("./asn1-der");
|
|
27
21
|
var oid = require("./oid");
|
|
@@ -30,16 +24,13 @@ var x509Schema = require("./schema-x509");
|
|
|
30
24
|
var signScheme = require("./sign-scheme");
|
|
31
25
|
var guard = require("./guard-all");
|
|
32
26
|
var intrinsic = require("./guard-intrinsic");
|
|
33
|
-
// Membership is asked through an operation taken at load. Written out, the question reads two
|
|
34
|
-
// replaceable properties to answer one thing, and either of them answering true admits a name the
|
|
35
|
-
// table never carried -- here, a reason code the CRL profile does not define.
|
|
36
27
|
var _hasOwn = intrinsic.hasOwn;
|
|
37
28
|
var frameworkError = require("./framework-error");
|
|
38
29
|
var pkix = require("./schema-pkix");
|
|
39
30
|
var schema = require("./schema-engine");
|
|
40
31
|
var pkiBuild = require("./pki-build");
|
|
41
32
|
var constants = require("./constants");
|
|
42
|
-
require("./path-validate");
|
|
33
|
+
require("./path-validate");
|
|
43
34
|
var crlVerify = require("./crl-verify");
|
|
44
35
|
|
|
45
36
|
var CrlError = frameworkError.CrlError;
|
|
@@ -49,92 +40,52 @@ var SPKI_SCHEMA = pkix.spki(NS);
|
|
|
49
40
|
var b = asn1.build;
|
|
50
41
|
var TAGS = asn1.TAGS;
|
|
51
42
|
|
|
52
|
-
// Two error factories (x509-sign pattern): `_err` takes a full crl/* code; `_signE` prepends the domain so
|
|
53
|
-
// the shared sign-scheme resolver/signer faults keep crl/* codes. Both are FACTORIES (guard.* and
|
|
54
|
-
// resolveSignScheme invoke them as E(code, msg) with no `new`).
|
|
55
43
|
function _err(code, message, cause) { return new CrlError(code, message, cause); }
|
|
56
44
|
function _signE(kind, message, cause) { return new CrlError("crl/" + kind, message, cause); }
|
|
57
45
|
function O(n) { return oid.byName(n); }
|
|
58
46
|
|
|
59
|
-
// The shared PKIX producing primitives (lib/pki-build.js), bound to the crl namespace so they keep the
|
|
60
|
-
// frozen crl/* codes (Hard rule #5: one encode definition, no per-format hand-roll).
|
|
61
47
|
var _b = pkiBuild.makeBuilder({ ErrorClass: CrlError, prefix: "crl", O: O, NS: NS, NAME_SCHEMA: NAME_SCHEMA, SPKI_SCHEMA: SPKI_SCHEMA, EXT_DECODERS: {} });
|
|
62
48
|
var _encodeName = _b.encodeName, _isEmptyName = _b.isEmptyName, _reqDer = _b.reqDer,
|
|
63
49
|
_assertValidSpki = _b.assertValidSpki, _assertValidExtension = _b.assertValidExtension,
|
|
64
50
|
_timeDer = _b.timeDer, _ext = _b.ext, _extAki = _b.extAki, _spkiKeyId = _b.spkiKeyId,
|
|
65
51
|
_serialInteger = _b.serialInteger, _encodeGeneralName = _b.encodeGeneralName,
|
|
66
52
|
_certLikeFromSpki = _b.certLikeFromSpki, _assertSignatureVerifies = _b.assertSignatureVerifies;
|
|
67
|
-
// The RFC 5280 sec. 4.2.1 extension value decoders (shared with pki.schema.x509.parse), used to confirm an
|
|
68
|
-
// issuer certificate's keyUsage asserts cRLSign before a CRL is signed under it.
|
|
69
53
|
var EXT_DECODERS = pkix.certExtensionDecoders(NS).byOid;
|
|
70
54
|
|
|
71
55
|
var OID_SKI = O("subjectKeyIdentifier");
|
|
72
56
|
|
|
73
|
-
// CRLReason value<->name (RFC 5280 sec. 5.3.1). The value->name table is the frozen constants source;
|
|
74
|
-
// build the name->value reverse once at load. Value 7 is unused/reserved and absent from the table.
|
|
75
57
|
var CRL_REASON = constants.NAMES.CRL_REASON;
|
|
76
58
|
var REASON_BY_NAME = {};
|
|
77
59
|
Object.keys(CRL_REASON).forEach(function (v) { REASON_BY_NAME[CRL_REASON[v]] = Number(v); });
|
|
78
60
|
|
|
79
61
|
var KNOWN_CRL_EXT_KEYS = { authorityKeyIdentifier: 1, issuingDistributionPoint: 1, deltaCRLIndicator: 1, freshestCRL: 1, authorityInfoAccess: 1 };
|
|
80
|
-
// The three caller-owned objects pki.crl.sign reads. `crlNumber` and `extensions` are read by
|
|
81
|
-
// _buildCrlExtensions, which _sign hands the whole spec to, so they belong here even though _sign
|
|
82
|
-
// never names them -- the table is derived from what the call ACTUALLY reads, including through
|
|
83
|
-
// its delegations, not from the doc block.
|
|
84
62
|
var KNOWN_SPEC_KEYS = {
|
|
85
63
|
issuer: 1, thisUpdate: 1, nextUpdate: 1, revoked: 1, crlNumber: 1, extensions: 1,
|
|
86
64
|
};
|
|
87
|
-
// spec.issuer is the third place an issuer distinguished name can come from, and it is read only
|
|
88
|
-
// when neither issuer.cert nor issuer.name supplies one: both of those win outright. A spec naming
|
|
89
|
-
// one issuer beside a certificate for another was signed under the certificate's.
|
|
90
65
|
var KNOWN_SPEC_KEYS_ISSUER_NAMED = {
|
|
91
66
|
thisUpdate: 1, nextUpdate: 1, revoked: 1, crlNumber: 1, extensions: 1,
|
|
92
67
|
};
|
|
93
|
-
// Two FORMS, exactly as x509-sign has: when `issuer.cert` is present the builder takes the DN and
|
|
94
|
-
// the public key from it and never reads `name` or `publicKey`, so a union signed under the
|
|
95
|
-
// certificate's identity while discarding the one the caller wrote out.
|
|
96
68
|
var KNOWN_ISSUER_CERT_KEYS = { key: 1, cert: 1 };
|
|
97
69
|
var KNOWN_ISSUER_EXPLICIT_KEYS = { key: 1, name: 1, publicKey: 1 };
|
|
98
70
|
var KNOWN_SIGN_OPTS = { digestAlgorithm: 1, pem: 1, pss: 1 };
|
|
99
71
|
|
|
100
72
|
var KNOWN_IDP_KEYS = { fullName: 1, onlyContainsUserCerts: 1, onlyContainsCACerts: 1, onlyContainsAttributeCerts: 1, indirectCRL: 1 };
|
|
101
73
|
|
|
102
|
-
// The fields ONE revoked-certificate entry carries. Every optional one is encoded only when
|
|
103
|
-
// present, so a misspelling is missed by nothing and the entry is signed without it: `reson` for
|
|
104
|
-
// `reason` lists the certificate as revoked with no reasonCode at all, and a relying party reading
|
|
105
|
-
// the CRL cannot tell a caller who meant keyCompromise from one who gave no reason. The entry is
|
|
106
|
-
// where this bites hardest, because the CRL as a whole still verifies. `certificateIssuer` is
|
|
107
|
-
// listed because the builder DOES read it -- to refuse it with its own message.
|
|
108
74
|
var KNOWN_REVOKED_KEYS = {
|
|
109
75
|
serialNumber: 1, revocationDate: 1, reason: 1, invalidityDate: 1, certificateIssuer: 1, extensions: 1,
|
|
110
76
|
};
|
|
111
77
|
|
|
112
|
-
// RFC 5280 sec. 5.2 -- the profile-fixed criticality of each recognized CRL extension. cRLNumber (5.2.3),
|
|
113
|
-
// authorityKeyIdentifier (5.2.1), freshestCRL (5.2.6), and authorityInfoAccess (5.2.7) MUST be non-critical;
|
|
114
|
-
// issuingDistributionPoint (5.2.5) and deltaCRLIndicator (5.2.4) MUST be critical. Enforced on the object
|
|
115
|
-
// form by construction AND on the pre-encoded escape hatch, so the hatch cannot bypass the profile.
|
|
116
78
|
var REQUIRED_CRITICALITY = {};
|
|
117
79
|
[["cRLNumber", false], ["authorityKeyIdentifier", false], ["freshestCRL", false], ["authorityInfoAccess", false],
|
|
118
80
|
["issuingDistributionPoint", true], ["deltaCRLIndicator", true]].forEach(function (r) { REQUIRED_CRITICALITY[O(r[0])] = r[1]; });
|
|
119
81
|
|
|
120
|
-
// RFC 5280 sec. 5.3 -- the profile-fixed criticality of each recognized crlEntryExtension: reasonCode (5.3.1)
|
|
121
|
-
// and invalidityDate (5.3.2) MUST be non-critical; certificateIssuer (5.3.3) MUST be critical.
|
|
122
82
|
var REQUIRED_ENTRY_CRITICALITY = {};
|
|
123
83
|
[["reasonCode", false], ["invalidityDate", false]].forEach(function (r) { REQUIRED_ENTRY_CRITICALITY[O(r[0])] = r[1]; });
|
|
124
84
|
|
|
125
|
-
// certificateIssuer (indirect CRLs) is deferred: pki.path.crlChecker skips a CRL carrying any critical entry
|
|
126
|
-
// extension other than reasonCode (it does not track the per-entry issuer of an indirect CRL), so a CRL
|
|
127
|
-
// signed with certificateIssuer would verify but never be authoritative for revocation. Reject it, on both
|
|
128
|
-
// the object form and the pre-encoded hatch, until the checker processes indirect CRLs.
|
|
129
85
|
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.";
|
|
130
|
-
// The IDP indirectCRL flag marks the whole CRL as indirect, which crlChecker skips outright. Deferred for
|
|
131
|
-
// the same reason as certificateIssuer, on both the object form and the pre-encoded hatch.
|
|
132
86
|
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.";
|
|
133
87
|
|
|
134
|
-
// ---- shared numeric / reason / key-id helpers ------------------------------
|
|
135
88
|
|
|
136
|
-
// A non-negative INTEGER (0..MAX) of at most 20 content octets: cRLNumber (sec. 5.2.3) and
|
|
137
|
-
// deltaCRLIndicator baseCRLNumber (sec. 5.2.4). b.integer has no size cap of its own (unlike serialInteger).
|
|
138
89
|
function _boundedInteger(v, label) {
|
|
139
90
|
var n;
|
|
140
91
|
if (typeof v === "bigint") n = v;
|
|
@@ -148,13 +99,10 @@ function _boundedInteger(v, label) {
|
|
|
148
99
|
return { tlv: tlv, value: n };
|
|
149
100
|
}
|
|
150
101
|
|
|
151
|
-
// Decode a pre-encoded Extension's extnValue (the trailing OCTET STRING's content) to one well-formed DER
|
|
152
|
-
// node, or reject, so a recognized extension's value is validated on the escape hatch, not emitted opaque.
|
|
153
102
|
function _extInner(node, label) {
|
|
154
103
|
try { return asn1.decode(asn1.read.octetString(node.children[node.children.length - 1])); }
|
|
155
104
|
catch (e) { throw _err("crl/bad-input", "pre-encoded " + label + " extension value is not valid DER", e); }
|
|
156
105
|
}
|
|
157
|
-
// A pre-encoded cRLNumber / baseCRLNumber value: a non-negative INTEGER of at most 20 octets, or reject.
|
|
158
106
|
function _requireBoundedInt(inner, label) {
|
|
159
107
|
if (inner.tagClass !== "universal" || inner.tagNumber !== TAGS.INTEGER) throw _err("crl/bad-crl-number", "pre-encoded " + label + " value must be an INTEGER (RFC 5280 sec. 5.2.3 / 5.2.4)");
|
|
160
108
|
var v = asn1.read.integer(inner);
|
|
@@ -163,8 +111,6 @@ function _requireBoundedInt(inner, label) {
|
|
|
163
111
|
return v;
|
|
164
112
|
}
|
|
165
113
|
|
|
166
|
-
// Resolve a CRLReason to its numeric code: validate membership, reject 7/undefined, gate removeFromCRL(8)
|
|
167
|
-
// to a delta CRL. unspecified(0) is returned so the caller OMITS it (SHOULD be absent, sec. 5.3.1).
|
|
168
114
|
function _resolveReason(reason, isDelta) {
|
|
169
115
|
var codeNum;
|
|
170
116
|
if (typeof reason === "number") codeNum = reason;
|
|
@@ -177,24 +123,19 @@ function _resolveReason(reason, isDelta) {
|
|
|
177
123
|
return codeNum;
|
|
178
124
|
}
|
|
179
125
|
|
|
180
|
-
// The AKI keyIdentifier from the issuer: an explicit Buffer, or true -> the issuer cert's subjectKeyIdentifier,
|
|
181
|
-
// else the SHA-1 of the issuer SPKI (RFC 5280 sec. 5.2.1 key-identifier method).
|
|
182
126
|
function _akiKeyId(val, ctx) {
|
|
183
127
|
if (guard.bytes.isByteSource(val)) return guard.bytes.snapshotSource(val, CrlError, "crl/bad-input", "the authorityKeyIdentifier keyIdentifier");
|
|
184
128
|
if (val === true) {
|
|
185
129
|
if (ctx.issuerCert) {
|
|
186
130
|
var ski = (ctx.issuerCert.extensions || []).filter(function (e) { return e.oid === OID_SKI; })[0];
|
|
187
|
-
if (ski) { try { return asn1.read.octetString(asn1.decode(ski.value)); } catch (_e) {
|
|
131
|
+
if (ski) { try { return asn1.read.octetString(asn1.decode(ski.value)); } catch (_e) { } } // allow:swallow-unverified -- a malformed issuer SKI is read as absent; the authorityKeyIdentifier keyIdentifier is then derived from the issuer public key
|
|
188
132
|
}
|
|
189
133
|
return _spkiKeyId(ctx.issuerSpki);
|
|
190
134
|
}
|
|
191
135
|
throw _err("crl/bad-input", "authorityKeyIdentifier must be true (auto-derive from the issuer) or a Buffer key id");
|
|
192
136
|
}
|
|
193
137
|
|
|
194
|
-
// ---- CRL extension value encoders (sec. 5.2) -------------------------------
|
|
195
138
|
|
|
196
|
-
// IssuingDistributionPoint (sec. 5.2.5): critical; MUST NOT DER-encode to an empty SEQUENCE; at most one
|
|
197
|
-
// scope boolean TRUE; onlyContainsAttributeCerts MUST be FALSE; DEFAULT-FALSE booleans omitted.
|
|
198
139
|
function _idpValue(idp) {
|
|
199
140
|
if (!idp || typeof idp !== "object" || Array.isArray(idp) || Buffer.isBuffer(idp)) throw _err("crl/bad-idp", "issuingDistributionPoint must be an object");
|
|
200
141
|
guard.identifier.assertKnownKeys(idp, KNOWN_IDP_KEYS, _err, "crl/bad-idp", function (k) {
|
|
@@ -205,7 +146,6 @@ function _idpValue(idp) {
|
|
|
205
146
|
if (idp.fullName != null) {
|
|
206
147
|
var entries = Array.isArray(idp.fullName) ? pkiBuild.reqDenseArray(idp.fullName, "issuingDistributionPoint.fullName", _err, "crl/bad-idp") : [idp.fullName];
|
|
207
148
|
if (!entries.length) throw _err("crl/bad-idp", "issuingDistributionPoint fullName must carry at least one GeneralName");
|
|
208
|
-
// distributionPoint [0] { fullName [0] IMPLICIT GeneralNames }
|
|
209
149
|
children.push(b.contextConstructed(0, b.contextConstructed(0, Buffer.concat(entries.map(_encodeGeneralName)))));
|
|
210
150
|
}
|
|
211
151
|
var scopeTrue = 0;
|
|
@@ -217,19 +157,10 @@ function _idpValue(idp) {
|
|
|
217
157
|
return b.sequence(children);
|
|
218
158
|
}
|
|
219
159
|
|
|
220
|
-
// Validate a pre-encoded IssuingDistributionPoint value against the same RFC 5280 sec. 5.2.5 profile the
|
|
221
|
-
// object form enforces: MUST NOT be empty; at most one of onlyContainsUserCerts [1] / onlyContainsCACerts [2]
|
|
222
|
-
// TRUE; onlyContainsAttributeCerts [5] MUST be FALSE; indirectCRL [4] deferred (crlChecker skips indirect CRLs).
|
|
223
|
-
// Read through the shared sec. 5.2.5 grammar, not a walk over the raw children: each scope flag is
|
|
224
|
-
// an IMPLICIT BOOLEAN whose meaning is fixed by the encoding rules, and a caller-supplied encoding
|
|
225
|
-
// is exactly where a flag that is not one content octet of 0x00 or 0xFF arrives. Deciding TRUE from
|
|
226
|
-
// a content byte would emit a CRL whose scope this toolkit and the relying party read differently.
|
|
227
160
|
var _PRE_ENCODED_IDP_SCHEMA = pkix.issuingDistributionPoint("crl/bad-idp");
|
|
228
161
|
function _validatePreEncodedIdp(inner) {
|
|
229
162
|
if (!inner.children || !inner.children.length) throw _err("crl/bad-idp", "pre-encoded issuingDistributionPoint MUST NOT be empty (RFC 5280 sec. 5.2.5)");
|
|
230
163
|
var f = schema.walk(_PRE_ENCODED_IDP_SCHEMA, inner, NS).fields;
|
|
231
|
-
// A present DEFAULT-FALSE flag encodes a default DER omits (X.690 sec. 11.5), so it is rejected
|
|
232
|
-
// whichever value it carries, never counted as the FALSE it spells.
|
|
233
164
|
["onlyContainsUserCerts", "onlyContainsCACerts", "onlyContainsAttributeCerts"].forEach(function (name) {
|
|
234
165
|
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)");
|
|
235
166
|
});
|
|
@@ -238,12 +169,9 @@ function _validatePreEncodedIdp(inner) {
|
|
|
238
169
|
if (f.onlyContainsUserCerts.present && f.onlyContainsCACerts.present) throw _err("crl/bad-idp", "at most one of onlyContainsUserCerts / onlyContainsCACerts may be TRUE (RFC 5280 sec. 5.2.5)");
|
|
239
170
|
}
|
|
240
171
|
|
|
241
|
-
// freshestCRL / CRLDistributionPoints (sec. 5.2.6): SEQUENCE OF DistributionPoint carrying only
|
|
242
|
-
// distributionPoint (reasons + cRLIssuer omitted). Accepts an array of GeneralName entries (one DP's
|
|
243
|
-
// fullName) or an array of { fullName } distribution points.
|
|
244
172
|
function _freshestValue(spec) {
|
|
245
173
|
if (!Array.isArray(spec) || !spec.length) throw _err("crl/bad-input", "freshestCRL must be a non-empty array of GeneralNames (or { fullName } distribution points)");
|
|
246
|
-
var dense = _b.reqDenseArray(spec, "freshestCRL");
|
|
174
|
+
var dense = _b.reqDenseArray(spec, "freshestCRL");
|
|
247
175
|
var dps = dense.every(function (e) { return e && typeof e === "object" && !Buffer.isBuffer(e) && Object.keys(e).length === 1 && e.fullName != null; })
|
|
248
176
|
? dense.map(function (dp) { return dp.fullName; })
|
|
249
177
|
: [dense];
|
|
@@ -254,17 +182,12 @@ function _freshestValue(spec) {
|
|
|
254
182
|
}));
|
|
255
183
|
}
|
|
256
184
|
|
|
257
|
-
// authorityInfoAccess (sec. 5.2.7): SEQUENCE OF AccessDescription, caIssuers-only. Accepts an array of
|
|
258
|
-
// GeneralName entries, each a caIssuers accessLocation.
|
|
259
185
|
function _aiaValue(spec) {
|
|
260
186
|
if (!Array.isArray(spec) || !spec.length) throw _err("crl/bad-input", "authorityInfoAccess must be a non-empty array of caIssuers GeneralNames");
|
|
261
187
|
var caIssuers = O("caIssuers");
|
|
262
188
|
return b.sequence(spec.map(function (gn) { return b.sequence([b.oid(caIssuers), _encodeGeneralName(gn)]); }));
|
|
263
189
|
}
|
|
264
190
|
|
|
265
|
-
// The crlExtensions [0] set. Fixed per-extension criticality (sec. 5.2); at most one instance of each
|
|
266
|
-
// (sec. 5.2). `spec.extensions` is an object of the recognized keys OR an array of pre-encoded Extension
|
|
267
|
-
// DER (escape hatch for an extension the object form does not model). Returns { exts, isDelta }.
|
|
268
191
|
function _buildCrlExtensions(spec, ctx) {
|
|
269
192
|
var out = [], seen = {}, isDelta = false;
|
|
270
193
|
function push(oidName, critical, valueDer) {
|
|
@@ -286,10 +209,6 @@ function _buildCrlExtensions(spec, ctx) {
|
|
|
286
209
|
var extnId = asn1.read.oid(node.children[0]);
|
|
287
210
|
if (seen[extnId]) throw _err("crl/bad-input", "duplicate extension " + extnId + " (RFC 5280 sec. 5.2)");
|
|
288
211
|
seen[extnId] = true;
|
|
289
|
-
// RFC 5280 sec. 5.2 -- a recognized CRL extension is held to the profile even via the escape hatch: its
|
|
290
|
-
// fixed criticality (assertValidExtension rejects an explicit critical=FALSE, so 3 children == critical,
|
|
291
|
-
// 2 == non-critical) AND a well-formed value of the right type (INTEGER for cRLNumber/deltaCRLIndicator,
|
|
292
|
-
// else a SEQUENCE), never emitted opaque.
|
|
293
212
|
if (_hasOwn(REQUIRED_CRITICALITY, extnId)) {
|
|
294
213
|
if ((node.children.length === 3) !== REQUIRED_CRITICALITY[extnId]) {
|
|
295
214
|
throw _err("crl/bad-input", "pre-encoded " + (oid.name(extnId) || extnId) + " extension has the wrong criticality (RFC 5280 sec. 5.2 requires it " + (REQUIRED_CRITICALITY[extnId] ? "critical" : "non-critical") + ")");
|
|
@@ -299,17 +218,13 @@ function _buildCrlExtensions(spec, ctx) {
|
|
|
299
218
|
else if (extnId === O("deltaCRLIndicator")) arrBase = _requireBoundedInt(inner, "deltaCRLIndicator baseCRLNumber");
|
|
300
219
|
else {
|
|
301
220
|
if (inner.tagClass !== "universal" || inner.tagNumber !== TAGS.SEQUENCE) throw _err("crl/bad-input", "pre-encoded " + (oid.name(extnId) || extnId) + " extension value must be a SEQUENCE (RFC 5280 sec. 5.2)");
|
|
302
|
-
// A pre-encoded IssuingDistributionPoint is held to the same sec. 5.2.5 profile as the object form.
|
|
303
221
|
if (extnId === O("issuingDistributionPoint")) _validatePreEncodedIdp(inner);
|
|
304
222
|
}
|
|
305
223
|
}
|
|
306
224
|
if (extnId === O("deltaCRLIndicator")) isDelta = true;
|
|
307
225
|
out.push(b.raw(der));
|
|
308
226
|
});
|
|
309
|
-
// RFC 5280 sec. 5.2.3 / 5.2.4 -- a delta CRL MUST carry a cRLNumber greater than its baseCRLNumber, whether
|
|
310
|
-
// that cRLNumber is pre-encoded here or supplied via spec.crlNumber; the escape hatch is held to the same rule.
|
|
311
227
|
if (isDelta) {
|
|
312
|
-
// RFC 5280 sec. 5.2.6 -- freshestCRL MUST NOT appear in a delta CRL, including via the pre-encoded hatch.
|
|
313
228
|
if (seen[O("freshestCRL")]) throw _err("crl/bad-input", "freshestCRL MUST NOT appear in a delta CRL (RFC 5280 sec. 5.2.6)");
|
|
314
229
|
var eff = crlNumberVal != null ? crlNumberVal : arrCrlNum;
|
|
315
230
|
if (eff == null) throw _err("crl/bad-input", "a delta CRL MUST include a cRLNumber (RFC 5280 sec. 5.2.3 / 5.2.4)");
|
|
@@ -326,8 +241,6 @@ function _buildCrlExtensions(spec, ctx) {
|
|
|
326
241
|
if (ext.issuingDistributionPoint != null) push("issuingDistributionPoint", true, _idpValue(ext.issuingDistributionPoint));
|
|
327
242
|
if (ext.deltaCRLIndicator != null) {
|
|
328
243
|
var base = _boundedInteger(ext.deltaCRLIndicator, "deltaCRLIndicator baseCRLNumber");
|
|
329
|
-
// RFC 5280 sec. 5.2.3 / 5.2.4 -- a conforming delta CRL MUST carry a cRLNumber (sec. 5.2.3 requires it in
|
|
330
|
-
// every CRL), and that cRLNumber MUST be greater than the BaseCRLNumber it is built against (sec. 5.2.4).
|
|
331
244
|
if (crlNumberVal == null) throw _err("crl/bad-input", "a delta CRL MUST include a cRLNumber (set spec.crlNumber) (RFC 5280 sec. 5.2.3 / 5.2.4)");
|
|
332
245
|
if (crlNumberVal <= base.value) throw _err("crl/bad-crl-number", "a delta CRL's cRLNumber (" + crlNumberVal + ") MUST be greater than its baseCRLNumber (" + base.value + ") (RFC 5280 sec. 5.2.4)");
|
|
333
246
|
push("deltaCRLIndicator", true, base.tlv);
|
|
@@ -340,25 +253,17 @@ function _buildCrlExtensions(spec, ctx) {
|
|
|
340
253
|
return { exts: out, isDelta: isDelta };
|
|
341
254
|
}
|
|
342
255
|
|
|
343
|
-
// The revokedCertificates entries (sec. 5.1.2.6 / 5.3). Each entry: serial + revocationDate + optional
|
|
344
|
-
// crlEntryExtensions (reasonCode ENUMERATED, invalidityDate GeneralizedTime-only, certificateIssuer, or a
|
|
345
|
-
// pre-encoded escape hatch). Returns { entries, anyExt }, where anyExt forces v2 (sec. 5.1.2.1).
|
|
346
256
|
function _buildRevoked(entryList, isDelta) {
|
|
347
257
|
if (!Array.isArray(entryList)) throw _err("crl/bad-input", "revoked must be an array of revoked-certificate entries");
|
|
348
258
|
var anyExt = false, seenSerials = {};
|
|
349
|
-
// Density before the map, which would otherwise skip a hole into b.sequence as a native error.
|
|
350
259
|
var entries = _b.reqDenseArray(entryList, "revoked").map(function (e, idx) {
|
|
351
260
|
if (!e || typeof e !== "object" || Buffer.isBuffer(e)) throw _err("crl/bad-input", "each revoked entry must be an object");
|
|
352
|
-
// Before any field of the entry is encoded, so an unrecognized name is a refusal rather than an
|
|
353
|
-
// entry signed without what it asked for.
|
|
354
261
|
guard.identifier.assertKnownKeys(e, KNOWN_REVOKED_KEYS, _err, "crl/bad-input", function (k) {
|
|
355
262
|
return "unknown field " + JSON.stringify(k) + " in revoked entry [" + idx +
|
|
356
263
|
"]; an entry is { serialNumber, revocationDate, reason?, invalidityDate?, extensions? }";
|
|
357
264
|
});
|
|
358
265
|
if (e.serialNumber == null) throw _err("crl/bad-input", "revoked entry [" + idx + "] requires a serialNumber");
|
|
359
266
|
var serialTlv = _serialInteger(e.serialNumber);
|
|
360
|
-
// RFC 5280 sec. 5.1.2.6 -- a serial number identifies a certificate uniquely, so a CRL MUST NOT list the
|
|
361
|
-
// same serial twice; dedup on the DER content (the same value the parser surfaces as serialNumberHex).
|
|
362
267
|
var serialKey = asn1.decode(serialTlv).content.toString("hex");
|
|
363
268
|
if (seenSerials[serialKey]) throw _err("crl/bad-input", "revoked entry [" + idx + "] duplicates a serial number already listed in this CRL (RFC 5280 sec. 5.1.2.6)");
|
|
364
269
|
seenSerials[serialKey] = true;
|
|
@@ -372,11 +277,10 @@ function _buildRevoked(entryList, isDelta) {
|
|
|
372
277
|
}
|
|
373
278
|
if (e.reason != null) {
|
|
374
279
|
var codeNum = _resolveReason(e.reason, isDelta);
|
|
375
|
-
if (codeNum !== 0) pushE("reasonCode", false, b.enumerated(BigInt(codeNum)));
|
|
280
|
+
if (codeNum !== 0) pushE("reasonCode", false, b.enumerated(BigInt(codeNum)));
|
|
376
281
|
}
|
|
377
282
|
if (e.invalidityDate != null) {
|
|
378
283
|
guard.time.assertValid(e.invalidityDate, _err, "crl/bad-input", "invalidityDate");
|
|
379
|
-
// sec. 5.3.2: always GeneralizedTime (never the UTCTime cutover), no fractional seconds.
|
|
380
284
|
pushE("invalidityDate", false, b.generalizedTime(e.invalidityDate));
|
|
381
285
|
}
|
|
382
286
|
if (e.certificateIssuer != null) throw _err("crl/bad-input", _CERT_ISSUER_DEFERRED);
|
|
@@ -389,11 +293,7 @@ function _buildRevoked(entryList, isDelta) {
|
|
|
389
293
|
var extnId = asn1.read.oid(xnode.children[0]);
|
|
390
294
|
if (seen[extnId]) throw _err("crl/bad-input", "duplicate entry extension " + extnId + " in revoked entry [" + idx + "]");
|
|
391
295
|
seen[extnId] = true;
|
|
392
|
-
// certificateIssuer (indirect CRLs) is not usable end-to-end yet (crlChecker skips such a CRL); reject it.
|
|
393
296
|
if (extnId === O("certificateIssuer")) throw _err("crl/bad-input", _CERT_ISSUER_DEFERRED);
|
|
394
|
-
// RFC 5280 sec. 5.3 -- a recognized entry extension is held to the profile even via the escape hatch:
|
|
395
|
-
// its fixed criticality AND a well-formed value of the right type (reasonCode an ENUMERATED in the
|
|
396
|
-
// legal set, invalidityDate a GeneralizedTime).
|
|
397
297
|
if (_hasOwn(REQUIRED_ENTRY_CRITICALITY, extnId)) {
|
|
398
298
|
if ((xnode.children.length === 3) !== REQUIRED_ENTRY_CRITICALITY[extnId]) throw _err("crl/bad-input", "pre-encoded entry extension " + (oid.name(extnId) || extnId) + " has the wrong criticality (RFC 5280 sec. 5.3 requires it " + (REQUIRED_ENTRY_CRITICALITY[extnId] ? "critical" : "non-critical") + ")");
|
|
399
299
|
var einner = _extInner(xnode, oid.name(extnId) || extnId);
|
|
@@ -416,19 +316,11 @@ function _buildRevoked(entryList, isDelta) {
|
|
|
416
316
|
return { entries: entries, anyExt: anyExt };
|
|
417
317
|
}
|
|
418
318
|
|
|
419
|
-
// ---- the primitives --------------------------------------------------------
|
|
420
319
|
|
|
421
320
|
function _parseIssuerCert(cert) {
|
|
422
|
-
// The issuer certificate decides who may have signed this CRL: its key verifies the signature and
|
|
423
|
-
// its keyUsage says whether it may sign CRLs at all. A caller-assembled one could carry a real
|
|
424
|
-
// CA's name and cRLSign bit beside a substituted key, so it is re-derived like every other.
|
|
425
321
|
return guard.parsed.acceptDerived(cert, "certificate", x509Schema.parse, _err, "crl/bad-input", "issuer.cert");
|
|
426
322
|
}
|
|
427
323
|
|
|
428
|
-
// RFC 5280 sec. 4.2.1.3 -- a certificate whose key signs CRLs asserts the cRLSign keyUsage bit. When the
|
|
429
|
-
// issuer certificate carries a keyUsage extension it MUST include cRLSign, or the CRL it signs is rejected
|
|
430
|
-
// by a conforming relying party (pki.path.crlChecker included). Mirrors x509-sign's keyCertSign gate; a
|
|
431
|
-
// certificate with no keyUsage extension is unrestricted.
|
|
432
324
|
function _assertIssuerCanSignCrl(issuerCert) {
|
|
433
325
|
var kuExt = (issuerCert.extensions || []).filter(function (e) { return e.oid === O("keyUsage"); })[0];
|
|
434
326
|
if (!kuExt) return;
|
|
@@ -442,13 +334,6 @@ function _sign(spec, issuer, opts) {
|
|
|
442
334
|
opts = opts || {};
|
|
443
335
|
if (!spec || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("crl/bad-input", "the CRL spec must be an object");
|
|
444
336
|
issuer = issuer || {};
|
|
445
|
-
// Before any field is read. A dropped key here is worse than elsewhere in the toolkit: a CRL
|
|
446
|
-
// asserts what is revoked, so the failure is a correctly signed, structurally valid CRL saying
|
|
447
|
-
// NOTHING is revoked. `revokedCertificates` earns its own hint because it is the name the parser
|
|
448
|
-
// uses for the same list -- a caller round-tripping a parsed CRL back into sign reaches for it
|
|
449
|
-
// first, and got a signed empty list.
|
|
450
|
-
// The issuer form is settled once, before either door, so the spec table matches the source the
|
|
451
|
-
// distinguished name will actually come from.
|
|
452
337
|
var byCert = issuer.cert != null, byName = issuer.name != null;
|
|
453
338
|
guard.identifier.assertKnownKeys(spec, byCert || byName ? KNOWN_SPEC_KEYS_ISSUER_NAMED : KNOWN_SPEC_KEYS,
|
|
454
339
|
_err, "crl/bad-input", function (k) {
|
|
@@ -467,7 +352,6 @@ function _sign(spec, issuer, opts) {
|
|
|
467
352
|
guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "crl/bad-input", "pki.crl.sign has an unknown option ");
|
|
468
353
|
if (issuer.key == null) throw _err("crl/bad-input", "a signing key (issuer.key, a WebCrypto CryptoKey or a PKCS#8 private key DER/PEM) is required");
|
|
469
354
|
|
|
470
|
-
// Resolve the issuer name + signing-key SPKI. `{ cert }` supplies both; else `{ name|spec.issuer, publicKey }`.
|
|
471
355
|
var issuerDer, issuerSpki, issuerCert = null;
|
|
472
356
|
if (byCert) {
|
|
473
357
|
issuerCert = _parseIssuerCert(issuer.cert);
|
|
@@ -481,43 +365,35 @@ function _sign(spec, issuer, opts) {
|
|
|
481
365
|
if (dnSource == null) throw _err("crl/bad-issuer", "an issuer distinguished name is required (issuer.name or spec.issuer) when no issuer.cert is given");
|
|
482
366
|
issuerDer = _encodeName(dnSource);
|
|
483
367
|
}
|
|
484
|
-
// RFC 5280 sec. 5.1.2.3 -- the issuer MUST be a non-empty distinguished name.
|
|
485
368
|
if (_isEmptyName(issuerDer)) throw _err("crl/bad-issuer", "issuer must be a non-empty distinguished name (RFC 5280 sec. 5.1.2.3)");
|
|
486
369
|
|
|
487
|
-
// thisUpdate (required) + nextUpdate (optional; MUST NOT precede thisUpdate). _timeDer validates each date
|
|
488
|
-
// and applies the RFC 5280 sec. 5.1.2.4/.5 UTCTime<=2049-else-GeneralizedTime cutover.
|
|
489
370
|
if (spec.thisUpdate == null) throw _err("crl/bad-input", "thisUpdate is required (RFC 5280 sec. 5.1.2.4)");
|
|
490
371
|
var thisU = _timeDer(spec.thisUpdate, "thisUpdate");
|
|
491
372
|
var nextU = null;
|
|
492
373
|
if (spec.nextUpdate != null) {
|
|
493
374
|
nextU = _timeDer(spec.nextUpdate, "nextUpdate");
|
|
494
375
|
// allow:nan-date-comparison-unguarded -- both operands are guard.time.assertValid'd via _timeDer on the
|
|
495
|
-
// two lines above (thisUpdate + nextUpdate), so an Invalid Date throws before this comparison.
|
|
496
376
|
if (guard.time.instantOf(spec.nextUpdate) < guard.time.instantOf(spec.thisUpdate)) throw _err("crl/bad-input", "nextUpdate must not be before thisUpdate (RFC 5280 sec. 5.1.2.5)");
|
|
497
377
|
}
|
|
498
378
|
|
|
499
379
|
var extResult = _buildCrlExtensions(spec, { issuerCert: issuerCert, issuerSpki: issuerSpki });
|
|
500
380
|
var crlExts = extResult.exts;
|
|
501
381
|
var revoked = spec.revoked != null ? _buildRevoked(spec.revoked, extResult.isDelta) : { entries: [], anyExt: false };
|
|
502
|
-
// RFC 5280 sec. 5.1.2.1 -- v2 iff any CRL or entry extension is present, else v1 (version omitted).
|
|
503
382
|
var version = (crlExts.length || revoked.anyExt) ? 2 : 1;
|
|
504
383
|
|
|
505
|
-
// The signature scheme resolves from the SIGNING key's SPKI algorithm (the whole registry, for free).
|
|
506
384
|
var scheme = signScheme.resolveSignScheme(_certLikeFromSpki(issuerSpki), { combinedRsaSig: true, pss: opts.pss, digestAlgorithm: opts.digestAlgorithm }, true, _signE);
|
|
507
385
|
|
|
508
386
|
var tbsChildren = [];
|
|
509
|
-
if (version === 2) tbsChildren.push(b.integer(1n));
|
|
510
|
-
tbsChildren.push(scheme.sigAlgId);
|
|
387
|
+
if (version === 2) tbsChildren.push(b.integer(1n));
|
|
388
|
+
tbsChildren.push(scheme.sigAlgId);
|
|
511
389
|
tbsChildren.push(issuerDer);
|
|
512
390
|
tbsChildren.push(thisU);
|
|
513
391
|
if (nextU) tbsChildren.push(nextU);
|
|
514
|
-
if (revoked.entries.length) tbsChildren.push(b.sequence(revoked.entries));
|
|
515
|
-
if (crlExts.length) tbsChildren.push(b.explicit(0, b.sequence(crlExts)));
|
|
392
|
+
if (revoked.entries.length) tbsChildren.push(b.sequence(revoked.entries));
|
|
393
|
+
if (crlExts.length) tbsChildren.push(b.explicit(0, b.sequence(crlExts)));
|
|
516
394
|
var tbsDer = b.sequence(tbsChildren);
|
|
517
395
|
|
|
518
396
|
return signScheme.signOverTbs(scheme, issuer.key, tbsDer, _signE).then(function (sig) {
|
|
519
|
-
// The signature MUST verify under the issuer public key before returning, or the CRL would be rejected
|
|
520
|
-
// downstream (composite returns a promise; the classical/PQC path throws synchronously on a mismatch).
|
|
521
397
|
return Promise.resolve(_assertSignatureVerifies(tbsDer, sig, issuerSpki, scheme)).then(function () {
|
|
522
398
|
var crlDer = b.sequence([tbsDer, scheme.sigAlgId, b.bitString(sig, 0)]);
|
|
523
399
|
return opts.pem ? crlSchema.pemEncode(crlDer, "X509 CRL") : crlDer;
|
|
@@ -575,36 +451,20 @@ function _sign(spec, issuer, opts) {
|
|
|
575
451
|
* pki.schema.crl.parse(der).revokedCertificates[0].serialNumberHex; // "1234"
|
|
576
452
|
*/
|
|
577
453
|
function sign(spec, issuer, opts) {
|
|
578
|
-
// Every caller-owned argument copied at entry and released when the call settles -- see the note
|
|
579
|
-
// on the same call in x509-sign.
|
|
580
454
|
return guard.bytes.fixedCall(CrlError, "crl/bad-input", [
|
|
581
455
|
[spec, "the CRL spec"], [issuer, "the issuer"], [opts, "pki.crl.sign options"],
|
|
582
456
|
], _sign);
|
|
583
457
|
}
|
|
584
458
|
|
|
585
|
-
// A CRL these verbs answer from is re-derived from the bytes its parser read. Completeness -- every
|
|
586
|
-
// field present with the right type -- is not enough for a verdict: the signature covers a byte
|
|
587
|
-
// range, while the revocation list and the scope extensions are separate properties of the parsed
|
|
588
|
-
// object. Keep a correctly signed CRL's `tbsBytes` and signature and empty `revokedCertificates`,
|
|
589
|
-
// and the signature still verifies while `isRevoked` answers from the edited list. An emptied
|
|
590
|
-
// `crlExtensions` does the same to scope: a shard that may only speak for some reasons, or a delta
|
|
591
|
-
// that may not answer alone, becomes one that answers for everything.
|
|
592
459
|
function _coerceCrl(crl) {
|
|
593
460
|
return guard.parsed.acceptDerived(crl, "crl", crlSchema.parse, _err, "crl/bad-input", "the CRL");
|
|
594
461
|
}
|
|
595
462
|
|
|
596
|
-
// The issuer's key AND, when the caller supplied a certificate rather than a bare key, the
|
|
597
|
-
// certificate itself -- because a CRL signature that verifies says only that SOME key signed these
|
|
598
|
-
// bytes. Whether that key was allowed to sign a CRL, and whether it belongs to the issuer this CRL
|
|
599
|
-
// names, are separate questions that only a certificate can answer.
|
|
600
463
|
function _resolveIssuer(issuer) {
|
|
601
464
|
if (issuer == null) throw _err("crl/bad-input", "an issuer is required to verify a CRL");
|
|
602
465
|
if (guard.bytes.isByteSource(issuer)) { var _isp = guard.bytes.source(issuer, CrlError, "crl/bad-input", "issuer SPKI"); _assertValidSpki(_isp, "issuer SPKI"); return { spki: _isp, cert: null }; }
|
|
603
466
|
if (issuer.cert != null) { var ic = _parseIssuerCert(issuer.cert); return { spki: ic.subjectPublicKeyInfo.bytes, cert: ic }; }
|
|
604
467
|
if (issuer.publicKey != null) { var spki = _reqDer(issuer.publicKey, "issuer.publicKey"); _assertValidSpki(spki, "issuer.publicKey"); return { spki: spki, cert: null }; }
|
|
605
|
-
// A parsed certificate passed directly. It reaches the same identity and cRLSign checks the
|
|
606
|
-
// { cert } form does, so it goes through the same door: an object carrying an SPKI but missing
|
|
607
|
-
// the subject those checks compare would decide them on undefined.
|
|
608
468
|
if (issuer.subjectPublicKeyInfo && issuer.subjectPublicKeyInfo.bytes) {
|
|
609
469
|
var pc = _parseIssuerCert(issuer);
|
|
610
470
|
return { spki: pc.subjectPublicKeyInfo.bytes, cert: pc };
|
|
@@ -612,26 +472,10 @@ function _resolveIssuer(issuer) {
|
|
|
612
472
|
throw _err("crl/bad-input", "issuer must be { cert }, { publicKey } (SPKI DER), or a raw SPKI Buffer");
|
|
613
473
|
}
|
|
614
474
|
|
|
615
|
-
// RFC 5280 sec. 5.1.1.2 / 6.3.3: the certificate that signed a CRL must BE the CRL's issuer, and if
|
|
616
|
-
// it carries a keyUsage extension that extension must assert cRLSign. Neither is implied by the
|
|
617
|
-
// signature verifying: any key can sign any bytes, and a certificate restricted to digitalSignature
|
|
618
|
-
// chains to its root perfectly well while having no authority to revoke anything. The producing
|
|
619
|
-
// side of this same file already refuses to SIGN a CRL with a certificate lacking cRLSign; the
|
|
620
|
-
// verifying side asked neither question, so a CRL minted under an end-entity certificate of the
|
|
621
|
-
// same CA verified as that CA's CRL.
|
|
622
|
-
// Returns false rather than throwing, because both answers are statements about the CRL, not about
|
|
623
|
-
// the caller's input: "this CRL was not validly issued by the certificate you named". That keeps
|
|
624
|
-
// the verb's boolean contract, and keeps working the ordinary pattern of trying each candidate
|
|
625
|
-
// issuer to find which one issued a CRL. A MALFORMED keyUsage is different -- it is a defect in the
|
|
626
|
-
// caller's own certificate rather than a verdict about the CRL -- and throws.
|
|
627
475
|
function _issuerMaySign(parsed, cert) {
|
|
628
476
|
if (!guard.name.dnEqual(parsed.issuer.rdns, cert.subject.rdns, _err, "crl/bad-issuer", "the CRL issuer")) return false;
|
|
629
|
-
// Through the shared reader, not a local BIT STRING read: keyUsage is a NamedBitList, so DER drops
|
|
630
|
-
// its trailing zero bits and sec. 4.2.1.3 requires at least one bit set, and a boundary reading
|
|
631
|
-
// the bits itself applies neither -- authorizing here a certificate the signing side and the path
|
|
632
|
-
// validator both refuse as malformed.
|
|
633
477
|
var ku = pkix.keyUsageOf(NS, cert, _err, "crl/bad-issuer", "issuer certificate");
|
|
634
|
-
if (!ku) return true;
|
|
478
|
+
if (!ku) return true;
|
|
635
479
|
return ku.cRLSign === true;
|
|
636
480
|
}
|
|
637
481
|
|
|
@@ -677,10 +521,6 @@ function verify(crl, issuer) { return guard.async.deferred(function () { return
|
|
|
677
521
|
function _verify(crl, issuer) {
|
|
678
522
|
var parsed = _coerceCrl(crl);
|
|
679
523
|
var resolved = _resolveIssuer(issuer);
|
|
680
|
-
// A bare SPKI carries no certificate, so the two cert-only questions cannot be asked and only the
|
|
681
|
-
// signature is checked (issuerMaySign is then reported true). Both checks are surfaced on the verdict:
|
|
682
|
-
// a bare `true` used to hide whether the signer was ALLOWED to sign this CRL, so `if (verify(...))`
|
|
683
|
-
// accepted a CRL minted under an end-entity certificate of the same CA as that CA's own.
|
|
684
524
|
var issuerMaySign = resolved.cert ? _issuerMaySign(parsed, resolved.cert) : true;
|
|
685
525
|
return crlVerify.verifyCrlSignature(parsed, resolved.spki).then(function (sigOk) {
|
|
686
526
|
var signatureValid = sigOk === true;
|
|
@@ -704,7 +544,6 @@ function _serialHexOf(serial) {
|
|
|
704
544
|
else if (guard.bytes.isByteSource(serial)) { var _sb = guard.bytes.source(serial, CrlError, "crl/bad-input", "serialNumber"); v = _sb.length ? BigInt("0x" + _sb.toString("hex")) : 0n; }
|
|
705
545
|
else throw _err("crl/bad-input", "serialNumber must be a BigInt, integer, hex string, or BufferSource");
|
|
706
546
|
if (v <= 0n) throw _err("crl/bad-input", "serialNumber must be a positive integer");
|
|
707
|
-
// Match schema-crl's serialNumberHex: the DER INTEGER content octets (preserving sign padding).
|
|
708
547
|
return asn1.decode(b.integer(v)).content.toString("hex");
|
|
709
548
|
}
|
|
710
549
|
|
|
@@ -770,8 +609,6 @@ function isRevoked(crl, serialNumber, opts) {
|
|
|
770
609
|
return "pki.crl.isRevoked has an unknown option " + JSON.stringify(k) +
|
|
771
610
|
"; it takes `time` and `historicalMode`";
|
|
772
611
|
});
|
|
773
|
-
// Both name the same instant, so one without the other asks nothing: historicalMode says how to
|
|
774
|
-
// read an entry against the time, and there is no time to read it against.
|
|
775
612
|
if (o.historicalMode !== undefined && o.time === undefined) {
|
|
776
613
|
throw _err("crl/bad-input", "pki.crl.isRevoked opts.historicalMode says how to read an entry " +
|
|
777
614
|
"against the instant asked about, so it needs opts.time as well");
|
|
@@ -781,16 +618,6 @@ function isRevoked(crl, serialNumber, opts) {
|
|
|
781
618
|
}
|
|
782
619
|
var parsed = _coerceCrl(crl);
|
|
783
620
|
var hex = _serialHexOf(serialNumber);
|
|
784
|
-
// CURRENCY, asked on the same footing as scope below and for the same reason: a CRL states the
|
|
785
|
-
// window it speaks for, and outside that window a serial being absent says nothing about the
|
|
786
|
-
// certificate -- the issuer promised a further list by `nextUpdate` and this one predates
|
|
787
|
-
// whatever it says. Read alone, `null` from an expired CRL reads as "not revoked", which is the
|
|
788
|
-
// composition this refusal exists to stop.
|
|
789
|
-
//
|
|
790
|
-
// Only when a time is given. This verb is handed a CRL and a serial, and the instant a question
|
|
791
|
-
// is asked at is the caller's to state throughout this toolkit rather than something read off
|
|
792
|
-
// the system clock here. Without one the verb stays the structural lookup it has always been,
|
|
793
|
-
// and its docstring says which question then went unasked.
|
|
794
621
|
if (o.time !== undefined) {
|
|
795
622
|
guard.time.assertValid(o.time, _err, "crl/bad-input", "pki.crl.isRevoked opts.time");
|
|
796
623
|
var at = guard.time.instantOf(o.time);
|
|
@@ -804,39 +631,17 @@ function isRevoked(crl, serialNumber, opts) {
|
|
|
804
631
|
"requires one of a conforming CA); ask pki.path.crlChecker, which decides currency against " +
|
|
805
632
|
"the material it fetched");
|
|
806
633
|
}
|
|
807
|
-
// Strictly past, so a time equal to nextUpdate is still covered -- the same boundary
|
|
808
|
-
// pki.path.crlChecker and the OCSP currency check use, and RFC 5280 sec. 5.1.2.5 makes
|
|
809
|
-
// nextUpdate the date BY WHICH the next list arrives rather than the moment this one lapses.
|
|
810
634
|
if (at > guard.time.instantOf(parsed.nextUpdate)) {
|
|
811
635
|
throw _err("crl/not-current", "this CRL was superseded after " +
|
|
812
636
|
parsed.nextUpdate.toISOString() + "; a serial missing from it says nothing about the " +
|
|
813
637
|
"certificate at the time asked about, since the issuer promised a further list by then");
|
|
814
638
|
}
|
|
815
639
|
}
|
|
816
|
-
// SCOPE, before the serial is looked for at all. A serial number is only meaningful within the
|
|
817
|
-
// set of certificates a CRL actually speaks for, and two shapes of CRL speak for a different set
|
|
818
|
-
// than a caller matching on serial alone assumes. pki.path.crlChecker already refuses both; this
|
|
819
|
-
// verb answered from any CRL it was handed, so the toolkit knew the rule and applied it in one
|
|
820
|
-
// consumer -- which is how the standalone verb came to give the opposite answer.
|
|
821
|
-
//
|
|
822
|
-
// A DELTA CRL lists CHANGES since its base, so a serial present in it may be there to say the
|
|
823
|
-
// certificate was RELEASED (removeFromCRL); read alone, the extension that means "no longer
|
|
824
|
-
// revoked" reads as "revoked". The answer is only derivable from the delta MERGED with its base,
|
|
825
|
-
// which pki.path.crlChecker does.
|
|
826
640
|
if (_findExtOid(parsed.crlExtensions, "deltaCRLIndicator")) {
|
|
827
641
|
throw _err("crl/delta-not-authoritative", "this is a delta CRL: it lists changes since a base CRL, so a serial appearing in it may be RELEASED rather than revoked. Merge it with its base -- pki.path.crlChecker does -- rather than reading a revocation status out of the delta alone");
|
|
828
642
|
}
|
|
829
|
-
// An INDIRECT CRL carries entries for OTHER issuers' certificates, identified by a per-entry
|
|
830
|
-
// certificateIssuer attribute. Matching on serial alone attributes another issuer's revocation to
|
|
831
|
-
// this one's certificate of the same serial number -- serials are unique per issuer, not globally.
|
|
832
643
|
var idp = _findExtOid(parsed.crlExtensions, "issuingDistributionPoint");
|
|
833
644
|
if (idp) _assertAuthoritativeScope(idp);
|
|
834
|
-
// certificateIssuer (sec. 5.3.3) names the issuer an entry belongs to, and is meaningful ONLY on
|
|
835
|
-
// an indirect CRL -- which was refused above. One here means the CRL declares itself direct while
|
|
836
|
-
// carrying an entry that says otherwise, and the two readings disagree about whose certificates
|
|
837
|
-
// this list covers. That is a property of the CRL, so it is settled BEFORE the lookup and over
|
|
838
|
-
// EVERY entry: checking only the entry that matches would leave the not-listed answer -- the one
|
|
839
|
-
// that says the certificate is fine -- coming from a list whose own entries dispute what it lists.
|
|
840
645
|
for (var i = 0; i < parsed.revokedCertificates.length; i++) {
|
|
841
646
|
if (_findExtOid(parsed.revokedCertificates[i].crlEntryExtensions, "certificateIssuer")) {
|
|
842
647
|
throw _err("crl/indirect-not-supported", "a revoked-certificate entry carries certificateIssuer, which names another issuer, on a CRL that does not declare itself indirect -- the entry and the CRL disagree about whose certificates this list covers, so no revocation status follows from it");
|
|
@@ -844,12 +649,6 @@ function isRevoked(crl, serialNumber, opts) {
|
|
|
844
649
|
}
|
|
845
650
|
for (var j = 0; j < parsed.revokedCertificates.length; j++) {
|
|
846
651
|
if (parsed.revokedCertificates[j].serialNumberHex !== hex) continue;
|
|
847
|
-
// A revocation takes effect on its revocationDate (RFC 5280 sec. 5.3), and how that reads
|
|
848
|
-
// against the instant asked about is the same question pki.path.crlChecker answers -- so the
|
|
849
|
-
// same answer is given here. Asked at the present moment a listed serial is revoked whatever
|
|
850
|
-
// that date says, because a future one is post-dating or clock skew and must not read good.
|
|
851
|
-
// Only an explicit historical reading -- validating as of a past instant, a timestamped
|
|
852
|
-
// signature say -- has an entry dated after that instant not yet applying.
|
|
853
652
|
if (o.historicalMode === true &&
|
|
854
653
|
guard.time.instantOf(parsed.revokedCertificates[j].revocationDate) >
|
|
855
654
|
guard.time.instantOf(o.time)) continue;
|
|
@@ -858,36 +657,11 @@ function isRevoked(crl, serialNumber, opts) {
|
|
|
858
657
|
return null;
|
|
859
658
|
}
|
|
860
659
|
|
|
861
|
-
// One extension out of a parsed list, by registered OID name. Null-safe: an absent list is no
|
|
862
|
-
// extension rather than a fault, which is what every caller here means by it.
|
|
863
660
|
function _findExtOid(list, name) {
|
|
864
661
|
var want = O(name);
|
|
865
662
|
return (list || []).filter(function (e) { return e.oid === want; })[0] || null;
|
|
866
663
|
}
|
|
867
664
|
|
|
868
|
-
// The issuingDistributionPoint states which certificates a CRL speaks for (sec. 5.2.5), and every
|
|
869
|
-
// form of it narrows that set below what a bare serial lookup assumes. `isRevoked` is handed a
|
|
870
|
-
// serial and nothing else, so it cannot tell whether the certificate behind that serial falls in
|
|
871
|
-
// the scope -- that decision needs the certificate, which is what pki.path.crlChecker has and this
|
|
872
|
-
// verb does not. So the presence of the extension is the refusal, whichever field carries it:
|
|
873
|
-
//
|
|
874
|
-
// indirectCRL entries name their own issuers, and serials are unique per issuer: a match
|
|
875
|
-
// would attribute another issuer's revocation to your certificate.
|
|
876
|
-
// distributionPoint the CRL covers the certificates whose own distribution point corresponds to
|
|
877
|
-
// this one (sec. 6.3.3(b)(2)(i)) -- a byte-exact comparison against a field of
|
|
878
|
-
// the certificate.
|
|
879
|
-
// onlyContainsUserCerts / onlyContainsCACerts / onlyContainsAttributeCerts
|
|
880
|
-
// the CRL covers one KIND of certificate; a serial does not say which kind it
|
|
881
|
-
// names, so an absent entry says nothing about a certificate of another kind.
|
|
882
|
-
// onlySomeReasons the CRL covers some revocation REASONS; a certificate revoked for a reason
|
|
883
|
-
// outside the set is absent from it while being revoked.
|
|
884
|
-
//
|
|
885
|
-
// Written as a throwing assertion rather than a predicate so the malformed case cannot be mistaken
|
|
886
|
-
// for a verdict about the certificate. The extension is read through the shared sec. 5.2.5 grammar
|
|
887
|
-
// rather than by walking its children, because the flags are IMPLICIT BOOLEANs and only the encoding
|
|
888
|
-
// rules say what their bytes mean: a walk that tests a content byte reads an EMPTY [4] as an absent
|
|
889
|
-
// flag and a multi-octet one by whichever byte it indexes, and "absent, so unscoped" is precisely
|
|
890
|
-
// the reading that lets an unreadable scope license an answer.
|
|
891
665
|
var _IDP_SCHEMA = pkix.issuingDistributionPoint("crl/scope-not-authoritative");
|
|
892
666
|
var _INDIRECT_REFUSAL = "this CRL is marked indirect (issuingDistributionPoint indirectCRL): its entries name their own issuers, so a serial alone does not identify a certificate. Reading a revocation status from it by serial would attribute another issuer's revocation to yours";
|
|
893
667
|
var _SCOPED_REFUSAL = "this CRL carries an issuingDistributionPoint, so it speaks for part of its issuer's certificates rather than all of them, and which part is decided against fields of the certificate -- which this verb is not given. A serial absent from it is not a certificate that is unrevoked. Use pki.path.crlChecker, which is handed the certificate and performs the RFC 5280 sec. 6.3.3 scope correspondence";
|
|
@@ -896,8 +670,6 @@ function _assertAuthoritativeScope(ext) {
|
|
|
896
670
|
try { m = schema.walk(_IDP_SCHEMA, asn1.decode(ext.value), NS); }
|
|
897
671
|
catch (e) { throw _err("crl/scope-not-authoritative", "the issuingDistributionPoint cannot be read, so this CRL's scope cannot be established", e); }
|
|
898
672
|
var f = m.fields;
|
|
899
|
-
// Named first so the sharper reason wins: indirect is not merely a narrower scope, it is a list
|
|
900
|
-
// whose serials belong to other issuers.
|
|
901
673
|
if (f.indirectCRL.present && f.indirectCRL.value === true) throw _err("crl/indirect-not-supported", _INDIRECT_REFUSAL);
|
|
902
674
|
throw _err("crl/scope-not-authoritative", _SCOPED_REFUSAL);
|
|
903
675
|
}
|