tttls1.3 0.2.12 → 0.2.16

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +32 -0
  3. data/.rubocop.yml +2 -2
  4. data/Gemfile +3 -4
  5. data/README.md +4 -1
  6. data/example/helper.rb +3 -3
  7. data/example/https_client.rb +1 -1
  8. data/example/https_client_using_0rtt.rb +2 -2
  9. data/example/https_client_using_hrr.rb +1 -1
  10. data/example/https_client_using_hrr_and_ticket.rb +2 -2
  11. data/example/https_client_using_status_request.rb +2 -2
  12. data/example/https_client_using_ticket.rb +2 -2
  13. data/example/https_server.rb +2 -2
  14. data/interop/client_spec.rb +6 -6
  15. data/interop/server_spec.rb +6 -6
  16. data/lib/tttls1.3/client.rb +106 -65
  17. data/lib/tttls1.3/connection.rb +43 -30
  18. data/lib/tttls1.3/cryptograph/aead.rb +20 -7
  19. data/lib/tttls1.3/cryptograph.rb +1 -1
  20. data/lib/tttls1.3/message/alert.rb +2 -2
  21. data/lib/tttls1.3/message/client_hello.rb +1 -0
  22. data/lib/tttls1.3/message/compressed_certificate.rb +82 -0
  23. data/lib/tttls1.3/message/extension/alpn.rb +5 -2
  24. data/lib/tttls1.3/message/extension/compress_certificate.rb +58 -0
  25. data/lib/tttls1.3/message/extension/signature_algorithms.rb +15 -5
  26. data/lib/tttls1.3/message/extension/signature_algorithms_cert.rb +5 -4
  27. data/lib/tttls1.3/message/extension/supported_groups.rb +2 -2
  28. data/lib/tttls1.3/message/extensions.rb +31 -18
  29. data/lib/tttls1.3/message/record.rb +28 -16
  30. data/lib/tttls1.3/message.rb +23 -21
  31. data/lib/tttls1.3/server.rb +88 -37
  32. data/lib/tttls1.3/transcript.rb +3 -7
  33. data/lib/tttls1.3/version.rb +1 -1
  34. data/spec/client_spec.rb +28 -19
  35. data/spec/compress_certificate_spec.rb +54 -0
  36. data/spec/connection_spec.rb +22 -15
  37. data/spec/extensions_spec.rb +16 -0
  38. data/spec/fixtures/rsa_rsa.crt +15 -15
  39. data/spec/fixtures/rsa_rsa.key +25 -25
  40. data/spec/key_schedule_spec.rb +48 -25
  41. data/spec/record_spec.rb +2 -2
  42. data/spec/server_hello_spec.rb +1 -1
  43. data/spec/server_spec.rb +23 -11
  44. data/spec/signature_algorithms_cert_spec.rb +4 -0
  45. data/spec/signature_algorithms_spec.rb +4 -0
  46. data/spec/spec_helper.rb +4 -0
  47. data/spec/transcript_spec.rb +34 -20
  48. data/tttls1.3.gemspec +0 -1
  49. metadata +11 -7
  50. data/.github/workflows/main.yml +0 -25
@@ -44,6 +44,11 @@ module TTTLS13
44
44
  ].freeze
45
45
  private_constant :DEFAULT_SP_NAMED_GROUP_LIST
46
46
 
47
+ DEFAULT_SP_COMPRESS_CERTIFICATE_ALGORITHMS = [
48
+ Message::Extension::CertificateCompressionAlgorithm::ZLIB
49
+ ].freeze
50
+ private_constant :DEFAULT_SP_COMPRESS_CERTIFICATE_ALGORITHMS
51
+
47
52
  DEFAULT_SERVER_SETTINGS = {
48
53
  crt_file: nil,
49
54
  chain_files: nil,
@@ -52,6 +57,8 @@ module TTTLS13
52
57
  signature_algorithms: DEFAULT_SP_SIGNATURE_ALGORITHMS,
53
58
  supported_groups: DEFAULT_SP_NAMED_GROUP_LIST,
54
59
  alpn: nil,
60
+ process_ocsp_response: nil,
61
+ compress_certificate_algorithms: DEFAULT_SP_COMPRESS_CERTIFICATE_ALGORITHMS,
55
62
  compatibility_mode: true,
56
63
  loglevel: Logger::WARN
57
64
  }.freeze
@@ -139,7 +146,6 @@ module TTTLS13
139
146
  transcript = Transcript.new
140
147
  key_schedule = nil # TTTLS13::KeySchedule
141
148
  priv_key = nil # OpenSSL::PKey::$Object
142
-
143
149
  hs_wcipher = nil # TTTLS13::Cryptograph::$Object
144
150
  hs_rcipher = nil # TTTLS13::Cryptograph::$Object
145
151
 
@@ -150,7 +156,7 @@ module TTTLS13
150
156
  logger.debug('ServerState::START')
151
157
 
152
158
  receivable_ccs = transcript.include?(CH1)
153
- ch = transcript[CH] = recv_client_hello(receivable_ccs)
159
+ ch, = transcript[CH] = recv_client_hello(receivable_ccs)
154
160
 
155
161
  # support only TLS 1.3
156
162
  terminate(:protocol_version) unless ch.negotiated_tls_1_3?
@@ -182,7 +188,7 @@ module TTTLS13
182
188
  logger.debug('ServerState::RECVD_CH')
183
189
 
184
190
  # select parameters
185
- ch = transcript[CH]
191
+ ch, = transcript[CH]
186
192
  @cipher_suite = select_cipher_suite(ch)
187
193
  @named_group = select_named_group(ch)
188
194
  @signature_scheme = select_signature_scheme(ch, @crt)
@@ -191,8 +197,9 @@ module TTTLS13
191
197
 
192
198
  # send HRR
193
199
  if @named_group.nil?
194
- ch1 = transcript[CH1] = transcript.delete(CH)
195
- transcript[HRR] = send_hello_retry_request(ch1, @cipher_suite)
200
+ ch1, = transcript[CH1] = transcript.delete(CH)
201
+ hrr = send_hello_retry_request(ch1, @cipher_suite)
202
+ transcript[HRR] = [hrr, hrr.serialize]
196
203
  @state = ServerState::START
197
204
  next
198
205
  end
@@ -200,10 +207,14 @@ module TTTLS13
200
207
  when ServerState::NEGOTIATED
201
208
  logger.debug('ServerState::NEGOTIATED')
202
209
 
203
- ch = transcript[CH]
210
+ ch, = transcript[CH]
204
211
  extensions, priv_key = gen_sh_extensions(@named_group)
205
- transcript[SH] = send_server_hello(extensions, @cipher_suite,
206
- ch.legacy_session_id)
212
+ sh = send_server_hello(
213
+ extensions,
214
+ @cipher_suite,
215
+ ch.legacy_session_id
216
+ )
217
+ transcript[SH] = [sh, sh.serialize]
207
218
  send_ccs if @settings[:compatibility_mode]
208
219
 
209
220
  # generate shared secret
@@ -234,25 +245,30 @@ module TTTLS13
234
245
  when ServerState::WAIT_FLIGHT2
235
246
  logger.debug('ServerState::WAIT_FLIGHT2')
236
247
 
237
- ch = transcript[CH]
248
+ ch, = transcript[CH]
238
249
  rsl = @send_record_size \
239
- unless ch.extensions[Message::ExtensionType::RECORD_SIZE_LIMIT].nil?
240
- ee = transcript[EE] = gen_encrypted_extensions(ch, @alpn, rsl)
250
+ if ch.extensions.include?(Message::ExtensionType::RECORD_SIZE_LIMIT)
251
+ ee = gen_encrypted_extensions(ch, @alpn, rsl)
252
+ transcript[EE] = [ee, ee.serialize]
241
253
  # TODO: [Send CertificateRequest]
242
- ct = transcript[CT] = gen_certificate(@crt, @chain)
254
+
255
+ # status_request
256
+ ocsp_response = fetch_ocsp_response \
257
+ if ch.extensions.include?(Message::ExtensionType::STATUS_REQUEST)
258
+ ct = gen_certificate(@crt, ch, @chain, ocsp_response)
259
+ transcript[CT] = [ct, ct.serialize]
243
260
  digest = CipherSuite.digest(@cipher_suite)
244
- cv = transcript[CV] = gen_certificate_verify(
245
- @key,
246
- @signature_scheme,
247
- transcript.hash(digest, CT)
248
- )
261
+ hash = transcript.hash(digest, CT)
262
+ cv = gen_certificate_verify(@key, @signature_scheme, hash)
263
+ transcript[CV] = [cv, cv.serialize]
249
264
  finished_key = key_schedule.server_finished_key
250
265
  signature = sign_finished(
251
266
  digest: digest,
252
267
  finished_key: finished_key,
253
268
  hash: transcript.hash(digest, CV)
254
269
  )
255
- sf = transcript[SF] = Message::Finished.new(signature)
270
+ sf = Message::Finished.new(signature)
271
+ transcript[SF] = [sf, sf.serialize]
256
272
  send_server_parameters([ee, ct, cv, sf], hs_wcipher)
257
273
  @state = ServerState::WAIT_FINISHED
258
274
  when ServerState::WAIT_CERT
@@ -262,7 +278,7 @@ module TTTLS13
262
278
  when ServerState::WAIT_FINISHED
263
279
  logger.debug('ServerState::WAIT_FINISHED')
264
280
 
265
- cf = transcript[CF] = recv_finished(hs_rcipher)
281
+ cf, = transcript[CF] = recv_finished(hs_rcipher)
266
282
  digest = CipherSuite.digest(@cipher_suite)
267
283
  verified = verified_finished?(
268
284
  finished: cf,
@@ -320,12 +336,15 @@ module TTTLS13
320
336
  # @raise [TTTLS13::Error::ErrorAlerts]
321
337
  #
322
338
  # @return [TTTLS13::Message::ClientHello]
339
+ # @return [String]
323
340
  def recv_client_hello(receivable_ccs)
324
- ch = recv_message(receivable_ccs: receivable_ccs,
325
- cipher: Cryptograph::Passer.new)
341
+ ch, orig_msg = recv_message(
342
+ receivable_ccs: receivable_ccs,
343
+ cipher: Cryptograph::Passer.new
344
+ )
326
345
  terminate(:unexpected_message) unless ch.is_a?(Message::ClientHello)
327
346
 
328
- ch
347
+ [ch, orig_msg]
329
348
  end
330
349
 
331
350
  # @param extensions [TTTLS13::Message::Extensions]
@@ -350,7 +369,7 @@ module TTTLS13
350
369
  #
351
370
  # @return [TTTLS13::Message::ServerHello]
352
371
  def send_hello_retry_request(ch1, cipher_suite)
353
- exs = []
372
+ exs = Message::Extensions.new
354
373
  # supported_versions
355
374
  exs << Message::Extension::SupportedVersions.new(
356
375
  msg_type: Message::HandshakeType::SERVER_HELLO
@@ -372,7 +391,7 @@ module TTTLS13
372
391
  random: Message::HRR_RANDOM,
373
392
  legacy_session_id_echo: ch1.legacy_session_id,
374
393
  cipher_suite: cipher_suite,
375
- extensions: Message::Extensions.new(exs)
394
+ extensions: exs
376
395
  )
377
396
  send_handshakes(Message::ContentType::HANDSHAKE, [sh],
378
397
  Cryptograph::Passer.new)
@@ -403,14 +422,39 @@ module TTTLS13
403
422
  end
404
423
 
405
424
  # @param crt [OpenSSL::X509::Certificate]
425
+ # @param ch [TTTLS13::Message::ClientHell]
406
426
  # @param chain [Array of OpenSSL::X509::Certificate]
427
+ # @param ocsp_response [OpenSSL::OCSP::Response]
407
428
  #
408
- # @return [TTTLS13::Message::Certificate, nil]
409
- def gen_certificate(crt, chain = [])
410
- ces = [crt] + (chain || [])
411
- ces.map! { |c| Message::CertificateEntry.new(c) }
412
- Message::Certificate.new(certificate_list: ces)
429
+ # @return [TTTLS13::Message::Certificate, CompressedCertificate, nil]
430
+ # rubocop: disable Metrics/CyclomaticComplexity
431
+ def gen_certificate(crt, ch, chain = [], ocsp_response = nil)
432
+ exs = Message::Extensions.new
433
+ # status_request
434
+ exs << Message::Extension::OCSPResponse.new(ocsp_response) \
435
+ unless ocsp_response.nil?
436
+ ces = [Message::CertificateEntry.new(crt, exs)] \
437
+ + (chain || []).map { |c| Message::CertificateEntry.new(c) }
438
+ ct = Message::Certificate.new(certificate_list: ces)
439
+
440
+ # compress_certificate
441
+ cc = ch.extensions[Message::ExtensionType::COMPRESS_CERTIFICATE]
442
+ if !cc.nil? && !cc.algorithms.empty?
443
+ cca = (@settings[:compress_certificate_algorithms] || []).find do |a|
444
+ cc.algorithms.include?(a)
445
+ end
446
+
447
+ unless cca.nil?
448
+ ct = Message::CompressedCertificate.new(
449
+ certificate_message: ct,
450
+ algorithm: cca
451
+ )
452
+ end
453
+ end
454
+
455
+ ct
413
456
  end
457
+ # rubocop: enable Metrics/CyclomaticComplexity
414
458
 
415
459
  # @param key [OpenSSL::PKey::PKey]
416
460
  # @param signature_scheme [TTTLS13::SignatureScheme]
@@ -432,11 +476,12 @@ module TTTLS13
432
476
  # @raise [TTTLS13::Error::ErrorAlerts]
433
477
  #
434
478
  # @return [TTTLS13::Message::Finished]
479
+ # @return [String]
435
480
  def recv_finished(cipher)
436
- cf = recv_message(receivable_ccs: true, cipher: cipher)
481
+ cf, orig_msg = recv_message(receivable_ccs: true, cipher: cipher)
437
482
  terminate(:unexpected_message) unless cf.is_a?(Message::Finished)
438
483
 
439
- cf
484
+ [cf, orig_msg]
440
485
  end
441
486
 
442
487
  # @param named_group [TTTLS13::NamedGroup]
@@ -444,7 +489,7 @@ module TTTLS13
444
489
  # @return [TTTLS13::Message::Extensions]
445
490
  # @return [OpenSSL::PKey::EC.$Object]
446
491
  def gen_sh_extensions(named_group)
447
- exs = []
492
+ exs = Message::Extensions.new
448
493
  # supported_versions: only TLS 1.3
449
494
  exs << Message::Extension::SupportedVersions.new(
450
495
  msg_type: Message::HandshakeType::SERVER_HELLO
@@ -455,7 +500,7 @@ module TTTLS13
455
500
  = Message::Extension::KeyShare.gen_sh_key_share(named_group)
456
501
  exs << key_share
457
502
 
458
- [Message::Extensions.new(exs), priv_key]
503
+ [exs, priv_key]
459
504
  end
460
505
 
461
506
  # @param ch [TTTLS13::Message::ClientHello]
@@ -464,15 +509,16 @@ module TTTLS13
464
509
  #
465
510
  # @return [TTTLS13::Message::Extensions]
466
511
  def gen_ee_extensions(ch, alpn, record_size_limit)
467
- exs = []
512
+ exs = Message::Extensions.new
468
513
 
469
514
  # server_name
470
515
  exs << Message::Extension::ServerName.new('') \
471
516
  if ch.extensions.include?(Message::ExtensionType::SERVER_NAME)
472
517
 
473
518
  # supported_groups
474
- exs \
475
- << Message::Extension::SupportedGroups.new(@settings[:supported_groups])
519
+ exs << Message::Extension::SupportedGroups.new(
520
+ @settings[:supported_groups]
521
+ )
476
522
 
477
523
  # alpn
478
524
  exs << Message::Extension::Alpn.new([alpn]) unless alpn.nil?
@@ -481,7 +527,7 @@ module TTTLS13
481
527
  exs << Message::Extension::RecordSizeLimit.new(record_size_limit) \
482
528
  unless record_size_limit.nil?
483
529
 
484
- Message::Extensions.new(exs)
530
+ exs
485
531
  end
486
532
 
487
533
  # @param key [OpenSSL::PKey::PKey]
@@ -543,6 +589,11 @@ module TTTLS13
543
589
 
544
590
  matching_san?(crt, server_name)
545
591
  end
592
+
593
+ # @return [OpenSSL::OCSP::Response, nil]
594
+ def fetch_ocsp_response
595
+ @settings[:process_ocsp_response]&.call
596
+ end
546
597
  end
547
598
  # rubocop: enable Metrics/ClassLength
548
599
  end
@@ -19,10 +19,6 @@ module TTTLS13
19
19
  CF = 12
20
20
 
21
21
  class Transcript < Hash
22
- def initialize
23
- super
24
- end
25
-
26
22
  alias super_include? include?
27
23
 
28
24
  # @param digest [String] name of digest algorithm
@@ -62,12 +58,12 @@ module TTTLS13
62
58
  exc_prefix = Message::HandshakeType::MESSAGE_HASH \
63
59
  + "\x00\x00" \
64
60
  + OpenSSL::Digest.new(digest).digest_length.to_uint8 \
65
- + OpenSSL::Digest.digest(digest, self[CH1].serialize) \
66
- + self[HRR].serialize
61
+ + OpenSSL::Digest.digest(digest, self[CH1].last) \
62
+ + self[HRR].last
67
63
  end
68
64
 
69
65
  messages = (CH..end_index).to_a.map do |m|
70
- include?(m) ? self[m].serialize : ''
66
+ include?(m) ? self[m].last : ''
71
67
  end
72
68
  exc_prefix + messages.join
73
69
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TTTLS13
4
- VERSION = '0.2.12'
4
+ VERSION = '0.2.16'
5
5
  end
data/spec/client_spec.rb CHANGED
@@ -11,7 +11,7 @@ RSpec.describe Client do
11
11
  client = Client.new(mock_socket, 'localhost')
12
12
  extensions, _priv_keys = client.send(:gen_ch_extensions)
13
13
  client.send(:send_client_hello, extensions)
14
- Record.deserialize(mock_socket.read, Cryptograph::Passer.new)
14
+ Record.deserialize(mock_socket.read, Cryptograph::Passer.new).first
15
15
  end
16
16
 
17
17
  it 'should send default ClientHello' do
@@ -37,7 +37,7 @@ RSpec.describe Client do
37
37
  + msg_len.to_uint16 \
38
38
  + TESTBINARY_SERVER_HELLO)
39
39
  client = Client.new(mock_socket, 'localhost')
40
- client.send(:recv_server_hello)
40
+ client.send(:recv_server_hello).first
41
41
  end
42
42
 
43
43
  it 'should receive ServerHello' do
@@ -65,20 +65,20 @@ RSpec.describe Client do
65
65
  end
66
66
 
67
67
  it 'should receive EncryptedExtensions' do
68
- message = client.send(:recv_encrypted_extensions, cipher)
68
+ message, = client.send(:recv_encrypted_extensions, cipher)
69
69
  expect(message.msg_type).to eq HandshakeType::ENCRYPTED_EXTENSIONS
70
70
  end
71
71
 
72
72
  it 'should receive Certificate' do
73
73
  client.send(:recv_encrypted_extensions, cipher) # to skip
74
- message = client.send(:recv_certificate, cipher)
74
+ message, = client.send(:recv_certificate, cipher)
75
75
  expect(message.msg_type).to eq HandshakeType::CERTIFICATE
76
76
  end
77
77
 
78
78
  it 'should receive CertificateVerify' do
79
79
  client.send(:recv_encrypted_extensions, cipher) # to skip
80
80
  client.send(:recv_certificate, cipher) # to skip
81
- message = client.send(:recv_certificate_verify, cipher)
81
+ message, = client.send(:recv_certificate_verify, cipher)
82
82
  expect(message.msg_type).to eq HandshakeType::CERTIFICATE_VERIFY
83
83
  end
84
84
 
@@ -86,7 +86,7 @@ RSpec.describe Client do
86
86
  client.send(:recv_encrypted_extensions, cipher) # to skip
87
87
  client.send(:recv_certificate, cipher) # to skip
88
88
  client.send(:recv_certificate_verify, cipher) # to skip
89
- message = client.send(:recv_finished, cipher)
89
+ message, = client.send(:recv_finished, cipher)
90
90
  expect(message.msg_type).to eq HandshakeType::FINISHED
91
91
  end
92
92
  end
@@ -97,14 +97,20 @@ RSpec.describe Client do
97
97
  end
98
98
 
99
99
  let(:transcript) do
100
+ ch = ClientHello.deserialize(TESTBINARY_CLIENT_HELLO)
101
+ sh = ServerHello.deserialize(TESTBINARY_SERVER_HELLO)
102
+ ee = EncryptedExtensions.deserialize(TESTBINARY_ENCRYPTED_EXTENSIONS)
103
+ ct = Certificate.deserialize(TESTBINARY_CERTIFICATE)
104
+ cv = CertificateVerify.deserialize(TESTBINARY_CERTIFICATE_VERIFY)
105
+ sf = Finished.deserialize(TESTBINARY_SERVER_FINISHED)
100
106
  transcript = Transcript.new
101
107
  transcript.merge!(
102
- CH => ClientHello.deserialize(TESTBINARY_CLIENT_HELLO),
103
- SH => ServerHello.deserialize(TESTBINARY_SERVER_HELLO),
104
- EE => EncryptedExtensions.deserialize(TESTBINARY_ENCRYPTED_EXTENSIONS),
105
- CT => Certificate.deserialize(TESTBINARY_CERTIFICATE),
106
- CV => CertificateVerify.deserialize(TESTBINARY_CERTIFICATE_VERIFY),
107
- SF => Finished.deserialize(TESTBINARY_SERVER_FINISHED)
108
+ CH => [ch, TESTBINARY_CLIENT_HELLO],
109
+ SH => [sh, TESTBINARY_SERVER_HELLO],
110
+ EE => [ee, TESTBINARY_ENCRYPTED_EXTENSIONS],
111
+ CT => [ct, TESTBINARY_CERTIFICATE],
112
+ CV => [cv, TESTBINARY_CERTIFICATE_VERIFY],
113
+ SF => [sf, TESTBINARY_SERVER_FINISHED]
108
114
  )
109
115
  transcript
110
116
  end
@@ -140,7 +146,7 @@ RSpec.describe Client do
140
146
  write_iv: TESTBINARY_CLIENT_FINISHED_WRITE_IV,
141
147
  sequence_number: SequenceNumber.new
142
148
  )
143
- Record.deserialize(mock_socket.read, hs_rcipher)
149
+ Record.deserialize(mock_socket.read, hs_rcipher).first
144
150
  end
145
151
 
146
152
  it 'should send Finished' do
@@ -170,14 +176,17 @@ RSpec.describe Client do
170
176
  end
171
177
 
172
178
  let(:transcript) do
179
+ ch = ClientHello.deserialize(TESTBINARY_CLIENT_HELLO)
180
+ sh = ServerHello.deserialize(TESTBINARY_SERVER_HELLO)
181
+ ee = EncryptedExtensions.deserialize(TESTBINARY_ENCRYPTED_EXTENSIONS)
173
182
  transcript = Transcript.new
174
183
  transcript.merge!(
175
- CH => ClientHello.deserialize(TESTBINARY_CLIENT_HELLO),
176
- SH => ServerHello.deserialize(TESTBINARY_SERVER_HELLO),
177
- EE => EncryptedExtensions.deserialize(TESTBINARY_ENCRYPTED_EXTENSIONS),
178
- CT => ct,
179
- CV => cv,
180
- SF => sf
184
+ CH => [ch, TESTBINARY_CLIENT_HELLO],
185
+ SH => [sh, TESTBINARY_SERVER_HELLO],
186
+ EE => [ee, TESTBINARY_ENCRYPTED_EXTENSIONS],
187
+ CT => [ct, TESTBINARY_CERTIFICATE],
188
+ CV => [cv, TESTBINARY_CERTIFICATE_VERIFY],
189
+ SF => [sf, TESTBINARY_SERVER_FINISHED]
181
190
  )
182
191
  end
183
192
 
@@ -0,0 +1,54 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'spec_helper'
5
+ using Refinements
6
+
7
+ RSpec.describe Alpn do
8
+ context 'valid compress_certificate' do
9
+ let(:algorithms) do
10
+ [CertificateCompressionAlgorithm::ZLIB]
11
+ end
12
+
13
+ let(:extension) do
14
+ CompressCertificate.new(algorithms)
15
+ end
16
+
17
+ it 'should be generated' do
18
+ expect(extension.extension_type)
19
+ .to eq ExtensionType::COMPRESS_CERTIFICATE
20
+ expect(extension.algorithms).to eq algorithms
21
+ end
22
+
23
+ it 'should be serialized' do
24
+ expect(extension.serialize)
25
+ .to eq ExtensionType::COMPRESS_CERTIFICATE \
26
+ + 3.to_uint16 \
27
+ + 2.to_uint8 \
28
+ + "\x00\x01"
29
+ end
30
+ end
31
+
32
+ context 'invalid compress_certificate, empty,' do
33
+ let(:extension) do
34
+ CompressCertificate.new([])
35
+ end
36
+
37
+ it 'should not be generated' do
38
+ expect { extension }.to raise_error(ErrorAlerts)
39
+ end
40
+ end
41
+
42
+ context 'valid compress_certificate binary' do
43
+ let(:extension) do
44
+ CompressCertificate.deserialize(TESTBINARY_COMPRESS_CERTIFICATE)
45
+ end
46
+
47
+ it 'should generate valid object' do
48
+ expect(extension.extension_type)
49
+ .to eq ExtensionType::COMPRESS_CERTIFICATE
50
+ expect(extension.algorithms)
51
+ .to eq [CertificateCompressionAlgorithm::ZLIB]
52
+ end
53
+ end
54
+ end
@@ -32,15 +32,18 @@ RSpec.describe Connection do
32
32
  end
33
33
 
34
34
  let(:transcript) do
35
+ ch = ClientHello.deserialize(TESTBINARY_CLIENT_HELLO)
36
+ sh = ServerHello.deserialize(TESTBINARY_SERVER_HELLO)
37
+ ee = EncryptedExtensions.deserialize(TESTBINARY_ENCRYPTED_EXTENSIONS)
35
38
  transcript = Transcript.new
36
39
  transcript.merge!(
37
- CH => ClientHello.deserialize(TESTBINARY_CLIENT_HELLO),
38
- SH => ServerHello.deserialize(TESTBINARY_SERVER_HELLO),
39
- EE => EncryptedExtensions.deserialize(TESTBINARY_ENCRYPTED_EXTENSIONS),
40
- CT => ct,
41
- CV => cv,
42
- CF => cf,
43
- SF => sf
40
+ CH => [ch, TESTBINARY_CLIENT_HELLO],
41
+ SH => [sh, TESTBINARY_SERVER_HELLO],
42
+ EE => [ee, TESTBINARY_ENCRYPTED_EXTENSIONS],
43
+ CT => [ct, TESTBINARY_CERTIFICATE],
44
+ CV => [cv, TESTBINARY_CERTIFICATE_VERIFY],
45
+ CF => [cf, TESTBINARY_CLIENT_FINISHED],
46
+ SF => [sf, TESTBINARY_SERVER_FINISHED]
44
47
  )
45
48
  end
46
49
 
@@ -115,16 +118,20 @@ RSpec.describe Connection do
115
118
  end
116
119
 
117
120
  let(:transcript) do
121
+ ch1 = ClientHello.deserialize(TESTBINARY_HRR_CLIENT_HELLO1)
122
+ hrr = ServerHello.deserialize(TESTBINARY_HRR_HELLO_RETRY_REQUEST)
123
+ ch = ClientHello.deserialize(TESTBINARY_HRR_CLIENT_HELLO)
124
+ sh = ServerHello.deserialize(TESTBINARY_HRR_SERVER_HELLO)
125
+ ee = EncryptedExtensions.deserialize(TESTBINARY_HRR_ENCRYPTED_EXTENSIONS)
118
126
  transcript = Transcript.new
119
127
  transcript.merge!(
120
- CH1 => ClientHello.deserialize(TESTBINARY_HRR_CLIENT_HELLO1),
121
- HRR => ServerHello.deserialize(TESTBINARY_HRR_HELLO_RETRY_REQUEST),
122
- CH => ClientHello.deserialize(TESTBINARY_HRR_CLIENT_HELLO),
123
- SH => ServerHello.deserialize(TESTBINARY_HRR_SERVER_HELLO),
124
- EE =>
125
- EncryptedExtensions.deserialize(TESTBINARY_HRR_ENCRYPTED_EXTENSIONS),
126
- CT => ct,
127
- CV => cv
128
+ CH1 => [ch1, TESTBINARY_HRR_CLIENT_HELLO1],
129
+ HRR => [hrr, TESTBINARY_HRR_HELLO_RETRY_REQUEST],
130
+ CH => [ch, TESTBINARY_HRR_CLIENT_HELLO],
131
+ SH => [sh, TESTBINARY_HRR_SERVER_HELLO],
132
+ EE => [ee, TESTBINARY_HRR_ENCRYPTED_EXTENSIONS],
133
+ CT => [ct, TESTBINARY_HRR_CERTIFICATE],
134
+ CV => [cv, TESTBINARY_HRR_CERTIFICATE_VERIFY]
128
135
  )
129
136
  end
130
137
 
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require_relative 'spec_helper'
5
+ using Refinements
5
6
 
6
7
  RSpec.describe Extensions do
7
8
  context 'empty extensions' do
@@ -167,4 +168,19 @@ RSpec.describe Extensions do
167
168
  expect(extensions).to include ExtensionType::RECORD_SIZE_LIMIT
168
169
  end
169
170
  end
171
+
172
+ context 'duplicated extension_type' do
173
+ let(:server_name) do
174
+ ServerName.new('example.com')
175
+ end
176
+
177
+ let(:testbinary) do
178
+ server_name.serialize * 2
179
+ end
180
+
181
+ it 'should raise error, if extension_type get duplicated' do
182
+ expect { Extensions.deserialize(testbinary, HandshakeType::CLIENT_HELLO) }
183
+ .to raise_error(ErrorAlerts)
184
+ end
185
+ end
170
186
  end
@@ -1,18 +1,18 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIC2TCCAcGgAwIBAgIJAM8aTIrMzHgzMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
3
- BAMMB3Rlc3QtY2EwHhcNMTkwNTI1MDEzODAyWhcNMjAwNTI0MDEzODAyWjAUMRIw
2
+ MIIC2TCCAcGgAwIBAgIJALo0YKZBVqYnMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
3
+ BAMMB3Rlc3QtY2EwHhcNMjAwNzE1MTU0NTE4WhcNMzAwNzEzMTU0NTE4WjAUMRIw
4
4
  EAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
5
- AQDb9cGc2hOrLp3VWpxw8WgDqEL3LzZ5a6iYwibeR4AEB5FJLhS3Wvxa1xOS510C
6
- Kyfk/0znJvN9y+C8tFpB1BAN1OpPvaMPcYWx9CfEeoXaA5+QtU0MWJV7uYMtEUEx
7
- mEOvDKK1ZvHhw7xUzwcJTFRo6ZY6LqjiozlSPkTrVRIWoy7qEzXnOza36xX18xVt
8
- azvJBBudtTrjjBfQv2DJdF44icWqOBvAwg54BAbaH3bZ1WOg5oRnOPeVumYbPBsl
9
- dCDs67S1+RHKMEjRTk7gzuGog9lxJVMluU7iyreROD9+GvJEY3ra2KH96rtIgzo6
10
- KFHlC4Ih18zRfJZePgMGi5zVAgMBAAGjMDAuMAkGA1UdEwQCMAAwCwYDVR0PBAQD
11
- AgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAAjDs
12
- 4PgPL2Tn8+TxFWEPjh3VUB2kNyYK4LFA/ooN81pDLmm9/qc0FcUs16YQIqYdZICc
13
- vE83z3RlTmSjsynaRXxYh0VGVE2g2pWiPzEGTGE5HJy2JOtidMiacskmvetbTyYd
14
- TLdTEFiAlXF9e24OanglmFr9QnA/Z/zQkuIb4t7KN8Dufsi3ljkoJ+puuPxrEQj0
15
- 4BfBo381jK5WULHJ2G9pz5pvy1GZLfj1tQyG2wkI/vV2tjFN+LLO7NCY3V6RjvEZ
16
- bH4ZdAQz9fbbp7eCXImP+OJYt97Q3RZFJjUWhmh4qFebelkeN3RnmWSFrgjh0O67
17
- pyNwVv0//MYIEhMUVQ==
5
+ AQC65xzvPQrsXXRVsQ4rcrmvOF0gdWV38JKlhHUrS50//T0S55FUSBkuVXUDCZDx
6
+ dOf0y/5HaMb3hm68+ld5B/oNtoPlJWW6Sgc8OLERQy9qGpwR0mXND4SnZ9or7RDV
7
+ 8tAEg/Hzq5rm6Xy2WClSR+nHg2tVh2Szde39j7o8ivJpHPzfEyZh37y9oIiY2/FP
8
+ QpbAe8n3Ses04D3jhZRoysdcuneWuG3h5DJ9X4IhZUBM54nEO5IQElyYnF6xY/Lt
9
+ Gykf8+ydiuAZpZF5FGGfoiKB7XdIwhSlK1XRFeBbHRqyAFjpSNtqy6RPdJINLseb
10
+ wG6DNSxcLm91C6ZJaaqu7Qp1AgMBAAGjMDAuMAkGA1UdEwQCMAAwCwYDVR0PBAQD
11
+ AgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEALqaQ
12
+ J5H9jB2VmIEDxhXAQTeqW1Hmp0oHhL1XcAvNS+JILjFfAdjMe/3Kei3hQJv8j8sE
13
+ uck3o7iA4kcE0ydUzO7TM7efjqcksyZrmWSB0xj+NHjcybwhD4Selr1vBSCU0IHN
14
+ Ap+zYbBX7eQawm2lIzniBvS6MmP+dgZjhy73FVQ4oSz+wTcg1iPkhulYL4iV/HSG
15
+ fND5gUvlRbLHGTETpCdq7iJNOpNl/OYboJLPvVpx8H7Jc+L2bQl05fj/koO35xaL
16
+ JuZGj5aVOKw45WvqERpe1RI3077dWE6bAr9DzrW13IqmFMbPD817pcB6+ILZnMAF
17
+ RhobWRU6PA4TdDP8bg==
18
18
  -----END CERTIFICATE-----
@@ -1,27 +1,27 @@
1
1
  -----BEGIN RSA PRIVATE KEY-----
2
- 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
- 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
- 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
- 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
- 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
- 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
- ufpGJCwhnZG8fubScQTrwy7mHw+lBDSFIv7atU61ZOhL9npfKLnXE1cp3eXOX510
9
- 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
- 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
- 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
- cu6MRuA2b24EYcEDQgbU2KsUke2vZ1i5hl5ptuc8+iubXCj2SICilBeVQNXLIr2j
13
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- +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
- u908C97ufroaG7fVMFLS+uEyPsssh5WjwtQCULaubVfntutIgwGdM+VYSZMMj4vf
23
- THS6m0Jarx2gNzFF3WuA2Ea4gtHKSo3guMHyDi8h7vUMd/4n9gFQgmq3PPQS7+J0
24
- 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
- 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
- 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
27
27
  -----END RSA PRIVATE KEY-----