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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +8 -5
- data/Gemfile +2 -0
- data/README.md +6 -3
- data/example/helper.rb +5 -2
- data/example/https_client_using_0rtt.rb +1 -1
- data/example/https_client_using_ech.rb +32 -0
- data/example/https_client_using_grease_ech.rb +26 -0
- data/example/https_client_using_grease_psk.rb +66 -0
- data/example/https_client_using_hrr_and_ech.rb +32 -0
- data/example/https_client_using_hrr_and_ticket.rb +1 -1
- data/example/https_client_using_ticket.rb +1 -1
- data/interop/client_spec.rb +3 -2
- data/interop/server_spec.rb +1 -3
- data/interop/{helper.rb → spec_helper.rb} +12 -5
- data/lib/tttls1.3/client.rb +553 -32
- data/lib/tttls1.3/connection.rb +9 -8
- data/lib/tttls1.3/cryptograph/aead.rb +1 -1
- data/lib/tttls1.3/error.rb +1 -1
- data/lib/tttls1.3/hpke.rb +91 -0
- data/lib/tttls1.3/key_schedule.rb +111 -8
- data/lib/tttls1.3/message/alert.rb +2 -1
- data/lib/tttls1.3/message/client_hello.rb +2 -1
- data/lib/tttls1.3/message/encrypted_extensions.rb +2 -1
- data/lib/tttls1.3/message/extension/alpn.rb +4 -5
- data/lib/tttls1.3/message/extension/compress_certificate.rb +1 -1
- data/lib/tttls1.3/message/extension/ech.rb +241 -0
- data/lib/tttls1.3/message/extension/key_share.rb +2 -4
- data/lib/tttls1.3/message/extension/server_name.rb +1 -1
- data/lib/tttls1.3/message/extensions.rb +20 -7
- data/lib/tttls1.3/message/record.rb +1 -1
- data/lib/tttls1.3/message/server_hello.rb +3 -5
- data/lib/tttls1.3/message.rb +3 -1
- data/lib/tttls1.3/named_group.rb +1 -1
- data/lib/tttls1.3/server.rb +2 -2
- data/lib/tttls1.3/utils.rb +8 -0
- data/lib/tttls1.3/version.rb +1 -1
- data/lib/tttls1.3.rb +4 -0
- data/spec/client_spec.rb +40 -0
- data/spec/connection_spec.rb +22 -7
- data/spec/ech_spec.rb +81 -0
- data/spec/extensions_spec.rb +1 -2
- data/spec/key_schedule_spec.rb +2 -2
- data/spec/server_spec.rb +22 -7
- data/spec/spec_helper.rb +41 -5
- data/tttls1.3.gemspec +2 -0
- metadata +39 -3
data/lib/tttls1.3/connection.rb
CHANGED
@@ -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
|
-
@
|
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 @
|
115
|
+
return nil if @exporter_secret.nil? || @cipher_suite.nil?
|
113
116
|
|
114
117
|
digest = CipherSuite.digest(@cipher_suite)
|
115
|
-
do_exporter(@
|
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_)
|
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
|
-
|
521
|
-
|
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 +
|
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
|
data/lib/tttls1.3/error.rb
CHANGED
@@ -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://
|
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 ||
|
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
|
-
|
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
|
-
|
138
|
-
|
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(
|
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(
|
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(
|
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(
|
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
|
-
|
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
|
-
|
32
|
-
|
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
|
@@ -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.
|
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.
|
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,
|