tttls1.3 0.2.12 → 0.2.16
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 +32 -0
- data/.rubocop.yml +2 -2
- data/Gemfile +3 -4
- data/README.md +4 -1
- data/example/helper.rb +3 -3
- data/example/https_client.rb +1 -1
- data/example/https_client_using_0rtt.rb +2 -2
- data/example/https_client_using_hrr.rb +1 -1
- data/example/https_client_using_hrr_and_ticket.rb +2 -2
- data/example/https_client_using_status_request.rb +2 -2
- data/example/https_client_using_ticket.rb +2 -2
- data/example/https_server.rb +2 -2
- data/interop/client_spec.rb +6 -6
- data/interop/server_spec.rb +6 -6
- data/lib/tttls1.3/client.rb +106 -65
- data/lib/tttls1.3/connection.rb +43 -30
- data/lib/tttls1.3/cryptograph/aead.rb +20 -7
- data/lib/tttls1.3/cryptograph.rb +1 -1
- data/lib/tttls1.3/message/alert.rb +2 -2
- data/lib/tttls1.3/message/client_hello.rb +1 -0
- data/lib/tttls1.3/message/compressed_certificate.rb +82 -0
- data/lib/tttls1.3/message/extension/alpn.rb +5 -2
- data/lib/tttls1.3/message/extension/compress_certificate.rb +58 -0
- data/lib/tttls1.3/message/extension/signature_algorithms.rb +15 -5
- data/lib/tttls1.3/message/extension/signature_algorithms_cert.rb +5 -4
- data/lib/tttls1.3/message/extension/supported_groups.rb +2 -2
- data/lib/tttls1.3/message/extensions.rb +31 -18
- data/lib/tttls1.3/message/record.rb +28 -16
- data/lib/tttls1.3/message.rb +23 -21
- data/lib/tttls1.3/server.rb +88 -37
- data/lib/tttls1.3/transcript.rb +3 -7
- data/lib/tttls1.3/version.rb +1 -1
- data/spec/client_spec.rb +28 -19
- data/spec/compress_certificate_spec.rb +54 -0
- data/spec/connection_spec.rb +22 -15
- data/spec/extensions_spec.rb +16 -0
- data/spec/fixtures/rsa_rsa.crt +15 -15
- data/spec/fixtures/rsa_rsa.key +25 -25
- data/spec/key_schedule_spec.rb +48 -25
- data/spec/record_spec.rb +2 -2
- data/spec/server_hello_spec.rb +1 -1
- data/spec/server_spec.rb +23 -11
- data/spec/signature_algorithms_cert_spec.rb +4 -0
- data/spec/signature_algorithms_spec.rb +4 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/transcript_spec.rb +34 -20
- data/tttls1.3.gemspec +0 -1
- metadata +11 -7
- data/.github/workflows/main.yml +0 -25
data/lib/tttls1.3/client.rb
CHANGED
@@ -43,6 +43,11 @@ module TTTLS13
|
|
43
43
|
].freeze
|
44
44
|
private_constant :DEFAULT_CH_NAMED_GROUP_LIST
|
45
45
|
|
46
|
+
DEFALUT_CH_COMPRESS_CERTIFICATE_ALGORITHMS = [
|
47
|
+
Message::Extension::CertificateCompressionAlgorithm::ZLIB
|
48
|
+
].freeze
|
49
|
+
private_constant :DEFALUT_CH_COMPRESS_CERTIFICATE_ALGORITHMS
|
50
|
+
|
46
51
|
DEFAULT_CLIENT_SETTINGS = {
|
47
52
|
ca_file: nil,
|
48
53
|
cipher_suites: DEFAULT_CH_CIPHER_SUITES,
|
@@ -61,6 +66,7 @@ module TTTLS13
|
|
61
66
|
record_size_limit: nil,
|
62
67
|
check_certificate_status: false,
|
63
68
|
process_certificate_status: nil,
|
69
|
+
compress_certificate_algorithms: DEFALUT_CH_COMPRESS_CERTIFICATE_ALGORITHMS,
|
64
70
|
compatibility_mode: true,
|
65
71
|
loglevel: Logger::WARN
|
66
72
|
}.freeze
|
@@ -154,8 +160,8 @@ module TTTLS13
|
|
154
160
|
|
155
161
|
extensions, priv_keys = gen_ch_extensions
|
156
162
|
binder_key = (use_psk? ? key_schedule.binder_key_res : nil)
|
157
|
-
|
158
|
-
|
163
|
+
ch = send_client_hello(extensions, binder_key)
|
164
|
+
transcript[CH] = [ch, ch.serialize]
|
159
165
|
send_ccs if @settings[:compatibility_mode]
|
160
166
|
if use_early_data?
|
161
167
|
e_wcipher = gen_cipher(
|
@@ -170,7 +176,7 @@ module TTTLS13
|
|
170
176
|
when ClientState::WAIT_SH
|
171
177
|
logger.debug('ClientState::WAIT_SH')
|
172
178
|
|
173
|
-
sh = transcript[SH] = recv_server_hello
|
179
|
+
sh, = transcript[SH] = recv_server_hello
|
174
180
|
|
175
181
|
# downgrade protection
|
176
182
|
if !sh.negotiated_tls_1_3? && sh.downgraded?
|
@@ -187,7 +193,7 @@ module TTTLS13
|
|
187
193
|
unless sh.legacy_compression_method == "\x00"
|
188
194
|
|
189
195
|
# validate sh using ch
|
190
|
-
ch = transcript[CH]
|
196
|
+
ch, = transcript[CH]
|
191
197
|
terminate(:illegal_parameter) \
|
192
198
|
unless sh.legacy_version == ch.legacy_version
|
193
199
|
terminate(:illegal_parameter) \
|
@@ -199,7 +205,7 @@ module TTTLS13
|
|
199
205
|
|
200
206
|
# validate sh using hrr
|
201
207
|
if transcript.include?(HRR)
|
202
|
-
hrr = transcript[HRR]
|
208
|
+
hrr, = transcript[HRR]
|
203
209
|
terminate(:illegal_parameter) \
|
204
210
|
unless sh.cipher_suite == hrr.cipher_suite
|
205
211
|
|
@@ -212,8 +218,9 @@ module TTTLS13
|
|
212
218
|
# handling HRR
|
213
219
|
if sh.hrr?
|
214
220
|
terminate(:unexpected_message) if transcript.include?(HRR)
|
215
|
-
|
216
|
-
|
221
|
+
|
222
|
+
ch1, = transcript[CH1] = transcript.delete(CH)
|
223
|
+
hrr, = transcript[HRR] = transcript.delete(SH)
|
217
224
|
|
218
225
|
# validate cookie
|
219
226
|
diff_sets = sh.extensions.keys - ch1.extensions.keys
|
@@ -235,8 +242,9 @@ module TTTLS13
|
|
235
242
|
extensions, pk = gen_newch_extensions(ch1, hrr)
|
236
243
|
priv_keys = pk.merge(priv_keys)
|
237
244
|
binder_key = (use_psk? ? key_schedule.binder_key_res : nil)
|
238
|
-
|
239
|
-
|
245
|
+
ch = send_new_client_hello(ch1, hrr, extensions, binder_key)
|
246
|
+
transcript[CH] = [ch, ch.serialize]
|
247
|
+
|
240
248
|
@state = ClientState::WAIT_SH
|
241
249
|
next
|
242
250
|
end
|
@@ -277,37 +285,46 @@ module TTTLS13
|
|
277
285
|
when ClientState::WAIT_EE
|
278
286
|
logger.debug('ClientState::WAIT_EE')
|
279
287
|
|
280
|
-
ee = transcript[EE] = recv_encrypted_extensions(hs_rcipher)
|
288
|
+
ee, = transcript[EE] = recv_encrypted_extensions(hs_rcipher)
|
281
289
|
terminate(:illegal_parameter) unless ee.appearable_extensions?
|
282
290
|
|
283
|
-
ch = transcript[CH]
|
291
|
+
ch, = transcript[CH]
|
284
292
|
terminate(:unsupported_extension) \
|
285
293
|
unless (ee.extensions.keys - ch.extensions.keys).empty?
|
286
294
|
|
287
295
|
rsl = ee.extensions[Message::ExtensionType::RECORD_SIZE_LIMIT]
|
288
296
|
@recv_record_size = rsl.record_size_limit unless rsl.nil?
|
289
|
-
|
290
297
|
@succeed_early_data = true \
|
291
298
|
if ee.extensions.include?(Message::ExtensionType::EARLY_DATA)
|
292
|
-
|
293
299
|
@alpn = ee.extensions[
|
294
300
|
Message::ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
|
295
301
|
]&.protocol_name_list&.first
|
296
|
-
|
297
302
|
@state = ClientState::WAIT_CERT_CR
|
298
303
|
@state = ClientState::WAIT_FINISHED unless psk.nil?
|
299
304
|
when ClientState::WAIT_CERT_CR
|
300
305
|
logger.debug('ClientState::WAIT_CERT_CR')
|
301
306
|
|
302
|
-
message = recv_message(
|
303
|
-
|
304
|
-
|
305
|
-
|
307
|
+
message, orig_msg = recv_message(
|
308
|
+
receivable_ccs: true,
|
309
|
+
cipher: hs_rcipher
|
310
|
+
)
|
311
|
+
case message.msg_type
|
312
|
+
when Message::HandshakeType::CERTIFICATE,
|
313
|
+
Message::HandshakeType::COMPRESSED_CERTIFICATE
|
314
|
+
ct, = transcript[CT] = [message, orig_msg]
|
315
|
+
terminate(:bad_certificate) \
|
316
|
+
if ct.is_a?(Message::CompressedCertificate) &&
|
317
|
+
!@settings[:compress_certificate_algorithms]
|
318
|
+
.include?(ct.algorithm)
|
319
|
+
|
320
|
+
ct = ct.certificate_message \
|
321
|
+
if ct.is_a?(Message::CompressedCertificate)
|
322
|
+
alert = check_invalid_certificate(ct, transcript[CH].first)
|
306
323
|
terminate(alert) unless alert.nil?
|
307
324
|
|
308
325
|
@state = ClientState::WAIT_CV
|
309
|
-
|
310
|
-
transcript[CR] = message
|
326
|
+
when Message::HandshakeType::CERTIFICATE_REQUEST
|
327
|
+
transcript[CR] = [message, orig_msg]
|
311
328
|
# TODO: client authentication
|
312
329
|
@state = ClientState::WAIT_CERT
|
313
330
|
else
|
@@ -316,46 +333,56 @@ module TTTLS13
|
|
316
333
|
when ClientState::WAIT_CERT
|
317
334
|
logger.debug('ClientState::WAIT_CERT')
|
318
335
|
|
319
|
-
ct = transcript[CT] = recv_certificate(hs_rcipher)
|
320
|
-
|
336
|
+
ct, = transcript[CT] = recv_certificate(hs_rcipher)
|
337
|
+
if ct.is_a?(Message::CompressedCertificate) &&
|
338
|
+
!@settings[:compress_certificate_algorithms].include?(ct.algorithm)
|
339
|
+
terminate(:bad_certificate)
|
340
|
+
elsif ct.is_a?(Message::CompressedCertificate)
|
341
|
+
ct = ct.certificate_message
|
342
|
+
end
|
343
|
+
|
344
|
+
alert = check_invalid_certificate(ct, transcript[CH].first)
|
321
345
|
terminate(alert) unless alert.nil?
|
322
346
|
|
323
347
|
@state = ClientState::WAIT_CV
|
324
348
|
when ClientState::WAIT_CV
|
325
349
|
logger.debug('ClientState::WAIT_CV')
|
326
350
|
|
327
|
-
cv = transcript[CV] = recv_certificate_verify(hs_rcipher)
|
351
|
+
cv, = transcript[CV] = recv_certificate_verify(hs_rcipher)
|
328
352
|
digest = CipherSuite.digest(@cipher_suite)
|
329
353
|
hash = transcript.hash(digest, CT)
|
354
|
+
ct, = transcript[CT]
|
355
|
+
ct = ct.certificate_message \
|
356
|
+
if ct.is_a?(Message::CompressedCertificate)
|
330
357
|
terminate(:decrypt_error) \
|
331
|
-
unless verified_certificate_verify?(
|
358
|
+
unless verified_certificate_verify?(ct, cv, hash)
|
332
359
|
|
333
360
|
@signature_scheme = cv.signature_scheme
|
334
|
-
|
335
361
|
@state = ClientState::WAIT_FINISHED
|
336
362
|
when ClientState::WAIT_FINISHED
|
337
363
|
logger.debug('ClientState::WAIT_FINISHED')
|
338
364
|
|
339
|
-
sf = transcript[SF] = recv_finished(hs_rcipher)
|
365
|
+
sf, = transcript[SF] = recv_finished(hs_rcipher)
|
340
366
|
digest = CipherSuite.digest(@cipher_suite)
|
341
|
-
|
367
|
+
terminate(:decrypt_error) unless verified_finished?(
|
342
368
|
finished: sf,
|
343
369
|
digest: digest,
|
344
370
|
finished_key: key_schedule.server_finished_key,
|
345
371
|
hash: transcript.hash(digest, CV)
|
346
372
|
)
|
347
|
-
terminate(:decrypt_error) unless verified
|
348
|
-
|
349
|
-
transcript[EOED] = send_eoed(e_wcipher) \
|
350
|
-
if use_early_data? && succeed_early_data?
|
351
373
|
|
374
|
+
if use_early_data? && succeed_early_data?
|
375
|
+
eoed = send_eoed(e_wcipher)
|
376
|
+
transcript[EOED] = [eoed, eoed.serialize]
|
377
|
+
end
|
352
378
|
# TODO: Send Certificate [+ CertificateVerify]
|
353
379
|
signature = sign_finished(
|
354
380
|
digest: digest,
|
355
381
|
finished_key: key_schedule.client_finished_key,
|
356
382
|
hash: transcript.hash(digest, EOED)
|
357
383
|
)
|
358
|
-
|
384
|
+
cf = send_finished(signature, hs_wcipher)
|
385
|
+
transcript[CF] = [cf, cf.serialize]
|
359
386
|
@alert_wcipher = @ap_wcipher = gen_cipher(
|
360
387
|
@cipher_suite,
|
361
388
|
key_schedule.client_application_write_key,
|
@@ -403,23 +430,16 @@ module TTTLS13
|
|
403
430
|
# @return [Boolean]
|
404
431
|
#
|
405
432
|
# @example
|
406
|
-
#
|
433
|
+
# m = Client.method(:softfail_check_certificate_status)
|
407
434
|
# Client.new(
|
408
435
|
# socket,
|
409
436
|
# hostname,
|
410
437
|
# check_certificate_status: true,
|
411
|
-
# process_certificate_status:
|
438
|
+
# process_certificate_status: m
|
412
439
|
# )
|
413
|
-
# rubocop: disable Metrics/AbcSize
|
414
|
-
# rubocop: disable Metrics/CyclomaticComplexity
|
415
|
-
# rubocop: disable Metrics/PerceivedComplexity
|
416
440
|
def self.softfail_check_certificate_status(res, cert, chain)
|
417
441
|
ocsp_response = res
|
418
|
-
|
419
|
-
store.set_default_paths
|
420
|
-
context = OpenSSL::X509::StoreContext.new(store, cert, chain)
|
421
|
-
context.verify
|
422
|
-
cid = OpenSSL::OCSP::CertificateId.new(cert, context.chain[1])
|
442
|
+
cid = OpenSSL::OCSP::CertificateId.new(cert, chain.first)
|
423
443
|
|
424
444
|
# When NOT received OCSPResponse in TLS handshake, this method will
|
425
445
|
# send OCSPRequest. If ocsp_uri is NOT presented in Certificate, return
|
@@ -430,23 +450,25 @@ module TTTLS13
|
|
430
450
|
return true if uri.nil?
|
431
451
|
|
432
452
|
begin
|
433
|
-
|
453
|
+
# send OCSP::Request
|
454
|
+
ocsp_request = gen_ocsp_request(cid)
|
455
|
+
Timeout.timeout(2) do
|
456
|
+
ocsp_response = send_ocsp_request(ocsp_request, uri)
|
457
|
+
end
|
458
|
+
|
459
|
+
# check nonce of OCSP::Response
|
460
|
+
check_nonce = ocsp_request.check_nonce(ocsp_response.basic)
|
461
|
+
return true unless [-1, 1].include?(check_nonce)
|
434
462
|
rescue StandardError
|
435
463
|
return true
|
436
464
|
end
|
437
465
|
end
|
438
|
-
return
|
466
|
+
return true \
|
439
467
|
if ocsp_response.status != OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL
|
440
468
|
|
441
469
|
status = ocsp_response.basic.status.find { |s| s.first.cmp(cid) }
|
442
|
-
|
443
|
-
return false if !status[3].nil? && status[3] < Time.now
|
444
|
-
|
445
|
-
ocsp_response.basic.verify(chain, store)
|
470
|
+
status[1] != OpenSSL::OCSP::V_CERTSTATUS_REVOKED
|
446
471
|
end
|
447
|
-
# rubocop: enable Metrics/AbcSize
|
448
|
-
# rubocop: enable Metrics/CyclomaticComplexity
|
449
|
-
# rubocop: enable Metrics/PerceivedComplexity
|
450
472
|
|
451
473
|
private
|
452
474
|
|
@@ -479,6 +501,9 @@ module TTTLS13
|
|
479
501
|
rsl = @settings[:record_size_limit]
|
480
502
|
return false if !rsl.nil? && (rsl < 64 || rsl > 2**14 + 1)
|
481
503
|
|
504
|
+
return false if @settings[:check_certificate_status] &&
|
505
|
+
@settings[:process_certificate_status].nil?
|
506
|
+
|
482
507
|
true
|
483
508
|
end
|
484
509
|
# rubocop: enable Metrics/AbcSize
|
@@ -530,7 +555,7 @@ module TTTLS13
|
|
530
555
|
# rubocop: disable Metrics/MethodLength
|
531
556
|
# rubocop: disable Metrics/PerceivedComplexity
|
532
557
|
def gen_ch_extensions
|
533
|
-
exs =
|
558
|
+
exs = Message::Extensions.new
|
534
559
|
# server_name
|
535
560
|
exs << Message::Extension::ServerName.new(@hostname)
|
536
561
|
|
@@ -580,7 +605,15 @@ module TTTLS13
|
|
580
605
|
exs << Message::Extension::OCSPStatusRequest.new \
|
581
606
|
if @settings[:check_certificate_status]
|
582
607
|
|
583
|
-
|
608
|
+
# compress_certificate
|
609
|
+
if !@settings[:compress_certificate_algorithms].nil? &&
|
610
|
+
!@settings[:compress_certificate_algorithms].empty?
|
611
|
+
exs << Message::Extension::CompressCertificate.new(
|
612
|
+
@settings[:compress_certificate_algorithms]
|
613
|
+
)
|
614
|
+
end
|
615
|
+
|
616
|
+
[exs, priv_keys]
|
584
617
|
end
|
585
618
|
# rubocop: enable Metrics/AbcSize
|
586
619
|
# rubocop: enable Metrics/CyclomaticComplexity
|
@@ -673,7 +706,7 @@ module TTTLS13
|
|
673
706
|
# @return [TTTLS13::Message::Extensions]
|
674
707
|
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
675
708
|
def gen_newch_extensions(ch1, hrr)
|
676
|
-
exs =
|
709
|
+
exs = Message::Extensions.new
|
677
710
|
# key_share
|
678
711
|
if hrr.extensions.include?(Message::ExtensionType::KEY_SHARE)
|
679
712
|
group = hrr.extensions[Message::ExtensionType::KEY_SHARE]
|
@@ -695,7 +728,7 @@ module TTTLS13
|
|
695
728
|
if hrr.extensions.include?(Message::ExtensionType::COOKIE)
|
696
729
|
|
697
730
|
# early_data
|
698
|
-
new_exs = ch1.extensions.merge(
|
731
|
+
new_exs = ch1.extensions.merge(exs)
|
699
732
|
new_exs.delete(Message::ExtensionType::EARLY_DATA)
|
700
733
|
|
701
734
|
[new_exs, priv_keys]
|
@@ -737,11 +770,15 @@ module TTTLS13
|
|
737
770
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
738
771
|
#
|
739
772
|
# @return [TTTLS13::Message::ServerHello]
|
773
|
+
# @return [String]
|
740
774
|
def recv_server_hello
|
741
|
-
sh = recv_message(
|
775
|
+
sh, orig_msg = recv_message(
|
776
|
+
receivable_ccs: true,
|
777
|
+
cipher: Cryptograph::Passer.new
|
778
|
+
)
|
742
779
|
terminate(:unexpected_message) unless sh.is_a?(Message::ServerHello)
|
743
780
|
|
744
|
-
sh
|
781
|
+
[sh, orig_msg]
|
745
782
|
end
|
746
783
|
|
747
784
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
@@ -749,12 +786,13 @@ module TTTLS13
|
|
749
786
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
750
787
|
#
|
751
788
|
# @return [TTTLS13::Message::EncryptedExtensions]
|
789
|
+
# @return [String]
|
752
790
|
def recv_encrypted_extensions(cipher)
|
753
|
-
ee = recv_message(receivable_ccs: true, cipher: cipher)
|
791
|
+
ee, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
|
754
792
|
terminate(:unexpected_message) \
|
755
793
|
unless ee.is_a?(Message::EncryptedExtensions)
|
756
794
|
|
757
|
-
ee
|
795
|
+
[ee, orig_msg]
|
758
796
|
end
|
759
797
|
|
760
798
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
@@ -762,11 +800,12 @@ module TTTLS13
|
|
762
800
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
763
801
|
#
|
764
802
|
# @return [TTTLS13::Message::Certificate]
|
803
|
+
# @return [String]
|
765
804
|
def recv_certificate(cipher)
|
766
|
-
ct = recv_message(receivable_ccs: true, cipher: cipher)
|
805
|
+
ct, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
|
767
806
|
terminate(:unexpected_message) unless ct.is_a?(Message::Certificate)
|
768
807
|
|
769
|
-
ct
|
808
|
+
[ct, orig_msg]
|
770
809
|
end
|
771
810
|
|
772
811
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
@@ -774,11 +813,12 @@ module TTTLS13
|
|
774
813
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
775
814
|
#
|
776
815
|
# @return [TTTLS13::Message::CertificateVerify]
|
816
|
+
# @return [String]
|
777
817
|
def recv_certificate_verify(cipher)
|
778
|
-
cv = recv_message(receivable_ccs: true, cipher: cipher)
|
818
|
+
cv, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
|
779
819
|
terminate(:unexpected_message) unless cv.is_a?(Message::CertificateVerify)
|
780
820
|
|
781
|
-
cv
|
821
|
+
[cv, orig_msg]
|
782
822
|
end
|
783
823
|
|
784
824
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
@@ -786,11 +826,12 @@ module TTTLS13
|
|
786
826
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
787
827
|
#
|
788
828
|
# @return [TTTLS13::Message::Finished]
|
829
|
+
# @return [String]
|
789
830
|
def recv_finished(cipher)
|
790
|
-
sf = recv_message(receivable_ccs: true, cipher: cipher)
|
831
|
+
sf, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
|
791
832
|
terminate(:unexpected_message) unless sf.is_a?(Message::Finished)
|
792
833
|
|
793
|
-
sf
|
834
|
+
[sf, orig_msg]
|
794
835
|
end
|
795
836
|
|
796
837
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
data/lib/tttls1.3/connection.rb
CHANGED
@@ -16,7 +16,7 @@ module TTTLS13
|
|
16
16
|
@ap_wcipher = Cryptograph::Passer.new
|
17
17
|
@ap_rcipher = Cryptograph::Passer.new
|
18
18
|
@alert_wcipher = Cryptograph::Passer.new
|
19
|
-
@message_queue = [] # Array of TTTLS13::Message::$Object
|
19
|
+
@message_queue = [] # Array of [TTTLS13::Message::$Object, String]
|
20
20
|
@binary_buffer = '' # deposit Record.surplus_binary
|
21
21
|
@cipher_suite = nil # TTTLS13::CipherSuite
|
22
22
|
@named_group = nil # TTTLS13::NamedGroup
|
@@ -42,7 +42,7 @@ module TTTLS13
|
|
42
42
|
|
43
43
|
message = nil
|
44
44
|
loop do
|
45
|
-
message = recv_message(receivable_ccs: false, cipher: @ap_rcipher)
|
45
|
+
message, = recv_message(receivable_ccs: false, cipher: @ap_rcipher)
|
46
46
|
# At any time after the server has received the client Finished
|
47
47
|
# message, it MAY send a NewSessionTicket message.
|
48
48
|
break unless message.is_a?(Message::NewSessionTicket)
|
@@ -220,13 +220,15 @@ module TTTLS13
|
|
220
220
|
# @raise [TTTLS13::Error::ErrorAlerts
|
221
221
|
#
|
222
222
|
# @return [TTTLS13::Message::$Object]
|
223
|
+
# @return [String]
|
223
224
|
# rubocop: disable Metrics/CyclomaticComplexity
|
224
225
|
def recv_message(receivable_ccs:, cipher:)
|
225
226
|
return @message_queue.shift unless @message_queue.empty?
|
226
227
|
|
227
228
|
messages = nil
|
229
|
+
orig_msgs = []
|
228
230
|
loop do
|
229
|
-
record = recv_record(cipher)
|
231
|
+
record, orig_msgs = recv_record(cipher)
|
230
232
|
case record.type
|
231
233
|
when Message::ContentType::HANDSHAKE,
|
232
234
|
Message::ContentType::APPLICATION_DATA
|
@@ -243,20 +245,22 @@ module TTTLS13
|
|
243
245
|
end
|
244
246
|
end
|
245
247
|
|
246
|
-
@message_queue += messages[1..]
|
248
|
+
@message_queue += messages[1..].zip(orig_msgs[1..])
|
247
249
|
message = messages.first
|
250
|
+
orig_msg = orig_msgs.first
|
248
251
|
if message.is_a?(Message::Alert)
|
249
252
|
handle_received_alert(message)
|
250
253
|
return nil
|
251
254
|
end
|
252
255
|
|
253
|
-
message
|
256
|
+
[message, orig_msg]
|
254
257
|
end
|
255
258
|
# rubocop: enable Metrics/CyclomaticComplexity
|
256
259
|
|
257
260
|
# @param cipher [TTTLS13::Cryptograph::Aead, Passer]
|
258
261
|
#
|
259
262
|
# @return [TTTLS13::Message::Record]
|
263
|
+
# @return [Array of String]
|
260
264
|
def recv_record(cipher)
|
261
265
|
binary = @socket.read(5)
|
262
266
|
record_len = Convert.bin2i(binary.slice(3, 2))
|
@@ -264,9 +268,13 @@ module TTTLS13
|
|
264
268
|
|
265
269
|
begin
|
266
270
|
buffer = @binary_buffer
|
267
|
-
record = Message::Record.deserialize(
|
268
|
-
|
269
|
-
|
271
|
+
record, orig_msgs, surplus_binary = Message::Record.deserialize(
|
272
|
+
binary,
|
273
|
+
cipher,
|
274
|
+
buffer,
|
275
|
+
@recv_record_size
|
276
|
+
)
|
277
|
+
@binary_buffer = surplus_binary
|
270
278
|
rescue Error::ErrorAlerts => e
|
271
279
|
terminate(e.message.to_sym)
|
272
280
|
end
|
@@ -278,7 +286,7 @@ module TTTLS13
|
|
278
286
|
end
|
279
287
|
|
280
288
|
logger.debug("receive \n" + record.pretty_inspect)
|
281
|
-
record
|
289
|
+
[record, orig_msgs]
|
282
290
|
end
|
283
291
|
|
284
292
|
# @param ch1 [TTTLS13::Message::ClientHello]
|
@@ -292,11 +300,9 @@ module TTTLS13
|
|
292
300
|
# TODO: ext binder
|
293
301
|
hash_len = OpenSSL::Digest.new(digest).digest_length
|
294
302
|
tt = Transcript.new
|
295
|
-
tt.
|
296
|
-
|
297
|
-
|
298
|
-
CH => ch
|
299
|
-
)
|
303
|
+
tt[CH1] = [ch1, ch1.serialize] unless ch1.nil?
|
304
|
+
tt[HRR] = [hrr, hrr.serialize] unless hrr.nil?
|
305
|
+
tt[CH] = [ch, ch.serialize]
|
300
306
|
# transcript-hash (CH1 + HRR +) truncated-CH
|
301
307
|
hash = tt.truncate_hash(digest, CH, hash_len + 3)
|
302
308
|
OpenSSL::HMAC.digest(digest, binder_key, hash)
|
@@ -500,11 +506,10 @@ module TTTLS13
|
|
500
506
|
return false if san.nil?
|
501
507
|
|
502
508
|
ostr = OpenSSL::ASN1.decode(san.to_der).value.last
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
matching
|
509
|
+
OpenSSL::ASN1.decode(ostr.value)
|
510
|
+
.map(&:value)
|
511
|
+
.map { |s| s.gsub('.', '\.').gsub('*', '.*') }
|
512
|
+
.any? { |s| name.match(/#{s}/) }
|
508
513
|
end
|
509
514
|
|
510
515
|
# @param signature_algorithms [Array of SignatureAlgorithms]
|
@@ -514,22 +519,22 @@ module TTTLS13
|
|
514
519
|
def do_select_signature_algorithms(signature_algorithms, crt)
|
515
520
|
spki = OpenSSL::Netscape::SPKI.new
|
516
521
|
spki.public_key = crt.public_key
|
517
|
-
|
518
|
-
|
522
|
+
pka = OpenSSL::ASN1.decode(spki.to_der)
|
523
|
+
.value.first.value.first.value.first.value.first.value
|
519
524
|
signature_algorithms.select do |sa|
|
520
525
|
case sa
|
521
526
|
when SignatureScheme::ECDSA_SECP256R1_SHA256,
|
522
527
|
SignatureScheme::ECDSA_SECP384R1_SHA384,
|
523
528
|
SignatureScheme::ECDSA_SECP521R1_SHA512
|
524
|
-
|
529
|
+
pka == 'id-ecPublicKey'
|
525
530
|
when SignatureScheme::RSA_PSS_PSS_SHA256,
|
526
531
|
SignatureScheme::RSA_PSS_PSS_SHA384,
|
527
532
|
SignatureScheme::RSA_PSS_PSS_SHA512
|
528
|
-
|
533
|
+
pka == 'rsassaPss'
|
529
534
|
when SignatureScheme::RSA_PSS_RSAE_SHA256,
|
530
535
|
SignatureScheme::RSA_PSS_RSAE_SHA384,
|
531
536
|
SignatureScheme::RSA_PSS_RSAE_SHA512
|
532
|
-
|
537
|
+
pka == 'rsaEncryption'
|
533
538
|
else
|
534
539
|
# RSASSA-PKCS1-v1_5 algorithms refer solely to signatures which appear
|
535
540
|
# in certificates and are not defined for use in signed TLS handshake
|
@@ -541,19 +546,27 @@ module TTTLS13
|
|
541
546
|
|
542
547
|
class << self
|
543
548
|
# @param cid [OpenSSL::OCSP::CertificateId]
|
544
|
-
# @param uri [String]
|
545
549
|
#
|
546
|
-
# @return [OpenSSL::OCSP::
|
547
|
-
def
|
548
|
-
# generate OCSPRequest
|
550
|
+
# @return [OpenSSL::OCSP::Request]
|
551
|
+
def gen_ocsp_request(cid)
|
549
552
|
ocsp_request = OpenSSL::OCSP::Request.new
|
550
553
|
ocsp_request.add_certid(cid)
|
551
554
|
ocsp_request.add_nonce
|
555
|
+
ocsp_request
|
556
|
+
end
|
557
|
+
|
558
|
+
# @param ocsp_request [OpenSSL::OCSP::Request]
|
559
|
+
# @param uri_string [String]
|
560
|
+
#
|
561
|
+
# @raise [Net::OpenTimeout, OpenSSL::OCSP::OCSPError, URI::$Exception]
|
562
|
+
#
|
563
|
+
# @return [OpenSSL::OCSP::Response, n
|
564
|
+
def send_ocsp_request(ocsp_request, uri_string)
|
552
565
|
# send HTTP POST
|
553
|
-
uri = URI.parse(
|
566
|
+
uri = URI.parse(uri_string)
|
554
567
|
path = uri.path
|
555
568
|
path = '/' if path.nil? || path.empty?
|
556
|
-
http_response = Net::HTTP.start
|
569
|
+
http_response = Net::HTTP.start(uri.host, uri.port) do |http|
|
557
570
|
http.post(
|
558
571
|
path,
|
559
572
|
ocsp_request.to_der,
|
@@ -44,8 +44,7 @@ module TTTLS13
|
|
44
44
|
#
|
45
45
|
# @return [String]
|
46
46
|
def encrypt(content, type)
|
47
|
-
reset_cipher
|
48
|
-
cipher = @cipher.encrypt
|
47
|
+
cipher = reset_cipher
|
49
48
|
plaintext = content + type + "\x00" * @length_of_padding
|
50
49
|
cipher.auth_data = additional_data(plaintext.length)
|
51
50
|
encrypted_data = cipher.update(plaintext) + cipher.final
|
@@ -66,8 +65,7 @@ module TTTLS13
|
|
66
65
|
# @return [String]
|
67
66
|
# @return [TTTLS13::Message::ContentType]
|
68
67
|
def decrypt(encrypted_record, auth_data)
|
69
|
-
|
70
|
-
decipher = @cipher.decrypt
|
68
|
+
decipher = reset_decipher
|
71
69
|
auth_tag = encrypted_record[-@auth_tag_len..-1]
|
72
70
|
decipher.auth_tag = auth_tag
|
73
71
|
decipher.auth_data = auth_data # record header of TLSCiphertext
|
@@ -105,11 +103,26 @@ module TTTLS13
|
|
105
103
|
+ ciphertext_len.to_uint16
|
106
104
|
end
|
107
105
|
|
106
|
+
# @return [OpenSSL::Cipher]
|
108
107
|
def reset_cipher
|
109
|
-
@cipher.
|
110
|
-
|
108
|
+
cipher = @cipher.encrypt
|
109
|
+
cipher.reset
|
110
|
+
cipher.key = @write_key
|
111
111
|
iv_len = CipherSuite.iv_len(@cipher_suite)
|
112
|
-
|
112
|
+
cipher.iv = @sequence_number.xor(@write_iv, iv_len)
|
113
|
+
|
114
|
+
cipher
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return [OpenSSL::Cipher]
|
118
|
+
def reset_decipher
|
119
|
+
decipher = @cipher.decrypt
|
120
|
+
decipher.reset
|
121
|
+
decipher.key = @write_key
|
122
|
+
iv_len = CipherSuite.iv_len(@cipher_suite)
|
123
|
+
decipher.iv = @sequence_number.xor(@write_iv, iv_len)
|
124
|
+
|
125
|
+
decipher
|
113
126
|
end
|
114
127
|
|
115
128
|
# @param clear [String]
|
data/lib/tttls1.3/cryptograph.rb
CHANGED
@@ -8,7 +8,7 @@ module TTTLS13
|
|
8
8
|
FATAL = "\x02"
|
9
9
|
end
|
10
10
|
|
11
|
-
# rubocop: disable Layout/
|
11
|
+
# rubocop: disable Layout/HashAlignment
|
12
12
|
ALERT_DESCRIPTION = {
|
13
13
|
close_notify: "\x00",
|
14
14
|
unexpected_message: "\x0a",
|
@@ -38,7 +38,7 @@ module TTTLS13
|
|
38
38
|
certificate_required: "\x74",
|
39
39
|
no_application_protocol: "\x78"
|
40
40
|
}.freeze
|
41
|
-
# rubocop: enable Layout/
|
41
|
+
# rubocop: enable Layout/HashAlignment
|
42
42
|
|
43
43
|
class Alert
|
44
44
|
attr_reader :level
|
@@ -18,6 +18,7 @@ module TTTLS13
|
|
18
18
|
ExtensionType::CLIENT_CERTIFICATE_TYPE,
|
19
19
|
ExtensionType::SERVER_CERTIFICATE_TYPE,
|
20
20
|
ExtensionType::PADDING,
|
21
|
+
ExtensionType::COMPRESS_CERTIFICATE,
|
21
22
|
ExtensionType::RECORD_SIZE_LIMIT,
|
22
23
|
ExtensionType::PWD_PROTECT,
|
23
24
|
ExtensionType::PWD_CLEAR,
|