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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba824030b1a295566777d4d12c35e259d379bc9a830b0cb95792356cc547a436
4
- data.tar.gz: c3f2e8fd07567133cce7e8f24fcaf02499b8cceff6088ca403ca2e9be9e5ab5f
3
+ metadata.gz: 7006bce3031f6232ae949b44eb31111562d581e359769952f48a537848d50418
4
+ data.tar.gz: d332e823eb8c677ff534e46a87e96c039aad2d5538c0f823ac7eb365f372ca88
5
5
  SHA512:
6
- metadata.gz: 00e939bf927db1923274985cdad6526d16b6d99216f62ff3978b3bae9cdcedf675482b96f49a224f9f982f1d8c6f7ca0b49f282086422e73e93b4b74d13f0722
7
- data.tar.gz: 1e523ad0d29d6f29dbd94388f9f895abf69324ceb4336406b13e21793395abdf795a0f043ee89899e888b851d85f2190064e93b70efec092964e34c94b15ac76
6
+ metadata.gz: ca37fd2570b905759da152932eb2e6f29c3e37f59135353b9e3cfbce0c683f7493fdd360f9a56c50f2ab252f0460728c09585d018d04b8668de4000b1567d249
7
+ data.tar.gz: 6e37405f3034de1fe648f0882bbc9d9b65baeac8d1ef142fc776010b8480cd987a87060edf6ae85fd20327afc5327aacb23318e460f510259c99056aa76fb389
@@ -13,7 +13,7 @@ jobs:
13
13
  runs-on: ubuntu-latest
14
14
  strategy:
15
15
  matrix:
16
- ruby-version: ['2.6.x', '2.7.x']
16
+ ruby-version: ['2.6.x', '2.7.x', '3.0.x']
17
17
  steps:
18
18
  - uses: docker://thekuwayama/openssl:latest
19
19
  - name: Set up Ruby
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
 
@@ -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
- transcript[CH] = send_client_hello(extensions, binder_key)
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
- ch1 = transcript[CH1] = transcript.delete(CH)
216
- hrr = transcript[HRR] = transcript.delete(SH)
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
- transcript[CH] = send_new_client_hello(ch1, hrr, extensions,
239
- binder_key)
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(receivable_ccs: true, cipher: hs_rcipher)
303
- if message.msg_type == Message::HandshakeType::CERTIFICATE
304
- ct = transcript[CT] = message
305
- alert = check_invalid_certificate(ct, transcript[CH])
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
- elsif message.msg_type == Message::HandshakeType::CERTIFICATE_REQUEST
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
- alert = check_invalid_certificate(ct, transcript[CH])
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?(transcript[CT], cv, hash)
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
- verified = verified_finished?(
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
- transcript[CF] = send_finished(signature, hs_wcipher)
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(receivable_ccs: true, cipher: Cryptograph::Passer.new)
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]
@@ -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(binary, cipher, buffer,
268
- @recv_record_size)
269
- @binary_buffer = record.surplus_binary
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.merge!(
296
- CH1 => ch1,
297
- HRR => hrr,
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
- matching = OpenSSL::ASN1.decode(ostr.value).map(&:value)
504
- .map { |s| s.gsub('.', '\.').gsub('*', '.*') }
505
- .any? { |s| name.match(/#{s}/) }
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.map(&:prefix_uint8_length).join
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.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.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.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