tttls1.3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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