tttls1.3 0.3.1 → 0.3.3

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/Gemfile +1 -0
  4. data/example/helper.rb +43 -0
  5. data/example/https_client_using_0rtt.rb +5 -3
  6. data/example/https_client_using_ech.rb +6 -7
  7. data/example/https_client_using_grease_ech.rb +0 -2
  8. data/example/https_client_using_hrr.rb +2 -1
  9. data/example/https_client_using_hrr_and_ech.rb +6 -7
  10. data/example/https_client_using_hrr_and_ticket.rb +4 -2
  11. data/example/https_client_using_status_request.rb +2 -1
  12. data/example/https_client_using_ticket.rb +4 -2
  13. data/example/https_client_using_ticket_and_ech.rb +57 -0
  14. data/example/https_server.rb +14 -1
  15. data/lib/tttls1.3/client.rb +205 -418
  16. data/lib/tttls1.3/connection.rb +21 -362
  17. data/lib/tttls1.3/ech.rb +426 -0
  18. data/lib/tttls1.3/endpoint.rb +276 -0
  19. data/lib/tttls1.3/message/certificate_verify.rb +1 -1
  20. data/lib/tttls1.3/message/extension/ech.rb +21 -24
  21. data/lib/tttls1.3/message/extension/ech_outer_extensions.rb +52 -0
  22. data/lib/tttls1.3/message/extension/signature_algorithms.rb +2 -2
  23. data/lib/tttls1.3/message/extension/supported_versions.rb +3 -3
  24. data/lib/tttls1.3/message/extension/unknown_extension.rb +2 -2
  25. data/lib/tttls1.3/message/extensions.rb +30 -0
  26. data/lib/tttls1.3/message.rb +1 -0
  27. data/lib/tttls1.3/server.rb +125 -63
  28. data/lib/tttls1.3/utils.rb +37 -0
  29. data/lib/tttls1.3/version.rb +1 -1
  30. data/lib/tttls1.3.rb +2 -1
  31. data/spec/client_spec.rb +21 -60
  32. data/spec/ech_outer_extensions_spec.rb +42 -0
  33. data/spec/ech_spec.rb +41 -0
  34. data/spec/{connection_spec.rb → endpoint_spec.rb} +41 -49
  35. data/spec/extensions_spec.rb +65 -0
  36. data/spec/server_spec.rb +12 -12
  37. data/spec/spec_helper.rb +4 -0
  38. metadata +11 -6
  39. data/lib/tttls1.3/hpke.rb +0 -91
@@ -26,12 +26,12 @@ module TTTLS13
26
26
  # };
27
27
  # } ECHClientHello;
28
28
  class ECHClientHello
29
- attr_accessor :extension_type
30
- attr_accessor :type
31
- attr_accessor :cipher_suite
32
- attr_accessor :config_id
33
- attr_accessor :enc
34
- attr_accessor :payload
29
+ attr_reader :extension_type
30
+ attr_reader :type
31
+ attr_reader :cipher_suite
32
+ attr_reader :config_id
33
+ attr_reader :enc
34
+ attr_reader :payload
35
35
 
36
36
  # @param type [TTTLS13::Message::Extension::ECHClientHelloType]
37
37
  # @param cipher_suite [HpkeSymmetricCipherSuite]
@@ -100,8 +100,9 @@ module TTTLS13
100
100
  #
101
101
  # @return [TTTLS13::Message::Extensions::ECHClientHello]
102
102
  def deserialize_outer_ech(binary)
103
- raise Error::ErrorAlerts, :internal_error \
104
- if binary.nil? || binary.length < 5
103
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
104
+
105
+ return nil if binary.length < 5
105
106
 
106
107
  kdf_id = \
107
108
  HpkeSymmetricCipherSuite::HpkeKdfId.decode(binary.slice(0, 2))
@@ -111,17 +112,14 @@ module TTTLS13
111
112
  cid = Convert.bin2i(binary.slice(4, 1))
112
113
  enc_len = Convert.bin2i(binary.slice(5, 2))
113
114
  i = 7
114
- raise Error::ErrorAlerts, :internal_error \
115
- if i + enc_len > binary.length
115
+ return nil if i + enc_len > binary.length
116
116
 
117
117
  enc = binary.slice(i, enc_len)
118
118
  i += enc_len
119
- raise Error::ErrorAlerts, :internal_error \
120
- if i + 2 > binary.length
119
+ return nil if i + 2 > binary.length
121
120
 
122
121
  payload_len = Convert.bin2i(binary.slice(i, 2))
123
- raise Error::ErrorAlerts, :internal_error \
124
- if i + payload_len > binary.length
122
+ return nil if i + payload_len > binary.length
125
123
 
126
124
  payload = binary.slice(i, payload_len)
127
125
  ECHClientHello.new(
@@ -139,7 +137,7 @@ module TTTLS13
139
137
  #
140
138
  # @return [TTTLS13::Message::Extensions::ECHClientHello]
141
139
  def deserialize_inner_ech(binary)
142
- raise Error::ErrorAlerts, :internal_error unless binary.empty?
140
+ raise Error::ErrorAlerts, :illegal_parameter unless binary.empty?
143
141
 
144
142
  ECHClientHello.new(type: ECHClientHelloType::INNER)
145
143
  end
@@ -172,8 +170,8 @@ module TTTLS13
172
170
  # ECHConfigList retry_configs;
173
171
  # } ECHEncryptedExtensions;
174
172
  class ECHEncryptedExtensions
175
- attr_accessor :extension_type
176
- attr_accessor :retry_configs
173
+ attr_reader :extension_type
174
+ attr_reader :retry_configs
177
175
 
178
176
  # @param retry_configs [Array of ECHConfig]
179
177
  def initialize(retry_configs)
@@ -195,9 +193,8 @@ module TTTLS13
195
193
  #
196
194
  # @return [TTTLS13::Message::Extensions::ECHEncryptedExtensions]
197
195
  def self.deserialize(binary)
198
- raise Error::ErrorAlerts, :decode_error \
199
- if binary.nil? ||
200
- binary.length != binary.slice(0, 2).unpack1('n') + 2
196
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
197
+ return nil if binary.length != binary.slice(0, 2).unpack1('n') + 2
201
198
 
202
199
  ECHEncryptedExtensions.new(
203
200
  ECHConfig.decode_vectors(binary.slice(2..))
@@ -210,8 +207,8 @@ module TTTLS13
210
207
  # opaque confirmation[8];
211
208
  # } ECHHelloRetryRequest;
212
209
  class ECHHelloRetryRequest
213
- attr_accessor :extension_type
214
- attr_accessor :confirmation
210
+ attr_reader :extension_type
211
+ attr_reader :confirmation
215
212
 
216
213
  # @param confirmation [String]
217
214
  def initialize(confirmation)
@@ -230,8 +227,8 @@ module TTTLS13
230
227
  #
231
228
  # @return [TTTLS13::Message::Extensions::ECHHelloRetryRequest]
232
229
  def self.deserialize(binary)
233
- raise Error::ErrorAlerts, :decode_error \
234
- if binary.nil? || binary.length != 8
230
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
231
+ return nil if binary.length != 8
235
232
 
236
233
  ECHHelloRetryRequest.new(binary)
237
234
  end
@@ -0,0 +1,52 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ # NOTE:
9
+ # ExtensionType OuterExtensions<2..254>;
10
+ class ECHOuterExtensions
11
+ attr_reader :extension_type
12
+ attr_reader :outer_extensions
13
+
14
+ # @param outer_extensions [Array of TTTLS13::Message::ExtensionType]
15
+ def initialize(outer_extensions)
16
+ @extension_type = ExtensionType::ECH_OUTER_EXTENSIONS
17
+ @outer_extensions = outer_extensions
18
+ end
19
+
20
+ # @raise [TTTLS13::Error::ErrorAlerts]
21
+ #
22
+ # @return [String]
23
+ def serialize
24
+ binary = @outer_extensions.join.prefix_uint8_length
25
+ @extension_type + binary.prefix_uint16_length
26
+ end
27
+
28
+ # @param binary [String]
29
+ #
30
+ # @raise [TTTLS13::Error::ErrorAlerts]
31
+ #
32
+ # @return [TTTLS13::Message::Extensions::ECHOuterExtensions]
33
+ def self.deserialize(binary)
34
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
35
+
36
+ return nil if binary.length < 2
37
+
38
+ exlist_len = Convert.bin2i(binary.slice(0, 1))
39
+ i = 1
40
+ outer_extensions = []
41
+ while i < exlist_len + 1
42
+ outer_extensions << binary.slice(i, 2)
43
+ i += 2
44
+ end
45
+ return nil unless outer_extensions.length * 2 == exlist_len
46
+
47
+ ECHOuterExtensions.new(outer_extensions)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -21,8 +21,8 @@ module TTTLS13
21
21
  SignatureScheme::RSA_PKCS1_SHA512
22
22
  ].freeze
23
23
 
24
- attr_accessor :extension_type # for signature_algorithms_cert getter
25
- attr_reader :supported_signature_algorithms
24
+ attr_reader :extension_type # for signature_algorithms_cert getter
25
+ attr_reader :supported_signature_algorithms
26
26
 
27
27
  # @param supported_signature_algorithms [Array of SignatureScheme]
28
28
  def initialize(supported_signature_algorithms)
@@ -6,9 +6,9 @@ module TTTLS13
6
6
  module Message
7
7
  module Extension
8
8
  class SupportedVersions
9
- attr_reader :extension_type
10
- attr_accessor :msg_type
11
- attr_accessor :versions
9
+ attr_reader :extension_type
10
+ attr_reader :msg_type
11
+ attr_reader :versions
12
12
 
13
13
  # @param msg_type [TTTLS13::Message::ContentType]
14
14
  # @param versions [Array of ProtocolVersion]
@@ -9,8 +9,8 @@ module TTTLS13
9
9
  # Client/Server MUST ignore unrecognized extensions,
10
10
  # but transcript MUST include unrecognized extensions.
11
11
  class UnknownExtension
12
- attr_accessor :extension_type
13
- attr_accessor :extension_data
12
+ attr_reader :extension_type
13
+ attr_reader :extension_data
14
14
 
15
15
  # @param extension_type [String]
16
16
  # @param extension_data [String]
@@ -105,6 +105,34 @@ module TTTLS13
105
105
  store(ex.extension_type, ex)
106
106
  end
107
107
 
108
+ # removing and replacing extensions from EncodedClientHelloInner
109
+ # with a single "ech_outer_extensions"
110
+ #
111
+ # for example
112
+ # - before
113
+ # - self.keys: [A B C D E]
114
+ # - param : [D B]
115
+ # - after remove_and_replace!
116
+ # - self.keys: [A C E B D]
117
+ # - return : [A C E ech_outer_extensions[B D]]
118
+ # @param outer_extensions [Array of TTTLS13::Message::ExtensionType]
119
+ #
120
+ # @return [TTTLS13::Message::Extensions] for EncodedClientHelloInner
121
+ def remove_and_replace!(outer_extensions)
122
+ tmp1 = filter { |k, _| !outer_extensions.include?(k) }
123
+ tmp2 = filter { |k, _| outer_extensions.include?(k) }
124
+
125
+ clear
126
+ replaced = Message::Extensions.new
127
+
128
+ tmp1.each_value { |v| self << v; replaced << v }
129
+ tmp2.each_value { |v| self << v }
130
+ replaced << Message::Extension::ECHOuterExtensions.new(tmp2.keys) \
131
+ unless tmp2.keys.empty?
132
+
133
+ replaced
134
+ end
135
+
108
136
  class << self
109
137
  private
110
138
 
@@ -173,6 +201,8 @@ module TTTLS13
173
201
  else
174
202
  Extension::UnknownExtension.deserialize(binary, extension_type)
175
203
  end
204
+ when ExtensionType::ECH_OUTER_EXTENSIONS
205
+ Extension::ECHOuterExtensions.deserialize(binary)
176
206
  else
177
207
  Extension::UnknownExtension.deserialize(binary, extension_type)
178
208
  end
@@ -74,6 +74,7 @@ module TTTLS13
74
74
  KEY_SHARE = "\x00\x33"
75
75
  # https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#section-11.1
76
76
  ENCRYPTED_CLIENT_HELLO = "\xfe\x0d"
77
+ ECH_OUTER_EXTENSIONS = "\xfd\x00"
77
78
  end
78
79
 
79
80
  DEFINED_EXTENSIONS = ExtensionType.constants.map do |c|
@@ -66,13 +66,15 @@ module TTTLS13
66
66
  private_constant :DEFAULT_SERVER_SETTINGS
67
67
 
68
68
  # rubocop: disable Metrics/ClassLength
69
- class Server < Connection
69
+ class Server
70
+ include Logging
71
+
72
+ attr_reader :transcript
73
+
70
74
  # @param socket [Socket]
71
75
  # @param settings [Hash]
72
76
  def initialize(socket, **settings)
73
- super(socket)
74
-
75
- @endpoint = :server
77
+ @connection = Connection.new(socket, :server)
76
78
  @settings = DEFAULT_SERVER_SETTINGS.merge(settings)
77
79
  logger.level = @settings[:loglevel]
78
80
 
@@ -144,7 +146,7 @@ module TTTLS13
144
146
  # rubocop: disable Metrics/MethodLength
145
147
  # rubocop: disable Metrics/PerceivedComplexity
146
148
  def accept
147
- transcript = Transcript.new
149
+ @transcript = Transcript.new
148
150
  key_schedule = nil # TTTLS13::KeySchedule
149
151
  priv_key = nil # OpenSSL::PKey::$Object
150
152
  hs_wcipher = nil # TTTLS13::Cryptograph::$Object
@@ -159,24 +161,27 @@ module TTTLS13
159
161
  end
160
162
  end
161
163
 
162
- @state = ServerState::START
164
+ @connection.state = ServerState::START
163
165
  loop do
164
- case @state
166
+ case @connection.state
165
167
  when ServerState::START
166
168
  logger.debug('ServerState::START')
167
169
 
168
- receivable_ccs = transcript.include?(CH1)
169
- ch, = transcript[CH] = recv_client_hello(receivable_ccs)
170
+ receivable_ccs = @transcript.include?(CH1)
171
+ ch, = @transcript[CH] = recv_client_hello(receivable_ccs)
170
172
 
171
173
  # support only TLS 1.3
172
- terminate(:protocol_version) unless ch.negotiated_tls_1_3?
174
+ @connection.terminate(:protocol_version) unless ch.negotiated_tls_1_3?
173
175
 
174
176
  # validate parameters
175
- terminate(:illegal_parameter) unless ch.appearable_extensions?
176
- terminamte(:illegal_parameter) \
177
+ @connection.terminate(:illegal_parameter) \
178
+ unless ch.appearable_extensions?
179
+ @connection.terminate(:illegal_parameter) \
177
180
  unless ch.legacy_compression_methods == ["\x00"]
178
- terminate(:illegal_parameter) unless ch.valid_key_share?
179
- terminate(:unrecognized_name) unless recognized_server_name?(ch, @crt)
181
+ @connection.terminate(:illegal_parameter) \
182
+ unless ch.valid_key_share?
183
+ @connection.terminate(:unrecognized_name) \
184
+ unless recognized_server_name?(ch, @crt)
180
185
 
181
186
  # alpn
182
187
  ch_alpn = ch.extensions[
@@ -186,109 +191,109 @@ module TTTLS13
186
191
  @alpn = ch_alpn.protocol_name_list
187
192
  .find { |p| @settings[:alpn].include?(p) }
188
193
 
189
- terminate(:no_application_protocol) if @alpn.nil?
194
+ @connection.terminate(:no_application_protocol) if @alpn.nil?
190
195
  end
191
196
 
192
197
  # record_size_limit
193
198
  ch_rsl = ch.extensions[Message::ExtensionType::RECORD_SIZE_LIMIT]
194
199
  @send_record_size = ch_rsl.record_size_limit unless ch_rsl.nil?
195
200
 
196
- @state = ServerState::RECVD_CH
201
+ @connection.state = ServerState::RECVD_CH
197
202
  when ServerState::RECVD_CH
198
203
  logger.debug('ServerState::RECVD_CH')
199
204
 
200
205
  # select parameters
201
- ch, = transcript[CH]
206
+ ch, = @transcript[CH]
202
207
  @cipher_suite = select_cipher_suite(ch)
203
208
  @named_group = select_named_group(ch)
204
209
  @signature_scheme = select_signature_scheme(ch, @crt)
205
- terminate(:handshake_failure) \
210
+ @connection.terminate(:handshake_failure) \
206
211
  if @cipher_suite.nil? || @signature_scheme.nil?
207
212
 
208
213
  # send HRR
209
214
  if @named_group.nil?
210
- ch1, = transcript[CH1] = transcript.delete(CH)
215
+ ch1, = @transcript[CH1] = @transcript.delete(CH)
211
216
  hrr = send_hello_retry_request(ch1, @cipher_suite)
212
- transcript[HRR] = [hrr, hrr.serialize]
213
- @state = ServerState::START
217
+ @transcript[HRR] = [hrr, hrr.serialize]
218
+ @connection.state = ServerState::START
214
219
  next
215
220
  end
216
- @state = ServerState::NEGOTIATED
221
+ @connection.state = ServerState::NEGOTIATED
217
222
  when ServerState::NEGOTIATED
218
223
  logger.debug('ServerState::NEGOTIATED')
219
224
 
220
- ch, = transcript[CH]
225
+ ch, = @transcript[CH]
221
226
  extensions, priv_key = gen_sh_extensions(@named_group)
222
227
  sh = send_server_hello(
223
228
  extensions,
224
229
  @cipher_suite,
225
230
  ch.legacy_session_id
226
231
  )
227
- transcript[SH] = [sh, sh.serialize]
228
- send_ccs if @settings[:compatibility_mode]
232
+ @transcript[SH] = [sh, sh.serialize]
233
+ @connection.send_ccs if @settings[:compatibility_mode]
229
234
 
230
235
  # generate shared secret
231
236
  ke = ch.extensions[Message::ExtensionType::KEY_SHARE]
232
237
  &.key_share_entry
233
238
  &.find { |kse| kse.group == @named_group }
234
239
  &.key_exchange
235
- shared_secret = gen_shared_secret(ke, priv_key, @named_group)
240
+ shared_secret = Endpoint.gen_shared_secret(ke, priv_key, @named_group)
236
241
  key_schedule = KeySchedule.new(
237
242
  psk: @psk,
238
243
  shared_secret: shared_secret,
239
244
  cipher_suite: @cipher_suite,
240
- transcript: transcript
245
+ transcript: @transcript
241
246
  )
242
- @alert_wcipher = hs_wcipher = gen_cipher(
247
+ @connection.alert_wcipher = hs_wcipher = Endpoint.gen_cipher(
243
248
  @cipher_suite,
244
249
  key_schedule.server_handshake_write_key,
245
250
  key_schedule.server_handshake_write_iv
246
251
  )
247
252
  sslkeylogfile&.write_server_handshake_traffic_secret(
248
- transcript[CH].first.random,
253
+ @transcript[CH].first.random,
249
254
  key_schedule.server_handshake_traffic_secret
250
255
  )
251
- hs_rcipher = gen_cipher(
256
+ hs_rcipher = Endpoint.gen_cipher(
252
257
  @cipher_suite,
253
258
  key_schedule.client_handshake_write_key,
254
259
  key_schedule.client_handshake_write_iv
255
260
  )
256
261
  sslkeylogfile&.write_client_handshake_traffic_secret(
257
- transcript[CH].first.random,
262
+ @transcript[CH].first.random,
258
263
  key_schedule.client_handshake_traffic_secret
259
264
  )
260
- @state = ServerState::WAIT_FLIGHT2
265
+ @connection.state = ServerState::WAIT_FLIGHT2
261
266
  when ServerState::WAIT_EOED
262
267
  logger.debug('ServerState::WAIT_EOED')
263
268
  when ServerState::WAIT_FLIGHT2
264
269
  logger.debug('ServerState::WAIT_FLIGHT2')
265
270
 
266
- ch, = transcript[CH]
271
+ ch, = @transcript[CH]
267
272
  rsl = @send_record_size \
268
273
  if ch.extensions.include?(Message::ExtensionType::RECORD_SIZE_LIMIT)
269
274
  ee = gen_encrypted_extensions(ch, @alpn, rsl)
270
- transcript[EE] = [ee, ee.serialize]
275
+ @transcript[EE] = [ee, ee.serialize]
271
276
  # TODO: [Send CertificateRequest]
272
277
 
273
278
  # status_request
274
279
  ocsp_response = fetch_ocsp_response \
275
280
  if ch.extensions.include?(Message::ExtensionType::STATUS_REQUEST)
276
281
  ct = gen_certificate(@crt, ch, @chain, ocsp_response)
277
- transcript[CT] = [ct, ct.serialize]
282
+ @transcript[CT] = [ct, ct.serialize]
278
283
  digest = CipherSuite.digest(@cipher_suite)
279
- hash = transcript.hash(digest, CT)
284
+ hash = @transcript.hash(digest, CT)
280
285
  cv = gen_certificate_verify(@key, @signature_scheme, hash)
281
- transcript[CV] = [cv, cv.serialize]
286
+ @transcript[CV] = [cv, cv.serialize]
282
287
  finished_key = key_schedule.server_finished_key
283
- signature = sign_finished(
288
+ signature = Endpoint.sign_finished(
284
289
  digest: digest,
285
290
  finished_key: finished_key,
286
- hash: transcript.hash(digest, CV)
291
+ hash: @transcript.hash(digest, CV)
287
292
  )
288
293
  sf = Message::Finished.new(signature)
289
- transcript[SF] = [sf, sf.serialize]
294
+ @transcript[SF] = [sf, sf.serialize]
290
295
  send_server_parameters([ee, ct, cv, sf], hs_wcipher)
291
- @state = ServerState::WAIT_FINISHED
296
+ @connection.state = ServerState::WAIT_FINISHED
292
297
  when ServerState::WAIT_CERT
293
298
  logger.debug('ServerState::WAIT_CERT')
294
299
  when ServerState::WAIT_CV
@@ -296,35 +301,36 @@ module TTTLS13
296
301
  when ServerState::WAIT_FINISHED
297
302
  logger.debug('ServerState::WAIT_FINISHED')
298
303
 
299
- cf, = transcript[CF] = recv_finished(hs_rcipher)
304
+ cf, = @transcript[CF] = recv_finished(hs_rcipher)
300
305
  digest = CipherSuite.digest(@cipher_suite)
301
- verified = verified_finished?(
306
+ verified = Endpoint.verified_finished?(
302
307
  finished: cf,
303
308
  digest: digest,
304
309
  finished_key: key_schedule.client_finished_key,
305
- hash: transcript.hash(digest, EOED)
310
+ hash: @transcript.hash(digest, EOED)
306
311
  )
307
- terminate(:decrypt_error) unless verified
308
- @alert_wcipher = @ap_wcipher = gen_cipher(
312
+ @connection.terminate(:decrypt_error) unless verified
313
+ @connection.ap_wcipher = Endpoint.gen_cipher(
309
314
  @cipher_suite,
310
315
  key_schedule.server_application_write_key,
311
316
  key_schedule.server_application_write_iv
312
317
  )
318
+ @connection.alert_wcipher = @connection.ap_wcipher
313
319
  sslkeylogfile&.write_server_traffic_secret_0(
314
- transcript[CH].first.random,
320
+ @transcript[CH].first.random,
315
321
  key_schedule.server_application_traffic_secret
316
322
  )
317
- @ap_rcipher = gen_cipher(
323
+ @connection.ap_rcipher = Endpoint.gen_cipher(
318
324
  @cipher_suite,
319
325
  key_schedule.client_application_write_key,
320
326
  key_schedule.client_application_write_iv
321
327
  )
322
328
  sslkeylogfile&.write_client_traffic_secret_0(
323
- transcript[CH].first.random,
329
+ @transcript[CH].first.random,
324
330
  key_schedule.client_application_traffic_secret
325
331
  )
326
332
  @exporter_secret = key_schedule.exporter_secret
327
- @state = ServerState::CONNECTED
333
+ @connection.state = ServerState::CONNECTED
328
334
  when ServerState::CONNECTED
329
335
  logger.debug('ServerState::CONNECTED')
330
336
 
@@ -339,6 +345,59 @@ module TTTLS13
339
345
  # rubocop: enable Metrics/MethodLength
340
346
  # rubocop: enable Metrics/PerceivedComplexity
341
347
 
348
+ # @raise [TTTLS13::Error::ConfigError]
349
+ #
350
+ # @return [String]
351
+ def read
352
+ @connection.read(nil)
353
+ end
354
+
355
+ # @param binary [String]
356
+ def write(binary)
357
+ @connection.write(binary)
358
+ end
359
+
360
+ # @return [Boolean]
361
+ def eof?
362
+ @connection.eof?
363
+ end
364
+
365
+ def close
366
+ @connection.close
367
+ end
368
+
369
+ # @return [TTTLS13::CipherSuite, nil]
370
+ def negotiated_cipher_suite
371
+ @cipher_suite
372
+ end
373
+
374
+ # @return [TTTLS13::NamedGroup, nil]
375
+ def negotiated_named_group
376
+ @named_group
377
+ end
378
+
379
+ # @return [TTTLS13::SignatureScheme, nil]
380
+ def negotiated_signature_scheme
381
+ @signature_scheme
382
+ end
383
+
384
+ # @return [String]
385
+ def negotiated_alpn
386
+ @alpn
387
+ end
388
+
389
+ # @param label [String]
390
+ # @param context [String]
391
+ # @param key_length [Integer]
392
+ #
393
+ # @return [String, nil]
394
+ def exporter(label, context, key_length)
395
+ return nil if @exporter_secret.nil? || @cipher_suite.nil?
396
+
397
+ digest = CipherSuite.digest(@cipher_suite)
398
+ Endpoint.exporter(@exporter_secret, digest, label, context, key_length)
399
+ end
400
+
342
401
  private
343
402
 
344
403
  # @return [Boolean]
@@ -365,11 +424,12 @@ module TTTLS13
365
424
  # @return [TTTLS13::Message::ClientHello]
366
425
  # @return [String]
367
426
  def recv_client_hello(receivable_ccs)
368
- ch, orig_msg = recv_message(
427
+ ch, orig_msg = @connection.recv_message(
369
428
  receivable_ccs: receivable_ccs,
370
429
  cipher: Cryptograph::Passer.new
371
430
  )
372
- terminate(:unexpected_message) unless ch.is_a?(Message::ClientHello)
431
+ @connection.terminate(:unexpected_message) \
432
+ unless ch.is_a?(Message::ClientHello)
373
433
 
374
434
  [ch, orig_msg]
375
435
  end
@@ -385,8 +445,8 @@ module TTTLS13
385
445
  cipher_suite: cipher_suite,
386
446
  extensions: extensions
387
447
  )
388
- send_handshakes(Message::ContentType::HANDSHAKE, [sh],
389
- Cryptograph::Passer.new)
448
+ @connection.send_handshakes(Message::ContentType::HANDSHAKE, [sh],
449
+ Cryptograph::Passer.new)
390
450
 
391
451
  sh
392
452
  end
@@ -420,8 +480,8 @@ module TTTLS13
420
480
  cipher_suite: cipher_suite,
421
481
  extensions: exs
422
482
  )
423
- send_handshakes(Message::ContentType::HANDSHAKE, [sh],
424
- Cryptograph::Passer.new)
483
+ @connection.send_handshakes(Message::ContentType::HANDSHAKE, [sh],
484
+ Cryptograph::Passer.new)
425
485
 
426
486
  sh
427
487
  end
@@ -431,8 +491,8 @@ module TTTLS13
431
491
  #
432
492
  # @return [Array of TTTLS13::Message::$Object]
433
493
  def send_server_parameters(messages, cipher)
434
- send_handshakes(Message::ContentType::APPLICATION_DATA,
435
- messages.reject(&:nil?), cipher)
494
+ @connection.send_handshakes(Message::ContentType::APPLICATION_DATA,
495
+ messages.reject(&:nil?), cipher)
436
496
 
437
497
  messages
438
498
  end
@@ -505,8 +565,10 @@ module TTTLS13
505
565
  # @return [TTTLS13::Message::Finished]
506
566
  # @return [String]
507
567
  def recv_finished(cipher)
508
- cf, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
509
- terminate(:unexpected_message) unless cf.is_a?(Message::Finished)
568
+ cf, orig_msg \
569
+ = @connection.recv_message(receivable_ccs: true, cipher: cipher)
570
+ @connection.terminate(:unexpected_message) \
571
+ unless cf.is_a?(Message::Finished)
510
572
 
511
573
  [cf, orig_msg]
512
574
  end
@@ -563,7 +625,7 @@ module TTTLS13
563
625
  #
564
626
  # @return [String]
565
627
  def sign_certificate_verify(key:, signature_scheme:, hash:)
566
- do_sign_certificate_verify(
628
+ Endpoint.sign_certificate_verify(
567
629
  key: key,
568
630
  signature_scheme: signature_scheme,
569
631
  context: 'TLS 1.3, server CertificateVerify',
@@ -600,7 +662,7 @@ module TTTLS13
600
662
  algorithms = ch.extensions[Message::ExtensionType::SIGNATURE_ALGORITHMS]
601
663
  &.supported_signature_algorithms || []
602
664
 
603
- do_select_signature_algorithms(algorithms, crt).find do |ss|
665
+ Endpoint.select_signature_algorithms(algorithms, crt).find do |ss|
604
666
  @settings[:signature_algorithms].include?(ss)
605
667
  end
606
668
  end
@@ -614,7 +676,7 @@ module TTTLS13
614
676
  &.server_name
615
677
  return true if server_name.nil?
616
678
 
617
- matching_san?(crt, server_name)
679
+ Endpoint.matching_san?(crt, server_name)
618
680
  end
619
681
 
620
682
  # @return [OpenSSL::OCSP::Response, nil]
@@ -91,6 +91,43 @@ module TTTLS13
91
91
  def bin2i(binary)
92
92
  OpenSSL::BN.new(binary, 2).to_i
93
93
  end
94
+
95
+ # rubocop: disable Metrics/AbcSize
96
+ # rubocop: disable Metrics/CyclomaticComplexity
97
+ # rubocop: disable Metrics/PerceivedComplexity
98
+ def obj2html(obj)
99
+ if obj.is_a?(OpenSSL::X509::Certificate)
100
+ obj.to_pem.gsub("\n", '<br>')
101
+ elsif obj.is_a?(Numeric) ||
102
+ obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
103
+ obj.pretty_print_inspect
104
+ elsif obj.is_a?(String) && obj.empty?
105
+ ''
106
+ elsif obj.is_a? String
107
+ '0x' + obj.unpack1('H*')
108
+ elsif obj.is_a? NilClass
109
+ ''
110
+ elsif obj.is_a? Array
111
+ '<ul>' + obj.map { |i| '<li>' + obj2html(i) + '</li>' }.join + '</ul>'
112
+ elsif obj.is_a? Hash
113
+ obj.map do |k, v|
114
+ '<details><summary>' + obj2html(k) + '</summary>' \
115
+ + obj2html(v) \
116
+ + '</details>'
117
+ end.join
118
+ elsif obj.is_a?(Object) && !obj.instance_variables.empty?
119
+ obj.instance_variables.map do |i|
120
+ '<details><summary>' + i[1..] + '</summary>' \
121
+ + obj2html(obj.instance_variable_get(i)) \
122
+ + '</details>'
123
+ end.join
124
+ else
125
+ obj.class.name
126
+ end
127
+ end
128
+ # rubocop: enable Metrics/AbcSize
129
+ # rubocop: enable Metrics/CyclomaticComplexity
130
+ # rubocop: enable Metrics/PerceivedComplexity
94
131
  end
95
132
  end
96
133
  end