@blamejs/pki 0.1.22 → 0.1.23
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 +35 -0
- package/README.md +16 -6
- package/index.js +16 -12
- package/lib/asn1-der.js +126 -68
- package/lib/constants.js +46 -21
- package/lib/ct.js +54 -39
- package/lib/framework-error.js +57 -34
- package/lib/oid.js +110 -59
- package/lib/path-validate.js +302 -135
- package/lib/schema-all.js +49 -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 +63 -14
- 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 +283 -100
- 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 +156 -22
- package/package.json +3 -2
- package/sbom.cdx.json +6 -6
package/lib/ct.js
CHANGED
|
@@ -14,26 +14,26 @@
|
|
|
14
14
|
* OCSP response) carries in the SCT extension into its individual signed
|
|
15
15
|
* certificate timestamps.
|
|
16
16
|
*
|
|
17
|
-
* The SCT payload is encoded in the TLS presentation language (RFC 8446
|
|
18
|
-
* RFC 5246
|
|
19
|
-
* integers and length-prefixed opaque vectors
|
|
17
|
+
* The SCT payload is encoded in the TLS presentation language (RFC 8446 sec. 3 /
|
|
18
|
+
* RFC 5246 sec. 4 conventions) -- positional, tag-less, fixed-width big-endian
|
|
19
|
+
* integers and length-prefixed opaque vectors -- NOT ASN.1/DER. So this module
|
|
20
20
|
* owns a bounded big-endian TLS-struct reader rather than composing the DER
|
|
21
|
-
* schema engine; the only ASN.1 surface is the
|
|
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
26
|
* Structure is decoded, crypto is surfaced RAW: each SCT surfaces its `logId`
|
|
27
|
-
* (32 raw bytes
|
|
27
|
+
* (32 raw bytes -- 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
29
|
* `hashAlg`/`sigAlg` code points, and the raw `signature`. The parser NEVER
|
|
30
|
-
* verifies a signature, recomputes a LogID, or trusts a log
|
|
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
|
*
|
|
34
34
|
* @card
|
|
35
35
|
* Parse RFC 6962 Certificate Transparency SCT lists from a certificate or OCSP
|
|
36
|
-
* extension
|
|
36
|
+
* extension -- per-SCT logId / timestamp (BigInt) / algorithm / raw signature,
|
|
37
37
|
* the signed-preimage reconstruction surfaced for external verification,
|
|
38
38
|
* bounded TLS-struct decode, fail-closed.
|
|
39
39
|
*/
|
|
@@ -45,8 +45,8 @@ var frameworkError = require("./framework-error.js");
|
|
|
45
45
|
var CtError = frameworkError.CtError;
|
|
46
46
|
var C = constants;
|
|
47
47
|
|
|
48
|
-
// RFC 5246
|
|
49
|
-
// unknown code surfaces as its numeric byte with a null name (never rejected
|
|
48
|
+
// RFC 5246 sec. 7.4.1.4.1 code points -- 1-byte, NOT OIDs. Surfaced named; an
|
|
49
|
+
// unknown code surfaces as its numeric byte with a null name (never rejected --
|
|
50
50
|
// off-profile-pair rejection is a verifier-tier log-conformance concern).
|
|
51
51
|
var HASH_ALGORITHMS = {
|
|
52
52
|
0: "none", 1: "md5", 2: "sha1", 3: "sha224", 4: "sha256", 5: "sha384", 6: "sha512",
|
|
@@ -59,7 +59,7 @@ var SCT_MIN_BODY = 47;
|
|
|
59
59
|
var LOGID_BYTES = 32;
|
|
60
60
|
var MAX_SAFE = 9007199254740991n; // 2^53 - 1; above this a Number loses precision
|
|
61
61
|
|
|
62
|
-
// ---- TlsReader
|
|
62
|
+
// ---- TlsReader -- the one net-new primitive (an ENCODING layer, not a schema
|
|
63
63
|
// combinator): a bounded big-endian TLS-vector cursor with a hard [pos, end).
|
|
64
64
|
// A lying inner length can overrun only the current sub-reader's `end`, never
|
|
65
65
|
// the parent buffer, so bounds-before-slice is structural.
|
|
@@ -81,7 +81,7 @@ TlsReader.prototype.fixed = function (n, code) {
|
|
|
81
81
|
this._need(n, code);
|
|
82
82
|
var s = this.buf.subarray(this.pos, this.pos + n); this.pos += n; return s;
|
|
83
83
|
};
|
|
84
|
-
// opaque<min..max>
|
|
84
|
+
// opaque<min..max> -- a length-prefixed vector (lenWidth-byte big-endian prefix).
|
|
85
85
|
// The length prefix itself is a plain read (a truncation there is ct/truncated);
|
|
86
86
|
// only a LYING length whose body overruns the bound carries the field's own code.
|
|
87
87
|
TlsReader.prototype.vector = function (lenWidth, min, max, code) {
|
|
@@ -98,16 +98,16 @@ TlsReader.prototype.subReader = function (len, code) {
|
|
|
98
98
|
TlsReader.prototype.remaining = function () { return this.end - this.pos; };
|
|
99
99
|
TlsReader.prototype.atEnd = function () { return this.pos === this.end; };
|
|
100
100
|
|
|
101
|
-
// Peel the RFC 6962
|
|
101
|
+
// Peel the RFC 6962 sec. 3.3 inner DER OCTET STRING (the certificate/OCSP layer
|
|
102
102
|
// already peeled the outer extnValue OCTET STRING). Rides the strict codec, so
|
|
103
103
|
// an indefinite length / constructed OCTET STRING / trailing bytes / single
|
|
104
104
|
// wrap all fail closed here; the asn1/* fault attaches as `.cause`.
|
|
105
105
|
function _peelInner(extValue) {
|
|
106
106
|
var node;
|
|
107
107
|
try { node = asn1.decode(extValue); }
|
|
108
|
-
catch (e) { throw new CtError("ct/bad-der", "the SCT-list extension value is not valid DER (RFC 6962
|
|
108
|
+
catch (e) { throw new CtError("ct/bad-der", "the SCT-list extension value is not valid DER (RFC 6962 sec. 3.3)", e); }
|
|
109
109
|
try { return asn1.read.octetString(node); }
|
|
110
|
-
catch (e) { throw new CtError("ct/bad-der", "the SCT-list extension value must be a DER OCTET STRING wrapping the TLS list (RFC 6962
|
|
110
|
+
catch (e) { throw new CtError("ct/bad-der", "the SCT-list extension value must be a DER OCTET STRING wrapping the TLS list (RFC 6962 sec. 3.3)", e); }
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
function _toBuffer(v, field) {
|
|
@@ -119,9 +119,9 @@ function _toBuffer(v, field) {
|
|
|
119
119
|
// Parse one SerializedSCT body inside a sub-reader bounded to the element (so a
|
|
120
120
|
// lying extensions/signature length overruns the element, never the list). A
|
|
121
121
|
// version this parser does not define is preserved OPAQUE, not rejected: RFC 6962
|
|
122
|
-
//
|
|
122
|
+
// sec. 3.3 gives every SerializedSCT its own length prefix precisely so a client "can
|
|
123
123
|
// still parse old SCTs while skipping over new SCTs whose versions they don't
|
|
124
|
-
// understand"
|
|
124
|
+
// understand" -- so an unknown version yields { unknown, version, rawSct } and the
|
|
125
125
|
// v1-specific field decode (and the 47-byte floor) is skipped.
|
|
126
126
|
function _parseSct(r, sctLen) {
|
|
127
127
|
var bodyStart = r.pos;
|
|
@@ -130,7 +130,7 @@ function _parseSct(r, sctLen) {
|
|
|
130
130
|
return { unknown: true, version: version, rawSct: r.buf.subarray(bodyStart, r.end) };
|
|
131
131
|
}
|
|
132
132
|
if (sctLen < SCT_MIN_BODY) {
|
|
133
|
-
throw new CtError("ct/sct-too-short", "a v1 SCT body is at least " + SCT_MIN_BODY + " bytes, got " + sctLen + " (RFC 6962
|
|
133
|
+
throw new CtError("ct/sct-too-short", "a v1 SCT body is at least " + SCT_MIN_BODY + " bytes, got " + sctLen + " (RFC 6962 sec. 3.2)");
|
|
134
134
|
}
|
|
135
135
|
var logId = r.fixed(LOGID_BYTES);
|
|
136
136
|
var timestamp = r.u64();
|
|
@@ -139,7 +139,7 @@ function _parseSct(r, sctLen) {
|
|
|
139
139
|
var sigAlg = r.u8();
|
|
140
140
|
var signature = r.vector(2, 0, null, "ct/sig-overrun");
|
|
141
141
|
if (!r.atEnd()) {
|
|
142
|
-
throw new CtError("ct/sct-trailing-bytes", (r.end - r.pos) + " byte(s) left in a SerializedSCT after the signature (RFC 6962
|
|
142
|
+
throw new CtError("ct/sct-trailing-bytes", (r.end - r.pos) + " byte(s) left in a SerializedSCT after the signature (RFC 6962 sec. 3.3)");
|
|
143
143
|
}
|
|
144
144
|
var timestampMs = timestamp <= MAX_SAFE ? Number(timestamp) : null;
|
|
145
145
|
return {
|
|
@@ -176,13 +176,13 @@ function _parseSct(r, sctLen) {
|
|
|
176
176
|
* `signatureAlgorithm`, the raw `signature` Buffer, and `rawSct` (the full
|
|
177
177
|
* SerializedSCT body). A SerializedSCT whose version this parser does not define
|
|
178
178
|
* is preserved OPAQUE in `unknownScts` as `{ version, rawSct }` rather than
|
|
179
|
-
* failing the list
|
|
179
|
+
* failing the list -- RFC 6962 sec. 3.3 frames each SerializedSCT with its own length
|
|
180
180
|
* so unknown versions are skippable (forward compatibility).
|
|
181
181
|
*
|
|
182
182
|
* The extension value is a DER `OCTET STRING` wrapping the TLS-encoded list
|
|
183
|
-
* (RFC 6962
|
|
183
|
+
* (RFC 6962 sec. 3.3 double wrap); everything below that peel is TLS presentation
|
|
184
184
|
* language, decoded with a bounded cursor. Structure is decoded, crypto is
|
|
185
|
-
* surfaced RAW
|
|
185
|
+
* surfaced RAW -- the signature is never verified and the LogID never recomputed.
|
|
186
186
|
*
|
|
187
187
|
* Throws `CtError` with a stable `ct/*` code on any malformed input (a bad inner
|
|
188
188
|
* DER wrap is `ct/bad-der` with the `asn1/*` fault as `.cause`), never a raw
|
|
@@ -209,22 +209,22 @@ function parseSctList(extValue) {
|
|
|
209
209
|
var outer = new TlsReader(blob, 0, blob.length);
|
|
210
210
|
var listLen = outer.u16("ct/bad-list");
|
|
211
211
|
if (listLen + 2 !== blob.length) {
|
|
212
|
-
throw new CtError("ct/bad-list", "the SCT list declared length " + listLen + " does not match the " + (blob.length - 2) + " byte(s) present (RFC 6962
|
|
212
|
+
throw new CtError("ct/bad-list", "the SCT list declared length " + listLen + " does not match the " + (blob.length - 2) + " byte(s) present (RFC 6962 sec. 3.3)");
|
|
213
213
|
}
|
|
214
214
|
if (listLen < 1) {
|
|
215
|
-
throw new CtError("ct/empty-list", "an SCT list must contain at least one SCT (RFC 6962
|
|
215
|
+
throw new CtError("ct/empty-list", "an SCT list must contain at least one SCT (RFC 6962 sec. 3.3)");
|
|
216
216
|
}
|
|
217
217
|
var scts = [], unknownScts = [];
|
|
218
218
|
while (!outer.atEnd()) {
|
|
219
219
|
if (outer.remaining() < 2) {
|
|
220
|
-
throw new CtError("ct/list-trailing-bytes", "a dangling partial element after the last complete SCT (RFC 6962
|
|
220
|
+
throw new CtError("ct/list-trailing-bytes", "a dangling partial element after the last complete SCT (RFC 6962 sec. 3.3)");
|
|
221
221
|
}
|
|
222
222
|
var sctLen = outer.u16("ct/list-trailing-bytes");
|
|
223
223
|
if (sctLen < 1) {
|
|
224
|
-
throw new CtError("ct/sct-empty", "a SerializedSCT must be non-empty (RFC 6962
|
|
224
|
+
throw new CtError("ct/sct-empty", "a SerializedSCT must be non-empty (RFC 6962 sec. 3.3)");
|
|
225
225
|
}
|
|
226
226
|
if (outer.remaining() < sctLen) {
|
|
227
|
-
throw new CtError("ct/list-trailing-bytes", "a SerializedSCT length " + sctLen + " overruns the list (RFC 6962
|
|
227
|
+
throw new CtError("ct/list-trailing-bytes", "a SerializedSCT length " + sctLen + " overruns the list (RFC 6962 sec. 3.3)");
|
|
228
228
|
}
|
|
229
229
|
var one = _parseSct(outer.subReader(sctLen, "ct/list-trailing-bytes"), sctLen);
|
|
230
230
|
if (one.unknown) unknownScts.push({ version: one.version, rawSct: one.rawSct });
|
|
@@ -240,7 +240,7 @@ function parseSctList(extValue) {
|
|
|
240
240
|
|
|
241
241
|
function _u24Bytes(n) {
|
|
242
242
|
if (n < 1 || n > 0xffffff) {
|
|
243
|
-
throw new CtError("ct/bad-tbs-length", "a certificate / TBSCertificate length must be in 1..2^24-1, got " + n + " (RFC 6962
|
|
243
|
+
throw new CtError("ct/bad-tbs-length", "a certificate / TBSCertificate length must be in 1..2^24-1, got " + n + " (RFC 6962 sec. 3.1)");
|
|
244
244
|
}
|
|
245
245
|
return Buffer.from([(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]);
|
|
246
246
|
}
|
|
@@ -254,20 +254,22 @@ function _u24Bytes(n) {
|
|
|
254
254
|
* @related pki.ct.parseSctList
|
|
255
255
|
*
|
|
256
256
|
* Rebuild the exact `digitally-signed` preimage bytes an external verifier
|
|
257
|
-
* hashes to check an SCT's signature (RFC 6962
|
|
257
|
+
* hashes to check an SCT's signature (RFC 6962 sec. 3.2), for a parsed `sct`.
|
|
258
258
|
* `entry` selects the log-entry arm:
|
|
259
|
-
* - `{ entryType: 0, leafCert: <DER Buffer> }`
|
|
259
|
+
* - `{ entryType: 0, leafCert: <DER Buffer> }` -- an SCT delivered over TLS /
|
|
260
260
|
* OCSP, signed over `x509_entry(0)` with the leaf certificate.
|
|
261
|
-
* - `{ entryType: 1, tbsCertificate: <DER Buffer>, issuerKeyHash: <32B> }`
|
|
261
|
+
* - `{ entryType: 1, tbsCertificate: <DER Buffer>, issuerKeyHash: <32B> }` --
|
|
262
262
|
* an SCT EMBEDDED in a certificate, signed over `precert_entry(1)` with the
|
|
263
263
|
* issuer key hash + the precertificate TBS (the TBS with only the SCT
|
|
264
264
|
* extension removed). `issuerKeyHash` is SHA-256 of the issuer's SPKI DER.
|
|
265
265
|
*
|
|
266
266
|
* The preimage reuses the parsed SCT's raw `extensions` byte-for-byte and
|
|
267
|
-
* re-emits the fixed-width scalars canonically. This never verifies anything
|
|
267
|
+
* re-emits the fixed-width scalars canonically. This never verifies anything --
|
|
268
268
|
* a verifier hashes the returned bytes and checks the signature with the log's
|
|
269
269
|
* public key (compose `webcrypto`). Throws `CtError` (`ct/bad-entry-type`,
|
|
270
|
-
* `ct/bad-issuer-key-hash`, `ct/bad-tbs-length`) on a malformed entry
|
|
270
|
+
* `ct/bad-issuer-key-hash`, `ct/bad-tbs-length`) on a malformed entry, and
|
|
271
|
+
* `ct/bad-input` / `ct/bad-extensions` on an `sct` whose timestamp or
|
|
272
|
+
* extensions exceed their RFC 6962 3.2 wire ranges (uint64 / opaque<0..2^16-1>).
|
|
271
273
|
*
|
|
272
274
|
* @example
|
|
273
275
|
* var sct = pki.ct.parseSctList(sctExtValue).scts[0];
|
|
@@ -278,17 +280,23 @@ function reconstructSignedData(entry, sct) {
|
|
|
278
280
|
entry = entry || {};
|
|
279
281
|
var entryType = entry.entryType;
|
|
280
282
|
if (entryType !== 0 && entryType !== 1) {
|
|
281
|
-
throw new CtError("ct/bad-entry-type", "entryType must be x509_entry(0) or precert_entry(1), got " + entryType + " (RFC 6962
|
|
283
|
+
throw new CtError("ct/bad-entry-type", "entryType must be x509_entry(0) or precert_entry(1), got " + entryType + " (RFC 6962 sec. 3.1)");
|
|
282
284
|
}
|
|
283
|
-
// A fully decoded v1 SCT (from parseSctList().scts[])
|
|
285
|
+
// A fully decoded v1 SCT (from parseSctList().scts[]) -- not an opaque
|
|
284
286
|
// unknownScts entry, whose body layout is undefined and cannot be signed over.
|
|
285
287
|
if (!sct || typeof sct.timestamp !== "bigint" || sct.version !== 0) {
|
|
286
288
|
throw new CtError("ct/bad-input", "reconstructSignedData expects a decoded v1 SCT from parseSctList().scts[]");
|
|
287
289
|
}
|
|
290
|
+
// RFC 6962 3.2: the timestamp is a uint64. A hand-built value outside
|
|
291
|
+
// 0..2^64-1 cannot fill the fixed 8-byte field -- refused typed here rather
|
|
292
|
+
// than escaping as a raw RangeError from the Buffer write.
|
|
293
|
+
if (sct.timestamp < 0n || sct.timestamp > 0xffffffffffffffffn) {
|
|
294
|
+
throw new CtError("ct/bad-input", "sct.timestamp must be a uint64 (0..2^64-1), got " + sct.timestamp.toString() + " (RFC 6962 3.2)");
|
|
295
|
+
}
|
|
288
296
|
var parts = [];
|
|
289
|
-
parts.push(Buffer.from([sct.version & 0xff])); // Version
|
|
290
|
-
parts.push(Buffer.from([0])); // SignatureType
|
|
291
|
-
var ts = Buffer.alloc(8); ts.writeBigUInt64BE(
|
|
297
|
+
parts.push(Buffer.from([sct.version & 0xff])); // Version -- v1(0)
|
|
298
|
+
parts.push(Buffer.from([0])); // SignatureType -- certificate_timestamp(0)
|
|
299
|
+
var ts = Buffer.alloc(8); ts.writeBigUInt64BE(sct.timestamp); parts.push(ts); // uint64 timestamp
|
|
292
300
|
parts.push(Buffer.from([(entryType >> 8) & 0xff, entryType & 0xff])); // LogEntryType (2 bytes BE)
|
|
293
301
|
if (entryType === 0) {
|
|
294
302
|
var cert = _toBuffer(entry.leafCert, "leafCert");
|
|
@@ -296,13 +304,20 @@ function reconstructSignedData(entry, sct) {
|
|
|
296
304
|
} else {
|
|
297
305
|
var ikh = _toBuffer(entry.issuerKeyHash, "issuerKeyHash");
|
|
298
306
|
if (ikh.length !== 32) {
|
|
299
|
-
throw new CtError("ct/bad-issuer-key-hash", "issuer_key_hash must be exactly 32 bytes (SHA-256 of the issuer SPKI), got " + ikh.length + " (RFC 6962
|
|
307
|
+
throw new CtError("ct/bad-issuer-key-hash", "issuer_key_hash must be exactly 32 bytes (SHA-256 of the issuer SPKI), got " + ikh.length + " (RFC 6962 sec. 3.2)");
|
|
300
308
|
}
|
|
301
309
|
var tbs = _toBuffer(entry.tbsCertificate, "tbsCertificate");
|
|
302
310
|
parts.push(ikh);
|
|
303
311
|
parts.push(_u24Bytes(tbs.length)); parts.push(tbs); // PreCert.tbs_certificate<1..2^24-1>
|
|
304
312
|
}
|
|
305
313
|
var ext = _toBuffer(sct.extensions, "sct.extensions"); // reuse the parsed raw bytes, never re-encode
|
|
314
|
+
// RFC 6962 3.2: CtExtensions is opaque<0..2^16-1>. A longer value is
|
|
315
|
+
// unrepresentable -- its 2-byte prefix would silently truncate mod 65536,
|
|
316
|
+
// an internally inconsistent preimage -- so it is refused, matching the
|
|
317
|
+
// range check the u24 certificate lengths get.
|
|
318
|
+
if (ext.length > 0xffff) {
|
|
319
|
+
throw new CtError("ct/bad-extensions", "CtExtensions must be 0..65535 bytes, got " + ext.length + " (RFC 6962 3.2)");
|
|
320
|
+
}
|
|
306
321
|
parts.push(Buffer.from([(ext.length >> 8) & 0xff, ext.length & 0xff])); parts.push(ext); // CtExtensions
|
|
307
322
|
return Buffer.concat(parts);
|
|
308
323
|
}
|
package/lib/framework-error.js
CHANGED
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
* `{ name, code, message, permanent, isPkiError: true }`.
|
|
17
17
|
*
|
|
18
18
|
* `code` is a stable, greppable `domain/reason` string
|
|
19
|
-
* (`asn1/indefinite-length`, `x509/not-a-certificate`)
|
|
19
|
+
* (`asn1/indefinite-length`, `x509/not-a-certificate`) -- safe to switch
|
|
20
20
|
* on and safe to log. Because every failure here is a deterministic
|
|
21
21
|
* verdict on the bytes in hand (a malformed length, an unknown OID
|
|
22
|
-
* shape, a truncated certificate), errors are `permanent: true`
|
|
22
|
+
* shape, a truncated certificate), errors are `permanent: true` -- the
|
|
23
23
|
* same input will never parse on retry.
|
|
24
24
|
*
|
|
25
25
|
* @card
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
* classes the toolkit throws.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
// The frozen shape of every error code: a `domain/reason` string of
|
|
31
|
+
// lowercase alphanumerics and dashes.
|
|
32
|
+
var CODE_SHAPE = /^[a-z0-9-]+\/[a-z0-9-]+$/;
|
|
33
|
+
|
|
30
34
|
/**
|
|
31
35
|
* @primitive pki.errors.PkiError
|
|
32
36
|
* @signature new PkiError(message, code)
|
|
@@ -36,6 +40,11 @@
|
|
|
36
40
|
*
|
|
37
41
|
* Base class every toolkit error extends. Provides the unified
|
|
38
42
|
* `instanceof` check plus the `{ name, code, isPkiError }` shape.
|
|
43
|
+
* A supplied `code` must be a `domain/reason` string (lowercase
|
|
44
|
+
* alphanumerics and dashes) -- the construction throws a `TypeError`
|
|
45
|
+
* otherwise, which catches an argument-order swap with the
|
|
46
|
+
* `defineClass` subclasses' `(code, message)` convention at the call
|
|
47
|
+
* site instead of shipping prose into a code-switching consumer.
|
|
39
48
|
*
|
|
40
49
|
* @example
|
|
41
50
|
* try { pki.asn1.decode(bytes); }
|
|
@@ -47,7 +56,14 @@ class PkiError extends Error {
|
|
|
47
56
|
constructor(message, code) {
|
|
48
57
|
super(message);
|
|
49
58
|
this.name = "PkiError";
|
|
50
|
-
|
|
59
|
+
var c = code || "pki/invalid";
|
|
60
|
+
if (typeof c !== "string" || !CODE_SHAPE.test(c)) {
|
|
61
|
+
throw new TypeError(
|
|
62
|
+
"PkiError code must be a domain/reason string, got " + String(c) +
|
|
63
|
+
" (PkiError takes (message, code); defineClass subclasses take (code, message))"
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
this.code = c;
|
|
51
67
|
this.isPkiError = true;
|
|
52
68
|
this.permanent = true;
|
|
53
69
|
}
|
|
@@ -60,13 +76,16 @@ class PkiError extends Error {
|
|
|
60
76
|
* @status stable
|
|
61
77
|
* @spec internal (design: error-class factory)
|
|
62
78
|
*
|
|
63
|
-
* Factory that produces a `PkiError` subclass with the standard shape
|
|
79
|
+
* Factory that produces a `PkiError` subclass with the standard shape --
|
|
64
80
|
* eliminating the per-domain boilerplate. The returned constructor takes
|
|
65
81
|
* `(code, message)`, stamps `name`, sets an `is<Name>` flag, and exposes
|
|
66
82
|
* a `.factory` static for the common `var _err = XxxError.factory` shape.
|
|
83
|
+
* The `code` must be a `domain/reason` string (the base-class contract);
|
|
84
|
+
* without `withCause`, a third constructor argument throws a `TypeError`
|
|
85
|
+
* rather than silently discarding a cause the caller meant to thread.
|
|
67
86
|
*
|
|
68
87
|
* @opts
|
|
69
|
-
* withCause: boolean, // default: false
|
|
88
|
+
* withCause: boolean, // default: false -- constructor becomes (code, message, cause)
|
|
70
89
|
*
|
|
71
90
|
* @example
|
|
72
91
|
* var MyError = pki.errors.defineClass("MyError");
|
|
@@ -85,7 +104,11 @@ function defineClass(name, opts) {
|
|
|
85
104
|
super(message, code);
|
|
86
105
|
this.name = name;
|
|
87
106
|
this[flagKey] = true;
|
|
88
|
-
if (withCause
|
|
107
|
+
if (withCause) {
|
|
108
|
+
if (arg3 !== undefined) this.cause = arg3;
|
|
109
|
+
} else if (arg3 !== undefined) {
|
|
110
|
+
throw new TypeError(name + " takes (code, message); to thread a cause, define the class with { withCause: true }");
|
|
111
|
+
}
|
|
89
112
|
}
|
|
90
113
|
};
|
|
91
114
|
Object.defineProperty(GeneratedError, "name", { value: name, configurable: true });
|
|
@@ -97,101 +120,101 @@ function defineClass(name, opts) {
|
|
|
97
120
|
|
|
98
121
|
// ---- Per-domain error classes ----
|
|
99
122
|
|
|
100
|
-
// ConstantsError
|
|
123
|
+
// ConstantsError -- a bad scale argument to C.TIME.* / C.BYTES.* (an
|
|
101
124
|
// authoring bug caught at config time).
|
|
102
125
|
var ConstantsError = defineClass("ConstantsError");
|
|
103
126
|
|
|
104
|
-
// Asn1Error
|
|
127
|
+
// Asn1Error -- malformed / non-canonical DER: truncated TLV, indefinite
|
|
105
128
|
// length, non-minimal length or integer, leftover trailing bytes, depth
|
|
106
129
|
// or size cap exceeded. DER is a canonical encoding, so anything the
|
|
107
130
|
// decoder rejects is permanently invalid.
|
|
108
131
|
var Asn1Error = defineClass("Asn1Error");
|
|
109
132
|
|
|
110
|
-
// OidError
|
|
133
|
+
// OidError -- a malformed object-identifier: fewer than two arcs, a first
|
|
111
134
|
// arc outside 0..2, a second arc >= 40 under arcs 0/1, or a non-minimal
|
|
112
135
|
// base-128 sub-identifier.
|
|
113
136
|
var OidError = defineClass("OidError");
|
|
114
137
|
|
|
115
|
-
// PemError
|
|
138
|
+
// PemError -- a malformed PEM envelope: missing / mismatched BEGIN/END
|
|
116
139
|
// markers, a bad label, or non-base64 body.
|
|
117
140
|
var PemError = defineClass("PemError");
|
|
118
141
|
|
|
119
|
-
// CertificateError
|
|
142
|
+
// CertificateError -- a byte sequence that is not a well-formed X.509
|
|
120
143
|
// certificate (wrong outer structure, unparseable field, unsupported
|
|
121
144
|
// version).
|
|
122
145
|
var CertificateError = defineClass("CertificateError", { withCause: true });
|
|
123
146
|
|
|
124
|
-
// CrlError
|
|
125
|
-
// (CertificateList / TBSCertList
|
|
147
|
+
// CrlError -- a byte sequence that is not a well-formed X.509 CRL
|
|
148
|
+
// (CertificateList / TBSCertList -- RFC 5280 sec. 5).
|
|
126
149
|
var CrlError = defineClass("CrlError", { withCause: true });
|
|
127
150
|
|
|
128
|
-
// SchemaError
|
|
151
|
+
// SchemaError -- the schema-family orchestrator's own errors (input that matches
|
|
129
152
|
// no registered format / does not decode). (code, message) shape like the rest.
|
|
130
153
|
var SchemaError = defineClass("SchemaError", { withCause: true });
|
|
131
154
|
|
|
132
|
-
// CsrError
|
|
155
|
+
// CsrError -- a byte sequence that is not a well-formed PKCS#10 CertificationRequest
|
|
133
156
|
// (RFC 2986).
|
|
134
157
|
var CsrError = defineClass("CsrError", { withCause: true });
|
|
135
158
|
|
|
136
|
-
// Pkcs8Error
|
|
137
|
-
// OneAsymmetricKey or EncryptedPrivateKeyInfo (RFC 5208
|
|
159
|
+
// Pkcs8Error -- a byte sequence that is not a well-formed PKCS#8 PrivateKeyInfo /
|
|
160
|
+
// OneAsymmetricKey or EncryptedPrivateKeyInfo (RFC 5208 sec. 5, RFC 5958 sec. 2/sec. 3).
|
|
138
161
|
var Pkcs8Error = defineClass("Pkcs8Error", { withCause: true });
|
|
139
162
|
|
|
140
|
-
// CmsError
|
|
163
|
+
// CmsError -- a byte sequence that is not a well-formed CMS ContentInfo /
|
|
141
164
|
// SignedData (RFC 5652). Carries the underlying leaf fault as `.cause`.
|
|
142
165
|
var CmsError = defineClass("CmsError", { withCause: true });
|
|
143
166
|
|
|
144
|
-
// OcspError
|
|
145
|
-
// (OCSPRequest / OCSPResponse
|
|
167
|
+
// OcspError -- a byte sequence that is not a well-formed OCSP request or response
|
|
168
|
+
// (OCSPRequest / OCSPResponse -- RFC 6960 sec. 4). Carries the leaf fault as `.cause`.
|
|
146
169
|
var OcspError = defineClass("OcspError", { withCause: true });
|
|
147
170
|
|
|
148
|
-
// TspError
|
|
171
|
+
// TspError -- a byte sequence that is not a well-formed RFC 3161 timestamp token,
|
|
149
172
|
// TSTInfo, or TimeStampResp. Carries the underlying leaf fault as `.cause`.
|
|
150
173
|
var TspError = defineClass("TspError", { withCause: true });
|
|
151
174
|
|
|
152
|
-
// AttrCertError
|
|
153
|
-
// Certificate (AttributeCertificate / AttributeCertificateInfo
|
|
175
|
+
// AttrCertError -- a byte sequence that is not a well-formed X.509 Attribute
|
|
176
|
+
// Certificate (AttributeCertificate / AttributeCertificateInfo -- RFC 5755 sec. 4),
|
|
154
177
|
// or a recognized-and-deferred legacy AttributeCertificateV1. Carries the
|
|
155
178
|
// underlying leaf fault as `.cause`.
|
|
156
179
|
var AttrCertError = defineClass("AttrCertError", { withCause: true });
|
|
157
180
|
|
|
158
|
-
// CrmfError
|
|
181
|
+
// CrmfError -- a byte sequence that is not a well-formed RFC 4211 CertReqMessages
|
|
159
182
|
// / CertReqMsg / CertRequest / CertTemplate. Carries the underlying leaf fault as
|
|
160
183
|
// `.cause`.
|
|
161
184
|
var CrmfError = defineClass("CrmfError", { withCause: true });
|
|
162
185
|
|
|
163
|
-
// Pkcs12Error
|
|
164
|
-
// (AuthenticatedSafe / SafeContents / SafeBag), or a PFX violating a
|
|
186
|
+
// Pkcs12Error -- a byte sequence that is not a well-formed RFC 7292 PFX
|
|
187
|
+
// (AuthenticatedSafe / SafeContents / SafeBag), or a PFX violating a sec. 4
|
|
165
188
|
// coherence rule (version, integrity mode, bag dispatch, MacData). Carries
|
|
166
189
|
// the underlying leaf fault as `.cause`.
|
|
167
190
|
var Pkcs12Error = defineClass("Pkcs12Error", { withCause: true });
|
|
168
191
|
|
|
169
|
-
// CmpError
|
|
170
|
-
// (PKIHeader / PKIBody / protection / extraCerts), or one violating a
|
|
171
|
-
// coherence rule (protection
|
|
192
|
+
// CmpError -- a byte sequence that is not a well-formed RFC 9810 PKIMessage
|
|
193
|
+
// (PKIHeader / PKIBody / protection / extraCerts), or one violating a sec. 5
|
|
194
|
+
// coherence rule (protection<=>protectionAlg, certConf-hashAlg<=>pvno). Carries
|
|
172
195
|
// the underlying leaf fault as `.cause`.
|
|
173
196
|
var CmpError = defineClass("CmpError", { withCause: true });
|
|
174
197
|
|
|
175
|
-
// PathError
|
|
198
|
+
// PathError -- a certification path that fails RFC 5280 sec. 6 validation, or a
|
|
176
199
|
// malformed input handed to the validator (an extension value that does not
|
|
177
200
|
// decode, an empty path, an unsupported signature algorithm). Carries the
|
|
178
201
|
// per-check reason in `.code` (`path/*`) and the underlying leaf fault as
|
|
179
202
|
// `.cause`.
|
|
180
203
|
var PathError = defineClass("PathError", { withCause: true });
|
|
181
204
|
|
|
182
|
-
// SmimeError
|
|
205
|
+
// SmimeError -- a byte sequence that is not a well-formed RFC 5035 ESS
|
|
183
206
|
// signing-certificate attribute (SigningCertificate / SigningCertificateV2 /
|
|
184
207
|
// ESSCertID(v2) / IssuerSerial) or RFC 8551 SMIMECapabilities, or a CMS
|
|
185
|
-
// attribute violating a
|
|
208
|
+
// attribute violating a sec. 2.5 coherence rule (single-AttributeValue, certs
|
|
186
209
|
// non-empty, non-canonical DEFAULT hashAlgorithm). Carries the underlying leaf
|
|
187
210
|
// fault (e.g. the inner asn1/* decode error) as `.cause`.
|
|
188
211
|
var SmimeError = defineClass("SmimeError", { withCause: true });
|
|
189
212
|
|
|
190
|
-
// CtError
|
|
213
|
+
// CtError -- a byte sequence that is not a well-formed RFC 6962 Certificate
|
|
191
214
|
// Transparency SCT list: a malformed inner DER OCTET-STRING wrap, or a TLS
|
|
192
215
|
// presentation-language framing violation (a lying vector length, a field read
|
|
193
216
|
// past its bound, a truncated element). An SCT whose version this parser does
|
|
194
|
-
// not define is NOT an error
|
|
217
|
+
// not define is NOT an error -- RFC 6962 sec. 3.3 makes unknown versions skippable,
|
|
195
218
|
// so they are preserved opaque. Carries the underlying leaf fault (e.g. the
|
|
196
219
|
// inner `asn1/*` decode error) as `.cause`.
|
|
197
220
|
var CtError = defineClass("CtError", { withCause: true });
|