lemon-tls 0.6.0 → 0.9.0

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/src/utils.js CHANGED
@@ -54,7 +54,13 @@ function arraysEqual(a, b) {
54
54
  return false;
55
55
  }
56
56
  }else{
57
- if(typeof a[i]=='string' && typeof b[i]=='string'){
57
+ // Either element may be null/undefined here: the branch above requires
58
+ // BOTH to be non-null typed arrays, so a null slips through to this
59
+ // side and the `.constructor` reads below threw a TypeError. Settle
60
+ // those cases by identity before anything is dereferenced.
61
+ if(a[i]==null || b[i]==null){
62
+ if(a[i]!==b[i]) return false;
63
+ }else if(typeof a[i]=='string' && typeof b[i]=='string'){
58
64
  if (a[i] !== b[i]){
59
65
  return false;
60
66
  }
@@ -119,10 +125,77 @@ function timingSafeEqualU8(a, b) {
119
125
  }
120
126
 
121
127
 
128
+ /**
129
+ * Parse an X509Certificate DN string into the object shape Node's certificate
130
+ * objects use.
131
+ *
132
+ * crypto.X509Certificate gives `subject`/`issuer` as newline-separated
133
+ * "KEY=value" text, but Node documents them on a certificate object as objects
134
+ * keyed by RDN type: { C, ST, L, O, OU, CN }. Passing the raw string through
135
+ * meant tls.checkServerIdentity() — which reads cert.subject.CN — could never
136
+ * find a Common Name, so any certificate without a SAN was rejected outright
137
+ * even when its CN matched. The exported identity check did not work against
138
+ * this library's own getPeerCertificate() output, which is exactly the pairing
139
+ * Node documents.
140
+ *
141
+ * A type may legitimately repeat (OU is the common case); Node represents a
142
+ * repeat as an array and a single occurrence as a plain string.
143
+ *
144
+ * Idempotent: an already-parsed object is returned unchanged, so callers can
145
+ * pass either form.
146
+ */
147
+ function parseDN(dn) {
148
+ if (dn == null) return {};
149
+ if (typeof dn === 'object') return dn;
150
+ let out = {};
151
+ let lines = String(dn).split('\n');
152
+ for (let i = 0; i < lines.length; i++) {
153
+ let line = lines[i];
154
+ if (!line) continue;
155
+ let eq = line.indexOf('=');
156
+ if (eq < 1) continue;
157
+ let k = line.slice(0, eq).trim();
158
+ let v = line.slice(eq + 1);
159
+ if (!Object.prototype.hasOwnProperty.call(out, k)) out[k] = v;
160
+ else if (Array.isArray(out[k])) out[k].push(v);
161
+ else out[k] = [out[k], v];
162
+ }
163
+ return out;
164
+ }
165
+
166
+ /**
167
+ * A certificate (DER) as node:tls's getPeerCertificate() describes it:
168
+ * subject and issuer as RDN objects, the SAN string, the validity dates,
169
+ * fingerprints, serial, and the raw DER. One place for TLSSocket, the
170
+ * session's identity check and anything above (QUIC) — they built it
171
+ * apiece. Unparseable: just { raw }.
172
+ */
173
+ function certificateObject(der) {
174
+ if (!der) return null;
175
+ try {
176
+ let x = new crypto.X509Certificate(der);
177
+ return {
178
+ subject: parseDN(x.subject),
179
+ issuer: parseDN(x.issuer),
180
+ subjectaltname: x.subjectAltName,
181
+ valid_from: x.validFrom,
182
+ valid_to: x.validTo,
183
+ fingerprint: x.fingerprint,
184
+ fingerprint256: x.fingerprint256,
185
+ serialNumber: x.serialNumber,
186
+ raw: der
187
+ };
188
+ } catch (e) {
189
+ return { raw: der };
190
+ }
191
+ }
192
+
122
193
  export {
194
+ certificateObject,
123
195
  concatUint8Arrays,
124
196
  arraybufferEqual,
125
197
  arraysEqual,
126
198
  uint8Equal,
127
- timingSafeEqualU8
199
+ timingSafeEqualU8,
200
+ parseDN
128
201
  };
package/src/wire.js CHANGED
@@ -17,6 +17,36 @@ const DTLS_VERSION = {
17
17
  DTLS1_3: 0xFEFC
18
18
  };
19
19
 
20
+ /**
21
+ * Does `v` denote the TLS 1.3 *message* format?
22
+ *
23
+ * SINGLE SOURCE OF TRUTH for every "is this 1.3?" branch in this file.
24
+ * The reason it has to exist: DTLS 1.3 is TLS 1.3 at the message layer, but
25
+ * its version constant is 0xFEFC, not 0x0304. Every `v === TLS_VERSION.TLS1_3`
26
+ * comparison is therefore silently FALSE for DTLS 1.3, and silently selects
27
+ * the legacy TLS 1.2 encoding of whatever message is being built.
28
+ *
29
+ * That is not hypothetical: build_certificate below did exactly this, so a
30
+ * DTLS 1.3 server emitted a TLS 1.2 Certificate (no certificate_request_context,
31
+ * no per-entry extensions vector). The peer parsed byte 0 as the context length
32
+ * — which happened to be 0x00 and so passed — then read the next three bytes as
33
+ * certificate_list length and got a value far past the end of the message.
34
+ * BoringSSL answers that with decode_error(50).
35
+ *
36
+ * It only ever bit the server role, because the client-side Certificate call
37
+ * sites hardcode TLS_VERSION.TLS1_3 instead of passing the negotiated version.
38
+ * Two copies of this library also agree with each other, because
39
+ * parse_certificate sniffs both encodings — so only a conforming peer objects.
40
+ */
41
+ function is_tls13_wire(v) {
42
+ return v === TLS_VERSION.TLS1_3 || v === DTLS_VERSION.DTLS1_3;
43
+ }
44
+
45
+ /** Companion to is_tls13_wire for the 1.2 message format. */
46
+ function is_tls12_wire(v) {
47
+ return v === TLS_VERSION.TLS1_2 || v === DTLS_VERSION.DTLS1_2;
48
+ }
49
+
20
50
  const TLS_CONTENT_TYPE = {
21
51
  CHANGE_CIPHER_SPEC: 20,
22
52
  ALERT: 21,
@@ -24,6 +54,14 @@ const TLS_CONTENT_TYPE = {
24
54
  APPLICATION_DATA: 23
25
55
  };
26
56
 
57
+ // The largest one handshake message may be, as its header declares it, or a
58
+ // CompressedCertificate decompressed to. OpenSSL allows a ClientHello of
59
+ // 128 KB and certificate chains up to 100 KB (max_cert_list); QUIC's CRYPTO
60
+ // buffer in QUICO is 64 KB. The 24-bit length would allow 16 MB — memory a
61
+ // peer could make us hold, or inflate a few hundred bytes into, before it
62
+ // has proven anything. A maxHandshakeSize below it lowers it.
63
+ const HANDSHAKE_MESSAGE_MAX = 128 * 1024;
64
+
27
65
  const TLS_ALERT_LEVEL = {
28
66
  WARNING: 1,
29
67
  FATAL: 2
@@ -45,12 +83,14 @@ const TLS_ALERT = {
45
83
  PROTOCOL_VERSION: 70,
46
84
  INSUFFICIENT_SECURITY: 71,
47
85
  INTERNAL_ERROR: 80,
86
+ INAPPROPRIATE_FALLBACK: 86, // RFC 7507 (TLS_FALLBACK_SCSV)
48
87
  USER_CANCELED: 90,
49
88
  MISSING_EXTENSION: 109,
50
89
  UNSUPPORTED_EXTENSION: 110,
51
90
  UNRECOGNIZED_NAME: 112,
52
91
  CERTIFICATE_REQUIRED: 116,
53
- NO_APPLICATION_PROTOCOL: 120
92
+ NO_APPLICATION_PROTOCOL: 120,
93
+ ECH_REQUIRED: 121
54
94
  };
55
95
 
56
96
  const TLS_MESSAGE_TYPE = {
@@ -67,6 +107,8 @@ const TLS_MESSAGE_TYPE = {
67
107
  CLIENT_KEY_EXCHANGE: 16,
68
108
  FINISHED: 20,
69
109
  KEY_UPDATE: 24,
110
+ CERTIFICATE_STATUS: 22, // RFC 6066 §8 (TLS 1.2: the stapled OCSP response)
111
+ COMPRESSED_CERTIFICATE: 25, // RFC 8879
70
112
  MESSAGE_HASH: 254 // HRR flow marker
71
113
  };
72
114
 
@@ -83,6 +125,7 @@ const TLS_EXT = {
83
125
  MAX_FRAGMENT_LENGTH: 1,
84
126
  STATUS_REQUEST: 5,
85
127
  SUPPORTED_GROUPS: 10,
128
+ EC_POINT_FORMATS: 11,
86
129
  SIGNATURE_ALGORITHMS: 13,
87
130
  USE_SRTP: 14,
88
131
  HEARTBEAT: 15,
@@ -92,6 +135,7 @@ const TLS_EXT = {
92
135
  SERVER_CERT_TYPE: 20,
93
136
  PADDING: 21,
94
137
  EXTENDED_MASTER_SECRET: 23,
138
+ COMPRESS_CERTIFICATE: 27, // RFC 8879
95
139
  SESSION_TICKET: 35,
96
140
  PRE_SHARED_KEY: 41,
97
141
  EARLY_DATA: 42,
@@ -146,6 +190,25 @@ function w_u48(buf, off, v) {
146
190
  return off;
147
191
  }
148
192
 
193
+ /**
194
+ * 64-bit big-endian write. Split into two 32-bit halves rather than using
195
+ * BigInt: DTLS epochs and sequence numbers are both far below 2^53, so Number
196
+ * arithmetic is exact here and roughly an order of magnitude faster.
197
+ */
198
+ function w_u64(buf, off, v) {
199
+ let hi = Math.floor(v / 0x100000000);
200
+ let lo = v >>> 0;
201
+ buf[off++] = (hi >>> 24) & 0xFF;
202
+ buf[off++] = (hi >>> 16) & 0xFF;
203
+ buf[off++] = (hi >>> 8) & 0xFF;
204
+ buf[off++] = (hi ) & 0xFF;
205
+ buf[off++] = (lo >>> 24) & 0xFF;
206
+ buf[off++] = (lo >>> 16) & 0xFF;
207
+ buf[off++] = (lo >>> 8) & 0xFF;
208
+ buf[off++] = (lo ) & 0xFF;
209
+ return off;
210
+ }
211
+
149
212
  function w_bytes(buf, off, b) {
150
213
  buf.set(b, off);
151
214
  return off + b.length;
@@ -255,6 +318,7 @@ let exts = {};
255
318
  exts.SERVER_NAME = { encode: null, decode: null };
256
319
  exts.SUPPORTED_VERSIONS = { encode: null, decode: null };
257
320
  exts.SUPPORTED_GROUPS = { encode: null, decode: null };
321
+ exts.EC_POINT_FORMATS = { encode: null, decode: null };
258
322
  exts.SIGNATURE_ALGORITHMS = { encode: null, decode: null };
259
323
  exts.PSK_KEY_EXCHANGE_MODES = { encode: null, decode: null };
260
324
  exts.KEY_SHARE = { encode: null, decode: null };
@@ -264,6 +328,47 @@ exts.RENEGOTIATION_INFO = { encode: null, decode: null };
264
328
  exts.SESSION_TICKET = { encode: null, decode: null };
265
329
  exts.EXTENDED_MASTER_SECRET = { encode: null, decode: null };
266
330
  exts.CERTIFICATE_AUTHORITIES = { encode: null, decode: null };
331
+ exts.COMPRESS_CERTIFICATE = { encode: null, decode: null };
332
+ exts.STATUS_REQUEST = { encode: null, decode: null };
333
+
334
+ /* ---------------------- STATUS_REQUEST (5) ---------------------- */
335
+ // RFC 6066 §8. Three places, three bodies:
336
+ // ClientHello: CertificateStatusRequest — status_type ocsp(1),
337
+ // responder_id_list<0..2^16-1>, request_extensions<0..2^16-1>
338
+ // ServerHello (1.2): empty — "a CertificateStatus message follows"
339
+ // CertificateEntry (1.3, RFC 8446 §4.4.2.1): CertificateStatus itself
340
+ // Encoded from { ocsp: true } (the request with empty lists, as every
341
+ // browser sends it) or a raw body; decoded to { status_type } or {} when
342
+ // empty. Unknown status types are not an error: the server just does not
343
+ // answer them.
344
+ exts.STATUS_REQUEST.encode = function (value) {
345
+ if (value instanceof Uint8Array) return value;
346
+ if (value && value.ocsp) return new Uint8Array([1, 0, 0, 0, 0]);
347
+ return new Uint8Array(0);
348
+ };
349
+ exts.STATUS_REQUEST.decode = function (data) {
350
+ if (data.length === 0) return {};
351
+ return { status_type: data[0] };
352
+ };
353
+
354
+ /* ---------------------- COMPRESS_CERTIFICATE (27) ---------------------- */
355
+ // RFC 8879 §3: CertificateCompressionAlgorithm algorithms<2..2^8-2> — a
356
+ // one-byte length, then 16-bit codes.
357
+ exts.COMPRESS_CERTIFICATE.encode = function (value) {
358
+ let list = Array.isArray(value) ? value : [];
359
+ let out = new Uint8Array(1 + list.length * 2);
360
+ out[0] = list.length * 2;
361
+ for (let i = 0; i < list.length; i++) w_u16(out, 1 + i * 2, list[i]);
362
+ return out;
363
+ };
364
+ exts.COMPRESS_CERTIFICATE.decode = function (data) {
365
+ if (data.length < 1) throw parseError('compress_certificate: empty');
366
+ let n = data[0];
367
+ if (n < 2 || n % 2 !== 0 || 1 + n !== data.length) throw parseError('compress_certificate: bad length');
368
+ let out = [];
369
+ for (let i = 1; i < 1 + n; i += 2) out.push((data[i] << 8) | data[i + 1]);
370
+ return out;
371
+ };
267
372
 
268
373
  /* ---------------------- CERTIFICATE_AUTHORITIES (47) ---------------------- */
269
374
  // RFC 8446 §4.2.4: DistinguishedName authorities<3..2^16-1> — the list is
@@ -478,6 +583,24 @@ exts.SUPPORTED_GROUPS.decode = function (data) {
478
583
  return out; // array of named groups
479
584
  };
480
585
 
586
+ /* ---------------------------- EC_POINT_FORMATS (11) ---------------------------- */
587
+ // RFC 8422 §5.1.2: ECPointFormat ec_point_format_list<1..2^8-1>, one byte per
588
+ // format. 0 is uncompressed — the only format TLS 1.2 still uses, and the one
589
+ // every list must contain.
590
+ exts.EC_POINT_FORMATS.encode = function (value) {
591
+ let formats = Array.isArray(value) && value.length > 0 ? value : [0];
592
+ return veclen(1, new Uint8Array(formats));
593
+ };
594
+
595
+ exts.EC_POINT_FORMATS.decode = function (data) {
596
+ let off = 0;
597
+ let list;
598
+ [list, off] = readVec(data, off, 1);
599
+ if (off !== data.length) throw parseError('trailing data after ec_point_formats');
600
+ if (list.length === 0) throw parseError('empty ec_point_formats list');
601
+ return Array.from(list); // array of point formats
602
+ };
603
+
481
604
  /* -------------------------- SIGNATURE_ALGORITHMS (13) -------------------------- */
482
605
  exts.SIGNATURE_ALGORITHMS.encode = function (value) {
483
606
  // A codec encodes what it is handed; the set of signature schemes is policy
@@ -674,7 +797,16 @@ exts.KEY_SHARE.encode = function (value) {
674
797
  return veclen(2, concatUint8Arrays(parts));
675
798
  };
676
799
 
677
- exts.KEY_SHARE.decode = function (data) {
800
+ // `kind` — the message the extension is in ('client_hello', 'server_hello';
801
+ // parse_hello passes it) — decides the form; the three forms cannot be told
802
+ // apart by length. An empty client_shares, which RFC 8446 §4.2.8 allows ("in
803
+ // order to request group selection from the server, at the cost of an
804
+ // additional round trip"), is two bytes [00 00]: read as a HelloRetryRequest's
805
+ // NamedGroup it became a share for group 0x0000, refused illegal_parameter
806
+ // (not in supported_groups) — where the client asked for a HelloRetryRequest.
807
+ // Scanners enumerate a server's groups exactly so; every probe failed.
808
+ exts.KEY_SHARE.decode = function (data, kind) {
809
+ if (kind === 'client_hello') return decode_client_shares(data);
678
810
  // HelloRetryRequest form: just NamedGroup (2 bytes, no key_exchange)
679
811
  if (data.length === 2) {
680
812
  let g, off = 0;
@@ -698,12 +830,17 @@ exts.KEY_SHARE.decode = function (data) {
698
830
  }
699
831
  }
700
832
 
701
- // ClientHello form: vector<2> of KeyShareEntry
833
+ return decode_client_shares(data);
834
+ };
835
+
836
+ // ClientHello form: vector<2> of KeyShareEntry, exactly filling the extension.
837
+ function decode_client_shares(data) {
702
838
  let off2 = 0;
703
839
  let listBytes;
704
840
  [listBytes, off2] = r_u16(data, off2);
705
841
 
706
842
  let end = off2 + listBytes;
843
+ if (end !== data.length) throw parseError('key_share: client_shares length does not match the extension');
707
844
  let out = [];
708
845
 
709
846
  while (off2 < end) {
@@ -718,9 +855,10 @@ exts.KEY_SHARE.decode = function (data) {
718
855
 
719
856
  out.push({ group: g2, key_exchange: ke2 });
720
857
  }
858
+ if (off2 !== end) throw parseError('key_share: an entry overruns client_shares');
721
859
 
722
860
  return out; // array of entries
723
- };
861
+ }
724
862
 
725
863
  /* ------------------------------------ ALPN (16) ----------------------------------- */
726
864
  exts.ALPN.encode = function (value) {
@@ -900,7 +1038,7 @@ function build_extensions(list) {
900
1038
  * CertificateRequest) → use parse_extension_list().
901
1039
  * Both share this core so the duplicate/overflow rules apply identically.
902
1040
  */
903
- function parse_extension_list(buf) {
1041
+ function parse_extension_list(buf, kind) {
904
1042
  let off = 0;
905
1043
  let out = [];
906
1044
  let seen = {}; // RFC 8446 §4.2: "There MUST NOT be more than one extension of the same type"
@@ -937,7 +1075,7 @@ function parse_extension_list(buf) {
937
1075
 
938
1076
  let name = ext_name_by_code(t);
939
1077
  let dec = exts[name] && exts[name].decode;
940
- let val = dec ? dec(d) : null;
1078
+ let val = dec ? dec(d, kind) : null;
941
1079
 
942
1080
  out.push({ type: t, name: name, data: d, value: val });
943
1081
  }
@@ -949,7 +1087,7 @@ function parse_extension_list(buf) {
949
1087
  * Parse a length-prefixed extensions vector that ENDS the enclosing structure.
950
1088
  * `buf` must START with the 2-byte vector length and contain nothing after it.
951
1089
  */
952
- function parse_extensions(buf) {
1090
+ function parse_extensions(buf, kind) {
953
1091
  let off = 0;
954
1092
  let n;
955
1093
  [n, off] = r_u16(buf, off);
@@ -961,7 +1099,7 @@ function parse_extensions(buf) {
961
1099
  if (end > buf.length) throw parseError('extensions vector length exceeds body');
962
1100
  if (end < buf.length) throw parseError('trailing data after extensions vector');
963
1101
 
964
- return parse_extension_list(buf.subarray(off, end));
1102
+ return parse_extension_list(buf.subarray(off, end), kind);
965
1103
  }
966
1104
 
967
1105
 
@@ -1109,7 +1247,7 @@ function parse_hello(params) {
1109
1247
  let extensions = [];
1110
1248
  if (body.length > off) {
1111
1249
  let extRaw = body.subarray(off);
1112
- extensions = parse_extensions(extRaw);
1250
+ extensions = parse_extensions(extRaw, type);
1113
1251
  }
1114
1252
 
1115
1253
  // --- version תמיד ערך יחיד ---
@@ -1151,6 +1289,14 @@ function build_certificate(params) {
1151
1289
  // TLS 1.3 → מחזיר: request_context (vec<1>) || certificate_list (vec<3> של [ cert(vec<3>) || extensions(vec<2>) ]*)
1152
1290
  // TLS 1.2 → מחזיר: certificate_list (vec<3> של cert(vec<3>)*), מתעלם מ-extensions/request_context
1153
1291
 
1292
+ // NOTE the asymmetry with build_certificate_request, which defaults to
1293
+ // TLS1_3. Both defaults are kept as they were, because callers outside this
1294
+ // file may rely on them; every call site inside the library passes `version`
1295
+ // explicitly, so the default is never actually exercised here. What matters
1296
+ // is that the branch below asks is_tls13_wire(v), not v === TLS1_3 — the
1297
+ // mismatched defaults are what made the two messages in the SAME server
1298
+ // flight come out in two different encodings, which is why this stayed
1299
+ // hidden for so long.
1154
1300
  let v = params.version || TLS_VERSION.TLS1_2;
1155
1301
 
1156
1302
  // Normalize to entries[] always
@@ -1161,8 +1307,14 @@ function build_certificate(params) {
1161
1307
  }
1162
1308
  if (!entries) entries = [];
1163
1309
 
1164
- if (v === TLS_VERSION.TLS1_3) {
1165
- let ctx = toU8(params.request_context || new Uint8Array(0));
1310
+ if (is_tls13_wire(v)) {
1311
+ // Accept either spelling of the context field. build_tls_message callers
1312
+ // pass `certificate_request_context` (the RFC 8446 field name); this
1313
+ // builder originally read only `request_context`, so the value was
1314
+ // dropped without a word. Harmless during the initial handshake, where
1315
+ // the context is always empty — but post-handshake client authentication
1316
+ // is identified BY that context, so it would have failed silently.
1317
+ let ctx = toU8(params.request_context || params.certificate_request_context || new Uint8Array(0));
1166
1318
 
1167
1319
  let entryParts = [];
1168
1320
  for (let i = 0; i < entries.length; i++) {
@@ -1199,6 +1351,51 @@ function build_certificate(params) {
1199
1351
  }
1200
1352
  }
1201
1353
 
1354
+ /* ------------------------ CompressedCertificate ------------------------ */
1355
+ // RFC 8879 §4: algorithm (u16), uncompressed_length (u24),
1356
+ // compressed_certificate_message<1..2^24-1> (u24-prefixed).
1357
+ /**
1358
+ * CertificateStatus (RFC 6066 §8): status_type ocsp(1), then the DER OCSP
1359
+ * response as an opaque<1..2^24-1>. The body of the TLS 1.2 handshake
1360
+ * message, and of the status_request extension in a TLS 1.3 CertificateEntry.
1361
+ */
1362
+ function build_certificate_status(response) {
1363
+ let r = toU8(response);
1364
+ let out = new Uint8Array(4 + r.length);
1365
+ out[0] = 1;
1366
+ out[1] = (r.length >>> 16) & 0xff; out[2] = (r.length >>> 8) & 0xff; out[3] = r.length & 0xff;
1367
+ out.set(r, 4);
1368
+ return out;
1369
+ }
1370
+
1371
+ /** CertificateStatus → { status_type, response }; decode_error on a bad length. */
1372
+ function parse_certificate_status(body) {
1373
+ if (body.length < 4) throw parseError('CertificateStatus: too short');
1374
+ let len = (body[1] << 16) | (body[2] << 8) | body[3];
1375
+ if (len === 0 || 4 + len !== body.length) throw parseError('CertificateStatus: bad length');
1376
+ return { status_type: body[0], response: body.slice(4) };
1377
+ }
1378
+
1379
+ function build_compressed_certificate(params) {
1380
+ let data = params.compressed;
1381
+ let out = new Uint8Array(2 + 3 + 3 + data.length);
1382
+ let o = 0;
1383
+ o = w_u16(out, o, params.algorithm);
1384
+ out[o++] = (params.uncompressed_length >>> 16) & 0xff; out[o++] = (params.uncompressed_length >>> 8) & 0xff; out[o++] = params.uncompressed_length & 0xff;
1385
+ out[o++] = (data.length >>> 16) & 0xff; out[o++] = (data.length >>> 8) & 0xff; out[o++] = data.length & 0xff;
1386
+ out.set(data, o);
1387
+ return out;
1388
+ }
1389
+
1390
+ function parse_compressed_certificate(body) {
1391
+ if (body.length < 8) throw parseError('CompressedCertificate: truncated');
1392
+ let algorithm = (body[0] << 8) | body[1];
1393
+ let uncompressed_length = (body[2] << 16) | (body[3] << 8) | body[4];
1394
+ let n = (body[5] << 16) | (body[6] << 8) | body[7];
1395
+ if (n < 1 || 8 + n !== body.length) throw parseError('CompressedCertificate: bad length');
1396
+ return { algorithm: algorithm, uncompressed_length: uncompressed_length, compressed: body.subarray(8) };
1397
+ }
1398
+
1202
1399
  function parse_certificate(body) {
1203
1400
  // always returns:
1204
1401
  // {
@@ -1400,8 +1597,10 @@ function parse_new_session_ticket(body) {
1400
1597
  function build_certificate_request(params) {
1401
1598
  let v = params && params.version || TLS_VERSION.TLS1_3;
1402
1599
 
1403
- if (v === TLS_VERSION.TLS1_3) {
1404
- let ctx = toU8((params && params.request_context) || new Uint8Array(0));
1600
+ if (is_tls13_wire(v)) {
1601
+ // Same dual-spelling accommodation as build_certificate above.
1602
+ let ctx = toU8((params && (params.request_context || params.certificate_request_context)) ||
1603
+ new Uint8Array(0));
1405
1604
 
1406
1605
  // RFC 8446 §4.3.2: "The 'signature_algorithms' extension MUST be specified"
1407
1606
  // in a TLS 1.3 CertificateRequest.
@@ -1867,8 +2066,11 @@ function parse_hello_verify_request(body) {
1867
2066
 
1868
2067
  /* ================================ Exports ================================= */
1869
2068
  export {
2069
+ HANDSHAKE_MESSAGE_MAX,
1870
2070
  TLS_VERSION,
1871
2071
  DTLS_VERSION,
2072
+ is_tls13_wire,
2073
+ is_tls12_wire,
1872
2074
  TLS_CONTENT_TYPE,
1873
2075
  TLS_ALERT_LEVEL,
1874
2076
  TLS_ALERT,
@@ -1879,6 +2081,7 @@ export {
1879
2081
  w_u16,
1880
2082
  w_u24,
1881
2083
  w_u48,
2084
+ w_u64,
1882
2085
  w_bytes,
1883
2086
  r_u8,
1884
2087
  r_u16,
@@ -1899,6 +2102,10 @@ export {
1899
2102
 
1900
2103
  build_certificate,
1901
2104
  parse_certificate,
2105
+ build_compressed_certificate,
2106
+ parse_compressed_certificate,
2107
+ build_certificate_status,
2108
+ parse_certificate_status,
1902
2109
 
1903
2110
  build_certificate_verify,
1904
2111
  parse_certificate_verify,