tttls1.3 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +16 -0
  5. data/.travis.yml +8 -0
  6. data/Gemfile +13 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +52 -0
  9. data/Rakefile +133 -0
  10. data/example/helper.rb +17 -0
  11. data/example/https_client.rb +32 -0
  12. data/example/https_client_using_0rtt.rb +64 -0
  13. data/example/https_client_using_hrr.rb +35 -0
  14. data/example/https_client_using_ticket.rb +56 -0
  15. data/lib/tttls1.3/cipher_suites.rb +102 -0
  16. data/lib/tttls1.3/client.rb +745 -0
  17. data/lib/tttls1.3/connection.rb +380 -0
  18. data/lib/tttls1.3/cryptograph/aead.rb +118 -0
  19. data/lib/tttls1.3/cryptograph/passer.rb +22 -0
  20. data/lib/tttls1.3/cryptograph.rb +3 -0
  21. data/lib/tttls1.3/error.rb +22 -0
  22. data/lib/tttls1.3/key_schedule.rb +242 -0
  23. data/lib/tttls1.3/message/alert.rb +86 -0
  24. data/lib/tttls1.3/message/application_data.rb +27 -0
  25. data/lib/tttls1.3/message/certificate.rb +121 -0
  26. data/lib/tttls1.3/message/certificate_verify.rb +59 -0
  27. data/lib/tttls1.3/message/change_cipher_spec.rb +26 -0
  28. data/lib/tttls1.3/message/client_hello.rb +100 -0
  29. data/lib/tttls1.3/message/encrypted_extensions.rb +65 -0
  30. data/lib/tttls1.3/message/end_of_early_data.rb +29 -0
  31. data/lib/tttls1.3/message/extension/alpn.rb +70 -0
  32. data/lib/tttls1.3/message/extension/cookie.rb +47 -0
  33. data/lib/tttls1.3/message/extension/early_data_indication.rb +58 -0
  34. data/lib/tttls1.3/message/extension/key_share.rb +236 -0
  35. data/lib/tttls1.3/message/extension/pre_shared_key.rb +205 -0
  36. data/lib/tttls1.3/message/extension/psk_key_exchange_modes.rb +54 -0
  37. data/lib/tttls1.3/message/extension/record_size_limit.rb +46 -0
  38. data/lib/tttls1.3/message/extension/server_name.rb +91 -0
  39. data/lib/tttls1.3/message/extension/signature_algorithms.rb +69 -0
  40. data/lib/tttls1.3/message/extension/signature_algorithms_cert.rb +25 -0
  41. data/lib/tttls1.3/message/extension/status_request.rb +106 -0
  42. data/lib/tttls1.3/message/extension/supported_groups.rb +145 -0
  43. data/lib/tttls1.3/message/extension/supported_versions.rb +98 -0
  44. data/lib/tttls1.3/message/extension/unknown_extension.rb +38 -0
  45. data/lib/tttls1.3/message/extensions.rb +173 -0
  46. data/lib/tttls1.3/message/finished.rb +44 -0
  47. data/lib/tttls1.3/message/new_session_ticket.rb +89 -0
  48. data/lib/tttls1.3/message/record.rb +232 -0
  49. data/lib/tttls1.3/message/server_hello.rb +116 -0
  50. data/lib/tttls1.3/message.rb +48 -0
  51. data/lib/tttls1.3/sequence_number.rb +31 -0
  52. data/lib/tttls1.3/signature_scheme.rb +31 -0
  53. data/lib/tttls1.3/transcript.rb +69 -0
  54. data/lib/tttls1.3/utils.rb +91 -0
  55. data/lib/tttls1.3/version.rb +5 -0
  56. data/lib/tttls1.3.rb +16 -0
  57. data/spec/aead_spec.rb +95 -0
  58. data/spec/alert_spec.rb +54 -0
  59. data/spec/alpn_spec.rb +55 -0
  60. data/spec/application_data_spec.rb +26 -0
  61. data/spec/certificate_spec.rb +55 -0
  62. data/spec/certificate_verify_spec.rb +51 -0
  63. data/spec/change_cipher_spec_spec.rb +26 -0
  64. data/spec/cipher_suites_spec.rb +39 -0
  65. data/spec/client_hello_spec.rb +83 -0
  66. data/spec/client_spec.rb +319 -0
  67. data/spec/connection_spec.rb +114 -0
  68. data/spec/cookie_spec.rb +98 -0
  69. data/spec/early_data_indication_spec.rb +64 -0
  70. data/spec/encrypted_extensions_spec.rb +94 -0
  71. data/spec/error_spec.rb +18 -0
  72. data/spec/extensions_spec.rb +170 -0
  73. data/spec/finished_spec.rb +55 -0
  74. data/spec/key_schedule_spec.rb +198 -0
  75. data/spec/key_share_spec.rb +199 -0
  76. data/spec/new_session_ticket_spec.rb +80 -0
  77. data/spec/pre_shared_key_spec.rb +167 -0
  78. data/spec/psk_key_exchange_modes_spec.rb +45 -0
  79. data/spec/record_size_limit_spec.rb +61 -0
  80. data/spec/record_spec.rb +105 -0
  81. data/spec/server_hello_spec.rb +101 -0
  82. data/spec/server_name_spec.rb +110 -0
  83. data/spec/signature_algorithms_cert_spec.rb +73 -0
  84. data/spec/signature_algorithms_spec.rb +100 -0
  85. data/spec/spec_helper.rb +872 -0
  86. data/spec/status_request_spec.rb +73 -0
  87. data/spec/supported_groups_spec.rb +79 -0
  88. data/spec/supported_versions_spec.rb +136 -0
  89. data/spec/transcript_spec.rb +69 -0
  90. data/spec/unknown_extension_spec.rb +90 -0
  91. data/spec/utils_spec.rb +215 -0
  92. data/tttls1.3.gemspec +25 -0
  93. metadata +197 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 50df67e1d621da0a19746a7461c07da05856348bdc6eed93048b25e18464d37f
4
+ data.tar.gz: c9c1b0ae4663816ae8f01695a413674bed8983b059f97717671d41b42e5f1def
5
+ SHA512:
6
+ metadata.gz: 2e8e5ac87b46489f8e10a9f95a46eb98815bb63aa0d03068cdea0b0f883d9cf77e6b40ffdcc9ad592191b6b6c1ca3788ddfafb21bbef70cd45d547fb50b94bc4
7
+ data.tar.gz: 553144700d2e68044e8bb6160e100bea9a1e44f46b970cc0cd1200cf0861ea8a05d29d3622e1d73e7928f92a310a9a40bbea057a3a03f3463558302abeff7d34
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ Gemfile.lock
4
+ .config
5
+ .rvmrc
6
+ /.bundle/
7
+ /vendor/
8
+ /lib/bundler/man/
9
+ /pkg/
10
+ /.yardoc/
11
+ /_yardoc/
12
+ /doc/
13
+ /rdoc/
14
+ /coverage/
15
+ /spec/reports/
16
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ autotest
2
+ --color
3
+ --format documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,16 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/Documentation:
5
+ Enabled: false
6
+
7
+ Metrics/AbcSize:
8
+ Max: 30
9
+
10
+ Metrics/MethodLength:
11
+ Max: 30
12
+
13
+ Metrics/BlockLength:
14
+ Exclude:
15
+ - 'Rakefile'
16
+ - 'spec/*.rb'
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.6.1
5
+ before_install:
6
+ - gem install bundler -v 2.0.1
7
+ - bundle install
8
+ script: bundle exec rake
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'openssl'
6
+ gem 'rake'
7
+
8
+ group :test do
9
+ gem 'pry'
10
+ gem 'pry-byebug'
11
+ gem 'rspec', '3.8.0'
12
+ gem 'rubocop', '0.67.2'
13
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Tomoya Kuwayama
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # tttls1.3
2
+
3
+ [![Build Status](https://travis-ci.org/thekuwayama/tttls1.3.svg?branch=master)](https://travis-ci.org/thekuwayama/tttls1.3) [![Maintainability](https://api.codeclimate.com/v1/badges/47f3c267d9cfd2c8e388/maintainability)](https://codeclimate.com/github/thekuwayama/tttls1.3/maintainability)
4
+
5
+ tttls1.3 is Ruby implementation of [TLS 1.3](https://tools.ietf.org/html/rfc8446) protocol.
6
+ tttls1.3 uses [openssl](https://github.com/ruby/openssl) as backend for crypto and X.509 operations.
7
+
8
+ It is the purpose of this project to understand the TLS 1.3 protocol and implement the TLS 1.3 protocol using Ruby.
9
+ Backward compatibility and performance are not an objective.
10
+ This gem should not be used for production software.
11
+
12
+
13
+ ## Features
14
+
15
+ tttls1.3 provides client API with the following features:
16
+
17
+ * Simple 1-RTT Handshake
18
+ * HelloRetryRequest
19
+ * Resumed 0-RTT Handshake (with PSK from ticket)
20
+
21
+ NOT supports X25519, X448, FFDHE, AES-CCM, Client Authentication, Post-Handshake Authentication, KeyUpdate, external PSKs.
22
+
23
+
24
+ ## Getting started
25
+
26
+ tttls1.3 gem is available at [rubygems.org](https://rubygems.org/gems/tttls1.3). You can install with:
27
+
28
+ ```
29
+ $ gem install tttls1.3
30
+ ```
31
+
32
+ This implementation provides only minimal API, so your code is responsible for application layer.
33
+ Roughly, this works as follows:
34
+
35
+ ```
36
+ require 'tttls1.3'
37
+
38
+ socket = YourTransport.new
39
+ client = TTTLS13::Client.new(socket, YOUR_HOSTNAME)
40
+ client.connect
41
+
42
+ client.write(YOUR_MESSAGE)
43
+ client.read
44
+ client.close
45
+ ```
46
+
47
+ HTTPS examples are [here](https://github.com/thekuwayama/tttls1.3/tree/master/example).
48
+
49
+
50
+ ## License
51
+
52
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rubocop/rake_task'
5
+ require 'rspec/core/rake_task'
6
+ require 'openssl'
7
+ require 'fileutils'
8
+
9
+ RuboCop::RakeTask.new
10
+
11
+ TMP_DIR = __dir__ + '/tmp'
12
+ CA_KEY = TMP_DIR + '/ca.key'
13
+ CA_CRT = TMP_DIR + '/ca.crt'
14
+ SERVER_KEY = TMP_DIR + '/server.key'
15
+ SERVER_CRT = TMP_DIR + '/server.crt'
16
+ certs = [CA_KEY, CA_CRT, SERVER_KEY, SERVER_CRT]
17
+
18
+ directory TMP_DIR
19
+
20
+ file CA_KEY => TMP_DIR do
21
+ puts "generate #{CA_KEY}..."
22
+ ca_key = OpenSSL::PKey::RSA.generate(4096)
23
+ File.write(CA_KEY, ca_key.to_pem)
24
+ end
25
+
26
+ file CA_CRT => [TMP_DIR, CA_KEY] do
27
+ ca_key = OpenSSL::PKey::RSA.new(File.read(CA_KEY))
28
+
29
+ puts "generate #{CA_CRT}..."
30
+ issu = sub = OpenSSL::X509::Name.new
31
+ sub.add_entry('CN', 'test-ca')
32
+
33
+ ca_crt = OpenSSL::X509::Certificate.new
34
+ ca_crt.not_before = Time.now
35
+ ca_crt.not_after = Time.now + (60 * 60 * 24 * 365 * 10)
36
+ ca_crt.public_key = ca_key.public_key
37
+ ca_crt.serial = 1
38
+ ca_crt.version = 2
39
+ ca_crt.issuer = issu
40
+ ca_crt.subject = sub
41
+
42
+ factory = OpenSSL::X509::ExtensionFactory.new
43
+ factory.subject_certificate = ca_crt
44
+ factory.issuer_certificate = ca_crt
45
+ ca_crt.add_extension(
46
+ factory.create_extension(
47
+ 'keyUsage',
48
+ 'critical, cRLSign, keyCertSign'
49
+ )
50
+ )
51
+ ca_crt.add_extension(
52
+ factory.create_extension(
53
+ 'basicConstraints',
54
+ 'critical, CA:true'
55
+ )
56
+ )
57
+ ca_crt.add_extension(
58
+ factory.create_extension(
59
+ 'subjectKeyIdentifier',
60
+ 'hash'
61
+ )
62
+ )
63
+
64
+ digest = OpenSSL::Digest::SHA256.new
65
+ ca_crt.sign(ca_key, digest)
66
+ File.write(CA_CRT, ca_crt.to_pem)
67
+ end
68
+
69
+ file SERVER_KEY => TMP_DIR do
70
+ puts "generate #{SERVER_KEY}..."
71
+ server_key = OpenSSL::PKey::RSA.generate(2048)
72
+ File.write(SERVER_KEY, server_key.to_pem)
73
+ end
74
+
75
+ file SERVER_CRT => [TMP_DIR, CA_CRT, SERVER_KEY] do
76
+ ca_key = OpenSSL::PKey::RSA.new(File.read(CA_KEY))
77
+ ca_crt = OpenSSL::X509::Certificate.new(File.read(CA_CRT))
78
+ server_key = OpenSSL::PKey::RSA.new(File.read(SERVER_KEY))
79
+
80
+ puts "generate #{SERVER_CRT}..."
81
+ sub = OpenSSL::X509::Name.new
82
+ sub.add_entry('CN', 'localhost')
83
+
84
+ server_crt = OpenSSL::X509::Certificate.new
85
+ server_crt.not_before = Time.now
86
+ server_crt.not_after = Time.now + (60 * 60 * 24 * 365)
87
+ server_crt.public_key = server_key.public_key
88
+ server_crt.serial = 2
89
+ server_crt.version = 2
90
+ server_crt.issuer = ca_crt.issuer
91
+ server_crt.subject = sub
92
+
93
+ factory = OpenSSL::X509::ExtensionFactory.new
94
+ factory.subject_certificate = server_crt
95
+ factory.issuer_certificate = ca_crt
96
+ server_crt.add_extension(
97
+ factory.create_extension(
98
+ 'basicConstraints',
99
+ 'CA:FALSE'
100
+ )
101
+ )
102
+ server_crt.add_extension(
103
+ factory.create_extension(
104
+ 'keyUsage',
105
+ 'digitalSignature, keyEncipherment'
106
+ )
107
+ )
108
+ server_crt.add_extension(
109
+ factory.create_extension(
110
+ 'subjectAltName',
111
+ 'DNS:localhost'
112
+ )
113
+ )
114
+
115
+ digest = OpenSSL::Digest::SHA256.new
116
+ server_crt.sign(ca_key, digest)
117
+ File.write(SERVER_CRT, server_crt.to_pem)
118
+ end
119
+
120
+ desc 'generate ' + certs.map { |path| File.basename(path) }.join(', ')
121
+ task gen_certs: certs
122
+
123
+ desc 'delete ' + certs.map { |path| File.basename(path) }.join(', ')
124
+ task :del_certs do
125
+ certs.each do |path|
126
+ puts "delete #{path}..."
127
+ FileUtils.rm(path, force: true)
128
+ end
129
+ end
130
+
131
+ RSpec::Core::RakeTask.new(spec: :gen_certs)
132
+
133
+ task default: %i[rubocop spec]
data/example/helper.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH << __dir__ + '/../lib'
4
+
5
+ require 'socket'
6
+ require 'openssl'
7
+ require 'tttls1.3'
8
+
9
+ def http_get(hostname)
10
+ <<~BIN
11
+ GET / HTTP/1.1\r
12
+ Host: #{hostname}\r
13
+ User-Agent: https_client\r
14
+ Accept: */*\r
15
+ \r
16
+ BIN
17
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'helper'
5
+
6
+ hostname, port = (ARGV[0] || 'localhost:4433').split(':')
7
+ http_get = http_get(hostname)
8
+
9
+ socket = TCPSocket.new(hostname, port)
10
+ settings = { ca_file: __dir__ + '/../tmp/ca.crt' }
11
+ client = TTTLS13::Client.new(socket, hostname, settings)
12
+ client.connect
13
+ client.write(http_get)
14
+
15
+ # status line, header
16
+ buffer = ''
17
+ buffer += client.read until buffer.include?("\r\n\r\n")
18
+ print header = buffer.split("\r\n\r\n").first
19
+ # header; Content-Length
20
+ cl_line = header.split("\r\n").find { |s| s.match(/Content-Length:/i) }
21
+
22
+ # body
23
+ unless cl_line.nil?
24
+ cl = cl_line.split(':').last.to_i
25
+ print buffer = buffer.split("\r\n\r\n")[1..].join
26
+ while buffer.length < cl
27
+ print s = client.read
28
+ buffer += s
29
+ end
30
+ end
31
+
32
+ client.close
@@ -0,0 +1,64 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'helper'
5
+
6
+ hostname, port = (ARGV[0] || 'localhost:4433').split(':')
7
+ http_get = http_get(hostname)
8
+
9
+ settings_2nd = {
10
+ ca_file: __dir__ + '/../tmp/ca.crt'
11
+ }
12
+ process_new_session_ticket = proc do |nst, rms, cs|
13
+ return if Time.now.to_i - nst.timestamp > nst.ticket_lifetime
14
+
15
+ settings_2nd[:ticket] = nst.ticket
16
+ settings_2nd[:resumption_master_secret] = rms
17
+ settings_2nd[:psk_cipher_suite] = cs
18
+ settings_2nd[:ticket_nonce] = nst.ticket_nonce
19
+ settings_2nd[:ticket_age_add] = nst.ticket_age_add
20
+ settings_2nd[:ticket_timestamp] = nst.timestamp
21
+ end
22
+ settings_1st = {
23
+ ca_file: __dir__ + '/../tmp/ca.crt',
24
+ process_new_session_ticket: process_new_session_ticket
25
+ }
26
+ accepted_early_data = false
27
+ [
28
+ # Initial Handshake:
29
+ settings_1st,
30
+ # Subsequent Handshake:
31
+ settings_2nd
32
+ ].each_with_index do |settings, i|
33
+ socket = TCPSocket.new(hostname, port)
34
+ client = TTTLS13::Client.new(socket, hostname, settings)
35
+
36
+ # send message using early data; 0-RTT
37
+ client.early_data(http_get) if i == 1 && settings.include?(:ticket)
38
+ client.connect
39
+ # send message after Simple 1-RTT Handshake
40
+ client.write(http_get) if i.zero? || !client.accepted_early_data?
41
+
42
+ # status line, header
43
+ buffer = ''
44
+ buffer += client.read until buffer.include?("\r\n\r\n")
45
+ print header = buffer.split("\r\n\r\n").first
46
+ # header; Content-Length
47
+ cl_line = header.split("\r\n").find { |s| s.match(/Content-Length:/i) }
48
+
49
+ # body
50
+ unless cl_line.nil?
51
+ cl = cl_line.split(':').last.to_i
52
+ print buffer = buffer.split("\r\n\r\n")[1..].join
53
+ while buffer.length < cl
54
+ print s = client.read
55
+ buffer += s
56
+ end
57
+ end
58
+
59
+ client.close
60
+ accepted_early_data = client.accepted_early_data?
61
+ end
62
+
63
+ puts "\n" + '-' * 10
64
+ puts "early data of 2nd handshake: #{accepted_early_data}"
@@ -0,0 +1,35 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'helper'
5
+
6
+ hostname, port = (ARGV[0] || 'localhost:4433').split(':')
7
+ http_get = http_get(hostname)
8
+
9
+ socket = TCPSocket.new(hostname, port)
10
+ settings = {
11
+ ca_file: __dir__ + '/../tmp/ca.crt',
12
+ key_share_groups: [] # empty KeyShareClientHello.client_shares
13
+ }
14
+ client = TTTLS13::Client.new(socket, hostname, settings)
15
+ client.connect
16
+ client.write(http_get)
17
+
18
+ # status line, header
19
+ buffer = ''
20
+ buffer += client.read until buffer.include?("\r\n\r\n")
21
+ print header = buffer.split("\r\n\r\n").first
22
+ # header; Content-Length
23
+ cl_line = header.split("\r\n").find { |s| s.match(/Content-Length:/i) }
24
+
25
+ # body
26
+ unless cl_line.nil?
27
+ cl = cl_line.split(':').last.to_i
28
+ print buffer = buffer.split("\r\n\r\n")[1..].join
29
+ while buffer.length < cl
30
+ print s = client.read
31
+ buffer += s
32
+ end
33
+ end
34
+
35
+ client.close
@@ -0,0 +1,56 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'helper'
5
+
6
+ hostname, port = (ARGV[0] || 'localhost:4433').split(':')
7
+ http_get = http_get(hostname)
8
+
9
+ settings_2nd = {
10
+ ca_file: __dir__ + '/../tmp/ca.crt'
11
+ }
12
+ process_new_session_ticket = proc do |nst, rms, cs|
13
+ return if Time.now.to_i - nst.timestamp > nst.ticket_lifetime
14
+
15
+ settings_2nd[:ticket] = nst.ticket
16
+ settings_2nd[:resumption_master_secret] = rms
17
+ settings_2nd[:psk_cipher_suite] = cs
18
+ settings_2nd[:ticket_nonce] = nst.ticket_nonce
19
+ settings_2nd[:ticket_age_add] = nst.ticket_age_add
20
+ settings_2nd[:ticket_timestamp] = nst.timestamp
21
+ end
22
+ settings_1st = {
23
+ ca_file: __dir__ + '/../tmp/ca.crt',
24
+ process_new_session_ticket: process_new_session_ticket
25
+ }
26
+
27
+ [
28
+ # Initial Handshake:
29
+ settings_1st,
30
+ # Subsequent Handshake:
31
+ settings_2nd
32
+ ].each do |settings|
33
+ socket = TCPSocket.new(hostname, port)
34
+ client = TTTLS13::Client.new(socket, hostname, settings)
35
+ client.connect
36
+ client.write(http_get)
37
+
38
+ # status line, header
39
+ buffer = ''
40
+ buffer += client.read until buffer.include?("\r\n\r\n")
41
+ print header = buffer.split("\r\n\r\n").first
42
+ # header; Content-Length
43
+ cl_line = header.split("\r\n").find { |s| s.match(/Content-Length:/i) }
44
+
45
+ # body
46
+ unless cl_line.nil?
47
+ cl = cl_line.split(':').last.to_i
48
+ print buffer = buffer.split("\r\n\r\n")[1..].join
49
+ while buffer.length < cl
50
+ print s = client.read
51
+ buffer += s
52
+ end
53
+ end
54
+
55
+ client.close
56
+ end
@@ -0,0 +1,102 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module CipherSuite
7
+ TLS_AES_128_GCM_SHA256 = "\x13\x01"
8
+ TLS_AES_256_GCM_SHA384 = "\x13\x02"
9
+ TLS_CHACHA20_POLY1305_SHA256 = "\x13\x03"
10
+ # TLS_AES_128_CCM_SHA256 = "\x13\x04" # UNSUPPORTED
11
+ # TLS_AES_128_CCM_8_SHA256 = "\x13\x05" # UNSUPPORTED
12
+
13
+ class << self
14
+ def digest(cipher_suite)
15
+ case cipher_suite
16
+ when TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256
17
+ # , TLS_AES_128_CCM_SHA256, TLS_AES_128_CCM_8_SHA256
18
+ 'SHA256'
19
+ when TLS_AES_256_GCM_SHA384
20
+ 'SHA384'
21
+ else
22
+ raise Error::ErrorAlerts, :internal_error
23
+ end
24
+ end
25
+
26
+ def hash_len(cipher_suite)
27
+ case cipher_suite
28
+ when TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256
29
+ # , TLS_AES_128_CCM_SHA256, TLS_AES_128_CCM_8_SHA256
30
+ 32
31
+ when TLS_AES_256_GCM_SHA384
32
+ 48
33
+ else
34
+ raise Error::ErrorAlerts, :internal_error
35
+ end
36
+ end
37
+
38
+ def key_len(cipher_suite)
39
+ case cipher_suite
40
+ when TLS_AES_128_GCM_SHA256
41
+ # , TLS_AES_128_CCM_SHA256, TLS_AES_128_CCM_8_SHA256
42
+ 16
43
+ when TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256
44
+ 32
45
+ else
46
+ raise Error::ErrorAlerts, :internal_error
47
+ end
48
+ end
49
+
50
+ def iv_len(cipher_suite)
51
+ case cipher_suite
52
+ when TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384,
53
+ TLS_CHACHA20_POLY1305_SHA256
54
+ # , TLS_AES_128_CCM_SHA256, TLS_AES_128_CCM_8_SHA256
55
+ 12
56
+ else
57
+ raise Error::ErrorAlerts, :internal_error
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ class CipherSuites < Array
64
+ # @param cipher_suites [Array of CipherSuite]
65
+ #
66
+ # @example
67
+ # CipherSuites.new([
68
+ # CipherSuite::TLS_AES_256_GCM_SHA384,
69
+ # CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
70
+ # CipherSuite::TLS_AES_128_GCM_SHA256
71
+ # ])
72
+ def initialize(cipher_suites)
73
+ super(cipher_suites)
74
+ end
75
+
76
+ # @return [String]
77
+ def serialize
78
+ join.prefix_uint16_length
79
+ end
80
+
81
+ # @param binary [String]
82
+ #
83
+ # @raise [TTTLS13::Error::ErrorAlerts]
84
+ #
85
+ # @return [TTTLS13::CipherSuites]
86
+ def self.deserialize(binary)
87
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
88
+
89
+ cipher_suites = []
90
+ i = 0
91
+ while i < binary.length
92
+ raise Error::ErrorAlerts, :decode_error if i + 2 > binary.length
93
+
94
+ cipher_suites << binary.slice(i, 2)
95
+ i += 2
96
+ end
97
+ raise Error::ErrorAlerts, :decode_error unless i == binary.length
98
+
99
+ CipherSuites.new(cipher_suites)
100
+ end
101
+ end
102
+ end