lemon-tls 0.1.1 → 0.2.1

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.
@@ -0,0 +1,2282 @@
1
+ import * as crypto from 'node:crypto';
2
+ import { EventEmitter } from 'node:events';
3
+
4
+ import {
5
+ TLS_CIPHER_SUITES,
6
+ build_cert_verify_tbs,
7
+ get_handshake_finished,
8
+ tls12_prf,
9
+ derive_handshake_traffic_secrets,
10
+ derive_app_traffic_secrets,
11
+ derive_resumption_master_secret,
12
+ derive_psk,
13
+ derive_binder_key,
14
+ compute_psk_binder,
15
+ derive_handshake_traffic_secrets_psk,
16
+ hkdf_expand_label,
17
+ getHashFn,
18
+ } from './crypto.js';
19
+
20
+ import {
21
+ concatUint8Arrays,
22
+ arraysEqual,
23
+ uint8Equal
24
+ } from './utils.js';
25
+
26
+ import * as wire from './wire.js';
27
+
28
+ // Extracted modules
29
+ import { pick_scheme, sign_with_scheme } from './session/signing.js';
30
+ import createSecureContext from './secure_context.js';
31
+ import { x25519_get_public_key, x25519_get_shared_secret, p256_generate_keypair, p256_get_shared_secret, p384_generate_keypair, p384_get_shared_secret } from './session/ecdh.js';
32
+ import { build_tls_message, parse_tls_message } from './session/message.js';
33
+
34
+
35
+ function TLSSession(options){
36
+ if (!(this instanceof TLSSession)) return new TLSSession(options);
37
+ options = options || {};
38
+
39
+ const ev = new EventEmitter();
40
+
41
+ let context = {
42
+ state: 'new', //new | negotiating | ...
43
+ isServer: !!options.isServer,
44
+ rejectUnauthorized: options.rejectUnauthorized !== false, // default true
45
+ ca: options.ca || null, // CA certificates (PEM strings or Buffers)
46
+
47
+ SNICallback: options.SNICallback || null,
48
+ ticketKeys: options.ticketKeys || null, // 48 bytes for ticket encryption
49
+
50
+ // Advanced options
51
+ maxHandshakeSize: options.maxHandshakeSize || 0, // 0 = no limit
52
+ customExtensions: options.customExtensions || [], // [{type:0xNN, data:Uint8Array}]
53
+ handshakeBytes: 0,
54
+ handshakeStartTime: null,
55
+ handshakeEndTime: null,
56
+ rawClientHello: null, // saved for JA3/JA4 and 'clienthello' event
57
+
58
+ //local stuff...
59
+ local_sni: options.servername || null,
60
+ local_session_id: 'sessionId' in options ? options.sessionId : null,
61
+
62
+ local_random: null,
63
+ local_extensions: [],
64
+
65
+
66
+ local_supported_versions: [],
67
+ local_supported_alpns: [],
68
+
69
+ local_supported_cipher_suites: [],
70
+ local_supported_signature_algorithms: [],
71
+ local_supported_groups: [],
72
+
73
+
74
+ //remote stuff...
75
+ remote_sni: null,
76
+ remote_session_id: null,
77
+
78
+ remote_random: null,
79
+ remote_extensions: [],
80
+
81
+ remote_supported_versions: [],
82
+ remote_supported_alpns: [],
83
+
84
+ remote_supported_cipher_suites: [],
85
+ remote_supported_signature_algorithms: [],
86
+ remote_supported_groups: [],
87
+
88
+
89
+ //selected stuff...
90
+ selected_extensions: [],
91
+ selected_sni: null,
92
+ selected_session_id: null,// for TLS 1.2 only
93
+
94
+ selected_version: null,
95
+ selected_alpn: null,
96
+ selected_cipher_suite: null,
97
+ selected_signature_algorithm: null,
98
+ selected_group: null,
99
+
100
+
101
+
102
+ local_key_groups: {},
103
+ remote_key_groups: {},
104
+
105
+
106
+ ecdhe_shared_secret: null,
107
+ base_secret: null,
108
+
109
+
110
+ transcript: [],
111
+
112
+
113
+ //both
114
+ hello_sent: false,
115
+ finished_sent: false,
116
+ cert_sent: false,
117
+
118
+ //1.2 only
119
+ key_exchange_sent: false,
120
+ hello_done_sent: false,
121
+ remote_hello_done: false,
122
+ use_extended_master_secret: false,
123
+
124
+ //1.3 only
125
+ encrypted_exts_sent: false,
126
+ cert_verify_sent: false,
127
+
128
+
129
+ message_sent_seq: 0,
130
+
131
+ remote_finished: null,
132
+ expected_remote_finished: null,
133
+ remote_finished_ok: false,
134
+ local_finished_data: null, // saved for getFinished()
135
+ remote_finished_data: null, // saved for getPeerFinished()
136
+
137
+ remote_handshake_traffic_secret: null,
138
+ local_handshake_traffic_secret: null,
139
+
140
+ remote_app_traffic_secret: null,
141
+ local_app_traffic_secret: null,
142
+
143
+ local_cert_chain: null,
144
+ remote_cert_chain: null,
145
+ peerAuthorized: false,
146
+ authorizationError: null,
147
+
148
+ selected_cert: null,
149
+
150
+ cert_private_key: null,
151
+
152
+ // Client certificate authentication
153
+ requestCert: !!options.requestCert, // server: send CertificateRequest?
154
+ clientCert: options.cert || null, // client: cert to send if requested
155
+ clientKey: options.key || null, // client: private key for CertificateVerify
156
+ certificateRequested: false, // client: server sent CertificateRequest?
157
+ certificateRequestSent: false, // server: we sent CertificateRequest?
158
+ certificateRequestContext: null,
159
+ certificateRequestSigAlgs: [],
160
+ clientCertSent: false,
161
+
162
+ // HelloRetryRequest
163
+ helloRetried: false, // true if HRR was sent/received
164
+
165
+ // TLS 1.3 resumption
166
+ tls13_master_secret: null,
167
+ resumption_master_secret: null,
168
+ ticket_nonce_counter: 0,
169
+ session_ticket_sent: false,
170
+ noTickets: !!options.noTickets,
171
+ psk_offered: null, // client: { identity, psk, cipher } offered in ClientHello
172
+ psk_accepted: false, // server accepted PSK → abbreviated handshake
173
+ isResumed: false, // true if PSK was accepted
174
+ };
175
+
176
+ function process_income_message(data){
177
+
178
+ // Track handshake start time
179
+ if (context.handshakeStartTime === null) context.handshakeStartTime = Date.now();
180
+
181
+ // Track handshake size and enforce limit
182
+ context.handshakeBytes += data.length;
183
+ if (context.maxHandshakeSize > 0 && context.handshakeBytes > context.maxHandshakeSize) {
184
+ ev.emit('error', new Error('Handshake size exceeded maxHandshakeSize (' + context.maxHandshakeSize + ')'));
185
+ return;
186
+ }
187
+
188
+ let message = parse_tls_message(data);
189
+
190
+ // Emit 'handshakeMessage' hook for every message
191
+ ev.emit('handshakeMessage', message.type, data, message);
192
+
193
+ if((context.isServer==false && message.type=='server_hello') || (context.isServer==true && message.type=='client_hello')){
194
+
195
+ context.transcript.push(data);
196
+
197
+ // Save raw ClientHello + emit event (server side)
198
+ if (context.isServer && message.type === 'client_hello') {
199
+ context.rawClientHello = data;
200
+ ev.emit('clienthello', data, message);
201
+ }
202
+
203
+ // Detect extended_master_secret (type 23 / 0x0017) from remote hello
204
+ // Server: detects from ClientHello. Client: detects from ServerHello.
205
+ if (Array.isArray(message.extensions)) {
206
+ for (let ei = 0; ei < message.extensions.length; ei++) {
207
+ if (message.extensions[ei].type === 0x0017) {
208
+ context.use_extended_master_secret = true;
209
+ break;
210
+ }
211
+ }
212
+ }
213
+
214
+ // Server: detect and validate PSK BEFORE set_context (so reactive loop sees psk_accepted)
215
+ if (context.isServer && message.pre_shared_key && message.pre_shared_key.identities && message.pre_shared_key.identities.length > 0) {
216
+ let pskIdentity = message.pre_shared_key.identities[0];
217
+ let pskBinder = message.pre_shared_key.binders ? message.pre_shared_key.binders[0] : null;
218
+
219
+ let pskResult = null;
220
+ ev.emit('psk', pskIdentity.identity, function(result) {
221
+ pskResult = result;
222
+ });
223
+ if (pskResult && pskResult.psk) {
224
+ let pskCipher = pskResult.cipher || 0x1301;
225
+ let hashName = TLS_CIPHER_SUITES[pskCipher] ? TLS_CIPHER_SUITES[pskCipher].hash : 'sha256';
226
+ let binder_key = derive_binder_key(hashName, pskResult.psk, false);
227
+
228
+ let hashLen = getHashFn(hashName).outputLen;
229
+ let bindersSize = 2 + 1 + hashLen;
230
+ let truncatedCH = data.slice(0, data.length - bindersSize);
231
+ let expectedBinder = compute_psk_binder(hashName, binder_key, truncatedCH);
232
+
233
+ let binderOk = pskBinder && expectedBinder.length === pskBinder.length;
234
+ if (binderOk) {
235
+ for (let bi = 0; bi < expectedBinder.length; bi++) {
236
+ if (expectedBinder[bi] !== pskBinder[bi]) { binderOk = false; break; }
237
+ }
238
+ }
239
+
240
+ if (binderOk) {
241
+ context.psk_accepted = true;
242
+ context.isResumed = true;
243
+ context.psk_offered = {
244
+ psk: pskResult.psk instanceof Uint8Array ? pskResult.psk : new Uint8Array(pskResult.psk),
245
+ cipher: pskCipher,
246
+ };
247
+ }
248
+ }
249
+ }
250
+
251
+ // Client: detect if server accepted PSK from ServerHello (BEFORE set_context)
252
+ if (!context.isServer && message.pre_shared_key && typeof message.pre_shared_key.selected === 'number') {
253
+ if (context.psk_offered) {
254
+ context.psk_accepted = true;
255
+ context.isResumed = true;
256
+ }
257
+ }
258
+
259
+ // Client: detect HelloRetryRequest (ServerHello with magic random)
260
+ if (!context.isServer && message.random && uint8Equal(message.random, wire.TLS13_HRR_RANDOM)) {
261
+ context.helloRetried = true;
262
+
263
+ // Get cipher from HRR to determine hash
264
+ let hrrCipher = null;
265
+ if (message.cipher_suites && message.cipher_suites.length > 0) hrrCipher = message.cipher_suites[0];
266
+ else if (message.cipher_suite) hrrCipher = message.cipher_suite;
267
+ if (!hrrCipher) hrrCipher = 0x1301;
268
+ let hashName = TLS_CIPHER_SUITES[hrrCipher] ? TLS_CIPHER_SUITES[hrrCipher].hash : 'sha256';
269
+
270
+ // Replace transcript: CH1 → message_hash (RFC 8446 §4.4.1)
271
+ // BUG FIX: The HRR was already pushed to transcript at the top of this block (line 195).
272
+ // We must remove it before hashing, since message_hash = Hash(ClientHello1) only.
273
+ let hrrData = context.transcript.pop(); // remove HRR
274
+ let ch1_hash = getHashFn(hashName)(concatUint8Arrays(context.transcript));
275
+ let message_hash = wire.build_message(wire.TLS_MESSAGE_TYPE.MESSAGE_HASH, ch1_hash);
276
+ context.transcript = [message_hash, hrrData]; // message_hash + HRR
277
+
278
+ // Find the requested group from HRR key_share extension
279
+ // After wire.js fix, key_groups contains [{group: N, key_exchange: empty}] for HRR
280
+ let requestedGroup = null;
281
+ if (message.key_groups && message.key_groups.length > 0) {
282
+ requestedGroup = message.key_groups[0].group;
283
+ } else if (message.supported_groups && message.supported_groups.length > 0) {
284
+ requestedGroup = message.supported_groups[0];
285
+ }
286
+
287
+ // Extract cookie from HRR (if present, must be echoed in CH2)
288
+ let hrrCookie = message.cookie || null;
289
+
290
+ if (requestedGroup) {
291
+ // Generate key for the requested group
292
+ let newKeyGroup = null;
293
+ if (requestedGroup === 0x001d) {
294
+ let pk = new Uint8Array(crypto.randomBytes(32));
295
+ let pub = x25519_get_public_key(pk);
296
+ newKeyGroup = { group: requestedGroup, public_key: pub, private_key: pk };
297
+ context.local_key_groups[requestedGroup] = { public_key: pub, private_key: pk };
298
+ } else if (requestedGroup === 0x0017) {
299
+ let kp = p256_generate_keypair();
300
+ newKeyGroup = { group: requestedGroup, public_key: kp.public_key, private_key: kp.private_key };
301
+ context.local_key_groups[requestedGroup] = { public_key: kp.public_key, private_key: kp.private_key };
302
+ } else if (requestedGroup === 0x0018) {
303
+ let kp = p384_generate_keypair();
304
+ newKeyGroup = { group: requestedGroup, public_key: kp.public_key, private_key: kp.private_key };
305
+ context.local_key_groups[requestedGroup] = { public_key: kp.public_key, private_key: kp.private_key };
306
+ }
307
+
308
+ if (newKeyGroup) {
309
+ // Build and send new ClientHello (CH2) with:
310
+ // - key_share for requested group
311
+ // - cookie (if HRR included one)
312
+ // - ALPN (same as CH1)
313
+ // - custom extensions (QUIC transport params etc.)
314
+ // - same cipher_suites, session_id, random as CH1
315
+ let extensions = [
316
+ { type: 'SUPPORTED_VERSIONS', value: context.local_supported_versions },
317
+ { type: 'SUPPORTED_GROUPS', value: context.local_supported_groups },
318
+ { type: 'KEY_SHARE', value: [{ group: requestedGroup, key_exchange: newKeyGroup.public_key }] },
319
+ { type: 'SIGNATURE_ALGORITHMS', value: [
320
+ // Must match CH1 exactly (RFC 8446 §4.1.2)
321
+ 0x0804, 0x0805, 0x0806,
322
+ 0x0403, 0x0503, 0x0603,
323
+ 0x0807, 0x0808,
324
+ 0x0401, 0x0501, 0x0601
325
+ ] },
326
+ { type: 'RENEGOTIATION_INFO', value: new Uint8Array(0) },
327
+ { type: 23, data: new Uint8Array(0) }, // extended_master_secret
328
+ ];
329
+
330
+ // SNI (must be first)
331
+ if (context.local_sni) extensions.unshift({ type: 'SERVER_NAME', value: context.local_sni });
332
+
333
+ // ALPN (same as CH1 — required for QUIC/h3)
334
+ if (context.local_supported_alpns && context.local_supported_alpns.length > 0) {
335
+ extensions.push({ type: 'ALPN', value: context.local_supported_alpns });
336
+ }
337
+
338
+ // Cookie from HRR (RFC 8446 §4.2.2 — MUST echo if present)
339
+ if (hrrCookie) {
340
+ extensions.push({ type: 'COOKIE', value: hrrCookie });
341
+ }
342
+
343
+ // Custom extensions (e.g. QUIC transport params 0x39)
344
+ for (let ci in context.local_extensions) {
345
+ extensions.push(context.local_extensions[ci]);
346
+ }
347
+
348
+ let ch2 = build_tls_message({
349
+ type: 'client_hello',
350
+ version: 0x0303,
351
+ random: context.local_random,
352
+ session_id: context.local_session_id,
353
+ cipher_suite: context.local_supported_cipher_suites,
354
+ extensions: extensions,
355
+ });
356
+
357
+ context.transcript.push(ch2);
358
+ ev.emit('message', 0, context.message_sent_seq, 'hello', ch2);
359
+ context.message_sent_seq++;
360
+ }
361
+ }
362
+
363
+ // Don't process HRR as a regular ServerHello
364
+ return;
365
+ }
366
+
367
+ set_context({
368
+ remote_random: message.random || null,
369
+ remote_sni: message.sni || null,
370
+ remote_session_id: message.session_id || null,
371
+ remote_supported_versions: message.supported_versions || [],
372
+ remote_supported_alpns: message.alpn || [],
373
+ remote_supported_cipher_suites: message.cipher_suites || [],
374
+ remote_supported_signature_algorithms: message.signature_algorithms || [],
375
+ remote_supported_groups: message.supported_groups || [],
376
+ remote_extensions: message.extensions || [],
377
+ add_remote_key_groups: message.key_groups || []
378
+ });
379
+
380
+ ev.emit('hello');
381
+
382
+ if(context.isServer==true){
383
+ if(typeof context.SNICallback=='function'){
384
+ context.SNICallback(context.remote_sni, function (err, creds) {
385
+ if (!err && creds) {
386
+ set_context({
387
+ local_cert_chain: creds.certificateChain,
388
+ cert_private_key: creds.privateKey
389
+ });
390
+ }
391
+ });
392
+ }
393
+ }
394
+
395
+
396
+
397
+ }else if(message.type=='client_key_exchange' || message.type=='server_key_exchange'){
398
+
399
+ context.transcript.push(data);
400
+
401
+ if ([0xC02F,0xC02B,0xC030,0xC02C,0xC013,0xC014,0xC009,0xC00A].includes(context.selected_cipher_suite)==true) {//ECDHE
402
+
403
+ // ServerKeyExchange carries the group; ClientKeyExchange does not (server already chose it)
404
+ let kex_group = message.group || context.selected_group;
405
+
406
+ set_context({
407
+ add_remote_key_groups: [
408
+ {
409
+ group: kex_group,
410
+ public_key: message.public_key
411
+ }
412
+ ],
413
+ });
414
+
415
+ }else if ([0x009E,0x009F,0x0033,0x0039,0x0067,0x006B].includes(context.selected_cipher_suite)==true) {//DHE
416
+
417
+ let client_dh_y=message.body.slice(2);
418
+
419
+ }else if ([0x002F,0x0035,0x003C,0x003D,0x0005,0x000A].includes(context.selected_cipher_suite)==true) {//RSA
420
+
421
+ let enc_pms=message.body.slice(2);
422
+
423
+ }else if ([0xC004,0xC005,0xC00B,0xC00C].includes(context.selected_cipher_suite)==true) {//ECDH
424
+
425
+ }
426
+
427
+ }else if(message.type=='server_hello_done'){
428
+
429
+ context.transcript.push(data);
430
+
431
+
432
+ set_context({
433
+ remote_hello_done: true,
434
+ });
435
+
436
+ }else if(message.type=='encrypted_extensions'){
437
+
438
+ context.transcript.push(data);
439
+
440
+ set_context({
441
+ remote_supported_groups: message.supported_groups || [],
442
+ });
443
+
444
+ }else if(message.type=='certificate'){
445
+
446
+ context.transcript.push(data);
447
+
448
+ set_context({
449
+ remote_cert_chain: message.entries,
450
+ });
451
+
452
+ // Validate peer certificate
453
+ validatePeerCertificate();
454
+ if (context.rejectUnauthorized && !context.peerAuthorized) {
455
+ sendAlert(2, 42); // fatal, bad_certificate
456
+ return;
457
+ }
458
+
459
+ }else if(message.type=='certificate_verify'){
460
+
461
+ context.transcript.push(data);
462
+
463
+ }else if(message.type=='finished'){
464
+
465
+ set_context({
466
+ remote_finished: message.body
467
+ });
468
+
469
+ }else if(message.type=='new_session_ticket'){
470
+
471
+ // Client receives NewSessionTicket from server (post-handshake)
472
+ if(!context.isServer && context.resumption_master_secret){
473
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
474
+ let psk = derive_psk(hashName, context.resumption_master_secret, message.ticket_nonce);
475
+
476
+ ev.emit('session', {
477
+ ticket: message.ticket,
478
+ ticket_nonce: message.ticket_nonce,
479
+ psk: psk,
480
+ cipher: context.selected_cipher_suite,
481
+ lifetime: message.ticket_lifetime,
482
+ age_add: message.ticket_age_add,
483
+ maxEarlyDataSize: 0,
484
+ });
485
+ }
486
+
487
+ }else if(message.type=='key_update'){
488
+
489
+ // Peer is updating their traffic secret (we update our read key)
490
+ if(context.state==='connected' && context.selected_version === wire.TLS_VERSION.TLS1_3){
491
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
492
+ let hashLen = getHashLen(hashName);
493
+ let newRemoteSecret = hkdf_expand_label(hashName, context.remote_app_traffic_secret, 'traffic upd', new Uint8Array(0), hashLen);
494
+ context.remote_app_traffic_secret = newRemoteSecret;
495
+ ev.emit('keyUpdate', { direction: 'receive', secret: newRemoteSecret });
496
+
497
+ // If peer requested us to update too
498
+ if(message.request_update === 1){
499
+ let newLocalSecret = hkdf_expand_label(hashName, context.local_app_traffic_secret, 'traffic upd', new Uint8Array(0), hashLen);
500
+ context.local_app_traffic_secret = newLocalSecret;
501
+
502
+ // Send our KeyUpdate (not requesting back)
503
+ let ku_data = build_tls_message({ type: 'key_update', request_update: 0 });
504
+ ev.emit('message', 2, context.message_sent_seq, 'key_update', ku_data);
505
+ context.message_sent_seq++;
506
+
507
+ ev.emit('keyUpdate', { direction: 'send', secret: newLocalSecret });
508
+ }
509
+ }
510
+
511
+ }else if(message.type=='certificate_request'){
512
+
513
+ // Server is requesting a client certificate (TLS 1.3)
514
+ if(!context.isServer){
515
+ context.transcript.push(data);
516
+ context.certificateRequested = true;
517
+ context.certificateRequestContext = message.certificate_request_context || new Uint8Array(0);
518
+ context.certificateRequestSigAlgs = message.signature_algorithms || [];
519
+ ev.emit('certificateRequest', message);
520
+ }
521
+
522
+ }
523
+
524
+ }
525
+
526
+
527
+ function set_context(options){
528
+ let has_changed=false;
529
+
530
+ if (options && typeof options === 'object'){
531
+
532
+
533
+ if('local_supported_versions' in options){
534
+ if(arraysEqual(options.local_supported_versions,context.local_supported_versions)==false){
535
+ context.local_supported_versions=options.local_supported_versions;
536
+ has_changed=true;
537
+ }
538
+ }
539
+
540
+ if('local_supported_cipher_suites' in options){
541
+ if(arraysEqual(options.local_supported_cipher_suites,context.local_supported_cipher_suites)==false){
542
+ context.local_supported_cipher_suites=options.local_supported_cipher_suites;
543
+ has_changed=true;
544
+ }
545
+ }
546
+
547
+ if('local_supported_alpns' in options){
548
+ if(arraysEqual(options.local_supported_alpns,context.local_supported_alpns)==false){
549
+ context.local_supported_alpns=options.local_supported_alpns;
550
+ has_changed=true;
551
+ }
552
+ }
553
+
554
+ if('local_supported_groups' in options){
555
+ if(arraysEqual(options.local_supported_groups,context.local_supported_groups)==false){
556
+ context.local_supported_groups=options.local_supported_groups;
557
+ has_changed=true;
558
+ }
559
+ }
560
+
561
+ if('local_supported_signature_algorithms' in options){
562
+ if(arraysEqual(options.local_supported_signature_algorithms,context.local_supported_signature_algorithms)==false){
563
+ context.local_supported_signature_algorithms=options.local_supported_signature_algorithms;
564
+ has_changed=true;
565
+ }
566
+ }
567
+
568
+ if('local_extensions' in options){
569
+ if(arraysEqual(options.local_extensions,context.local_extensions)==false){
570
+ context.local_extensions=options.local_extensions;
571
+ has_changed=true;
572
+ }
573
+ }
574
+
575
+ if('remote_supported_versions' in options){
576
+ if(arraysEqual(options.remote_supported_versions,context.remote_supported_versions)==false){
577
+ context.remote_supported_versions=options.remote_supported_versions;
578
+ has_changed=true;
579
+ }
580
+ }
581
+
582
+ if('remote_supported_cipher_suites' in options){
583
+ if(arraysEqual(options.remote_supported_cipher_suites,context.remote_supported_cipher_suites)==false){
584
+ context.remote_supported_cipher_suites=options.remote_supported_cipher_suites;
585
+ has_changed=true;
586
+ }
587
+ }
588
+
589
+ if('remote_supported_alpns' in options){
590
+ if(arraysEqual(options.remote_supported_alpns,context.remote_supported_alpns)==false){
591
+ context.remote_supported_alpns=options.remote_supported_alpns;
592
+ has_changed=true;
593
+ }
594
+ }
595
+
596
+ if('remote_supported_groups' in options){
597
+ if(arraysEqual(options.remote_supported_groups,context.remote_supported_groups)==false){
598
+ context.remote_supported_groups=options.remote_supported_groups;
599
+ has_changed=true;
600
+ }
601
+ }
602
+
603
+ if('remote_supported_signature_algorithms' in options){
604
+ if(arraysEqual(options.remote_supported_signature_algorithms,context.remote_supported_signature_algorithms)==false){
605
+ context.remote_supported_signature_algorithms=options.remote_supported_signature_algorithms;
606
+ has_changed=true;
607
+ }
608
+ }
609
+
610
+ if('remote_extensions' in options){
611
+ if(arraysEqual(options.remote_extensions,context.remote_extensions)==false){
612
+ context.remote_extensions=options.remote_extensions;
613
+ has_changed=true;
614
+ }
615
+ }
616
+
617
+ if('remote_sni' in options){
618
+ if(options.remote_sni!==context.remote_sni){
619
+ context.remote_sni=options.remote_sni;
620
+ has_changed=true;
621
+ }
622
+ }
623
+
624
+ if('remote_session_id' in options){
625
+ if(!uint8Equal(options.remote_session_id, context.remote_session_id)){
626
+ context.remote_session_id=options.remote_session_id;
627
+ has_changed=true;
628
+ }
629
+ }
630
+
631
+ if('remote_random' in options){
632
+ if(!uint8Equal(options.remote_random, context.remote_random)){
633
+ context.remote_random=options.remote_random;
634
+ has_changed=true;
635
+ }
636
+ }
637
+
638
+
639
+
640
+
641
+ if('add_local_key_groups' in options){
642
+ for(let i in options['add_local_key_groups']){
643
+
644
+ let group=options['add_local_key_groups'][i].group;
645
+ if(group in context.local_key_groups==false){
646
+ context.local_key_groups[group]={
647
+ public_key: null,
648
+ private_key: null
649
+ };
650
+ has_changed=true;
651
+ }
652
+
653
+ if(context.local_key_groups[group].public_key==null && options['add_local_key_groups'][i].public_key!==null){
654
+ context.local_key_groups[group].public_key=options['add_local_key_groups'][i].public_key;
655
+ has_changed=true;
656
+ }
657
+
658
+ if(context.local_key_groups[group].private_key==null && options['add_local_key_groups'][i].private_key!==null){
659
+ context.local_key_groups[group].private_key=options['add_local_key_groups'][i].private_key;
660
+ has_changed=true;
661
+
662
+ if(context.local_supported_groups.indexOf(Number(group))<0){
663
+ context.local_supported_groups.push(Number(group));
664
+ }
665
+ }
666
+
667
+ }
668
+ }
669
+
670
+
671
+ if('add_remote_key_groups' in options){
672
+ for(let i in options['add_remote_key_groups']){
673
+
674
+ let group=options['add_remote_key_groups'][i].group;
675
+ if(group in context.remote_key_groups==false){
676
+ context.remote_key_groups[group]={
677
+ public_key: null,
678
+ };
679
+ has_changed=true;
680
+ }
681
+
682
+ if(context.remote_key_groups[group].public_key==null && options['add_remote_key_groups'][i].public_key!==null){
683
+ context.remote_key_groups[group].public_key=options['add_remote_key_groups'][i].public_key;
684
+ has_changed=true;
685
+
686
+ if(context.remote_supported_groups.indexOf(Number(group))<0){
687
+ context.remote_supported_groups.push(Number(group));
688
+ }
689
+ }
690
+
691
+ }
692
+ }
693
+
694
+ if('remote_cert_chain' in options){
695
+ if(context.remote_cert_chain==null || arraysEqual(options.remote_cert_chain,context.remote_cert_chain)==false){
696
+ context.remote_cert_chain=options.remote_cert_chain;
697
+ has_changed=true;
698
+ }
699
+ }
700
+
701
+ if('remote_hello_done' in options){
702
+ if(options.remote_hello_done!==context.remote_hello_done){
703
+ context.remote_hello_done=options.remote_hello_done;
704
+ has_changed=true;
705
+ }
706
+ }
707
+
708
+ if('key_exchange_sent' in options){
709
+ if(options.key_exchange_sent!==context.key_exchange_sent){
710
+ context.key_exchange_sent=options.key_exchange_sent;
711
+ has_changed=true;
712
+ }
713
+ }
714
+
715
+
716
+ //selected stuff...
717
+
718
+ if('selected_version' in options){
719
+ if(options.selected_version!==context.selected_version){
720
+ context.selected_version=options.selected_version;
721
+ has_changed=true;
722
+ }
723
+ }
724
+
725
+ if('selected_cipher_suite' in options){
726
+ if(options.selected_cipher_suite!==context.selected_cipher_suite){
727
+ context.selected_cipher_suite=options.selected_cipher_suite;
728
+ has_changed=true;
729
+ }
730
+ }
731
+
732
+ if('selected_alpn' in options){
733
+ if(options.selected_alpn!==context.selected_alpn){
734
+ context.selected_alpn=options.selected_alpn;
735
+ has_changed=true;
736
+ }
737
+ }
738
+
739
+ if('selected_group' in options){
740
+ if(options.selected_group!==context.selected_group){
741
+ context.selected_group=options.selected_group;
742
+ has_changed=true;
743
+ }
744
+ }
745
+
746
+ if('selected_signature_algorithm' in options){
747
+ if(options.selected_signature_algorithm!==context.selected_signature_algorithm){
748
+ context.selected_signature_algorithm=options.selected_signature_algorithm;
749
+ has_changed=true;
750
+ }
751
+ }
752
+
753
+ if('selected_extensions' in options){
754
+ if(arraysEqual(options.selected_extensions,context.selected_extensions)==false){
755
+ context.selected_extensions=options.selected_extensions;
756
+ has_changed=true;
757
+ }
758
+ }
759
+
760
+ if('selected_sni' in options){
761
+ if(options.selected_sni!==context.selected_sni){
762
+ context.selected_sni=options.selected_sni;
763
+ has_changed=true;
764
+ }
765
+ }
766
+
767
+ if('selected_session_id' in options){
768
+ if(!uint8Equal(options.selected_session_id, context.selected_session_id)){
769
+ context.selected_session_id=options.selected_session_id;
770
+ has_changed=true;
771
+ }
772
+ }
773
+
774
+ if('ecdhe_shared_secret' in options){
775
+ if(context.ecdhe_shared_secret==null && options.ecdhe_shared_secret!==null){
776
+ context.ecdhe_shared_secret=options.ecdhe_shared_secret;
777
+ has_changed=true;
778
+ }
779
+ }
780
+
781
+ if('base_secret' in options){
782
+ // base_secret transitions: null → handshake_secret → null (after app secrets derived)
783
+ if(options.base_secret !== context.base_secret){
784
+ context.base_secret=options.base_secret;
785
+ has_changed=true;
786
+ }
787
+ }
788
+
789
+ if('tls13_master_secret' in options){
790
+ if(context.tls13_master_secret==null && options.tls13_master_secret!==null){
791
+ context.tls13_master_secret=options.tls13_master_secret;
792
+ has_changed=true;
793
+ }
794
+ }
795
+
796
+
797
+ if('remote_handshake_traffic_secret' in options){
798
+ if(context.remote_handshake_traffic_secret==null && options.remote_handshake_traffic_secret!==null){
799
+ context.remote_handshake_traffic_secret=options.remote_handshake_traffic_secret;
800
+ has_changed=true;
801
+ if(context.local_handshake_traffic_secret!==null){
802
+ ev.emit('handshakeSecrets', context.local_handshake_traffic_secret, context.remote_handshake_traffic_secret);
803
+ }
804
+ }
805
+ }
806
+
807
+ if('local_handshake_traffic_secret' in options){
808
+ if(context.local_handshake_traffic_secret==null && options.local_handshake_traffic_secret!==null){
809
+ context.local_handshake_traffic_secret=options.local_handshake_traffic_secret;
810
+ has_changed=true;
811
+ if(context.remote_handshake_traffic_secret!==null){
812
+ ev.emit('handshakeSecrets', context.local_handshake_traffic_secret, context.remote_handshake_traffic_secret);
813
+ }
814
+ }
815
+ }
816
+
817
+ if('remote_app_traffic_secret' in options){
818
+ if(context.remote_app_traffic_secret==null && options.remote_app_traffic_secret!==null){
819
+ context.remote_app_traffic_secret=options.remote_app_traffic_secret;
820
+ has_changed=true;
821
+ if(context.local_app_traffic_secret!==null){
822
+ ev.emit('appSecrets', context.local_app_traffic_secret, context.remote_app_traffic_secret);
823
+ }
824
+ }
825
+ }
826
+
827
+ if('local_app_traffic_secret' in options){
828
+ if(context.local_app_traffic_secret==null && options.local_app_traffic_secret!==null){
829
+ context.local_app_traffic_secret=options.local_app_traffic_secret;
830
+ has_changed=true;
831
+ if(context.remote_app_traffic_secret!==null){
832
+ ev.emit('appSecrets', context.local_app_traffic_secret, context.remote_app_traffic_secret);
833
+ }
834
+ }
835
+ }
836
+
837
+
838
+
839
+ if('local_cert_chain' in options){
840
+ if(context.local_cert_chain==null && options.local_cert_chain!==null){
841
+ context.local_cert_chain=options.local_cert_chain;
842
+ has_changed=true;
843
+ }
844
+ }
845
+
846
+ if('cert_private_key' in options){
847
+ if(context.cert_private_key==null && options.cert_private_key!==null){
848
+ context.cert_private_key=options.cert_private_key;
849
+ has_changed=true;
850
+ }
851
+ }
852
+
853
+ if('expected_remote_finished' in options){
854
+ if(context.expected_remote_finished==null && options.expected_remote_finished!==null){
855
+ context.expected_remote_finished=options.expected_remote_finished;
856
+ has_changed=true;
857
+ }
858
+ }
859
+
860
+ if('remote_finished' in options){
861
+ if(context.remote_finished==null && options.remote_finished!==null){
862
+ context.remote_finished=options.remote_finished;
863
+ has_changed=true;
864
+ }
865
+ }
866
+
867
+ if('remote_finished_ok' in options){
868
+ if(context.remote_finished_ok!==options.remote_finished_ok){
869
+ context.remote_finished_ok=options.remote_finished_ok;
870
+ has_changed=true;
871
+ }
872
+ }
873
+
874
+
875
+
876
+
877
+ }
878
+
879
+
880
+ if(has_changed==true){
881
+
882
+ let params_to_set = {};
883
+
884
+
885
+
886
+
887
+
888
+
889
+ //select version...
890
+ if (context.selected_version == null && context.local_supported_versions.length > 0 && context.remote_supported_versions.length > 0) {
891
+ for (let i = 0; i < context.local_supported_versions.length; i++) {
892
+ let v = context.local_supported_versions[i] | 0;
893
+ for (let j = 0; j < context.remote_supported_versions.length; j++) {
894
+ if ((context.remote_supported_versions[j] | 0) == v) {
895
+ params_to_set['selected_version'] = v;
896
+ break;
897
+ }
898
+ }
899
+ if ('selected_version' in params_to_set==true && params_to_set.selected_version !== null) break;
900
+ }
901
+
902
+ if('selected_version' in params_to_set==false || params_to_set.selected_version==null){
903
+ }
904
+ }
905
+
906
+ //select selected_cipher...
907
+ if (context.selected_cipher_suite == null && context.local_supported_cipher_suites.length > 0 && context.remote_supported_cipher_suites.length > 0) {
908
+
909
+ for (let i2 = 0; i2 < context.local_supported_cipher_suites.length; i2++) {
910
+ let cs = context.local_supported_cipher_suites[i2] | 0;
911
+ for (let j2 = 0; j2 < context.remote_supported_cipher_suites.length; j2++) {
912
+
913
+ if ((context.remote_supported_cipher_suites[j2] | 0) == cs) {
914
+ params_to_set['selected_cipher_suite'] = cs;
915
+ break;
916
+ }
917
+ }
918
+ if ('selected_cipher_suite' in params_to_set==true && params_to_set.selected_cipher_suite !== null) break;
919
+ }
920
+
921
+ if('selected_cipher_suite' in params_to_set==false || params_to_set.selected_cipher_suite==null){
922
+ }
923
+ }
924
+
925
+ //select alpn...
926
+ if (context.selected_alpn == null && context.local_supported_alpns && context.remote_supported_alpns) {
927
+ // iterate local list by preference order
928
+ for (let a = 0; a < context.local_supported_alpns.length; a++) {
929
+ let cand = context.local_supported_alpns[a];
930
+ for (let b = 0; b < context.remote_supported_alpns.length; b++) {
931
+ if (context.remote_supported_alpns[b] === cand) {
932
+ params_to_set['selected_alpn'] = cand;
933
+ break;
934
+ }
935
+ }
936
+ if ('selected_alpn' in params_to_set==true && params_to_set.selected_alpn !== null) break;
937
+ }
938
+ }
939
+
940
+ //select sni...
941
+ if (context.selected_sni == null && context.remote_sni!==null) {
942
+ params_to_set['selected_sni'] = context.remote_sni || null;
943
+ }
944
+
945
+ //select selected_session_id... (tls 1.2 only)
946
+ if (context.selected_session_id == null) {
947
+ params_to_set['selected_session_id'] = context.remote_session_id || new Uint8Array(0);
948
+ }
949
+
950
+
951
+ //select group...
952
+ if (context.selected_group == null){
953
+ if(context.local_supported_groups.length > 0 && context.remote_supported_groups.length > 0) {
954
+ for (let i = 0; i < context.local_supported_groups.length; i++) {
955
+ if(context.remote_supported_groups.indexOf(context.local_supported_groups[i])>=0){
956
+ params_to_set['selected_group'] = context.local_supported_groups[i];
957
+ break;
958
+ }
959
+ }
960
+ }
961
+ }
962
+
963
+
964
+
965
+ //create the key by the group if dont have...
966
+ if(context.selected_group !== null && context.selected_group in context.local_key_groups==false){
967
+
968
+ if (context.selected_group === 0x001d) {
969
+
970
+ const private_key = new Uint8Array(crypto.randomBytes(32));
971
+ let public_key = x25519_get_public_key(private_key);
972
+
973
+ params_to_set['add_local_key_groups']=[
974
+ {
975
+ group: context.selected_group,
976
+ private_key: private_key,
977
+ public_key: public_key
978
+ }
979
+ ];
980
+
981
+ } else if (context.selected_group === 0x0017) {
982
+
983
+ let kp = p256_generate_keypair();
984
+ let private_key = kp.private_key;
985
+ let public_key = kp.public_key;
986
+
987
+ params_to_set['add_local_key_groups']=[
988
+ {
989
+ group: context.selected_group,
990
+ private_key: private_key,
991
+ public_key: public_key
992
+ }
993
+ ];
994
+
995
+ } else if (context.selected_group === 0x0018) {
996
+
997
+ let kp = p384_generate_keypair();
998
+ let private_key = kp.private_key;
999
+ let public_key = kp.public_key;
1000
+
1001
+ params_to_set['add_local_key_groups']=[
1002
+ {
1003
+ group: context.selected_group,
1004
+ private_key: private_key,
1005
+ public_key: public_key
1006
+ }
1007
+ ];
1008
+
1009
+ }
1010
+
1011
+
1012
+ }
1013
+
1014
+ //get shared_secret...
1015
+ if(context.selected_group !== null && context.ecdhe_shared_secret == null && context.selected_group in context.local_key_groups==true && context.selected_group in context.remote_key_groups==true){
1016
+
1017
+ //check we have remote public key and local private key...
1018
+ if(context.remote_key_groups[context.selected_group].public_key!==null && context.local_key_groups[context.selected_group].private_key!==null){
1019
+
1020
+ let remote_public_key=context.remote_key_groups[context.selected_group].public_key;
1021
+ let local_private_key=context.local_key_groups[context.selected_group].private_key;
1022
+
1023
+ if (context.selected_group === 0x001d) { // X25519
1024
+
1025
+ let ecdhe_shared_secret = x25519_get_shared_secret(local_private_key, remote_public_key);
1026
+
1027
+ params_to_set['ecdhe_shared_secret']=ecdhe_shared_secret;
1028
+
1029
+ } else if (context.selected_group === 0x0017) { // secp256r1 (P-256)
1030
+
1031
+ let ecdhe_shared_secret = p256_get_shared_secret(local_private_key, remote_public_key);
1032
+
1033
+ params_to_set['ecdhe_shared_secret']=ecdhe_shared_secret;
1034
+
1035
+ } else if (context.selected_group === 0x0018) { // secp384r1 (P-384)
1036
+
1037
+ let ecdhe_shared_secret = p384_get_shared_secret(local_private_key, remote_public_key);
1038
+
1039
+ params_to_set['ecdhe_shared_secret']=ecdhe_shared_secret;
1040
+
1041
+ }
1042
+
1043
+ }
1044
+
1045
+ }
1046
+
1047
+
1048
+
1049
+
1050
+ if(context.isServer==true){
1051
+
1052
+ // HelloRetryRequest: if we selected a group but client didn't send a key_share for it
1053
+ if(context.hello_sent==false && !context.helloRetried && context.selected_version === wire.TLS_VERSION.TLS1_3 &&
1054
+ context.selected_group !== null && context.selected_cipher_suite !== null &&
1055
+ !(context.selected_group in context.remote_key_groups)){
1056
+
1057
+ context.helloRetried = true;
1058
+
1059
+ // Replace transcript with message_hash (RFC 8446 §4.4.1)
1060
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
1061
+ let ch1_hash = getHashFn(hashName)(concatUint8Arrays(context.transcript));
1062
+ let message_hash = wire.build_message(wire.TLS_MESSAGE_TYPE.MESSAGE_HASH, ch1_hash);
1063
+ context.transcript = [message_hash];
1064
+
1065
+ // Build and send HRR (it's a ServerHello with magic random)
1066
+ let hrr_body = wire.build_hello_retry_request({
1067
+ cipher_suite: context.selected_cipher_suite,
1068
+ selected_version: wire.TLS_VERSION.TLS1_3,
1069
+ selected_group: context.selected_group,
1070
+ session_id: context.remote_session_id,
1071
+ });
1072
+ let hrr_data = wire.build_message(wire.TLS_MESSAGE_TYPE.SERVER_HELLO, hrr_body);
1073
+ context.transcript.push(hrr_data);
1074
+
1075
+ ev.emit('message', 0, context.message_sent_seq, 'hello_retry_request', hrr_data);
1076
+ context.message_sent_seq++;
1077
+
1078
+ // Reset for second ClientHello
1079
+ context.remote_random = null;
1080
+ context.remote_extensions = [];
1081
+ context.remote_supported_versions = [];
1082
+ context.remote_supported_cipher_suites = [];
1083
+ context.remote_supported_signature_algorithms = [];
1084
+
1085
+ // Don't proceed to ServerHello — wait for new ClientHello
1086
+ }
1087
+
1088
+ let can_send_hello=false;
1089
+
1090
+ if(context.hello_sent==false){
1091
+
1092
+ if(context.selected_version!==null && context.selected_cipher_suite!==null && context.selected_session_id!==null){
1093
+ if(context.selected_version === wire.TLS_VERSION.TLS1_3){
1094
+ if(context.selected_group in context.local_key_groups==true && context.local_key_groups[context.selected_group].public_key!==null){
1095
+ // After HRR, don't send ServerHello until CH2 provides the requested key_share
1096
+ if (!context.helloRetried || (context.selected_group in context.remote_key_groups)) {
1097
+ can_send_hello=true;
1098
+ }
1099
+ }
1100
+ }else if(context.selected_version === wire.TLS_VERSION.TLS1_2){
1101
+ can_send_hello=true;
1102
+ }
1103
+ }
1104
+ }
1105
+
1106
+ if(can_send_hello==true){
1107
+
1108
+ if(context.local_random==null){
1109
+ context.local_random=new Uint8Array(crypto.randomBytes(32));
1110
+ }
1111
+
1112
+ let build_message_params=null;
1113
+
1114
+ if(context.selected_version==wire.TLS_VERSION.TLS1_3){
1115
+
1116
+ let shExtensions = [
1117
+ {
1118
+ type: 'SUPPORTED_VERSIONS',
1119
+ value: wire.TLS_VERSION.TLS1_3
1120
+ },
1121
+ {
1122
+ type: 'KEY_SHARE',
1123
+ value: {
1124
+ group: context.selected_group,
1125
+ key_exchange: context.local_key_groups[context.selected_group].public_key
1126
+ }
1127
+ }
1128
+ ];
1129
+
1130
+ // PSK accepted → include PRE_SHARED_KEY with selected identity index
1131
+ if (context.psk_accepted) {
1132
+ shExtensions.push({ type: 'PRE_SHARED_KEY', value: { selected: 0 } });
1133
+ }
1134
+
1135
+ build_message_params={
1136
+ type: 'server_hello',
1137
+ version: context.selected_version,
1138
+ random: context.local_random,
1139
+ session_id: context.remote_session_id,
1140
+ cipher_suite: context.selected_cipher_suite,
1141
+ extensions: shExtensions
1142
+ };
1143
+
1144
+
1145
+ }else if(context.selected_version==wire.TLS_VERSION.TLS1_2){
1146
+
1147
+
1148
+ // TLS 1.2 ServerHello: no SUPPORTED_VERSIONS or KEY_SHARE.
1149
+ // Include renegotiation_info (empty for initial handshake) and extended_master_secret.
1150
+ // ALPN (type=16) — optional, echoes selected protocol.
1151
+
1152
+ let ext_list = [
1153
+ { type: 'RENEGOTIATION_INFO', value: new Uint8Array(0) }
1154
+ ];
1155
+
1156
+ // Only echo extended_master_secret if client sent it
1157
+ if (context.use_extended_master_secret) {
1158
+ ext_list.push({ type: 23, data: new Uint8Array(0) });
1159
+ }
1160
+
1161
+ if (context.alpn_selected) {
1162
+ // RFC 7301: ServerHello echoes a single selected protocol
1163
+ ext_list.push({ type: 'ALPN', value: [ String(context.alpn_selected) ] });
1164
+ }
1165
+
1166
+ build_message_params = {
1167
+ type: 'server_hello',
1168
+ version: context.selected_version,
1169
+ random: context.local_random,
1170
+ session_id: context.remote_session_id || new Uint8Array(0), // echo client session_id
1171
+ cipher_suite: context.selected_cipher_suite, // e.g. 0xC02F
1172
+ // compression_method always 0
1173
+ extensions: ext_list
1174
+ };
1175
+
1176
+
1177
+
1178
+
1179
+
1180
+ }
1181
+
1182
+ if(build_message_params!==null){
1183
+
1184
+
1185
+ let message_data = build_tls_message(build_message_params);
1186
+
1187
+ context.transcript.push(message_data);
1188
+
1189
+ context.hello_sent=true;
1190
+
1191
+ ev.emit('message',0,context.message_sent_seq,'hello',message_data);
1192
+
1193
+ context.message_sent_seq++;
1194
+ }
1195
+ }
1196
+
1197
+ }else{
1198
+
1199
+ }
1200
+
1201
+
1202
+
1203
+
1204
+
1205
+ //get base_secret
1206
+ if (context.base_secret==null && context.selected_cipher_suite !== null){
1207
+ if(context.selected_version == wire.TLS_VERSION.TLS1_3 && (context.ecdhe_shared_secret !== null)){
1208
+
1209
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
1210
+ let result;
1211
+ if (context.psk_accepted && context.psk_offered && context.psk_offered.psk) {
1212
+ // PSK + ECDHE key schedule
1213
+ result = derive_handshake_traffic_secrets_psk(hashName, context.psk_offered.psk, context.ecdhe_shared_secret, concatUint8Arrays(context.transcript));
1214
+ } else {
1215
+ // Standard key schedule (no PSK)
1216
+ result = derive_handshake_traffic_secrets(hashName, context.ecdhe_shared_secret, concatUint8Arrays(context.transcript));
1217
+ }
1218
+
1219
+ params_to_set['base_secret']=result.handshake_secret;
1220
+
1221
+ if(context.isServer==true){
1222
+ params_to_set['remote_handshake_traffic_secret']=result.client_handshake_traffic_secret;
1223
+ params_to_set['local_handshake_traffic_secret']=result.server_handshake_traffic_secret;
1224
+ }else{
1225
+ params_to_set['local_handshake_traffic_secret']=result.client_handshake_traffic_secret;
1226
+ params_to_set['remote_handshake_traffic_secret']=result.server_handshake_traffic_secret;
1227
+ }
1228
+
1229
+ }else if(context.selected_version === wire.TLS_VERSION.TLS1_2 && context.local_random!==null && context.remote_random!==null){
1230
+ if(context.ecdhe_shared_secret !== null){
1231
+
1232
+
1233
+ let server_random, client_random;
1234
+ if(context.isServer==true){
1235
+ server_random=context.local_random;
1236
+ client_random=context.remote_random;
1237
+ }else{
1238
+ server_random=context.remote_random;
1239
+ client_random=context.local_random;
1240
+ }
1241
+
1242
+ if(context.use_extended_master_secret){
1243
+ // RFC 7627: extended master secret uses transcript hash through ClientKeyExchange.
1244
+ // Server: CKE just arrived, transcript is complete.
1245
+ // Client: must wait until CKE is sent and in transcript.
1246
+ if(context.isServer || context.key_exchange_sent){
1247
+ let hashFn = getHashFn(TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1248
+
1249
+ let transcript_hash = hashFn(concatUint8Arrays(context.transcript));
1250
+
1251
+ let master_secret = tls12_prf(context.ecdhe_shared_secret, "extended master secret", transcript_hash, 48, TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1252
+
1253
+ params_to_set['base_secret']=master_secret;
1254
+ }
1255
+ }else{
1256
+ let master_secret = tls12_prf(context.ecdhe_shared_secret, "master secret", concatUint8Arrays([client_random, server_random]), 48, TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1257
+
1258
+ params_to_set['base_secret']=master_secret;
1259
+ }
1260
+
1261
+
1262
+
1263
+
1264
+
1265
+
1266
+ }
1267
+ }
1268
+ }
1269
+
1270
+
1271
+
1272
+ //send encrypted_extensions...
1273
+ if (context.isServer==true && context.selected_version === wire.TLS_VERSION.TLS1_3){
1274
+ if(context.encrypted_exts_sent==false && context.hello_sent==true && context.local_handshake_traffic_secret!==null){
1275
+
1276
+ let extensions=[];
1277
+ if(context.selected_alpn!==null){
1278
+ extensions.push({ type: 'ALPN', value: [context.selected_alpn] });
1279
+ }
1280
+
1281
+
1282
+ for(let i in context.local_extensions){
1283
+ extensions.push(context.local_extensions[i]);
1284
+ }
1285
+
1286
+ let message_data = build_tls_message({
1287
+ type: 'encrypted_extensions',
1288
+ extensions: extensions
1289
+ });
1290
+
1291
+ context.transcript.push(message_data);
1292
+
1293
+ context.encrypted_exts_sent=true;
1294
+
1295
+ ev.emit('message',1,context.message_sent_seq,'encrypted_extensions',message_data);
1296
+
1297
+ context.message_sent_seq++;
1298
+
1299
+ }
1300
+ }
1301
+
1302
+
1303
+ //send certificate... (skip for PSK resumption — no cert needed)
1304
+ // But first: send CertificateRequest if requestCert is set (TLS 1.3 only, between EE and Cert)
1305
+ if(context.isServer==true && context.requestCert==true && !context.certificateRequestSent && context.encrypted_exts_sent==true && context.local_handshake_traffic_secret!==null && context.selected_version === wire.TLS_VERSION.TLS1_3 && !context.psk_accepted){
1306
+ let cr_data = build_tls_message({
1307
+ type: 'certificate_request',
1308
+ certificate_request_context: new Uint8Array(0),
1309
+ signature_algorithms: context.local_supported_signature_algorithms,
1310
+ });
1311
+ context.transcript.push(cr_data);
1312
+ context.certificateRequestSent = true;
1313
+ ev.emit('message', 1, context.message_sent_seq, 'certificate_request', cr_data);
1314
+ context.message_sent_seq++;
1315
+ }
1316
+
1317
+ if(context.isServer==true && context.cert_sent==false && context.local_cert_chain!==null && !context.psk_accepted){
1318
+ if((context.selected_version === wire.TLS_VERSION.TLS1_3 && context.encrypted_exts_sent==true && context.local_handshake_traffic_secret!==null) || (context.selected_version === wire.TLS_VERSION.TLS1_2 && context.hello_sent==true)){
1319
+
1320
+ let message_data = build_tls_message({
1321
+ type: 'certificate',
1322
+ version: context.selected_version,
1323
+ entries: context.local_cert_chain
1324
+ });
1325
+ context.transcript.push(message_data);
1326
+
1327
+ context.cert_sent=true;
1328
+
1329
+ if (context.selected_version === wire.TLS_VERSION.TLS1_3){
1330
+ ev.emit('message',1,context.message_sent_seq,'certificate',message_data);
1331
+ }else{
1332
+ ev.emit('message',0,context.message_sent_seq,'certificate',message_data);
1333
+ }
1334
+
1335
+ context.message_sent_seq++;
1336
+
1337
+
1338
+ }
1339
+ }
1340
+
1341
+
1342
+
1343
+
1344
+
1345
+
1346
+ //send certificate verify...
1347
+ if (context.isServer==true && context.selected_version === wire.TLS_VERSION.TLS1_3){
1348
+ if(context.cert_sent==true && context.cert_verify_sent==false && context.local_cert_chain!==null && context.local_handshake_traffic_secret!==null && context.selected_cipher_suite!==null){
1349
+
1350
+ let tbs_data = build_cert_verify_tbs(TLS_CIPHER_SUITES[context.selected_cipher_suite].hash,true,concatUint8Arrays(context.transcript));
1351
+
1352
+ let cert_private_key_obj = crypto.createPrivateKey({
1353
+ key: Buffer.from(context.cert_private_key),
1354
+ format: 'der',
1355
+ type: 'pkcs8',
1356
+ });
1357
+
1358
+ const SIG = {
1359
+ ECDSA_P256_SHA256: 0x0403,
1360
+ ECDSA_P384_SHA384: 0x0503,
1361
+ ECDSA_P521_SHA512: 0x0603,
1362
+ RSA_PSS_SHA256: 0x0804,
1363
+ RSA_PSS_SHA384: 0x0805,
1364
+ RSA_PSS_SHA512: 0x0806,
1365
+ ED25519: 0x0807,
1366
+ ED448: 0x0808
1367
+ };
1368
+
1369
+ let candidates=[];
1370
+ if (cert_private_key_obj.asymmetricKeyType === 'ed25519') candidates.push(SIG.ED25519);
1371
+ if (cert_private_key_obj.asymmetricKeyType === 'ed448') candidates.push(SIG.ED448);
1372
+ if (cert_private_key_obj.asymmetricKeyType === 'rsa') candidates.push(SIG.RSA_PSS_SHA256, SIG.RSA_PSS_SHA384, SIG.RSA_PSS_SHA512); // TLS 1.3: PSS only
1373
+
1374
+ if (cert_private_key_obj.asymmetricKeyType === 'ec') {
1375
+ let c = (cert_private_key_obj.asymmetricKeyDetails && cert_private_key_obj.asymmetricKeyDetails && cert_private_key_obj.asymmetricKeyDetails.namedCurve) || '';
1376
+ if (c === 'prime256v1') candidates.push(SIG.ECDSA_P256_SHA256);
1377
+ if (c === 'secp384r1') candidates.push(SIG.ECDSA_P384_SHA384);
1378
+ if (c === 'secp521r1') candidates.push(SIG.ECDSA_P521_SHA512);
1379
+ }
1380
+
1381
+
1382
+ let preference_order = [
1383
+ SIG.ED25519,
1384
+ SIG.ED448,
1385
+ SIG.ECDSA_P256_SHA256,
1386
+ SIG.ECDSA_P384_SHA384,
1387
+ SIG.ECDSA_P521_SHA512,
1388
+ SIG.RSA_PSS_SHA256,
1389
+ SIG.RSA_PSS_SHA384,
1390
+ SIG.RSA_PSS_SHA512
1391
+ ];
1392
+
1393
+ let selected_scheme = null;
1394
+ for (let s of preference_order) {
1395
+ if (context.remote_supported_signature_algorithms.includes(s)==true && candidates.includes(s)==true) {
1396
+ selected_scheme = s;
1397
+ break;
1398
+ }
1399
+ }
1400
+
1401
+ let sig_data=null;
1402
+
1403
+ switch (selected_scheme) {
1404
+ case SIG.ED25519:
1405
+ sig_data = new Uint8Array(crypto.sign(null, tbs_data, cert_private_key_obj));
1406
+ break;
1407
+
1408
+ case SIG.ECDSA_P256_SHA256:
1409
+ sig_data = new Uint8Array(crypto.sign('sha256', tbs_data, cert_private_key_obj));
1410
+ break;
1411
+
1412
+ case SIG.ECDSA_P384_SHA384:
1413
+ sig_data = new Uint8Array(crypto.sign('sha384', tbs_data, cert_private_key_obj));
1414
+ break;
1415
+
1416
+ case SIG.ECDSA_P521_SHA512:
1417
+ sig_data = new Uint8Array(crypto.sign('sha512', tbs_data, cert_private_key_obj));
1418
+ break;
1419
+
1420
+ case SIG.RSA_PSS_SHA256:
1421
+ sig_data = new Uint8Array(crypto.sign('sha256', tbs_data, {
1422
+ key: cert_private_key_obj,
1423
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
1424
+ saltLength: 32
1425
+ }));
1426
+ break;
1427
+
1428
+ case SIG.RSA_PSS_SHA384:
1429
+ sig_data = new Uint8Array(crypto.sign('sha384', tbs_data, {
1430
+ key: cert_private_key_obj,
1431
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
1432
+ saltLength: 48
1433
+ }));
1434
+ break;
1435
+
1436
+ case SIG.RSA_PSS_SHA512:
1437
+ sig_data = new Uint8Array(crypto.sign('sha512', tbs_data, {
1438
+ key: cert_private_key_obj,
1439
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
1440
+ saltLength: 64
1441
+ }));
1442
+ break;
1443
+ }
1444
+
1445
+
1446
+
1447
+ if(sig_data){
1448
+
1449
+ let message_data = build_tls_message({
1450
+ type: 'certificate_verify',
1451
+ scheme: selected_scheme,
1452
+ signature: sig_data
1453
+ });
1454
+
1455
+
1456
+
1457
+ context.transcript.push(message_data);
1458
+
1459
+ context.cert_verify_sent=true;
1460
+
1461
+ ev.emit('message',1,context.message_sent_seq,'certificate_verify',message_data);
1462
+
1463
+ context.message_sent_seq++;
1464
+ }else{
1465
+
1466
+ //..
1467
+ }
1468
+
1469
+
1470
+
1471
+
1472
+ }
1473
+ }
1474
+
1475
+
1476
+
1477
+
1478
+
1479
+ // client/server key exchange - 1.2 only...
1480
+ if (context.key_exchange_sent == false && context.selected_version == wire.TLS_VERSION.TLS1_2) {
1481
+ if(context.selected_group!==null && context.selected_group in context.local_key_groups==true && context.local_key_groups[context.selected_group].public_key!==null){
1482
+
1483
+ if (context.isServer==false && context.remote_hello_done==true) {
1484
+
1485
+ let public_key = context.local_key_groups[context.selected_group].public_key;
1486
+
1487
+ let message_data = build_tls_message({
1488
+ type: 'client_key_exchange',
1489
+ public_key: public_key,
1490
+ });
1491
+ context.transcript.push(message_data);
1492
+
1493
+ // Set via params_to_set to trigger re-evaluation (EMS needs this)
1494
+ params_to_set['key_exchange_sent'] = true;
1495
+
1496
+ ev.emit('message', 0, context.message_sent_seq, 'client_key_exchange', message_data);
1497
+
1498
+ context.message_sent_seq++;
1499
+
1500
+
1501
+ }else if (context.isServer==true && context.cert_sent == true) {
1502
+
1503
+ // Build ServerECDHParams + sign (curve_type | namedcurve | ec_point)
1504
+ // curve_type=3 (named_curve)
1505
+
1506
+ let public_key = context.local_key_groups[context.selected_group].public_key;
1507
+
1508
+ let params_head = wire.build_server_ecdh_params(context.selected_group,public_key);
1509
+
1510
+ let tbs_data = concatUint8Arrays([ context.remote_random, context.local_random, params_head ]);
1511
+
1512
+
1513
+ let cert_private_key_obj = crypto.createPrivateKey({
1514
+ key: Buffer.from(context.cert_private_key),
1515
+ format: 'der',
1516
+ type: 'pkcs8'
1517
+ });
1518
+
1519
+ let scheme12 = pick_scheme(wire.TLS_VERSION.TLS1_2, cert_private_key_obj, context.remote_supported_signature_algorithms);
1520
+
1521
+ let sig_data = sign_with_scheme(wire.TLS_VERSION.TLS1_2, scheme12, tbs_data, cert_private_key_obj);
1522
+
1523
+
1524
+ let message_data = build_tls_message({
1525
+ type: 'server_key_exchange',
1526
+ group: context.selected_group,
1527
+ public_key: public_key,
1528
+ sig_alg: scheme12,
1529
+ signature: sig_data
1530
+ });
1531
+ context.transcript.push(message_data);
1532
+
1533
+ context.key_exchange_sent = true;
1534
+
1535
+ ev.emit('message', 0, context.message_sent_seq, 'server_key_exchange', message_data);
1536
+
1537
+ context.message_sent_seq++;
1538
+
1539
+ }
1540
+ }
1541
+ }
1542
+
1543
+ //server hello done - 1.2 only...
1544
+ if(context.isServer==true && context.selected_version == wire.TLS_VERSION.TLS1_2){
1545
+ if(context.hello_done_sent==false && context.key_exchange_sent==true){
1546
+
1547
+ let message_data = build_tls_message({
1548
+ type: 'server_hello_done'});
1549
+ context.transcript.push(message_data);
1550
+
1551
+ context.hello_done_sent=true;
1552
+
1553
+ ev.emit('message',0,context.message_sent_seq,'certificate',message_data);
1554
+
1555
+ context.message_sent_seq++;
1556
+
1557
+ }
1558
+ }
1559
+
1560
+
1561
+
1562
+ //send finished...
1563
+ // Client: send Certificate + CertificateVerify before Finished (if server requested)
1564
+ if(context.isServer==false && context.certificateRequested && !context.clientCertSent &&
1565
+ context.remote_finished_ok==true && context.local_handshake_traffic_secret!==null){
1566
+ context.clientCertSent = true;
1567
+
1568
+ if(context.clientCert && context.clientKey){
1569
+ // Send client certificate
1570
+ let certCtx = createSecureContext({ key: context.clientKey, cert: context.clientCert });
1571
+ let cert_data = build_tls_message({
1572
+ type: 'certificate',
1573
+ version: wire.TLS_VERSION.TLS1_3,
1574
+ entries: certCtx.certificateChain,
1575
+ certificate_request_context: context.certificateRequestContext || new Uint8Array(0),
1576
+ });
1577
+ context.transcript.push(cert_data);
1578
+ ev.emit('message', 1, context.message_sent_seq, 'certificate', cert_data);
1579
+ context.message_sent_seq++;
1580
+
1581
+ // Send CertificateVerify
1582
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
1583
+ let transcript_hash = getHashFn(hashName)(concatUint8Arrays(context.transcript));
1584
+ let scheme = pick_scheme(context.certificateRequestSigAlgs.length > 0 ? context.certificateRequestSigAlgs : context.local_supported_signature_algorithms, certCtx.privateKey);
1585
+ let signature = sign_with_scheme(scheme, certCtx.privateKey, transcript_hash, false);
1586
+ let cv_data = build_tls_message({
1587
+ type: 'certificate_verify',
1588
+ scheme: scheme,
1589
+ signature: signature,
1590
+ });
1591
+ context.transcript.push(cv_data);
1592
+ ev.emit('message', 1, context.message_sent_seq, 'certificate_verify', cv_data);
1593
+ context.message_sent_seq++;
1594
+ } else {
1595
+ // No client cert — send empty certificate
1596
+ let cert_data = build_tls_message({
1597
+ type: 'certificate',
1598
+ version: wire.TLS_VERSION.TLS1_3,
1599
+ entries: [],
1600
+ certificate_request_context: context.certificateRequestContext || new Uint8Array(0),
1601
+ });
1602
+ context.transcript.push(cert_data);
1603
+ ev.emit('message', 1, context.message_sent_seq, 'certificate', cert_data);
1604
+ context.message_sent_seq++;
1605
+ }
1606
+ }
1607
+
1608
+ // Note: TLS 1.3 uses local_handshake_traffic_secret for Finished (not base_secret).
1609
+ // base_secret may be null after app secrets are derived, so we also check handshake secret.
1610
+ if (context.finished_sent==false && context.selected_cipher_suite!==null && (context.base_secret!==null || context.local_handshake_traffic_secret!==null)){
1611
+
1612
+ if(context.selected_version === wire.TLS_VERSION.TLS1_3 && context.local_handshake_traffic_secret!==null){
1613
+
1614
+ if((context.isServer==false && context.remote_finished_ok==true && context.local_app_traffic_secret!==null && context.remote_app_traffic_secret!==null) || (context.isServer==true && context.cert_verify_sent==true && context.local_cert_chain!==null) || (context.isServer==true && context.psk_accepted==true && context.encrypted_exts_sent==true)){
1615
+
1616
+ let finished_data=get_handshake_finished(TLS_CIPHER_SUITES[context.selected_cipher_suite].hash,context.local_handshake_traffic_secret,concatUint8Arrays(context.transcript));
1617
+ context.local_finished_data = finished_data;
1618
+
1619
+ let message_data = build_tls_message({
1620
+ type: 'finished',
1621
+ data: finished_data
1622
+ });
1623
+
1624
+ context.transcript.push(message_data);
1625
+
1626
+ context.finished_sent=true;
1627
+
1628
+ ev.emit('message',1,context.message_sent_seq,'finished',message_data);
1629
+
1630
+ context.message_sent_seq++;
1631
+
1632
+ }
1633
+
1634
+ }else if(context.selected_version === wire.TLS_VERSION.TLS1_2){
1635
+
1636
+ if((context.isServer==true && context.remote_finished_ok==true) || (context.isServer==false && context.key_exchange_sent==true)){
1637
+
1638
+ let hashFn = getHashFn(TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1639
+ let transcript_hash = hashFn(concatUint8Arrays(context.transcript));
1640
+
1641
+ let finished_data;
1642
+ if(context.isServer==true){
1643
+ finished_data=tls12_prf(context.base_secret, "server finished", transcript_hash, 12, TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1644
+ }else{
1645
+ finished_data=tls12_prf(context.base_secret, "client finished", transcript_hash, 12, TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1646
+ }
1647
+ context.local_finished_data = finished_data;
1648
+
1649
+ let message_data = build_tls_message({
1650
+ type: 'finished',
1651
+ data: finished_data
1652
+ });
1653
+
1654
+ context.transcript.push(message_data);
1655
+
1656
+ context.finished_sent=true;
1657
+
1658
+ ev.emit('message',1,context.message_sent_seq,'finished',message_data);
1659
+
1660
+ context.message_sent_seq++;
1661
+
1662
+ }
1663
+
1664
+ }
1665
+
1666
+ }
1667
+
1668
+ //get app traffic secret...
1669
+ if (context.selected_version === wire.TLS_VERSION.TLS1_3){
1670
+ if(context.base_secret!==null && context.local_app_traffic_secret==null && context.remote_app_traffic_secret==null){
1671
+
1672
+ if((context.isServer==true && context.finished_sent==true && context.remote_finished_ok==false) || (context.isServer==false && context.finished_sent==false && context.remote_finished_ok==true)){
1673
+
1674
+ let result2 = derive_app_traffic_secrets(TLS_CIPHER_SUITES[context.selected_cipher_suite].hash, context.base_secret, concatUint8Arrays(context.transcript));
1675
+
1676
+ // Save master_secret for resumption before clearing
1677
+ params_to_set['tls13_master_secret'] = result2.master_secret;
1678
+ params_to_set['base_secret']=null;
1679
+
1680
+ if (context.isServer === true) {
1681
+ params_to_set['local_app_traffic_secret'] = result2.server_app_traffic_secret;
1682
+ params_to_set['remote_app_traffic_secret'] = result2.client_app_traffic_secret;
1683
+ } else {
1684
+ params_to_set['local_app_traffic_secret'] = result2.client_app_traffic_secret;
1685
+ params_to_set['remote_app_traffic_secret'] = result2.server_app_traffic_secret;
1686
+ }
1687
+ }
1688
+
1689
+ }
1690
+ }
1691
+
1692
+ //expected_remote_finished...
1693
+ if (context.expected_remote_finished==null && context.selected_cipher_suite!==null){
1694
+
1695
+ if(context.selected_version == wire.TLS_VERSION.TLS1_3 && context.remote_handshake_traffic_secret!==null){
1696
+
1697
+ if((context.isServer==true && context.finished_sent==true) || (context.isServer==false && context.remote_finished !== null)){
1698
+
1699
+ params_to_set['expected_remote_finished']=get_handshake_finished(TLS_CIPHER_SUITES[context.selected_cipher_suite].hash,context.remote_handshake_traffic_secret,concatUint8Arrays(context.transcript));
1700
+
1701
+ }
1702
+
1703
+ }else if(context.selected_version === wire.TLS_VERSION.TLS1_2 && context.base_secret!==null){
1704
+
1705
+ if(context.remote_finished!==null){
1706
+
1707
+
1708
+ let hashFn = getHashFn(TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1709
+ let transcript_hash = hashFn(concatUint8Arrays(context.transcript));
1710
+
1711
+ if(context.isServer==true){
1712
+ params_to_set['expected_remote_finished']=tls12_prf(context.base_secret, "client finished", transcript_hash, 12, TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1713
+ }else{
1714
+ params_to_set['expected_remote_finished']=tls12_prf(context.base_secret, "server finished", transcript_hash, 12, TLS_CIPHER_SUITES[context.selected_cipher_suite].hash);
1715
+ }
1716
+
1717
+
1718
+
1719
+
1720
+
1721
+ }
1722
+
1723
+ }
1724
+
1725
+ }
1726
+
1727
+
1728
+
1729
+ //compare finished to expected...
1730
+ if(context.remote_finished_ok==false && context.remote_finished!==null && context.expected_remote_finished!==null){
1731
+
1732
+ if(uint8Equal(context.remote_finished, context.expected_remote_finished)==true){
1733
+
1734
+ let message_data = build_tls_message({
1735
+ type: 'finished',
1736
+ data: context.remote_finished
1737
+ });
1738
+
1739
+ context.transcript.push(message_data);
1740
+
1741
+ params_to_set['remote_finished_ok']=true;
1742
+
1743
+ context.remote_finished_data = context.remote_finished;
1744
+ context.remote_finished=null;
1745
+ context.expected_remote_finished=null;
1746
+
1747
+
1748
+
1749
+ }else{
1750
+ context.remote_finished=null;
1751
+ }
1752
+
1753
+ }
1754
+
1755
+
1756
+
1757
+
1758
+ if(context.state!=='connected' && context.remote_finished_ok==true && ((context.selected_version === wire.TLS_VERSION.TLS1_3 && context.local_app_traffic_secret!==null && context.remote_app_traffic_secret!==null) || context.selected_version === wire.TLS_VERSION.TLS1_2)){
1759
+ context.state='connected';
1760
+ context.handshakeEndTime = Date.now();
1761
+ ev.emit('secureConnect');
1762
+
1763
+ // TLS 1.3: compute resumption_master_secret (both client and server need it)
1764
+ if (context.selected_version === wire.TLS_VERSION.TLS1_3 && context.tls13_master_secret && !context.resumption_master_secret) {
1765
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
1766
+ context.resumption_master_secret = derive_resumption_master_secret(
1767
+ hashName, context.tls13_master_secret, concatUint8Arrays(context.transcript)
1768
+ );
1769
+ }
1770
+
1771
+ // TLS 1.3 server: send NewSessionTicket
1772
+ if (context.selected_version === wire.TLS_VERSION.TLS1_3 && context.isServer && !context.session_ticket_sent && !context.noTickets && context.resumption_master_secret) {
1773
+ context.session_ticket_sent = true;
1774
+
1775
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
1776
+ let ticket_nonce = new Uint8Array([context.ticket_nonce_counter++]);
1777
+ let psk = derive_psk(hashName, context.resumption_master_secret, ticket_nonce);
1778
+ let ticket_age_add = crypto.randomBytes(4).readUInt32BE(0);
1779
+ let ticket_lifetime = 7200; // 2 hours
1780
+
1781
+ // Ticket = encrypted PSK + metadata using ticketKeys (or random fallback)
1782
+ // Format: iv(12) || encrypted_json || tag(16)
1783
+ // ticketKeys: 48 bytes = name(16) + aes_key(16) + hmac_key(16)
1784
+ // We use first 32 bytes as AES-256-GCM key for simplicity
1785
+ let tk = context.ticketKeys || crypto.randomBytes(48);
1786
+ let ticket_enc_key = tk.length >= 32 ? tk.slice(0, 32) : Buffer.concat([tk, crypto.randomBytes(32 - tk.length)]);
1787
+ let ticket_iv = crypto.randomBytes(12);
1788
+ let ticket_plaintext = Buffer.from(JSON.stringify({
1789
+ psk: Buffer.from(psk).toString('base64'),
1790
+ cipher: context.selected_cipher_suite,
1791
+ age_add: ticket_age_add,
1792
+ created: Date.now()
1793
+ }));
1794
+ let ticket_cipher = crypto.createCipheriv('aes-256-gcm', ticket_enc_key, ticket_iv);
1795
+ let ticket_ct = ticket_cipher.update(ticket_plaintext);
1796
+ ticket_cipher.final();
1797
+ let ticket_tag = ticket_cipher.getAuthTag();
1798
+ let ticket = Buffer.concat([ticket_iv, ticket_ct, ticket_tag]);
1799
+
1800
+ let nst_data = wire.build_message(wire.TLS_MESSAGE_TYPE.NEW_SESSION_TICKET,
1801
+ wire.build_new_session_ticket({
1802
+ ticket_lifetime: ticket_lifetime,
1803
+ ticket_age_add: ticket_age_add,
1804
+ ticket_nonce: ticket_nonce,
1805
+ ticket: new Uint8Array(ticket),
1806
+ extensions: []
1807
+ })
1808
+ );
1809
+
1810
+ ev.emit('message', 2, context.message_sent_seq, 'new_session_ticket', nst_data);
1811
+ context.message_sent_seq++;
1812
+
1813
+ ev.emit('session', {
1814
+ ticket: new Uint8Array(ticket),
1815
+ ticket_nonce: ticket_nonce,
1816
+ psk: psk,
1817
+ cipher: context.selected_cipher_suite,
1818
+ lifetime: ticket_lifetime,
1819
+ age_add: ticket_age_add,
1820
+ maxEarlyDataSize: 0,
1821
+ });
1822
+ }
1823
+ }
1824
+
1825
+
1826
+
1827
+ set_context(params_to_set);
1828
+ }
1829
+ }
1830
+
1831
+
1832
+ function validatePeerCertificate() {
1833
+ if (!context.remote_cert_chain || context.remote_cert_chain.length === 0) {
1834
+ context.authorizationError = 'NO_PEER_CERTIFICATE';
1835
+ context.peerAuthorized = false;
1836
+ return;
1837
+ }
1838
+
1839
+ try {
1840
+ // Parse the leaf certificate (first in chain)
1841
+ let certDer = context.remote_cert_chain[0].cert;
1842
+ let x509 = new crypto.X509Certificate(certDer);
1843
+
1844
+ // Check validity dates
1845
+ let now = new Date();
1846
+ if (now < new Date(x509.validFrom)) {
1847
+ context.authorizationError = 'CERT_NOT_YET_VALID';
1848
+ context.peerAuthorized = false;
1849
+ return;
1850
+ }
1851
+ if (now > new Date(x509.validTo)) {
1852
+ context.authorizationError = 'CERT_HAS_EXPIRED';
1853
+ context.peerAuthorized = false;
1854
+ return;
1855
+ }
1856
+
1857
+ // Check hostname (client-side only, when SNI is set)
1858
+ if (!context.isServer && context.local_sni) {
1859
+ if (!x509.checkHost(context.local_sni)) {
1860
+ context.authorizationError = 'ERR_TLS_CERT_ALTNAME_INVALID';
1861
+ context.peerAuthorized = false;
1862
+ return;
1863
+ }
1864
+ }
1865
+
1866
+ // Verify against CA if provided
1867
+ if (context.ca) {
1868
+ let cas = Array.isArray(context.ca) ? context.ca : [context.ca];
1869
+ let verified = false;
1870
+ for (let i = 0; i < cas.length; i++) {
1871
+ try {
1872
+ let caX509 = new crypto.X509Certificate(cas[i]);
1873
+ if (x509.checkIssued(caX509) && x509.verify(caX509.publicKey)) {
1874
+ verified = true;
1875
+ break;
1876
+ }
1877
+ } catch(e) { /* try next CA */ }
1878
+ }
1879
+ if (!verified) {
1880
+ context.authorizationError = 'UNABLE_TO_VERIFY_LEAF_SIGNATURE';
1881
+ context.peerAuthorized = false;
1882
+ return;
1883
+ }
1884
+ }
1885
+
1886
+ // All checks passed
1887
+ context.peerAuthorized = true;
1888
+ context.authorizationError = null;
1889
+
1890
+ } catch(e) {
1891
+ context.authorizationError = e.message || 'CERTIFICATE_PARSE_ERROR';
1892
+ context.peerAuthorized = false;
1893
+ }
1894
+ }
1895
+
1896
+
1897
+ function sendAlert(level, description) {
1898
+ let alertData = new Uint8Array([level, description]);
1899
+ // Epoch 0 for alerts during/before handshake
1900
+ let epoch = (context.state === 'connected') ? 2 : 0;
1901
+ ev.emit('message', epoch, 0, 'alert', alertData);
1902
+ ev.emit('alert', { level: level, description: description });
1903
+ if (level === 2) {
1904
+ // Fatal alert — session is dead
1905
+ context.state = 'error';
1906
+ }
1907
+ }
1908
+
1909
+ function close(){
1910
+ if (context.state === 'closed') return;
1911
+ // Send close_notify (warning level, description 0)
1912
+ sendAlert(1, 0);
1913
+ context.state = 'closed';
1914
+ }
1915
+
1916
+
1917
+ if(context.isServer==false){
1918
+ setTimeout(function(){
1919
+
1920
+ if(context.local_random==null){
1921
+ context.local_random=new Uint8Array(crypto.randomBytes(32));
1922
+ }
1923
+
1924
+ if(context.local_session_id==null){
1925
+ context.local_session_id=new Uint8Array(crypto.randomBytes(32));
1926
+ }
1927
+
1928
+ // Support both TLS 1.3 and 1.2 (server picks the best)
1929
+ if(context.local_supported_cipher_suites.length<=0){
1930
+ context.local_supported_cipher_suites=[
1931
+ // TLS 1.3
1932
+ 0x1301, // TLS_AES_128_GCM_SHA256
1933
+ 0x1302, // TLS_AES_256_GCM_SHA384
1934
+ 0x1303, // TLS_CHACHA20_POLY1305_SHA256
1935
+ // TLS 1.2 ECDHE
1936
+ 0xC02F, // ECDHE_RSA_WITH_AES_128_GCM_SHA256
1937
+ 0xC030, // ECDHE_RSA_WITH_AES_256_GCM_SHA384
1938
+ 0xC02B, // ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
1939
+ 0xCCA8, // ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
1940
+ ];
1941
+ }
1942
+
1943
+ if(context.local_supported_groups.length<=0){
1944
+ context.local_supported_groups=[0x001d, 0x0017]; // X25519, P-256
1945
+ }
1946
+
1947
+ if(context.local_supported_versions.length<=0){
1948
+ context.local_supported_versions=[0x0304, 0x0303]; // TLS 1.3, TLS 1.2
1949
+ }
1950
+
1951
+ // Generate X25519 keypair for key_share (TLS 1.3) and ECDHE (TLS 1.2)
1952
+ const private_key = new Uint8Array(crypto.randomBytes(32));
1953
+ let public_key = x25519_get_public_key(private_key);
1954
+
1955
+ context.local_key_groups[0x001d]={
1956
+ public_key: public_key,
1957
+ private_key: private_key
1958
+ };
1959
+
1960
+ let extensions = [
1961
+ {
1962
+ type: 'SUPPORTED_VERSIONS',
1963
+ value: context.local_supported_versions
1964
+ },
1965
+ {
1966
+ type: 'SUPPORTED_GROUPS',
1967
+ value: context.local_supported_groups
1968
+ },
1969
+ {
1970
+ type: 'KEY_SHARE',
1971
+ value: [{
1972
+ group: 0x001d,
1973
+ key_exchange: public_key
1974
+ }]
1975
+ },
1976
+ {
1977
+ type: 'SIGNATURE_ALGORITHMS',
1978
+ value: [
1979
+ // TLS 1.3 (PSS + ECDSA)
1980
+ 0x0804, 0x0805, 0x0806,
1981
+ 0x0403, 0x0503, 0x0603,
1982
+ 0x0807, 0x0808,
1983
+ // TLS 1.2 (PKCS1)
1984
+ 0x0401, 0x0501, 0x0601
1985
+ ]
1986
+ },
1987
+ // TLS 1.2 compatibility
1988
+ { type: 'RENEGOTIATION_INFO', value: new Uint8Array(0) },
1989
+ { type: 23, data: new Uint8Array(0) } // extended_master_secret
1990
+ ];
1991
+
1992
+ // Add SNI if servername was provided
1993
+ if (context.local_sni) {
1994
+ extensions.unshift({ type: 'SERVER_NAME', value: context.local_sni });
1995
+ }
1996
+
1997
+ // Add ALPN if provided (e.g. 'h3' for QUIC)
1998
+ if (context.local_supported_alpns && context.local_supported_alpns.length > 0) {
1999
+ extensions.push({ type: 'ALPN', value: context.local_supported_alpns });
2000
+ }
2001
+
2002
+ // Add custom extensions (e.g. QUIC transport params 0x39)
2003
+ for (let i in context.local_extensions) {
2004
+ extensions.push(context.local_extensions[i]);
2005
+ }
2006
+
2007
+ // PSK resumption: check if session/psk was provided
2008
+ let pskData = options.session || options.psk || null;
2009
+ let message_data;
2010
+
2011
+ if (pskData && pskData.psk && pskData.ticket && pskData.cipher) {
2012
+ // Add PSK key exchange modes (psk_dhe_ke = 1)
2013
+ extensions.push({ type: 'PSK_KEY_EXCHANGE_MODES', value: [1] });
2014
+
2015
+ // Save PSK for later verification
2016
+ context.psk_offered = {
2017
+ identity: pskData.ticket,
2018
+ psk: pskData.psk instanceof Uint8Array ? pskData.psk : new Uint8Array(pskData.psk),
2019
+ cipher: pskData.cipher,
2020
+ age_add: pskData.age_add || 0,
2021
+ };
2022
+
2023
+ // Compute obfuscated ticket age
2024
+ let ticketAge = pskData.lifetime ? Math.min((Date.now() - (pskData.created || Date.now())) / 1000, pskData.lifetime) * 1000 : 0;
2025
+ let obfuscatedAge = ((ticketAge + (pskData.age_add || 0)) & 0xFFFFFFFF) >>> 0;
2026
+
2027
+ // Build ClientHello with placeholder binder to compute truncated hash
2028
+ let hashName = TLS_CIPHER_SUITES[pskData.cipher] ? TLS_CIPHER_SUITES[pskData.cipher].hash : 'sha256';
2029
+ let hashLen = getHashFn(hashName).outputLen;
2030
+ let placeholderBinder = new Uint8Array(hashLen);
2031
+
2032
+ let pskExt = {
2033
+ type: 'PRE_SHARED_KEY',
2034
+ value: {
2035
+ identities: [{ identity: pskData.ticket, age: obfuscatedAge }],
2036
+ binders: [placeholderBinder]
2037
+ }
2038
+ };
2039
+ extensions.push(pskExt); // MUST be last
2040
+
2041
+ // Build the full message with placeholder
2042
+ let build_message_params = {
2043
+ type: 'client_hello',
2044
+ version: 0x0303,
2045
+ random: context.local_random,
2046
+ session_id: context.local_session_id,
2047
+ cipher_suite: context.local_supported_cipher_suites,
2048
+ extensions: extensions
2049
+ };
2050
+ let tempMessage = build_tls_message(build_message_params);
2051
+
2052
+ // Truncation point: message length - binders vec (2 + 1 + hashLen)
2053
+ let bindersSize = 2 + 1 + hashLen;
2054
+ let truncatedMessage = tempMessage.slice(0, tempMessage.length - bindersSize);
2055
+
2056
+ // Compute real binder
2057
+ let binder_key = derive_binder_key(hashName, context.psk_offered.psk, false);
2058
+ let binder = compute_psk_binder(hashName, binder_key, truncatedMessage);
2059
+
2060
+ // Rebuild with real binder
2061
+ pskExt.value.binders = [binder];
2062
+ message_data = build_tls_message(build_message_params);
2063
+
2064
+ } else {
2065
+ // No PSK — standard ClientHello
2066
+ let build_message_params = {
2067
+ type: 'client_hello',
2068
+ version: 0x0303,
2069
+ random: context.local_random,
2070
+ session_id: context.local_session_id,
2071
+ cipher_suite: context.local_supported_cipher_suites,
2072
+ extensions: extensions
2073
+ };
2074
+ message_data = build_tls_message(build_message_params);
2075
+ }
2076
+
2077
+ context.transcript.push(message_data);
2078
+
2079
+ context.hello_sent=true;
2080
+
2081
+ ev.emit('message',0,context.message_sent_seq,'hello',message_data);
2082
+
2083
+ context.message_sent_seq++;
2084
+
2085
+ },0);
2086
+ }
2087
+
2088
+
2089
+
2090
+ let api = {
2091
+ /**
2092
+ * Raw context object. Advanced users (QUIC, DTLS) can read/write
2093
+ * any internal state directly. Use convenience getters below when possible.
2094
+ */
2095
+ context: context,
2096
+
2097
+ /** Whether this session is server-side. */
2098
+ isServer: context.isServer,
2099
+
2100
+ /** Whether this connection used PSK resumption (true after secureConnect if PSK was accepted). */
2101
+ get isResumed() { return context.isResumed; },
2102
+
2103
+ /** Register an event listener.
2104
+ * Events:
2105
+ * 'hello' — fired when remote Hello is received. Server should
2106
+ * call set_context() with local preferences here.
2107
+ * 'message' — (epoch, seq, type, data) handshake/alert message ready to send.
2108
+ * epoch 0=cleartext, 1=handshake-encrypted, 2=app-encrypted.
2109
+ * type: 'hello'|'finished'|'alert'|etc.
2110
+ * The caller must frame this into a TLS record.
2111
+ * 'alert' — ({level, description}) TLS alert sent or received.
2112
+ * 'secureConnect' — handshake complete, app data can flow.
2113
+ */
2114
+ on: function(name, fn){ ev.on(name, fn); },
2115
+ off: function(name, fn){ ev.off(name, fn); },
2116
+
2117
+ /** Feed an incoming handshake message (without record header). */
2118
+ message: process_income_message,
2119
+
2120
+ /** Set negotiation parameters. See context fields for available keys. */
2121
+ set_context: set_context,
2122
+
2123
+ /** Close the session (sends close_notify alert). */
2124
+ close: close,
2125
+
2126
+ /** Send a TLS alert. level: 1=warning, 2=fatal. See wire.TLS_ALERT for descriptions. */
2127
+ sendAlert: sendAlert,
2128
+
2129
+ // ---- Convenience getters ----
2130
+
2131
+ /** Returns the negotiated TLS version (e.g. 0x0303 for TLS 1.2, 0x0304 for TLS 1.3), or null. */
2132
+ getVersion: function(){
2133
+ return context.selected_version;
2134
+ },
2135
+
2136
+ /** Returns the negotiated cipher suite code (e.g. 0x1301, 0xC02F), or null. */
2137
+ getCipher: function(){
2138
+ return context.selected_cipher_suite;
2139
+ },
2140
+
2141
+ /** Returns the negotiated ALPN protocol string (e.g. 'h2'), or null. */
2142
+ getALPN: function(){
2143
+ return context.alpn_selected || null;
2144
+ },
2145
+
2146
+ /** Returns the remote certificate chain, or null. */
2147
+ getPeerCertificate: function(){
2148
+ return context.remote_cert_chain || null;
2149
+ },
2150
+
2151
+ /** Whether the peer certificate passed validation. */
2152
+ get authorized() { return context.peerAuthorized; },
2153
+
2154
+ /** The authorization error string, or null if authorized. */
2155
+ get authorizationError() { return context.authorizationError; },
2156
+
2157
+ /** Returns traffic secrets for record-layer key derivation.
2158
+ * Individual fields are null until negotiated.
2159
+ * TLS 1.3: use localAppSecret/remoteAppSecret after secureConnect.
2160
+ * TLS 1.2: use masterSecret + randoms after key exchange.
2161
+ */
2162
+ getTrafficSecrets: function(){
2163
+ return {
2164
+ isServer: context.isServer,
2165
+ version: context.selected_version,
2166
+ cipher: context.selected_cipher_suite,
2167
+ // TLS 1.3
2168
+ localAppSecret: context.local_app_traffic_secret,
2169
+ remoteAppSecret: context.remote_app_traffic_secret,
2170
+ // TLS 1.2
2171
+ masterSecret: context.base_secret,
2172
+ localRandom: context.local_random,
2173
+ remoteRandom: context.remote_random,
2174
+ };
2175
+ },
2176
+
2177
+ /** Returns handshake traffic secrets (available during handshake, before secureConnect). */
2178
+ getHandshakeSecrets: function(){
2179
+ return {
2180
+ localSecret: context.local_handshake_traffic_secret,
2181
+ remoteSecret: context.remote_handshake_traffic_secret,
2182
+ cipher: context.selected_cipher_suite,
2183
+ };
2184
+ },
2185
+
2186
+ exportKeyingMaterial: function(length, label, context_value){
2187
+ if (!context.local_app_traffic_secret || !context.selected_cipher_suite) return new Uint8Array(0);
2188
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
2189
+ let hashFn = getHashFn(hashName);
2190
+ let ctx_hash = hashFn(context_value || new Uint8Array(0));
2191
+ return hkdf_expand_label(hashName, context.local_app_traffic_secret, label || 'exporter', ctx_hash, length || 32);
2192
+ },
2193
+
2194
+ /** Returns the local Finished verify_data (Buffer), or null. */
2195
+ getFinished: function(){
2196
+ return context.local_finished_data ? Buffer.from(context.local_finished_data) : null;
2197
+ },
2198
+
2199
+ /** Returns the peer Finished verify_data (Buffer), or null. */
2200
+ getPeerFinished: function(){
2201
+ return context.remote_finished_data ? Buffer.from(context.remote_finished_data) : null;
2202
+ },
2203
+
2204
+ /** Returns the ECDHE shared secret (Uint8Array), or null. For research/advanced use. */
2205
+ getSharedSecret: function(){
2206
+ return context.ecdhe_shared_secret ? Buffer.from(context.ecdhe_shared_secret) : null;
2207
+ },
2208
+
2209
+ /** Handshake duration in ms, or null if not completed. */
2210
+ get handshakeDuration() {
2211
+ if (context.handshakeStartTime && context.handshakeEndTime)
2212
+ return context.handshakeEndTime - context.handshakeStartTime;
2213
+ return null;
2214
+ },
2215
+
2216
+ /** Full negotiation result — all selected parameters in one object. */
2217
+ getNegotiationResult: function(){
2218
+ let cipherInfo = context.selected_cipher_suite ? TLS_CIPHER_SUITES[context.selected_cipher_suite] : null;
2219
+ return {
2220
+ version: context.selected_version,
2221
+ versionName: context.selected_version === 0x0304 ? 'TLSv1.3' : context.selected_version === 0x0303 ? 'TLSv1.2' : null,
2222
+ cipher: context.selected_cipher_suite,
2223
+ cipherName: cipherInfo ? cipherInfo.name : null,
2224
+ group: context.selected_group,
2225
+ groupName: context.selected_group === 0x001d ? 'X25519' : context.selected_group === 0x0017 ? 'P-256' : context.selected_group === 0x0018 ? 'P-384' : null,
2226
+ signatureAlgorithm: context.selected_signature_algorithm,
2227
+ alpn: context.selected_alpn,
2228
+ sni: context.selected_sni || context.local_sni,
2229
+ resumed: context.isResumed,
2230
+ helloRetried: context.helloRetried,
2231
+ handshakeDuration: context.handshakeEndTime && context.handshakeStartTime ? context.handshakeEndTime - context.handshakeStartTime : null,
2232
+ };
2233
+ },
2234
+
2235
+ /** Compute JA3 fingerprint from the ClientHello (server-side only).
2236
+ * Returns { hash, raw } or null if no ClientHello available.
2237
+ * JA3 = md5(SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePointFormats)
2238
+ */
2239
+ getJA3: function(){
2240
+ if (!context.rawClientHello) return null;
2241
+ try {
2242
+ let hello = parse_tls_message(context.rawClientHello);
2243
+
2244
+ let version = hello.client_version || 0x0303;
2245
+ let ciphers = (hello.cipher_suites || []).filter(c => (c & 0x0F0F) !== 0x0A0A).join('-');
2246
+ let extensions = (hello.extensions || []).map(e => e.type).filter(t => t !== 0x0A0A).join('-');
2247
+ let curves = (hello.supported_groups || []).filter(g => (g & 0x0F0F) !== 0x0A0A).join('-');
2248
+ let pointFormats = (hello.ec_point_formats || [0]).join('-');
2249
+
2250
+ let raw = [version, ciphers, extensions, curves, pointFormats].join(',');
2251
+ let hash = crypto.createHash('md5').update(raw).digest('hex');
2252
+ return { hash, raw };
2253
+ } catch(e) { return null; }
2254
+ },
2255
+
2256
+ /** Request a TLS 1.3 Key Update. requestPeer=true means ask the other side to update too. */
2257
+ requestKeyUpdate: function(requestPeer){
2258
+ if (context.state !== 'connected' || context.selected_version !== wire.TLS_VERSION.TLS1_3) return;
2259
+ let hashName = TLS_CIPHER_SUITES[context.selected_cipher_suite].hash;
2260
+ let hashLen = getHashLen(hashName);
2261
+
2262
+ // Derive new local traffic secret
2263
+ let newLocalSecret = hkdf_expand_label(hashName, context.local_app_traffic_secret, 'traffic upd', new Uint8Array(0), hashLen);
2264
+ context.local_app_traffic_secret = newLocalSecret;
2265
+
2266
+ // Send KeyUpdate message
2267
+ let ku_data = build_tls_message({ type: 'key_update', request_update: requestPeer ? 1 : 0 });
2268
+ ev.emit('message', 2, context.message_sent_seq, 'key_update', ku_data);
2269
+ context.message_sent_seq++;
2270
+
2271
+ ev.emit('keyUpdate', { direction: 'send', secret: newLocalSecret });
2272
+ },
2273
+ };
2274
+
2275
+ for (let k in api) if (Object.prototype.hasOwnProperty.call(api,k)) this[k] = api[k];
2276
+ // Re-define dynamic getters (the for-in loop flattens them to values)
2277
+ Object.defineProperty(this, 'isResumed', { get: function() { return context.isResumed; }, configurable: true, enumerable: true });
2278
+ return this;
2279
+ }
2280
+
2281
+ export default TLSSession;
2282
+