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