lemon-tls 0.5.0 → 0.6.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lemon-tls",
3
- "version": "0.5.0",
4
- "description": "TLS 1.3/1.2 implementation for Node.js - full control over cryptographic keys, record layer, and handshake. Drop-in replacement for node:tls with advanced options impossible in OpenSSL.",
3
+ "version": "0.6.0",
4
+ "description": "TLS 1.3/1.2 DTLS 1.3/1.2 implementation for Node.js - full control over cryptographic keys, record layer, and handshake. Drop-in replacement for node:tls with advanced options impossible in OpenSSL.",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "types": "index.d.ts",
@@ -31,8 +31,11 @@
31
31
  },
32
32
  "keywords": [
33
33
  "tls",
34
+ "dtls",
34
35
  "tls13",
35
36
  "tls12",
37
+ "dtls13",
38
+ "dtls12",
36
39
  "tls-1.3",
37
40
  "tls-1.2",
38
41
  "ssl",
@@ -72,7 +75,6 @@
72
75
  "wireshark",
73
76
  "quic",
74
77
  "http3",
75
- "dtls",
76
78
  "record-layer",
77
79
  "pure-javascript",
78
80
  "zero-dependencies",
package/src/compat.js CHANGED
@@ -281,6 +281,16 @@ function Server(options, connectionListener) {
281
281
  maxRecordSize: options.maxRecordSize,
282
282
  sessionTickets: options.sessionTickets,
283
283
  requestCert: options.requestCert,
284
+ // Client-authentication policy. These were missing, so a server's
285
+ // rejectUnauthorized/ca never reached TLSSession and it fell back to the
286
+ // session default (rejectUnauthorized: true). The effect was invisible
287
+ // until client auth was actually exercised: `requestCert: true` WITHOUT
288
+ // `rejectUnauthorized` is the standard "ask for a certificate but accept
289
+ // clients that have none" configuration, and it rejected them instead.
290
+ // requestCert was forwarded and its two companions were not, which is
291
+ // exactly the kind of half-wired option that looks correct at a glance.
292
+ rejectUnauthorized: options.rejectUnauthorized,
293
+ ca: options.ca,
284
294
  maxHandshakeSize: options.maxHandshakeSize,
285
295
  allowedCipherSuites: options.allowedCipherSuites,
286
296
  handshakeTimeout: options.handshakeTimeout,
package/src/crypto.js CHANGED
@@ -303,8 +303,26 @@ function hkdf_expand(hashName, prkU8, infoU8, length) {
303
303
  // to handshakes (TextEncoder construction is not free in V8).
304
304
  const _TEXT_ENCODER = new TextEncoder();
305
305
 
306
- function build_hkdf_label(label, context, length) {
307
- const full = _TEXT_ENCODER.encode('tls13 ' + label);
306
+ // Label prefixes (RFC 8446 §7.1 / RFC 9147 §5.9). Exported so transports pick
307
+ // one explicitly instead of spelling the string out at each derivation site.
308
+ const LABEL_PREFIX_TLS13 = 'tls13 '; // trailing space is part of the label
309
+ const LABEL_PREFIX_DTLS13 = 'dtls13'; // no trailing space — deliberate
310
+
311
+ /**
312
+ * RFC 8446 §7.1 HkdfLabel, with the protocol's label prefix.
313
+ *
314
+ * TLS 1.3 uses "tls13 " (trailing space). DTLS 1.3 uses "dtls13" with NO
315
+ * trailing space — RFC 9147 §5.9: "For DTLS 1.3, that label SHALL be
316
+ * 'dtls13'. This ensures key separation between DTLS 1.3 and TLS 1.3. Note
317
+ * that there is no trailing space; this is necessary in order to keep the
318
+ * overall label size inside of one hash iteration."
319
+ *
320
+ * The prefix is threaded as a parameter rather than read from module state
321
+ * because it is a property of ONE session's key schedule; two sessions of
322
+ * different protocols can be live in the same process.
323
+ */
324
+ function build_hkdf_label(label, context, length, labelPrefix) {
325
+ const full = _TEXT_ENCODER.encode((labelPrefix || LABEL_PREFIX_TLS13) + label);
308
326
  const info = new Uint8Array(2 + 1 + full.length + 1 + context.length);
309
327
 
310
328
  info[0] = (length >>> 8) & 0xff;
@@ -319,8 +337,8 @@ function build_hkdf_label(label, context, length) {
319
337
  return info;
320
338
  }
321
339
 
322
- function hkdf_expand_label(hashName, secret, label, context, length) {
323
- let info = build_hkdf_label(label, context, length | 0);
340
+ function hkdf_expand_label(hashName, secret, label, context, length, labelPrefix) {
341
+ let info = build_hkdf_label(label, context, length | 0, labelPrefix);
324
342
  return hkdf_expand(hashName, secret, info, length | 0);
325
343
  }
326
344
 
@@ -329,7 +347,7 @@ function hkdf_expand_label(hashName, secret, label, context, length) {
329
347
  // TLS 1.3: derive handshake traffic secrets
330
348
  // ============================================================
331
349
 
332
- function derive_handshake_traffic_secrets(hashName, shared_secret, transcript) {
350
+ function derive_handshake_traffic_secrets(hashName, shared_secret, transcript, labelPrefix) {
333
351
  let hashFn = getHashFn(hashName);
334
352
  let hashLen = hashFn.outputLen | 0;
335
353
  const empty = new Uint8Array(0);
@@ -337,11 +355,11 @@ function derive_handshake_traffic_secrets(hashName, shared_secret, transcript) {
337
355
 
338
356
  let early_secret = hkdf_extract(hashName, empty, zeros);
339
357
  let h_empty = hashFn(empty);
340
- let derived_secret = hkdf_expand_label(hashName, early_secret, 'derived', h_empty, hashLen);
358
+ let derived_secret = hkdf_expand_label(hashName, early_secret, 'derived', h_empty, hashLen, labelPrefix);
341
359
  let handshake_secret = hkdf_extract(hashName, derived_secret, shared_secret);
342
360
  let transcript_hash = hashFn(transcript);
343
- let client_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 'c hs traffic', transcript_hash, hashLen);
344
- let server_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 's hs traffic', transcript_hash, hashLen);
361
+ let client_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 'c hs traffic', transcript_hash, hashLen, labelPrefix);
362
+ let server_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 's hs traffic', transcript_hash, hashLen, labelPrefix);
345
363
 
346
364
  return {
347
365
  handshake_secret: handshake_secret,
@@ -355,7 +373,7 @@ function derive_handshake_traffic_secrets(hashName, shared_secret, transcript) {
355
373
  * hash — skips the hashFn(transcript) step and its allocation. Use this when
356
374
  * the caller has already computed the hash via an incremental running hash.
357
375
  */
358
- function derive_handshake_traffic_secrets_with_hash(hashName, shared_secret, transcript_hash) {
376
+ function derive_handshake_traffic_secrets_with_hash(hashName, shared_secret, transcript_hash, labelPrefix) {
359
377
  let hashFn = getHashFn(hashName);
360
378
  let hashLen = hashFn.outputLen | 0;
361
379
  const empty = new Uint8Array(0);
@@ -363,10 +381,10 @@ function derive_handshake_traffic_secrets_with_hash(hashName, shared_secret, tra
363
381
 
364
382
  let early_secret = hkdf_extract(hashName, empty, zeros);
365
383
  let h_empty = hashFn(empty);
366
- let derived_secret = hkdf_expand_label(hashName, early_secret, 'derived', h_empty, hashLen);
384
+ let derived_secret = hkdf_expand_label(hashName, early_secret, 'derived', h_empty, hashLen, labelPrefix);
367
385
  let handshake_secret = hkdf_extract(hashName, derived_secret, shared_secret);
368
- let client_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 'c hs traffic', transcript_hash, hashLen);
369
- let server_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 's hs traffic', transcript_hash, hashLen);
386
+ let client_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 'c hs traffic', transcript_hash, hashLen, labelPrefix);
387
+ let server_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 's hs traffic', transcript_hash, hashLen, labelPrefix);
370
388
 
371
389
  return {
372
390
  handshake_secret: handshake_secret,
@@ -380,18 +398,18 @@ function derive_handshake_traffic_secrets_with_hash(hashName, shared_secret, tra
380
398
  // TLS 1.3: derive application traffic secrets
381
399
  // ============================================================
382
400
 
383
- function derive_app_traffic_secrets(hashName, handshake_secret, transcript) {
401
+ function derive_app_traffic_secrets(hashName, handshake_secret, transcript, labelPrefix) {
384
402
  let hashFn = getHashFn(hashName);
385
403
  let hashLen = hashFn.outputLen | 0;
386
404
  const empty = new Uint8Array(0);
387
405
  const zeros = new Uint8Array(hashLen);
388
406
 
389
407
  let h_empty = hashFn(empty);
390
- let derived_secret = hkdf_expand_label(hashName, handshake_secret, 'derived', h_empty, hashLen);
408
+ let derived_secret = hkdf_expand_label(hashName, handshake_secret, 'derived', h_empty, hashLen, labelPrefix);
391
409
  let master_secret = hkdf_extract(hashName, derived_secret, zeros);
392
410
  let transcript_hash = hashFn(transcript);
393
- let client_app_traffic_secret = hkdf_expand_label(hashName, master_secret, 'c ap traffic', transcript_hash, hashLen);
394
- let server_app_traffic_secret = hkdf_expand_label(hashName, master_secret, 's ap traffic', transcript_hash, hashLen);
411
+ let client_app_traffic_secret = hkdf_expand_label(hashName, master_secret, 'c ap traffic', transcript_hash, hashLen, labelPrefix);
412
+ let server_app_traffic_secret = hkdf_expand_label(hashName, master_secret, 's ap traffic', transcript_hash, hashLen, labelPrefix);
395
413
 
396
414
  return {
397
415
  client_app_traffic_secret: client_app_traffic_secret,
@@ -403,17 +421,17 @@ function derive_app_traffic_secrets(hashName, handshake_secret, transcript) {
403
421
  /**
404
422
  * Like derive_app_traffic_secrets but accepts a pre-computed transcript hash.
405
423
  */
406
- function derive_app_traffic_secrets_with_hash(hashName, handshake_secret, transcript_hash) {
424
+ function derive_app_traffic_secrets_with_hash(hashName, handshake_secret, transcript_hash, labelPrefix) {
407
425
  let hashFn = getHashFn(hashName);
408
426
  let hashLen = hashFn.outputLen | 0;
409
427
  const empty = new Uint8Array(0);
410
428
  const zeros = new Uint8Array(hashLen);
411
429
 
412
430
  let h_empty = hashFn(empty);
413
- let derived_secret = hkdf_expand_label(hashName, handshake_secret, 'derived', h_empty, hashLen);
431
+ let derived_secret = hkdf_expand_label(hashName, handshake_secret, 'derived', h_empty, hashLen, labelPrefix);
414
432
  let master_secret = hkdf_extract(hashName, derived_secret, zeros);
415
- let client_app_traffic_secret = hkdf_expand_label(hashName, master_secret, 'c ap traffic', transcript_hash, hashLen);
416
- let server_app_traffic_secret = hkdf_expand_label(hashName, master_secret, 's ap traffic', transcript_hash, hashLen);
433
+ let client_app_traffic_secret = hkdf_expand_label(hashName, master_secret, 'c ap traffic', transcript_hash, hashLen, labelPrefix);
434
+ let server_app_traffic_secret = hkdf_expand_label(hashName, master_secret, 's ap traffic', transcript_hash, hashLen, labelPrefix);
417
435
 
418
436
  return {
419
437
  client_app_traffic_secret: client_app_traffic_secret,
@@ -431,28 +449,28 @@ function derive_app_traffic_secrets_with_hash(hashName, handshake_secret, transc
431
449
  * Derive the resumption_master_secret from the master_secret.
432
450
  * transcript = all handshake messages including both Finished.
433
451
  */
434
- function derive_resumption_master_secret(hashName, master_secret, transcript) {
452
+ function derive_resumption_master_secret(hashName, master_secret, transcript, labelPrefix) {
435
453
  let hashFn = getHashFn(hashName);
436
454
  let hashLen = hashFn.outputLen | 0;
437
455
  let transcript_hash = hashFn(transcript);
438
- return hkdf_expand_label(hashName, master_secret, 'res master', transcript_hash, hashLen);
456
+ return hkdf_expand_label(hashName, master_secret, 'res master', transcript_hash, hashLen, labelPrefix);
439
457
  }
440
458
 
441
459
  /**
442
460
  * Like derive_resumption_master_secret but accepts a pre-computed transcript hash.
443
461
  */
444
- function derive_resumption_master_secret_with_hash(hashName, master_secret, transcript_hash) {
462
+ function derive_resumption_master_secret_with_hash(hashName, master_secret, transcript_hash, labelPrefix) {
445
463
  let hashLen = getHashLen(hashName);
446
- return hkdf_expand_label(hashName, master_secret, 'res master', transcript_hash, hashLen);
464
+ return hkdf_expand_label(hashName, master_secret, 'res master', transcript_hash, hashLen, labelPrefix);
447
465
  }
448
466
 
449
467
  /**
450
468
  * Derive PSK from a resumption_master_secret + ticket_nonce.
451
469
  * Used by the server when creating a ticket, and by the client when resuming.
452
470
  */
453
- function derive_psk(hashName, resumption_master_secret, ticket_nonce) {
471
+ function derive_psk(hashName, resumption_master_secret, ticket_nonce, labelPrefix) {
454
472
  let hashLen = getHashFn(hashName).outputLen | 0;
455
- return hkdf_expand_label(hashName, resumption_master_secret, 'resumption', ticket_nonce, hashLen);
473
+ return hkdf_expand_label(hashName, resumption_master_secret, 'resumption', ticket_nonce, hashLen, labelPrefix);
456
474
  }
457
475
 
458
476
  /**
@@ -460,7 +478,7 @@ function derive_psk(hashName, resumption_master_secret, ticket_nonce) {
460
478
  * For resumption PSK, label is "res binder".
461
479
  * For external PSK, label is "ext binder".
462
480
  */
463
- function derive_binder_key(hashName, psk, isExternal) {
481
+ function derive_binder_key(hashName, psk, isExternal, labelPrefix) {
464
482
  let hashFn = getHashFn(hashName);
465
483
  let hashLen = hashFn.outputLen | 0;
466
484
  const empty = new Uint8Array(0);
@@ -469,7 +487,7 @@ function derive_binder_key(hashName, psk, isExternal) {
469
487
  let early_secret = hkdf_extract(hashName, empty, psk);
470
488
  let h_empty = hashFn(empty);
471
489
  let label = isExternal ? 'ext binder' : 'res binder';
472
- return hkdf_expand_label(hashName, early_secret, label, h_empty, hashLen);
490
+ return hkdf_expand_label(hashName, early_secret, label, h_empty, hashLen, labelPrefix);
473
491
  }
474
492
 
475
493
  /**
@@ -484,11 +502,11 @@ function derive_binder_key(hashName, psk, isExternal) {
484
502
  * So we must derive a finished_key from binder_key (as in get_handshake_finished)
485
503
  * before the HMAC, NOT use binder_key directly.
486
504
  */
487
- function compute_psk_binder(hashName, binder_key, truncated_transcript) {
505
+ function compute_psk_binder(hashName, binder_key, truncated_transcript, labelPrefix) {
488
506
  let hashFn = getHashFn(hashName);
489
507
  let hashLen = hashFn.outputLen | 0;
490
508
  const empty = new Uint8Array(0);
491
- let finished_key = hkdf_expand_label(hashName, binder_key, 'finished', empty, hashLen);
509
+ let finished_key = hkdf_expand_label(hashName, binder_key, 'finished', empty, hashLen, labelPrefix);
492
510
  let transcript_hash = hashFn(truncated_transcript);
493
511
  return hmac(hashName, finished_key, transcript_hash);
494
512
  }
@@ -497,18 +515,18 @@ function compute_psk_binder(hashName, binder_key, truncated_transcript) {
497
515
  * Derive handshake secrets for a PSK-based handshake (with ECDHE).
498
516
  * Uses the PSK as input to early_secret instead of zeros.
499
517
  */
500
- function derive_handshake_traffic_secrets_psk(hashName, psk, shared_secret, transcript) {
518
+ function derive_handshake_traffic_secrets_psk(hashName, psk, shared_secret, transcript, labelPrefix) {
501
519
  let hashFn = getHashFn(hashName);
502
520
  let hashLen = hashFn.outputLen | 0;
503
521
  const empty = new Uint8Array(0);
504
522
 
505
523
  let early_secret = hkdf_extract(hashName, empty, psk);
506
524
  let h_empty = hashFn(empty);
507
- let derived_secret = hkdf_expand_label(hashName, early_secret, 'derived', h_empty, hashLen);
525
+ let derived_secret = hkdf_expand_label(hashName, early_secret, 'derived', h_empty, hashLen, labelPrefix);
508
526
  let handshake_secret = hkdf_extract(hashName, derived_secret, shared_secret);
509
527
  let transcript_hash = hashFn(transcript);
510
- let client_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 'c hs traffic', transcript_hash, hashLen);
511
- let server_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 's hs traffic', transcript_hash, hashLen);
528
+ let client_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 'c hs traffic', transcript_hash, hashLen, labelPrefix);
529
+ let server_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 's hs traffic', transcript_hash, hashLen, labelPrefix);
512
530
 
513
531
  return {
514
532
  handshake_secret: handshake_secret,
@@ -520,17 +538,17 @@ function derive_handshake_traffic_secrets_psk(hashName, psk, shared_secret, tran
520
538
  /**
521
539
  * Like derive_handshake_traffic_secrets_psk but accepts a pre-computed transcript hash.
522
540
  */
523
- function derive_handshake_traffic_secrets_psk_with_hash(hashName, psk, shared_secret, transcript_hash) {
541
+ function derive_handshake_traffic_secrets_psk_with_hash(hashName, psk, shared_secret, transcript_hash, labelPrefix) {
524
542
  let hashFn = getHashFn(hashName);
525
543
  let hashLen = hashFn.outputLen | 0;
526
544
  const empty = new Uint8Array(0);
527
545
 
528
546
  let early_secret = hkdf_extract(hashName, empty, psk);
529
547
  let h_empty = hashFn(empty);
530
- let derived_secret = hkdf_expand_label(hashName, early_secret, 'derived', h_empty, hashLen);
548
+ let derived_secret = hkdf_expand_label(hashName, early_secret, 'derived', h_empty, hashLen, labelPrefix);
531
549
  let handshake_secret = hkdf_extract(hashName, derived_secret, shared_secret);
532
- let client_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 'c hs traffic', transcript_hash, hashLen);
533
- let server_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 's hs traffic', transcript_hash, hashLen);
550
+ let client_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 'c hs traffic', transcript_hash, hashLen, labelPrefix);
551
+ let server_handshake_traffic_secret = hkdf_expand_label(hashName, handshake_secret, 's hs traffic', transcript_hash, hashLen, labelPrefix);
534
552
 
535
553
  return {
536
554
  handshake_secret: handshake_secret,
@@ -648,10 +666,10 @@ function build_cert_verify_tbs_with_hash(hashName, isServer, transcript_hash) {
648
666
  // TLS 1.3 Finished verify_data
649
667
  // ============================================================
650
668
 
651
- function get_handshake_finished(hashName, traffic_secret, transcript) {
669
+ function get_handshake_finished(hashName, traffic_secret, transcript, labelPrefix) {
652
670
  let hashLen = getHashLen(hashName);
653
671
  const empty = new Uint8Array(0);
654
- let finished_key = hkdf_expand_label(hashName, traffic_secret, 'finished', empty, hashLen);
672
+ let finished_key = hkdf_expand_label(hashName, traffic_secret, 'finished', empty, hashLen, labelPrefix);
655
673
  let transcript_hash = getHashFn(hashName)(transcript);
656
674
  return hmac(hashName, finished_key, transcript_hash);
657
675
  }
@@ -659,10 +677,10 @@ function get_handshake_finished(hashName, traffic_secret, transcript) {
659
677
  /**
660
678
  * Like get_handshake_finished but accepts a pre-computed transcript hash.
661
679
  */
662
- function get_handshake_finished_with_hash(hashName, traffic_secret, transcript_hash) {
680
+ function get_handshake_finished_with_hash(hashName, traffic_secret, transcript_hash, labelPrefix) {
663
681
  let hashLen = getHashLen(hashName);
664
682
  const empty = new Uint8Array(0);
665
- let finished_key = hkdf_expand_label(hashName, traffic_secret, 'finished', empty, hashLen);
683
+ let finished_key = hkdf_expand_label(hashName, traffic_secret, 'finished', empty, hashLen, labelPrefix);
666
684
  return hmac(hashName, finished_key, transcript_hash);
667
685
  }
668
686
 
@@ -686,9 +704,9 @@ function get_handshake_finished_with_hash(hashName, traffic_secret, transcript_h
686
704
  * Called at the same point (and with the same transcript hash) as the
687
705
  * application traffic secrets.
688
706
  */
689
- function derive_exporter_master_secret_with_hash(hashName, master_secret, transcript_hash) {
707
+ function derive_exporter_master_secret_with_hash(hashName, master_secret, transcript_hash, labelPrefix) {
690
708
  let hashLen = getHashLen(hashName);
691
- return hkdf_expand_label(hashName, master_secret, 'exp master', transcript_hash, hashLen);
709
+ return hkdf_expand_label(hashName, master_secret, 'exp master', transcript_hash, hashLen, labelPrefix);
692
710
  }
693
711
 
694
712
  /**
@@ -711,11 +729,11 @@ function derive_exporter_master_secret_with_hash(hashName, master_secret, transc
711
729
  * @param {number} length
712
730
  * @returns {Uint8Array}
713
731
  */
714
- function tls13_exporter(hashName, exporter_master_secret, label, context_value, length) {
732
+ function tls13_exporter(hashName, exporter_master_secret, label, context_value, length, labelPrefix) {
715
733
  let hashFn = getHashFn(hashName);
716
734
  let hashLen = getHashLen(hashName);
717
735
  let h_empty = hashFn(new Uint8Array(0));
718
- let derived = hkdf_expand_label(hashName, exporter_master_secret, label, h_empty, hashLen);
736
+ let derived = hkdf_expand_label(hashName, exporter_master_secret, label, h_empty, hashLen, labelPrefix);
719
737
  let h_ctx = hashFn(context_value || new Uint8Array(0));
720
738
  return hkdf_expand(hashName, derived, build_hkdf_label('exporter', h_ctx, length), length);
721
739
  }
@@ -758,9 +776,9 @@ function tls12_exporter(hashName, master_secret, label, client_random, server_ra
758
776
  return tls12_prf(master_secret, label, seed, length, hashName);
759
777
  }
760
778
 
761
- function derive_sn_key(hashName, traffic_secret, cipher_suite) {
779
+ function derive_sn_key(hashName, traffic_secret, cipher_suite, labelPrefix) {
762
780
  let keylen = TLS_CIPHER_SUITES[cipher_suite].keylen;
763
- return hkdf_expand_label(hashName, traffic_secret, 'sn', new Uint8Array(0), keylen);
781
+ return hkdf_expand_label(hashName, traffic_secret, 'sn', new Uint8Array(0), keylen, labelPrefix);
764
782
  }
765
783
 
766
784
 
@@ -768,7 +786,87 @@ function derive_sn_key(hashName, traffic_secret, cipher_suite) {
768
786
  // Exports — identical API surface
769
787
  // ============================================================
770
788
 
789
+ /**
790
+ * Default cipher-suite offers, in preference order.
791
+ *
792
+ * SINGLE SOURCE OF TRUTH, alongside the TLS_CIPHER_SUITES table this module
793
+ * already owns — the same rule as ecdh.js/SUPPORTED_GROUPS and
794
+ * signing.js/default_signature_schemes. Previously TLSSocket, DTLSSession and
795
+ * the TLSSession client default each carried their own hardcoded list, and
796
+ * they had already drifted (one offered ChaCha20 for 1.2, another offered
797
+ * AES-256-ECDSA instead).
798
+ */
799
+ const DEFAULT_CIPHER_SUITES_TLS13 = [
800
+ 0x1301, // TLS_AES_128_GCM_SHA256
801
+ 0x1302, // TLS_AES_256_GCM_SHA384
802
+ 0x1303, // TLS_CHACHA20_POLY1305_SHA256
803
+ ];
804
+
805
+ const DEFAULT_CIPHER_SUITES_TLS12 = [
806
+ 0xC02F, // ECDHE_RSA_WITH_AES_128_GCM_SHA256
807
+ 0xC030, // ECDHE_RSA_WITH_AES_256_GCM_SHA384
808
+ 0xC02B, // ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
809
+ 0xC02C, // ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
810
+ 0xCCA8, // ECDHE_RSA_WITH_CHACHA20_POLY1305
811
+ 0xCCA9, // ECDHE_ECDSA_WITH_CHACHA20_POLY1305
812
+ ];
813
+
814
+ /** Default offer list for a version range. */
815
+ /**
816
+ * Can the record layer actually protect traffic with this cipher suite?
817
+ *
818
+ * TLS_CIPHER_SUITES describes suites the code can NAME — including CBC ones
819
+ * kept for parsing peer offers and for reporting — but record.js implements
820
+ * AEAD only. A suite that is merely described is not a capability: selecting
821
+ * one produces a handshake that agrees on parameters and then cannot encrypt a
822
+ * single record.
823
+ *
824
+ * This mirrors ecdh.js's is_supported_group, and exists for the same reason: a
825
+ * configured list is not an implementation. Selection asks this, never the
826
+ * table's mere presence.
827
+ */
828
+ function is_usable_cipher_suite(id) {
829
+ let m = TLS_CIPHER_SUITES[id];
830
+ return !!(m && m.aead === true);
831
+ }
832
+
833
+ /**
834
+ * May this cipher suite be used with this negotiated version?
835
+ *
836
+ * RFC 8446 §B.4 defines 0x1301..0x1303 for TLS 1.3 ONLY; they are not valid
837
+ * TLS 1.2 suites and a 1.2 peer must ignore them. The converse holds too — an
838
+ * ECDHE_RSA_WITH_AES_128_GCM suite has no meaning in a 1.3 handshake, where
839
+ * key exchange and authentication are negotiated separately from the AEAD.
840
+ *
841
+ * The suite table already records which version each entry belongs to (`tls`:
842
+ * 12 or 13); this makes selection ask. Without it a server whose configured
843
+ * list happened to contain a 1.3 suite would select it while negotiating 1.2,
844
+ * producing a ServerHello no 1.2 client can act on.
845
+ */
846
+ function suite_matches_version(id, negotiatedVersion) {
847
+ let m = TLS_CIPHER_SUITES[id];
848
+ if (!m) return false;
849
+ if (negotiatedVersion === null || negotiatedVersion === undefined) return true;
850
+ let is13 = negotiatedVersion === 0x0304 || negotiatedVersion === 0xFEFC;
851
+ return is13 ? (m.tls === 13) : (m.tls === 12);
852
+ }
853
+
854
+ function default_cipher_suites(includeTls13, includeTls12) {
855
+ let out = [];
856
+ if (includeTls13) out = out.concat(DEFAULT_CIPHER_SUITES_TLS13);
857
+ if (includeTls12) out = out.concat(DEFAULT_CIPHER_SUITES_TLS12);
858
+ return out;
859
+ }
860
+
771
861
  export {
862
+ is_usable_cipher_suite,
863
+ suite_matches_version,
864
+ LABEL_PREFIX_TLS13,
865
+ LABEL_PREFIX_DTLS13,
866
+ DEFAULT_CIPHER_SUITES_TLS13,
867
+ DEFAULT_CIPHER_SUITES_TLS12,
868
+ default_cipher_suites,
869
+
772
870
  TLS_CIPHER_SUITES,
773
871
  getHashFn,
774
872
  getHashLen,