tttls1.3 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -58,8 +58,6 @@ module TTTLS13
58
58
  alpn: nil,
59
59
  process_new_session_ticket: nil,
60
60
  ticket: nil,
61
- # @deprecated Please use `resumption_secret` instead
62
- resumption_master_secret: nil,
63
61
  resumption_secret: nil,
64
62
  psk_cipher_suite: nil,
65
63
  ticket_nonce: nil,
@@ -80,36 +78,29 @@ module TTTLS13
80
78
  STANDARD_CLIENT_ECH_HPKE_SYMMETRIC_CIPHER_SUITES = [
81
79
  HpkeSymmetricCipherSuite.new(
82
80
  HpkeSymmetricCipherSuite::HpkeKdfId.new(
83
- Hpke::KdfId::HKDF_SHA256
81
+ Ech::KdfId::HKDF_SHA256
84
82
  ),
85
83
  HpkeSymmetricCipherSuite::HpkeAeadId.new(
86
- Hpke::AeadId::AES_128_GCM
84
+ Ech::AeadId::AES_128_GCM
87
85
  )
88
86
  )
89
87
  ].freeze
90
88
  # rubocop: disable Metrics/ClassLength
91
- class Client < Connection
89
+ class Client
90
+ include Logging
91
+
92
92
  HpkeSymmetricCipherSuit = \
93
93
  ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite
94
94
 
95
+ attr_reader :transcript
96
+
95
97
  # @param socket [Socket]
96
98
  # @param hostname [String]
97
99
  # @param settings [Hash]
98
100
  def initialize(socket, hostname, **settings)
99
- super(socket)
100
-
101
- @endpoint = :client
101
+ @connection = Connection.new(socket, :client)
102
102
  @hostname = hostname
103
103
  @settings = DEFAULT_CLIENT_SETTINGS.merge(settings)
104
- # NOTE: backward compatibility
105
- if @settings[:resumption_secret].nil? &&
106
- !@settings[:resumption_master_secret].nil?
107
- @settings[:resumption_secret] =
108
- @settings.delete(:resumption_master_secret) \
109
- end
110
- raise Error::ConfigError if @settings[:resumption_secret] !=
111
- @settings[:resumption_master_secret]
112
-
113
104
  logger.level = @settings[:loglevel]
114
105
 
115
106
  @early_data = ''
@@ -159,7 +150,7 @@ module TTTLS13
159
150
  # rubocop: disable Metrics/MethodLength
160
151
  # rubocop: disable Metrics/PerceivedComplexity
161
152
  def connect
162
- transcript = Transcript.new
153
+ @transcript = Transcript.new
163
154
  key_schedule = nil # TTTLS13::KeySchedule
164
155
  psk = nil
165
156
  priv_keys = {} # Hash of NamedGroup => OpenSSL::PKey::$Object
@@ -173,7 +164,7 @@ module TTTLS13
173
164
  psk: psk,
174
165
  shared_secret: nil,
175
166
  cipher_suite: @settings[:psk_cipher_suite],
176
- transcript: transcript
167
+ transcript: @transcript
177
168
  )
178
169
  end
179
170
 
@@ -183,7 +174,7 @@ module TTTLS13
183
174
  sslkeylogfile = nil # TTTLS13::SslKeyLogFile::Writer
184
175
  ch1_outer = nil # TTTLS13::Message::ClientHello for rejected ECH
185
176
  ch_outer = nil # TTTLS13::Message::ClientHello for rejected ECH
186
- ech_state = nil # TTTLS13::Client::EchState for ECH with HRR
177
+ ech_state = nil # TTTLS13::EchState for ECH with HRR
187
178
  unless @settings[:sslkeylogfile].nil?
188
179
  begin
189
180
  sslkeylogfile = SslKeyLogFile::Writer.new(@settings[:sslkeylogfile])
@@ -193,9 +184,9 @@ module TTTLS13
193
184
  end
194
185
  end
195
186
 
196
- @state = ClientState::START
187
+ @connection.state = ClientState::START
197
188
  loop do
198
- case @state
189
+ case @connection.state
199
190
  when ClientState::START
200
191
  logger.debug('ClientState::START')
201
192
 
@@ -205,76 +196,77 @@ module TTTLS13
205
196
  ch_outer = ch
206
197
  # use ClientHelloInner messages for the transcript hash
207
198
  ch = inner.nil? ? ch : inner
208
- transcript[CH] = [ch, ch.serialize]
209
- send_ccs if @settings[:compatibility_mode]
199
+ @transcript[CH] = [ch, ch.serialize]
200
+ @connection.send_ccs if @settings[:compatibility_mode]
210
201
  if use_early_data?
211
- e_wcipher = gen_cipher(
202
+ e_wcipher = Endpoint.gen_cipher(
212
203
  @settings[:psk_cipher_suite],
213
204
  key_schedule.early_data_write_key,
214
205
  key_schedule.early_data_write_iv
215
206
  )
216
207
  sslkeylogfile&.write_client_early_traffic_secret(
217
- transcript[CH].first.random,
208
+ @transcript[CH].first.random,
218
209
  key_schedule.client_early_traffic_secret
219
210
  )
220
211
  send_early_data(e_wcipher)
221
212
  end
222
213
 
223
- @state = ClientState::WAIT_SH
214
+ @connection.state = ClientState::WAIT_SH
224
215
  when ClientState::WAIT_SH
225
216
  logger.debug('ClientState::WAIT_SH')
226
217
 
227
- sh, = transcript[SH] = recv_server_hello
218
+ sh, = @transcript[SH] = recv_server_hello
228
219
 
229
220
  # downgrade protection
230
221
  if !sh.negotiated_tls_1_3? && sh.downgraded?
231
- terminate(:illegal_parameter)
222
+ @connection.terminate(:illegal_parameter)
232
223
  # support only TLS 1.3
233
224
  elsif !sh.negotiated_tls_1_3?
234
- terminate(:protocol_version)
225
+ @connection.terminate(:protocol_version)
235
226
  end
236
227
 
237
228
  # validate parameters
238
- terminate(:illegal_parameter) \
229
+ @connection.terminate(:illegal_parameter) \
239
230
  unless sh.appearable_extensions?
240
- terminate(:illegal_parameter) \
231
+ @connection.terminate(:illegal_parameter) \
241
232
  unless sh.legacy_compression_method == "\x00"
242
233
 
243
234
  # validate sh using ch
244
- ch, = transcript[CH]
245
- terminate(:illegal_parameter) \
235
+ ch, = @transcript[CH]
236
+ @connection.terminate(:illegal_parameter) \
246
237
  unless sh.legacy_version == ch.legacy_version
247
- terminate(:illegal_parameter) \
238
+ @connection.terminate(:illegal_parameter) \
248
239
  unless sh.legacy_session_id_echo == ch.legacy_session_id
249
- terminate(:illegal_parameter) \
240
+ @connection.terminate(:illegal_parameter) \
250
241
  unless ch.cipher_suites.include?(sh.cipher_suite)
251
- terminate(:unsupported_extension) \
242
+ @connection.terminate(:unsupported_extension) \
252
243
  unless (sh.extensions.keys - ch.extensions.keys).empty?
253
244
 
254
245
  # validate sh using hrr
255
- if transcript.include?(HRR)
256
- hrr, = transcript[HRR]
257
- terminate(:illegal_parameter) \
246
+ if @transcript.include?(HRR)
247
+ hrr, = @transcript[HRR]
248
+ @connection.terminate(:illegal_parameter) \
258
249
  unless sh.cipher_suite == hrr.cipher_suite
259
250
 
260
251
  sh_sv = sh.extensions[Message::ExtensionType::SUPPORTED_VERSIONS]
261
252
  hrr_sv = hrr.extensions[Message::ExtensionType::SUPPORTED_VERSIONS]
262
- terminate(:illegal_parameter) \
253
+ @connection.terminate(:illegal_parameter) \
263
254
  unless sh_sv.versions == hrr_sv.versions
264
255
  end
265
256
 
266
257
  # handling HRR
267
258
  if sh.hrr?
268
- terminate(:unexpected_message) if transcript.include?(HRR)
259
+ @connection.terminate(:unexpected_message) \
260
+ if @transcript.include?(HRR)
269
261
 
270
- ch1, = transcript[CH1] = transcript.delete(CH)
271
- hrr, = transcript[HRR] = transcript.delete(SH)
262
+ ch1, = @transcript[CH1] = @transcript.delete(CH)
263
+ hrr, = @transcript[HRR] = @transcript.delete(SH)
272
264
  ch1_outer = ch_outer
273
265
  ch_outer = nil
274
266
 
275
267
  # validate cookie
276
268
  diff_sets = sh.extensions.keys - ch1.extensions.keys
277
- terminate(:unsupported_extension) \
269
+ @connection.terminate(:unsupported_extension) \
278
270
  unless (diff_sets - [Message::ExtensionType::COOKIE]).empty?
279
271
 
280
272
  # validate key_share
@@ -285,7 +277,7 @@ module TTTLS13
285
277
  .key_share_entry
286
278
  group = hrr.extensions[Message::ExtensionType::KEY_SHARE]
287
279
  .key_share_entry.first.group
288
- terminate(:illegal_parameter) \
280
+ @connection.terminate(:illegal_parameter) \
289
281
  unless ngl.include?(group) && !kse.map(&:group).include?(group)
290
282
 
291
283
  # send new client_hello
@@ -302,9 +294,9 @@ module TTTLS13
302
294
  # use ClientHelloInner messages for the transcript hash
303
295
  ch_outer = ch
304
296
  ch = inner.nil? ? ch : inner
305
- transcript[CH] = [ch, ch.serialize]
297
+ @transcript[CH] = [ch, ch.serialize]
306
298
 
307
- @state = ClientState::WAIT_SH
299
+ @connection.state = ClientState::WAIT_SH
308
300
  next
309
301
  end
310
302
 
@@ -318,69 +310,70 @@ module TTTLS13
318
310
  .key_share_entry.map(&:group)
319
311
  sh_ks = sh.extensions[Message::ExtensionType::KEY_SHARE]
320
312
  .key_share_entry.first.group
321
- terminate(:illegal_parameter) unless ch_ks.include?(sh_ks)
313
+ @connection.terminate(:illegal_parameter) unless ch_ks.include?(sh_ks)
322
314
 
323
315
  kse = sh.extensions[Message::ExtensionType::KEY_SHARE]
324
316
  .key_share_entry.first
325
317
  ke = kse.key_exchange
326
318
  @named_group = kse.group
327
319
  priv_key = priv_keys[@named_group]
328
- shared_secret = gen_shared_secret(ke, priv_key, @named_group)
320
+ shared_secret = Endpoint.gen_shared_secret(ke, priv_key, @named_group)
329
321
  @cipher_suite = sh.cipher_suite
330
322
  key_schedule = KeySchedule.new(
331
323
  psk: psk,
332
324
  shared_secret: shared_secret,
333
325
  cipher_suite: @cipher_suite,
334
- transcript: transcript
326
+ transcript: @transcript
335
327
  )
336
328
 
337
329
  # rejected ECH
338
330
  # NOTE: It can compute (hrr_)accept_ech until client selects the
339
331
  # cipher_suite.
340
332
  if !sh.hrr? && use_ech?
341
- if !transcript.include?(HRR) && !key_schedule.accept_ech?
333
+ if !@transcript.include?(HRR) && !key_schedule.accept_ech?
342
334
  # 1sh SH
343
- transcript[CH] = [ch_outer, ch_outer.serialize]
335
+ @transcript[CH] = [ch_outer, ch_outer.serialize]
344
336
  @rejected_ech = true
345
- elsif transcript.include?(HRR) &&
337
+ elsif @transcript.include?(HRR) &&
346
338
  key_schedule.hrr_accept_ech? != key_schedule.accept_ech?
347
339
  # 2nd SH
348
- terminate(:illegal_parameter)
349
- elsif transcript.include?(HRR) && !key_schedule.hrr_accept_ech?
340
+ @connection.terminate(:illegal_parameter)
341
+ elsif @transcript.include?(HRR) && !key_schedule.hrr_accept_ech?
350
342
  # 2nd SH
351
- transcript[CH1] = [ch1_outer, ch1_outer.serialize]
352
- transcript[CH] = [ch_outer, ch_outer.serialize]
343
+ @transcript[CH1] = [ch1_outer, ch1_outer.serialize]
344
+ @transcript[CH] = [ch_outer, ch_outer.serialize]
353
345
  @rejected_ech = true
354
346
  end
355
347
  end
356
348
 
357
- @alert_wcipher = hs_wcipher = gen_cipher(
349
+ @connection.alert_wcipher = hs_wcipher = Endpoint.gen_cipher(
358
350
  @cipher_suite,
359
351
  key_schedule.client_handshake_write_key,
360
352
  key_schedule.client_handshake_write_iv
361
353
  )
362
354
  sslkeylogfile&.write_client_handshake_traffic_secret(
363
- transcript[CH].first.random,
355
+ @transcript[CH].first.random,
364
356
  key_schedule.client_handshake_traffic_secret
365
357
  )
366
- hs_rcipher = gen_cipher(
358
+ hs_rcipher = Endpoint.gen_cipher(
367
359
  @cipher_suite,
368
360
  key_schedule.server_handshake_write_key,
369
361
  key_schedule.server_handshake_write_iv
370
362
  )
371
363
  sslkeylogfile&.write_server_handshake_traffic_secret(
372
- transcript[CH].first.random,
364
+ @transcript[CH].first.random,
373
365
  key_schedule.server_handshake_traffic_secret
374
366
  )
375
- @state = ClientState::WAIT_EE
367
+ @connection.state = ClientState::WAIT_EE
376
368
  when ClientState::WAIT_EE
377
369
  logger.debug('ClientState::WAIT_EE')
378
370
 
379
- ee, = transcript[EE] = recv_encrypted_extensions(hs_rcipher)
380
- terminate(:illegal_parameter) unless ee.appearable_extensions?
371
+ ee, = @transcript[EE] = recv_encrypted_extensions(hs_rcipher)
372
+ @connection.terminate(:illegal_parameter) \
373
+ unless ee.appearable_extensions?
381
374
 
382
- ch, = transcript[CH]
383
- terminate(:unsupported_extension) \
375
+ ch, = @transcript[CH]
376
+ @connection.terminate(:unsupported_extension) \
384
377
  unless (ee.extensions.keys - ch.extensions.keys).empty?
385
378
 
386
379
  rsl = ee.extensions[Message::ExtensionType::RECORD_SIZE_LIMIT]
@@ -393,118 +386,120 @@ module TTTLS13
393
386
  @retry_configs = ee.extensions[
394
387
  Message::ExtensionType::ENCRYPTED_CLIENT_HELLO
395
388
  ]&.retry_configs
396
- terminate(:unsupported_extension) \
389
+ @connection.terminate(:unsupported_extension) \
397
390
  if !rejected_ech? && !@retry_configs.nil?
398
391
 
399
- @state = ClientState::WAIT_CERT_CR
400
- @state = ClientState::WAIT_FINISHED unless psk.nil?
392
+ @connection.state = ClientState::WAIT_CERT_CR
393
+ @connection.state = ClientState::WAIT_FINISHED unless psk.nil?
401
394
  when ClientState::WAIT_CERT_CR
402
395
  logger.debug('ClientState::WAIT_CERT_CR')
403
396
 
404
- message, orig_msg = recv_message(
397
+ message, orig_msg = @connection.recv_message(
405
398
  receivable_ccs: true,
406
399
  cipher: hs_rcipher
407
400
  )
408
401
  case message.msg_type
409
402
  when Message::HandshakeType::CERTIFICATE,
410
403
  Message::HandshakeType::COMPRESSED_CERTIFICATE
411
- ct, = transcript[CT] = [message, orig_msg]
412
- terminate(:bad_certificate) \
404
+ ct, = @transcript[CT] = [message, orig_msg]
405
+ @connection.terminate(:bad_certificate) \
413
406
  if ct.is_a?(Message::CompressedCertificate) &&
414
407
  !@settings[:compress_certificate_algorithms]
415
408
  .include?(ct.algorithm)
416
409
 
417
410
  ct = ct.certificate_message \
418
411
  if ct.is_a?(Message::CompressedCertificate)
419
- alert = check_invalid_certificate(ct, transcript[CH].first)
420
- terminate(alert) unless alert.nil?
412
+ alert = check_invalid_certificate(ct, @transcript[CH].first)
413
+ @connection.terminate(alert) unless alert.nil?
421
414
 
422
- @state = ClientState::WAIT_CV
415
+ @connection.state = ClientState::WAIT_CV
423
416
  when Message::HandshakeType::CERTIFICATE_REQUEST
424
- transcript[CR] = [message, orig_msg]
417
+ @transcript[CR] = [message, orig_msg]
425
418
  # TODO: client authentication
426
- @state = ClientState::WAIT_CERT
419
+ @connection.state = ClientState::WAIT_CERT
427
420
  else
428
- terminate(:unexpected_message)
421
+ @connection.terminate(:unexpected_message)
429
422
  end
430
423
  when ClientState::WAIT_CERT
431
424
  logger.debug('ClientState::WAIT_CERT')
432
425
 
433
- ct, = transcript[CT] = recv_certificate(hs_rcipher)
426
+ ct, = @transcript[CT] = recv_certificate(hs_rcipher)
434
427
  if ct.is_a?(Message::CompressedCertificate) &&
435
428
  !@settings[:compress_certificate_algorithms].include?(ct.algorithm)
436
- terminate(:bad_certificate)
429
+ @connection.terminate(:bad_certificate)
437
430
  elsif ct.is_a?(Message::CompressedCertificate)
438
431
  ct = ct.certificate_message
439
432
  end
440
433
 
441
- alert = check_invalid_certificate(ct, transcript[CH].first)
442
- terminate(alert) unless alert.nil?
434
+ alert = check_invalid_certificate(ct, @transcript[CH].first)
435
+ @connection.terminate(alert) unless alert.nil?
443
436
 
444
- @state = ClientState::WAIT_CV
437
+ @connection.state = ClientState::WAIT_CV
445
438
  when ClientState::WAIT_CV
446
439
  logger.debug('ClientState::WAIT_CV')
447
440
 
448
- cv, = transcript[CV] = recv_certificate_verify(hs_rcipher)
441
+ cv, = @transcript[CV] = recv_certificate_verify(hs_rcipher)
449
442
  digest = CipherSuite.digest(@cipher_suite)
450
- hash = transcript.hash(digest, CT)
451
- ct, = transcript[CT]
443
+ hash = @transcript.hash(digest, CT)
444
+ ct, = @transcript[CT]
452
445
  ct = ct.certificate_message \
453
446
  if ct.is_a?(Message::CompressedCertificate)
454
- terminate(:decrypt_error) \
447
+ @connection.terminate(:decrypt_error) \
455
448
  unless verified_certificate_verify?(ct, cv, hash)
456
449
 
457
450
  @signature_scheme = cv.signature_scheme
458
- @state = ClientState::WAIT_FINISHED
451
+ @connection.state = ClientState::WAIT_FINISHED
459
452
  when ClientState::WAIT_FINISHED
460
453
  logger.debug('ClientState::WAIT_FINISHED')
461
454
 
462
- sf, = transcript[SF] = recv_finished(hs_rcipher)
455
+ sf, = @transcript[SF] = recv_finished(hs_rcipher)
463
456
  digest = CipherSuite.digest(@cipher_suite)
464
- terminate(:decrypt_error) unless verified_finished?(
465
- finished: sf,
466
- digest: digest,
467
- finished_key: key_schedule.server_finished_key,
468
- hash: transcript.hash(digest, CV)
469
- )
457
+ @connection.terminate(:decrypt_error) \
458
+ unless Endpoint.verified_finished?(
459
+ finished: sf,
460
+ digest: digest,
461
+ finished_key: key_schedule.server_finished_key,
462
+ hash: @transcript.hash(digest, CV)
463
+ )
470
464
 
471
465
  if use_early_data? && succeed_early_data?
472
466
  eoed = send_eoed(e_wcipher)
473
- transcript[EOED] = [eoed, eoed.serialize]
467
+ @transcript[EOED] = [eoed, eoed.serialize]
474
468
  end
475
469
  # TODO: Send Certificate [+ CertificateVerify]
476
- signature = sign_finished(
470
+ signature = Endpoint.sign_finished(
477
471
  digest: digest,
478
472
  finished_key: key_schedule.client_finished_key,
479
- hash: transcript.hash(digest, EOED)
473
+ hash: @transcript.hash(digest, EOED)
480
474
  )
481
475
  cf = send_finished(signature, hs_wcipher)
482
- transcript[CF] = [cf, cf.serialize]
483
- @alert_wcipher = @ap_wcipher = gen_cipher(
476
+ @transcript[CF] = [cf, cf.serialize]
477
+ @connection.ap_wcipher = Endpoint.gen_cipher(
484
478
  @cipher_suite,
485
479
  key_schedule.client_application_write_key,
486
480
  key_schedule.client_application_write_iv
487
481
  )
482
+ @connection.alert_wcipher = @connection.ap_wcipher
488
483
  sslkeylogfile&.write_client_traffic_secret_0(
489
- transcript[CH].first.random,
484
+ @transcript[CH].first.random,
490
485
  key_schedule.client_application_traffic_secret
491
486
  )
492
- @ap_rcipher = gen_cipher(
487
+ @connection.ap_rcipher = Endpoint.gen_cipher(
493
488
  @cipher_suite,
494
489
  key_schedule.server_application_write_key,
495
490
  key_schedule.server_application_write_iv
496
491
  )
497
492
  sslkeylogfile&.write_server_traffic_secret_0(
498
- transcript[CH].first.random,
493
+ @transcript[CH].first.random,
499
494
  key_schedule.server_application_traffic_secret
500
495
  )
501
496
  @exporter_secret = key_schedule.exporter_secret
502
497
  @resumption_secret = key_schedule.resumption_secret
503
- @state = ClientState::CONNECTED
498
+ @connection.state = ClientState::CONNECTED
504
499
  when ClientState::CONNECTED
505
500
  logger.debug('ClientState::CONNECTED')
506
501
 
507
- send_alert(:ech_required) \
502
+ @connection.send_alert(:ech_required) \
508
503
  if use_ech? && (!@retry_configs.nil? && !@retry_configs.empty?)
509
504
  break
510
505
  end
@@ -517,6 +512,14 @@ module TTTLS13
517
512
  # rubocop: enable Metrics/MethodLength
518
513
  # rubocop: enable Metrics/PerceivedComplexity
519
514
 
515
+ # @raise [TTTLS13::Error::ConfigError]
516
+ #
517
+ # @return [String]
518
+ def read
519
+ nst_process = method(:process_new_session_ticket)
520
+ @connection.read(nst_process)
521
+ end
522
+
520
523
  # @param binary [String]
521
524
  def write(binary)
522
525
  # the client can regard ECH as securely disabled by the server, and it
@@ -528,14 +531,55 @@ module TTTLS13
528
531
  return
529
532
  end
530
533
 
531
- super(binary)
534
+ @connection.write(binary)
535
+ end
536
+
537
+ # return [Boolean]
538
+ def eof?
539
+ @connection.eof?
540
+ end
541
+
542
+ def close
543
+ @connection.close
544
+ end
545
+
546
+ # @return [TTTLS13::CipherSuite, nil]
547
+ def negotiated_cipher_suite
548
+ @cipher_suite
549
+ end
550
+
551
+ # @return [TTTLS13::NamedGroup, nil]
552
+ def negotiated_named_group
553
+ @named_group
554
+ end
555
+
556
+ # @return [TTTLS13::SignatureScheme, nil]
557
+ def negotiated_signature_scheme
558
+ @signature_scheme
559
+ end
560
+
561
+ # @return [String]
562
+ def negotiated_alpn
563
+ @alpn
564
+ end
565
+
566
+ # @param label [String]
567
+ # @param context [String]
568
+ # @param key_length [Integer]
569
+ #
570
+ # @return [String, nil]
571
+ def exporter(label, context, key_length)
572
+ return nil if @exporter_secret.nil? || @cipher_suite.nil?
573
+
574
+ digest = CipherSuite.digest(@cipher_suite)
575
+ Endpoint.exporter(@exporter_secret, digest, label, context, key_length)
532
576
  end
533
577
 
534
578
  # @param binary [String]
535
579
  #
536
580
  # @raise [TTTLS13::Error::ConfigError]
537
581
  def early_data(binary)
538
- raise Error::ConfigError unless @state == INITIAL && use_psk?
582
+ raise Error::ConfigError unless @connection.state == INITIAL && use_psk?
539
583
 
540
584
  @early_data = binary
541
585
  end
@@ -677,7 +721,7 @@ module TTTLS13
677
721
  messages: [ap],
678
722
  cipher: cipher
679
723
  )
680
- send_record(ap_record)
724
+ @connection.send_record(ap_record)
681
725
  end
682
726
 
683
727
  # @param resumption_secret [String]
@@ -768,7 +812,7 @@ module TTTLS13
768
812
  #
769
813
  # @return [TTTLS13::Message::ClientHello] outer
770
814
  # @return [TTTLS13::Message::ClientHello] inner
771
- # @return [TTTLS13::Client::EchState]
815
+ # @return [TTTLS13::EchState]
772
816
  # rubocop: disable Metrics/MethodLength
773
817
  def send_client_hello(extensions, binder_key = nil)
774
818
  ch = Message::ClientHello.new(
@@ -783,7 +827,11 @@ module TTTLS13
783
827
  inner_ech = Message::Extension::ECHClientHello.new_inner
784
828
  inner.extensions[Message::ExtensionType::ENCRYPTED_CLIENT_HELLO] \
785
829
  = inner_ech
786
- ch, inner, ech_state = offer_ech(inner, @settings[:ech_config])
830
+ ch, inner, ech_state = Ech.offer_ech(
831
+ inner,
832
+ @settings[:ech_config],
833
+ method(:select_ech_hpke_cipher_suite)
834
+ )
787
835
  end
788
836
 
789
837
  # psk_key_exchange_modes
@@ -814,8 +862,8 @@ module TTTLS13
814
862
  end
815
863
  end
816
864
 
817
- send_handshakes(Message::ContentType::HANDSHAKE, [ch],
818
- Cryptograph::Passer.new)
865
+ @connection.send_handshakes(Message::ContentType::HANDSHAKE, [ch],
866
+ Cryptograph::Passer.new)
819
867
 
820
868
  [ch, inner, ech_state]
821
869
  end
@@ -850,7 +898,7 @@ module TTTLS13
850
898
  )
851
899
  ch.extensions[Message::ExtensionType::PRE_SHARED_KEY] = psk
852
900
 
853
- psk.offered_psks.binders[0] = do_sign_psk_binder(
901
+ psk.offered_psks.binders[0] = Endpoint.sign_psk_binder(
854
902
  ch1: ch1,
855
903
  hrr: hrr,
856
904
  ch: ch,
@@ -900,7 +948,7 @@ module TTTLS13
900
948
  )
901
949
  ch_outer.extensions[Message::ExtensionType::PRE_SHARED_KEY] = psk
902
950
 
903
- psk.offered_psks.binders[0] = do_sign_psk_binder(
951
+ psk.offered_psks.binders[0] = Endpoint.sign_psk_binder(
904
952
  ch1: ch1,
905
953
  hrr: hrr,
906
954
  ch: ch_outer,
@@ -909,209 +957,6 @@ module TTTLS13
909
957
  )
910
958
  end
911
959
 
912
- # @param inner [TTTLS13::Message::ClientHello]
913
- # @param ech_config [ECHConfig]
914
- #
915
- # @return [TTTLS13::Message::ClientHello]
916
- # @return [TTTLS13::Message::ClientHello]
917
- # @return [TTTLS13::Client::EchState]
918
- # rubocop: disable Metrics/AbcSize
919
- # rubocop: disable Metrics/MethodLength
920
- def offer_ech(inner, ech_config)
921
- return [new_greased_ch(inner, new_grease_ech), nil, nil] \
922
- if ech_config.nil? ||
923
- !SUPPORTED_ECHCONFIG_VERSIONS.include?(ech_config.version)
924
-
925
- # Encrypted ClientHello Configuration
926
- public_name = ech_config.echconfig_contents.public_name
927
- key_config = ech_config.echconfig_contents.key_config
928
- public_key = key_config.public_key.opaque
929
- kem_id = key_config&.kem_id&.uint16
930
- config_id = key_config.config_id
931
- cipher_suite = select_ech_hpke_cipher_suite(key_config)
932
- overhead_len = Hpke.aead_id2overhead_len(cipher_suite&.aead_id&.uint16)
933
- aead_cipher = Hpke.aead_id2aead_cipher(cipher_suite&.aead_id&.uint16)
934
- kdf_hash = Hpke.kdf_id2kdf_hash(cipher_suite&.kdf_id&.uint16)
935
- return [new_greased_ch(inner, new_grease_ech), nil, nil] \
936
- if [kem_id, overhead_len, aead_cipher, kdf_hash].any?(&:nil?)
937
-
938
- kem_curve_name, kem_hash = Hpke.kem_id2dhkem(kem_id)
939
- dhkem = Hpke.kem_curve_name2dhkem(kem_curve_name)
940
- pkr = dhkem&.new(kem_hash)&.deserialize_public_key(public_key)
941
- return [new_greased_ch(inner, new_grease_ech), nil, nil] if pkr.nil?
942
-
943
- hpke = HPKE.new(kem_curve_name, kem_hash, kdf_hash, aead_cipher)
944
- base_s = hpke.setup_base_s(pkr, "tls ech\x00" + ech_config.encode)
945
- enc = base_s[:enc]
946
- ctx = base_s[:context_s]
947
- mnl = ech_config.echconfig_contents.maximum_name_length
948
- encoded = encode_ch_inner(inner, mnl)
949
-
950
- # Encoding the ClientHelloInner
951
- aad = new_ch_outer_aad(
952
- inner,
953
- cipher_suite,
954
- config_id,
955
- enc,
956
- encoded.length + overhead_len,
957
- public_name
958
- )
959
- # Authenticating the ClientHelloOuter
960
- # which does not include the Handshake structure's four byte header.
961
- outer = new_ch_outer(
962
- aad,
963
- cipher_suite,
964
- config_id,
965
- enc,
966
- ctx.seal(aad.serialize[4..], encoded)
967
- )
968
-
969
- ech_state = EchState.new(mnl, config_id, cipher_suite, public_name, ctx)
970
- [outer, inner, ech_state]
971
- end
972
- # rubocop: enable Metrics/AbcSize
973
- # rubocop: enable Metrics/MethodLength
974
-
975
- # @param inner [TTTLS13::Message::ClientHello]
976
- # @param ech_state [TTTLS13::Client::EchState]
977
- #
978
- # @return [TTTLS13::Message::ClientHello]
979
- # @return [TTTLS13::Message::ClientHello]
980
- def offer_new_ech(inner, ech_state)
981
- encoded = encode_ch_inner(inner, ech_state.maximum_name_length)
982
- overhead_len \
983
- = Hpke.aead_id2overhead_len(ech_state.cipher_suite.aead_id.uint16)
984
-
985
- # It encrypts EncodedClientHelloInner as described in Section 6.1.1, using
986
- # the second partial ClientHelloOuterAAD, to obtain a second
987
- # ClientHelloOuter. It reuses the original HPKE encryption context
988
- # computed in Section 6.1 and uses the empty string for enc.
989
- #
990
- # https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#section-6.1.5-4.4.1
991
- aad = new_ch_outer_aad(
992
- inner,
993
- ech_state.cipher_suite,
994
- ech_state.config_id,
995
- '',
996
- encoded.length + overhead_len,
997
- ech_state.public_name
998
- )
999
- # Authenticating the ClientHelloOuter
1000
- # which does not include the Handshake structure's four byte header.
1001
- outer = new_ch_outer(
1002
- aad,
1003
- ech_state.cipher_suite,
1004
- ech_state.config_id,
1005
- '',
1006
- ech_state.ctx.seal(aad.serialize[4..], encoded)
1007
- )
1008
-
1009
- [outer, inner]
1010
- end
1011
-
1012
- # @param inner [TTTLS13::Message::ClientHello]
1013
- # @param maximum_name_length [Integer]
1014
- #
1015
- # @return [String] EncodedClientHelloInner
1016
- def encode_ch_inner(inner, maximum_name_length)
1017
- # TODO: ech_outer_extensions
1018
- encoded = Message::ClientHello.new(
1019
- legacy_version: inner.legacy_version,
1020
- random: inner.random,
1021
- legacy_session_id: '',
1022
- cipher_suites: inner.cipher_suites,
1023
- legacy_compression_methods: inner.legacy_compression_methods,
1024
- extensions: inner.extensions
1025
- )
1026
- server_name_length = \
1027
- inner.extensions[Message::ExtensionType::SERVER_NAME].server_name.length
1028
-
1029
- # which does not include the Handshake structure's four byte header.
1030
- padding_encoded_ch_inner(
1031
- encoded.serialize[4..],
1032
- server_name_length,
1033
- maximum_name_length
1034
- )
1035
- end
1036
-
1037
- # @param s [String]
1038
- # @param server_name_length [Integer]
1039
- # @param maximum_name_length [Integer]
1040
- #
1041
- # @return [String]
1042
- def padding_encoded_ch_inner(s, server_name_length, maximum_name_length)
1043
- padding_len =
1044
- if server_name_length.positive?
1045
- [maximum_name_length - server_name_length, 0].max
1046
- else
1047
- 9 + maximum_name_length
1048
- end
1049
-
1050
- padding_len = 31 - ((s.length + padding_len - 1) % 32)
1051
- s + padding_len.zeros
1052
- end
1053
-
1054
- # @param inner [TTTLS13::Message::ClientHello]
1055
- # @param cipher_suite [HpkeSymmetricCipherSuite]
1056
- # @param config_id [Integer]
1057
- # @param enc [String]
1058
- # @param payload_len [Integer]
1059
- # @param server_name [String]
1060
- #
1061
- # @return [TTTLS13::Message::ClientHello]
1062
- # rubocop: disable Metrics/ParameterLists
1063
- def new_ch_outer_aad(inner,
1064
- cipher_suite,
1065
- config_id,
1066
- enc,
1067
- payload_len,
1068
- server_name)
1069
- aad_ech = Message::Extension::ECHClientHello.new_outer(
1070
- cipher_suite: cipher_suite,
1071
- config_id: config_id,
1072
- enc: enc,
1073
- payload: payload_len.zeros
1074
- )
1075
- Message::ClientHello.new(
1076
- legacy_version: inner.legacy_version,
1077
- legacy_session_id: inner.legacy_session_id,
1078
- cipher_suites: inner.cipher_suites,
1079
- legacy_compression_methods: inner.legacy_compression_methods,
1080
- extensions: inner.extensions.merge(
1081
- Message::ExtensionType::ENCRYPTED_CLIENT_HELLO => aad_ech,
1082
- Message::ExtensionType::SERVER_NAME => \
1083
- Message::Extension::ServerName.new(server_name)
1084
- )
1085
- )
1086
- end
1087
- # rubocop: enable Metrics/ParameterLists
1088
-
1089
- # @param aad [TTTLS13::Message::ClientHello]
1090
- # @param cipher_suite [HpkeSymmetricCipherSuite]
1091
- # @param config_id [Integer]
1092
- # @param enc [String]
1093
- # @param payload [String]
1094
- #
1095
- # @return [TTTLS13::Message::ClientHello]
1096
- def new_ch_outer(aad, cipher_suite, config_id, enc, payload)
1097
- outer_ech = Message::Extension::ECHClientHello.new_outer(
1098
- cipher_suite: cipher_suite,
1099
- config_id: config_id,
1100
- enc: enc,
1101
- payload: payload
1102
- )
1103
- Message::ClientHello.new(
1104
- legacy_version: aad.legacy_version,
1105
- random: aad.random,
1106
- legacy_session_id: aad.legacy_session_id,
1107
- cipher_suites: aad.cipher_suites,
1108
- legacy_compression_methods: aad.legacy_compression_methods,
1109
- extensions: aad.extensions.merge(
1110
- Message::ExtensionType::ENCRYPTED_CLIENT_HELLO => outer_ech
1111
- )
1112
- )
1113
- end
1114
-
1115
960
  # @param conf [HpkeKeyConfig]
1116
961
  #
1117
962
  # @return [HpkeSymmetricCipherSuite, nil]
@@ -1121,63 +966,6 @@ module TTTLS13
1121
966
  end
1122
967
  end
1123
968
 
1124
- # @return [Message::Extension::ECHClientHello]
1125
- def new_grease_ech
1126
- # https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#name-compliance-requirements
1127
- cipher_suite = HpkeSymmetricCipherSuite.new(
1128
- HpkeSymmetricCipherSuite::HpkeKdfId.new(
1129
- TTTLS13::Hpke::KdfId::HKDF_SHA256
1130
- ),
1131
- HpkeSymmetricCipherSuite::HpkeAeadId.new(
1132
- TTTLS13::Hpke::AeadId::AES_128_GCM
1133
- )
1134
- )
1135
- # Set the enc field to a randomly-generated valid encapsulated public key
1136
- # output by the HPKE KEM.
1137
- #
1138
- # https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#section-6.2-2.3.1
1139
- public_key = OpenSSL::PKey.read(
1140
- OpenSSL::PKey.generate_key('X25519').public_to_pem
1141
- )
1142
- hpke = HPKE.new(:x25519, :sha256, :sha256, :aes_128_gcm)
1143
- enc = hpke.setup_base_s(public_key, '')[:enc]
1144
- # Set the payload field to a randomly-generated string of L+C bytes, where
1145
- # C is the ciphertext expansion of the selected AEAD scheme and L is the
1146
- # size of the EncodedClientHelloInner the client would compute when
1147
- # offering ECH, padded according to Section 6.1.3.
1148
- #
1149
- # https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#section-6.2-2.4.1
1150
- payload_len = placeholder_encoded_ch_inner_len \
1151
- + Hpke.aead_id2overhead_len(Hpke::AeadId::AES_128_GCM)
1152
-
1153
- Message::Extension::ECHClientHello.new_outer(
1154
- cipher_suite: cipher_suite,
1155
- config_id: Convert.bin2i(OpenSSL::Random.random_bytes(1)),
1156
- enc: enc,
1157
- payload: OpenSSL::Random.random_bytes(payload_len)
1158
- )
1159
- end
1160
-
1161
- # @return [Integer]
1162
- def placeholder_encoded_ch_inner_len
1163
- 448
1164
- end
1165
-
1166
- # @param inner [TTTLS13::Message::ClientHello]
1167
- # @param ech [Message::Extension::ECHClientHello]
1168
- def new_greased_ch(inner, ech)
1169
- Message::ClientHello.new(
1170
- legacy_version: inner.legacy_version,
1171
- random: inner.random,
1172
- legacy_session_id: inner.legacy_session_id,
1173
- cipher_suites: inner.cipher_suites,
1174
- legacy_compression_methods: inner.legacy_compression_methods,
1175
- extensions: inner.extensions.merge(
1176
- Message::ExtensionType::ENCRYPTED_CLIENT_HELLO => ech
1177
- )
1178
- )
1179
- end
1180
-
1181
969
  # @return [Integer]
1182
970
  def calc_obfuscated_ticket_age
1183
971
  # the "ticket_lifetime" field in the NewSessionTicket message is
@@ -1227,7 +1015,7 @@ module TTTLS13
1227
1015
  # @param hrr [TTTLS13::Message::ServerHello]
1228
1016
  # @param extensions [TTTLS13::Message::Extensions]
1229
1017
  # @param binder_key [String, nil]
1230
- # @param ech_state [TTTLS13::Client::EchState]
1018
+ # @param ech_state [TTTLS13::EchState]
1231
1019
  #
1232
1020
  # @return [TTTLS13::Message::ClientHello] outer
1233
1021
  # @return [TTTLS13::Message::ClientHello] inner
@@ -1258,7 +1046,7 @@ module TTTLS13
1258
1046
  ch.extensions[Message::ExtensionType::ENCRYPTED_CLIENT_HELLO] \
1259
1047
  = ch1.extensions[Message::ExtensionType::ENCRYPTED_CLIENT_HELLO]
1260
1048
  elsif use_ech?
1261
- ch, inner = offer_new_ech(ch, ech_state)
1049
+ ch, inner = Ech.offer_new_ech(ch, ech_state)
1262
1050
  end
1263
1051
 
1264
1052
  # pre_shared_key
@@ -1286,8 +1074,8 @@ module TTTLS13
1286
1074
  end
1287
1075
  end
1288
1076
 
1289
- send_handshakes(Message::ContentType::HANDSHAKE, [ch],
1290
- Cryptograph::Passer.new)
1077
+ @connection.send_handshakes(Message::ContentType::HANDSHAKE, [ch],
1078
+ Cryptograph::Passer.new)
1291
1079
 
1292
1080
  [ch, inner]
1293
1081
  end
@@ -1299,11 +1087,12 @@ module TTTLS13
1299
1087
  # @return [TTTLS13::Message::ServerHello]
1300
1088
  # @return [String]
1301
1089
  def recv_server_hello
1302
- sh, orig_msg = recv_message(
1090
+ sh, orig_msg = @connection.recv_message(
1303
1091
  receivable_ccs: true,
1304
1092
  cipher: Cryptograph::Passer.new
1305
1093
  )
1306
- terminate(:unexpected_message) unless sh.is_a?(Message::ServerHello)
1094
+ @connection.terminate(:unexpected_message) \
1095
+ unless sh.is_a?(Message::ServerHello)
1307
1096
 
1308
1097
  [sh, orig_msg]
1309
1098
  end
@@ -1315,8 +1104,9 @@ module TTTLS13
1315
1104
  # @return [TTTLS13::Message::EncryptedExtensions]
1316
1105
  # @return [String]
1317
1106
  def recv_encrypted_extensions(cipher)
1318
- ee, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
1319
- terminate(:unexpected_message) \
1107
+ ee, orig_msg \
1108
+ = @connection.recv_message(receivable_ccs: true, cipher: cipher)
1109
+ @connection.terminate(:unexpected_message) \
1320
1110
  unless ee.is_a?(Message::EncryptedExtensions)
1321
1111
 
1322
1112
  [ee, orig_msg]
@@ -1329,8 +1119,10 @@ module TTTLS13
1329
1119
  # @return [TTTLS13::Message::Certificate]
1330
1120
  # @return [String]
1331
1121
  def recv_certificate(cipher)
1332
- ct, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
1333
- terminate(:unexpected_message) unless ct.is_a?(Message::Certificate)
1122
+ ct, orig_msg \
1123
+ = @connection.recv_message(receivable_ccs: true, cipher: cipher)
1124
+ @connection.terminate(:unexpected_message) \
1125
+ unless ct.is_a?(Message::Certificate)
1334
1126
 
1335
1127
  [ct, orig_msg]
1336
1128
  end
@@ -1342,8 +1134,10 @@ module TTTLS13
1342
1134
  # @return [TTTLS13::Message::CertificateVerify]
1343
1135
  # @return [String]
1344
1136
  def recv_certificate_verify(cipher)
1345
- cv, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
1346
- terminate(:unexpected_message) unless cv.is_a?(Message::CertificateVerify)
1137
+ cv, orig_msg \
1138
+ = @connection.recv_message(receivable_ccs: true, cipher: cipher)
1139
+ @connection.terminate(:unexpected_message) \
1140
+ unless cv.is_a?(Message::CertificateVerify)
1347
1141
 
1348
1142
  [cv, orig_msg]
1349
1143
  end
@@ -1355,8 +1149,10 @@ module TTTLS13
1355
1149
  # @return [TTTLS13::Message::Finished]
1356
1150
  # @return [String]
1357
1151
  def recv_finished(cipher)
1358
- sf, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
1359
- terminate(:unexpected_message) unless sf.is_a?(Message::Finished)
1152
+ sf, orig_msg \
1153
+ = @connection.recv_message(receivable_ccs: true, cipher: cipher)
1154
+ @connection.terminate(:unexpected_message) \
1155
+ unless sf.is_a?(Message::Finished)
1360
1156
 
1361
1157
  [sf, orig_msg]
1362
1158
  end
@@ -1366,7 +1162,11 @@ module TTTLS13
1366
1162
  # @return [TTTLS13::Message::Finished]
1367
1163
  def send_finished(signature, cipher)
1368
1164
  cf = Message::Finished.new(signature)
1369
- send_handshakes(Message::ContentType::APPLICATION_DATA, [cf], cipher)
1165
+ @connection.send_handshakes(
1166
+ Message::ContentType::APPLICATION_DATA,
1167
+ [cf],
1168
+ cipher
1169
+ )
1370
1170
 
1371
1171
  cf
1372
1172
  end
@@ -1376,7 +1176,11 @@ module TTTLS13
1376
1176
  # @return [TTTLS13::Message::EndOfEarlyData]
1377
1177
  def send_eoed(cipher)
1378
1178
  eoed = Message::EndOfEarlyData.new
1379
- send_handshakes(Message::ContentType::APPLICATION_DATA, [eoed], cipher)
1179
+ @connection.send_handshakes(
1180
+ Message::ContentType::APPLICATION_DATA,
1181
+ [eoed],
1182
+ cipher
1183
+ )
1380
1184
 
1381
1185
  eoed
1382
1186
  end
@@ -1392,7 +1196,7 @@ module TTTLS13
1392
1196
  unless ct.certificate_list.map(&:extensions)
1393
1197
  .all? { |e| (e.keys - ch.extensions.keys).empty? }
1394
1198
 
1395
- return :certificate_unknown unless trusted_certificate?(
1199
+ return :certificate_unknown unless Endpoint.trusted_certificate?(
1396
1200
  ct.certificate_list,
1397
1201
  @settings[:ca_file],
1398
1202
  @hostname
@@ -1421,7 +1225,7 @@ module TTTLS13
1421
1225
  signature_scheme = cv.signature_scheme
1422
1226
  signature = cv.signature
1423
1227
 
1424
- do_verified_certificate_verify?(
1228
+ Endpoint.verified_certificate_verify?(
1425
1229
  public_key: public_key,
1426
1230
  signature_scheme: signature_scheme,
1427
1231
  signature: signature,
@@ -1443,36 +1247,19 @@ module TTTLS13
1443
1247
  #
1444
1248
  # @raise [TTTLS13::Error::ErrorAlerts]
1445
1249
  def process_new_session_ticket(nst)
1446
- super(nst)
1447
-
1448
1250
  rms = @resumption_secret
1449
1251
  cs = @cipher_suite
1450
1252
  @settings[:process_new_session_ticket]&.call(nst, rms, cs)
1451
1253
  end
1452
1254
 
1453
- class EchState
1454
- attr_accessor :maximum_name_length
1455
- attr_accessor :config_id
1456
- attr_accessor :cipher_suite
1457
- attr_accessor :public_name
1458
- attr_accessor :ctx
1459
-
1460
- # @param maximum_name_length [Integer]
1461
- # @param config_id [Integer]
1462
- # @param cipher_suite [HpkeSymmetricCipherSuite]
1463
- # @param public_name [String]
1464
- # @param ctx [[HPKE::ContextS]
1465
- def initialize(maximum_name_length,
1466
- config_id,
1467
- cipher_suite,
1468
- public_name,
1469
- ctx)
1470
- @maximum_name_length = maximum_name_length
1471
- @config_id = config_id
1472
- @cipher_suite = cipher_suite
1473
- @public_name = public_name
1474
- @ctx = ctx
1475
- end
1255
+ # @param cid [OpenSSL::OCSP::CertificateId]
1256
+ #
1257
+ # @return [OpenSSL::OCSP::Request]
1258
+ def gen_ocsp_request(cid)
1259
+ ocsp_request = OpenSSL::OCSP::Request.new
1260
+ ocsp_request.add_certid(cid)
1261
+ ocsp_request.add_nonce
1262
+ ocsp_request
1476
1263
  end
1477
1264
  end
1478
1265
  # rubocop: enable Metrics/ClassLength