tttls1.3 0.3.1 → 0.3.2

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.
@@ -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