tttls1.3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,65 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ class EncryptedExtensions
8
+ attr_reader :msg_type
9
+ attr_reader :extensions
10
+
11
+ # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1
12
+ ALLOWED_EXTENSIONS = [
13
+ ExtensionType::SERVER_NAME,
14
+ ExtensionType::MAX_FRAGMENT_LENGTH,
15
+ ExtensionType::SUPPORTED_GROUPS,
16
+ ExtensionType::USE_SRTP,
17
+ ExtensionType::HEARTBEAT,
18
+ ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION,
19
+ ExtensionType::CLIENT_CERTIFICATE_TYPE,
20
+ ExtensionType::SERVER_CERTIFICATE_TYPE,
21
+ ExtensionType::RECORD_SIZE_LIMIT,
22
+ ExtensionType::EARLY_DATA
23
+ ].freeze
24
+
25
+ # @param extensions [TTTLS13::Message::Extensions]
26
+ def initialize(extensions = Extensions.new)
27
+ @msg_type = HandshakeType::ENCRYPTED_EXTENSIONS
28
+ @extensions = extensions || Extensions.new
29
+ end
30
+
31
+ # @return [String]
32
+ def serialize
33
+ @msg_type + @extensions.serialize.prefix_uint24_length
34
+ end
35
+
36
+ alias fragment serialize
37
+
38
+ # @param binary [String]
39
+ #
40
+ # @raise [TTTLS13::Error::ErrorAlerts]
41
+ #
42
+ # @return [TTTLS13::Message::EncryptedExtensions]
43
+ def self.deserialize(binary)
44
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
45
+ raise Error::ErrorAlerts, :decode_error if binary.length < 6
46
+ raise Error::ErrorAlerts, :internal_error \
47
+ unless binary[0] == HandshakeType::ENCRYPTED_EXTENSIONS
48
+
49
+ ee_len = Convert.bin2i(binary.slice(1, 3))
50
+ exs_len = Convert.bin2i(binary.slice(4, 2))
51
+ extensions = Extensions.deserialize(binary.slice(6, exs_len),
52
+ HandshakeType::ENCRYPTED_EXTENSIONS)
53
+ raise Error::ErrorAlerts, :decode_error \
54
+ unless exs_len + 2 == ee_len && exs_len + 6 == binary.length
55
+
56
+ EncryptedExtensions.new(extensions)
57
+ end
58
+
59
+ # @return [Boolean]
60
+ def any_forbidden_extensions?
61
+ !(@extensions.keys - ALLOWED_EXTENSIONS).empty?
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ module Message
6
+ class EndOfEarlyData
7
+ # @return [String]
8
+ def serialize
9
+ ''
10
+ end
11
+
12
+ # @param binary [String]
13
+ #
14
+ # @raise [TTTLS13::Error::ErrorAlerts]
15
+ #
16
+ # @return [TTTLS13::Message::EndOfEarlyData]
17
+ def self.deserialize(binary)
18
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
19
+ raise Error::ErrorAlerts, :decode_error unless binary.length == 4
20
+ raise Error::ErrorAlerts, :unexpected_message \
21
+ unless binary[0] == HandshakeType::END_OF_EARLY_DATA
22
+ raise Error::ErrorAlerts, :decode_error \
23
+ unless binary == "\x05\x00\x00\x00"
24
+
25
+ EndOfEarlyData.new
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ class Alpn
9
+ attr_reader :extension_type
10
+ attr_reader :protocol_name_list
11
+
12
+ # @param named_group_list [Array of String]
13
+ #
14
+ # @raise [TTTLS13::Error::ErrorAlerts]
15
+ #
16
+ # @example
17
+ # Alpn.new(['h2', 'http/1.1'])
18
+ #
19
+ # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
20
+ def initialize(protocol_name_list)
21
+ @extension_type \
22
+ = ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
23
+ @protocol_name_list = protocol_name_list || []
24
+ raise Error::ErrorAlerts, :internal_error \
25
+ if @protocol_name_list.empty?
26
+ end
27
+
28
+ # @return [String]
29
+ def serialize
30
+ binary = @protocol_name_list.map(&:prefix_uint8_length).join
31
+
32
+ @extension_type + binary.prefix_uint16_length.prefix_uint16_length
33
+ end
34
+
35
+ # @param binary [String]
36
+ #
37
+ # @raise [TTTLS13::Error::ErrorAlerts]
38
+ #
39
+ # @return [TTTLS13::Message::Extension::Alpn, nil]
40
+ # rubocop: disable Metrics/CyclomaticComplexity
41
+ # rubocop: disable Metrics/PerceivedComplexity
42
+ def self.deserialize(binary)
43
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
44
+
45
+ return nil if binary.length < 2
46
+
47
+ pnlist_len = Convert.bin2i(binary.slice(0, 2))
48
+ i = 2
49
+ protocol_name_list = []
50
+ while i < pnlist_len + 2
51
+ return nil if i + 1 > binary.length
52
+
53
+ pn_len = Convert.bin2i(binary.slice(i, 1))
54
+ i += 1
55
+ return nil if i + pn_len > binary.length
56
+
57
+ protocol_name_list << binary.slice(i, pn_len)
58
+ i += pn_len
59
+ end
60
+ return nil unless i == binary.length &&
61
+ pnlist_len + 2 == binary.length
62
+
63
+ Alpn.new(protocol_name_list)
64
+ end
65
+ # rubocop: enable Metrics/CyclomaticComplexity
66
+ # rubocop: enable Metrics/PerceivedComplexity
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ class Cookie
9
+ attr_reader :extension_type
10
+ attr_reader :cookie
11
+
12
+ # @param cookie [String]
13
+ #
14
+ # @raise [TTTLS13::Error::ErrorAlerts]
15
+ def initialize(cookie)
16
+ @extension_type = ExtensionType::COOKIE
17
+ @cookie = cookie || ''
18
+ raise Error::ErrorAlerts, :internal_error \
19
+ if @cookie.length > 2**16 - 3
20
+ end
21
+
22
+ # @return [String]
23
+ def serialize
24
+ @extension_type + @cookie.prefix_uint16_length.prefix_uint16_length
25
+ end
26
+
27
+ # @param binary [String]
28
+ #
29
+ # @raise [TTTLS13::Error::ErrorAlerts]
30
+ #
31
+ # @return [TTTLS13::Message::Extensions::Cookie, nil]
32
+ def self.deserialize(binary)
33
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
34
+
35
+ return nil if binary.length < 2
36
+
37
+ cookie_len = Convert.bin2i(binary.slice(0, 2))
38
+ cookie = binary.slice(2, cookie_len)
39
+ return nil unless cookie_len + 2 == binary.length &&
40
+ cookie_len <= 2**16 - 3
41
+
42
+ Cookie.new(cookie)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ class EarlyDataIndication
9
+ attr_reader :extension_type
10
+ attr_reader :max_early_data_size
11
+
12
+ # @param max_early_data_size [Integer, nil]
13
+ #
14
+ # @raise [TTTLS13::Error::ErrorAlerts]
15
+ def initialize(max_early_data_size = nil)
16
+ @extension_type = ExtensionType::EARLY_DATA
17
+ @max_early_data_size = max_early_data_size
18
+ raise Error::ErrorAlerts, :internal_error \
19
+ unless @max_early_data_size.nil? || @max_early_data_size < 2**32
20
+ end
21
+
22
+ # @return [String]
23
+ def serialize
24
+ binary = ''
25
+ binary = @max_early_data_size.to_uint32 \
26
+ unless @max_early_data_size.nil?
27
+
28
+ @extension_type + binary.prefix_uint16_length
29
+ end
30
+
31
+ # @param binary [String]
32
+ # @param msg_type [TTTLS13::Message::ContentType]
33
+ #
34
+ # @raise [TTTLS13::Error::ErrorAlerts]
35
+ #
36
+ # @return [TTTLS13::Message::Extensions::EarlyDataIndication, nil]
37
+ def self.deserialize(binary, msg_type)
38
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
39
+
40
+ case msg_type
41
+ when HandshakeType::CLIENT_HELLO, HandshakeType::ENCRYPTED_EXTENSIONS
42
+ return nil unless binary.empty?
43
+
44
+ max_early_data_size = nil
45
+ when HandshakeType::NEW_SESSION_TICKET
46
+ return nil unless binary.length == 4
47
+
48
+ max_early_data_size = Convert.bin2i(binary)
49
+ else
50
+ raise Error::ErrorAlerts, :internal_error
51
+ end
52
+
53
+ EarlyDataIndication.new(max_early_data_size)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,236 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ module Extension
8
+ # rubocop: disable Metrics/ClassLength
9
+ class KeyShare
10
+ attr_reader :extension_type
11
+ attr_reader :msg_type
12
+ attr_reader :key_share_entry
13
+
14
+ # @param msg_type [TTTLS13::Message::ContentType]
15
+ # @param key_share_entry [Array of KeyShareEntry]
16
+ #
17
+ # @raise [TTTLS13::Error::ErrorAlerts]
18
+ # rubocop: disable Metrics/CyclomaticComplexity
19
+ # rubocop: disable Metrics/PerceivedComplexity
20
+ def initialize(msg_type:, key_share_entry: [])
21
+ @extension_type = ExtensionType::KEY_SHARE
22
+ @msg_type = msg_type
23
+ @key_share_entry = key_share_entry || []
24
+ raise Error::ErrorAlerts, :internal_error \
25
+ unless (@msg_type == HandshakeType::CLIENT_HELLO &&
26
+ @key_share_entry.length >= 0 &&
27
+ @key_share_entry.all?(&:valid_key_share_client_hello?)) ||
28
+ (@msg_type == HandshakeType::SERVER_HELLO &&
29
+ @key_share_entry.length == 1 &&
30
+ @key_share_entry.first.valid_key_share_server_hello?) ||
31
+ (@msg_type == HandshakeType::HELLO_RETRY_REQUEST &&
32
+ @key_share_entry.length == 1 &&
33
+ @key_share_entry.first.valid_key_share_hello_retry_request?)
34
+ end
35
+ # rubocop: enable Metrics/CyclomaticComplexity
36
+ # rubocop: enable Metrics/PerceivedComplexity
37
+
38
+ # @raise [TTTLS13::Error::ErrorAlerts]
39
+ #
40
+ # @return [String]
41
+ def serialize
42
+ case @msg_type
43
+ when HandshakeType::CLIENT_HELLO
44
+ binary = @key_share_entry.map(&:serialize).join.prefix_uint16_length
45
+ when HandshakeType::SERVER_HELLO, HandshakeType::HELLO_RETRY_REQUEST
46
+ binary = @key_share_entry.first.serialize
47
+ else
48
+ raise Error::ErrorAlerts, :internal_error
49
+ end
50
+ @extension_type + binary.prefix_uint16_length
51
+ end
52
+
53
+ # @param binary [String]
54
+ # @param msg_type [TTTLS13::Message::HandshakeType]
55
+ #
56
+ # @raise [TTTLS13::Error::ErrorAlerts]
57
+ #
58
+ # @return [TTTLS13::Message::Extensions::KeyShare, nil]
59
+ # rubocop: disable Metrics/CyclomaticComplexity
60
+ def self.deserialize(binary, msg_type)
61
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
62
+
63
+ case msg_type
64
+ when HandshakeType::CLIENT_HELLO
65
+ key_share_entry = deserialize_keyshare_ch(binary)
66
+ return nil \
67
+ unless key_share_entry.all?(&:valid_key_share_client_hello?)
68
+ when HandshakeType::SERVER_HELLO
69
+ key_share_entry = deserialize_keyshare_sh(binary)
70
+ return nil \
71
+ unless key_share_entry.first.valid_key_share_server_hello?
72
+ when HandshakeType::HELLO_RETRY_REQUEST
73
+ key_share_entry = deserialize_keyshare_hrr(binary)
74
+ return nil \
75
+ unless key_share_entry.first.valid_key_share_hello_retry_request?
76
+ else
77
+ raise Error::ErrorAlerts, :internal_error
78
+ end
79
+ return nil if key_share_entry.nil?
80
+
81
+ KeyShare.new(msg_type: msg_type,
82
+ key_share_entry: key_share_entry)
83
+ end
84
+ # rubocop: enable Metrics/CyclomaticComplexity
85
+
86
+ # @param groups [Array of TTTLS13::Message::Extension::NamedGroup]
87
+ #
88
+ # @return [TTTLS13::Message::Extensions::KeyShare]
89
+ # @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
90
+ def self.gen_ch_key_share(groups)
91
+ priv_keys = {}
92
+ kse = groups.map do |group|
93
+ curve = NamedGroup.curve_name(group)
94
+ ec = OpenSSL::PKey::EC.new(curve)
95
+ ec.generate_key!
96
+ # store private key to do the key-exchange
97
+ priv_keys.store(group, ec)
98
+ KeyShareEntry.new(
99
+ group: group,
100
+ key_exchange: ec.public_key.to_octet_string(:uncompressed)
101
+ )
102
+ end
103
+
104
+ key_share = KeyShare.new(
105
+ msg_type: HandshakeType::CLIENT_HELLO,
106
+ key_share_entry: kse
107
+ )
108
+
109
+ [key_share, priv_keys]
110
+ end
111
+
112
+ class << self
113
+ private
114
+
115
+ # NOTE:
116
+ # struct {
117
+ # KeyShareEntry client_shares<0..2^16-1>;
118
+ # } KeyShareClientHello;
119
+ #
120
+ # @param binary [String]
121
+ #
122
+ # @raise [TTTLS13::Error::ErrorAlerts]
123
+ #
124
+ # @return [Array of KeyShareEntry, nil]
125
+ def deserialize_keyshare_ch(binary)
126
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
127
+
128
+ return nil if binary.length < 2
129
+
130
+ cs_len = Convert.bin2i(binary.slice(0, 2))
131
+ key_share_entry = []
132
+ itr = 2
133
+ while itr < cs_len + 2
134
+ return nil if itr + 4 > binary.length
135
+
136
+ group = binary.slice(itr, 2)
137
+ itr += 2
138
+ ke_len = Convert.bin2i(binary.slice(itr, 2))
139
+ itr += 2
140
+ key_exchange = binary.slice(itr, ke_len)
141
+ key_share_entry << KeyShareEntry.new(group: group,
142
+ key_exchange: key_exchange)
143
+ itr += ke_len
144
+ end
145
+ return nil unless itr == binary.length
146
+
147
+ key_share_entry
148
+ end
149
+
150
+ # NOTE:
151
+ # struct {
152
+ # KeyShareEntry server_share;
153
+ # } KeyShareServerHello;
154
+ #
155
+ # @param binary [String]
156
+ #
157
+ # @raise [TTTLS13::Error::ErrorAlerts]
158
+ #
159
+ # @return [Array of KeyShareEntry, nil]
160
+ def deserialize_keyshare_sh(binary)
161
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
162
+
163
+ return nil if binary.length < 4
164
+
165
+ group = binary.slice(0, 2)
166
+ ke_len = Convert.bin2i(binary.slice(2, 2))
167
+ key_exchange = binary.slice(4, ke_len)
168
+ return nil unless ke_len + 4 == binary.length
169
+
170
+ [KeyShareEntry.new(group: group, key_exchange: key_exchange)]
171
+ end
172
+
173
+ # NOTE:
174
+ # struct {
175
+ # NamedGroup selected_group;
176
+ # } KeyShareHelloRetryRequest;
177
+ #
178
+ # @param binary [String]
179
+ #
180
+ # @raise [TTTLS13::Error::ErrorAlerts]
181
+ #
182
+ # @return [Array of KeyShareEntry, nil]
183
+ def deserialize_keyshare_hrr(binary)
184
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
185
+
186
+ return nil unless binary.length == 2
187
+
188
+ group = binary.slice(0, 2)
189
+ [KeyShareEntry.new(group: group)]
190
+ end
191
+ end
192
+ end
193
+ # rubocop: enable Metrics/ClassLength
194
+
195
+ class KeyShareEntry
196
+ attr_reader :group
197
+ attr_reader :key_exchange
198
+
199
+ # @param group [TTTLS13::Message::Extension::NamedGroup]
200
+ # @param key_exchange [String]
201
+ #
202
+ # @raise [TTTLS13::Error::ErrorAlerts]
203
+ def initialize(group:, key_exchange: nil)
204
+ @group = group || ''
205
+ @key_exchange = key_exchange || ''
206
+ raise Error::ErrorAlerts, :internal_error unless @group.length == 2
207
+ end
208
+
209
+ # @return [Boolean]
210
+ def valid_key_share_client_hello?
211
+ @group.length == 2 && @key_exchange.length.positive?
212
+ end
213
+
214
+ # @return [Boolean]
215
+ def valid_key_share_server_hello?
216
+ @group.length == 2 && @key_exchange.length.positive?
217
+ end
218
+
219
+ # @return [Boolean]
220
+ def valid_key_share_hello_retry_request?
221
+ @group.length == 2 && @key_exchange.empty?
222
+ end
223
+
224
+ # @return [String]
225
+ def serialize
226
+ binary = ''
227
+ binary += @group
228
+ # KeyShareHelloRetryRequest doesn't have key_exchange.
229
+ binary += @key_exchange.prefix_uint16_length \
230
+ unless @key_exchange.empty?
231
+ binary
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end