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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile +1 -0
- data/example/helper.rb +43 -0
- data/example/https_client_using_0rtt.rb +5 -3
- data/example/https_client_using_ech.rb +6 -7
- data/example/https_client_using_grease_ech.rb +0 -2
- data/example/https_client_using_hrr.rb +2 -1
- data/example/https_client_using_hrr_and_ech.rb +6 -7
- data/example/https_client_using_hrr_and_ticket.rb +4 -2
- data/example/https_client_using_status_request.rb +2 -1
- data/example/https_client_using_ticket.rb +4 -2
- data/example/https_client_using_ticket_and_ech.rb +57 -0
- data/example/https_server.rb +14 -1
- data/lib/tttls1.3/client.rb +205 -418
- data/lib/tttls1.3/connection.rb +21 -362
- data/lib/tttls1.3/ech.rb +426 -0
- data/lib/tttls1.3/endpoint.rb +276 -0
- data/lib/tttls1.3/message/certificate_verify.rb +1 -1
- data/lib/tttls1.3/message/extension/ech.rb +21 -24
- data/lib/tttls1.3/message/extension/ech_outer_extensions.rb +52 -0
- data/lib/tttls1.3/message/extension/signature_algorithms.rb +2 -2
- data/lib/tttls1.3/message/extension/supported_versions.rb +3 -3
- data/lib/tttls1.3/message/extension/unknown_extension.rb +2 -2
- data/lib/tttls1.3/message/extensions.rb +30 -0
- data/lib/tttls1.3/message.rb +1 -0
- data/lib/tttls1.3/server.rb +125 -63
- data/lib/tttls1.3/utils.rb +37 -0
- data/lib/tttls1.3/version.rb +1 -1
- data/lib/tttls1.3.rb +2 -1
- data/spec/client_spec.rb +21 -60
- data/spec/ech_outer_extensions_spec.rb +42 -0
- data/spec/ech_spec.rb +41 -0
- data/spec/{connection_spec.rb → endpoint_spec.rb} +41 -49
- data/spec/extensions_spec.rb +65 -0
- data/spec/server_spec.rb +12 -12
- data/spec/spec_helper.rb +4 -0
- metadata +11 -6
- data/lib/tttls1.3/hpke.rb +0 -91
@@ -26,12 +26,12 @@ module TTTLS13
|
|
26
26
|
# };
|
27
27
|
# } ECHClientHello;
|
28
28
|
class ECHClientHello
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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, :
|
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
|
-
|
176
|
-
|
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, :
|
199
|
-
|
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
|
-
|
214
|
-
|
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, :
|
234
|
-
|
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
|
-
|
25
|
-
attr_reader
|
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
|
10
|
-
|
11
|
-
|
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
|
-
|
13
|
-
|
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
|
data/lib/tttls1.3/message.rb
CHANGED
@@ -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|
|
data/lib/tttls1.3/server.rb
CHANGED
@@ -66,13 +66,15 @@ module TTTLS13
|
|
66
66
|
private_constant :DEFAULT_SERVER_SETTINGS
|
67
67
|
|
68
68
|
# rubocop: disable Metrics/ClassLength
|
69
|
-
class Server
|
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
|
-
|
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)
|
176
|
-
|
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)
|
179
|
-
|
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
|
-
@
|
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)
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
509
|
-
|
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
|
-
|
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
|
-
|
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]
|
data/lib/tttls1.3/utils.rb
CHANGED
@@ -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
|