tttls1.3 0.2.12 → 0.2.16
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/.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,
|