@blamejs/pki 0.6.14 → 0.6.15
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 +14 -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 +8 -252
- 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/asn1-der.js
CHANGED
|
@@ -35,30 +35,17 @@ var constants = require("./constants");
|
|
|
35
35
|
var frameworkError = require("./framework-error");
|
|
36
36
|
var guard = require("./guard-all");
|
|
37
37
|
var intrinsic = require("./guard-intrinsic");
|
|
38
|
-
// The list test at each builder door decides whether a caller's argument is refused. Read at the
|
|
39
|
-
// moment it runs, a replacement answering true admits a non-list and the door it guards is gone.
|
|
40
38
|
var _isArray = intrinsic.isArray;
|
|
41
39
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
42
40
|
|
|
43
41
|
var Asn1Error = frameworkError.Asn1Error;
|
|
44
42
|
var OidError = frameworkError.OidError;
|
|
45
43
|
|
|
46
|
-
// (code, message) -> Asn1Error, the factory shape the composed guards throw
|
|
47
|
-
// through so a malformed codec argument keeps the asn1/* typed verdict.
|
|
48
44
|
function _asn1Error(c, m) { return new Asn1Error(c, m); }
|
|
49
45
|
|
|
50
|
-
// (code, message) -> OidError, the factory the OID string guard throws through
|
|
51
|
-
// so build.oid keeps its oid/* typed verdict (oid/bad-input, oid/bad-arc).
|
|
52
46
|
function _oidErr(c, m) { return new OidError(c, m); }
|
|
53
47
|
|
|
54
|
-
// ---- Tag constants (universal class) --------------------------------
|
|
55
48
|
|
|
56
|
-
// L1 -- the ASN.1 universal-type descriptor registry: the single source of
|
|
57
|
-
// truth for the codec's type metadata. Each entry is DECLARATIVE DATA --
|
|
58
|
-
// { tag, form } where form is the DER encoding form ("primitive" or
|
|
59
|
-
// "constructed", X.690 sec. 8/sec. 10.2). TAGS and the two structural type-sets below
|
|
60
|
-
// are all DERIVED from it, so registering a universal type is one descriptor
|
|
61
|
-
// entry and the decoder's form checks (below) cover it automatically.
|
|
62
49
|
var UNIVERSAL_TYPES = {
|
|
63
50
|
BOOLEAN: { tag: 0x01, form: "primitive" },
|
|
64
51
|
INTEGER: { tag: 0x02, form: "primitive" },
|
|
@@ -84,8 +71,6 @@ var UNIVERSAL_TYPES = {
|
|
|
84
71
|
BMP_STRING: { tag: 0x1e, form: "primitive" },
|
|
85
72
|
};
|
|
86
73
|
|
|
87
|
-
// TAGS { name: tag }, derived from the registry; the public constant consumers
|
|
88
|
-
// read (pki.asn1.TAGS.INTEGER, ...).
|
|
89
74
|
var TAGS = {};
|
|
90
75
|
Object.keys(UNIVERSAL_TYPES).forEach(function (k) { TAGS[k] = UNIVERSAL_TYPES[k].tag; });
|
|
91
76
|
|
|
@@ -95,15 +80,6 @@ var CLASS_CONTEXT = 0x80;
|
|
|
95
80
|
var CLASS_PRIVATE = 0xc0;
|
|
96
81
|
var CONSTRUCTED_BIT = 0x20;
|
|
97
82
|
|
|
98
|
-
// X.690 sec. 8.9.1 / sec. 8.11.1 / sec. 10.2 (DER), the mirror rules: an
|
|
99
|
-
// always-constructed universal type (SEQUENCE, SET, and the SEQUENCE-based
|
|
100
|
-
// EXTERNAL / EMBEDDED PDV / unrestricted CHARACTER STRING) MUST be encoded
|
|
101
|
-
// constructed, and every other universal type MUST be encoded primitive. The
|
|
102
|
-
// constructed-capable set is derived from the registry above and keyed by tag
|
|
103
|
-
// for an O(1) decode-time form check. It is a whitelist: a universal tag
|
|
104
|
-
// outside it, a registered primitive type and an unregistered one
|
|
105
|
-
// (GeneralString, ObjectDescriptor, REAL, ...) alike, has no
|
|
106
|
-
// DER constructed form, so the default is reject.
|
|
107
83
|
var CONSTRUCTED_ONLY_UNIVERSAL_TAGS = Object.create(null);
|
|
108
84
|
Object.keys(UNIVERSAL_TYPES).forEach(function (k) {
|
|
109
85
|
var d = UNIVERSAL_TYPES[k];
|
|
@@ -116,7 +92,6 @@ function _className(bits) {
|
|
|
116
92
|
case CLASS_APPLICATION: return "application";
|
|
117
93
|
case CLASS_CONTEXT: return "context";
|
|
118
94
|
case CLASS_PRIVATE: return "private";
|
|
119
|
-
// Coverage residual -- tagClassBits is (first & 0xc0), always one of the 4 enumerated class values; the default arm is unreachable defensive code.
|
|
120
95
|
default: return "universal";
|
|
121
96
|
}
|
|
122
97
|
}
|
|
@@ -125,7 +100,6 @@ function _asBuffer(input, who) {
|
|
|
125
100
|
return guard.bytes.view(input, Asn1Error, "asn1/not-buffer", who);
|
|
126
101
|
}
|
|
127
102
|
|
|
128
|
-
// ---- Decoder --------------------------------------------------------
|
|
129
103
|
|
|
130
104
|
/**
|
|
131
105
|
* @primitive pki.asn1.decode
|
|
@@ -169,15 +143,8 @@ function _asBuffer(input, who) {
|
|
|
169
143
|
function decode(input, opts) {
|
|
170
144
|
opts = opts || {};
|
|
171
145
|
var buf = _asBuffer(input, "decode");
|
|
172
|
-
// A size/depth cap opt: absent means the constants.LIMITS default; a provided
|
|
173
|
-
// value must be a non-negative integer (guard.limits.cap). depthCap also
|
|
174
|
-
// refuses a maxDepth above the stack-safe ceiling so a raised cap cannot drive
|
|
175
|
-
// recursive descent into a bare RangeError instead of the typed asn1/too-deep.
|
|
176
146
|
var maxBytes = guard.limits.cap(opts.maxBytes, "maxBytes", constants.LIMITS.DER_MAX_BYTES);
|
|
177
147
|
var maxDepth = guard.limits.depthCap(opts.maxDepth, "maxDepth", constants.LIMITS.DER_MAX_DEPTH);
|
|
178
|
-
// Total decoded-node ceiling: the eager node tree lets a dense run of tiny TLVs
|
|
179
|
-
// amplify a small input into a huge tree, so a per-node counter fails closed at
|
|
180
|
-
// the cap (the memory-fanout class the CBOR codec closed with CBOR_MAX_ITEMS).
|
|
181
148
|
var maxItems = guard.limits.cap(opts.maxItems, "maxItems", constants.LIMITS.DER_MAX_ITEMS);
|
|
182
149
|
if (buf.length > maxBytes) {
|
|
183
150
|
throw new Asn1Error("asn1/too-large", "input " + buf.length + " bytes exceeds cap " + maxBytes);
|
|
@@ -194,7 +161,7 @@ function _decodeTLV(buf, start, limit, depth, maxDepth, ber, strDepth, ctr) {
|
|
|
194
161
|
if (depth > maxDepth) {
|
|
195
162
|
throw new Asn1Error("asn1/too-deep", "nesting exceeds depth cap " + maxDepth);
|
|
196
163
|
}
|
|
197
|
-
if (ctr) ctr.tick();
|
|
164
|
+
if (ctr) ctr.tick();
|
|
198
165
|
var p = start;
|
|
199
166
|
if (p >= limit) throw new Asn1Error("asn1/truncated", "expected an identifier octet");
|
|
200
167
|
var first = buf[p]; p += 1;
|
|
@@ -203,7 +170,6 @@ function _decodeTLV(buf, start, limit, depth, maxDepth, ber, strDepth, ctr) {
|
|
|
203
170
|
var tagNumber = first & 0x1f;
|
|
204
171
|
|
|
205
172
|
if (tagNumber === 0x1f) {
|
|
206
|
-
// High-tag-number form: base-128, minimal (no leading 0x80).
|
|
207
173
|
tagNumber = 0;
|
|
208
174
|
var seen = 0;
|
|
209
175
|
for (;;) {
|
|
@@ -222,29 +188,10 @@ function _decodeTLV(buf, start, limit, depth, maxDepth, ber, strDepth, ctr) {
|
|
|
222
188
|
}
|
|
223
189
|
}
|
|
224
190
|
|
|
225
|
-
// X.690 8.9.1 / 8.11.1 -- an always-constructed universal type (SEQUENCE, SET,
|
|
226
|
-
// EXTERNAL, EMBEDDED PDV, CHARACTER STRING) is always constructed; a
|
|
227
|
-
// primitive-tagged one is not valid DER (and would decode to a leaf that
|
|
228
|
-
// constructed-structure consumers dereference as a parent). Driven by the
|
|
229
|
-
// registry-derived set, not a hardcoded tag list.
|
|
230
191
|
if (tagClassBits === CLASS_UNIVERSAL && CONSTRUCTED_ONLY_UNIVERSAL_TAGS[tagNumber] && !constructed) {
|
|
231
192
|
throw new Asn1Error("asn1/bad-tlv", "a universal constructed-only type (SEQUENCE/SET/EXTERNAL/EMBEDDED PDV/CHARACTER STRING) must be constructed");
|
|
232
193
|
}
|
|
233
194
|
|
|
234
|
-
// X.690 sec. 8.x / sec. 10.2 -- the mirror rule: a universal type other than
|
|
235
|
-
// SEQUENCE/SET (INTEGER, OID, BOOLEAN, every restricted string type --
|
|
236
|
-
// registered or not -- times, BIT/OCTET STRING, REAL, ...) encoded
|
|
237
|
-
// constructed is not valid DER. Without this a constructed string tag
|
|
238
|
-
// decodes to a childless node that later paths (an X.509 DN attribute
|
|
239
|
-
// value) would hex-render or dereference instead of failing closed; the
|
|
240
|
-
// constructed-capable set is a whitelist so an UNREGISTERED string type
|
|
241
|
-
// (a constructed GeneralString) cannot slip past the form check either.
|
|
242
|
-
// The ber mode licenses exactly one constructed primitive-only type -- the
|
|
243
|
-
// OCTET STRING (the streamed content carrier RFC 7292 sec. 4.1 permits); its
|
|
244
|
-
// segments are reassembled below. Every other primitive-only type stays
|
|
245
|
-
// primitive even in BER. Reassembly re-copies each nesting level's payload,
|
|
246
|
-
// so nesting is capped on the way down: real producers segment one level
|
|
247
|
-
// deep, and a deep chain multiplies transient memory without carrying data.
|
|
248
195
|
if (tagClassBits === CLASS_UNIVERSAL && constructed && !CONSTRUCTED_ONLY_UNIVERSAL_TAGS[tagNumber]) {
|
|
249
196
|
if (!(ber && tagNumber === TAGS.OCTET_STRING)) {
|
|
250
197
|
throw new Asn1Error("asn1/constructed-primitive-type", "a universal primitive-only type must be encoded primitive in DER");
|
|
@@ -261,8 +208,6 @@ function _decodeTLV(buf, start, limit, depth, maxDepth, ber, strDepth, ctr) {
|
|
|
261
208
|
if (lenByte < 0x80) {
|
|
262
209
|
length = lenByte;
|
|
263
210
|
} else if (lenByte === 0x80) {
|
|
264
|
-
// X.690 sec. 8.1.3.6 -- indefinite length is only defined for a constructed
|
|
265
|
-
// encoding; the value runs to the end-of-contents octets.
|
|
266
211
|
if (!ber || !constructed) {
|
|
267
212
|
throw new Asn1Error("asn1/indefinite-length", "indefinite length is not valid DER");
|
|
268
213
|
}
|
|
@@ -326,12 +271,6 @@ function _decodeTLV(buf, start, limit, depth, maxDepth, ber, strDepth, ctr) {
|
|
|
326
271
|
bytes: buf.subarray(start, end),
|
|
327
272
|
};
|
|
328
273
|
|
|
329
|
-
// BER reassembly: a constructed OCTET STRING's segments concatenate into
|
|
330
|
-
// one primitive content, so every downstream reader sees the value a
|
|
331
|
-
// single-segment encoding would carry; `bytes` keeps the original wire
|
|
332
|
-
// range for raw round-trip surfaces. Nested constructed segments arrive
|
|
333
|
-
// already reassembled by the recursion (their nesting depth is capped on
|
|
334
|
-
// the way down -- see the strDepth check above).
|
|
335
274
|
if (ber && constructed && tagClassBits === CLASS_UNIVERSAL && tagNumber === TAGS.OCTET_STRING) {
|
|
336
275
|
var segments = [];
|
|
337
276
|
for (var s = 0; s < children.length; s++) {
|
|
@@ -344,17 +283,12 @@ function _decodeTLV(buf, start, limit, depth, maxDepth, ber, strDepth, ctr) {
|
|
|
344
283
|
node.constructed = false;
|
|
345
284
|
node.children = null;
|
|
346
285
|
}
|
|
347
|
-
// A context-tagged constructed node from a ber decode may be an IMPLICIT
|
|
348
|
-
// streamed string (the tag hides the underlying type), which only the
|
|
349
|
-
// schema-driven typed reader can identify -- mark it so readers reassemble
|
|
350
|
-
// exactly those nodes and a strict decode's verdicts never change.
|
|
351
286
|
if (ber && constructed && tagClassBits === CLASS_CONTEXT && node.children) {
|
|
352
287
|
node.ber = true;
|
|
353
288
|
}
|
|
354
289
|
return { node: node, end: end };
|
|
355
290
|
}
|
|
356
291
|
|
|
357
|
-
// ---- Node assertions ------------------------------------------------
|
|
358
292
|
|
|
359
293
|
function _expectUniversal(node, tag, who) {
|
|
360
294
|
if (node.tagClass !== "universal" || node.tagNumber !== tag) {
|
|
@@ -367,7 +301,6 @@ function _expectPrimitive(node, who) {
|
|
|
367
301
|
if (node.constructed) throw new Asn1Error("asn1/expected-primitive", who + ": expected a primitive encoding");
|
|
368
302
|
}
|
|
369
303
|
|
|
370
|
-
// ---- Typed readers --------------------------------------------------
|
|
371
304
|
|
|
372
305
|
function readBoolean(node) {
|
|
373
306
|
_expectUniversal(node, TAGS.BOOLEAN, "readBoolean");
|
|
@@ -379,21 +312,10 @@ function readBoolean(node) {
|
|
|
379
312
|
throw new Asn1Error("asn1/bad-boolean", "DER BOOLEAN must be 0x00 or 0xFF, got 0x" + v.toString(16));
|
|
380
313
|
}
|
|
381
314
|
|
|
382
|
-
// INTEGER and ENUMERATED share DER content encoding (a two's-complement big-endian
|
|
383
|
-
// value) but are DISTINCT universal types. This decodes that shared content -- the
|
|
384
|
-
// caller asserts the tag first. `typeName` names the type in error messages; the
|
|
385
|
-
// stable error codes stay the integer family (a caller switching on `code` treats
|
|
386
|
-
// both alike since the encoding is identical).
|
|
387
315
|
function _readIntegerLikeContent(node, typeName, who) {
|
|
388
316
|
_expectPrimitive(node, who);
|
|
389
317
|
var c = node.content;
|
|
390
318
|
if (c.length === 0) throw new Asn1Error("asn1/bad-integer", typeName + " must have at least 1 content octet");
|
|
391
|
-
// Defense-in-depth: refuse an over-cap magnitude before touching BigInt.
|
|
392
|
-
// A one-shot hex parse is linear, but the length still bounds worst-case
|
|
393
|
-
// work -- reject a hostile length prefix up front. The cap bounds the
|
|
394
|
-
// MAGNITUDE; a positive INTEGER whose top bit is set carries one leading
|
|
395
|
-
// 0x00 DER sign octet, so allow cap + 1 content bytes (an RSA-131072
|
|
396
|
-
// modulus is 16384 magnitude bytes plus that sign pad).
|
|
397
319
|
if (c.length > constants.LIMITS.DER_MAX_INTEGER_BYTES + 1) {
|
|
398
320
|
throw new Asn1Error("asn1/integer-too-large",
|
|
399
321
|
typeName + " content " + c.length + " bytes exceeds cap " + (constants.LIMITS.DER_MAX_INTEGER_BYTES + 1));
|
|
@@ -403,32 +325,20 @@ function _readIntegerLikeContent(node, typeName, who) {
|
|
|
403
325
|
if (c[0] === 0xff && (c[1] & 0x80) !== 0) throw new Asn1Error("asn1/non-minimal-integer", "non-minimal negative " + typeName);
|
|
404
326
|
}
|
|
405
327
|
var neg = (c[0] & 0x80) !== 0;
|
|
406
|
-
// Coverage residual -- the c.length===0 guard above throws, so c.length is always >=1 here; the 0n alternate is unreachable.
|
|
407
328
|
var mag = c.length ? BigInt("0x" + Buffer.from(c).toString("hex")) : 0n;
|
|
408
329
|
return neg ? mag - (1n << BigInt(c.length * 8)) : mag;
|
|
409
330
|
}
|
|
410
331
|
|
|
411
|
-
// read.integer is STRICT on the tag: an ENUMERATED node (which shares INTEGER's
|
|
412
|
-
// content encoding) is NOT accepted here. Coercing an ENUMERATED to an INTEGER is a
|
|
413
|
-
// type confusion -- an INTEGER-pinned field (a certificate/CSR version, a serial
|
|
414
|
-
// number, a cRLNumber) encoded as ENUMERATED would slip past a lenient reader.
|
|
415
|
-
// Read ENUMERATED values with read.enumerated.
|
|
416
332
|
function readInteger(node) {
|
|
417
333
|
_expectUniversal(node, TAGS.INTEGER, "readInteger");
|
|
418
334
|
return _readIntegerLikeContent(node, "INTEGER", "readInteger");
|
|
419
335
|
}
|
|
420
336
|
|
|
421
|
-
// read.enumerated reads an ENUMERATED value; its content rules are identical to
|
|
422
|
-
// INTEGER (non-empty, minimally encoded, magnitude within the cap).
|
|
423
337
|
function readEnumerated(node) {
|
|
424
338
|
_expectUniversal(node, TAGS.ENUMERATED, "readEnumerated");
|
|
425
339
|
return _readIntegerLikeContent(node, "ENUMERATED", "readEnumerated");
|
|
426
340
|
}
|
|
427
341
|
|
|
428
|
-
// Read a [tag] IMPLICIT INTEGER -- a context-class PRIMITIVE node whose content is an
|
|
429
|
-
// integer body (the IMPLICIT tag replaces the universal INTEGER tag). Same minimal
|
|
430
|
-
// two's-complement content rules as a universal INTEGER. The integer counterpart of
|
|
431
|
-
// readBitStringImplicit, for the RFC 3161 Accuracy millis [0] / micros [1] fields.
|
|
432
342
|
function readIntegerImplicit(node, tag) {
|
|
433
343
|
if (node.tagClass !== "context" || node.tagNumber !== tag) {
|
|
434
344
|
throw new Asn1Error("asn1/unexpected-tag", "readIntegerImplicit: expected context tag [" + tag +
|
|
@@ -437,9 +347,6 @@ function readIntegerImplicit(node, tag) {
|
|
|
437
347
|
return _readIntegerLikeContent(node, "INTEGER", "readIntegerImplicit");
|
|
438
348
|
}
|
|
439
349
|
|
|
440
|
-
// The BIT STRING content decoder (the leading unused-bit octet + body, with the
|
|
441
|
-
// DER zero-pad rule). Shared by the universal reader and the IMPLICIT
|
|
442
|
-
// context-tagged reader -- the caller asserts the tag first.
|
|
443
350
|
function _readBitStringContent(node, who) {
|
|
444
351
|
_expectPrimitive(node, who);
|
|
445
352
|
var c = node.content;
|
|
@@ -447,8 +354,6 @@ function _readBitStringContent(node, who) {
|
|
|
447
354
|
var unusedBits = c[0];
|
|
448
355
|
if (unusedBits > 7) throw new Asn1Error("asn1/bad-bit-string", "unused-bit count " + unusedBits + " > 7");
|
|
449
356
|
if (unusedBits > 0 && c.length === 1) throw new Asn1Error("asn1/bad-bit-string", "unused bits declared over an empty body");
|
|
450
|
-
// X.690 11.2.1 -- the declared unused low bits of the final octet must be
|
|
451
|
-
// zero in DER.
|
|
452
357
|
if (unusedBits > 0 && c.length > 1) {
|
|
453
358
|
var mask = (1 << unusedBits) - 1;
|
|
454
359
|
if ((c[c.length - 1] & mask) !== 0) throw new Asn1Error("asn1/bad-bit-string", "DER requires unused bits to be zero");
|
|
@@ -461,10 +366,6 @@ function readBitString(node) {
|
|
|
461
366
|
return _readBitStringContent(node, "readBitString");
|
|
462
367
|
}
|
|
463
368
|
|
|
464
|
-
// Read a [tag] IMPLICIT BIT STRING -- a context-class PRIMITIVE node whose content
|
|
465
|
-
// is a bit-string body (identical content rules to a universal BIT STRING). The
|
|
466
|
-
// IMPLICIT tag replaces the universal one, so there is no inner universal node.
|
|
467
|
-
// Used for the PKCS#8 OneAsymmetricKey publicKey [1] (RFC 5958 sec. 2).
|
|
468
369
|
function readBitStringImplicit(node, tag) {
|
|
469
370
|
if (node.tagClass !== "context" || node.tagNumber !== tag) {
|
|
470
371
|
throw new Asn1Error("asn1/unexpected-tag", "readBitStringImplicit: expected context tag [" + tag +
|
|
@@ -479,16 +380,6 @@ function readOctetString(node) {
|
|
|
479
380
|
return node.content;
|
|
480
381
|
}
|
|
481
382
|
|
|
482
|
-
// Read a [tag] IMPLICIT OCTET STRING -- a context-class PRIMITIVE node whose
|
|
483
|
-
// content is the octet-string body (the IMPLICIT tag replaces the universal one,
|
|
484
|
-
// so there is no inner universal node). Primitive-only, like its universal
|
|
485
|
-
// counterpart, so a strict decode's constructed/streamed form is rejected.
|
|
486
|
-
// A node from a ber decode (the `ber` marker) may carry the streamed form --
|
|
487
|
-
// the context tag hides the underlying type from the decoder, so THIS reader
|
|
488
|
-
// is where a [tag] IMPLICIT OCTET STRING's segments reassemble (the shape CMS
|
|
489
|
-
// ciphertext streams as). Used for the CMS SignerIdentifier
|
|
490
|
-
// subjectKeyIdentifier [0] (RFC 5652 sec. 5.3) and EncryptedContentInfo
|
|
491
|
-
// encryptedContent [0] (sec. 6.1).
|
|
492
383
|
function readOctetStringImplicit(node, tag) {
|
|
493
384
|
if (node.tagClass !== "context" || node.tagNumber !== tag) {
|
|
494
385
|
throw new Asn1Error("asn1/unexpected-tag", "readOctetStringImplicit: expected context tag [" + tag +
|
|
@@ -516,10 +407,6 @@ function readNull(node) {
|
|
|
516
407
|
return null;
|
|
517
408
|
}
|
|
518
409
|
|
|
519
|
-
// Read a [tag] IMPLICIT BOOLEAN -- a context-class PRIMITIVE node whose single
|
|
520
|
-
// content octet obeys the DER BOOLEAN rules (0x00 FALSE / 0xFF TRUE; anything
|
|
521
|
-
// else rejected). The IMPLICIT tag replaces the universal BOOLEAN tag. Used for
|
|
522
|
-
// the RFC 5280 sec. 5.2.5 IssuingDistributionPoint scope flags.
|
|
523
410
|
function readBooleanImplicit(node, tag) {
|
|
524
411
|
if (node.tagClass !== "context" || node.tagNumber !== tag) {
|
|
525
412
|
throw new Asn1Error("asn1/unexpected-tag", "readBooleanImplicit: expected context tag [" + tag +
|
|
@@ -533,10 +420,6 @@ function readBooleanImplicit(node, tag) {
|
|
|
533
420
|
throw new Asn1Error("asn1/bad-boolean", "DER BOOLEAN must be 0x00 or 0xFF, got 0x" + v.toString(16));
|
|
534
421
|
}
|
|
535
422
|
|
|
536
|
-
// Read a [tag] IMPLICIT NULL -- a context-class PRIMITIVE node with empty content.
|
|
537
|
-
// The IMPLICIT tag replaces the universal NULL tag, so there is no inner universal
|
|
538
|
-
// node. The empty-content and primitive-form rules of a universal NULL still hold.
|
|
539
|
-
// Used for the OCSP CertStatus good [0] / unknown [2] arms (RFC 6960 sec. 4.2.1).
|
|
540
423
|
function readNullImplicit(node, tag) {
|
|
541
424
|
if (node.tagClass !== "context" || node.tagNumber !== tag) {
|
|
542
425
|
throw new Asn1Error("asn1/unexpected-tag", "readNullImplicit: expected context tag [" + tag +
|
|
@@ -576,16 +459,11 @@ function decodeOidContent(buf) {
|
|
|
576
459
|
for (var i = 0; i < buf.length; i++) {
|
|
577
460
|
var b = buf[i];
|
|
578
461
|
if (i === arcStart && b === 0x80) throw new OidError("oid/non-minimal", "non-minimal sub-identifier (leading 0x80)");
|
|
579
|
-
// Cap the continuation-byte run so a single arc can't drive unbounded
|
|
580
|
-
// BigInt growth before it terminates.
|
|
581
462
|
if (i - arcStart >= constants.LIMITS.OID_MAX_SUBIDENTIFIER_BYTES) {
|
|
582
463
|
throw new OidError("oid/subidentifier-too-large",
|
|
583
464
|
"OID sub-identifier exceeds " + constants.LIMITS.OID_MAX_SUBIDENTIFIER_BYTES + " octets");
|
|
584
465
|
}
|
|
585
466
|
if ((b & 0x80) === 0) {
|
|
586
|
-
// Terminal octet: fold the whole [arcStart..i] slice base-128 in one
|
|
587
|
-
// bounded pass (the run is capped above), no per-bit accumulation
|
|
588
|
-
// across the full OID content.
|
|
589
467
|
var value = 0n;
|
|
590
468
|
for (var k = arcStart; k <= i; k++) value = value * 128n + BigInt(buf[k] & 0x7f);
|
|
591
469
|
arcs.push(value);
|
|
@@ -607,7 +485,6 @@ function _decodeText(buf, encoding) {
|
|
|
607
485
|
return buf.toString(encoding);
|
|
608
486
|
}
|
|
609
487
|
|
|
610
|
-
// IA5String is 7-bit ASCII (T.50 / IA5) -- every content octet is 0x00..0x7F.
|
|
611
488
|
function _decodeIa5(buf) {
|
|
612
489
|
for (var i = 0; i < buf.length; i++) {
|
|
613
490
|
if (buf[i] > 0x7F) throw new Asn1Error("asn1/bad-ia5-string", "IA5String requires 7-bit ASCII");
|
|
@@ -615,8 +492,6 @@ function _decodeIa5(buf) {
|
|
|
615
492
|
return buf.toString("latin1");
|
|
616
493
|
}
|
|
617
494
|
|
|
618
|
-
// VisibleString (ISO 646 IRV / X.690) is the printable ASCII range 0x20..0x7E
|
|
619
|
-
// -- no control characters and no bytes with the high bit set.
|
|
620
495
|
function _decodeVisible(buf) {
|
|
621
496
|
for (var i = 0; i < buf.length; i++) {
|
|
622
497
|
if (buf[i] < 0x20 || buf[i] > 0x7E) throw new Asn1Error("asn1/bad-visible-string", "VisibleString must be 0x20..0x7E");
|
|
@@ -624,23 +499,12 @@ function _decodeVisible(buf) {
|
|
|
624
499
|
return buf.toString("latin1");
|
|
625
500
|
}
|
|
626
501
|
|
|
627
|
-
// PrintableString is restricted to A-Z a-z 0-9 space and ' ( ) + , - . / : = ?
|
|
628
502
|
function _decodePrintable(buf) {
|
|
629
503
|
var s = buf.toString("latin1");
|
|
630
504
|
if (!isPrintableString(s)) throw new Asn1Error("asn1/bad-printable-string", "PrintableString has characters outside the restricted set");
|
|
631
505
|
return s;
|
|
632
506
|
}
|
|
633
507
|
|
|
634
|
-
// NumericString (X.680 sec. 41, Table 9) permits ONLY the digits and SPACE. It carries the X.520 syntax of the
|
|
635
|
-
// x121Address and internationalISDNNumber directory attributes.
|
|
636
|
-
//
|
|
637
|
-
// It is read through its OWN reader, deliberately NOT through readString. Every type readString returns a
|
|
638
|
-
// plain string for enters the RFC 5280 sec. 7.1 name-comparison identity class, and
|
|
639
|
-
// guard.name.dnEqual -- the single choke point for name chaining, revocation-issuer matching and name
|
|
640
|
-
// constraints -- compares the strings it returns. Admitting NumericString there would make a NumericString
|
|
641
|
-
// attribute value compare EQUAL to a PrintableString / UTF8String one with the same characters, silently
|
|
642
|
-
// widening distinguished-name identity across the whole toolkit. A caller that genuinely wants this type asks
|
|
643
|
-
// for it by name.
|
|
644
508
|
function readNumericString(node) {
|
|
645
509
|
_expectUniversal(node, TAGS.NUMERIC_STRING, "readNumericString");
|
|
646
510
|
_expectPrimitive(node, "readNumericString");
|
|
@@ -649,8 +513,6 @@ function readNumericString(node) {
|
|
|
649
513
|
return s;
|
|
650
514
|
}
|
|
651
515
|
|
|
652
|
-
// UTF8String must be well-formed UTF-8; a lenient decoder substitutes U+FFFD
|
|
653
|
-
// for invalid sequences, silently mangling hostile input into valid text.
|
|
654
516
|
function _decodeUtf8Strict(buf) {
|
|
655
517
|
try {
|
|
656
518
|
return new TextDecoder("utf-8", { fatal: true }).decode(buf);
|
|
@@ -677,8 +539,6 @@ function readString(node) {
|
|
|
677
539
|
|
|
678
540
|
function _decodeUtf16be(buf) {
|
|
679
541
|
if (buf.length % 2 !== 0) throw new Asn1Error("asn1/bad-bmp-string", "BMPString length must be even");
|
|
680
|
-
// BMPString encodes BMP scalar values only; a UTF-16 surrogate code unit
|
|
681
|
-
// here is a lone surrogate, not valid text.
|
|
682
542
|
for (var i = 0; i < buf.length; i += 2) {
|
|
683
543
|
var u = (buf[i] << 8) | buf[i + 1];
|
|
684
544
|
if (u >= 0xD800 && u <= 0xDFFF) throw new Asn1Error("asn1/bad-bmp-string", "code point out of range");
|
|
@@ -693,8 +553,6 @@ function _decodeUtf32be(buf) {
|
|
|
693
553
|
var out = "";
|
|
694
554
|
for (var i = 0; i < buf.length; i += 4) {
|
|
695
555
|
var cp = (buf[i] * 0x1000000) + (buf[i + 1] << 16) + (buf[i + 2] << 8) + buf[i + 3];
|
|
696
|
-
// Reject non-scalar values (> U+10FFFF or a surrogate) before
|
|
697
|
-
// String.fromCodePoint would throw a bare RangeError / emit a lone surrogate.
|
|
698
556
|
if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) {
|
|
699
557
|
throw new Asn1Error("asn1/bad-universal-string", "code point out of range");
|
|
700
558
|
}
|
|
@@ -703,9 +561,6 @@ function _decodeUtf32be(buf) {
|
|
|
703
561
|
return out;
|
|
704
562
|
}
|
|
705
563
|
|
|
706
|
-
// Exactly `count` ASCII digits at `start`, as an integer, or null on any non-digit or short read.
|
|
707
|
-
// Character-scanned so no regex match protocol -- a live, replaceable `exec` -- stands between the
|
|
708
|
-
// codec and the time field it slices out.
|
|
709
564
|
function _digitsToInt(s, start, count) {
|
|
710
565
|
if (start + count > s.length) return null;
|
|
711
566
|
var v = 0;
|
|
@@ -717,12 +572,6 @@ function _digitsToInt(s, start, count) {
|
|
|
717
572
|
return v;
|
|
718
573
|
}
|
|
719
574
|
|
|
720
|
-
// The fixed DER time layout, scanned into its components, or null on any format violation:
|
|
721
|
-
// yearLen digits (2 for UTCTime YY, 4 for GeneralizedTime YYYY), then MM DD HH MM SS, then --
|
|
722
|
-
// when allowFrac -- the X.690 sec. 11.7 fractional-seconds profile (a "." decimal separator, never
|
|
723
|
-
// ",", and a non-empty fraction), then a terminating "Z" as the final character with nothing after.
|
|
724
|
-
// `ms` is the first three fraction digits right-zero-padded (matching (frac + "000").slice(0,3));
|
|
725
|
-
// `fracLastIsZero` carries the sec. 11.7.3 trailing-zero check the caller enforces.
|
|
726
575
|
function _scanTimeFields(s, yearLen, allowFrac) {
|
|
727
576
|
var pos = 0;
|
|
728
577
|
var year = _digitsToInt(s, pos, yearLen); if (year === null) return null; pos += yearLen;
|
|
@@ -733,7 +582,7 @@ function _scanTimeFields(s, yearLen, allowFrac) {
|
|
|
733
582
|
var sec = _digitsToInt(s, pos, 2); if (sec === null) return null; pos += 2;
|
|
734
583
|
var hasFrac = false, ms = 0, fracLastIsZero = false;
|
|
735
584
|
if (allowFrac) {
|
|
736
|
-
if (_charCodeAt(s, pos) !== 46) return null;
|
|
585
|
+
if (_charCodeAt(s, pos) !== 46) return null;
|
|
737
586
|
pos += 1;
|
|
738
587
|
var f = [0, 0, 0], fi = 0;
|
|
739
588
|
while (pos < s.length) {
|
|
@@ -742,21 +591,16 @@ function _scanTimeFields(s, yearLen, allowFrac) {
|
|
|
742
591
|
if (fi < 3) f[fi] = c - 48;
|
|
743
592
|
fi += 1; pos += 1;
|
|
744
593
|
}
|
|
745
|
-
if (fi === 0) return null;
|
|
594
|
+
if (fi === 0) return null;
|
|
746
595
|
hasFrac = true;
|
|
747
596
|
fracLastIsZero = _charCodeAt(s, pos - 1) === 48;
|
|
748
597
|
ms = f[0] * 100 + f[1] * 10 + f[2];
|
|
749
598
|
}
|
|
750
|
-
if (pos !== s.length - 1 || _charCodeAt(s, pos) !== 90) return null;
|
|
599
|
+
if (pos !== s.length - 1 || _charCodeAt(s, pos) !== 90) return null;
|
|
751
600
|
return { year: year, month: month, day: day, hour: hour, min: min, sec: sec,
|
|
752
601
|
hasFrac: hasFrac, ms: ms, fracLastIsZero: fracLastIsZero };
|
|
753
602
|
}
|
|
754
603
|
|
|
755
|
-
// readTime(node, opts?) -- opts.allowFractional enables the RFC 3161 / X.690 sec. 11.7
|
|
756
|
-
// fractional-seconds GeneralizedTime profile. Fractional is OFF by default because
|
|
757
|
-
// most profiles (RFC 5280 certificate/CRL Validity, sec. 4.1.2.5.2) require the integer-
|
|
758
|
-
// second YYYYMMDDHHMMSSZ form and MUST NOT include a fraction; only callers that
|
|
759
|
-
// permit sub-second precision (TSP genTime) pass allowFractional.
|
|
760
604
|
function readTime(node, opts) {
|
|
761
605
|
if (node.tagClass !== "universal") throw new Asn1Error("asn1/expected-time", "readTime: not a universal time type");
|
|
762
606
|
_expectPrimitive(node, "readTime");
|
|
@@ -766,7 +610,6 @@ function readTime(node, opts) {
|
|
|
766
610
|
t = _scanTimeFields(s, 2, false);
|
|
767
611
|
if (!t) throw new Asn1Error("asn1/bad-utctime", "UTCTime must be YYMMDDHHMMSSZ, got " + JSON.stringify(s));
|
|
768
612
|
year = t.year;
|
|
769
|
-
// RFC 5280 sec. 4.1.2.5.1 -- YY < 50 => 20YY, else 19YY.
|
|
770
613
|
year += (year < 50) ? 2000 : 1900;
|
|
771
614
|
} else if (node.tagNumber === TAGS.GENERALIZED_TIME) {
|
|
772
615
|
t = _scanTimeFields(s, 4, false);
|
|
@@ -774,13 +617,6 @@ function readTime(node, opts) {
|
|
|
774
617
|
if (!(opts && opts.allowFractional)) throw new Asn1Error("asn1/bad-generalizedtime", "GeneralizedTime must be YYYYMMDDHHMMSSZ, got " + JSON.stringify(s));
|
|
775
618
|
t = _scanTimeFields(s, 4, true);
|
|
776
619
|
if (!t) throw new Asn1Error("asn1/bad-generalizedtime", "GeneralizedTime must be YYYYMMDDHHMMSS[.fraction]Z, got " + JSON.stringify(s));
|
|
777
|
-
// X.690 sec. 11.7.3 -- the fractional part MUST NOT end in a zero (and is non-empty
|
|
778
|
-
// by the scan). ".5Z" is valid; ".50Z" and ".500Z" are non-canonical. X.690
|
|
779
|
-
// sec. 11.7 places NO cap on the fraction length (RFC 3161 sec. 2.4.2 gives the 5-digit
|
|
780
|
-
// example 19990609001326.34352Z), so a fraction of any length is accepted; the
|
|
781
|
-
// returned Date is millisecond-precision, and a caller needing the exact fraction
|
|
782
|
-
// reads it from node.content (the raw bytes, surfaced losslessly like the OID /
|
|
783
|
-
// serial paths).
|
|
784
620
|
if (t.fracLastIsZero) throw new Asn1Error("asn1/bad-generalizedtime", "GeneralizedTime fraction must not have trailing zeros, got " + JSON.stringify(s));
|
|
785
621
|
}
|
|
786
622
|
year = t.year;
|
|
@@ -792,22 +628,11 @@ function readTime(node, opts) {
|
|
|
792
628
|
var hour = t.hour;
|
|
793
629
|
var min = t.min;
|
|
794
630
|
var sec = t.sec;
|
|
795
|
-
// Build from the LITERAL year via setUTCFullYear: the Date.UTC() / new Date()
|
|
796
|
-
// constructors remap a year in 0..99 to 1900..1999, which would corrupt a
|
|
797
|
-
// GeneralizedTime year below 100 (0099 -> 1999). setUTCFullYear(year, ...)
|
|
798
|
-
// takes the year literally and uses that year's own calendar, so years
|
|
799
|
-
// below 100 and proleptic leap years are handled correctly.
|
|
800
|
-
// A fractional GeneralizedTime carries sub-second precision; surface it on the
|
|
801
|
-
// Date at millisecond resolution (the first three fraction digits, zero-padded).
|
|
802
631
|
var ms = (node.tagNumber === TAGS.GENERALIZED_TIME && t.hasFrac) ? t.ms : 0;
|
|
803
632
|
var d = new Date(0);
|
|
804
633
|
d.setUTCFullYear(year, month - 1, day);
|
|
805
634
|
d.setUTCHours(hour, min, sec, ms);
|
|
806
|
-
// Coverage residual -- scan-validated fields are finite integers and Date rolls over (never NaN); out-of-range is caught by the component round-trip check below.
|
|
807
635
|
if (isNaN(guard.time.instantOf(d))) throw new Asn1Error("asn1/bad-time", "unparseable time " + JSON.stringify(s));
|
|
808
|
-
// Date/setUTC* silently roll over out-of-range fields (Feb 30 -> Mar 2,
|
|
809
|
-
// month 13 -> next Jan, hour 25 -> +1 day). Reject unless every component
|
|
810
|
-
// round-trips exactly.
|
|
811
636
|
if (d.getUTCFullYear() !== year || d.getUTCMonth() !== month - 1 || d.getUTCDate() !== day ||
|
|
812
637
|
d.getUTCHours() !== hour || d.getUTCMinutes() !== min || d.getUTCSeconds() !== sec) {
|
|
813
638
|
throw new Asn1Error("asn1/bad-time", "time component out of range " + JSON.stringify(s));
|
|
@@ -815,7 +640,6 @@ function readTime(node, opts) {
|
|
|
815
640
|
return d;
|
|
816
641
|
}
|
|
817
642
|
|
|
818
|
-
// ---- Encoder / builders ---------------------------------------------
|
|
819
643
|
|
|
820
644
|
function encodeLength(n) {
|
|
821
645
|
if (typeof n !== "number" || !isFinite(n) || n < 0 || Math.floor(n) !== n) {
|
|
@@ -829,24 +653,12 @@ function encodeLength(n) {
|
|
|
829
653
|
return Buffer.concat([Buffer.from([0x80 | bytes.length]), Buffer.from(bytes)]);
|
|
830
654
|
}
|
|
831
655
|
|
|
832
|
-
// The DER of a universal SEQUENCE recovered from a decoded IMPLICIT [n] SEQUENCE
|
|
833
|
-
// `node`, whose context tag replaced the universal 0x30 on the wire (e.g. an RFC
|
|
834
|
-
// 4211 CertTemplate publicKey [6] SubjectPublicKeyInfo, or a POPOSigningKey
|
|
835
|
-
// poposkInput [0]): the field's value is imported / signed as a standalone SEQUENCE,
|
|
836
|
-
// not as the context-tagged wire form. A constructed node exposes its content as
|
|
837
|
-
// `children` (not `content`), so the content is those children's DER concatenated;
|
|
838
|
-
// the identifier becomes the SEQUENCE tag and the length is canonical DER.
|
|
839
656
|
function sequenceTlv(node) {
|
|
840
|
-
// Coverage residual -- a decoded node with content==null is always constructed with a children array; the || [] guards a non-decoder-built node and is unreachable via decode.
|
|
841
657
|
var content = node.content != null ? node.content : Buffer.concat((node.children || []).map(function (c) { return c.bytes; }));
|
|
842
658
|
return Buffer.concat([Buffer.from([TAGS.SEQUENCE | CONSTRUCTED_BIT]), encodeLength(content.length), content]);
|
|
843
659
|
}
|
|
844
660
|
|
|
845
661
|
function encodeIdentifier(classBits, constructed, tagNumber) {
|
|
846
|
-
// Mirror encodeLength's input guard: a negative tag corrupts the identifier
|
|
847
|
-
// octet (-1 emits 0xff..), and a fractional tag is silently bit-truncated to
|
|
848
|
-
// a DIFFERENT tag -- an entry-point builder handed a bad tag throws instead
|
|
849
|
-
// of emitting malformed or non-round-trippable DER.
|
|
850
662
|
if (typeof tagNumber !== "number" || !isFinite(tagNumber) || tagNumber < 0 || Math.floor(tagNumber) !== tagNumber) {
|
|
851
663
|
throw new Asn1Error("asn1/bad-tag", "tag number must be a non-negative integer");
|
|
852
664
|
}
|
|
@@ -855,8 +667,6 @@ function encodeIdentifier(classBits, constructed, tagNumber) {
|
|
|
855
667
|
var body = [];
|
|
856
668
|
var v = tagNumber;
|
|
857
669
|
do { body.unshift(v & 0x7f); v = Math.floor(v / 128); } while (v > 0);
|
|
858
|
-
// Symmetric with the decoder's 4-octet high-tag-number cap, so the encoder
|
|
859
|
-
// can't emit an identifier decode() refuses as hostile.
|
|
860
670
|
if (body.length > 4) throw new Asn1Error("asn1/tag-too-large", "high-tag-number too large");
|
|
861
671
|
for (var i = 0; i < body.length - 1; i++) body[i] |= 0x80;
|
|
862
672
|
return Buffer.concat([Buffer.from([lead | 0x1f]), Buffer.from(body)]);
|
|
@@ -878,9 +688,6 @@ function encodeIdentifier(classBits, constructed, tagNumber) {
|
|
|
878
688
|
* pki.asn1.encode(0x00, false, pki.asn1.TAGS.NULL, Buffer.alloc(0));
|
|
879
689
|
*/
|
|
880
690
|
function encodeTLV(classBits, constructed, tagNumber, content) {
|
|
881
|
-
// A Buffer content is re-viewed so a detached backing store fails closed with a
|
|
882
|
-
// typed asn1/not-buffer instead of a raw TypeError out of Buffer.concat; an
|
|
883
|
-
// array / undefined content stays the normal builder path.
|
|
884
691
|
var body = Buffer.isBuffer(content) ? guard.bytes.view(content, Asn1Error, "asn1/not-buffer", "encodeTLV content") : Buffer.from(content || []);
|
|
885
692
|
var id = encodeIdentifier(classBits, constructed, tagNumber);
|
|
886
693
|
return Buffer.concat([id, encodeLength(body.length), body]);
|
|
@@ -912,9 +719,6 @@ function intToDer(v) {
|
|
|
912
719
|
}
|
|
913
720
|
|
|
914
721
|
function encodeOidContent(dotted) {
|
|
915
|
-
// Canonical dotted-decimal + X.660 arc bounds live in the one string-OID guard,
|
|
916
|
-
// so build.oid cannot emit bytes that decode to a DIFFERENT OID than the string
|
|
917
|
-
// names (a leading-zero arc) or an arc that can never DER-encode.
|
|
918
722
|
guard.identifier.assertCanonicalOid(dotted, _oidErr, "oid/bad-input", "OID", "oid/bad-arc");
|
|
919
723
|
var arcs = dotted.split(".").map(function (p) { return BigInt(p); });
|
|
920
724
|
var a1 = arcs[0], a2 = arcs[1];
|
|
@@ -924,8 +728,6 @@ function encodeOidContent(dotted) {
|
|
|
924
728
|
var body = [];
|
|
925
729
|
var v = subids[s];
|
|
926
730
|
do { body.unshift(Number(v & 0x7fn)); v >>= 7n; } while (v > 0n);
|
|
927
|
-
// Keep the encoder symmetric with the decoder's per-arc cap so build.oid
|
|
928
|
-
// can't emit a sub-identifier read.oid would reject as hostile.
|
|
929
731
|
if (body.length > constants.LIMITS.OID_MAX_SUBIDENTIFIER_BYTES) {
|
|
930
732
|
throw new OidError("oid/subidentifier-too-large",
|
|
931
733
|
"OID sub-identifier exceeds " + constants.LIMITS.OID_MAX_SUBIDENTIFIER_BYTES + " octets");
|
|
@@ -936,13 +738,11 @@ function encodeOidContent(dotted) {
|
|
|
936
738
|
return Buffer.from(out);
|
|
937
739
|
}
|
|
938
740
|
|
|
939
|
-
// PrintableString (X.680): A-Z a-z 0-9 space and ' ( ) + , - . / : = ? -- checked by character scan (no
|
|
940
|
-
// regex). NumericString: digits and space only.
|
|
941
741
|
function _isPrintableChar(c) {
|
|
942
|
-
if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 57)) return true;
|
|
742
|
+
if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 57)) return true;
|
|
943
743
|
switch (c) {
|
|
944
|
-
case 32: case 39: case 40: case 41: case 43: case 44: case 45: case 46: case 47:
|
|
945
|
-
case 58: case 61: case 63:
|
|
744
|
+
case 32: case 39: case 40: case 41: case 43: case 44: case 45: case 46: case 47:
|
|
745
|
+
case 58: case 61: case 63:
|
|
946
746
|
return true;
|
|
947
747
|
default: return false;
|
|
948
748
|
}
|
|
@@ -960,8 +760,6 @@ function _isNumericString(s) {
|
|
|
960
760
|
function _fmtTwo(n) { return (n < 10 ? "0" : "") + n; }
|
|
961
761
|
|
|
962
762
|
function _generalizedTimeString(date) {
|
|
963
|
-
// GeneralizedTime is YYYYMMDDHHMMSSZ -- a year below 1000 must still emit
|
|
964
|
-
// four digits, or read.time rejects the short (12-14 char) content.
|
|
965
763
|
var y = date.getUTCFullYear();
|
|
966
764
|
if (y < 0 || y > 9999) throw new Asn1Error("asn1/bad-generalizedtime", "GeneralizedTime year " + y + " outside 0000..9999");
|
|
967
765
|
var yyyy = ("000" + y).slice(-4);
|
|
@@ -971,8 +769,6 @@ function _generalizedTimeString(date) {
|
|
|
971
769
|
}
|
|
972
770
|
|
|
973
771
|
function _utcTimeString(date) {
|
|
974
|
-
// RFC 5280 sec. 4.1.2.5.1 restricts UTCTime to 1950..2049; a year outside that
|
|
975
|
-
// wraps to the wrong century (2050 -> "50" -> decodes as 1950).
|
|
976
772
|
var y = date.getUTCFullYear();
|
|
977
773
|
if (y < 1950 || y > 2049) throw new Asn1Error("asn1/bad-utctime", "UTCTime year " + y + " outside 1950..2049; use GeneralizedTime");
|
|
978
774
|
var yy = y % 100;
|
|
@@ -1007,27 +803,15 @@ var build = {
|
|
|
1007
803
|
oid: function (dotted) { return _universal(TAGS.OBJECT_IDENTIFIER, false, encodeOidContent(dotted)); },
|
|
1008
804
|
octetString: function (buf) { return _universal(TAGS.OCTET_STRING, false, _asBuffer(buf, "build.octetString")); },
|
|
1009
805
|
bitString: function (buf, unusedBits) {
|
|
1010
|
-
// An integer in 0..7 (X.690 sec. 8.6.2.2). A fractional / non-finite count
|
|
1011
|
-
// would be silently bit-truncated into a DIFFERENT value when written into
|
|
1012
|
-
// the leading octet -- the shared integer cap rejects it like the other
|
|
1013
|
-
// numeric builder inputs (encodeLength, the tag number).
|
|
1014
806
|
var u = guard.limits.cap(unusedBits == null ? 0 : unusedBits, "unusedBits", 0, { E: _asn1Error, code: "asn1/bad-bit-string", min: 0, max: 7 });
|
|
1015
807
|
var body = _asBuffer(buf, "build.bitString");
|
|
1016
|
-
// X.690 8.6.2.3: an empty BIT STRING has no content octets, so its
|
|
1017
|
-
// unused-bit count must be zero -- there are no bits to leave unused.
|
|
1018
808
|
if (u > 0 && body.length === 0) throw new Asn1Error("asn1/bad-bit-string", "empty BIT STRING must declare zero unused bits");
|
|
1019
|
-
// Stay canonical: refuse a tail whose declared unused low bits are set.
|
|
1020
809
|
if (u > 0 && body.length > 0) {
|
|
1021
810
|
var mask = (1 << u) - 1;
|
|
1022
811
|
if ((body[body.length - 1] & mask) !== 0) throw new Asn1Error("asn1/bad-bit-string", "unused bits must be zero");
|
|
1023
812
|
}
|
|
1024
813
|
return _universal(TAGS.BIT_STRING, false, Buffer.concat([Buffer.from([u]), body]));
|
|
1025
814
|
},
|
|
1026
|
-
// A minimal DER NamedBitList BIT STRING (X.690 sec. 11.2.2): the asserted `positions` (0-based bit
|
|
1027
|
-
// indices, 0 = the MSB of the first octet) with every trailing zero bit removed and the unused-bit
|
|
1028
|
-
// count set below the last set bit. The single home the keyUsage / PKIFailureInfo / any named-bit
|
|
1029
|
-
// encoder composes, so the minimal-encoding rule lives once (the CHECK side is
|
|
1030
|
-
// schema.assertMinimalNamedBits). No bits asserted -> the empty BIT STRING.
|
|
1031
815
|
namedBitString: function (positions) {
|
|
1032
816
|
if (!_isArray(positions)) throw new Asn1Error("asn1/bad-bit-string", "namedBitString requires an array of bit positions");
|
|
1033
817
|
var hi = -1, i, p;
|
|
@@ -1043,9 +827,6 @@ var build = {
|
|
|
1043
827
|
},
|
|
1044
828
|
utf8: function (s) { return _universal(TAGS.UTF8_STRING, false, Buffer.from(String(s), "utf8")); },
|
|
1045
829
|
ia5: function (s) {
|
|
1046
|
-
// Validate the INPUT code points, not the latin1 output bytes: latin1
|
|
1047
|
-
// truncates a code point > 0xFF to its low byte (U+0141 -> 0x41), which
|
|
1048
|
-
// would silently pass a non-IA5 character through as a different one.
|
|
1049
830
|
s = String(s);
|
|
1050
831
|
for (var i = 0; i < s.length; i++) {
|
|
1051
832
|
if (_charCodeAt(s, i) > 0x7F) throw new Asn1Error("asn1/bad-ia5-string", "IA5String requires 7-bit ASCII");
|
|
@@ -1058,9 +839,6 @@ var build = {
|
|
|
1058
839
|
return _universal(TAGS.PRINTABLE_STRING, false, Buffer.from(s, "latin1"));
|
|
1059
840
|
},
|
|
1060
841
|
bmpString: function (s) {
|
|
1061
|
-
// BMPString (UCS-2, X.680) encodes each character as a 2-byte big-endian BMP scalar value -- the exact
|
|
1062
|
-
// inverse of _decodeUtf16be. A surrogate code unit (U+D800..U+DFFF), whether lone or half of a >U+FFFF
|
|
1063
|
-
// pair, cannot be a BMPString character, so it is rejected rather than silently emitted.
|
|
1064
842
|
s = String(s);
|
|
1065
843
|
var out = Buffer.alloc(s.length * 2);
|
|
1066
844
|
for (var i = 0; i < s.length; i++) {
|
|
@@ -1075,7 +853,6 @@ var build = {
|
|
|
1075
853
|
generalizedTime: function (date) { return _universal(TAGS.GENERALIZED_TIME, false, Buffer.from(_generalizedTimeString(date), "latin1")); },
|
|
1076
854
|
sequence: function (children) { return _universal(TAGS.SEQUENCE, true, Buffer.concat(_asBufferArray(children, "build.sequence"))); },
|
|
1077
855
|
set: function (children) {
|
|
1078
|
-
// DER (X.690 11.6) requires SET component encodings in ascending order.
|
|
1079
856
|
var arr = _asBufferArray(children, "build.set").slice();
|
|
1080
857
|
arr.sort(Buffer.compare);
|
|
1081
858
|
return _universal(TAGS.SET, true, Buffer.concat(arr));
|
|
@@ -1085,14 +862,9 @@ var build = {
|
|
|
1085
862
|
arr.sort(Buffer.compare);
|
|
1086
863
|
return _universal(TAGS.SET, true, Buffer.concat(arr));
|
|
1087
864
|
},
|
|
1088
|
-
// Context-tagged constructions for [n] EXPLICIT / IMPLICIT tagging.
|
|
1089
865
|
explicit: function (tagNumber, inner) { return encodeTLV(CLASS_CONTEXT, true, tagNumber, _asBuffer(inner, "build.explicit")); },
|
|
1090
866
|
contextPrimitive: function (tagNumber, content) { return encodeTLV(CLASS_CONTEXT, false, tagNumber, _asBuffer(content, "build.contextPrimitive")); },
|
|
1091
867
|
contextConstructed: function (tagNumber, content) { return encodeTLV(CLASS_CONTEXT, true, tagNumber, _asBuffer(content, "build.contextConstructed")); },
|
|
1092
|
-
// IMPLICIT [tag] retag of an already-encoded UNIVERSAL TLV: replace the source's tag with a context-class
|
|
1093
|
-
// [tag] identifier, PRESERVING the source's primitive/constructed bit and content octets (X.680 sec. 30.6).
|
|
1094
|
-
// A single home for the tag replacement so no format module re-inlines the P/C-bit handling (a decode of
|
|
1095
|
-
// the source validates it is exactly one well-formed TLV; its content is the trailing `length` octets).
|
|
1096
868
|
implicit: function (tagNumber, tlv) {
|
|
1097
869
|
var buf = _asBuffer(tlv, "build.implicit");
|
|
1098
870
|
var node = decode(buf);
|
|
@@ -1100,14 +872,8 @@ var build = {
|
|
|
1100
872
|
},
|
|
1101
873
|
raw: function (buf) { return _asBuffer(buf, "build.raw"); },
|
|
1102
874
|
};
|
|
1103
|
-
// Frozen at load so a builder method cannot be replaced afterward: every format module captures `build`
|
|
1104
|
-
// and calls its methods to produce emitted DER, so a mutable `build.contextPrimitive` (or any builder)
|
|
1105
|
-
// would let co-resident code steer the bytes a certificate carries. Freezing closes that once, toolkit-wide.
|
|
1106
875
|
intrinsic.freeze(build);
|
|
1107
876
|
|
|
1108
|
-
// A raw INTEGER/ENUMERATED content Buffer must satisfy the same DER shape
|
|
1109
|
-
// read.integer enforces: non-empty and minimally encoded (no redundant sign
|
|
1110
|
-
// octet). A BigInt/number goes through intToDer, which is minimal by build.
|
|
1111
877
|
function _intContent(v, who) {
|
|
1112
878
|
if (Buffer.isBuffer(v)) {
|
|
1113
879
|
if (v.length === 0) throw new Asn1Error("asn1/bad-integer", who + ": INTEGER content is empty");
|
|
@@ -1117,8 +883,6 @@ function _intContent(v, who) {
|
|
|
1117
883
|
}
|
|
1118
884
|
}
|
|
1119
885
|
var content = Buffer.isBuffer(v) ? v : intToDer(v);
|
|
1120
|
-
// Symmetric with read.integer's per-value ceiling (magnitude + DER sign
|
|
1121
|
-
// octet), so a builder can't emit an INTEGER the decoder would refuse.
|
|
1122
886
|
if (content.length > constants.LIMITS.DER_MAX_INTEGER_BYTES + 1) {
|
|
1123
887
|
throw new Asn1Error("asn1/integer-too-large", who + ": INTEGER content " + content.length + " bytes exceeds cap " + (constants.LIMITS.DER_MAX_INTEGER_BYTES + 1));
|
|
1124
888
|
}
|
|
@@ -1134,8 +898,6 @@ module.exports = {
|
|
|
1134
898
|
TAGS: TAGS,
|
|
1135
899
|
decode: decode,
|
|
1136
900
|
isPrintableString: isPrintableString,
|
|
1137
|
-
// `encode` is the documented public name for the low-level TLV encoder;
|
|
1138
|
-
// `encodeTLV` remains as the descriptive alias.
|
|
1139
901
|
encode: encodeTLV,
|
|
1140
902
|
encodeTLV: encodeTLV,
|
|
1141
903
|
encodeLength: encodeLength,
|