tttls1.3 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|