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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.rubocop.yml +16 -0
- data/.travis.yml +8 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/Rakefile +133 -0
- data/example/helper.rb +17 -0
- data/example/https_client.rb +32 -0
- data/example/https_client_using_0rtt.rb +64 -0
- data/example/https_client_using_hrr.rb +35 -0
- data/example/https_client_using_ticket.rb +56 -0
- data/lib/tttls1.3/cipher_suites.rb +102 -0
- data/lib/tttls1.3/client.rb +745 -0
- data/lib/tttls1.3/connection.rb +380 -0
- data/lib/tttls1.3/cryptograph/aead.rb +118 -0
- data/lib/tttls1.3/cryptograph/passer.rb +22 -0
- data/lib/tttls1.3/cryptograph.rb +3 -0
- data/lib/tttls1.3/error.rb +22 -0
- data/lib/tttls1.3/key_schedule.rb +242 -0
- data/lib/tttls1.3/message/alert.rb +86 -0
- data/lib/tttls1.3/message/application_data.rb +27 -0
- data/lib/tttls1.3/message/certificate.rb +121 -0
- data/lib/tttls1.3/message/certificate_verify.rb +59 -0
- data/lib/tttls1.3/message/change_cipher_spec.rb +26 -0
- data/lib/tttls1.3/message/client_hello.rb +100 -0
- data/lib/tttls1.3/message/encrypted_extensions.rb +65 -0
- data/lib/tttls1.3/message/end_of_early_data.rb +29 -0
- data/lib/tttls1.3/message/extension/alpn.rb +70 -0
- data/lib/tttls1.3/message/extension/cookie.rb +47 -0
- data/lib/tttls1.3/message/extension/early_data_indication.rb +58 -0
- data/lib/tttls1.3/message/extension/key_share.rb +236 -0
- data/lib/tttls1.3/message/extension/pre_shared_key.rb +205 -0
- data/lib/tttls1.3/message/extension/psk_key_exchange_modes.rb +54 -0
- data/lib/tttls1.3/message/extension/record_size_limit.rb +46 -0
- data/lib/tttls1.3/message/extension/server_name.rb +91 -0
- data/lib/tttls1.3/message/extension/signature_algorithms.rb +69 -0
- data/lib/tttls1.3/message/extension/signature_algorithms_cert.rb +25 -0
- data/lib/tttls1.3/message/extension/status_request.rb +106 -0
- data/lib/tttls1.3/message/extension/supported_groups.rb +145 -0
- data/lib/tttls1.3/message/extension/supported_versions.rb +98 -0
- data/lib/tttls1.3/message/extension/unknown_extension.rb +38 -0
- data/lib/tttls1.3/message/extensions.rb +173 -0
- data/lib/tttls1.3/message/finished.rb +44 -0
- data/lib/tttls1.3/message/new_session_ticket.rb +89 -0
- data/lib/tttls1.3/message/record.rb +232 -0
- data/lib/tttls1.3/message/server_hello.rb +116 -0
- data/lib/tttls1.3/message.rb +48 -0
- data/lib/tttls1.3/sequence_number.rb +31 -0
- data/lib/tttls1.3/signature_scheme.rb +31 -0
- data/lib/tttls1.3/transcript.rb +69 -0
- data/lib/tttls1.3/utils.rb +91 -0
- data/lib/tttls1.3/version.rb +5 -0
- data/lib/tttls1.3.rb +16 -0
- data/spec/aead_spec.rb +95 -0
- data/spec/alert_spec.rb +54 -0
- data/spec/alpn_spec.rb +55 -0
- data/spec/application_data_spec.rb +26 -0
- data/spec/certificate_spec.rb +55 -0
- data/spec/certificate_verify_spec.rb +51 -0
- data/spec/change_cipher_spec_spec.rb +26 -0
- data/spec/cipher_suites_spec.rb +39 -0
- data/spec/client_hello_spec.rb +83 -0
- data/spec/client_spec.rb +319 -0
- data/spec/connection_spec.rb +114 -0
- data/spec/cookie_spec.rb +98 -0
- data/spec/early_data_indication_spec.rb +64 -0
- data/spec/encrypted_extensions_spec.rb +94 -0
- data/spec/error_spec.rb +18 -0
- data/spec/extensions_spec.rb +170 -0
- data/spec/finished_spec.rb +55 -0
- data/spec/key_schedule_spec.rb +198 -0
- data/spec/key_share_spec.rb +199 -0
- data/spec/new_session_ticket_spec.rb +80 -0
- data/spec/pre_shared_key_spec.rb +167 -0
- data/spec/psk_key_exchange_modes_spec.rb +45 -0
- data/spec/record_size_limit_spec.rb +61 -0
- data/spec/record_spec.rb +105 -0
- data/spec/server_hello_spec.rb +101 -0
- data/spec/server_name_spec.rb +110 -0
- data/spec/signature_algorithms_cert_spec.rb +73 -0
- data/spec/signature_algorithms_spec.rb +100 -0
- data/spec/spec_helper.rb +872 -0
- data/spec/status_request_spec.rb +73 -0
- data/spec/supported_groups_spec.rb +79 -0
- data/spec/supported_versions_spec.rb +136 -0
- data/spec/transcript_spec.rb +69 -0
- data/spec/unknown_extension_spec.rb +90 -0
- data/spec/utils_spec.rb +215 -0
- data/tttls1.3.gemspec +25 -0
- 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
|