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.
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