tttls1.3 0.2.18 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +8 -5
  3. data/Gemfile +2 -0
  4. data/README.md +6 -3
  5. data/example/helper.rb +5 -2
  6. data/example/https_client_using_0rtt.rb +1 -1
  7. data/example/https_client_using_ech.rb +32 -0
  8. data/example/https_client_using_grease_ech.rb +26 -0
  9. data/example/https_client_using_grease_psk.rb +66 -0
  10. data/example/https_client_using_hrr_and_ech.rb +32 -0
  11. data/example/https_client_using_hrr_and_ticket.rb +1 -1
  12. data/example/https_client_using_ticket.rb +1 -1
  13. data/interop/client_spec.rb +3 -2
  14. data/interop/server_spec.rb +1 -3
  15. data/interop/{helper.rb → spec_helper.rb} +12 -5
  16. data/lib/tttls1.3/client.rb +553 -32
  17. data/lib/tttls1.3/connection.rb +9 -8
  18. data/lib/tttls1.3/cryptograph/aead.rb +1 -1
  19. data/lib/tttls1.3/error.rb +1 -1
  20. data/lib/tttls1.3/hpke.rb +91 -0
  21. data/lib/tttls1.3/key_schedule.rb +111 -8
  22. data/lib/tttls1.3/message/alert.rb +2 -1
  23. data/lib/tttls1.3/message/client_hello.rb +2 -1
  24. data/lib/tttls1.3/message/encrypted_extensions.rb +2 -1
  25. data/lib/tttls1.3/message/extension/alpn.rb +4 -5
  26. data/lib/tttls1.3/message/extension/compress_certificate.rb +1 -1
  27. data/lib/tttls1.3/message/extension/ech.rb +241 -0
  28. data/lib/tttls1.3/message/extension/key_share.rb +2 -4
  29. data/lib/tttls1.3/message/extension/server_name.rb +1 -1
  30. data/lib/tttls1.3/message/extensions.rb +20 -7
  31. data/lib/tttls1.3/message/record.rb +1 -1
  32. data/lib/tttls1.3/message/server_hello.rb +3 -5
  33. data/lib/tttls1.3/message.rb +3 -1
  34. data/lib/tttls1.3/named_group.rb +1 -1
  35. data/lib/tttls1.3/server.rb +2 -2
  36. data/lib/tttls1.3/utils.rb +8 -0
  37. data/lib/tttls1.3/version.rb +1 -1
  38. data/lib/tttls1.3.rb +4 -0
  39. data/spec/client_spec.rb +40 -0
  40. data/spec/connection_spec.rb +22 -7
  41. data/spec/ech_spec.rb +81 -0
  42. data/spec/extensions_spec.rb +1 -2
  43. data/spec/key_schedule_spec.rb +2 -2
  44. data/spec/server_spec.rb +22 -7
  45. data/spec/spec_helper.rb +41 -5
  46. data/tttls1.3.gemspec +2 -0
  47. metadata +39 -3
@@ -5,6 +5,9 @@ module TTTLS13
5
5
  INITIAL = 0
6
6
  EOF = -1
7
7
 
8
+ SUPPORTED_ECHCONFIG_VERSIONS = ["\xfe\x0d"].freeze
9
+ private_constant :SUPPORTED_ECHCONFIG_VERSIONS
10
+
8
11
  # rubocop: disable Metrics/ClassLength
9
12
  class Connection
10
13
  include Logging
@@ -25,7 +28,7 @@ module TTTLS13
25
28
  @send_record_size = Message::DEFAULT_RECORD_SIZE_LIMIT
26
29
  @recv_record_size = Message::DEFAULT_RECORD_SIZE_LIMIT
27
30
  @alpn = nil # String
28
- @exporter_master_secret = nil # String
31
+ @exporter_secret = nil # String
29
32
  end
30
33
 
31
34
  # @raise [TTTLS13::Error::ConfigError]
@@ -109,15 +112,15 @@ module TTTLS13
109
112
  #
110
113
  # @return [String, nil]
111
114
  def exporter(label, context, key_length)
112
- return nil if @exporter_master_secret.nil? || @cipher_suite.nil?
115
+ return nil if @exporter_secret.nil? || @cipher_suite.nil?
113
116
 
114
117
  digest = CipherSuite.digest(@cipher_suite)
115
- do_exporter(@exporter_master_secret, digest, label, context, key_length)
118
+ do_exporter(@exporter_secret, digest, label, context, key_length)
116
119
  end
117
120
 
118
121
  private
119
122
 
120
- # @param secret [String] (early_)exporter_master_secret
123
+ # @param secret [String] (early_)exporter_secret
121
124
  # @param digest [String] name of digest algorithm
122
125
  # @param label [String]
123
126
  # @param context [String]
@@ -517,10 +520,8 @@ module TTTLS13
517
520
  #
518
521
  # @return [Array of TTTLS13::Message::Extension::SignatureAlgorithms]
519
522
  def do_select_signature_algorithms(signature_algorithms, crt)
520
- spki = OpenSSL::Netscape::SPKI.new
521
- spki.public_key = crt.public_key
522
- pka = OpenSSL::ASN1.decode(spki.to_der)
523
- .value.first.value.first.value.first.value.first.value
523
+ pka = OpenSSL::ASN1.decode(crt.public_key.to_der)
524
+ .value.first.value.first.value
524
525
  signature_algorithms.select do |sa|
525
526
  case sa
526
527
  when SignatureScheme::ECDSA_SECP256R1_SHA256,
@@ -45,7 +45,7 @@ module TTTLS13
45
45
  # @return [String]
46
46
  def encrypt(content, type)
47
47
  cipher = reset_cipher
48
- plaintext = content + type + "\x00" * @length_of_padding
48
+ plaintext = content + type + @length_of_padding.zeros
49
49
  cipher.auth_data = additional_data(plaintext.length)
50
50
  encrypted_data = cipher.update(plaintext) + cipher.final
51
51
  @sequence_number.succ
@@ -10,7 +10,7 @@ module TTTLS13
10
10
  class ConfigError < Error; end
11
11
 
12
12
  # Raised on received Error Alerts message or invalid message.
13
- # https://tools.ietf.org/html/rfc8446#section-6.2
13
+ # https://datatracker.ietf.org/doc/html/rfc8446#section-6.2
14
14
  # Terminated the connection, so you *cannot* recover from this exception.
15
15
  class ErrorAlerts < Error
16
16
  # @return [TTTLS13::Message::Alert]
@@ -0,0 +1,91 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ # NOTE: Hpke module is the adapter for ech_config using hpke-rb.
6
+ module Hpke
7
+ module KemId
8
+ # https://www.iana.org/assignments/hpke/hpke.xhtml#hpke-kem-ids
9
+ P_256_SHA256 = 0x0010
10
+ P_384_SHA384 = 0x0011
11
+ P_521_SHA512 = 0x0012
12
+ X25519_SHA256 = 0x0020
13
+ X448_SHA512 = 0x0021
14
+ end
15
+
16
+ def self.kem_id2dhkem(kem_id)
17
+ case kem_id
18
+ when KemId::P_256_SHA256
19
+ %i[p_256 sha256]
20
+ when KemId::P_384_SHA384
21
+ %i[p_384 sha384]
22
+ when KemId::P_521_SHA512
23
+ %i[p_521 sha512]
24
+ when KemId::X25519_SHA256
25
+ %i[x25519 sha256]
26
+ when KemId::X448_SHA512
27
+ %i[x448 sha512]
28
+ end
29
+ end
30
+
31
+ def self.kem_curve_name2dhkem(kem_curve_name)
32
+ case kem_curve_name
33
+ when :p_256
34
+ HPKE::DHKEM::EC::P_256
35
+ when :p_384
36
+ HPKE::DHKEM::EC::P_384
37
+ when :p_521
38
+ HPKE::DHKEM::EC::P_521
39
+ when :x25519
40
+ HPKE::DHKEM::X25519
41
+ when :x448
42
+ HPKE::DHKEM::X448
43
+ end
44
+ end
45
+
46
+ module KdfId
47
+ # https://www.iana.org/assignments/hpke/hpke.xhtml#hpke-kdf-ids
48
+ HKDF_SHA256 = 0x0001
49
+ HKDF_SHA384 = 0x0002
50
+ HKDF_SHA512 = 0x0003
51
+ end
52
+
53
+ def self.kdf_id2kdf_hash(kdf_id)
54
+ case kdf_id
55
+ when KdfId::HKDF_SHA256
56
+ :sha256
57
+ when KdfId::HKDF_SHA384
58
+ :sha384
59
+ when KdfId::HKDF_SHA512
60
+ :sha512
61
+ end
62
+ end
63
+
64
+ module AeadId
65
+ # https://www.iana.org/assignments/hpke/hpke.xhtml#hpke-aead-ids
66
+ AES_128_GCM = 0x0001
67
+ AES_256_GCM = 0x0002
68
+ CHACHA20_POLY1305 = 0x0003
69
+ end
70
+
71
+ def self.aead_id2overhead_len(aead_id)
72
+ case aead_id
73
+ when AeadId::AES_128_GCM, AeadId::CHACHA20_POLY1305
74
+ 16
75
+ when AeadId::AES_256_GCM
76
+ 32
77
+ end
78
+ end
79
+
80
+ def self.aead_id2aead_cipher(aead_id)
81
+ case aead_id
82
+ when AeadId::AES_128_GCM
83
+ :aes_128_gcm
84
+ when AeadId::AES_256_GCM
85
+ :aes_256_gcm
86
+ when AeadId::CHACHA20_POLY1305
87
+ :chacha20_poly1305
88
+ end
89
+ end
90
+ end
91
+ end
@@ -14,14 +14,14 @@ module TTTLS13
14
14
  @hash_len = CipherSuite.hash_len(cipher_suite)
15
15
  @key_len = CipherSuite.key_len(cipher_suite)
16
16
  @iv_len = CipherSuite.iv_len(cipher_suite)
17
- @psk = psk || "\x00" * @hash_len
17
+ @psk = psk || @hash_len.zeros
18
18
  @shared_secret = shared_secret
19
19
  @transcript = transcript
20
20
  end
21
21
 
22
22
  # @return [String]
23
23
  def early_salt
24
- "\x00" * @hash_len
24
+ @hash_len.zeros
25
25
  end
26
26
 
27
27
  # @return [String]
@@ -61,8 +61,15 @@ module TTTLS13
61
61
  self.class.hkdf_expand_label(secret, 'iv', '', @iv_len, @digest)
62
62
  end
63
63
 
64
+ # @deprecated Please use `early_exporter_secret` instead
65
+ #
64
66
  # @return [String]
65
67
  def early_exporter_master_secret
68
+ early_exporter_secret
69
+ end
70
+
71
+ # @return [String]
72
+ def early_exporter_secret
66
73
  hash = OpenSSL::Digest.digest(@digest, '')
67
74
  derive_secret(early_secret, 'e exp master', hash)
68
75
  end
@@ -126,22 +133,36 @@ module TTTLS13
126
133
  self.class.hkdf_expand_label(secret, 'iv', '', @iv_len, @digest)
127
134
  end
128
135
 
136
+ # @deprecated Please use `main_salt` instead
137
+ #
129
138
  # @return [String]
130
139
  def master_salt
140
+ main_salt
141
+ end
142
+
143
+ # @return [String]
144
+ def main_salt
131
145
  hash = OpenSSL::Digest.digest(@digest, '')
132
146
  derive_secret(handshake_secret, 'derived', hash)
133
147
  end
134
148
 
149
+ # @deprecated Please use `main_secret` instead
150
+ #
135
151
  # @return [String]
136
152
  def master_secret
137
- ikm = "\x00" * @hash_len
138
- hkdf_extract(ikm, master_salt)
153
+ main_secret
154
+ end
155
+
156
+ # @return [String]
157
+ def main_secret
158
+ ikm = @hash_len.zeros
159
+ hkdf_extract(ikm, main_salt)
139
160
  end
140
161
 
141
162
  # @return [String]
142
163
  def client_application_traffic_secret
143
164
  hash = @transcript.hash(@digest, SF)
144
- derive_secret(master_secret, 'c ap traffic', hash)
165
+ derive_secret(main_secret, 'c ap traffic', hash)
145
166
  end
146
167
 
147
168
  # @return [String]
@@ -159,7 +180,7 @@ module TTTLS13
159
180
  # @return [String]
160
181
  def server_application_traffic_secret
161
182
  hash = @transcript.hash(@digest, SF)
162
- derive_secret(master_secret, 's ap traffic', hash)
183
+ derive_secret(main_secret, 's ap traffic', hash)
163
184
  end
164
185
 
165
186
  # @return [String]
@@ -174,16 +195,30 @@ module TTTLS13
174
195
  self.class.hkdf_expand_label(secret, 'iv', '', @iv_len, @digest)
175
196
  end
176
197
 
198
+ # @deprecated Please use `exporter_secret` instead
199
+ #
177
200
  # @return [String]
178
201
  def exporter_master_secret
202
+ exporter_secret
203
+ end
204
+
205
+ # @return [String]
206
+ def exporter_secret
179
207
  hash = @transcript.hash(@digest, SF)
180
- derive_secret(master_secret, 'exp master', hash)
208
+ derive_secret(main_secret, 'exp master', hash)
181
209
  end
182
210
 
211
+ # @deprecated Please use `resumption_secret` instead
212
+ #
183
213
  # @return [String]
184
214
  def resumption_master_secret
215
+ resumption_secret
216
+ end
217
+
218
+ # @return [String]
219
+ def resumption_secret
185
220
  hash = @transcript.hash(@digest, CF)
186
- derive_secret(master_secret, 'res master', hash)
221
+ derive_secret(main_secret, 'res master', hash)
187
222
  end
188
223
 
189
224
  # @param ikm [String]
@@ -238,6 +273,74 @@ module TTTLS13
238
273
  def derive_secret(secret, label, context)
239
274
  self.class.hkdf_expand_label(secret, label, context, @hash_len, @digest)
240
275
  end
276
+
277
+ # @return [String]
278
+ def accept_confirmation
279
+ ch_inner_random = @transcript[CH].first.random
280
+ sh = @transcript[SH].first
281
+ sh = Message::ServerHello.new(
282
+ random: sh.random[...-8] + 8.zeros,
283
+ legacy_session_id_echo: sh.legacy_session_id_echo,
284
+ cipher_suite: sh.cipher_suite,
285
+ extensions: sh.extensions
286
+ )
287
+ transcript = @transcript.clone
288
+ transcript[SH] = [sh, sh.serialize]
289
+ transcript_ech_conf = transcript.hash(@digest, SH)
290
+
291
+ self.class.hkdf_expand_label(
292
+ hkdf_extract(ch_inner_random, ''),
293
+ 'ech accept confirmation',
294
+ transcript_ech_conf,
295
+ 8,
296
+ @digest
297
+ )
298
+ end
299
+
300
+ # @return [Boolean]
301
+ def accept_ech?
302
+ accept_confirmation == @transcript[SH].first.random[-8..]
303
+ end
304
+
305
+ # @return [String]
306
+ def hrr_accept_confirmation
307
+ ch1_inner_random = @transcript[CH1].first.random
308
+ hrr = @transcript[HRR].first
309
+ ech = Message::Extension::ECHHelloRetryRequest.new("\x00" * 8)
310
+ hrr = Message::ServerHello.new(
311
+ random: hrr.random,
312
+ legacy_session_id_echo: hrr.legacy_session_id_echo,
313
+ cipher_suite: hrr.cipher_suite,
314
+ extensions: hrr.extensions.merge(
315
+ Message::ExtensionType::ENCRYPTED_CLIENT_HELLO => ech
316
+ )
317
+ )
318
+
319
+ # It then computes the transcript hash for the first ClientHelloInner,
320
+ # denoted ClientHelloInner1, up to and including the modified
321
+ # HelloRetryRequest.
322
+ #
323
+ # https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#section-7.2.1-2
324
+ transcript = @transcript.clone
325
+ transcript[HRR] = [hrr, hrr.serialize]
326
+ transcript_hrr_ech_conf = transcript.hash(@digest, HRR)
327
+
328
+ self.class.hkdf_expand_label(
329
+ hkdf_extract(ch1_inner_random, ''),
330
+ 'hrr ech accept confirmation',
331
+ transcript_hrr_ech_conf,
332
+ 8,
333
+ @digest
334
+ )
335
+ end
336
+
337
+ # @return [Boolean]
338
+ def hrr_accept_ech?
339
+ hrr_ech = @transcript[HRR]
340
+ .first
341
+ .extensions[Message::ExtensionType::ENCRYPTED_CLIENT_HELLO]
342
+ hrr_accept_confirmation == hrr_ech.confirmation
343
+ end
241
344
  end
242
345
  # rubocop: enable Metrics/ClassLength
243
346
  end
@@ -36,7 +36,8 @@ module TTTLS13
36
36
  bad_certificate_status_response: "\x71",
37
37
  unknown_psk_identity: "\x73",
38
38
  certificate_required: "\x74",
39
- no_application_protocol: "\x78"
39
+ no_application_protocol: "\x78",
40
+ ech_required: "\x79"
40
41
  }.freeze
41
42
  # rubocop: enable Layout/HashAlignment
42
43
 
@@ -31,7 +31,8 @@ module TTTLS13
31
31
  ExtensionType::CERTIFICATE_AUTHORITIES,
32
32
  ExtensionType::POST_HANDSHAKE_AUTH,
33
33
  ExtensionType::SIGNATURE_ALGORITHMS_CERT,
34
- ExtensionType::KEY_SHARE
34
+ ExtensionType::KEY_SHARE,
35
+ ExtensionType::ENCRYPTED_CLIENT_HELLO
35
36
  ].freeze
36
37
  private_constant :APPEARABLE_CH_EXTENSIONS
37
38
 
@@ -15,7 +15,8 @@ module TTTLS13
15
15
  ExtensionType::CLIENT_CERTIFICATE_TYPE,
16
16
  ExtensionType::SERVER_CERTIFICATE_TYPE,
17
17
  ExtensionType::RECORD_SIZE_LIMIT,
18
- ExtensionType::EARLY_DATA
18
+ ExtensionType::EARLY_DATA,
19
+ ExtensionType::ENCRYPTED_CLIENT_HELLO
19
20
  ].freeze
20
21
  private_constant :APPEARABLE_EE_EXTENSIONS
21
22
 
@@ -19,7 +19,7 @@ module TTTLS13
19
19
  # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
20
20
  def initialize(protocol_name_list)
21
21
  @extension_type \
22
- = ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
22
+ = ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
23
23
  @protocol_name_list = protocol_name_list || []
24
24
  raise Error::ErrorAlerts, :internal_error \
25
25
  if @protocol_name_list.empty?
@@ -27,10 +27,9 @@ module TTTLS13
27
27
 
28
28
  # @return [String]
29
29
  def serialize
30
- binary = @protocol_name_list
31
- .map(&:prefix_uint8_length)
32
- .join
33
- .prefix_uint16_length
30
+ binary = @protocol_name_list.map(&:prefix_uint8_length)
31
+ .join
32
+ .prefix_uint16_length
34
33
 
35
34
  @extension_type + binary.prefix_uint16_length
36
35
  end
@@ -11,7 +11,7 @@ module TTTLS13
11
11
  # ZSTD = "\x00\x03" # UNSUPPORTED
12
12
  end
13
13
 
14
- # https://tools.ietf.org/html/rfc8879
14
+ # https://datatracker.ietf.org/doc/html/rfc8879
15
15
  class CompressCertificate
16
16
  attr_reader :extension_type
17
17
  attr_reader :algorithms
@@ -0,0 +1,241 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ HpkeSymmetricCipherSuite = \
7
+ ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite
8
+ module Message
9
+ module Extension
10
+ module ECHClientHelloType
11
+ OUTER = "\x00"
12
+ INNER = "\x01"
13
+ end
14
+
15
+ # NOTE:
16
+ # struct {
17
+ # ECHClientHelloType type;
18
+ # select (ECHClientHello.type) {
19
+ # case outer:
20
+ # HpkeSymmetricCipherSuite cipher_suite;
21
+ # uint8 config_id;
22
+ # opaque enc<0..2^16-1>;
23
+ # opaque payload<1..2^16-1>;
24
+ # case inner:
25
+ # Empty;
26
+ # };
27
+ # } ECHClientHello;
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
35
+
36
+ # @param type [TTTLS13::Message::Extension::ECHClientHelloType]
37
+ # @param cipher_suite [HpkeSymmetricCipherSuite]
38
+ # @param config_id [Integer]
39
+ # @param enc [String]
40
+ # @param payload [String]
41
+ def initialize(type:,
42
+ cipher_suite: nil,
43
+ config_id: nil,
44
+ enc: nil,
45
+ payload: nil)
46
+ @extension_type = ExtensionType::ENCRYPTED_CLIENT_HELLO
47
+ @type = type
48
+ @cipher_suite = cipher_suite
49
+ raise Error::ErrorAlerts, :internal_error \
50
+ if @type == ECHClientHelloType::OUTER && \
51
+ !@cipher_suite.is_a?(HpkeSymmetricCipherSuite)
52
+
53
+ @config_id = config_id
54
+ @enc = enc
55
+ @payload = payload
56
+ end
57
+
58
+ # @raise [TTTLS13::Error::ErrorAlerts]
59
+ #
60
+ # @return [String]
61
+ def serialize
62
+ case @type
63
+ when ECHClientHelloType::OUTER
64
+ binary = @type + @cipher_suite.encode + @config_id.to_uint8 \
65
+ + @enc.prefix_uint16_length + @payload.prefix_uint16_length
66
+ when ECHClientHelloType::INNER
67
+ binary = @type
68
+ else
69
+ raise Error::ErrorAlerts, :internal_error
70
+ end
71
+
72
+ @extension_type + binary.prefix_uint16_length
73
+ end
74
+
75
+ # @param binary [String]
76
+ #
77
+ # @raise [TTTLS13::Error::ErrorAlerts]
78
+ #
79
+ # @return [TTTLS13::Message::Extensions::ECHClientHello]
80
+ def self.deserialize(binary)
81
+ raise Error::ErrorAlerts, :internal_error \
82
+ if binary.nil? || binary.empty?
83
+
84
+ case binary[0]
85
+ when ECHClientHelloType::OUTER
86
+ return deserialize_outer_ech(binary[1..])
87
+ when ECHClientHelloType::INNER
88
+ return deserialize_inner_ech(binary[1..])
89
+ end
90
+
91
+ raise Error::ErrorAlerts, :internal_error
92
+ end
93
+
94
+ class << self
95
+ private
96
+
97
+ # @param binary [String]
98
+ #
99
+ # @raise [TTTLS13::Error::ErrorAlerts]
100
+ #
101
+ # @return [TTTLS13::Message::Extensions::ECHClientHello]
102
+ def deserialize_outer_ech(binary)
103
+ raise Error::ErrorAlerts, :internal_error \
104
+ if binary.nil? || binary.length < 5
105
+
106
+ kdf_id = \
107
+ HpkeSymmetricCipherSuite::HpkeKdfId.decode(binary.slice(0, 2))
108
+ aead_id = \
109
+ HpkeSymmetricCipherSuite::HpkeAeadId.decode(binary.slice(2, 2))
110
+ cs = HpkeSymmetricCipherSuite.new(kdf_id, aead_id)
111
+ cid = Convert.bin2i(binary.slice(4, 1))
112
+ enc_len = Convert.bin2i(binary.slice(5, 2))
113
+ i = 7
114
+ raise Error::ErrorAlerts, :internal_error \
115
+ if i + enc_len > binary.length
116
+
117
+ enc = binary.slice(i, enc_len)
118
+ i += enc_len
119
+ raise Error::ErrorAlerts, :internal_error \
120
+ if i + 2 > binary.length
121
+
122
+ payload_len = Convert.bin2i(binary.slice(i, 2))
123
+ raise Error::ErrorAlerts, :internal_error \
124
+ if i + payload_len > binary.length
125
+
126
+ payload = binary.slice(i, payload_len)
127
+ ECHClientHello.new(
128
+ type: ECHClientHelloType::OUTER,
129
+ cipher_suite: cs,
130
+ config_id: cid,
131
+ enc: enc,
132
+ payload: payload
133
+ )
134
+ end
135
+
136
+ # @param binary [String]
137
+ #
138
+ # @raise [TTTLS13::Error::ErrorAlerts]
139
+ #
140
+ # @return [TTTLS13::Message::Extensions::ECHClientHello]
141
+ def deserialize_inner_ech(binary)
142
+ raise Error::ErrorAlerts, :internal_error unless binary.empty?
143
+
144
+ ECHClientHello.new(type: ECHClientHelloType::INNER)
145
+ end
146
+ end
147
+
148
+ # @return [TTTLS13::Message::Extensions::ECHClientHello]
149
+ def self.new_inner
150
+ ECHClientHello.new(type: ECHClientHelloType::INNER)
151
+ end
152
+
153
+ # @param cipher_suite [HpkeSymmetricCipherSuite]
154
+ # @param config_id [Integer]
155
+ # @param enc [String]
156
+ # @param payload [String]
157
+ #
158
+ # @return [TTTLS13::Message::Extensions::ECHClientHello]
159
+ def self.new_outer(cipher_suite:, config_id:, enc:, payload:)
160
+ ECHClientHello.new(
161
+ type: ECHClientHelloType::OUTER,
162
+ cipher_suite: cipher_suite,
163
+ config_id: config_id,
164
+ enc: enc,
165
+ payload: payload
166
+ )
167
+ end
168
+ end
169
+
170
+ # NOTE:
171
+ # struct {
172
+ # ECHConfigList retry_configs;
173
+ # } ECHEncryptedExtensions;
174
+ class ECHEncryptedExtensions
175
+ attr_accessor :extension_type
176
+ attr_accessor :retry_configs
177
+
178
+ # @param retry_configs [Array of ECHConfig]
179
+ def initialize(retry_configs)
180
+ @extension_type = ExtensionType::ENCRYPTED_CLIENT_HELLO
181
+ @retry_configs = retry_configs
182
+ end
183
+
184
+ # @return [String]
185
+ def serialize
186
+ @extension_type + @retry_configs.map(&:encode)
187
+ .join
188
+ .prefix_uint16_length
189
+ .prefix_uint16_length
190
+ end
191
+
192
+ # @param binary [String]
193
+ #
194
+ # @raise [TTTLS13::Error::ErrorAlerts]
195
+ #
196
+ # @return [TTTLS13::Message::Extensions::ECHEncryptedExtensions]
197
+ def self.deserialize(binary)
198
+ raise Error::ErrorAlerts, :decode_error \
199
+ if binary.nil? ||
200
+ binary.length != binary.slice(0, 2).unpack1('n') + 2
201
+
202
+ ECHEncryptedExtensions.new(
203
+ ECHConfig.decode_vectors(binary.slice(2..))
204
+ )
205
+ end
206
+ end
207
+
208
+ # NOTE:
209
+ # struct {
210
+ # opaque confirmation[8];
211
+ # } ECHHelloRetryRequest;
212
+ class ECHHelloRetryRequest
213
+ attr_accessor :extension_type
214
+ attr_accessor :confirmation
215
+
216
+ # @param confirmation [String]
217
+ def initialize(confirmation)
218
+ @extension_type = ExtensionType::ENCRYPTED_CLIENT_HELLO
219
+ @confirmation = confirmation
220
+ end
221
+
222
+ # @return [String]
223
+ def serialize
224
+ @extension_type + @confirmation.prefix_uint16_length
225
+ end
226
+
227
+ # @param binary [String]
228
+ #
229
+ # @raise [TTTLS13::Error::ErrorAlerts]
230
+ #
231
+ # @return [TTTLS13::Message::Extensions::ECHHelloRetryRequest]
232
+ def self.deserialize(binary)
233
+ raise Error::ErrorAlerts, :decode_error \
234
+ if binary.nil? || binary.length != 8
235
+
236
+ ECHHelloRetryRequest.new(binary)
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
@@ -91,8 +91,7 @@ module TTTLS13
91
91
  priv_keys = {}
92
92
  kse = groups.map do |group|
93
93
  curve = NamedGroup.curve_name(group)
94
- ec = OpenSSL::PKey::EC.new(curve)
95
- ec.generate_key!
94
+ ec = OpenSSL::PKey::EC.generate(curve)
96
95
  # store private key to do the key-exchange
97
96
  priv_keys.store(group, ec)
98
97
  KeyShareEntry.new(
@@ -115,8 +114,7 @@ module TTTLS13
115
114
  # @return [OpenSSL::PKey::EC.$Object]
116
115
  def self.gen_sh_key_share(group)
117
116
  curve = NamedGroup.curve_name(group)
118
- ec = OpenSSL::PKey::EC.new(curve)
119
- ec.generate_key!
117
+ ec = OpenSSL::PKey::EC.generate(curve)
120
118
 
121
119
  key_share = KeyShare.new(
122
120
  msg_type: HandshakeType::SERVER_HELLO,
@@ -15,7 +15,7 @@ module TTTLS13
15
15
  #
16
16
  # 00 00 00 00
17
17
  #
18
- # https://tools.ietf.org/html/rfc6066#section-3
18
+ # https://datatracker.ietf.org/doc/html/rfc6066#section-3
19
19
  class ServerName
20
20
  attr_reader :extension_type
21
21
  attr_reader :server_name