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,242 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ # rubocop: disable Metrics/ClassLength
7
+ class KeySchedule
8
+ # @param psk [String]
9
+ # @param shared_secret [String]
10
+ # @param cipher_suite [TTTLS13::CipherSuite]
11
+ # @param transcript [TTTLS13::Transcript]
12
+ def initialize(psk: nil, shared_secret:, cipher_suite:, transcript:)
13
+ @digest = CipherSuite.digest(cipher_suite)
14
+ @hash_len = CipherSuite.hash_len(cipher_suite)
15
+ @key_len = CipherSuite.key_len(cipher_suite)
16
+ @iv_len = CipherSuite.iv_len(cipher_suite)
17
+ @psk = psk || "\x00" * @hash_len
18
+ @shared_secret = shared_secret
19
+ @transcript = transcript
20
+ end
21
+
22
+ # @return [String]
23
+ def early_salt
24
+ "\x00" * @hash_len
25
+ end
26
+
27
+ # @return [String]
28
+ def early_secret
29
+ hkdf_extract(@psk, early_salt)
30
+ end
31
+
32
+ # @return [String]
33
+ def binder_key_ext
34
+ hash = OpenSSL::Digest.digest(@digest, '')
35
+ base_key = derive_secret(early_secret, 'ext binder', hash)
36
+ hkdf_expand_label(base_key, 'finished', '', @hash_len)
37
+ end
38
+
39
+ # @return [String]
40
+ def binder_key_res
41
+ hash = OpenSSL::Digest.digest(@digest, '')
42
+ base_key = derive_secret(early_secret, 'res binder', hash)
43
+ hkdf_expand_label(base_key, 'finished', '', @hash_len)
44
+ end
45
+
46
+ # @return [String]
47
+ def client_early_traffic_secret
48
+ hash = @transcript.hash(@digest, CH)
49
+ derive_secret(early_secret, 'c e traffic', hash)
50
+ end
51
+
52
+ # @return [String]
53
+ def early_data_write_key
54
+ secret = client_early_traffic_secret
55
+ hkdf_expand_label(secret, 'key', '', @key_len)
56
+ end
57
+
58
+ # @return [String]
59
+ def early_data_write_iv
60
+ secret = client_early_traffic_secret
61
+ hkdf_expand_label(secret, 'iv', '', @iv_len)
62
+ end
63
+
64
+ # @return [String]
65
+ def early_exporter_master_secret
66
+ hash = OpenSSL::Digest.digest(@digest, '')
67
+ derive_secret(early_secret, 'e exp master', hash)
68
+ end
69
+
70
+ # @return [String]
71
+ def handshake_salt
72
+ hash = OpenSSL::Digest.digest(@digest, '')
73
+ derive_secret(early_secret, 'derived', hash)
74
+ end
75
+
76
+ # @return [String]
77
+ def handshake_secret
78
+ hkdf_extract(@shared_secret, handshake_salt)
79
+ end
80
+
81
+ # @return [String]
82
+ def client_handshake_traffic_secret
83
+ hash = @transcript.hash(@digest, SH)
84
+ derive_secret(handshake_secret, 'c hs traffic', hash)
85
+ end
86
+
87
+ # @return [String]
88
+ def client_finished_key
89
+ secret = client_handshake_traffic_secret
90
+ hkdf_expand_label(secret, 'finished', '', @hash_len)
91
+ end
92
+
93
+ # @return [String]
94
+ def client_handshake_write_key
95
+ secret = client_handshake_traffic_secret
96
+ hkdf_expand_label(secret, 'key', '', @key_len)
97
+ end
98
+
99
+ # @return [String]
100
+ def client_handshake_write_iv
101
+ secret = client_handshake_traffic_secret
102
+ hkdf_expand_label(secret, 'iv', '', @iv_len)
103
+ end
104
+
105
+ # @return [String]
106
+ def server_handshake_traffic_secret
107
+ hash = @transcript.hash(@digest, SH)
108
+ derive_secret(handshake_secret, 's hs traffic', hash)
109
+ end
110
+
111
+ # @return [String]
112
+ def server_finished_key
113
+ secret = server_handshake_traffic_secret
114
+ hkdf_expand_label(secret, 'finished', '', @hash_len)
115
+ end
116
+
117
+ # @return [String]
118
+ def server_handshake_write_key
119
+ secret = server_handshake_traffic_secret
120
+ hkdf_expand_label(secret, 'key', '', @key_len)
121
+ end
122
+
123
+ # @return [String]
124
+ def server_handshake_write_iv
125
+ secret = server_handshake_traffic_secret
126
+ hkdf_expand_label(secret, 'iv', '', @iv_len)
127
+ end
128
+
129
+ # @return [String]
130
+ def master_salt
131
+ hash = OpenSSL::Digest.digest(@digest, '')
132
+ derive_secret(handshake_secret, 'derived', hash)
133
+ end
134
+
135
+ # @return [String]
136
+ def master_secret
137
+ ikm = "\x00" * @hash_len
138
+ hkdf_extract(ikm, master_salt)
139
+ end
140
+
141
+ # @return [String]
142
+ def client_application_traffic_secret
143
+ hash = @transcript.hash(@digest, SF)
144
+ derive_secret(master_secret, 'c ap traffic', hash)
145
+ end
146
+
147
+ # @return [String]
148
+ def client_application_write_key
149
+ secret = client_application_traffic_secret
150
+ hkdf_expand_label(secret, 'key', '', @key_len)
151
+ end
152
+
153
+ # @return [String]
154
+ def client_application_write_iv
155
+ secret = client_application_traffic_secret
156
+ hkdf_expand_label(secret, 'iv', '', @iv_len)
157
+ end
158
+
159
+ # @return [String]
160
+ def server_application_traffic_secret
161
+ hash = @transcript.hash(@digest, SF)
162
+ derive_secret(master_secret, 's ap traffic', hash)
163
+ end
164
+
165
+ # @return [String]
166
+ def server_application_write_key
167
+ secret = server_application_traffic_secret
168
+ hkdf_expand_label(secret, 'key', '', @key_len)
169
+ end
170
+
171
+ # @return [String]
172
+ def server_application_write_iv
173
+ secret = server_application_traffic_secret
174
+ hkdf_expand_label(secret, 'iv', '', @iv_len)
175
+ end
176
+
177
+ # @return [String]
178
+ def exporter_master_secret
179
+ hash = @transcript.hash(@digest, SF)
180
+ derive_secret(master_secret, 'exp master', hash)
181
+ end
182
+
183
+ # @return [String]
184
+ def resumption_master_secret
185
+ hash = @transcript.hash(@digest, CF)
186
+ derive_secret(master_secret, 'res master', hash)
187
+ end
188
+
189
+ # @param ikm [String]
190
+ # @param salt [String]
191
+ #
192
+ # @return [String]
193
+ def hkdf_extract(ikm, salt)
194
+ OpenSSL::HMAC.digest(@digest, salt, ikm)
195
+ end
196
+
197
+ # @param secret [String]
198
+ # @param label [String]
199
+ # @param context [String]
200
+ # @param length [Integer]
201
+ #
202
+ # @return [String]
203
+ def hkdf_expand_label(secret, label, context, length)
204
+ binary = length.to_uint16
205
+ binary += ('tls13 ' + label).prefix_uint8_length
206
+ binary += context.prefix_uint8_length
207
+ self.class.hkdf_expand(secret, binary, length, @digest)
208
+ end
209
+
210
+ # @param secret [String]
211
+ # @param info [String]
212
+ # @param length [Integer]
213
+ # @param digest [String] name of digest algorithm
214
+ #
215
+ # @raise [TTTLS13::Error::ErrorAlerts]
216
+ #
217
+ # @param [String]
218
+ def self.hkdf_expand(secret, info, length, digest)
219
+ hash_len = OpenSSL::Digest.new(digest).digest_length
220
+ raise Error::ErrorAlerts, :internal_error if length > 255 * hash_len
221
+
222
+ n = (length.to_f / hash_len).ceil
223
+ okm = ''
224
+ t = ''
225
+ (1..n).each do |i|
226
+ t = OpenSSL::HMAC.digest(digest, secret, t + info + i.chr)
227
+ okm += t
228
+ end
229
+ okm[0...length]
230
+ end
231
+
232
+ # @param secret [String]
233
+ # @param label [String]
234
+ # @param context [String]
235
+ #
236
+ # @return [String]
237
+ def derive_secret(secret, label, context)
238
+ hkdf_expand_label(secret, label, context, @hash_len)
239
+ end
240
+ end
241
+ # rubocop: enable Metrics/ClassLength
242
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ module Message
6
+ module AlertLevel
7
+ WARNING = "\x01"
8
+ FATAL = "\x02"
9
+ end
10
+
11
+ # rubocop: disable Layout/AlignHash
12
+ ALERT_DESCRIPTION = {
13
+ close_notify: "\x00",
14
+ unexpected_message: "\x0a",
15
+ bad_record_mac: "\x14",
16
+ record_overflow: "\x16",
17
+ handshake_failure: "\x28",
18
+ bad_certificate: "\x2a",
19
+ unsupported_certificate: "\x2b",
20
+ certificate_revoked: "\x2c",
21
+ certificate_expired: "\x2d",
22
+ certificate_unknown: "\x2e",
23
+ illegal_parameter: "\x2f",
24
+ unknown_ca: "\x30",
25
+ access_denied: "\x31",
26
+ decode_error: "\x32",
27
+ decrypt_error: "\x33",
28
+ protocol_version: "\x46",
29
+ insufficient_security: "\x47",
30
+ internal_error: "\x50",
31
+ inappropriate_fallback: "\x56",
32
+ user_canceled: "\x5a",
33
+ missing_extension: "\x6d",
34
+ unsupported_extension: "\x6e",
35
+ unrecognized_name: "\x70",
36
+ bad_certificate_status_response: "\x71",
37
+ unknown_psk_identity: "\x73",
38
+ certificate_required: "\x74",
39
+ no_application_protocol: "\x78"
40
+ }.freeze
41
+ # rubocop: enable Layout/AlignHash
42
+
43
+ class Alert
44
+ attr_reader :level
45
+ attr_reader :description
46
+
47
+ # @param level [TTTLS13::Message::AlertLevel]
48
+ # @param description [String] value of ALERT_DESCRIPTION
49
+ def initialize(level: nil, description:)
50
+ @level = level
51
+ @description = description
52
+ if @level.nil? && (@description == ALERT_DESCRIPTION[:user_canceled] ||
53
+ @description == ALERT_DESCRIPTION[:close_notify])
54
+ @level = AlertLevel::WARNING
55
+ elsif @level.nil?
56
+ @level = AlertLevel::FATAL
57
+ end
58
+ end
59
+
60
+ # @return [String]
61
+ def serialize
62
+ @level + @description
63
+ end
64
+
65
+ # @param binary [String]
66
+ #
67
+ # @raise [TTTLS13::Error::ErrorAlerts]
68
+ #
69
+ # @return [TTTLS13::Message::Alert]
70
+ def self.deserialize(binary)
71
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
72
+ raise Error::ErrorAlerts, :decode_error unless binary.length == 2
73
+
74
+ level = binary[0]
75
+ description = binary[1]
76
+ Alert.new(level: level, description: description)
77
+ end
78
+
79
+ # @return [TTTLS13::Error::ErrorAlerts]
80
+ def to_error
81
+ desc = ALERT_DESCRIPTION.invert[@description]
82
+ Error::ErrorAlerts.new(desc)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ module Message
6
+ class ApplicationData
7
+ attr_reader :fragment
8
+
9
+ # @param [String]
10
+ def initialize(fragment)
11
+ @fragment = fragment
12
+ end
13
+
14
+ # @return [String]
15
+ def serialize
16
+ @fragment
17
+ end
18
+
19
+ # @param binary [String]
20
+ #
21
+ # @return [TTTLS13::Message::ApplicationData]
22
+ def self.deserialize(binary)
23
+ ApplicationData.new(binary || '')
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,121 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ class Certificate
8
+ attr_reader :msg_type
9
+ attr_reader :certificate_request_context
10
+ attr_reader :certificate_list
11
+
12
+ # @param certificate_request_context [String]
13
+ # @param certificate_list [Array of CertificateEntry]
14
+ def initialize(certificate_request_context: '',
15
+ certificate_list: [])
16
+ @msg_type = HandshakeType::CERTIFICATE
17
+ @certificate_request_context = certificate_request_context || ''
18
+ @certificate_list = certificate_list || []
19
+ end
20
+
21
+ # @return [String]
22
+ def serialize
23
+ binary = ''
24
+ binary += @certificate_request_context.prefix_uint8_length
25
+ binary += @certificate_list.map(&:serialize).join.prefix_uint24_length
26
+
27
+ @msg_type + binary.prefix_uint24_length
28
+ end
29
+
30
+ alias fragment serialize
31
+
32
+ # @param binary [String]
33
+ #
34
+ # @raise [TTTLS13::Error::ErrorAlerts]
35
+ #
36
+ # @return [TTTLS13::Message::Certificate]
37
+ def self.deserialize(binary)
38
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
39
+ raise Error::ErrorAlerts, :decode_error if binary.length < 5
40
+ raise Error::ErrorAlerts, :internal_error \
41
+ unless binary[0] == HandshakeType::CERTIFICATE
42
+
43
+ msg_len = Convert.bin2i(binary.slice(1, 3))
44
+ crc_len = Convert.bin2i(binary.slice(4, 1))
45
+ certificate_request_context = binary.slice(5, crc_len)
46
+ i = 5 + crc_len
47
+ cl_len = Convert.bin2i(binary.slice(i, 3))
48
+ i += 3
49
+ cl_bin = binary.slice(i, cl_len)
50
+ i += cl_len
51
+ certificate_list = deserialize_certificate_list(cl_bin)
52
+ raise Error::ErrorAlerts, :decode_error unless i == msg_len + 4 &&
53
+ i == binary.length
54
+
55
+ Certificate.new(
56
+ certificate_request_context: certificate_request_context,
57
+ certificate_list: certificate_list
58
+ )
59
+ end
60
+
61
+ class << self
62
+ private
63
+
64
+ # @param binary [String]
65
+ #
66
+ # @raise [TTTLS13::Error::ErrorAlerts]
67
+ #
68
+ # @return [Array of CertificateEntry]
69
+ def deserialize_certificate_list(binary)
70
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
71
+
72
+ i = 0
73
+ certificate_list = []
74
+ while i < binary.length
75
+ raise Error::ErrorAlerts, :decode_error if i + 3 > binary.length
76
+
77
+ cd_len = Convert.bin2i(binary.slice(i, 3))
78
+ i += 3
79
+ cd_bin = binary.slice(i, cd_len)
80
+ cert_data = OpenSSL::X509::Certificate.new(cd_bin)
81
+ i += cd_len
82
+ raise Error::ErrorAlerts, :decode_error if i + 2 > binary.length
83
+
84
+ exs_len = Convert.bin2i(binary.slice(i, 2))
85
+ i += 2
86
+ exs_bin = binary.slice(i, exs_len)
87
+ extensions = Extensions.deserialize(exs_bin,
88
+ HandshakeType::CERTIFICATE)
89
+ i += exs_len
90
+ certificate_list << CertificateEntry.new(cert_data, extensions)
91
+ end
92
+ raise Error::ErrorAlerts, :decode_error unless i == binary.length
93
+
94
+ certificate_list
95
+ end
96
+ end
97
+ end
98
+
99
+ class CertificateEntry
100
+ attr_reader :cert_data
101
+ attr_reader :extensions
102
+
103
+ # @param cert_data [OpenSSL::X509::Certificate]
104
+ # @param extensions [TTTLS13::Message::Extensions]
105
+ #
106
+ # @return [CertificateEntry]
107
+ def initialize(cert_data, extensions = Extensions.new)
108
+ @cert_data = cert_data
109
+ @extensions = extensions || Extensions.new
110
+ end
111
+
112
+ # @return [String]
113
+ def serialize
114
+ binary = ''
115
+ binary += @cert_data.to_der.prefix_uint24_length
116
+ binary += @extensions.serialize
117
+ binary
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ class CertificateVerify
8
+ attr_reader :msg_type
9
+ attr_reader :signature_scheme
10
+ attr_reader :signature
11
+
12
+ # @param signature_scheme [TTTLS13::SignatureScheme]
13
+ # @param signature [String]
14
+ #
15
+ # @raise [TTTLS13::Error::ErrorAlerts]
16
+ def initialize(signature_scheme:, signature:)
17
+ @msg_type = HandshakeType::CERTIFICATE_VERIFY
18
+ @signature_scheme = signature_scheme
19
+ @signature = signature
20
+ raise Error::ErrorAlerts, :internal_error \
21
+ if @signature.length > 2**16 - 1
22
+ end
23
+
24
+ # @return [String]
25
+ def serialize
26
+ binary = ''
27
+ binary += @signature_scheme
28
+ binary += @signature.prefix_uint16_length
29
+
30
+ @msg_type + binary.prefix_uint24_length
31
+ end
32
+
33
+ alias fragment serialize
34
+
35
+ # @param binary [String]
36
+ #
37
+ # @raise [TTTLS13::Error::ErrorAlerts]
38
+ #
39
+ # @return [TTTLS13::Message::CertificateVerify]
40
+ def self.deserialize(binary)
41
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
42
+ raise Error::ErrorAlerts, :decode_error if binary.length < 8
43
+ raise Error::ErrorAlerts, :internal_error \
44
+ unless binary[0] == HandshakeType::CERTIFICATE_VERIFY
45
+
46
+ msg_len = Convert.bin2i(binary.slice(1, 3))
47
+ signature_scheme = binary.slice(4, 2)
48
+ signature_len = Convert.bin2i(binary.slice(6, 2))
49
+ signature = binary.slice(8, signature_len)
50
+ raise Error::ErrorAlerts, :internal_error \
51
+ unless signature_len + 4 == msg_len &&
52
+ signature_len + 8 == binary.length
53
+
54
+ CertificateVerify.new(signature_scheme: signature_scheme,
55
+ signature: signature)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ module Message
6
+ class ChangeCipherSpec
7
+ # @return [String]
8
+ def serialize
9
+ "\x01"
10
+ end
11
+
12
+ # @param binary [String]
13
+ #
14
+ # @raise [TTTLS13::Error::ErrorAlerts]
15
+ #
16
+ # @return [TTTLS13::Message::ChangeCipherSpec]
17
+ def self.deserialize(binary)
18
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
19
+ raise Error::ErrorAlerts, :decode_error unless binary.length == 1
20
+ raise Error::ErrorAlerts, :unexpected_message unless binary[0] == "\x01"
21
+
22
+ ChangeCipherSpec.new
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,100 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ class ClientHello
8
+ attr_reader :msg_type
9
+ attr_reader :legacy_version
10
+ attr_reader :random
11
+ attr_reader :legacy_session_id
12
+ attr_reader :cipher_suites
13
+ attr_reader :legacy_compression_methods
14
+ attr_reader :extensions
15
+
16
+ # @param legacy_version [String]
17
+ # @param random [String]
18
+ # @param legacy_session_id [String]
19
+ # @param cipher_suites [TTTLS13::CipherSuites]
20
+ # @param legacy_compression_methods [Array of String]
21
+ # @param extensions [TTTLS13::Message::Extensions]
22
+ # rubocop: disable Metrics/ParameterLists
23
+ def initialize(legacy_version: ProtocolVersion::TLS_1_2,
24
+ random: OpenSSL::Random.random_bytes(32),
25
+ legacy_session_id: OpenSSL::Random.random_bytes(32),
26
+ cipher_suites:,
27
+ legacy_compression_methods: ["\x00"],
28
+ extensions: Extensions.new)
29
+ @msg_type = HandshakeType::CLIENT_HELLO
30
+ @legacy_version = legacy_version
31
+ @random = random
32
+ @legacy_session_id = legacy_session_id
33
+ @cipher_suites = cipher_suites
34
+ @legacy_compression_methods = legacy_compression_methods
35
+ @extensions = extensions
36
+ end
37
+ # rubocop: enable Metrics/ParameterLists
38
+
39
+ # @return [String]
40
+ def serialize
41
+ binary = ''
42
+ binary += @legacy_version
43
+ binary += @random
44
+ binary += @legacy_session_id.prefix_uint8_length
45
+ binary += @cipher_suites.serialize
46
+ binary += @legacy_compression_methods.join.prefix_uint8_length
47
+ binary += @extensions.serialize
48
+
49
+ @msg_type + binary.prefix_uint24_length
50
+ end
51
+
52
+ # @param binary [String]
53
+ #
54
+ # @raise [TTTLS13::Error::ErrorAlerts]
55
+ #
56
+ # @return [TTTLS13::Message::ClientHello]
57
+ # rubocop: disable Metrics/AbcSize
58
+ # rubocop: disable Metrics/MethodLength
59
+ def self.deserialize(binary)
60
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
61
+ raise Error::ErrorAlerts, :decode_error if binary.length < 39
62
+ raise Error::ErrorAlerts, :internal_error \
63
+ unless binary[0] == HandshakeType::CLIENT_HELLO
64
+
65
+ msg_len = Convert.bin2i(binary.slice(1, 3))
66
+ legacy_version = binary.slice(4, 2)
67
+ random = binary.slice(6, 32)
68
+ lsid_len = Convert.bin2i(binary[38])
69
+ legacy_session_id = binary.slice(39, lsid_len)
70
+ i = 39 + lsid_len
71
+ cs_len = Convert.bin2i(binary.slice(i, 2))
72
+ i += 2
73
+ cs_bin = binary.slice(i, cs_len)
74
+ cipher_suites = CipherSuites.deserialize(cs_bin)
75
+ i += cs_len
76
+ cm_len = Convert.bin2i(binary[i])
77
+ i += 1
78
+ legacy_compression_methods = binary.slice(i, cm_len).split('')
79
+ i += cm_len
80
+ exs_len = Convert.bin2i(binary.slice(i, 2))
81
+ i += 2
82
+ exs_bin = binary.slice(i, exs_len)
83
+ extensions = Extensions.deserialize(exs_bin,
84
+ HandshakeType::CLIENT_HELLO)
85
+ i += exs_len
86
+ raise Error::ErrorAlerts, :decode_error unless i == msg_len + 4 &&
87
+ i == binary.length
88
+
89
+ ClientHello.new(legacy_version: legacy_version,
90
+ random: random,
91
+ legacy_session_id: legacy_session_id,
92
+ cipher_suites: cipher_suites,
93
+ legacy_compression_methods: legacy_compression_methods,
94
+ extensions: extensions)
95
+ end
96
+ # rubocop: enable Metrics/AbcSize
97
+ # rubocop: enable Metrics/MethodLength
98
+ end
99
+ end
100
+ end