tttls1.3 0.2.10 → 0.2.15
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 +6 -3
- data/Gemfile +3 -4
- data/README.md +5 -1
- data/Rakefile +66 -7
- data/example/helper.rb +3 -3
- data/example/https_client.rb +1 -1
- data/example/https_client_using_0rtt.rb +3 -3
- 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 +31 -0
- data/example/https_client_using_ticket.rb +2 -2
- data/example/https_server.rb +3 -2
- data/interop/client_spec.rb +6 -6
- data/interop/server_spec.rb +6 -6
- data/lib/tttls1.3.rb +1 -0
- data/lib/tttls1.3/client.rb +97 -12
- data/lib/tttls1.3/connection.rb +44 -11
- data/lib/tttls1.3/cryptograph.rb +1 -1
- data/lib/tttls1.3/cryptograph/aead.rb +20 -7
- data/lib/tttls1.3/message.rb +1 -1
- data/lib/tttls1.3/message/alert.rb +2 -2
- data/lib/tttls1.3/message/extension/signature_algorithms.rb +13 -3
- data/lib/tttls1.3/message/extension/signature_algorithms_cert.rb +5 -4
- data/lib/tttls1.3/message/extension/status_request.rb +73 -17
- data/lib/tttls1.3/message/extensions.rb +33 -11
- data/lib/tttls1.3/server.rb +40 -13
- data/lib/tttls1.3/utils.rb +15 -0
- data/lib/tttls1.3/version.rb +1 -1
- 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/fixtures/rsa_rsa_ocsp.crt +18 -0
- data/spec/fixtures/rsa_rsa_ocsp.key +27 -0
- data/spec/server_hello_spec.rb +1 -1
- data/spec/signature_algorithms_cert_spec.rb +4 -0
- data/spec/signature_algorithms_spec.rb +4 -0
- data/spec/spec_helper.rb +35 -1
- data/spec/status_request_spec.rb +77 -10
- data/tttls1.3.gemspec +0 -1
- metadata +12 -7
- data/.github/workflows/main.yml +0 -25
data/lib/tttls1.3.rb
CHANGED
data/lib/tttls1.3/client.rb
CHANGED
@@ -59,6 +59,8 @@ module TTTLS13
|
|
59
59
|
ticket_age_add: nil,
|
60
60
|
ticket_timestamp: nil,
|
61
61
|
record_size_limit: nil,
|
62
|
+
check_certificate_status: false,
|
63
|
+
process_certificate_status: nil,
|
62
64
|
compatibility_mode: true,
|
63
65
|
loglevel: Logger::WARN
|
64
66
|
}.freeze
|
@@ -300,7 +302,8 @@ module TTTLS13
|
|
300
302
|
message = recv_message(receivable_ccs: true, cipher: hs_rcipher)
|
301
303
|
if message.msg_type == Message::HandshakeType::CERTIFICATE
|
302
304
|
ct = transcript[CT] = message
|
303
|
-
|
305
|
+
alert = check_invalid_certificate(ct, transcript[CH])
|
306
|
+
terminate(alert) unless alert.nil?
|
304
307
|
|
305
308
|
@state = ClientState::WAIT_CV
|
306
309
|
elsif message.msg_type == Message::HandshakeType::CERTIFICATE_REQUEST
|
@@ -314,7 +317,8 @@ module TTTLS13
|
|
314
317
|
logger.debug('ClientState::WAIT_CERT')
|
315
318
|
|
316
319
|
ct = transcript[CT] = recv_certificate(hs_rcipher)
|
317
|
-
|
320
|
+
alert = check_invalid_certificate(ct, transcript[CH])
|
321
|
+
terminate(alert) unless alert.nil?
|
318
322
|
|
319
323
|
@state = ClientState::WAIT_CV
|
320
324
|
when ClientState::WAIT_CV
|
@@ -392,6 +396,53 @@ module TTTLS13
|
|
392
396
|
@succeed_early_data
|
393
397
|
end
|
394
398
|
|
399
|
+
# @param res [OpenSSL::OCSP::Response]
|
400
|
+
# @param cert [OpenSSL::X509::Certificate]
|
401
|
+
# @param chain [Array of OpenSSL::X509::Certificate, nil]
|
402
|
+
#
|
403
|
+
# @return [Boolean]
|
404
|
+
#
|
405
|
+
# @example
|
406
|
+
# m = Client.method(:softfail_check_certificate_status)
|
407
|
+
# Client.new(
|
408
|
+
# socket,
|
409
|
+
# hostname,
|
410
|
+
# check_certificate_status: true,
|
411
|
+
# process_certificate_status: m
|
412
|
+
# )
|
413
|
+
def self.softfail_check_certificate_status(res, cert, chain)
|
414
|
+
ocsp_response = res
|
415
|
+
cid = OpenSSL::OCSP::CertificateId.new(cert, chain.first)
|
416
|
+
|
417
|
+
# When NOT received OCSPResponse in TLS handshake, this method will
|
418
|
+
# send OCSPRequest. If ocsp_uri is NOT presented in Certificate, return
|
419
|
+
# true. Also, if it sends OCSPRequest and does NOT receive a HTTPresponse
|
420
|
+
# within 2 seconds, return true.
|
421
|
+
if ocsp_response.nil?
|
422
|
+
uri = cert.ocsp_uris&.find { |u| URI::DEFAULT_PARSER.make_regexp =~ u }
|
423
|
+
return true if uri.nil?
|
424
|
+
|
425
|
+
begin
|
426
|
+
# send OCSP::Request
|
427
|
+
ocsp_request = gen_ocsp_request(cid)
|
428
|
+
Timeout.timeout(2) do
|
429
|
+
ocsp_response = send_ocsp_request(ocsp_request, uri)
|
430
|
+
end
|
431
|
+
|
432
|
+
# check nonce of OCSP::Response
|
433
|
+
check_nonce = ocsp_request.check_nonce(ocsp_response.basic)
|
434
|
+
return true unless [-1, 1].include?(check_nonce)
|
435
|
+
rescue StandardError
|
436
|
+
return true
|
437
|
+
end
|
438
|
+
end
|
439
|
+
return true \
|
440
|
+
if ocsp_response.status != OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL
|
441
|
+
|
442
|
+
status = ocsp_response.basic.status.find { |s| s.first.cmp(cid) }
|
443
|
+
status[1] != OpenSSL::OCSP::V_CERTSTATUS_REVOKED
|
444
|
+
end
|
445
|
+
|
395
446
|
private
|
396
447
|
|
397
448
|
# @return [Boolean]
|
@@ -423,6 +474,9 @@ module TTTLS13
|
|
423
474
|
rsl = @settings[:record_size_limit]
|
424
475
|
return false if !rsl.nil? && (rsl < 64 || rsl > 2**14 + 1)
|
425
476
|
|
477
|
+
return false if @settings[:check_certificate_status] &&
|
478
|
+
@settings[:process_certificate_status].nil?
|
479
|
+
|
426
480
|
true
|
427
481
|
end
|
428
482
|
# rubocop: enable Metrics/AbcSize
|
@@ -471,9 +525,10 @@ module TTTLS13
|
|
471
525
|
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
472
526
|
# rubocop: disable Metrics/AbcSize
|
473
527
|
# rubocop: disable Metrics/CyclomaticComplexity
|
528
|
+
# rubocop: disable Metrics/MethodLength
|
474
529
|
# rubocop: disable Metrics/PerceivedComplexity
|
475
530
|
def gen_ch_extensions
|
476
|
-
exs =
|
531
|
+
exs = Message::Extensions.new
|
477
532
|
# server_name
|
478
533
|
exs << Message::Extension::ServerName.new(@hostname)
|
479
534
|
|
@@ -519,10 +574,15 @@ module TTTLS13
|
|
519
574
|
exs << Message::Extension::Alpn.new(@settings[:alpn].reject(&:empty?)) \
|
520
575
|
if !@settings[:alpn].nil? && !@settings[:alpn].empty?
|
521
576
|
|
522
|
-
|
577
|
+
# status_request
|
578
|
+
exs << Message::Extension::OCSPStatusRequest.new \
|
579
|
+
if @settings[:check_certificate_status]
|
580
|
+
|
581
|
+
[exs, priv_keys]
|
523
582
|
end
|
524
583
|
# rubocop: enable Metrics/AbcSize
|
525
584
|
# rubocop: enable Metrics/CyclomaticComplexity
|
585
|
+
# rubocop: enable Metrics/MethodLength
|
526
586
|
# rubocop: enable Metrics/PerceivedComplexity
|
527
587
|
|
528
588
|
# @param extensions [TTTLS13::Message::Extensions]
|
@@ -611,7 +671,7 @@ module TTTLS13
|
|
611
671
|
# @return [TTTLS13::Message::Extensions]
|
612
672
|
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
613
673
|
def gen_newch_extensions(ch1, hrr)
|
614
|
-
exs =
|
674
|
+
exs = Message::Extensions.new
|
615
675
|
# key_share
|
616
676
|
if hrr.extensions.include?(Message::ExtensionType::KEY_SHARE)
|
617
677
|
group = hrr.extensions[Message::ExtensionType::KEY_SHARE]
|
@@ -633,7 +693,7 @@ module TTTLS13
|
|
633
693
|
if hrr.extensions.include?(Message::ExtensionType::COOKIE)
|
634
694
|
|
635
695
|
# early_data
|
636
|
-
new_exs = ch1.extensions.merge(
|
696
|
+
new_exs = ch1.extensions.merge(exs)
|
637
697
|
new_exs.delete(Message::ExtensionType::EARLY_DATA)
|
638
698
|
|
639
699
|
[new_exs, priv_keys]
|
@@ -753,16 +813,32 @@ module TTTLS13
|
|
753
813
|
|
754
814
|
# @param ct [TTTLS13::Message::Certificate]
|
755
815
|
# @param ch [TTTLS13::Message::ClientHello]
|
756
|
-
|
757
|
-
|
816
|
+
#
|
817
|
+
# @return [Symbol, nil] return key of ALERT_DESCRIPTION, if invalid
|
818
|
+
def check_invalid_certificate(ct, ch)
|
819
|
+
return :illegal_parameter unless ct.appearable_extensions?
|
758
820
|
|
759
|
-
|
821
|
+
return :unsupported_extension \
|
760
822
|
unless ct.certificate_list.map(&:extensions)
|
761
823
|
.all? { |e| (e.keys - ch.extensions.keys).empty? }
|
762
824
|
|
763
|
-
|
764
|
-
|
765
|
-
|
825
|
+
return :certificate_unknown unless trusted_certificate?(
|
826
|
+
ct.certificate_list,
|
827
|
+
@settings[:ca_file],
|
828
|
+
@hostname
|
829
|
+
)
|
830
|
+
|
831
|
+
if @settings[:check_certificate_status]
|
832
|
+
ee = ct.certificate_list.first
|
833
|
+
ocsp_response = ee.extensions[Message::ExtensionType::STATUS_REQUEST]
|
834
|
+
&.ocsp_response
|
835
|
+
cert = ee.cert_data
|
836
|
+
chain = ct.certificate_list[1..]&.map(&:cert_data)
|
837
|
+
return :bad_certificate_status_response \
|
838
|
+
unless satisfactory_certificate_status?(ocsp_response, cert, chain)
|
839
|
+
end
|
840
|
+
|
841
|
+
nil
|
766
842
|
end
|
767
843
|
|
768
844
|
# @param ct [TTTLS13::Message::Certificate]
|
@@ -784,6 +860,15 @@ module TTTLS13
|
|
784
860
|
)
|
785
861
|
end
|
786
862
|
|
863
|
+
# @param ocsp_response [OpenSSL::OCSP::Response]
|
864
|
+
# @param cert [OpenSSL::X509::Certificate]
|
865
|
+
# @param chain [Array of OpenSSL::X509::Certificate, nil]
|
866
|
+
#
|
867
|
+
# @return [Boolean]
|
868
|
+
def satisfactory_certificate_status?(ocsp_response, cert, chain)
|
869
|
+
@settings[:process_certificate_status]&.call(ocsp_response, cert, chain)
|
870
|
+
end
|
871
|
+
|
787
872
|
# @param nst [TTTLS13::Message::NewSessionTicket]
|
788
873
|
#
|
789
874
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
data/lib/tttls1.3/connection.rb
CHANGED
@@ -273,7 +273,7 @@ module TTTLS13
|
|
273
273
|
|
274
274
|
# Received a protected ccs, peer MUST abort the handshake.
|
275
275
|
if record.type == Message::ContentType::APPLICATION_DATA &&
|
276
|
-
record.messages.
|
276
|
+
record.messages.any? { |m| m.is_a?(Message::ChangeCipherSpec) }
|
277
277
|
terminate(:unexpected_message)
|
278
278
|
end
|
279
279
|
|
@@ -474,8 +474,10 @@ module TTTLS13
|
|
474
474
|
#
|
475
475
|
# @return [Boolean]
|
476
476
|
def trusted_certificate?(certificate_list, ca_file = nil, hostname = nil)
|
477
|
-
|
478
|
-
|
477
|
+
chain = certificate_list.map(&:cert_data).map do |c|
|
478
|
+
OpenSSL::X509::Certificate.new(c)
|
479
|
+
end
|
480
|
+
cert = chain.shift
|
479
481
|
|
480
482
|
# not support CN matching, only support SAN matching
|
481
483
|
return false if !hostname.nil? && !matching_san?(cert, hostname)
|
@@ -483,9 +485,6 @@ module TTTLS13
|
|
483
485
|
store = OpenSSL::X509::Store.new
|
484
486
|
store.set_default_paths
|
485
487
|
store.add_file(ca_file) unless ca_file.nil?
|
486
|
-
chain = certificate_list[1..].map(&:cert_data).map do |c|
|
487
|
-
OpenSSL::X509::Certificate.new(c)
|
488
|
-
end
|
489
488
|
# TODO: parse authorityInfoAccess::CA Issuers
|
490
489
|
ctx = OpenSSL::X509::StoreContext.new(store, cert, chain)
|
491
490
|
now = Time.now
|
@@ -515,22 +514,22 @@ module TTTLS13
|
|
515
514
|
def do_select_signature_algorithms(signature_algorithms, crt)
|
516
515
|
spki = OpenSSL::Netscape::SPKI.new
|
517
516
|
spki.public_key = crt.public_key
|
518
|
-
|
519
|
-
|
517
|
+
pka = OpenSSL::ASN1.decode(spki.to_der)
|
518
|
+
.value.first.value.first.value.first.value.first.value
|
520
519
|
signature_algorithms.select do |sa|
|
521
520
|
case sa
|
522
521
|
when SignatureScheme::ECDSA_SECP256R1_SHA256,
|
523
522
|
SignatureScheme::ECDSA_SECP384R1_SHA384,
|
524
523
|
SignatureScheme::ECDSA_SECP521R1_SHA512
|
525
|
-
|
524
|
+
pka == 'id-ecPublicKey'
|
526
525
|
when SignatureScheme::RSA_PSS_PSS_SHA256,
|
527
526
|
SignatureScheme::RSA_PSS_PSS_SHA384,
|
528
527
|
SignatureScheme::RSA_PSS_PSS_SHA512
|
529
|
-
|
528
|
+
pka == 'rsassaPss'
|
530
529
|
when SignatureScheme::RSA_PSS_RSAE_SHA256,
|
531
530
|
SignatureScheme::RSA_PSS_RSAE_SHA384,
|
532
531
|
SignatureScheme::RSA_PSS_RSAE_SHA512
|
533
|
-
|
532
|
+
pka == 'rsaEncryption'
|
534
533
|
else
|
535
534
|
# RSASSA-PKCS1-v1_5 algorithms refer solely to signatures which appear
|
536
535
|
# in certificates and are not defined for use in signed TLS handshake
|
@@ -539,6 +538,40 @@ module TTTLS13
|
|
539
538
|
end
|
540
539
|
end
|
541
540
|
end
|
541
|
+
|
542
|
+
class << self
|
543
|
+
# @param cid [OpenSSL::OCSP::CertificateId]
|
544
|
+
#
|
545
|
+
# @return [OpenSSL::OCSP::Request]
|
546
|
+
def gen_ocsp_request(cid)
|
547
|
+
ocsp_request = OpenSSL::OCSP::Request.new
|
548
|
+
ocsp_request.add_certid(cid)
|
549
|
+
ocsp_request.add_nonce
|
550
|
+
ocsp_request
|
551
|
+
end
|
552
|
+
|
553
|
+
# @param ocsp_request [OpenSSL::OCSP::Request]
|
554
|
+
# @param uri_string [String]
|
555
|
+
#
|
556
|
+
# @raise [Net::OpenTimeout, OpenSSL::OCSP::OCSPError, URI::$Exception]
|
557
|
+
#
|
558
|
+
# @return [OpenSSL::OCSP::Response, n
|
559
|
+
def send_ocsp_request(ocsp_request, uri_string)
|
560
|
+
# send HTTP POST
|
561
|
+
uri = URI.parse(uri_string)
|
562
|
+
path = uri.path
|
563
|
+
path = '/' if path.nil? || path.empty?
|
564
|
+
http_response = Net::HTTP.start(uri.host, uri.port) do |http|
|
565
|
+
http.post(
|
566
|
+
path,
|
567
|
+
ocsp_request.to_der,
|
568
|
+
'content-type' => 'application/ocsp-request'
|
569
|
+
)
|
570
|
+
end
|
571
|
+
|
572
|
+
OpenSSL::OCSP::Response.new(http_response.body)
|
573
|
+
end
|
574
|
+
end
|
542
575
|
end
|
543
576
|
# rubocop: enable Metrics/ClassLength
|
544
577
|
end
|
data/lib/tttls1.3/cryptograph.rb
CHANGED
@@ -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/message.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
|
@@ -44,8 +44,8 @@ module TTTLS13
|
|
44
44
|
#
|
45
45
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
46
46
|
#
|
47
|
-
# @return [
|
48
|
-
def self.
|
47
|
+
# @return [Array of SignatureScheme]
|
48
|
+
def self.deserialize_supported_signature_algorithms(binary)
|
49
49
|
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
50
50
|
|
51
51
|
return nil if binary.length < 2
|
@@ -61,7 +61,17 @@ module TTTLS13
|
|
61
61
|
end
|
62
62
|
return nil unless ssa_len + 2 == binary.length
|
63
63
|
|
64
|
-
|
64
|
+
supported_signature_algorithms
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param binary [String]
|
68
|
+
#
|
69
|
+
# @return [TTTLS13::Message::Extensions::SignatureAlgorithms, nil]
|
70
|
+
def self.deserialize(binary)
|
71
|
+
ssa = deserialize_supported_signature_algorithms(binary)
|
72
|
+
return nil if ssa.nil?
|
73
|
+
|
74
|
+
SignatureAlgorithms.new(ssa)
|
65
75
|
end
|
66
76
|
end
|
67
77
|
end
|
@@ -13,11 +13,12 @@ module TTTLS13
|
|
13
13
|
|
14
14
|
# @param binary [String]
|
15
15
|
#
|
16
|
-
# @return [TTTLS13::Message::Extensions::SignatureAlgorithmsCert]
|
16
|
+
# @return [TTTLS13::Message::Extensions::SignatureAlgorithmsCert, nil]
|
17
17
|
def self.deserialize(binary)
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
ssa = deserialize_supported_signature_algorithms(binary)
|
19
|
+
return nil if ssa.nil?
|
20
|
+
|
21
|
+
SignatureAlgorithmsCert.new(ssa)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
@@ -9,23 +9,20 @@ module TTTLS13
|
|
9
9
|
OCSP = "\x01"
|
10
10
|
end
|
11
11
|
|
12
|
-
class
|
12
|
+
class OCSPStatusRequest
|
13
13
|
attr_reader :extension_type
|
14
14
|
attr_reader :responder_id_list
|
15
15
|
attr_reader :request_extensions
|
16
16
|
|
17
|
-
# @param responder_id_list [Array of
|
18
|
-
# @param request_extensions [
|
17
|
+
# @param responder_id_list [Array of OpenSSL::ASN1::ASN1Data]
|
18
|
+
# @param request_extensions [Array of OpenSSL::ASN1::ASN1Data]
|
19
19
|
#
|
20
20
|
# @example
|
21
|
-
#
|
22
|
-
|
23
|
-
# request_extensions: []
|
24
|
-
# )
|
25
|
-
def initialize(responder_id_list: [], request_extensions: '')
|
21
|
+
# OCSPStatusRequest.new
|
22
|
+
def initialize(responder_id_list: [], request_extensions: [])
|
26
23
|
@extension_type = ExtensionType::STATUS_REQUEST
|
27
24
|
@responder_id_list = responder_id_list || []
|
28
|
-
@request_extensions = request_extensions ||
|
25
|
+
@request_extensions = request_extensions || []
|
29
26
|
end
|
30
27
|
|
31
28
|
# @return [String]
|
@@ -34,9 +31,9 @@ module TTTLS13
|
|
34
31
|
binary += CertificateStatusType::OCSP
|
35
32
|
binary += @responder_id_list.length.to_uint16
|
36
33
|
binary += @responder_id_list.map do |id|
|
37
|
-
id.
|
34
|
+
id.to_der.prefix_uint16_length
|
38
35
|
end.join
|
39
|
-
binary += @request_extensions.prefix_uint16_length
|
36
|
+
binary += @request_extensions.map(&:to_der).join.prefix_uint16_length
|
40
37
|
|
41
38
|
@extension_type + binary.prefix_uint16_length
|
42
39
|
end
|
@@ -45,8 +42,9 @@ module TTTLS13
|
|
45
42
|
#
|
46
43
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
47
44
|
#
|
48
|
-
# @return [TTTLS13::Message::Extension::
|
45
|
+
# @return [TTTLS13::Message::Extension::OCSPStatusRequest, nil]
|
49
46
|
# rubocop: disable Metrics/CyclomaticComplexity
|
47
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
50
48
|
def self.deserialize(binary)
|
51
49
|
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
52
50
|
return nil if binary.length < 5 ||
|
@@ -64,14 +62,20 @@ module TTTLS13
|
|
64
62
|
|
65
63
|
re_len = Convert.bin2i(binary.slice(i, 2))
|
66
64
|
i += 2
|
67
|
-
|
65
|
+
exs_bin = binary.slice(i, re_len)
|
66
|
+
begin
|
67
|
+
request_extensions = OpenSSL::ASN1.decode_all(exs_bin)
|
68
|
+
rescue OpenSSL::ASN1::ASN1Error
|
69
|
+
return nil
|
70
|
+
end
|
68
71
|
i += re_len
|
69
72
|
return nil unless i == binary.length
|
70
73
|
|
71
|
-
|
72
|
-
|
74
|
+
OCSPStatusRequest.new(responder_id_list: responder_id_list,
|
75
|
+
request_extensions: request_extensions)
|
73
76
|
end
|
74
77
|
# rubocop: enable Metrics/CyclomaticComplexity
|
78
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
75
79
|
|
76
80
|
class << self
|
77
81
|
private
|
@@ -80,7 +84,7 @@ module TTTLS13
|
|
80
84
|
#
|
81
85
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
82
86
|
#
|
83
|
-
# @return [Array of
|
87
|
+
# @return [Array of ASN1Data, nil] received unparsable binary, nil
|
84
88
|
def deserialize_request_ids(binary)
|
85
89
|
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
86
90
|
|
@@ -92,7 +96,11 @@ module TTTLS13
|
|
92
96
|
id_len = Convert.bin2i(binary.slice(i, 2))
|
93
97
|
i += 2
|
94
98
|
id = binary.slice(i, id_len)
|
95
|
-
|
99
|
+
begin
|
100
|
+
request_ids += OpenSSL::ASN1.decode(id)
|
101
|
+
rescue OpenSSL::ASN1::ASN1Error
|
102
|
+
return nil
|
103
|
+
end
|
96
104
|
i += id_len
|
97
105
|
end
|
98
106
|
return nil if i != binary.length
|
@@ -101,6 +109,54 @@ module TTTLS13
|
|
101
109
|
end
|
102
110
|
end
|
103
111
|
end
|
112
|
+
|
113
|
+
class OCSPResponse
|
114
|
+
attr_reader :extension_type
|
115
|
+
attr_reader :ocsp_response
|
116
|
+
|
117
|
+
# @param ocsp_response [OpenSSL::OCSP::Response]
|
118
|
+
#
|
119
|
+
# @example
|
120
|
+
# OCSPResponse.new(
|
121
|
+
# OpenSSL::OCSP::Response.create(status, basic_resp)
|
122
|
+
# )
|
123
|
+
def initialize(ocsp_response)
|
124
|
+
@extension_type = ExtensionType::STATUS_REQUEST
|
125
|
+
@ocsp_response = ocsp_response
|
126
|
+
end
|
127
|
+
|
128
|
+
# @return [String]
|
129
|
+
def serialize
|
130
|
+
binary = ''
|
131
|
+
binary += CertificateStatusType::OCSP
|
132
|
+
binary += @ocsp_response.to_der.prefix_uint24_length
|
133
|
+
|
134
|
+
@extension_type + binary.prefix_uint16_length
|
135
|
+
end
|
136
|
+
|
137
|
+
# @param binary [String]
|
138
|
+
#
|
139
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
140
|
+
#
|
141
|
+
# @return [TTTLS13::Message::Extension::OCSPResponse, nil]
|
142
|
+
def self.deserialize(binary)
|
143
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
144
|
+
return nil if binary.length < 4 ||
|
145
|
+
binary[0] != CertificateStatusType::OCSP
|
146
|
+
|
147
|
+
res_len = Convert.bin2i(binary.slice(1, 3))
|
148
|
+
res = binary.slice(4, res_len)
|
149
|
+
ocsp_response = nil
|
150
|
+
begin
|
151
|
+
ocsp_response = OpenSSL::OCSP::Response.new(res)
|
152
|
+
rescue OpenSSL::OCSP::OCSPError
|
153
|
+
return nil
|
154
|
+
end
|
155
|
+
return nil if 4 + res_len != binary.length
|
156
|
+
|
157
|
+
OCSPResponse.new(ocsp_response)
|
158
|
+
end
|
159
|
+
end
|
104
160
|
end
|
105
161
|
end
|
106
162
|
end
|