@blamejs/pki 0.1.22 → 0.1.24
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 +47 -0
- package/README.md +18 -6
- package/index.js +22 -12
- package/lib/asn1-der.js +126 -68
- package/lib/constants.js +46 -21
- package/lib/ct.js +54 -39
- package/lib/est.js +717 -0
- package/lib/framework-error.js +74 -34
- package/lib/oid.js +116 -60
- package/lib/path-validate.js +302 -135
- package/lib/schema-all.js +65 -41
- package/lib/schema-attrcert.js +101 -64
- package/lib/schema-cmp.js +152 -131
- package/lib/schema-cms.js +621 -163
- package/lib/schema-crl.js +43 -21
- package/lib/schema-crmf.js +136 -79
- package/lib/schema-csr.js +78 -15
- package/lib/schema-csrattrs.js +371 -0
- package/lib/schema-engine.js +92 -43
- package/lib/schema-ocsp.js +101 -55
- package/lib/schema-pkcs12.js +80 -64
- package/lib/schema-pkcs8.js +12 -12
- package/lib/schema-pkix.js +295 -105
- package/lib/schema-smime.js +39 -39
- package/lib/schema-tsp.js +108 -59
- package/lib/schema-x509.js +36 -25
- package/lib/webcrypto.js +161 -26
- package/package.json +3 -2
- package/sbom.cdx.json +6 -6
package/lib/schema-crl.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @slug crl
|
|
10
10
|
*
|
|
11
11
|
* @intro
|
|
12
|
-
* X.509 Certificate Revocation List handling per RFC 5280
|
|
12
|
+
* X.509 Certificate Revocation List handling per RFC 5280 sec. 5. `parse` turns a
|
|
13
13
|
* DER or PEM CRL into a structured, fully-decoded object: version, issuer
|
|
14
14
|
* distinguished name, this/next update as real `Date`s, the ordered list of
|
|
15
15
|
* revoked certificates (serial + revocation date + entry extensions), and the
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* rules, and the raw `tbsCertList` bytes are returned for signature checking.
|
|
20
20
|
*
|
|
21
21
|
* @card
|
|
22
|
-
* Parse DER / PEM X.509 CRLs into structured, validated fields
|
|
22
|
+
* Parse DER / PEM X.509 CRLs into structured, validated fields -- revoked
|
|
23
23
|
* serials with real-`Date` revocation times, named extensions, fail-closed.
|
|
24
24
|
*/
|
|
25
25
|
|
|
@@ -34,11 +34,13 @@ var PemError = frameworkError.PemError;
|
|
|
34
34
|
var TAGS = asn1.TAGS;
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
// CRLReason ::= ENUMERATED (RFC 5280
|
|
38
|
-
|
|
37
|
+
// CRLReason ::= ENUMERATED (RFC 5280 sec. 5.3.1) -- value 7 is unused/reserved.
|
|
38
|
+
// The legal set lives once in pkix.CRL_REASON_NAMES (shared with the OCSP
|
|
39
|
+
// RevokedInfo decoder, which surfaces the name; this decoder the numeric code).
|
|
40
|
+
var CRL_REASONS = pkix.CRL_REASON_NAMES;
|
|
39
41
|
|
|
40
42
|
// Extension-value decoding is keyed off the STABLE dotted OID (resolved once at
|
|
41
|
-
// load from the canonical name), not the mutable display name
|
|
43
|
+
// load from the canonical name), not the mutable display name -- a caller's
|
|
42
44
|
// pki.oid.register() display override must not change parse behaviour.
|
|
43
45
|
var OID_CRL_NUMBER = oid.byName("cRLNumber");
|
|
44
46
|
var OID_REASON_CODE = oid.byName("reasonCode");
|
|
@@ -51,16 +53,19 @@ var NS = pkix.makeNS("crl", CrlError, oid);
|
|
|
51
53
|
var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
|
|
52
54
|
var NAME = pkix.name(NS);
|
|
53
55
|
var EXTENSIONS = pkix.extensions(NS);
|
|
54
|
-
|
|
56
|
+
// pkix.time enforces the RFC 5280 encoding cutover on decode (sec. 5.1.2.4 -
|
|
57
|
+
// 5.1.2.6: thisUpdate / nextUpdate / revocationDate through 2049 must be
|
|
58
|
+
// UTCTime). invalidityDate stays GeneralizedTime-only (sec. 5.3.2, decodeExt).
|
|
59
|
+
var TIME = pkix.time(NS);
|
|
55
60
|
|
|
56
|
-
// CRL Version ::= INTEGER { v1(0), v2(1) }
|
|
61
|
+
// CRL Version ::= INTEGER { v1(0), v2(1) } -- a BARE INTEGER (not [0] EXPLICIT).
|
|
57
62
|
// A CRL is at most v2; reject an explicit v1 (DER forbids the default) and any
|
|
58
63
|
// value >= 2. Do NOT reuse the certificate readVersion (it maps 2 -> v3).
|
|
59
64
|
var CRL_VERSION = pkix.versionReader(NS, { "1": 2 });
|
|
60
65
|
|
|
61
66
|
// The three cheap, high-value CRL extension values are decoded from their raw
|
|
62
|
-
// extnValue octets (RFC 5280
|
|
63
|
-
// (issuingDistributionPoint, certificateIssuer, authorityKeyIdentifier,
|
|
67
|
+
// extnValue octets (RFC 5280 sec. 5.2/sec. 5.3); GeneralNames-based extensions
|
|
68
|
+
// (issuingDistributionPoint, certificateIssuer, authorityKeyIdentifier, ...) stay
|
|
64
69
|
// raw with their bytes reachable. A malformed decoded value fails closed.
|
|
65
70
|
function decodeExt(ext) {
|
|
66
71
|
var value = ext.value;
|
|
@@ -69,10 +74,10 @@ function decodeExt(ext) {
|
|
|
69
74
|
value = asn1.read.integer(asn1.decode(ext.value));
|
|
70
75
|
if (value < 0n) throw new Error("cRLNumber must be non-negative (INTEGER 0..MAX)");
|
|
71
76
|
} else if (ext.oid === OID_REASON_CODE) { // reasonCode ::= ENUMERATED (CRLReason)
|
|
72
|
-
// read.enumerated asserts the ENUMERATED tag (a bare INTEGER is rejected)
|
|
77
|
+
// read.enumerated asserts the ENUMERATED tag (a bare INTEGER is rejected) --
|
|
73
78
|
// the codec owns the tag check, so this reader need not repeat it.
|
|
74
79
|
var reason = asn1.read.enumerated(asn1.decode(ext.value));
|
|
75
|
-
if (CRL_REASONS.
|
|
80
|
+
if (!Object.prototype.hasOwnProperty.call(CRL_REASONS, reason.toString())) throw new Error("undefined CRLReason " + reason.toString());
|
|
76
81
|
value = Number(reason);
|
|
77
82
|
} else if (ext.oid === OID_INVALIDITY_DATE) { // invalidityDate ::= GeneralizedTime
|
|
78
83
|
var n = asn1.decode(ext.value);
|
|
@@ -121,7 +126,7 @@ var REVOKED_LIST = schema.seqOf(REVOKED_ENTRY, {
|
|
|
121
126
|
// revokedCertificates=SEQUENCE) are disambiguated by their universal tag;
|
|
122
127
|
// crlExtensions is modeled as a trailing [0]..[0] so a stray non-[0] trailing
|
|
123
128
|
// context tag is REJECTED (crl/bad-tbs), not silently ignored.
|
|
124
|
-
// `signature` is consumed by the CERTIFICATE_LIST build (the
|
|
129
|
+
// `signature` is consumed by the CERTIFICATE_LIST build (the sec. 5.1.1.2
|
|
125
130
|
// outer==inner agreement check reads tbsMatch.fields.signature.node.bytes);
|
|
126
131
|
// the operator reads the surfaced outer signatureAlgorithm, which that
|
|
127
132
|
// check proves byte-identical.
|
|
@@ -140,7 +145,7 @@ var TBS_CERTLIST = schema.seq([
|
|
|
140
145
|
build: function (m) {
|
|
141
146
|
return {
|
|
142
147
|
version: m.fields.version.present ? m.fields.version.value : 1,
|
|
143
|
-
issuer: m.fields.issuer.value.result, // Name is a seqOf
|
|
148
|
+
issuer: m.fields.issuer.value.result, // Name is a seqOf -> field.value is the match; .result is the {rdns, dn} build
|
|
144
149
|
thisUpdate: m.fields.thisUpdate.value,
|
|
145
150
|
nextUpdate: m.fields.nextUpdate.present ? m.fields.nextUpdate.value : null,
|
|
146
151
|
revokedCertificates: m.fields.revokedCertificates.present ? m.fields.revokedCertificates.value.result : [],
|
|
@@ -151,22 +156,22 @@ var TBS_CERTLIST = schema.seq([
|
|
|
151
156
|
});
|
|
152
157
|
|
|
153
158
|
// CertificateList ::= SEQUENCE { tbsCertList, signatureAlgorithm, signatureValue }
|
|
154
|
-
//
|
|
159
|
+
// -- the shared SIGNED envelope. The CRL-specific invariants (outer==inner
|
|
155
160
|
// signatureAlgorithm agreement, non-empty issuer, v2-only extensions) live in the
|
|
156
161
|
// build; the envelope owns the SEQUENCE-of-3 shape and signature extraction.
|
|
157
162
|
var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
|
|
158
163
|
code: "crl/not-a-crl", what: "CertificateList",
|
|
159
164
|
build: function (e) {
|
|
160
165
|
var tbs = e.tbsMatch.result;
|
|
161
|
-
// RFC 5280
|
|
166
|
+
// RFC 5280 sec. 5.1.1.2 -- the outer signatureAlgorithm MUST equal tbsCertList.signature.
|
|
162
167
|
if (!e.outerSignatureAlgorithmBytes.equals(e.tbsMatch.fields.signature.node.bytes)) {
|
|
163
|
-
throw NS.E("crl/bad-signature-algorithm", "signatureAlgorithm must match tbsCertList.signature (RFC 5280
|
|
168
|
+
throw NS.E("crl/bad-signature-algorithm", "signatureAlgorithm must match tbsCertList.signature (RFC 5280 sec. 5.1.1.2)");
|
|
164
169
|
}
|
|
165
|
-
// RFC 5280
|
|
170
|
+
// RFC 5280 sec. 5.1.2.3 -- the issuer MUST be a non-empty distinguished name.
|
|
166
171
|
if (!tbs.issuer.rdns.length) {
|
|
167
172
|
throw NS.E("crl/bad-issuer", "issuer must be a non-empty distinguished name");
|
|
168
173
|
}
|
|
169
|
-
// RFC 5280
|
|
174
|
+
// RFC 5280 sec. 5.1.2.1 -- crlExtensions / crlEntryExtensions appear only in a v2 CRL.
|
|
170
175
|
var hasExtensions = tbs.crlExtensionsPresent ||
|
|
171
176
|
tbs.revokedCertificates.some(function (r) { return r.crlEntryExtensions.length > 0; });
|
|
172
177
|
if (hasExtensions && tbs.version !== 2) {
|
|
@@ -202,7 +207,7 @@ var CERTIFICATE_LIST = pkix.signedEnvelope(NS, TBS_CERTLIST, {
|
|
|
202
207
|
*
|
|
203
208
|
* @example
|
|
204
209
|
* var crl = pki.schema.crl.parse(der);
|
|
205
|
-
* crl.revokedCertificates[0].serialNumberHex; //
|
|
210
|
+
* crl.revokedCertificates[0].serialNumberHex; // -> "0a3f..."
|
|
206
211
|
*/
|
|
207
212
|
var parse = pkix.makeParser({ pemLabel: "X509 CRL", PemError: PemError, ErrorClass: CrlError, prefix: "crl", what: "CRL", topSchema: CERTIFICATE_LIST, ns: NS });
|
|
208
213
|
|
|
@@ -222,8 +227,24 @@ var parse = pkix.makeParser({ pemLabel: "X509 CRL", PemError: PemError, ErrorCla
|
|
|
222
227
|
*/
|
|
223
228
|
function pemDecode(text, label) { return pkix.pemDecode(text, label || "X509 CRL", PemError); }
|
|
224
229
|
|
|
230
|
+
/**
|
|
231
|
+
* @primitive pki.schema.crl.pemEncode
|
|
232
|
+
* @signature pki.schema.crl.pemEncode(der, label?) -> string
|
|
233
|
+
* @since 0.1.23
|
|
234
|
+
* @status stable
|
|
235
|
+
* @spec RFC 7468, RFC 5280
|
|
236
|
+
* @related pki.schema.crl.pemDecode
|
|
237
|
+
*
|
|
238
|
+
* Wrap CRL DER bytes in a PEM envelope with 64-column base64 lines (default
|
|
239
|
+
* label `X509 CRL`, the RFC 7468 sec. 6 armor `pemDecode` expects back).
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* var pem = pki.schema.crl.pemEncode(der);
|
|
243
|
+
*/
|
|
244
|
+
function pemEncode(der, label) { return pkix.pemEncode(der, label || "X509 CRL", PemError); }
|
|
245
|
+
|
|
225
246
|
// Certificate and CertificateList share the outer SEQUENCE-of-3 shape; a CRL is
|
|
226
|
-
// distinguished by its tbsCertList
|
|
247
|
+
// distinguished by its tbsCertList -- the first tbs element is a bare INTEGER
|
|
227
248
|
// (version) or an AlgorithmIdentifier (signature) SEQUENCE, and crucially the
|
|
228
249
|
// field at the certificate's Validity position is a bare Time (thisUpdate). The
|
|
229
250
|
// orchestrator uses `matches` to route; a cert's tbs leads with a [0] EXPLICIT
|
|
@@ -232,7 +253,7 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || "X509 CRL
|
|
|
232
253
|
function matches(root) {
|
|
233
254
|
var tbs = pkix.signedEnvelopeTbs(root);
|
|
234
255
|
if (!tbs) return false;
|
|
235
|
-
// A certificate's tbs leads with [0] EXPLICIT version
|
|
256
|
+
// A certificate's tbs leads with [0] EXPLICIT version -- a CRL never does.
|
|
236
257
|
if (tbs.children[0] && tbs.children[0].tagClass === "context") return false;
|
|
237
258
|
// Walk to the thisUpdate / validity position: skip an optional bare INTEGER
|
|
238
259
|
// version, then signature (SEQUENCE) + issuer (SEQUENCE); the next element is
|
|
@@ -247,5 +268,6 @@ function matches(root) {
|
|
|
247
268
|
module.exports = {
|
|
248
269
|
parse: parse,
|
|
249
270
|
pemDecode: pemDecode,
|
|
271
|
+
pemEncode: pemEncode,
|
|
250
272
|
matches: matches,
|
|
251
273
|
};
|