tttls1.3 0.2.10 → 0.2.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +32 -0
  3. data/.rubocop.yml +6 -3
  4. data/Gemfile +3 -4
  5. data/README.md +5 -1
  6. data/Rakefile +66 -7
  7. data/example/helper.rb +3 -3
  8. data/example/https_client.rb +1 -1
  9. data/example/https_client_using_0rtt.rb +3 -3
  10. data/example/https_client_using_hrr.rb +1 -1
  11. data/example/https_client_using_hrr_and_ticket.rb +2 -2
  12. data/example/https_client_using_status_request.rb +31 -0
  13. data/example/https_client_using_ticket.rb +2 -2
  14. data/example/https_server.rb +3 -2
  15. data/interop/client_spec.rb +6 -6
  16. data/interop/server_spec.rb +6 -6
  17. data/lib/tttls1.3.rb +1 -0
  18. data/lib/tttls1.3/client.rb +97 -12
  19. data/lib/tttls1.3/connection.rb +44 -11
  20. data/lib/tttls1.3/cryptograph.rb +1 -1
  21. data/lib/tttls1.3/cryptograph/aead.rb +20 -7
  22. data/lib/tttls1.3/message.rb +1 -1
  23. data/lib/tttls1.3/message/alert.rb +2 -2
  24. data/lib/tttls1.3/message/extension/signature_algorithms.rb +13 -3
  25. data/lib/tttls1.3/message/extension/signature_algorithms_cert.rb +5 -4
  26. data/lib/tttls1.3/message/extension/status_request.rb +73 -17
  27. data/lib/tttls1.3/message/extensions.rb +33 -11
  28. data/lib/tttls1.3/server.rb +40 -13
  29. data/lib/tttls1.3/utils.rb +15 -0
  30. data/lib/tttls1.3/version.rb +1 -1
  31. data/spec/extensions_spec.rb +16 -0
  32. data/spec/fixtures/rsa_rsa.crt +15 -15
  33. data/spec/fixtures/rsa_rsa.key +25 -25
  34. data/spec/fixtures/rsa_rsa_ocsp.crt +18 -0
  35. data/spec/fixtures/rsa_rsa_ocsp.key +27 -0
  36. data/spec/server_hello_spec.rb +1 -1
  37. data/spec/signature_algorithms_cert_spec.rb +4 -0
  38. data/spec/signature_algorithms_spec.rb +4 -0
  39. data/spec/spec_helper.rb +35 -1
  40. data/spec/status_request_spec.rb +77 -10
  41. data/tttls1.3.gemspec +0 -1
  42. metadata +12 -7
  43. data/.github/workflows/main.yml +0 -25
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openssl'
4
+ require 'net/http'
4
5
  require 'pp'
5
6
  require 'logger'
6
7
 
@@ -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
- terminate_invalid_certificate(ct, transcript[CH])
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
- terminate_invalid_certificate(ct, transcript[CH])
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
- [Message::Extensions.new(exs), priv_keys]
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(Message::Extensions.new(exs))
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
- def terminate_invalid_certificate(ct, ch)
757
- terminate(:illegal_parameter) unless ct.appearable_extensions?
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
- terminate(:unsupported_extension) \
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
- terminate(:certificate_unknown) \
764
- unless trusted_certificate?(ct.certificate_list,
765
- @settings[:ca_file], @hostname)
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]
@@ -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.first.is_a?(Message::ChangeCipherSpec)
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
- cert_bin = certificate_list.first.cert_data
478
- cert = OpenSSL::X509::Certificate.new(cert_bin)
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
- oid_str = spki.to_text.split("\n")
519
- .find { |l| l.include?('Public Key Algorithm:') }
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
- oid_str.include?('id-ecPublicKey')
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
- oid_str.include?('rsassaPss')
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
- oid_str.include?('rsaEncryption')
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
@@ -1,3 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Dir[File.dirname(__FILE__) + '/cryptograph/*.rb'].each { |f| require f }
3
+ Dir[File.dirname(__FILE__) + '/cryptograph/*.rb'].sort.each { |f| require f }
@@ -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
- reset_cipher
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.reset
110
- @cipher.key = @write_key
108
+ cipher = @cipher.encrypt
109
+ cipher.reset
110
+ cipher.key = @write_key
111
111
  iv_len = CipherSuite.iv_len(@cipher_suite)
112
- @cipher.iv = @sequence_number.xor(@write_iv, iv_len)
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]
@@ -78,4 +78,4 @@ module TTTLS13
78
78
  end
79
79
  end
80
80
 
81
- Dir[File.dirname(__FILE__) + '/message/*.rb'].each { |f| require f }
81
+ Dir[File.dirname(__FILE__) + '/message/*.rb'].sort.each { |f| require f }
@@ -8,7 +8,7 @@ module TTTLS13
8
8
  FATAL = "\x02"
9
9
  end
10
10
 
11
- # rubocop: disable Layout/AlignHash
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/AlignHash
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 [TTTLS13::Message::Extensions::SignatureAlgorithms, nil]
48
- def self.deserialize(binary)
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
- SignatureAlgorithms.new(supported_signature_algorithms)
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
- extension = SignatureAlgorithms.deserialize(binary)
19
- extension.extension_type = ExtensionType::SIGNATURE_ALGORITHMS_CERT
20
- extension
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 StatusRequest
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 String]
18
- # @param request_extensions [String]
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
- # StatusRequest.new(
22
- # responder_id_list: [],
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.length.to_uint16 + 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::StatusRequest, nil]
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
- request_extensions = binary.slice(i, re_len)
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
- StatusRequest.new(responder_id_list: responder_id_list,
72
- request_extensions: request_extensions)
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 String, nil] received unparsable binary, nil
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
- request_ids += id
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