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
@@ -0,0 +1,232 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ # https://tools.ietf.org/html/rfc8449#section-4
8
+ DEFAULT_RECORD_SIZE_LIMIT = 2**14 + 1
9
+
10
+ # rubocop: disable Metrics/ClassLength
11
+ class Record
12
+ attr_reader :type
13
+ attr_reader :legacy_record_version
14
+ attr_reader :messages
15
+ attr_reader :surplus_binary
16
+ attr_reader :cipher
17
+
18
+ # @param type [TTTLS13::Message::ContentType]
19
+ # @param legacy_record_version [TTTLS13::Message::ProtocolVersion]
20
+ # @param messages [Array of TTTLS13::Message::$Object]
21
+ # @param surplus_binary [String]
22
+ # @param cipher [TTTLS13::Cryptograph::$Object]
23
+ def initialize(type:,
24
+ legacy_record_version: ProtocolVersion::TLS_1_2,
25
+ messages: [],
26
+ surplus_binary: '',
27
+ cipher:)
28
+ @type = type
29
+ @legacy_record_version = legacy_record_version
30
+ @messages = messages
31
+ @surplus_binary = surplus_binary
32
+ @cipher = cipher
33
+ end
34
+
35
+ # NOTE:
36
+ # serialize joins messages.
37
+ # If serialize is received Server Parameters(EE, CT, CV),
38
+ # it returns one binary.
39
+ #
40
+ # @param record_size_limit [Integer]
41
+ #
42
+ # @return [String]
43
+ def serialize(record_size_limit = DEFAULT_RECORD_SIZE_LIMIT)
44
+ tlsplaintext = @messages.map(&:serialize).join + @surplus_binary
45
+ if messages_type == ContentType::APPLICATION_DATA
46
+ max = cipher.tlsplaintext_length_limit(record_size_limit)
47
+ fragments = tlsplaintext.scan(/.{1,#{max}}/m)
48
+ else
49
+ fragments = [tlsplaintext]
50
+ end
51
+ fragments = [''] if fragments.empty?
52
+
53
+ binary = ''
54
+ fragments.each do |s|
55
+ binary += @type + @legacy_record_version
56
+ binary += @cipher.encrypt(s, messages_type).prefix_uint16_length
57
+ end
58
+
59
+ binary
60
+ end
61
+
62
+ # NOTE:
63
+ # If previous Record has surplus_binary,
64
+ # surplus_binary should is given to Record.deserialize as buffered.
65
+ #
66
+ # @param binary [String]
67
+ # @param cipher [TTTLS13::Cryptograph::$Object]
68
+ # @param buffered [String] surplus_binary
69
+ #
70
+ # @raise [TTTLS13::Error::ErrorAlerts]
71
+ #
72
+ # @return [TTTLS13::Message::Record, String]
73
+ # rubocop: disable Metrics/CyclomaticComplexity
74
+ # rubocop: disable Metrics/PerceivedComplexity
75
+ def self.deserialize(binary, cipher, buffered = '')
76
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
77
+ raise Error::ErrorAlerts, :decode_error if binary.length < 5
78
+
79
+ type = binary[0]
80
+ legacy_record_version = binary.slice(1, 2)
81
+ fragment_len = Convert.bin2i(binary.slice(3, 2))
82
+ raise Error::ErrorAlerts, :record_overflow \
83
+ if (cipher.is_a?(Cryptograph::Passer) && fragment_len > 2**14) ||
84
+ (cipher.is_a?(Cryptograph::Aead) && fragment_len > 2**14 + 256)
85
+
86
+ fragment = binary.slice(5, fragment_len)
87
+ raise Error::ErrorAlerts, :decode_error \
88
+ unless binary.length == 5 + fragment_len
89
+
90
+ if type == ContentType::APPLICATION_DATA
91
+ fragment, inner_type = cipher.decrypt(fragment, binary.slice(0, 5))
92
+ end
93
+ messages, surplus_binary = deserialize_fragment(buffered + fragment,
94
+ inner_type || type)
95
+
96
+ Record.new(type: type,
97
+ legacy_record_version: legacy_record_version,
98
+ messages: messages,
99
+ surplus_binary: surplus_binary,
100
+ cipher: cipher)
101
+ end
102
+ # rubocop: enable Metrics/CyclomaticComplexity
103
+ # rubocop: enable Metrics/PerceivedComplexity
104
+
105
+ private
106
+
107
+ # @raise [TTTLS13::Error::ErrorAlerts]
108
+ #
109
+ # @return [TTTLS13::Message::ContentType]
110
+ def messages_type
111
+ types = @messages.map(&:class).uniq
112
+ raise Error::ErrorAlerts, :internal_error unless types.length == 1
113
+
114
+ type = types.first
115
+ if [Message::ClientHello,
116
+ Message::ServerHello,
117
+ Message::EncryptedExtensions,
118
+ Message::Certificate,
119
+ Message::CertificateVerify,
120
+ Message::Finished,
121
+ Message::EndOfEarlyData,
122
+ Message::NewSessionTicket].include?(type)
123
+ ContentType::HANDSHAKE
124
+ elsif type == ChangeCipherSpec
125
+ ContentType::CCS
126
+ elsif type == Message::ApplicationData
127
+ ContentType::APPLICATION_DATA
128
+ elsif type == Message::Alert
129
+ ContentType::ALERT
130
+ else
131
+ raise Error::ErrorAlerts, :internal_error
132
+ end
133
+ end
134
+
135
+ class << self
136
+ private
137
+
138
+ # @param binary [String]
139
+ # @param type [TTTLS13::Message::ContentType]
140
+ #
141
+ # @raise [TTTLS13::Error::ErrorAlerts]
142
+ #
143
+ # @return [Array of TTTLS13::Message::$Object, String]
144
+ # @return [String]
145
+ def deserialize_fragment(binary, type)
146
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
147
+
148
+ surplus_binary = ''
149
+ case type
150
+ when ContentType::HANDSHAKE
151
+ messages, surplus_binary = deserialize_handshake(binary)
152
+ when ContentType::CCS
153
+ messages = [ChangeCipherSpec.deserialize(binary)]
154
+ when ContentType::APPLICATION_DATA
155
+ messages = [ApplicationData.deserialize(binary)]
156
+ when ContentType::ALERT
157
+ messages = [Alert.deserialize(binary)]
158
+ else
159
+ raise Error::ErrorAlerts, :unexpected_message
160
+ end
161
+
162
+ [messages, surplus_binary]
163
+ end
164
+
165
+ # @param binary [String]
166
+ #
167
+ # @raise [TTTLS13::Error::ErrorAlerts]
168
+ #
169
+ # @return [Array of TTTLS13::Message::$Object]
170
+ # @return [String]
171
+ def deserialize_handshake(binary)
172
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
173
+
174
+ handshakes = []
175
+ i = 0
176
+ while i < binary.length
177
+ # Handshake.length is kind of uint24 and Record.length is kind of
178
+ # uint16, so Handshake can be longer than Record capacity.
179
+ if binary.length < 4 + i ||
180
+ binary.length < 4 + i + Convert.bin2i(binary.slice(i + 1, 3))
181
+ surplus_binary = binary[i..]
182
+ return [handshakes, surplus_binary]
183
+ end
184
+
185
+ msg_len = Convert.bin2i(binary.slice(i + 1, 3))
186
+ msg_bin = binary.slice(i, msg_len + 4)
187
+ message = do_deserialize_handshake(msg_bin)
188
+ i += msg_len + 4
189
+ handshakes << message
190
+ end
191
+
192
+ surplus_binary = binary[i..]
193
+ [handshakes, surplus_binary]
194
+ end
195
+
196
+ # @param binary [String]
197
+ #
198
+ # @raise [TTTLS13::Error::ErrorAlerts]
199
+ #
200
+ # @return [Array of TTTLS13::Message::$Object]
201
+ # rubocop: disable Metrics/CyclomaticComplexity
202
+ def do_deserialize_handshake(binary)
203
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
204
+ raise Error::ErrorAlerts, :decode_error if binary.empty?
205
+
206
+ case binary[0]
207
+ when HandshakeType::CLIENT_HELLO
208
+ ClientHello.deserialize(binary)
209
+ when HandshakeType::SERVER_HELLO
210
+ ServerHello.deserialize(binary)
211
+ when HandshakeType::ENCRYPTED_EXTENSIONS
212
+ EncryptedExtensions.deserialize(binary)
213
+ when HandshakeType::CERTIFICATE
214
+ Certificate.deserialize(binary)
215
+ when HandshakeType::CERTIFICATE_VERIFY
216
+ CertificateVerify.deserialize(binary)
217
+ when HandshakeType::FINISHED
218
+ Finished.deserialize(binary)
219
+ when HandshakeType::NEW_SESSION_TICKET
220
+ NewSessionTicket.deserialize(binary)
221
+ when HandshakeType::END_OF_EARLY_DATA
222
+ EndOfEarlyData.deserialize(binary)
223
+ else
224
+ raise Error::ErrorAlerts, :unexpected_message
225
+ end
226
+ end
227
+ # rubocop: enable Metrics/CyclomaticComplexity
228
+ end
229
+ end
230
+ # rubocop: enable Metrics/ClassLength
231
+ end
232
+ end
@@ -0,0 +1,116 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module Message
7
+ class ServerHello
8
+ # special value of the SHA-256 of "HelloRetryRequest"
9
+ HRR_RANDOM \
10
+ = "\xcf\x21\xad\x74\xe5\x9a\x61\x11\xbe\x1d\x8c\x02\x1e\x65\xb8\x91" \
11
+ "\xc2\xa2\x11\x16\x7a\xbb\x8c\x5e\x07\x9e\x09\xe2\xc8\xa8\x33\x9c"
12
+
13
+ attr_reader :msg_type
14
+ attr_reader :legacy_version
15
+ attr_reader :random
16
+ attr_reader :legacy_session_id_echo
17
+ attr_reader :cipher_suite
18
+ attr_reader :legacy_compression_method
19
+ attr_reader :extensions
20
+
21
+ # @param legacy_version [String]
22
+ # @param random [String]
23
+ # @param legacy_session_id_echo [String]
24
+ # @param cipher_suite [TTTLS13::CipherSuite]
25
+ # @param legacy_compression_method [String]
26
+ # @param extensions [TTTLS13::Message::Extensions]
27
+ # rubocop: disable Metrics/ParameterLists
28
+ def initialize(legacy_version: ProtocolVersion::TLS_1_2,
29
+ random: OpenSSL::Random.random_bytes(32),
30
+ legacy_session_id_echo:,
31
+ cipher_suite:,
32
+ legacy_compression_method: "\x00",
33
+ extensions: Extensions.new)
34
+ @msg_type = HandshakeType::SERVER_HELLO
35
+ @legacy_version = legacy_version
36
+ @random = random
37
+ @legacy_session_id_echo = legacy_session_id_echo
38
+ @cipher_suite = cipher_suite
39
+ @legacy_compression_method = legacy_compression_method
40
+ @extensions = extensions
41
+ @hrr = (random == HRR_RANDOM)
42
+ end
43
+ # rubocop: enable Metrics/ParameterLists
44
+
45
+ # @return [String]
46
+ def serialize
47
+ binary = ''
48
+ binary += @legacy_version
49
+ binary += @random
50
+ binary += @legacy_session_id_echo.prefix_uint8_length
51
+ binary += @cipher_suite
52
+ binary += @legacy_compression_method
53
+ binary += @extensions.serialize
54
+
55
+ @msg_type + binary.prefix_uint24_length
56
+ end
57
+
58
+ # @param binary [String]
59
+ #
60
+ # @raise [TTTLS13::Error::ErrorAlerts]
61
+ #
62
+ # @return [TTTLS13::Message::ServerHello]
63
+ # rubocop: disable Metrics/AbcSize
64
+ # rubocop: disable Metrics/CyclomaticComplexity
65
+ # rubocop: disable Metrics/MethodLength
66
+ # rubocop: disable Metrics/PerceivedComplexity
67
+ def self.deserialize(binary)
68
+ raise Error::ErrorAlerts, :internal_error if binary.nil?
69
+ raise Error::ErrorAlerts, :decode_error if binary.length < 39
70
+ raise Error::ErrorAlerts, :internal_error \
71
+ unless binary[0] == HandshakeType::SERVER_HELLO
72
+
73
+ msg_len = Convert.bin2i(binary.slice(1, 3))
74
+ legacy_version = binary.slice(4, 2)
75
+ random = binary.slice(6, 32)
76
+ lsid_len = Convert.bin2i(binary[38])
77
+ legacy_session_id_echo = binary.slice(39, lsid_len)
78
+ i = 39 + lsid_len
79
+ cipher_suite = binary.slice(i, 2)
80
+ i += 2
81
+ legacy_compression_method = binary[i]
82
+ i += 1
83
+ exs_len = Convert.bin2i(binary.slice(i, 2))
84
+ i += 2
85
+ exs_bin = binary.slice(i, exs_len)
86
+ if random == HRR_RANDOM
87
+ msg_type = HandshakeType::HELLO_RETRY_REQUEST
88
+ @hrr = true
89
+ else
90
+ msg_type = HandshakeType::SERVER_HELLO
91
+ @hrr = false
92
+ end
93
+ extensions = Extensions.deserialize(exs_bin, msg_type)
94
+ i += exs_len
95
+ raise Error::ErrorAlerts, :decode_error unless i == msg_len + 4 &&
96
+ i == binary.length
97
+
98
+ ServerHello.new(legacy_version: legacy_version,
99
+ random: random,
100
+ legacy_session_id_echo: legacy_session_id_echo,
101
+ cipher_suite: cipher_suite,
102
+ legacy_compression_method: legacy_compression_method,
103
+ extensions: extensions)
104
+ end
105
+ # rubocop: enable Metrics/AbcSize
106
+ # rubocop: enable Metrics/CyclomaticComplexity
107
+ # rubocop: enable Metrics/MethodLength
108
+ # rubocop: enable Metrics/PerceivedComplexity
109
+
110
+ # @return [Boolean]
111
+ def hrr?
112
+ @hrr
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ Dir[File.dirname(__FILE__) + '/message/*.rb'].each { |f| require f }
5
+
6
+ module TTTLS13
7
+ module Message
8
+ module ContentType
9
+ INVALID = "\x00"
10
+ CCS = "\x14"
11
+ ALERT = "\x15"
12
+ HANDSHAKE = "\x16"
13
+ APPLICATION_DATA = "\x17"
14
+ end
15
+
16
+ module ProtocolVersion
17
+ TLS_1_0 = "\x03\x01"
18
+ TLS_1_1 = "\x03\x02"
19
+ TLS_1_2 = "\x03\x03"
20
+ TLS_1_3 = "\x03\x04"
21
+ end
22
+
23
+ DEFAULT_VERSIONS = [ProtocolVersion::TLS_1_3].freeze
24
+
25
+ module HandshakeType
26
+ HELLO_REQUEST = "\x00" # RESERVED
27
+ CLIENT_HELLO = "\x01"
28
+ SERVER_HELLO = "\x02"
29
+ HELLO_VERIFY_REQUEST = "\x03" # RESERVED
30
+ NEW_SESSION_TICKET = "\x04"
31
+ END_OF_EARLY_DATA = "\x05"
32
+ HELLO_RETRY_REQUEST = "\x06" # RESERVED
33
+ ENCRYPTED_EXTENSIONS = "\x08"
34
+ CERTIFICATE = "\x0b"
35
+ SERVER_KEY_EXCHANGE = "\x0c" # RESERVED
36
+ CERTIFICATE_REQUEST = "\x0d"
37
+ SERVER_HELLO_DONE = "\x0e" # RESERVED
38
+ CERTIFICATE_VERIFY = "\x0f"
39
+ CLIENT_KEY_EXCHANGE = "\x10" # RESERVED
40
+ FINISHED = "\x14"
41
+ CERTIFICATE_URL = "\x15" # RESERVED
42
+ CERTIFICATE_STATUS = "\x16" # RESERVED
43
+ SUPPLEMENTAL_DATA = "\x17" # RESERVED
44
+ KEY_UPDATE = "\x18"
45
+ MESSAGE_HASH = "\xfe"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ class SequenceNumber
7
+ def initialize
8
+ @seq_num = 0
9
+ end
10
+
11
+ # @param str [String]
12
+ # @param iv_len [Integer]
13
+ #
14
+ # @return [String]
15
+ def xor(str, iv_len)
16
+ l = @seq_num.to_uint64.unpack('C*')
17
+ l.unshift(0) while l.length < iv_len
18
+ r = str.unpack('C*')
19
+ l.zip(r).map { |x, y| (x ^ y).chr }.join
20
+ end
21
+
22
+ def succ
23
+ @seq_num += 1
24
+ end
25
+
26
+ # @return [Boolean]
27
+ def next?
28
+ @seq_num < 2**64 - 1
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ module SignatureScheme
6
+ # RSASSA-PKCS1-v1_5 algorithms
7
+ RSA_PKCS1_SHA256 = "\x04\x01"
8
+ RSA_PKCS1_SHA384 = "\x05\x01"
9
+ RSA_PKCS1_SHA512 = "\x06\x01"
10
+ # ECDSA algorithms
11
+ ECDSA_SECP256R1_SHA256 = "\x04\x03"
12
+ ECDSA_SECP384R1_SHA384 = "\x05\x03"
13
+ ECDSA_SECP521R1_SHA512 = "\x06\x03"
14
+ # RSASSA-PSS algorithms with public key OID rsaEncryption
15
+ RSA_PSS_RSAE_SHA256 = "\x08\x04"
16
+ RSA_PSS_RSAE_SHA384 = "\x08\x05"
17
+ RSA_PSS_RSAE_SHA512 = "\x08\x06"
18
+ # EdDSA algorithms
19
+ # ED25519 = "\x08\x07" # UNSUPPORTED
20
+ # ED448 = "\x08\x08" # UNSUPPORTED
21
+ # RSASSA-PSS algorithms with public key OID RSASSA-PSS
22
+ RSA_PSS_PSS_SHA256 = "\x08\x09"
23
+ RSA_PSS_PSS_SHA384 = "\x08\x0a"
24
+ RSA_PSS_PSS_SHA512 = "\x08\x0b"
25
+ # Legacy algorithms
26
+ # RSA_PKCS1_SHA1 = "\x02\x01" # UNSUPPORTED
27
+ # ECDSA_SHA1 = "\x02\x03" # UNSUPPORTED
28
+ # Reserved Code Points
29
+ # private_use "\xfe\x00" ~ "\xff\xff"
30
+ end
31
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+
7
+ CH1 = 0
8
+ HRR = 1
9
+ CH = 2
10
+ SH = 3
11
+ EE = 4
12
+ CR = 5
13
+ CT = 6
14
+ CV = 7
15
+ SF = 8
16
+ EOED = 9
17
+ CCT = 10
18
+ CCV = 11
19
+ CF = 12
20
+
21
+ class Transcript < Hash
22
+ def initialize
23
+ super
24
+ end
25
+
26
+ # @param digest [String] name of digest algorithm
27
+ # @param end_index [Integer]
28
+ #
29
+ # @return [String]
30
+ def hash(digest, end_index)
31
+ s = concat_messages(digest, end_index)
32
+ OpenSSL::Digest.digest(digest, s)
33
+ end
34
+
35
+ # @param digest [String] name of digest algorithm
36
+ # @param end_index [Integer]
37
+ # @param truncate_bytes [Integer]
38
+ #
39
+ # @return [String]
40
+ def truncate_hash(digest, end_index, truncate_bytes)
41
+ s = concat_messages(digest, end_index)
42
+ truncated = s[0...-truncate_bytes]
43
+ OpenSSL::Digest.digest(digest, truncated)
44
+ end
45
+
46
+ private
47
+
48
+ # @param digest [String] name of digest algorithm
49
+ # @param end_index [Integer]
50
+ #
51
+ # @return [String]
52
+ def concat_messages(digest, end_index)
53
+ exc_prefix = ''
54
+ if include?(HRR)
55
+ # as an exception to the general rule
56
+ exc_prefix = Message::HandshakeType::MESSAGE_HASH \
57
+ + "\x00\x00" \
58
+ + OpenSSL::Digest.new(digest).digest_length.to_uint8 \
59
+ + OpenSSL::Digest.digest(digest, self[CH1].serialize) \
60
+ + self[HRR].serialize
61
+ end
62
+
63
+ messages = (CH..end_index).to_a.map do |m|
64
+ include?(m) ? self[m].serialize : ''
65
+ end
66
+ exc_prefix + messages.join
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ module Refinements
6
+ refine Integer do
7
+ def to_uint8
8
+ raise Error::ErrorAlerts, :internal_error \
9
+ if negative? || self >= (1 << 8)
10
+
11
+ chr
12
+ end
13
+
14
+ def to_uint16
15
+ raise Error::ErrorAlerts, :internal_error \
16
+ if negative? || self >= (1 << 16)
17
+
18
+ [
19
+ self / (1 << 8),
20
+ self % (1 << 8)
21
+ ].map(&:chr).join
22
+ end
23
+
24
+ def to_uint24
25
+ raise Error::ErrorAlerts, :internal_error \
26
+ if negative? || self >= (1 << 24)
27
+
28
+ [
29
+ self / (1 << 16),
30
+ self % (1 << 16) / (1 << 8),
31
+ self % (1 << 8)
32
+ ].map(&:chr).join
33
+ end
34
+
35
+ def to_uint32
36
+ raise Error::ErrorAlerts, :internal_error \
37
+ if negative? || self >= (1 << 32)
38
+
39
+ [
40
+ self / (1 << 24),
41
+ self % (1 << 24) / (1 << 16),
42
+ self % (1 << 16) / (1 << 8),
43
+ self % (1 << 8)
44
+ ].map(&:chr).join
45
+ end
46
+
47
+ def to_uint64
48
+ raise Error::ErrorAlerts, :internal_error \
49
+ if negative? || self >= (1 << 64)
50
+
51
+ [
52
+ self / (1 << 32),
53
+ self % (1 << 32) / (1 << 24),
54
+ self % (1 << 24) / (1 << 16),
55
+ self % (1 << 16) / (1 << 8),
56
+ self % (1 << 8)
57
+ ].map(&:chr).join
58
+ end
59
+ end
60
+
61
+ refine String do
62
+ def prefix_uint8_length
63
+ length.to_uint8 + self
64
+ end
65
+
66
+ def prefix_uint16_length
67
+ length.to_uint16 + self
68
+ end
69
+
70
+ def prefix_uint24_length
71
+ length.to_uint24 + self
72
+ end
73
+
74
+ def prefix_uint32_length
75
+ length.to_uint32 + self
76
+ end
77
+
78
+ def prefix_uint64_length
79
+ length.to_uint64 + self
80
+ end
81
+ end
82
+ end
83
+
84
+ module Convert
85
+ class << self
86
+ def bin2i(binary)
87
+ binary.unpack('C*').reverse.map.with_index { |x, i| x << 8 * i }.sum
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTTLS13
4
+ VERSION = '0.1.0'
5
+ end
data/lib/tttls1.3.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ require 'tttls1.3/version'
6
+ require 'tttls1.3/utils'
7
+ require 'tttls1.3/error'
8
+ require 'tttls1.3/cipher_suites'
9
+ require 'tttls1.3/signature_scheme'
10
+ require 'tttls1.3/cryptograph'
11
+ require 'tttls1.3/transcript'
12
+ require 'tttls1.3/key_schedule'
13
+ require 'tttls1.3/message'
14
+ require 'tttls1.3/sequence_number'
15
+ require 'tttls1.3/connection'
16
+ require 'tttls1.3/client'