tttls1.3 0.1.0

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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +16 -0
  5. data/.travis.yml +8 -0
  6. data/Gemfile +13 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +52 -0
  9. data/Rakefile +133 -0
  10. data/example/helper.rb +17 -0
  11. data/example/https_client.rb +32 -0
  12. data/example/https_client_using_0rtt.rb +64 -0
  13. data/example/https_client_using_hrr.rb +35 -0
  14. data/example/https_client_using_ticket.rb +56 -0
  15. data/lib/tttls1.3/cipher_suites.rb +102 -0
  16. data/lib/tttls1.3/client.rb +745 -0
  17. data/lib/tttls1.3/connection.rb +380 -0
  18. data/lib/tttls1.3/cryptograph/aead.rb +118 -0
  19. data/lib/tttls1.3/cryptograph/passer.rb +22 -0
  20. data/lib/tttls1.3/cryptograph.rb +3 -0
  21. data/lib/tttls1.3/error.rb +22 -0
  22. data/lib/tttls1.3/key_schedule.rb +242 -0
  23. data/lib/tttls1.3/message/alert.rb +86 -0
  24. data/lib/tttls1.3/message/application_data.rb +27 -0
  25. data/lib/tttls1.3/message/certificate.rb +121 -0
  26. data/lib/tttls1.3/message/certificate_verify.rb +59 -0
  27. data/lib/tttls1.3/message/change_cipher_spec.rb +26 -0
  28. data/lib/tttls1.3/message/client_hello.rb +100 -0
  29. data/lib/tttls1.3/message/encrypted_extensions.rb +65 -0
  30. data/lib/tttls1.3/message/end_of_early_data.rb +29 -0
  31. data/lib/tttls1.3/message/extension/alpn.rb +70 -0
  32. data/lib/tttls1.3/message/extension/cookie.rb +47 -0
  33. data/lib/tttls1.3/message/extension/early_data_indication.rb +58 -0
  34. data/lib/tttls1.3/message/extension/key_share.rb +236 -0
  35. data/lib/tttls1.3/message/extension/pre_shared_key.rb +205 -0
  36. data/lib/tttls1.3/message/extension/psk_key_exchange_modes.rb +54 -0
  37. data/lib/tttls1.3/message/extension/record_size_limit.rb +46 -0
  38. data/lib/tttls1.3/message/extension/server_name.rb +91 -0
  39. data/lib/tttls1.3/message/extension/signature_algorithms.rb +69 -0
  40. data/lib/tttls1.3/message/extension/signature_algorithms_cert.rb +25 -0
  41. data/lib/tttls1.3/message/extension/status_request.rb +106 -0
  42. data/lib/tttls1.3/message/extension/supported_groups.rb +145 -0
  43. data/lib/tttls1.3/message/extension/supported_versions.rb +98 -0
  44. data/lib/tttls1.3/message/extension/unknown_extension.rb +38 -0
  45. data/lib/tttls1.3/message/extensions.rb +173 -0
  46. data/lib/tttls1.3/message/finished.rb +44 -0
  47. data/lib/tttls1.3/message/new_session_ticket.rb +89 -0
  48. data/lib/tttls1.3/message/record.rb +232 -0
  49. data/lib/tttls1.3/message/server_hello.rb +116 -0
  50. data/lib/tttls1.3/message.rb +48 -0
  51. data/lib/tttls1.3/sequence_number.rb +31 -0
  52. data/lib/tttls1.3/signature_scheme.rb +31 -0
  53. data/lib/tttls1.3/transcript.rb +69 -0
  54. data/lib/tttls1.3/utils.rb +91 -0
  55. data/lib/tttls1.3/version.rb +5 -0
  56. data/lib/tttls1.3.rb +16 -0
  57. data/spec/aead_spec.rb +95 -0
  58. data/spec/alert_spec.rb +54 -0
  59. data/spec/alpn_spec.rb +55 -0
  60. data/spec/application_data_spec.rb +26 -0
  61. data/spec/certificate_spec.rb +55 -0
  62. data/spec/certificate_verify_spec.rb +51 -0
  63. data/spec/change_cipher_spec_spec.rb +26 -0
  64. data/spec/cipher_suites_spec.rb +39 -0
  65. data/spec/client_hello_spec.rb +83 -0
  66. data/spec/client_spec.rb +319 -0
  67. data/spec/connection_spec.rb +114 -0
  68. data/spec/cookie_spec.rb +98 -0
  69. data/spec/early_data_indication_spec.rb +64 -0
  70. data/spec/encrypted_extensions_spec.rb +94 -0
  71. data/spec/error_spec.rb +18 -0
  72. data/spec/extensions_spec.rb +170 -0
  73. data/spec/finished_spec.rb +55 -0
  74. data/spec/key_schedule_spec.rb +198 -0
  75. data/spec/key_share_spec.rb +199 -0
  76. data/spec/new_session_ticket_spec.rb +80 -0
  77. data/spec/pre_shared_key_spec.rb +167 -0
  78. data/spec/psk_key_exchange_modes_spec.rb +45 -0
  79. data/spec/record_size_limit_spec.rb +61 -0
  80. data/spec/record_spec.rb +105 -0
  81. data/spec/server_hello_spec.rb +101 -0
  82. data/spec/server_name_spec.rb +110 -0
  83. data/spec/signature_algorithms_cert_spec.rb +73 -0
  84. data/spec/signature_algorithms_spec.rb +100 -0
  85. data/spec/spec_helper.rb +872 -0
  86. data/spec/status_request_spec.rb +73 -0
  87. data/spec/supported_groups_spec.rb +79 -0
  88. data/spec/supported_versions_spec.rb +136 -0
  89. data/spec/transcript_spec.rb +69 -0
  90. data/spec/unknown_extension_spec.rb +90 -0
  91. data/spec/utils_spec.rb +215 -0
  92. data/tttls1.3.gemspec +25 -0
  93. metadata +197 -0
@@ -0,0 +1,205 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ # NOTE:
9
+ # struct {
10
+ # select (Handshake.msg_type) {
11
+ # case client_hello: OfferedPsks;
12
+ # case server_hello: uint16 selected_identity;
13
+ # };
14
+ # } PreSharedKeyExtension;
15
+ class PreSharedKey
16
+ attr_reader :extension_type
17
+ attr_reader :msg_type
18
+ attr_reader :offered_psks
19
+ attr_reader :selected_identity
20
+
21
+ # @param msg_type [TTTLS13::Message::ContentType]
22
+ # @param offered_psks [TTTLS13::Message::Extension::OfferedPsks]
23
+ # @param selected_identity [String]
24
+ #
25
+ # @raise [TTTLS13::Error::ErrorAlerts]
26
+ def initialize(msg_type:, offered_psks: nil, selected_identity: '')
27
+ @extension_type = ExtensionType::PRE_SHARED_KEY
28
+ @msg_type = msg_type
29
+ case @msg_type
30
+ when HandshakeType::CLIENT_HELLO
31
+ @offered_psks = offered_psks
32
+ when HandshakeType::SERVER_HELLO
33
+ @selected_identity = selected_identity || ''
34
+ raise Error::ErrorAlerts, :internal_error \
35
+ unless @selected_identity.length == 2
36
+ else
37
+ raise Error::ErrorAlerts, :internal_error
38
+ end
39
+ end
40
+
41
+ # @raise [TTTLS13::Error::ErrorAlerts]
42
+ #
43
+ # @return [String]
44
+ def serialize
45
+ binary = ''
46
+ case @msg_type
47
+ when HandshakeType::CLIENT_HELLO
48
+ binary += @offered_psks.serialize
49
+ when HandshakeType::SERVER_HELLO
50
+ binary += @selected_identity
51
+ else
52
+ raise Error::ErrorAlerts, :internal_error
53
+ end
54
+
55
+ @extension_type + binary.prefix_uint16_length
56
+ end
57
+
58
+ # @param binary [String]
59
+ # @param msg_type [TTTLS13::Message::ContentType]
60
+ #
61
+ # @raise [TTTLS13::Error::ErrorAlerts]
62
+ #
63
+ # @return [TTTLS13::Message::Extensions::PreSharedKey, nil]
64
+ def self.deserialize(binary, msg_type)
65
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
66
+
67
+ case msg_type
68
+ when HandshakeType::CLIENT_HELLO
69
+ offered_psks = OfferedPsks.deserialize(binary)
70
+ return nil if offered_psks.nil?
71
+
72
+ PreSharedKey.new(msg_type: HandshakeType::CLIENT_HELLO,
73
+ offered_psks: offered_psks)
74
+ when HandshakeType::SERVER_HELLO
75
+ return nil unless binary.length == 2
76
+
77
+ selected_identity = binary
78
+ PreSharedKey.new(msg_type: HandshakeType::SERVER_HELLO,
79
+ selected_identity: selected_identity)
80
+ else
81
+ raise Error::ErrorAlerts, :internal_error
82
+ end
83
+ end
84
+ end
85
+
86
+ # NOTE:
87
+ # opaque PskBinderEntry<32..255>;
88
+ #
89
+ # struct {
90
+ # PskIdentity identities<7..2^16-1>;
91
+ # PskBinderEntry binders<33..2^16-1>;
92
+ # } OfferedPsks;
93
+ class OfferedPsks
94
+ attr_reader :identities
95
+ attr_reader :binders
96
+
97
+ # @param identities [Array of PskIdentity]
98
+ # @param binders [Array of String]
99
+ #
100
+ # @raise [TTTLS13::Error::ErrorAlerts]
101
+ def initialize(identities: [], binders: [])
102
+ @identities = identities || []
103
+ @binders = binders || []
104
+ raise Error::ErrorAlerts, :internal_error \
105
+ if @identities.empty? || @binders.empty?
106
+ end
107
+
108
+ # @return [String]
109
+ def serialize
110
+ binary = @identities.map(&:serialize).join
111
+ identities_bin = binary.prefix_uint16_length
112
+
113
+ binary = @binders.map(&:prefix_uint8_length).join
114
+ binders_bin = binary.prefix_uint16_length
115
+
116
+ identities_bin + binders_bin
117
+ end
118
+
119
+ # @param binary [String]
120
+ #
121
+ # @return [TTTLS13::Message::Extensions::OfferedPsks, nil]
122
+ # rubocop: disable Metrics/AbcSize
123
+ # rubocop: disable Metrics/CyclomaticComplexity
124
+ # rubocop: disable Metrics/MethodLength
125
+ # rubocop: disable Metrics/PerceivedComplexity
126
+ def self.deserialize(binary)
127
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
128
+ return nil if binary.length < 2
129
+
130
+ pskids_len = Convert.bin2i(binary.slice(0, 2))
131
+ i = 2
132
+ identities = [] # Array of PskIdentity
133
+ while i < pskids_len + 2
134
+ return nil if i + 2 > binary.length
135
+
136
+ id_len = Convert.bin2i(binary.slice(i, 2))
137
+ return nil if id_len.zero?
138
+
139
+ i += 2
140
+ identity = binary.slice(i, id_len)
141
+ i += id_len
142
+
143
+ return nil if i + 4 > binary.length
144
+
145
+ obfuscated_ticket_age = Convert.bin2i(binary.slice(i, 4))
146
+ i += 4
147
+ identities << PskIdentity.new(
148
+ identity: identity,
149
+ obfuscated_ticket_age: obfuscated_ticket_age
150
+ )
151
+ end
152
+
153
+ i += 2
154
+ binders = [] # Array of String
155
+ while i < binary.length
156
+ return nil if i > binary.length
157
+
158
+ pbe_len = Convert.bin2i(binary[i])
159
+ return nil if pbe_len < 32
160
+
161
+ i += 1
162
+ binders << binary.slice(i, pbe_len)
163
+ i += pbe_len
164
+ end
165
+ return nil unless i == binary.length
166
+
167
+ OfferedPsks.new(identities: identities, binders: binders)
168
+ end
169
+ # rubocop: enable Metrics/AbcSize
170
+ # rubocop: enable Metrics/CyclomaticComplexity
171
+ # rubocop: enable Metrics/MethodLength
172
+ # rubocop: enable Metrics/PerceivedComplexity
173
+ end
174
+
175
+ # NOTE:
176
+ # struct {
177
+ # opaque identity<1..2^16-1>;
178
+ # uint32 obfuscated_ticket_age;
179
+ # } PskIdentity;
180
+ class PskIdentity
181
+ attr_reader :identity
182
+ attr_reader :obfuscated_ticket_age
183
+
184
+ # @param identity [String]
185
+ # @param obfuscated_ticket_age [Integer]
186
+ #
187
+ # @raise [TTTLS13::Error::ErrorAlerts]
188
+ def initialize(identity: '', obfuscated_ticket_age: 0)
189
+ @identity = identity || ''
190
+ @obfuscated_ticket_age = obfuscated_ticket_age
191
+ raise Error::ErrorAlerts, :internal_error \
192
+ if @identity.empty? || @obfuscated_ticket_age.negative?
193
+ end
194
+
195
+ # @return [String]
196
+ def serialize
197
+ binary = ''
198
+ binary += @identity.prefix_uint16_length
199
+ binary += @obfuscated_ticket_age.to_uint32
200
+ binary
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ module PskKeyExchangeMode
9
+ PSK_KE = "\x00"
10
+ PSK_DHE_KE = "\x01"
11
+ end
12
+
13
+ class PskKeyExchangeModes
14
+ attr_reader :extension_type
15
+ attr_reader :ke_modes
16
+
17
+ # @param ke_modes [Array of PskKeyExchangeMode]
18
+ def initialize(ke_modes = [])
19
+ @extension_type = ExtensionType::PSK_KEY_EXCHANGE_MODES
20
+ @ke_modes = ke_modes || []
21
+ end
22
+
23
+ # @return [String]
24
+ def serialize
25
+ binary = @ke_modes.join.prefix_uint8_length
26
+
27
+ @extension_type + binary.prefix_uint16_length
28
+ end
29
+
30
+ # @param binary [String]
31
+ #
32
+ # @raise [TTTLS13::Error::ErrorAlerts]
33
+ #
34
+ # @return [TTTLS13::Message::Extensions::PskKeyExchangeModes, nil]
35
+ def self.deserialize(binary)
36
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
37
+
38
+ return nil if binary.empty?
39
+
40
+ kem_len = Convert.bin2i(binary[0])
41
+ ke_modes = []
42
+ i = 1
43
+ while i < kem_len + 1
44
+ ke_modes << binary[i]
45
+ i += 1
46
+ end
47
+ return nil unless kem_len + 1 == binary.length
48
+
49
+ PskKeyExchangeModes.new(ke_modes)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,46 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ class RecordSizeLimit
9
+ attr_reader :extension_type
10
+ attr_reader :record_size_limit
11
+
12
+ # @param record_size_limit [Integer]
13
+ #
14
+ # @raise [TTTLS13::Error::ErrorAlerts]
15
+ def initialize(record_size_limit)
16
+ @extension_type = ExtensionType::RECORD_SIZE_LIMIT
17
+ @record_size_limit = record_size_limit
18
+ raise Error::ErrorAlerts, :internal_error if @record_size_limit < 64
19
+ end
20
+
21
+ # @return [String]
22
+ def serialize
23
+ binary = @record_size_limit.to_uint16
24
+
25
+ @extension_type + binary.prefix_uint16_length
26
+ end
27
+
28
+ # @param binary [String]
29
+ #
30
+ # @raise [TTTLS13::Error::ErrorAlerts]
31
+ #
32
+ # @return [TTTLS13::Message::Extensions::RecordSizeLimit, nil]
33
+ def self.deserialize(binary)
34
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
35
+
36
+ return nil if binary.length != 2
37
+
38
+ record_size_limit = Convert.bin2i(binary)
39
+ raise Error::ErrorAlerts, :illegal_parameter if record_size_limit < 64
40
+
41
+ RecordSizeLimit.new(record_size_limit)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ module NameType
9
+ HOST_NAME = "\x00"
10
+ end
11
+
12
+ # NOTE:
13
+ # The extension_data field SHALL be empty when @server_name is empty.
14
+ # Then, serialized extension_data is
15
+ #
16
+ # 00 00 00 00
17
+ #
18
+ # https://tools.ietf.org/html/rfc6066#section-3
19
+ class ServerName
20
+ attr_reader :extension_type
21
+ attr_reader :server_name
22
+
23
+ # @param server_name [String]
24
+ #
25
+ # @raise [TTTLS13::Error::ErrorAlerts]
26
+ #
27
+ # @example
28
+ # ServerName.new('example.com')
29
+ def initialize(server_name)
30
+ @extension_type = ExtensionType::SERVER_NAME
31
+ @server_name = server_name || ''
32
+ raise Error::ErrorAlerts, :internal_error \
33
+ if @server_name.length > 2**16 - 5
34
+ end
35
+
36
+ # @return [String]
37
+ def serialize
38
+ return "\x00\x00\x00\x00" if @server_name.empty?
39
+
40
+ sn_len = @server_name.length
41
+ binary = ''
42
+ binary += @extension_type
43
+ binary += (sn_len + 5).to_uint16
44
+ binary += (sn_len + 3).to_uint16
45
+ binary += NameType::HOST_NAME
46
+ binary += sn_len.to_uint16
47
+ binary += @server_name
48
+ binary
49
+ end
50
+
51
+ # @param binary [String]
52
+ #
53
+ # @raise [TTTLS13::Error::ErrorAlerts]
54
+ #
55
+ # @return [TTTLS13::Message::Extension::ServerName, nil]
56
+ def self.deserialize(binary)
57
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
58
+
59
+ return nil if binary.length == 1
60
+ return ServerName.new('') if binary.empty?
61
+
62
+ deserialize_host_name(binary)
63
+ end
64
+
65
+ class << self
66
+ private
67
+
68
+ # @param binary [String]
69
+ #
70
+ # @raise [TTTLS13::Error::ErrorAlerts]
71
+ #
72
+ # @return [TTTLS13::Message::Extension::ServerName, nil]
73
+ def deserialize_host_name(binary)
74
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
75
+
76
+ return nil unless binary.length > 5 &&
77
+ binary[2] == NameType::HOST_NAME
78
+
79
+ snlist_len = Convert.bin2i(binary.slice(0, 2))
80
+ sn_len = Convert.bin2i(binary.slice(3, 2))
81
+ server_name = binary.slice(5, sn_len)
82
+ return nil unless snlist_len + 2 == binary.length &&
83
+ sn_len + 5 == binary.length
84
+
85
+ ServerName.new(server_name)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ class SignatureAlgorithms
9
+ DEFAULT_SIGNATURE_ALGORITHMS = [
10
+ SignatureScheme::ECDSA_SECP256R1_SHA256,
11
+ SignatureScheme::ECDSA_SECP384R1_SHA384,
12
+ SignatureScheme::ECDSA_SECP521R1_SHA512,
13
+ SignatureScheme::RSA_PSS_PSS_SHA256,
14
+ SignatureScheme::RSA_PSS_PSS_SHA384,
15
+ SignatureScheme::RSA_PSS_PSS_SHA512,
16
+ SignatureScheme::RSA_PSS_RSAE_SHA256,
17
+ SignatureScheme::RSA_PSS_RSAE_SHA384,
18
+ SignatureScheme::RSA_PSS_RSAE_SHA512,
19
+ SignatureScheme::RSA_PKCS1_SHA256,
20
+ SignatureScheme::RSA_PKCS1_SHA384,
21
+ SignatureScheme::RSA_PKCS1_SHA512
22
+ ].freeze
23
+
24
+ attr_accessor :extension_type # for signature_algorithms_cert getter
25
+ attr_reader :supported_signature_algorithms
26
+
27
+ # @param supported_signature_algorithms [Array of SignatureScheme]
28
+ def initialize(supported_signature_algorithms)
29
+ @extension_type = ExtensionType::SIGNATURE_ALGORITHMS
30
+ @supported_signature_algorithms = supported_signature_algorithms || []
31
+ raise Error::ErrorAlerts, :internal_error \
32
+ if @supported_signature_algorithms.empty? ||
33
+ @supported_signature_algorithms.length * 2 > 2**16 - 3
34
+ end
35
+
36
+ # @return [String]
37
+ def serialize
38
+ binary = @supported_signature_algorithms.join
39
+
40
+ @extension_type + binary.prefix_uint16_length.prefix_uint16_length
41
+ end
42
+
43
+ # @param binary [String]
44
+ #
45
+ # @raise [TTTLS13::Error::ErrorAlerts]
46
+ #
47
+ # @return [TTTLS13::Message::Extensions::SignatureAlgorithms, nil]
48
+ def self.deserialize(binary)
49
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
50
+
51
+ return nil if binary.length < 2
52
+
53
+ ssa_len = Convert.bin2i(binary.slice(0, 2))
54
+ i = 2
55
+ supported_signature_algorithms = []
56
+ while i < ssa_len + 2
57
+ return nil if i + 2 > binary.length
58
+
59
+ supported_signature_algorithms << binary.slice(i, 2)
60
+ i += 2
61
+ end
62
+ return nil unless ssa_len + 2 == binary.length
63
+
64
+ SignatureAlgorithms.new(supported_signature_algorithms)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ module Message
6
+ module Extension
7
+ class SignatureAlgorithmsCert < SignatureAlgorithms
8
+ # @param versions [Array of SignatureScheme]
9
+ def initialize(supported_signature_algorithms)
10
+ super(supported_signature_algorithms)
11
+ @extension_type = ExtensionType::SIGNATURE_ALGORITHMS_CERT
12
+ end
13
+
14
+ # @param binary [String]
15
+ #
16
+ # @return [TTTLS13::Message::Extensions::SignatureAlgorithmsCert]
17
+ def self.deserialize(binary)
18
+ extension = SignatureAlgorithms.deserialize(binary)
19
+ extension.extension_type = ExtensionType::SIGNATURE_ALGORITHMS_CERT
20
+ extension
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,106 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ module CertificateStatusType
9
+ OCSP = "\x01"
10
+ end
11
+
12
+ class StatusRequest
13
+ attr_reader :extension_type
14
+ attr_reader :responder_id_list
15
+ attr_reader :request_extensions
16
+
17
+ # @param responder_id_list [Array of String]
18
+ # @param request_extensions [String]
19
+ #
20
+ # @example
21
+ # StatusRequest.new(
22
+ # responder_id_list: [],
23
+ # request_extensions: []
24
+ # )
25
+ def initialize(responder_id_list: [], request_extensions: '')
26
+ @extension_type = ExtensionType::STATUS_REQUEST
27
+ @responder_id_list = responder_id_list || []
28
+ @request_extensions = request_extensions || ''
29
+ end
30
+
31
+ # @return [String]
32
+ def serialize
33
+ binary = ''
34
+ binary += CertificateStatusType::OCSP
35
+ binary += @responder_id_list.length.to_uint16
36
+ binary += @responder_id_list.map do |id|
37
+ id.length.to_uint16 + id
38
+ end.join
39
+ binary += @request_extensions.prefix_uint16_length
40
+
41
+ @extension_type + binary.prefix_uint16_length
42
+ end
43
+
44
+ # @param binary [String]
45
+ #
46
+ # @raise [TTTLS13::Error::ErrorAlerts]
47
+ #
48
+ # @return [TTTLS13::Message::Extension::StatusRequest, nil]
49
+ # rubocop: disable Metrics/CyclomaticComplexity
50
+ def self.deserialize(binary)
51
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
52
+ return nil if binary.length < 5 ||
53
+ binary[0] != CertificateStatusType::OCSP
54
+
55
+ ril_len = Convert.bin2i(binary.slice(1, 2))
56
+ i = 3
57
+ responder_id_list =
58
+ deserialize_request_ids(binary.slice(i, ril_len))
59
+ # unparsable responder_id_list
60
+ return nil if responder_id_list.nil?
61
+
62
+ i += ril_len
63
+ return nil if i + 2 > binary.length
64
+
65
+ re_len = Convert.bin2i(binary.slice(i, 2))
66
+ i += 2
67
+ request_extensions = binary.slice(i, re_len)
68
+ i += re_len
69
+ return nil unless i == binary.length
70
+
71
+ StatusRequest.new(responder_id_list: responder_id_list,
72
+ request_extensions: request_extensions)
73
+ end
74
+ # rubocop: enable Metrics/CyclomaticComplexity
75
+
76
+ class << self
77
+ private
78
+
79
+ # @param binary [String]
80
+ #
81
+ # @raise [TTTLS13::Error::ErrorAlerts]
82
+ #
83
+ # @return [Array of String, nil] received unparsable binary, nil
84
+ def deserialize_request_ids(binary)
85
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
86
+
87
+ i = 0
88
+ request_ids = []
89
+ while i < binary.length
90
+ return nil if i + 2 > binary.length
91
+
92
+ id_len = Convert.bin2i(binary.slice(i, 2))
93
+ i += 2
94
+ id = binary.slice(i, id_len)
95
+ request_ids += id
96
+ i += id_len
97
+ end
98
+ return nil if i != binary.length
99
+
100
+ request_ids
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end