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