lemon-tls 0.2.1 → 0.3.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/wire.js CHANGED
@@ -11,6 +11,12 @@ const TLS_VERSION = {
11
11
  TLS1_3: 0x0304
12
12
  };
13
13
 
14
+ const DTLS_VERSION = {
15
+ DTLS1_0: 0xFEFF,
16
+ DTLS1_2: 0xFEFD,
17
+ DTLS1_3: 0xFEFC
18
+ };
19
+
14
20
  const TLS_CONTENT_TYPE = {
15
21
  CHANGE_CIPHER_SPEC: 20,
16
22
  ALERT: 21,
@@ -84,6 +90,8 @@ const TLS_EXT = {
84
90
  CLIENT_CERT_TYPE: 19,
85
91
  SERVER_CERT_TYPE: 20,
86
92
  PADDING: 21,
93
+ EXTENDED_MASTER_SECRET: 23,
94
+ SESSION_TICKET: 35,
87
95
  PRE_SHARED_KEY: 41,
88
96
  EARLY_DATA: 42,
89
97
  SUPPORTED_VERSIONS: 43,
@@ -125,6 +133,18 @@ function w_u24(buf, off, v) {
125
133
  return off;
126
134
  }
127
135
 
136
+ function w_u48(buf, off, v) {
137
+ let hi = Math.floor(v / 0x100000000);
138
+ let lo = v >>> 0;
139
+ buf[off++] = (hi >>> 8) & 0xFF;
140
+ buf[off++] = hi & 0xFF;
141
+ buf[off++] = (lo >>> 24) & 0xFF;
142
+ buf[off++] = (lo >>> 16) & 0xFF;
143
+ buf[off++] = (lo >>> 8) & 0xFF;
144
+ buf[off++] = lo & 0xFF;
145
+ return off;
146
+ }
147
+
128
148
  function w_bytes(buf, off, b) {
129
149
  buf.set(b, off);
130
150
  return off + b.length;
@@ -222,6 +242,8 @@ exts.KEY_SHARE = { encode: null, decode: null };
222
242
  exts.ALPN = { encode: null, decode: null };
223
243
  exts.COOKIE = { encode: null, decode: null };
224
244
  exts.RENEGOTIATION_INFO = { encode: null, decode: null };
245
+ exts.SESSION_TICKET = { encode: null, decode: null };
246
+ exts.EXTENDED_MASTER_SECRET = { encode: null, decode: null };
225
247
 
226
248
  /* ------------------------------ SERVER_NAME (0) ------------------------------ */
227
249
  exts.SERVER_NAME.encode = function (value) {
@@ -648,6 +670,27 @@ exts.COOKIE.decode = function (data) {
648
670
  return v; // Uint8Array — opaque cookie
649
671
  };
650
672
 
673
+ /* ---------------------------- SESSION_TICKET (35) ---------------------------- */
674
+ // RFC 5077. Both directions carry opaque bytes (not a length-prefixed vector).
675
+ // ClientHello: empty = "I support tickets" / non-empty = "resume using this ticket"
676
+ // ServerHello: empty = "I will send a NewSessionTicket" (never non-empty in ServerHello)
677
+ exts.SESSION_TICKET.encode = function (value) {
678
+ return toU8(value || new Uint8Array(0));
679
+ };
680
+
681
+ exts.SESSION_TICKET.decode = function (data) {
682
+ return data; // opaque bytes — caller interprets
683
+ };
684
+
685
+ /* -------------------------- EXTENDED_MASTER_SECRET (23) -------------------------- */
686
+ // RFC 7627. Both directions: empty body. Signals support for Extended Master Secret.
687
+ exts.EXTENDED_MASTER_SECRET.encode = function (value) {
688
+ return new Uint8Array(0);
689
+ };
690
+
691
+ exts.EXTENDED_MASTER_SECRET.decode = function (data) {
692
+ return true; // presence is the signal
693
+ };
651
694
  /* ============================= Extensions helpers ============================= */
652
695
  function ext_name_by_code(code) {
653
696
  // best-effort pretty name
@@ -739,10 +782,13 @@ function parse_extensions(buf) {
739
782
 
740
783
 
741
784
  /* ================================ Hello I/O ================================ */
742
- function build_hello(kind, params) {
785
+ function build_hello(params) {
743
786
  params = params || {};
787
+ let kind = params.kind;
744
788
 
745
- let legacy_version = TLS_VERSION.TLS1_2; // even for TLS1.3 legacy fields
789
+ let isDtls = (params.cookie !== undefined) ||
790
+ (params.version !== undefined && (params.version & 0xFF00) === 0xFE00);
791
+ let legacy_version = isDtls ? DTLS_VERSION.DTLS1_2 : TLS_VERSION.TLS1_2;
746
792
 
747
793
  let sid = toU8(params.session_id || "");
748
794
  if (sid.length > 32) sid = sid.subarray(0, 32);
@@ -767,8 +813,19 @@ function build_hello(kind, params) {
767
813
  oc = w_u8(compBlock, oc, comp[j]);
768
814
  }
769
815
 
816
+ // DTLS cookie field (between session_id and cipher_suites)
817
+ let cookieBuf = null;
818
+ if (params.cookie !== undefined) {
819
+ let cookie = toU8(params.cookie);
820
+ cookieBuf = new Uint8Array(1 + cookie.length);
821
+ cookieBuf[0] = cookie.length;
822
+ if (cookie.length > 0) cookieBuf.set(cookie, 1);
823
+ }
824
+
770
825
  const out = new Uint8Array(
771
- 2 + 32 + 1 + sid.length + csBlock.length + compBlock.length + extsBuf.length
826
+ 2 + 32 + 1 + sid.length +
827
+ (cookieBuf ? cookieBuf.length : 0) +
828
+ csBlock.length + compBlock.length + extsBuf.length
772
829
  );
773
830
 
774
831
  let off = 0;
@@ -776,6 +833,7 @@ function build_hello(kind, params) {
776
833
  off = w_bytes(out, off, params.random);
777
834
  off = w_u8(out, off, sid.length);
778
835
  off = w_bytes(out, off, sid);
836
+ if (cookieBuf) off = w_bytes(out, off, cookieBuf);
779
837
  off = w_bytes(out, off, csBlock);
780
838
  off = w_bytes(out, off, compBlock);
781
839
  off = w_bytes(out, off, extsBuf);
@@ -803,11 +861,13 @@ function build_hello(kind, params) {
803
861
  throw new Error('build_hello: kind must be "client" or "server"');
804
862
  }
805
863
 
806
- function parse_hello(hsType, body) {
807
- let isClient = (hsType === TLS_MESSAGE_TYPE.CLIENT_HELLO || hsType === 'client_hello');
864
+ function parse_hello(params) {
865
+ let hsType = params.kind;
866
+ let body = params.body;
867
+ let isClient = (hsType === 'client' || hsType === TLS_MESSAGE_TYPE.CLIENT_HELLO || hsType === 'client_hello');
808
868
  let off = 0;
809
869
 
810
- // --- שדות משותפים ---
870
+ // --- shared fields ---
811
871
  let legacy_version; [legacy_version, off] = r_u16(body, off);
812
872
  let random; [random, off] = r_bytes(body, off, 32);
813
873
  let sidLen; [sidLen, off] = r_u8(body, off);
@@ -817,8 +877,20 @@ function parse_hello(hsType, body) {
817
877
  let legacy_compression = [];
818
878
  let type = isClient ? 'client_hello' : 'server_hello';
819
879
 
880
+ // DTLS cookie — auto-detect by version (DTLS versions have 0xFE in high byte)
881
+ let dtls_cookie = null;
882
+ let isDTLS = (legacy_version & 0xFF00) === 0xFE00;
883
+
820
884
  if (isClient) {
821
885
  // --- ClientHello ---
886
+ // DTLS ClientHello has a cookie field between session_id and cipher_suites
887
+ if (isDTLS) {
888
+ let cookieLen; [cookieLen, off] = r_u8(body, off);
889
+ if (cookieLen > 0) {
890
+ [dtls_cookie, off] = r_bytes(body, off, cookieLen);
891
+ }
892
+ }
893
+
822
894
  let csLen; [csLen, off] = r_u16(body, off);
823
895
  let csEnd = off + csLen;
824
896
  while (off < csEnd) {
@@ -858,9 +930,10 @@ function parse_hello(hsType, body) {
858
930
  version: version, // single (u16)
859
931
  random: random, // single (Uint8Array(32))
860
932
  session_id: session_id, // single (Uint8Array)
933
+ dtls_cookie: dtls_cookie, // Uint8Array or null (DTLS only)
861
934
  cipher_suites: cipher_suites, // array
862
935
  legacy_compression: legacy_compression, // array
863
- extensions: extensions // array (לא נוגעים בפנים)
936
+ extensions: extensions // array
864
937
  };
865
938
  }
866
939
 
@@ -1456,9 +1529,89 @@ function parse_key_update(body) {
1456
1529
  }
1457
1530
 
1458
1531
 
1532
+ /* ============================== DTLS Handshake Message ============================== */
1533
+
1534
+ /**
1535
+ * Build a DTLS handshake message from a TLS message.
1536
+ * Adds 8-byte reconstruction header: msg_seq(2) + frag_offset(3) + frag_length(3).
1537
+ *
1538
+ * tls_msg: Uint8Array — TLS format: type(1) + length(3) + body
1539
+ * msg_seq: u16 — handshake message sequence number
1540
+ * frag_offset: optional, defaults to 0
1541
+ * frag_length: optional, defaults to full body length
1542
+ */
1543
+ function build_dtls_handshake(tls_msg, msg_seq, frag_offset, frag_length) {
1544
+ let type = tls_msg[0];
1545
+ let total_length = (tls_msg[1] << 16) | (tls_msg[2] << 8) | tls_msg[3];
1546
+ let body = tls_msg.subarray(4);
1547
+
1548
+ if (frag_offset === undefined) frag_offset = 0;
1549
+ if (frag_length === undefined) frag_length = body.length;
1550
+
1551
+ let frag_body = body.subarray(frag_offset, frag_offset + frag_length);
1552
+
1553
+ let out = new Uint8Array(12 + frag_body.length);
1554
+ let off = 0;
1555
+ off = w_u8(out, off, type);
1556
+ off = w_u24(out, off, total_length);
1557
+ off = w_u16(out, off, msg_seq);
1558
+ off = w_u24(out, off, frag_offset);
1559
+ off = w_u24(out, off, frag_length);
1560
+ off = w_bytes(out, off, frag_body);
1561
+ return out;
1562
+ }
1563
+
1564
+ /**
1565
+ * Parse a DTLS handshake message.
1566
+ * Returns { type, length, msg_seq, frag_offset, frag_length, body }.
1567
+ */
1568
+ function parse_dtls_handshake(buf) {
1569
+ let off = 0;
1570
+ let type; [type, off] = r_u8(buf, off);
1571
+ let length; [length, off] = r_u24(buf, off);
1572
+ let msg_seq; [msg_seq, off] = r_u16(buf, off);
1573
+ let frag_offset;[frag_offset, off]= r_u24(buf, off);
1574
+ let frag_length;[frag_length, off]= r_u24(buf, off);
1575
+ let body; [body, off] = r_bytes(buf, off, frag_length);
1576
+ return { type, length, msg_seq, frag_offset, frag_length, body };
1577
+ }
1578
+
1579
+
1580
+ /* ============================== DTLS 1.2 HelloVerifyRequest ============================== */
1581
+
1582
+ /**
1583
+ * Build HelloVerifyRequest body (DTLS 1.2 — message type 3).
1584
+ * params: { server_version?, cookie: Uint8Array }
1585
+ */
1586
+ function build_hello_verify_request(params) {
1587
+ let version = (params && params.server_version) || DTLS_VERSION.DTLS1_2;
1588
+ let cookie = toU8(params && params.cookie || new Uint8Array(0));
1589
+ let out = new Uint8Array(2 + 1 + cookie.length);
1590
+ let off = 0;
1591
+ off = w_u16(out, off, version);
1592
+ off = w_u8(out, off, cookie.length);
1593
+ if (cookie.length > 0) off = w_bytes(out, off, cookie);
1594
+ return out;
1595
+ }
1596
+
1597
+ /**
1598
+ * Parse HelloVerifyRequest body.
1599
+ * Returns { server_version, cookie }.
1600
+ */
1601
+ function parse_hello_verify_request(body) {
1602
+ let off = 0;
1603
+ let server_version; [server_version, off] = r_u16(body, off);
1604
+ let cookieLen; [cookieLen, off] = r_u8(body, off);
1605
+ let cookie = new Uint8Array(0);
1606
+ if (cookieLen > 0) { [cookie, off] = r_bytes(body, off, cookieLen); }
1607
+ return { server_version, cookie };
1608
+ }
1609
+
1610
+
1459
1611
  /* ================================ Exports ================================= */
1460
1612
  export {
1461
1613
  TLS_VERSION,
1614
+ DTLS_VERSION,
1462
1615
  TLS_CONTENT_TYPE,
1463
1616
  TLS_ALERT_LEVEL,
1464
1617
  TLS_ALERT,
@@ -1468,6 +1621,7 @@ export {
1468
1621
  w_u8,
1469
1622
  w_u16,
1470
1623
  w_u24,
1624
+ w_u48,
1471
1625
  w_bytes,
1472
1626
  r_u8,
1473
1627
  r_u16,
@@ -1516,8 +1670,10 @@ export {
1516
1670
 
1517
1671
  build_key_update,
1518
1672
  parse_key_update,
1519
- };
1520
-
1521
-
1522
-
1523
1673
 
1674
+ // DTLS
1675
+ build_dtls_handshake,
1676
+ parse_dtls_handshake,
1677
+ build_hello_verify_request,
1678
+ parse_hello_verify_request,
1679
+ };