tttls1.3 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/Rakefile +3 -3
- data/example/https_client_using_hrr_and_ticket.rb +40 -0
- data/interop/client_spec.rb +15 -0
- data/interop/server_spec.rb +8 -0
- data/lib/tttls1.3/client.rb +265 -272
- data/lib/tttls1.3/connection.rb +85 -62
- data/lib/tttls1.3/message/certificate.rb +1 -1
- data/lib/tttls1.3/message/client_hello.rb +26 -1
- data/lib/tttls1.3/message/encrypted_extensions.rb +1 -1
- data/lib/tttls1.3/message/new_session_ticket.rb +1 -1
- data/lib/tttls1.3/message/server_hello.rb +21 -1
- data/lib/tttls1.3/server.rb +179 -157
- data/lib/tttls1.3/version.rb +1 -1
- data/spec/certificate_spec.rb +4 -4
- data/spec/client_hello_spec.rb +3 -0
- data/spec/client_spec.rb +96 -157
- data/spec/connection_spec.rb +32 -23
- data/spec/encrypted_extensions_spec.rb +4 -4
- data/spec/fixtures/rsa_ca.crt +16 -27
- data/spec/fixtures/rsa_ca.key +25 -49
- data/spec/fixtures/rsa_rsa.crt +16 -21
- data/spec/fixtures/rsa_rsa.key +25 -25
- data/spec/fixtures/rsa_rsassaPss.crt +20 -0
- data/spec/fixtures/rsa_rsassaPss.key +27 -0
- data/spec/fixtures/rsa_secp256r1.crt +12 -17
- data/spec/fixtures/rsa_secp256r1.key +3 -3
- data/spec/fixtures/rsa_secp384r1.crt +12 -17
- data/spec/fixtures/rsa_secp384r1.key +4 -4
- data/spec/fixtures/rsa_secp521r1.crt +13 -18
- data/spec/fixtures/rsa_secp521r1.key +5 -5
- data/spec/server_hello_spec.rb +60 -0
- data/spec/server_spec.rb +79 -60
- metadata +7 -2
data/lib/tttls1.3/client.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
|
4
4
|
module TTTLS13
|
5
5
|
using Refinements
|
6
|
+
|
6
7
|
module ClientState
|
7
8
|
# initial value is 0, eof value is -1
|
8
9
|
START = 1
|
@@ -62,11 +63,6 @@ module TTTLS13
|
|
62
63
|
|
63
64
|
# rubocop: disable Metrics/ClassLength
|
64
65
|
class Client < Connection
|
65
|
-
DOWNGRADE_PROTECTION_TLS_1_2 = "\x44\x4F\x57\x4E\x47\x52\x44\x01"
|
66
|
-
private_constant :DOWNGRADE_PROTECTION_TLS_1_2
|
67
|
-
DOWNGRADE_PROTECTION_TLS_1_1 = "\x44\x4F\x57\x4E\x47\x52\x44\x00"
|
68
|
-
private_constant :DOWNGRADE_PROTECTION_TLS_1_1
|
69
|
-
|
70
66
|
# @param socket [Socket]
|
71
67
|
# @param hostname [String]
|
72
68
|
# @param settings [Hash]
|
@@ -79,20 +75,8 @@ module TTTLS13
|
|
79
75
|
logger.level = @settings[:loglevel]
|
80
76
|
|
81
77
|
@early_data = ''
|
82
|
-
@early_data_write_cipher = nil # Cryptograph::$Object
|
83
78
|
@succeed_early_data = false
|
84
79
|
raise Error::ConfigError unless valid_settings?
|
85
|
-
return unless use_psk?
|
86
|
-
|
87
|
-
digest = CipherSuite.digest(@settings[:psk_cipher_suite])
|
88
|
-
@psk = gen_psk_from_nst(@settings[:resumption_master_secret],
|
89
|
-
@settings[:ticket_nonce], digest)
|
90
|
-
@key_schedule = KeySchedule.new(
|
91
|
-
psk: @psk,
|
92
|
-
shared_secret: nil,
|
93
|
-
cipher_suite: @settings[:psk_cipher_suite],
|
94
|
-
transcript: @transcript
|
95
|
-
)
|
96
80
|
end
|
97
81
|
|
98
82
|
# NOTE:
|
@@ -135,98 +119,160 @@ module TTTLS13
|
|
135
119
|
# rubocop: disable Metrics/MethodLength
|
136
120
|
# rubocop: disable Metrics/PerceivedComplexity
|
137
121
|
def connect
|
122
|
+
transcript = Transcript.new
|
123
|
+
key_schedule = nil # TTTLS13::KeySchedule
|
124
|
+
psk = nil
|
125
|
+
priv_keys = {} # Hash of NamedGroup => OpenSSL::PKey::$Object
|
126
|
+
if use_psk?
|
127
|
+
psk = gen_psk_from_nst(
|
128
|
+
@settings[:resumption_master_secret],
|
129
|
+
@settings[:ticket_nonce],
|
130
|
+
CipherSuite.digest(@settings[:psk_cipher_suite])
|
131
|
+
)
|
132
|
+
key_schedule = KeySchedule.new(
|
133
|
+
psk: psk,
|
134
|
+
shared_secret: nil,
|
135
|
+
cipher_suite: @settings[:psk_cipher_suite],
|
136
|
+
transcript: transcript
|
137
|
+
)
|
138
|
+
end
|
139
|
+
|
140
|
+
hs_wcipher = nil # TTTLS13::Cryptograph::$Object
|
141
|
+
hs_rcipher = nil # TTTLS13::Cryptograph::$Object
|
142
|
+
e_wcipher = nil # TTTLS13::Cryptograph::$Object
|
143
|
+
|
138
144
|
@state = ClientState::START
|
139
145
|
loop do
|
140
146
|
case @state
|
141
147
|
when ClientState::START
|
142
148
|
logger.debug('ClientState::START')
|
143
149
|
|
144
|
-
|
145
|
-
|
150
|
+
extensions, priv_keys = gen_ch_extensions
|
151
|
+
binder_key = (use_psk? ? key_schedule.binder_key_res : nil)
|
152
|
+
transcript[CH] = send_client_hello(extensions, binder_key)
|
153
|
+
|
146
154
|
send_ccs # compatibility mode
|
147
155
|
if use_early_data?
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
156
|
+
e_wcipher = gen_cipher(
|
157
|
+
@settings[:psk_cipher_suite],
|
158
|
+
key_schedule.early_data_write_key,
|
159
|
+
key_schedule.early_data_write_iv
|
160
|
+
)
|
161
|
+
send_early_data(e_wcipher)
|
153
162
|
end
|
154
163
|
|
155
164
|
@state = ClientState::WAIT_SH
|
156
165
|
when ClientState::WAIT_SH
|
157
166
|
logger.debug('ClientState::WAIT_SH')
|
158
167
|
|
159
|
-
sh =
|
160
|
-
|
168
|
+
sh = transcript[SH] = recv_server_hello
|
169
|
+
|
161
170
|
# support only TLS 1.3
|
162
|
-
terminate(:protocol_version) unless negotiated_tls_1_3?
|
171
|
+
terminate(:protocol_version) unless sh.negotiated_tls_1_3?
|
163
172
|
|
164
173
|
# validate parameters
|
165
|
-
terminate(:illegal_parameter) unless
|
166
|
-
terminate(:illegal_parameter)
|
167
|
-
terminate(:illegal_parameter) unless valid_sh_legacy_session_id_echo?
|
168
|
-
terminate(:illegal_parameter) unless valid_sh_cipher_suite?
|
174
|
+
terminate(:illegal_parameter) unless sh.appearable_extensions?
|
175
|
+
terminate(:illegal_parameter) if sh.downgraded?
|
169
176
|
terminate(:illegal_parameter) \
|
170
|
-
|
171
|
-
|
172
|
-
|
177
|
+
unless sh.legacy_compression_method == "\x00"
|
178
|
+
|
179
|
+
# validate sh using ch
|
180
|
+
ch = transcript[CH]
|
181
|
+
terminate(:illegal_parameter) \
|
182
|
+
unless sh.legacy_version == ch.legacy_version
|
183
|
+
terminate(:illegal_parameter) \
|
184
|
+
unless sh.legacy_session_id_echo == ch.legacy_session_id
|
185
|
+
terminate(:illegal_parameter) \
|
186
|
+
unless ch.cipher_suites.include?(sh.cipher_suite)
|
187
|
+
terminate(:unsupported_extension) \
|
188
|
+
unless (sh.extensions.keys - ch.extensions.keys).empty?
|
189
|
+
|
190
|
+
# validate sh using hrr
|
191
|
+
if transcript.include?(HRR)
|
192
|
+
hrr = transcript[HRR]
|
193
|
+
terminate(:illegal_parameter) \
|
194
|
+
unless sh.cipher_suite == hrr.cipher_suite
|
195
|
+
|
196
|
+
sh_sv = sh.extensions[Message::ExtensionType::SUPPORTED_VERSIONS]
|
197
|
+
hrr_sv = hrr.extensions[Message::ExtensionType::SUPPORTED_VERSIONS]
|
198
|
+
terminate(:illegal_parameter) \
|
199
|
+
unless sh_sv.versions == hrr_sv.versions
|
200
|
+
end
|
173
201
|
|
174
202
|
# handling HRR
|
175
203
|
if sh.hrr?
|
176
|
-
terminate(:unexpected_message) if
|
204
|
+
terminate(:unexpected_message) if transcript.include?(HRR)
|
205
|
+
ch1 = transcript[CH1] = transcript.delete(CH)
|
206
|
+
hrr = transcript[HRR] = transcript.delete(SH)
|
177
207
|
|
178
|
-
|
179
|
-
|
208
|
+
# validate cookie
|
209
|
+
diff_sets = sh.extensions.keys - ch1.extensions.keys
|
180
210
|
terminate(:unsupported_extension) \
|
181
|
-
unless
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
211
|
+
unless (diff_sets - [Message::ExtensionType::COOKIE]).empty?
|
212
|
+
|
213
|
+
# validate key_share
|
214
|
+
# TODO: pre_shared_key
|
215
|
+
ngl = ch1.extensions[Message::ExtensionType::SUPPORTED_GROUPS]
|
216
|
+
.named_group_list
|
217
|
+
kse = ch1.extensions[Message::ExtensionType::KEY_SHARE]
|
218
|
+
.key_share_entry
|
219
|
+
group = hrr.extensions[Message::ExtensionType::KEY_SHARE]
|
220
|
+
.key_share_entry.first.group
|
221
|
+
terminate(:illegal_parameter) \
|
222
|
+
unless ngl.include?(group) && !kse.map(&:group).include?(group)
|
223
|
+
|
224
|
+
# send new client_hello
|
225
|
+
extensions, pk = gen_newch_extensions(ch1, hrr)
|
226
|
+
priv_keys = pk.merge(priv_keys)
|
227
|
+
binder_key = (use_psk? ? key_schedule.binder_key_res : nil)
|
228
|
+
transcript[CH] = send_new_client_hello(ch1, hrr, extensions,
|
229
|
+
binder_key)
|
186
230
|
@state = ClientState::WAIT_SH
|
187
231
|
next
|
188
232
|
end
|
189
233
|
|
190
|
-
# validate extensions
|
191
|
-
terminate(:unsupported_extension) \
|
192
|
-
unless offered_ch_extensions?(sh.extensions)
|
193
|
-
|
194
|
-
versions \
|
195
|
-
= sh.extensions[Message::ExtensionType::SUPPORTED_VERSIONS].versions
|
196
|
-
terminate(:illegal_parameter) \
|
197
|
-
if @transcript.include?(HRR) &&
|
198
|
-
neq_hrr_supported_versions?(versions)
|
199
|
-
|
200
234
|
# generate shared secret
|
201
|
-
|
202
|
-
|
203
|
-
|
235
|
+
psk = nil unless sh.extensions
|
236
|
+
.include?(Message::ExtensionType::PRE_SHARED_KEY)
|
237
|
+
ch_ks = ch.extensions[Message::ExtensionType::KEY_SHARE]
|
238
|
+
.key_share_entry.map(&:group)
|
239
|
+
sh_ks = sh.extensions[Message::ExtensionType::KEY_SHARE]
|
240
|
+
.key_share_entry.first.group
|
241
|
+
terminate(:illegal_parameter) unless ch_ks.include?(sh_ks)
|
204
242
|
|
205
243
|
kse = sh.extensions[Message::ExtensionType::KEY_SHARE]
|
206
244
|
.key_share_entry.first
|
207
245
|
ke = kse.key_exchange
|
208
|
-
|
209
|
-
priv_key =
|
210
|
-
shared_secret = gen_shared_secret(ke, priv_key,
|
246
|
+
@named_group = kse.group
|
247
|
+
priv_key = priv_keys[@named_group]
|
248
|
+
shared_secret = gen_shared_secret(ke, priv_key, @named_group)
|
211
249
|
@cipher_suite = sh.cipher_suite
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
250
|
+
key_schedule = KeySchedule.new(
|
251
|
+
psk: psk,
|
252
|
+
shared_secret: shared_secret,
|
253
|
+
cipher_suite: @cipher_suite,
|
254
|
+
transcript: transcript
|
255
|
+
)
|
256
|
+
@alert_wcipher = hs_wcipher = gen_cipher(
|
257
|
+
@cipher_suite,
|
258
|
+
key_schedule.client_handshake_write_key,
|
259
|
+
key_schedule.client_handshake_write_iv
|
260
|
+
)
|
261
|
+
hs_rcipher = gen_cipher(
|
262
|
+
@cipher_suite,
|
263
|
+
key_schedule.server_handshake_write_key,
|
264
|
+
key_schedule.server_handshake_write_iv
|
265
|
+
)
|
222
266
|
@state = ClientState::WAIT_EE
|
223
267
|
when ClientState::WAIT_EE
|
224
268
|
logger.debug('ClientState::WAIT_EE')
|
225
269
|
|
226
|
-
ee =
|
227
|
-
terminate(:illegal_parameter) unless ee.
|
270
|
+
ee = transcript[EE] = recv_encrypted_extensions(hs_rcipher)
|
271
|
+
terminate(:illegal_parameter) unless ee.appearable_extensions?
|
272
|
+
|
273
|
+
ch = transcript[CH]
|
228
274
|
terminate(:unsupported_extension) \
|
229
|
-
unless
|
275
|
+
unless (ee.extensions.keys - ch.extensions.keys).empty?
|
230
276
|
|
231
277
|
rsl = ee.extensions[Message::ExtensionType::RECORD_SIZE_LIMIT]
|
232
278
|
@send_record_size = rsl.record_size_limit unless rsl.nil?
|
@@ -235,17 +281,19 @@ module TTTLS13
|
|
235
281
|
if ee.extensions.include?(Message::ExtensionType::EARLY_DATA)
|
236
282
|
|
237
283
|
@state = ClientState::WAIT_CERT_CR
|
238
|
-
@state = ClientState::WAIT_FINISHED unless
|
284
|
+
@state = ClientState::WAIT_FINISHED unless psk.nil?
|
239
285
|
when ClientState::WAIT_CERT_CR
|
240
286
|
logger.debug('ClientState::WAIT_EE')
|
241
287
|
|
242
|
-
message = recv_message
|
288
|
+
message = recv_message(receivable_ccs: true, cipher: hs_rcipher)
|
243
289
|
if message.msg_type == Message::HandshakeType::CERTIFICATE
|
244
|
-
ct =
|
245
|
-
terminate(:illegal_parameter) unless ct.
|
290
|
+
ct = transcript[CT] = message
|
291
|
+
terminate(:illegal_parameter) unless ct.appearable_extensions?
|
292
|
+
|
293
|
+
ch = transcript[CH]
|
246
294
|
terminate(:unsupported_extension) \
|
247
295
|
unless ct.certificate_list.map(&:extensions)
|
248
|
-
.all? { |
|
296
|
+
.all? { |e| (e.keys - ch.extensions.keys).empty? }
|
249
297
|
|
250
298
|
terminate(:certificate_unknown) \
|
251
299
|
unless trusted_certificate?(ct.certificate_list,
|
@@ -253,7 +301,7 @@ module TTTLS13
|
|
253
301
|
|
254
302
|
@state = ClientState::WAIT_CV
|
255
303
|
elsif message.msg_type == Message::HandshakeType::CERTIFICATE_REQUEST
|
256
|
-
|
304
|
+
transcript[CR] = message
|
257
305
|
# TODO: client authentication
|
258
306
|
@state = ClientState::WAIT_CERT
|
259
307
|
else
|
@@ -262,11 +310,13 @@ module TTTLS13
|
|
262
310
|
when ClientState::WAIT_CERT
|
263
311
|
logger.debug('ClientState::WAIT_EE')
|
264
312
|
|
265
|
-
ct =
|
266
|
-
terminate(:illegal_parameter) unless ct.
|
313
|
+
ct = transcript[CT] = recv_certificate(hs_rcipher)
|
314
|
+
terminate(:illegal_parameter) unless ct.appearable_extensions?
|
315
|
+
|
316
|
+
ch = transcript[CH]
|
267
317
|
terminate(:unsupported_extension) \
|
268
318
|
unless ct.certificate_list.map(&:extensions)
|
269
|
-
.all? { |
|
319
|
+
.all? { |e| (e.keys - ch.extensions.keys).empty? }
|
270
320
|
|
271
321
|
terminate(:certificate_unknown) \
|
272
322
|
unless trusted_certificate?(ct.certificate_list,
|
@@ -276,27 +326,52 @@ module TTTLS13
|
|
276
326
|
when ClientState::WAIT_CV
|
277
327
|
logger.debug('ClientState::WAIT_EE')
|
278
328
|
|
279
|
-
|
280
|
-
|
329
|
+
cv = transcript[CV] = recv_certificate_verify(hs_rcipher)
|
330
|
+
digest = CipherSuite.digest(@cipher_suite)
|
331
|
+
hash = transcript.hash(digest, CT)
|
332
|
+
terminate(:decrypt_error) \
|
333
|
+
unless verified_certificate_verify?(transcript[CT], cv, hash)
|
334
|
+
|
335
|
+
@signature_scheme = cv.signature_scheme
|
336
|
+
|
281
337
|
@state = ClientState::WAIT_FINISHED
|
282
338
|
when ClientState::WAIT_FINISHED
|
283
339
|
logger.debug('ClientState::WAIT_EE')
|
284
340
|
|
285
|
-
|
286
|
-
|
287
|
-
|
341
|
+
sf = transcript[SF] = recv_finished(hs_rcipher)
|
342
|
+
digest = CipherSuite.digest(@cipher_suite)
|
343
|
+
verified = verified_finished?(
|
344
|
+
finished: sf,
|
345
|
+
digest: digest,
|
346
|
+
finished_key: key_schedule.server_finished_key,
|
347
|
+
hash: transcript.hash(digest, CV)
|
348
|
+
)
|
349
|
+
terminate(:decrypt_error) unless verified
|
350
|
+
|
351
|
+
transcript[EOED] = send_eoed(e_wcipher) \
|
288
352
|
if use_early_data? && succeed_early_data?
|
353
|
+
|
289
354
|
# TODO: Send Certificate [+ CertificateVerify]
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
355
|
+
signature = sign_finished(
|
356
|
+
digest: digest,
|
357
|
+
finished_key: key_schedule.client_finished_key,
|
358
|
+
hash: transcript.hash(digest, EOED)
|
359
|
+
)
|
360
|
+
transcript[CF] = send_finished(signature, hs_wcipher)
|
361
|
+
@alert_wcipher = @ap_wcipher = gen_cipher(
|
362
|
+
@cipher_suite,
|
363
|
+
key_schedule.client_application_write_key,
|
364
|
+
key_schedule.client_application_write_iv
|
365
|
+
)
|
366
|
+
@ap_rcipher = gen_cipher(
|
367
|
+
@cipher_suite,
|
368
|
+
key_schedule.server_application_write_key,
|
369
|
+
key_schedule.server_application_write_iv
|
370
|
+
)
|
371
|
+
@resumption_master_secret = key_schedule.resumption_master_secret
|
297
372
|
@state = ClientState::CONNECTED
|
298
373
|
when ClientState::CONNECTED
|
299
|
-
logger.debug('ClientState::
|
374
|
+
logger.debug('ClientState::CONNECTED')
|
300
375
|
|
301
376
|
break
|
302
377
|
end
|
@@ -369,13 +444,14 @@ module TTTLS13
|
|
369
444
|
!(@early_data.nil? || @early_data.empty?)
|
370
445
|
end
|
371
446
|
|
372
|
-
|
447
|
+
# @param cipher [TTTLS13::Cryptograph::Aead]
|
448
|
+
def send_early_data(cipher)
|
373
449
|
ap = Message::ApplicationData.new(@early_data)
|
374
450
|
ap_record = Message::Record.new(
|
375
451
|
type: Message::ContentType::APPLICATION_DATA,
|
376
452
|
legacy_record_version: Message::ProtocolVersion::TLS_1_2,
|
377
453
|
messages: [ap],
|
378
|
-
cipher:
|
454
|
+
cipher: cipher
|
379
455
|
)
|
380
456
|
send_record(ap_record)
|
381
457
|
end
|
@@ -395,8 +471,6 @@ module TTTLS13
|
|
395
471
|
|
396
472
|
# @return [TTTLS13::Message::Extensions]
|
397
473
|
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
398
|
-
# rubocop: disable Metrics/AbcSize
|
399
|
-
# rubocop: disable Metrics/CyclomaticComplexity
|
400
474
|
def gen_ch_extensions
|
401
475
|
exs = []
|
402
476
|
# supported_versions: only TLS 1.3
|
@@ -428,24 +502,22 @@ module TTTLS13
|
|
428
502
|
exs << key_share
|
429
503
|
|
430
504
|
# server_name
|
431
|
-
exs << Message::Extension::ServerName.new(@hostname)
|
432
|
-
if !@hostname.nil? && !@hostname.empty?
|
505
|
+
exs << Message::Extension::ServerName.new(@hostname)
|
433
506
|
|
434
507
|
# early_data
|
435
508
|
exs << Message::Extension::EarlyDataIndication.new if use_early_data?
|
436
509
|
|
437
510
|
[Message::Extensions.new(exs), priv_keys]
|
438
511
|
end
|
439
|
-
# rubocop: enable Metrics/AbcSize
|
440
|
-
# rubocop: enable Metrics/CyclomaticComplexity
|
441
512
|
|
442
|
-
# @param
|
513
|
+
# @param extensions [TTTLS13::Message::Extensions]
|
514
|
+
# @param binder_key [String, nil]
|
443
515
|
#
|
444
516
|
# @return [TTTLS13::Message::ClientHello]
|
445
|
-
def send_client_hello(
|
517
|
+
def send_client_hello(extensions, binder_key = nil)
|
446
518
|
ch = Message::ClientHello.new(
|
447
519
|
cipher_suites: CipherSuites.new(@settings[:cipher_suites]),
|
448
|
-
extensions:
|
520
|
+
extensions: extensions
|
449
521
|
)
|
450
522
|
|
451
523
|
if use_psk?
|
@@ -460,18 +532,25 @@ module TTTLS13
|
|
460
532
|
)
|
461
533
|
ch.extensions[Message::ExtensionType::PSK_KEY_EXCHANGE_MODES] = pkem
|
462
534
|
# at the end, sign PSK binder
|
463
|
-
sign_psk_binder(
|
535
|
+
sign_psk_binder(
|
536
|
+
ch: ch,
|
537
|
+
binder_key: binder_key
|
538
|
+
)
|
464
539
|
end
|
465
540
|
|
466
|
-
send_handshakes(Message::ContentType::HANDSHAKE, [ch],
|
541
|
+
send_handshakes(Message::ContentType::HANDSHAKE, [ch],
|
542
|
+
Cryptograph::Passer.new)
|
467
543
|
|
468
544
|
ch
|
469
545
|
end
|
470
546
|
|
547
|
+
# @param ch1 [TTTLS13::Message::ClientHello]
|
548
|
+
# @param hrr [TTTLS13::Message::ServerHello]
|
471
549
|
# @param ch [TTTLS13::Message::ClientHello]
|
550
|
+
# @param binder_key [String]
|
472
551
|
#
|
473
552
|
# @return [String]
|
474
|
-
def sign_psk_binder(ch)
|
553
|
+
def sign_psk_binder(ch1: nil, hrr: nil, ch:, binder_key:)
|
475
554
|
# pre_shared_key
|
476
555
|
#
|
477
556
|
# binder is computed as an HMAC over a transcript hash containing a
|
@@ -494,9 +573,13 @@ module TTTLS13
|
|
494
573
|
)
|
495
574
|
ch.extensions[Message::ExtensionType::PRE_SHARED_KEY] = psk
|
496
575
|
|
497
|
-
|
498
|
-
|
499
|
-
|
576
|
+
psk.offered_psks.binders[0] = do_sign_psk_binder(
|
577
|
+
ch1: ch1,
|
578
|
+
hrr: hrr,
|
579
|
+
ch: ch,
|
580
|
+
binder_key: binder_key,
|
581
|
+
digest: digest
|
582
|
+
)
|
500
583
|
end
|
501
584
|
|
502
585
|
# @return [Integer]
|
@@ -507,24 +590,20 @@ module TTTLS13
|
|
507
590
|
(age + Convert.bin2i(@settings[:ticket_age_add])) % (2**32)
|
508
591
|
end
|
509
592
|
|
510
|
-
# NOTE:
|
511
|
-
# https://tools.ietf.org/html/rfc8446#section-4.1.2
|
512
|
-
#
|
513
593
|
# @param ch1 [TTTLS13::Message::ClientHello]
|
514
594
|
# @param hrr [TTTLS13::Message::ServerHello]
|
515
595
|
#
|
516
|
-
# @return [TTTLS13::Message::
|
517
|
-
|
518
|
-
|
519
|
-
|
596
|
+
# @return [TTTLS13::Message::Extensions]
|
597
|
+
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
598
|
+
def gen_newch_extensions(ch1, hrr)
|
599
|
+
exs = []
|
520
600
|
# key_share
|
521
601
|
if hrr.extensions.include?(Message::ExtensionType::KEY_SHARE)
|
522
602
|
group = hrr.extensions[Message::ExtensionType::KEY_SHARE]
|
523
603
|
.key_share_entry.first.group
|
524
604
|
key_share, priv_keys \
|
525
605
|
= Message::Extension::KeyShare.gen_ch_key_share([group])
|
526
|
-
|
527
|
-
@priv_keys = priv_keys.merge(@priv_keys)
|
606
|
+
exs << key_share
|
528
607
|
end
|
529
608
|
|
530
609
|
# cookie
|
@@ -535,21 +614,45 @@ module TTTLS13
|
|
535
614
|
# HelloRetryRequest into a "cookie" extension in the new ClientHello.
|
536
615
|
#
|
537
616
|
# https://tools.ietf.org/html/rfc8446#section-4.2.2
|
538
|
-
|
617
|
+
exs << hrr.extensions[Message::ExtensionType::COOKIE] \
|
539
618
|
if hrr.extensions.include?(Message::ExtensionType::COOKIE)
|
540
619
|
|
541
620
|
# early_data
|
542
|
-
new_exs = ch1.extensions.merge(Message::Extensions.new(
|
621
|
+
new_exs = ch1.extensions.merge(Message::Extensions.new(exs))
|
543
622
|
new_exs.delete(Message::ExtensionType::EARLY_DATA)
|
623
|
+
|
624
|
+
[new_exs, priv_keys]
|
625
|
+
end
|
626
|
+
|
627
|
+
# NOTE:
|
628
|
+
# https://tools.ietf.org/html/rfc8446#section-4.1.2
|
629
|
+
#
|
630
|
+
# @param ch1 [TTTLS13::Message::ClientHello]
|
631
|
+
# @param hrr [TTTLS13::Message::ServerHello]
|
632
|
+
# @param extensions [TTTLS13::Message::Extensions]
|
633
|
+
# @param binder_key [String, nil]
|
634
|
+
#
|
635
|
+
# @return [TTTLS13::Message::ClientHello]
|
636
|
+
def send_new_client_hello(ch1, hrr, extensions, binder_key = nil)
|
544
637
|
ch = Message::ClientHello.new(
|
545
638
|
legacy_version: ch1.legacy_version,
|
546
639
|
random: ch1.random,
|
547
640
|
legacy_session_id: ch1.legacy_session_id,
|
548
641
|
cipher_suites: ch1.cipher_suites,
|
549
642
|
legacy_compression_methods: ch1.legacy_compression_methods,
|
550
|
-
extensions:
|
643
|
+
extensions: extensions
|
551
644
|
)
|
552
|
-
|
645
|
+
|
646
|
+
# pre_shared_key
|
647
|
+
#
|
648
|
+
# Updating the "pre_shared_key" extension if present by recomputing
|
649
|
+
# the "obfuscated_ticket_age" and binder values.
|
650
|
+
if ch1.extensions.include?(Message::ExtensionType::PRE_SHARED_KEY)
|
651
|
+
sign_psk_binder(ch1: ch1, hrr: hrr, ch: ch, binder_key: binder_key)
|
652
|
+
end
|
653
|
+
|
654
|
+
send_handshakes(Message::ContentType::HANDSHAKE, [ch],
|
655
|
+
Cryptograph::Passer.new)
|
553
656
|
|
554
657
|
ch
|
555
658
|
end
|
@@ -558,208 +661,98 @@ module TTTLS13
|
|
558
661
|
#
|
559
662
|
# @return [TTTLS13::Message::ServerHello]
|
560
663
|
def recv_server_hello
|
561
|
-
sh = recv_message
|
664
|
+
sh = recv_message(receivable_ccs: true, cipher: Cryptograph::Passer.new)
|
562
665
|
terminate(:unexpected_message) unless sh.is_a?(Message::ServerHello)
|
563
666
|
|
564
667
|
sh
|
565
668
|
end
|
566
669
|
|
670
|
+
# @param cipher [TTTLS13::Cryptograph::Aead]
|
671
|
+
#
|
567
672
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
568
673
|
#
|
569
674
|
# @return [TTTLS13::Message::EncryptedExtensions]
|
570
|
-
def recv_encrypted_extensions
|
571
|
-
ee = recv_message
|
675
|
+
def recv_encrypted_extensions(cipher)
|
676
|
+
ee = recv_message(receivable_ccs: true, cipher: cipher)
|
572
677
|
terminate(:unexpected_message) \
|
573
678
|
unless ee.is_a?(Message::EncryptedExtensions)
|
574
679
|
|
575
680
|
ee
|
576
681
|
end
|
577
682
|
|
683
|
+
# @param cipher [TTTLS13::Cryptograph::Aead]
|
684
|
+
#
|
578
685
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
579
686
|
#
|
580
687
|
# @return [TTTLS13::Message::Certificate]
|
581
|
-
def recv_certificate
|
582
|
-
ct = recv_message
|
688
|
+
def recv_certificate(cipher)
|
689
|
+
ct = recv_message(receivable_ccs: true, cipher: cipher)
|
583
690
|
terminate(:unexpected_message) unless ct.is_a?(Message::Certificate)
|
584
691
|
|
585
692
|
ct
|
586
693
|
end
|
587
694
|
|
695
|
+
# @param cipher [TTTLS13::Cryptograph::Aead]
|
696
|
+
#
|
588
697
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
589
698
|
#
|
590
699
|
# @return [TTTLS13::Message::CertificateVerify]
|
591
|
-
def recv_certificate_verify
|
592
|
-
cv = recv_message
|
700
|
+
def recv_certificate_verify(cipher)
|
701
|
+
cv = recv_message(receivable_ccs: true, cipher: cipher)
|
593
702
|
terminate(:unexpected_message) unless cv.is_a?(Message::CertificateVerify)
|
594
703
|
|
595
704
|
cv
|
596
705
|
end
|
597
706
|
|
707
|
+
# @param cipher [TTTLS13::Cryptograph::Aead]
|
708
|
+
#
|
598
709
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
599
710
|
#
|
600
711
|
# @return [TTTLS13::Message::Finished]
|
601
|
-
def recv_finished
|
602
|
-
sf = recv_message
|
712
|
+
def recv_finished(cipher)
|
713
|
+
sf = recv_message(receivable_ccs: true, cipher: cipher)
|
603
714
|
terminate(:unexpected_message) unless sf.is_a?(Message::Finished)
|
604
715
|
|
605
716
|
sf
|
606
717
|
end
|
607
718
|
|
719
|
+
# @param cipher [TTTLS13::Cryptograph::Aead]
|
720
|
+
#
|
608
721
|
# @return [TTTLS13::Message::Finished]
|
609
|
-
def send_finished
|
610
|
-
cf = Message::Finished.new(
|
611
|
-
send_handshakes(Message::ContentType::APPLICATION_DATA, [cf],
|
612
|
-
@write_cipher)
|
722
|
+
def send_finished(signature, cipher)
|
723
|
+
cf = Message::Finished.new(signature)
|
724
|
+
send_handshakes(Message::ContentType::APPLICATION_DATA, [cf], cipher)
|
613
725
|
|
614
726
|
cf
|
615
727
|
end
|
616
728
|
|
729
|
+
# @param cipher [TTTLS13::Cryptograph::Aead]
|
730
|
+
#
|
617
731
|
# @return [TTTLS13::Message::EndOfEarlyData]
|
618
|
-
def send_eoed
|
732
|
+
def send_eoed(cipher)
|
619
733
|
eoed = Message::EndOfEarlyData.new
|
620
|
-
send_handshakes(Message::ContentType::APPLICATION_DATA, [eoed],
|
621
|
-
@early_data_write_cipher)
|
734
|
+
send_handshakes(Message::ContentType::APPLICATION_DATA, [eoed], cipher)
|
622
735
|
|
623
736
|
eoed
|
624
737
|
end
|
625
738
|
|
739
|
+
# @param ct [TTTLS13::Message::Certificate]
|
740
|
+
# @param cv [TTTLS13::Message::CertificateVerify]
|
741
|
+
# @param hash [String]
|
742
|
+
#
|
626
743
|
# @return [Boolean]
|
627
|
-
def verified_certificate_verify?
|
628
|
-
ct = @transcript[CT]
|
744
|
+
def verified_certificate_verify?(ct, cv, hash)
|
629
745
|
public_key = ct.certificate_list.first.cert_data.public_key
|
630
|
-
cv = @transcript[CV]
|
631
746
|
signature_scheme = cv.signature_scheme
|
632
747
|
signature = cv.signature
|
633
|
-
context = 'TLS 1.3, server CertificateVerify'
|
634
|
-
do_verified_certificate_verify?(public_key: public_key,
|
635
|
-
signature_scheme: signature_scheme,
|
636
|
-
signature: signature,
|
637
|
-
context: context,
|
638
|
-
handshake_context_end: CT)
|
639
|
-
end
|
640
|
-
|
641
|
-
# @return [String]
|
642
|
-
def sign_finished
|
643
|
-
digest = CipherSuite.digest(@cipher_suite)
|
644
|
-
finished_key = @key_schedule.client_finished_key
|
645
|
-
do_sign_finished(digest: digest,
|
646
|
-
finished_key: finished_key,
|
647
|
-
handshake_context_end: EOED)
|
648
|
-
end
|
649
|
-
|
650
|
-
# @return [Boolean]
|
651
|
-
def verified_finished?
|
652
|
-
digest = CipherSuite.digest(@cipher_suite)
|
653
|
-
finished_key = @key_schedule.server_finished_key
|
654
|
-
signature = @transcript[SF].verify_data
|
655
|
-
do_verified_finished?(digest: digest,
|
656
|
-
finished_key: finished_key,
|
657
|
-
handshake_context_end: CV,
|
658
|
-
signature: signature)
|
659
|
-
end
|
660
|
-
|
661
|
-
# NOTE:
|
662
|
-
# This implementation supports only TLS 1.3,
|
663
|
-
# so negotiated_tls_1_3? assumes that it sent ClientHello with:
|
664
|
-
# 1. supported_versions == ["\x03\x04"]
|
665
|
-
# 2. legacy_versions == ["\x03\x03"]
|
666
|
-
#
|
667
|
-
# @return [Boolean]
|
668
|
-
def negotiated_tls_1_3?
|
669
|
-
sh = @transcript[SH]
|
670
|
-
sh_lv = sh.legacy_version
|
671
|
-
sh_sv = sh.extensions[Message::ExtensionType::SUPPORTED_VERSIONS]
|
672
|
-
&.versions || []
|
673
|
-
|
674
|
-
sh_lv == Message::ProtocolVersion::TLS_1_2 &&
|
675
|
-
sh_sv.first == Message::ProtocolVersion::TLS_1_3
|
676
|
-
end
|
677
|
-
|
678
|
-
# @return [Boolean]
|
679
|
-
def valid_sh_random?
|
680
|
-
sh_r8 = @transcript[SH].random[-8..]
|
681
|
-
|
682
|
-
sh_r8 != DOWNGRADE_PROTECTION_TLS_1_2 &&
|
683
|
-
sh_r8 != DOWNGRADE_PROTECTION_TLS_1_1
|
684
|
-
end
|
685
|
-
|
686
|
-
# @return [Boolean]
|
687
|
-
def valid_sh_legacy_version?
|
688
|
-
@transcript[CH].legacy_version ==
|
689
|
-
@transcript[SH].legacy_version
|
690
|
-
end
|
691
|
-
|
692
|
-
# @return [Boolean]
|
693
|
-
def valid_sh_legacy_session_id_echo?
|
694
|
-
@transcript[CH].legacy_session_id ==
|
695
|
-
@transcript[SH].legacy_session_id_echo
|
696
|
-
end
|
697
|
-
|
698
|
-
# @return [Boolean]
|
699
|
-
def valid_sh_cipher_suite?
|
700
|
-
@transcript[CH].cipher_suites.include?(@transcript[SH].cipher_suite)
|
701
|
-
end
|
702
|
-
|
703
|
-
# @return [Boolean]
|
704
|
-
def valid_sh_compression_method?
|
705
|
-
@transcript[SH].legacy_compression_method == "\x00"
|
706
|
-
end
|
707
|
-
|
708
|
-
# @param extensions [TTTLS13::Message::Extensions]
|
709
|
-
# @param transcript_index [Integer]
|
710
|
-
#
|
711
|
-
# @return [Boolean]
|
712
|
-
def offered_ch_extensions?(extensions, transcript_index = nil)
|
713
|
-
keys = extensions.keys
|
714
|
-
if transcript_index == HRR
|
715
|
-
keys -= @transcript[CH1].extensions.keys
|
716
|
-
keys -= [Message::ExtensionType::COOKIE]
|
717
|
-
else
|
718
|
-
keys -= @transcript[CH].extensions.keys
|
719
|
-
end
|
720
|
-
keys.empty?
|
721
|
-
end
|
722
|
-
|
723
|
-
# @return [Boolean]
|
724
|
-
def received_2nd_hrr?
|
725
|
-
@transcript.include?(HRR)
|
726
|
-
end
|
727
|
-
|
728
|
-
# @param cipher_suite [TTTLS13::CipherSuite]
|
729
|
-
#
|
730
|
-
# @return [Boolean]
|
731
|
-
def neq_hrr_cipher_suite?(cipher_suite)
|
732
|
-
cipher_suite != @transcript[HRR].cipher_suite
|
733
|
-
end
|
734
748
|
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
end
|
743
|
-
|
744
|
-
# @return [Boolean]
|
745
|
-
def valid_sh_key_share?
|
746
|
-
offered = @transcript[CH].extensions[Message::ExtensionType::KEY_SHARE]
|
747
|
-
.key_share_entry.map(&:group)
|
748
|
-
selected = @transcript[SH].extensions[Message::ExtensionType::KEY_SHARE]
|
749
|
-
.key_share_entry.first.group
|
750
|
-
offered.include?(selected)
|
751
|
-
end
|
752
|
-
|
753
|
-
# @return [Boolean]
|
754
|
-
def valid_hrr_key_share?
|
755
|
-
# TODO: pre_shared_key
|
756
|
-
ch1_exs = @transcript[CH1].extensions
|
757
|
-
ngl = ch1_exs[Message::ExtensionType::SUPPORTED_GROUPS].named_group_list
|
758
|
-
kse = ch1_exs[Message::ExtensionType::KEY_SHARE].key_share_entry
|
759
|
-
group = @transcript[HRR].extensions[Message::ExtensionType::KEY_SHARE]
|
760
|
-
.key_share_entry.first.group
|
761
|
-
|
762
|
-
ngl.include?(group) && !kse.map(&:group).include?(group)
|
749
|
+
do_verified_certificate_verify?(
|
750
|
+
public_key: public_key,
|
751
|
+
signature_scheme: signature_scheme,
|
752
|
+
signature: signature,
|
753
|
+
context: 'TLS 1.3, server CertificateVerify',
|
754
|
+
hash: hash
|
755
|
+
)
|
763
756
|
end
|
764
757
|
|
765
758
|
# @param nst [TTTLS13::Message::NewSessionTicket]
|
@@ -768,7 +761,7 @@ module TTTLS13
|
|
768
761
|
def process_new_session_ticket(nst)
|
769
762
|
super(nst)
|
770
763
|
|
771
|
-
rms = @
|
764
|
+
rms = @resumption_master_secret
|
772
765
|
cs = @cipher_suite
|
773
766
|
@settings[:process_new_session_ticket]&.call(nst, rms, cs)
|
774
767
|
end
|