tttls1.3 0.2.15 → 0.2.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/README.md +2 -0
- data/lib/tttls1.3/client.rb +85 -42
- data/lib/tttls1.3/connection.rb +24 -19
- data/lib/tttls1.3/message/client_hello.rb +1 -0
- data/lib/tttls1.3/message/compressed_certificate.rb +82 -0
- data/lib/tttls1.3/message/extension/alpn.rb +5 -2
- data/lib/tttls1.3/message/extension/compress_certificate.rb +58 -0
- data/lib/tttls1.3/message/extension/signature_algorithms.rb +2 -2
- data/lib/tttls1.3/message/extension/supported_groups.rb +2 -2
- data/lib/tttls1.3/message/extensions.rb +4 -0
- data/lib/tttls1.3/message/record.rb +28 -16
- data/lib/tttls1.3/message.rb +22 -20
- data/lib/tttls1.3/server.rb +61 -26
- data/lib/tttls1.3/transcript.rb +3 -7
- data/lib/tttls1.3/version.rb +1 -1
- data/spec/client_spec.rb +28 -19
- data/spec/compress_certificate_spec.rb +54 -0
- data/spec/connection_spec.rb +22 -15
- data/spec/key_schedule_spec.rb +48 -25
- data/spec/record_spec.rb +2 -2
- data/spec/server_spec.rb +23 -11
- data/spec/spec_helper.rb +4 -0
- data/spec/transcript_spec.rb +34 -20
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7006bce3031f6232ae949b44eb31111562d581e359769952f48a537848d50418
|
4
|
+
data.tar.gz: d332e823eb8c677ff534e46a87e96c039aad2d5538c0f823ac7eb365f372ca88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca37fd2570b905759da152932eb2e6f29c3e37f59135353b9e3cfbce0c683f7493fdd360f9a56c50f2ab252f0460728c09585d018d04b8668de4000b1567d249
|
7
|
+
data.tar.gz: 6e37405f3034de1fe648f0882bbc9d9b65baeac8d1ef142fc776010b8480cd987a87060edf6ae85fd20327afc5327aacb23318e460f510259c99056aa76fb389
|
data/.github/workflows/ci.yml
CHANGED
data/README.md
CHANGED
@@ -102,6 +102,7 @@ tttls1.3 client is configurable using keyword arguments.
|
|
102
102
|
| `:record_size_limit` | Integer | nil | The record\_size\_limit offerd in ClientHello extensions. If not needed to be present, set nil. |
|
103
103
|
| `:check_certificate_status` | Boolean | false | If needed to check certificate status, set true. |
|
104
104
|
| `:process_certificate_status` | Proc | `TTTLS13::Client.method(:softfail_check_certificate_status)` | Proc(or Method) that checks received OCSPResponse. Its 3 arguments are OpenSSL::OCSP::Response, end-entity certificate(OpenSSL::X509::Certificate) and certificates chain(Array of Certificate) used for verification and it returns Boolean. |
|
105
|
+
| `:compress_certificate_algorithms` | Array of TTTLS13::Message::Extension::CertificateCompressionAlgorithm constant | `ZLIB` | The compression algorithms are supported for compressing the Certificate message. |
|
105
106
|
| `:compatibility_mode` | Boolean | true | If needed to send ChangeCipherSpec, set true. |
|
106
107
|
| `:loglevel` | Logger constant | Logger::WARN | If needed to print verbose, set Logger::DEBUG. |
|
107
108
|
|
@@ -120,6 +121,7 @@ tttls1.3 server is configurable using keyword arguments.
|
|
120
121
|
| `:supported_groups` | Array of TTTLS13::NamedGroup constant | `SECP256R1`, `SECP384R1`, `SECP521R1` | List of supported named groups. |
|
121
122
|
| `:alpn` | Array of String | nil | List of supported application protocols. If not needed to check this extension, set nil. |
|
122
123
|
| `:process_ocsp_response` | Proc | nil | Proc that gets OpenSSL::OCSP::Response. If not needed to staple OCSP::Response, set nil. |
|
124
|
+
| `:compress_certificate_algorithms` | Array of TTTLS13::Message::Extension::CertificateCompressionAlgorithm constant | `ZLIB` | The compression algorithms are supported for compressing the Certificate message. |
|
123
125
|
| `:compatibility_mode` | Boolean | true | If needed to send ChangeCipherSpec, set true. |
|
124
126
|
| `:loglevel` | Logger constant | Logger::WARN | If needed to print verbose, set Logger::DEBUG. |
|
125
127
|
|
data/lib/tttls1.3/client.rb
CHANGED
@@ -43,6 +43,11 @@ module TTTLS13
|
|
43
43
|
].freeze
|
44
44
|
private_constant :DEFAULT_CH_NAMED_GROUP_LIST
|
45
45
|
|
46
|
+
DEFALUT_CH_COMPRESS_CERTIFICATE_ALGORITHMS = [
|
47
|
+
Message::Extension::CertificateCompressionAlgorithm::ZLIB
|
48
|
+
].freeze
|
49
|
+
private_constant :DEFALUT_CH_COMPRESS_CERTIFICATE_ALGORITHMS
|
50
|
+
|
46
51
|
DEFAULT_CLIENT_SETTINGS = {
|
47
52
|
ca_file: nil,
|
48
53
|
cipher_suites: DEFAULT_CH_CIPHER_SUITES,
|
@@ -61,6 +66,7 @@ module TTTLS13
|
|
61
66
|
record_size_limit: nil,
|
62
67
|
check_certificate_status: false,
|
63
68
|
process_certificate_status: nil,
|
69
|
+
compress_certificate_algorithms: DEFALUT_CH_COMPRESS_CERTIFICATE_ALGORITHMS,
|
64
70
|
compatibility_mode: true,
|
65
71
|
loglevel: Logger::WARN
|
66
72
|
}.freeze
|
@@ -154,8 +160,8 @@ module TTTLS13
|
|
154
160
|
|
155
161
|
extensions, priv_keys = gen_ch_extensions
|
156
162
|
binder_key = (use_psk? ? key_schedule.binder_key_res : nil)
|
157
|
-
|
158
|
-
|
163
|
+
ch = send_client_hello(extensions, binder_key)
|
164
|
+
transcript[CH] = [ch, ch.serialize]
|
159
165
|
send_ccs if @settings[:compatibility_mode]
|
160
166
|
if use_early_data?
|
161
167
|
e_wcipher = gen_cipher(
|
@@ -170,7 +176,7 @@ module TTTLS13
|
|
170
176
|
when ClientState::WAIT_SH
|
171
177
|
logger.debug('ClientState::WAIT_SH')
|
172
178
|
|
173
|
-
sh = transcript[SH] = recv_server_hello
|
179
|
+
sh, = transcript[SH] = recv_server_hello
|
174
180
|
|
175
181
|
# downgrade protection
|
176
182
|
if !sh.negotiated_tls_1_3? && sh.downgraded?
|
@@ -187,7 +193,7 @@ module TTTLS13
|
|
187
193
|
unless sh.legacy_compression_method == "\x00"
|
188
194
|
|
189
195
|
# validate sh using ch
|
190
|
-
ch = transcript[CH]
|
196
|
+
ch, = transcript[CH]
|
191
197
|
terminate(:illegal_parameter) \
|
192
198
|
unless sh.legacy_version == ch.legacy_version
|
193
199
|
terminate(:illegal_parameter) \
|
@@ -199,7 +205,7 @@ module TTTLS13
|
|
199
205
|
|
200
206
|
# validate sh using hrr
|
201
207
|
if transcript.include?(HRR)
|
202
|
-
hrr = transcript[HRR]
|
208
|
+
hrr, = transcript[HRR]
|
203
209
|
terminate(:illegal_parameter) \
|
204
210
|
unless sh.cipher_suite == hrr.cipher_suite
|
205
211
|
|
@@ -212,8 +218,9 @@ module TTTLS13
|
|
212
218
|
# handling HRR
|
213
219
|
if sh.hrr?
|
214
220
|
terminate(:unexpected_message) if transcript.include?(HRR)
|
215
|
-
|
216
|
-
|
221
|
+
|
222
|
+
ch1, = transcript[CH1] = transcript.delete(CH)
|
223
|
+
hrr, = transcript[HRR] = transcript.delete(SH)
|
217
224
|
|
218
225
|
# validate cookie
|
219
226
|
diff_sets = sh.extensions.keys - ch1.extensions.keys
|
@@ -235,8 +242,9 @@ module TTTLS13
|
|
235
242
|
extensions, pk = gen_newch_extensions(ch1, hrr)
|
236
243
|
priv_keys = pk.merge(priv_keys)
|
237
244
|
binder_key = (use_psk? ? key_schedule.binder_key_res : nil)
|
238
|
-
|
239
|
-
|
245
|
+
ch = send_new_client_hello(ch1, hrr, extensions, binder_key)
|
246
|
+
transcript[CH] = [ch, ch.serialize]
|
247
|
+
|
240
248
|
@state = ClientState::WAIT_SH
|
241
249
|
next
|
242
250
|
end
|
@@ -277,37 +285,46 @@ module TTTLS13
|
|
277
285
|
when ClientState::WAIT_EE
|
278
286
|
logger.debug('ClientState::WAIT_EE')
|
279
287
|
|
280
|
-
ee = transcript[EE] = recv_encrypted_extensions(hs_rcipher)
|
288
|
+
ee, = transcript[EE] = recv_encrypted_extensions(hs_rcipher)
|
281
289
|
terminate(:illegal_parameter) unless ee.appearable_extensions?
|
282
290
|
|
283
|
-
ch = transcript[CH]
|
291
|
+
ch, = transcript[CH]
|
284
292
|
terminate(:unsupported_extension) \
|
285
293
|
unless (ee.extensions.keys - ch.extensions.keys).empty?
|
286
294
|
|
287
295
|
rsl = ee.extensions[Message::ExtensionType::RECORD_SIZE_LIMIT]
|
288
296
|
@recv_record_size = rsl.record_size_limit unless rsl.nil?
|
289
|
-
|
290
297
|
@succeed_early_data = true \
|
291
298
|
if ee.extensions.include?(Message::ExtensionType::EARLY_DATA)
|
292
|
-
|
293
299
|
@alpn = ee.extensions[
|
294
300
|
Message::ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
|
295
301
|
]&.protocol_name_list&.first
|
296
|
-
|
297
302
|
@state = ClientState::WAIT_CERT_CR
|
298
303
|
@state = ClientState::WAIT_FINISHED unless psk.nil?
|
299
304
|
when ClientState::WAIT_CERT_CR
|
300
305
|
logger.debug('ClientState::WAIT_CERT_CR')
|
301
306
|
|
302
|
-
message = recv_message(
|
303
|
-
|
304
|
-
|
305
|
-
|
307
|
+
message, orig_msg = recv_message(
|
308
|
+
receivable_ccs: true,
|
309
|
+
cipher: hs_rcipher
|
310
|
+
)
|
311
|
+
case message.msg_type
|
312
|
+
when Message::HandshakeType::CERTIFICATE,
|
313
|
+
Message::HandshakeType::COMPRESSED_CERTIFICATE
|
314
|
+
ct, = transcript[CT] = [message, orig_msg]
|
315
|
+
terminate(:bad_certificate) \
|
316
|
+
if ct.is_a?(Message::CompressedCertificate) &&
|
317
|
+
!@settings[:compress_certificate_algorithms]
|
318
|
+
.include?(ct.algorithm)
|
319
|
+
|
320
|
+
ct = ct.certificate_message \
|
321
|
+
if ct.is_a?(Message::CompressedCertificate)
|
322
|
+
alert = check_invalid_certificate(ct, transcript[CH].first)
|
306
323
|
terminate(alert) unless alert.nil?
|
307
324
|
|
308
325
|
@state = ClientState::WAIT_CV
|
309
|
-
|
310
|
-
transcript[CR] = message
|
326
|
+
when Message::HandshakeType::CERTIFICATE_REQUEST
|
327
|
+
transcript[CR] = [message, orig_msg]
|
311
328
|
# TODO: client authentication
|
312
329
|
@state = ClientState::WAIT_CERT
|
313
330
|
else
|
@@ -316,46 +333,56 @@ module TTTLS13
|
|
316
333
|
when ClientState::WAIT_CERT
|
317
334
|
logger.debug('ClientState::WAIT_CERT')
|
318
335
|
|
319
|
-
ct = transcript[CT] = recv_certificate(hs_rcipher)
|
320
|
-
|
336
|
+
ct, = transcript[CT] = recv_certificate(hs_rcipher)
|
337
|
+
if ct.is_a?(Message::CompressedCertificate) &&
|
338
|
+
!@settings[:compress_certificate_algorithms].include?(ct.algorithm)
|
339
|
+
terminate(:bad_certificate)
|
340
|
+
elsif ct.is_a?(Message::CompressedCertificate)
|
341
|
+
ct = ct.certificate_message
|
342
|
+
end
|
343
|
+
|
344
|
+
alert = check_invalid_certificate(ct, transcript[CH].first)
|
321
345
|
terminate(alert) unless alert.nil?
|
322
346
|
|
323
347
|
@state = ClientState::WAIT_CV
|
324
348
|
when ClientState::WAIT_CV
|
325
349
|
logger.debug('ClientState::WAIT_CV')
|
326
350
|
|
327
|
-
cv = transcript[CV] = recv_certificate_verify(hs_rcipher)
|
351
|
+
cv, = transcript[CV] = recv_certificate_verify(hs_rcipher)
|
328
352
|
digest = CipherSuite.digest(@cipher_suite)
|
329
353
|
hash = transcript.hash(digest, CT)
|
354
|
+
ct, = transcript[CT]
|
355
|
+
ct = ct.certificate_message \
|
356
|
+
if ct.is_a?(Message::CompressedCertificate)
|
330
357
|
terminate(:decrypt_error) \
|
331
|
-
unless verified_certificate_verify?(
|
358
|
+
unless verified_certificate_verify?(ct, cv, hash)
|
332
359
|
|
333
360
|
@signature_scheme = cv.signature_scheme
|
334
|
-
|
335
361
|
@state = ClientState::WAIT_FINISHED
|
336
362
|
when ClientState::WAIT_FINISHED
|
337
363
|
logger.debug('ClientState::WAIT_FINISHED')
|
338
364
|
|
339
|
-
sf = transcript[SF] = recv_finished(hs_rcipher)
|
365
|
+
sf, = transcript[SF] = recv_finished(hs_rcipher)
|
340
366
|
digest = CipherSuite.digest(@cipher_suite)
|
341
|
-
|
367
|
+
terminate(:decrypt_error) unless verified_finished?(
|
342
368
|
finished: sf,
|
343
369
|
digest: digest,
|
344
370
|
finished_key: key_schedule.server_finished_key,
|
345
371
|
hash: transcript.hash(digest, CV)
|
346
372
|
)
|
347
|
-
terminate(:decrypt_error) unless verified
|
348
|
-
|
349
|
-
transcript[EOED] = send_eoed(e_wcipher) \
|
350
|
-
if use_early_data? && succeed_early_data?
|
351
373
|
|
374
|
+
if use_early_data? && succeed_early_data?
|
375
|
+
eoed = send_eoed(e_wcipher)
|
376
|
+
transcript[EOED] = [eoed, eoed.serialize]
|
377
|
+
end
|
352
378
|
# TODO: Send Certificate [+ CertificateVerify]
|
353
379
|
signature = sign_finished(
|
354
380
|
digest: digest,
|
355
381
|
finished_key: key_schedule.client_finished_key,
|
356
382
|
hash: transcript.hash(digest, EOED)
|
357
383
|
)
|
358
|
-
|
384
|
+
cf = send_finished(signature, hs_wcipher)
|
385
|
+
transcript[CF] = [cf, cf.serialize]
|
359
386
|
@alert_wcipher = @ap_wcipher = gen_cipher(
|
360
387
|
@cipher_suite,
|
361
388
|
key_schedule.client_application_write_key,
|
@@ -578,6 +605,14 @@ module TTTLS13
|
|
578
605
|
exs << Message::Extension::OCSPStatusRequest.new \
|
579
606
|
if @settings[:check_certificate_status]
|
580
607
|
|
608
|
+
# compress_certificate
|
609
|
+
if !@settings[:compress_certificate_algorithms].nil? &&
|
610
|
+
!@settings[:compress_certificate_algorithms].empty?
|
611
|
+
exs << Message::Extension::CompressCertificate.new(
|
612
|
+
@settings[:compress_certificate_algorithms]
|
613
|
+
)
|
614
|
+
end
|
615
|
+
|
581
616
|
[exs, priv_keys]
|
582
617
|
end
|
583
618
|
# rubocop: enable Metrics/AbcSize
|
@@ -735,11 +770,15 @@ module TTTLS13
|
|
735
770
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
736
771
|
#
|
737
772
|
# @return [TTTLS13::Message::ServerHello]
|
773
|
+
# @return [String]
|
738
774
|
def recv_server_hello
|
739
|
-
sh = recv_message(
|
775
|
+
sh, orig_msg = recv_message(
|
776
|
+
receivable_ccs: true,
|
777
|
+
cipher: Cryptograph::Passer.new
|
778
|
+
)
|
740
779
|
terminate(:unexpected_message) unless sh.is_a?(Message::ServerHello)
|
741
780
|
|
742
|
-
sh
|
781
|
+
[sh, orig_msg]
|
743
782
|
end
|
744
783
|
|
745
784
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
@@ -747,12 +786,13 @@ module TTTLS13
|
|
747
786
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
748
787
|
#
|
749
788
|
# @return [TTTLS13::Message::EncryptedExtensions]
|
789
|
+
# @return [String]
|
750
790
|
def recv_encrypted_extensions(cipher)
|
751
|
-
ee = recv_message(receivable_ccs: true, cipher: cipher)
|
791
|
+
ee, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
|
752
792
|
terminate(:unexpected_message) \
|
753
793
|
unless ee.is_a?(Message::EncryptedExtensions)
|
754
794
|
|
755
|
-
ee
|
795
|
+
[ee, orig_msg]
|
756
796
|
end
|
757
797
|
|
758
798
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
@@ -760,11 +800,12 @@ module TTTLS13
|
|
760
800
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
761
801
|
#
|
762
802
|
# @return [TTTLS13::Message::Certificate]
|
803
|
+
# @return [String]
|
763
804
|
def recv_certificate(cipher)
|
764
|
-
ct = recv_message(receivable_ccs: true, cipher: cipher)
|
805
|
+
ct, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
|
765
806
|
terminate(:unexpected_message) unless ct.is_a?(Message::Certificate)
|
766
807
|
|
767
|
-
ct
|
808
|
+
[ct, orig_msg]
|
768
809
|
end
|
769
810
|
|
770
811
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
@@ -772,11 +813,12 @@ module TTTLS13
|
|
772
813
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
773
814
|
#
|
774
815
|
# @return [TTTLS13::Message::CertificateVerify]
|
816
|
+
# @return [String]
|
775
817
|
def recv_certificate_verify(cipher)
|
776
|
-
cv = recv_message(receivable_ccs: true, cipher: cipher)
|
818
|
+
cv, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
|
777
819
|
terminate(:unexpected_message) unless cv.is_a?(Message::CertificateVerify)
|
778
820
|
|
779
|
-
cv
|
821
|
+
[cv, orig_msg]
|
780
822
|
end
|
781
823
|
|
782
824
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
@@ -784,11 +826,12 @@ module TTTLS13
|
|
784
826
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
785
827
|
#
|
786
828
|
# @return [TTTLS13::Message::Finished]
|
829
|
+
# @return [String]
|
787
830
|
def recv_finished(cipher)
|
788
|
-
sf = recv_message(receivable_ccs: true, cipher: cipher)
|
831
|
+
sf, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
|
789
832
|
terminate(:unexpected_message) unless sf.is_a?(Message::Finished)
|
790
833
|
|
791
|
-
sf
|
834
|
+
[sf, orig_msg]
|
792
835
|
end
|
793
836
|
|
794
837
|
# @param cipher [TTTLS13::Cryptograph::Aead]
|
data/lib/tttls1.3/connection.rb
CHANGED
@@ -16,7 +16,7 @@ module TTTLS13
|
|
16
16
|
@ap_wcipher = Cryptograph::Passer.new
|
17
17
|
@ap_rcipher = Cryptograph::Passer.new
|
18
18
|
@alert_wcipher = Cryptograph::Passer.new
|
19
|
-
@message_queue = [] # Array of TTTLS13::Message::$Object
|
19
|
+
@message_queue = [] # Array of [TTTLS13::Message::$Object, String]
|
20
20
|
@binary_buffer = '' # deposit Record.surplus_binary
|
21
21
|
@cipher_suite = nil # TTTLS13::CipherSuite
|
22
22
|
@named_group = nil # TTTLS13::NamedGroup
|
@@ -42,7 +42,7 @@ module TTTLS13
|
|
42
42
|
|
43
43
|
message = nil
|
44
44
|
loop do
|
45
|
-
message = recv_message(receivable_ccs: false, cipher: @ap_rcipher)
|
45
|
+
message, = recv_message(receivable_ccs: false, cipher: @ap_rcipher)
|
46
46
|
# At any time after the server has received the client Finished
|
47
47
|
# message, it MAY send a NewSessionTicket message.
|
48
48
|
break unless message.is_a?(Message::NewSessionTicket)
|
@@ -220,13 +220,15 @@ module TTTLS13
|
|
220
220
|
# @raise [TTTLS13::Error::ErrorAlerts
|
221
221
|
#
|
222
222
|
# @return [TTTLS13::Message::$Object]
|
223
|
+
# @return [String]
|
223
224
|
# rubocop: disable Metrics/CyclomaticComplexity
|
224
225
|
def recv_message(receivable_ccs:, cipher:)
|
225
226
|
return @message_queue.shift unless @message_queue.empty?
|
226
227
|
|
227
228
|
messages = nil
|
229
|
+
orig_msgs = []
|
228
230
|
loop do
|
229
|
-
record = recv_record(cipher)
|
231
|
+
record, orig_msgs = recv_record(cipher)
|
230
232
|
case record.type
|
231
233
|
when Message::ContentType::HANDSHAKE,
|
232
234
|
Message::ContentType::APPLICATION_DATA
|
@@ -243,20 +245,22 @@ module TTTLS13
|
|
243
245
|
end
|
244
246
|
end
|
245
247
|
|
246
|
-
@message_queue += messages[1..]
|
248
|
+
@message_queue += messages[1..].zip(orig_msgs[1..])
|
247
249
|
message = messages.first
|
250
|
+
orig_msg = orig_msgs.first
|
248
251
|
if message.is_a?(Message::Alert)
|
249
252
|
handle_received_alert(message)
|
250
253
|
return nil
|
251
254
|
end
|
252
255
|
|
253
|
-
message
|
256
|
+
[message, orig_msg]
|
254
257
|
end
|
255
258
|
# rubocop: enable Metrics/CyclomaticComplexity
|
256
259
|
|
257
260
|
# @param cipher [TTTLS13::Cryptograph::Aead, Passer]
|
258
261
|
#
|
259
262
|
# @return [TTTLS13::Message::Record]
|
263
|
+
# @return [Array of String]
|
260
264
|
def recv_record(cipher)
|
261
265
|
binary = @socket.read(5)
|
262
266
|
record_len = Convert.bin2i(binary.slice(3, 2))
|
@@ -264,9 +268,13 @@ module TTTLS13
|
|
264
268
|
|
265
269
|
begin
|
266
270
|
buffer = @binary_buffer
|
267
|
-
record = Message::Record.deserialize(
|
268
|
-
|
269
|
-
|
271
|
+
record, orig_msgs, surplus_binary = Message::Record.deserialize(
|
272
|
+
binary,
|
273
|
+
cipher,
|
274
|
+
buffer,
|
275
|
+
@recv_record_size
|
276
|
+
)
|
277
|
+
@binary_buffer = surplus_binary
|
270
278
|
rescue Error::ErrorAlerts => e
|
271
279
|
terminate(e.message.to_sym)
|
272
280
|
end
|
@@ -278,7 +286,7 @@ module TTTLS13
|
|
278
286
|
end
|
279
287
|
|
280
288
|
logger.debug("receive \n" + record.pretty_inspect)
|
281
|
-
record
|
289
|
+
[record, orig_msgs]
|
282
290
|
end
|
283
291
|
|
284
292
|
# @param ch1 [TTTLS13::Message::ClientHello]
|
@@ -292,11 +300,9 @@ module TTTLS13
|
|
292
300
|
# TODO: ext binder
|
293
301
|
hash_len = OpenSSL::Digest.new(digest).digest_length
|
294
302
|
tt = Transcript.new
|
295
|
-
tt.
|
296
|
-
|
297
|
-
|
298
|
-
CH => ch
|
299
|
-
)
|
303
|
+
tt[CH1] = [ch1, ch1.serialize] unless ch1.nil?
|
304
|
+
tt[HRR] = [hrr, hrr.serialize] unless hrr.nil?
|
305
|
+
tt[CH] = [ch, ch.serialize]
|
300
306
|
# transcript-hash (CH1 + HRR +) truncated-CH
|
301
307
|
hash = tt.truncate_hash(digest, CH, hash_len + 3)
|
302
308
|
OpenSSL::HMAC.digest(digest, binder_key, hash)
|
@@ -500,11 +506,10 @@ module TTTLS13
|
|
500
506
|
return false if san.nil?
|
501
507
|
|
502
508
|
ostr = OpenSSL::ASN1.decode(san.to_der).value.last
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
matching
|
509
|
+
OpenSSL::ASN1.decode(ostr.value)
|
510
|
+
.map(&:value)
|
511
|
+
.map { |s| s.gsub('.', '\.').gsub('*', '.*') }
|
512
|
+
.any? { |s| name.match(/#{s}/) }
|
508
513
|
end
|
509
514
|
|
510
515
|
# @param signature_algorithms [Array of SignatureAlgorithms]
|
@@ -18,6 +18,7 @@ module TTTLS13
|
|
18
18
|
ExtensionType::CLIENT_CERTIFICATE_TYPE,
|
19
19
|
ExtensionType::SERVER_CERTIFICATE_TYPE,
|
20
20
|
ExtensionType::PADDING,
|
21
|
+
ExtensionType::COMPRESS_CERTIFICATE,
|
21
22
|
ExtensionType::RECORD_SIZE_LIMIT,
|
22
23
|
ExtensionType::PWD_PROTECT,
|
23
24
|
ExtensionType::PWD_CLEAR,
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
using Refinements
|
6
|
+
module Message
|
7
|
+
class CompressedCertificate
|
8
|
+
attr_reader :msg_type
|
9
|
+
attr_reader :certificate_message
|
10
|
+
attr_reader :algorithm
|
11
|
+
|
12
|
+
# @param certificate_message [TTTLS13::Message::Certificate]
|
13
|
+
# @param algorithm [CertificateCompressionAlgorithm]
|
14
|
+
def initialize(certificate_message:, algorithm:)
|
15
|
+
@msg_type = HandshakeType::COMPRESSED_CERTIFICATE
|
16
|
+
@certificate_message = certificate_message
|
17
|
+
@algorithm = algorithm
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String]
|
21
|
+
def serialize
|
22
|
+
binary = ''
|
23
|
+
binary += @algorithm
|
24
|
+
ct_bin = @certificate_message.serialize[4..]
|
25
|
+
binary += ct_bin.length.to_uint24
|
26
|
+
case @algorithm
|
27
|
+
when Extension::CertificateCompressionAlgorithm::ZLIB
|
28
|
+
binary += Zlib::Deflate.deflate(ct_bin).prefix_uint24_length
|
29
|
+
else # TODO: orig_msgs, ZSTD
|
30
|
+
raise Error::ErrorAlerts, :internal_error
|
31
|
+
end
|
32
|
+
|
33
|
+
@msg_type + binary.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::CompressedCertificate]
|
43
|
+
# rubocop: disable Metrics/AbcSize
|
44
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
45
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
46
|
+
def self.deserialize(binary)
|
47
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
48
|
+
raise Error::ErrorAlerts, :decode_error if binary.length < 5
|
49
|
+
raise Error::ErrorAlerts, :internal_error \
|
50
|
+
unless binary[0] == HandshakeType::COMPRESSED_CERTIFICATE
|
51
|
+
|
52
|
+
msg_len = Convert.bin2i(binary.slice(1, 3))
|
53
|
+
algorithm = binary.slice(4, 2)
|
54
|
+
uncompressed_length = Convert.bin2i(binary.slice(6, 3))
|
55
|
+
ccm_len = Convert.bin2i(binary.slice(9, 3))
|
56
|
+
ct_bin = ''
|
57
|
+
case algorithm
|
58
|
+
when Extension::CertificateCompressionAlgorithm::ZLIB
|
59
|
+
ct_bin = Zlib::Inflate.inflate(binary.slice(12, ccm_len))
|
60
|
+
else # TODO: BROTLI, ZSTD
|
61
|
+
raise Error::ErrorAlerts, :bad_certificate
|
62
|
+
end
|
63
|
+
|
64
|
+
raise Error::ErrorAlerts, :bad_certificate \
|
65
|
+
unless ct_bin.length == uncompressed_length
|
66
|
+
raise Error::ErrorAlerts, :decode_error \
|
67
|
+
unless ccm_len + 12 == binary.length && msg_len + 4 == binary.length
|
68
|
+
|
69
|
+
certificate_message = Certificate.deserialize(
|
70
|
+
HandshakeType::CERTIFICATE + ct_bin.prefix_uint24_length
|
71
|
+
)
|
72
|
+
CompressedCertificate.new(
|
73
|
+
certificate_message: certificate_message,
|
74
|
+
algorithm: algorithm
|
75
|
+
)
|
76
|
+
end
|
77
|
+
# rubocop: enable Metrics/AbcSize
|
78
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
79
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -27,9 +27,12 @@ module TTTLS13
|
|
27
27
|
|
28
28
|
# @return [String]
|
29
29
|
def serialize
|
30
|
-
binary = @protocol_name_list
|
30
|
+
binary = @protocol_name_list
|
31
|
+
.map(&:prefix_uint8_length)
|
32
|
+
.join
|
33
|
+
.prefix_uint16_length
|
31
34
|
|
32
|
-
@extension_type + binary.prefix_uint16_length
|
35
|
+
@extension_type + binary.prefix_uint16_length
|
33
36
|
end
|
34
37
|
|
35
38
|
# @param binary [String]
|
@@ -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
|
+
module CertificateCompressionAlgorithm
|
9
|
+
ZLIB = "\x00\x01"
|
10
|
+
# BROTLI = "\x00\x02" # UNSUPPORTED
|
11
|
+
# ZSTD = "\x00\x03" # UNSUPPORTED
|
12
|
+
end
|
13
|
+
|
14
|
+
# https://tools.ietf.org/html/rfc8879
|
15
|
+
class CompressCertificate
|
16
|
+
attr_reader :extension_type
|
17
|
+
attr_reader :algorithms
|
18
|
+
|
19
|
+
# @param algorithms [Array of CertificateCompressionAlgorithm]
|
20
|
+
#
|
21
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# CompressCertificate([CertificateCompressionAlgorithm::ZLIB])
|
25
|
+
def initialize(algorithms)
|
26
|
+
@extension_type = ExtensionType::COMPRESS_CERTIFICATE
|
27
|
+
@algorithms = algorithms || []
|
28
|
+
raise Error::ErrorAlerts, :internal_error \
|
29
|
+
if @algorithms.join.length < 2 ||
|
30
|
+
@algorithms.join.length > 2**8 - 2
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [String]
|
34
|
+
def serialize
|
35
|
+
binary = @algorithms.join.prefix_uint8_length
|
36
|
+
|
37
|
+
@extension_type + binary.prefix_uint16_length
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param binary [String]
|
41
|
+
#
|
42
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
43
|
+
#
|
44
|
+
# @return [TTTLS13::Message::Extension::CompressCertificate, nil]
|
45
|
+
def self.deserialize(binary)
|
46
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
47
|
+
|
48
|
+
return nil if binary.length < 3
|
49
|
+
|
50
|
+
al_len = Convert.bin2i(binary.slice(0, 1))
|
51
|
+
return nil if binary.length != al_len + 1
|
52
|
+
|
53
|
+
CompressCertificate.new(binary.slice(1, al_len + 1).scan(/.{2}/m))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -35,9 +35,9 @@ module TTTLS13
|
|
35
35
|
|
36
36
|
# @return [String]
|
37
37
|
def serialize
|
38
|
-
binary = @supported_signature_algorithms.join
|
38
|
+
binary = @supported_signature_algorithms.join.prefix_uint16_length
|
39
39
|
|
40
|
-
@extension_type + binary.prefix_uint16_length
|
40
|
+
@extension_type + binary.prefix_uint16_length
|
41
41
|
end
|
42
42
|
|
43
43
|
# @param binary [String]
|
@@ -21,9 +21,9 @@ module TTTLS13
|
|
21
21
|
|
22
22
|
# @return [String]
|
23
23
|
def serialize
|
24
|
-
binary = @named_group_list.join
|
24
|
+
binary = @named_group_list.join.prefix_uint16_length
|
25
25
|
|
26
|
-
@extension_type + binary.prefix_uint16_length
|
26
|
+
@extension_type + binary.prefix_uint16_length
|
27
27
|
end
|
28
28
|
|
29
29
|
# @param binary [String]
|
@@ -121,6 +121,7 @@ module TTTLS13
|
|
121
121
|
# @return [TTTLS13::Message::Extension::$Object, nil]
|
122
122
|
# rubocop: disable Metrics/CyclomaticComplexity
|
123
123
|
# rubocop: disable Metrics/MethodLength
|
124
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
124
125
|
def deserialize_extension(binary, extension_type, msg_type)
|
125
126
|
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
126
127
|
|
@@ -143,6 +144,8 @@ module TTTLS13
|
|
143
144
|
Extension::SignatureAlgorithms.deserialize(binary)
|
144
145
|
when ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
|
145
146
|
Extension::Alpn.deserialize(binary)
|
147
|
+
when ExtensionType::COMPRESS_CERTIFICATE
|
148
|
+
Extension::CompressCertificate.deserialize(binary)
|
146
149
|
when ExtensionType::RECORD_SIZE_LIMIT
|
147
150
|
Extension::RecordSizeLimit.deserialize(binary)
|
148
151
|
when ExtensionType::PRE_SHARED_KEY
|
@@ -165,6 +168,7 @@ module TTTLS13
|
|
165
168
|
end
|
166
169
|
# rubocop: enable Metrics/CyclomaticComplexity
|
167
170
|
# rubocop: enable Metrics/MethodLength
|
171
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
168
172
|
end
|
169
173
|
end
|
170
174
|
end
|