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 +5 -3
- package/src/compat.js +10 -0
- package/src/crypto.js +146 -48
- package/src/dtls_session.js +501 -66
- package/src/dtls_socket.js +7 -0
- package/src/record.js +88 -21
- package/src/session/ecdh.js +124 -0
- package/src/session/message.js +35 -0
- package/src/session/signing.js +240 -47
- package/src/tls_session.js +1839 -431
- package/src/tls_socket.js +378 -39
- package/src/wire.js +272 -14
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lemon-tls",
|
|
3
|
-
"version": "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
|
-
|
|
307
|
-
|
|
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,
|