tttls1.3 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/README.md +35 -13
  4. data/Rakefile +2 -4
  5. data/example/helper.rb +30 -7
  6. data/example/https_client.rb +3 -20
  7. data/example/https_client_using_0rtt.rb +10 -24
  8. data/example/https_client_using_hrr.rb +3 -20
  9. data/example/https_client_using_ticket.rb +3 -20
  10. data/example/https_server.rb +43 -0
  11. data/interop/client_spec.rb +111 -22
  12. data/interop/helper.rb +1 -0
  13. data/interop/server_spec.rb +182 -0
  14. data/lib/tttls1.3/client.rb +115 -98
  15. data/lib/tttls1.3/connection.rb +119 -32
  16. data/lib/tttls1.3/message/certificate.rb +18 -0
  17. data/lib/tttls1.3/message/client_hello.rb +38 -0
  18. data/lib/tttls1.3/message/encrypted_extensions.rb +20 -16
  19. data/lib/tttls1.3/message/extension/key_share.rb +24 -2
  20. data/lib/tttls1.3/message/extension/supported_groups.rb +0 -87
  21. data/lib/tttls1.3/message/extensions.rb +1 -27
  22. data/lib/tttls1.3/message/new_session_ticket.rb +14 -0
  23. data/lib/tttls1.3/message/record.rb +23 -20
  24. data/lib/tttls1.3/message/server_hello.rb +27 -0
  25. data/lib/tttls1.3/message.rb +35 -2
  26. data/lib/tttls1.3/named_group.rb +89 -0
  27. data/lib/tttls1.3/server.rb +439 -0
  28. data/lib/tttls1.3/transcript.rb +6 -0
  29. data/lib/tttls1.3/version.rb +1 -1
  30. data/lib/tttls1.3.rb +3 -0
  31. data/spec/certificate_spec.rb +28 -1
  32. data/spec/client_spec.rb +14 -10
  33. data/spec/connection_spec.rb +43 -13
  34. data/spec/encrypted_extensions_spec.rb +4 -4
  35. data/spec/fixtures/rsa_ca.crt +29 -0
  36. data/spec/fixtures/rsa_ca.key +51 -0
  37. data/spec/fixtures/rsa_rsa.crt +23 -0
  38. data/spec/fixtures/rsa_rsa.key +27 -0
  39. data/spec/fixtures/rsa_secp256r1.crt +19 -0
  40. data/spec/fixtures/rsa_secp256r1.key +5 -0
  41. data/spec/fixtures/rsa_secp384r1.crt +19 -0
  42. data/spec/fixtures/rsa_secp384r1.key +6 -0
  43. data/spec/fixtures/rsa_secp521r1.crt +20 -0
  44. data/spec/fixtures/rsa_secp521r1.key +7 -0
  45. data/spec/server_spec.rb +186 -0
  46. data/spec/spec_helper.rb +43 -0
  47. metadata +28 -2
@@ -4,11 +4,29 @@
4
4
  module TTTLS13
5
5
  using Refinements
6
6
  module Message
7
+ # https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1
8
+ APPEARABLE_SH_EXTENSIONS = [
9
+ ExtensionType::PRE_SHARED_KEY,
10
+ ExtensionType::PASSWORD_SALT,
11
+ ExtensionType::SUPPORTED_VERSIONS,
12
+ ExtensionType::KEY_SHARE
13
+ ].freeze
14
+ private_constant :APPEARABLE_SH_EXTENSIONS
15
+
16
+ APPEARABLE_HRR_EXTENSIONS = [
17
+ ExtensionType::COOKIE,
18
+ ExtensionType::PASSWORD_SALT,
19
+ ExtensionType::SUPPORTED_VERSIONS,
20
+ ExtensionType::KEY_SHARE
21
+ ].freeze
22
+ private_constant :APPEARABLE_HRR_EXTENSIONS
23
+
7
24
  class ServerHello
8
25
  # special value of the SHA-256 of "HelloRetryRequest"
9
26
  HRR_RANDOM \
10
27
  = "\xcf\x21\xad\x74\xe5\x9a\x61\x11\xbe\x1d\x8c\x02\x1e\x65\xb8\x91" \
11
28
  "\xc2\xa2\x11\x16\x7a\xbb\x8c\x5e\x07\x9e\x09\xe2\xc8\xa8\x33\x9c"
29
+ private_constant :HRR_RANDOM
12
30
 
13
31
  attr_reader :msg_type
14
32
  attr_reader :legacy_version
@@ -111,6 +129,15 @@ module TTTLS13
111
129
  def hrr?
112
130
  @hrr
113
131
  end
132
+
133
+ # @return [Boolean]
134
+ def only_appearable_extensions?
135
+ exs = @extensions.keys - APPEARABLE_SH_EXTENSIONS
136
+ exs = @extensions.keys - APPEARABLE_HRR_EXTENSIONS if hrr?
137
+ return true if exs.empty?
138
+
139
+ !(exs - DEFINED_EXTENSIONS).empty?
140
+ end
114
141
  end
115
142
  end
116
143
  end
@@ -1,8 +1,6 @@
1
1
  # encoding: ascii-8bit
2
2
  # frozen_string_literal: true
3
3
 
4
- Dir[File.dirname(__FILE__) + '/message/*.rb'].each { |f| require f }
5
-
6
4
  module TTTLS13
7
5
  module Message
8
6
  module ContentType
@@ -44,5 +42,40 @@ module TTTLS13
44
42
  KEY_UPDATE = "\x18"
45
43
  MESSAGE_HASH = "\xfe"
46
44
  end
45
+
46
+ module ExtensionType
47
+ SERVER_NAME = "\x00\x00"
48
+ MAX_FRAGMENT_LENGTH = "\x00\x01"
49
+ STATUS_REQUEST = "\x00\x05"
50
+ SUPPORTED_GROUPS = "\x00\x0a"
51
+ SIGNATURE_ALGORITHMS = "\x00\x0d"
52
+ USE_SRTP = "\x00\x0e"
53
+ HEARTBEAT = "\x00\x0f"
54
+ APPLICATION_LAYER_PROTOCOL_NEGOTIATION = "\x00\x10"
55
+ SIGNED_CERTIFICATE_TIMESTAMP = "\x00\x12"
56
+ CLIENT_CERTIFICATE_TYPE = "\x00\x13"
57
+ SERVER_CERTIFICATE_TYPE = "\x00\x14"
58
+ PADDING = "\x00\x15"
59
+ RECORD_SIZE_LIMIT = "\x00\x1c"
60
+ PWD_PROTECT = "\x00\x1d"
61
+ PWD_CLEAR = "\x00\x1e"
62
+ PASSWORD_SALT = "\x00\x1f"
63
+ PRE_SHARED_KEY = "\x00\x29"
64
+ EARLY_DATA = "\x00\x2a"
65
+ SUPPORTED_VERSIONS = "\x00\x2b"
66
+ COOKIE = "\x00\x2c"
67
+ PSK_KEY_EXCHANGE_MODES = "\x00\x2d"
68
+ CERTIFICATE_AUTHORITIES = "\x00\x2f"
69
+ OID_FILTERS = "\x00\x30"
70
+ POST_HANDSHAKE_AUTH = "\x00\x31"
71
+ SIGNATURE_ALGORITHMS_CERT = "\x00\x32"
72
+ KEY_SHARE = "\x00\x33"
73
+ end
74
+
75
+ DEFINED_EXTENSIONS = ExtensionType.constants.map do |c|
76
+ ExtensionType.const_get(c)
77
+ end.freeze
47
78
  end
48
79
  end
80
+
81
+ Dir[File.dirname(__FILE__) + '/message/*.rb'].each { |f| require f }
@@ -0,0 +1,89 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ module NamedGroup
6
+ SECP256R1 = "\x00\x17"
7
+ SECP384R1 = "\x00\x18"
8
+ SECP521R1 = "\x00\x19"
9
+ # X25519 = "\x00\x1d" # UNSUPPORTED
10
+ # X448 = "\x00\x1e" # UNSUPPORTED
11
+ # FFDHE2048 = "\x01\x00" # UNSUPPORTED
12
+ # FFDHE3072 = "\x01\x01" # UNSUPPORTED
13
+ # FFDHE4096 = "\x01\x02" # UNSUPPORTED
14
+ # FFDHE6144 = "\x01\x03" # UNSUPPORTED
15
+ # FFDHE8192 = "\x01\x04" # UNSUPPORTED
16
+ # ffdhe_private_use "\x01\xfc" ~ "\x01\xff"
17
+ # ecdhe_private_use "\xfe\x00" ~ "\xfe\xff"
18
+
19
+ class << self
20
+ # NOTE:
21
+ # For secp256r1, secp384r1, and secp521r1
22
+ #
23
+ # struct {
24
+ # uint8 legacy_form = 4;
25
+ # opaque X[coordinate_length];
26
+ # opaque Y[coordinate_length];
27
+ # } UncompressedPointRepresentation;
28
+ #
29
+ # @param group [TTTLS13::Message::Extension::NamedGroup]
30
+ #
31
+ # @raise [TTTLS13::Error::ErrorAlerts]
32
+ #
33
+ # @return [Integer]
34
+ def key_exchange_len(group)
35
+ case group
36
+ when SECP256R1
37
+ 65
38
+ when SECP384R1
39
+ 97
40
+ when SECP521R1
41
+ 133
42
+ # not supported other NamedGroup
43
+ # when X25519
44
+ # 32
45
+ # when X448
46
+ # 56
47
+ # when FFDHE2048
48
+ # 256
49
+ # when FFDHE4096
50
+ # 512
51
+ # when FFDHE6144
52
+ # 768
53
+ # when FFDHE8192
54
+ # 1024
55
+ else
56
+ raise Error::ErrorAlerts, :internal_error
57
+ end
58
+ end
59
+
60
+ # NOTE:
61
+ # SECG | ANSI X9.62 | NIST
62
+ # ------------+---------------+-------------
63
+ # secp256r1 | prime256v1 | NIST P-256
64
+ # secp384r1 | | NIST P-384
65
+ # secp521r1 | | NIST P-521
66
+ #
67
+ # https://tools.ietf.org/html/rfc4492#appendix-A
68
+ #
69
+ # @param groups [Array of TTTLS13::Message::Extension::NamedGroup]
70
+ #
71
+ # @raise [TTTLS13::Error::ErrorAlerts]
72
+ #
73
+ # @return [String] EC_builtin_curves
74
+ def curve_name(group)
75
+ case group
76
+ when SECP256R1
77
+ 'prime256v1'
78
+ when SECP384R1
79
+ 'secp384r1'
80
+ when SECP521R1
81
+ 'secp521r1'
82
+ else
83
+ # not supported other NamedGroup
84
+ raise Error::ErrorAlerts, :internal_error
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,439 @@
1
+ # encoding: ascii-8bit
2
+ # frozen_string_literal: true
3
+
4
+ module TTTLS13
5
+ using Refinements
6
+ module ServerState
7
+ # initial value is 0, eof value is -1
8
+ START = 1
9
+ RECVD_CH = 2
10
+ NEGOTIATED = 3
11
+ WAIT_EOED = 4
12
+ WAIT_FLIGHT2 = 5
13
+ WAIT_CERT = 6
14
+ WAIT_CV = 7
15
+ WAIT_FINISHED = 8
16
+ CONNECTED = 9
17
+ end
18
+
19
+ DEFAULT_SP_CIPHER_SUITES = [
20
+ CipherSuite::TLS_AES_256_GCM_SHA384,
21
+ CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
22
+ CipherSuite::TLS_AES_128_GCM_SHA256
23
+ ].freeze
24
+ private_constant :DEFAULT_SP_CIPHER_SUITES
25
+
26
+ DEFAULT_SP_SIGNATURE_ALGORITHMS = [
27
+ SignatureScheme::ECDSA_SECP256R1_SHA256,
28
+ SignatureScheme::ECDSA_SECP384R1_SHA384,
29
+ SignatureScheme::ECDSA_SECP521R1_SHA512,
30
+ SignatureScheme::RSA_PSS_RSAE_SHA256,
31
+ SignatureScheme::RSA_PSS_RSAE_SHA384,
32
+ SignatureScheme::RSA_PSS_RSAE_SHA512,
33
+ SignatureScheme::RSA_PKCS1_SHA256,
34
+ SignatureScheme::RSA_PKCS1_SHA384,
35
+ SignatureScheme::RSA_PKCS1_SHA512
36
+ ].freeze
37
+ private_constant :DEFAULT_SP_SIGNATURE_ALGORITHMS
38
+
39
+ DEFAULT_SP_NAMED_GROUP_LIST = [
40
+ NamedGroup::SECP256R1,
41
+ NamedGroup::SECP384R1,
42
+ NamedGroup::SECP521R1
43
+ ].freeze
44
+ private_constant :DEFAULT_SP_NAMED_GROUP_LIST
45
+
46
+ DEFAULT_SERVER_SETTINGS = {
47
+ crt_file: nil,
48
+ key_file: nil,
49
+ cipher_suites: DEFAULT_SP_CIPHER_SUITES,
50
+ signature_algorithms: DEFAULT_SP_SIGNATURE_ALGORITHMS,
51
+ supported_groups: DEFAULT_SP_NAMED_GROUP_LIST,
52
+ loglevel: Logger::WARN
53
+ }.freeze
54
+ private_constant :DEFAULT_SERVER_SETTINGS
55
+
56
+ # rubocop: disable Metrics/ClassLength
57
+ class Server < Connection
58
+ # @param socket [Socket]
59
+ # @param settings [Hash]
60
+ def initialize(socket, **settings)
61
+ super(socket)
62
+
63
+ @endpoint = :server
64
+ @settings = DEFAULT_SERVER_SETTINGS.merge(settings)
65
+ logger.level = @settings[:loglevel]
66
+
67
+ raise Error::ConfigError unless valid_settings?
68
+ return if @settings[:crt_file].nil?
69
+
70
+ crt_str = File.read(@settings[:crt_file])
71
+ @crt = OpenSSL::X509::Certificate.new(crt_str) # TODO: spki rsassaPss
72
+ klass = @crt.public_key.class
73
+ @key = klass.new(File.read(@settings[:key_file]))
74
+ raise Error::ConfigError unless @crt.check_private_key(@key)
75
+ end
76
+
77
+ # NOTE:
78
+ # START <-----+
79
+ # Recv ClientHello | | Send HelloRetryRequest
80
+ # v |
81
+ # RECVD_CH ----+
82
+ # | Select parameters
83
+ # v
84
+ # NEGOTIATED
85
+ # | Send ServerHello
86
+ # | K_send = handshake
87
+ # | Send EncryptedExtensions
88
+ # | [Send CertificateRequest]
89
+ # Can send | [Send Certificate + CertificateVerify]
90
+ # app data | Send Finished
91
+ # after --> | K_send = application
92
+ # here +--------+--------+
93
+ # No 0-RTT | | 0-RTT
94
+ # | |
95
+ # K_recv = handshake | | K_recv = early data
96
+ # [Skip decrypt errors] | +------> WAIT_EOED -+
97
+ # | | Recv | | Recv EndOfEarlyData
98
+ # | | early data | | K_recv = handshake
99
+ # | +------------+ |
100
+ # | |
101
+ # +> WAIT_FLIGHT2 <--------+
102
+ # |
103
+ # +--------+--------+
104
+ # No auth | | Client auth
105
+ # | |
106
+ # | v
107
+ # | WAIT_CERT
108
+ # | Recv | | Recv Certificate
109
+ # | empty | v
110
+ # | Certificate | WAIT_CV
111
+ # | | | Recv
112
+ # | v | CertificateVerify
113
+ # +-> WAIT_FINISHED <---+
114
+ # | Recv Finished
115
+ # | K_recv = application
116
+ # v
117
+ # CONNECTED
118
+ #
119
+ # https://tools.ietf.org/html/rfc8446#appendix-A.2
120
+ #
121
+ # rubocop: disable Metrics/AbcSize
122
+ # rubocop: disable Metrics/BlockLength
123
+ # rubocop: disable Metrics/CyclomaticComplexity
124
+ # rubocop: disable Metrics/MethodLength
125
+ # rubocop: disable Metrics/PerceivedComplexity
126
+ def accept
127
+ @state = ServerState::START
128
+ loop do
129
+ case @state
130
+ when ServerState::START
131
+ logger.debug('ServerState::START')
132
+
133
+ ch = @transcript[CH] = recv_client_hello
134
+ terminate(:illegal_parameter) unless ch.only_appearable_extensions?
135
+ @state = ServerState::RECVD_CH
136
+ when ServerState::RECVD_CH
137
+ logger.debug('ServerState::RECVD_CH')
138
+
139
+ # support only TLS 1.3
140
+ terminate(:protocol_version) unless negotiated_tls_1_3?
141
+
142
+ # validate/select parameters
143
+ @cipher_suite = select_cipher_suite
144
+ @named_group = select_named_group
145
+ @signature_scheme = select_signature_scheme
146
+ terminate(:handshake_failure) \
147
+ if @cipher_suite.nil? || @named_group.nil? || @signature_scheme.nil?
148
+ terminamte(:illegal_parameter) unless valid_ch_compression_methods?
149
+ terminate(:unrecognized_name) unless recognized_server_name?
150
+
151
+ @state = ServerState::NEGOTIATED
152
+ when ServerState::NEGOTIATED
153
+ logger.debug('ServerState::NEGOTIATED')
154
+
155
+ exs, @priv_key = gen_sh_extensions
156
+ @transcript[SH] = send_server_hello(exs)
157
+ send_ccs # compatibility mode
158
+
159
+ # generate shared secret
160
+ terminate(:illegal_parameter) unless valid_ch_key_share?
161
+ ke = @transcript[CH].extensions[Message::ExtensionType::KEY_SHARE]
162
+ &.key_share_entry
163
+ &.find { |e| e.group == @named_group }
164
+ &.key_exchange
165
+ # TODO: Send HelloRetryRequest
166
+ shared_secret = gen_shared_secret(ke, @priv_key, @named_group)
167
+ @key_schedule = KeySchedule.new(psk: @psk,
168
+ shared_secret: shared_secret,
169
+ cipher_suite: @cipher_suite,
170
+ transcript: @transcript)
171
+ @write_cipher = gen_cipher(@cipher_suite,
172
+ @key_schedule.server_handshake_write_key,
173
+ @key_schedule.server_handshake_write_iv)
174
+ @read_cipher = gen_cipher(@cipher_suite,
175
+ @key_schedule.client_handshake_write_key,
176
+ @key_schedule.client_handshake_write_iv)
177
+ @state = ServerState::WAIT_FLIGHT2
178
+ when ServerState::WAIT_EOED
179
+ logger.debug('ServerState::WAIT_EOED')
180
+ when ServerState::WAIT_FLIGHT2
181
+ logger.debug('ServerState::WAIT_FLIGHT2')
182
+
183
+ ee = @transcript[EE] = gen_encrypted_extensions
184
+ # TODO: [Send CertificateRequest]
185
+ ct = @transcript[CT] = gen_certificate
186
+ cv = @transcript[CV] = gen_certificate_verify
187
+ sf = @transcript[SF] = gen_finished
188
+ send_server_parameters([ee, ct, cv, sf])
189
+ @state = ServerState::WAIT_FINISHED
190
+ when ServerState::WAIT_CERT
191
+ logger.debug('ServerState::WAIT_CERT')
192
+ when ServerState::WAIT_CV
193
+ logger.debug('ServerState::WAIT_CV')
194
+ when ServerState::WAIT_FINISHED
195
+ logger.debug('ServerState::WAIT_FINISHED')
196
+
197
+ @transcript[CF] = recv_finished
198
+ terminate(:decrypt_error) unless verified_finished?
199
+ @write_cipher = gen_cipher(@cipher_suite,
200
+ @key_schedule.server_application_write_key,
201
+ @key_schedule.server_application_write_iv)
202
+ @read_cipher = gen_cipher(@cipher_suite,
203
+ @key_schedule.client_application_write_key,
204
+ @key_schedule.client_application_write_iv)
205
+ @state = ServerState::CONNECTED
206
+ when ServerState::CONNECTED
207
+ logger.debug('ServerState::CONNECTED')
208
+
209
+ break
210
+ end
211
+ end
212
+ end
213
+ # rubocop: enable Metrics/AbcSize
214
+ # rubocop: enable Metrics/BlockLength
215
+ # rubocop: enable Metrics/CyclomaticComplexity
216
+ # rubocop: enable Metrics/MethodLength
217
+ # rubocop: enable Metrics/PerceivedComplexity
218
+
219
+ private
220
+
221
+ # @return [Boolean]
222
+ def valid_settings?
223
+ mod = CipherSuite
224
+ defined = mod.constants.map { |c| mod.const_get(c) }
225
+ return false unless (@settings[:cipher_suites] - defined).empty?
226
+
227
+ mod = SignatureScheme
228
+ defined = mod.constants.map { |c| mod.const_get(c) }
229
+ return false unless (@settings[:signature_algorithms] - defined).empty?
230
+
231
+ mod = NamedGroup
232
+ defined = mod.constants.map { |c| mod.const_get(c) }
233
+ return false unless (@settings[:supported_groups] - defined).empty?
234
+
235
+ true
236
+ end
237
+
238
+ # @raise [TTTLS13::Error::ErrorAlerts]
239
+ #
240
+ # @return [TTTLS13::Message::ClientHello]
241
+ def recv_client_hello
242
+ ch = recv_message
243
+ terminate(:unexpected_message) unless ch.is_a?(Message::ClientHello)
244
+
245
+ ch
246
+ end
247
+
248
+ # @param exs [TTTLS13::Message::Extensions]
249
+ #
250
+ # @return [TTTLS13::Message::ServerHello]
251
+ def send_server_hello(exs)
252
+ ch_session_id = @transcript[CH].legacy_session_id
253
+ sh = Message::ServerHello.new(
254
+ legacy_session_id_echo: ch_session_id,
255
+ cipher_suite: @cipher_suite,
256
+ extensions: exs
257
+ )
258
+ send_handshakes(Message::ContentType::HANDSHAKE, [sh], @write_cipher)
259
+
260
+ sh
261
+ end
262
+
263
+ # @param messages [Array of TTTLS13::Message::$Object]
264
+ #
265
+ # @return [Array of TTTLS13::Message::$Object]
266
+ def send_server_parameters(messages)
267
+ send_handshakes(Message::ContentType::APPLICATION_DATA,
268
+ messages.reject(&:nil?),
269
+ @write_cipher)
270
+
271
+ messages
272
+ end
273
+
274
+ # @return [TTTLS13::Message::EncryptedExtensions]
275
+ def gen_encrypted_extensions
276
+ Message::EncryptedExtensions.new(gen_ee_extensions)
277
+ end
278
+
279
+ # @return [TTTLS13::Message::Certificate, nil]
280
+ def gen_certificate
281
+ return nil if @crt.nil?
282
+
283
+ ce = Message::CertificateEntry.new(@crt)
284
+ Message::Certificate.new(certificate_list: [ce])
285
+ end
286
+
287
+ # @return [TTTLS13::Message::CertificateVerify, nil]
288
+ def gen_certificate_verify
289
+ return nil if @key.nil?
290
+
291
+ Message::CertificateVerify.new(signature_scheme: @signature_scheme,
292
+ signature: sign_certificate_verify)
293
+ end
294
+
295
+ # @return [TTTLS13::Message::Finished]
296
+ def gen_finished
297
+ Message::Finished.new(sign_finished)
298
+ end
299
+
300
+ # @raise [TTTLS13::Error::ErrorAlerts]
301
+ #
302
+ # @return [TTTLS13::Message::Finished]
303
+ def recv_finished
304
+ cf = recv_message
305
+ terminate(:unexpected_message) unless cf.is_a?(Message::Finished)
306
+
307
+ cf
308
+ end
309
+
310
+ # @return [TTTLS13::Message::Extensions]
311
+ # @return [OpenSSL::PKey::EC.$Object]
312
+ def gen_sh_extensions
313
+ exs = []
314
+ # supported_versions: only TLS 1.3
315
+ exs << Message::Extension::SupportedVersions.new(
316
+ msg_type: Message::HandshakeType::SERVER_HELLO
317
+ )
318
+
319
+ # key_share
320
+ key_share, priv_key \
321
+ = Message::Extension::KeyShare.gen_sh_key_share(@named_group)
322
+ exs << key_share
323
+
324
+ [Message::Extensions.new(exs), priv_key]
325
+ end
326
+
327
+ # @return [TTTLS13::Message::Extensions]
328
+ def gen_ee_extensions
329
+ exs = []
330
+
331
+ # server_name
332
+ exs << Message::Extension::ServerName.new('') \
333
+ if @transcript[CH].extensions
334
+ .include?(Message::ExtensionType::SERVER_NAME)
335
+
336
+ # supported_groups
337
+ exs \
338
+ << Message::Extension::SupportedGroups.new(@settings[:supported_groups])
339
+
340
+ Message::Extensions.new(exs)
341
+ end
342
+
343
+ # @return [String]
344
+ def sign_certificate_verify
345
+ context = 'TLS 1.3, server CertificateVerify'
346
+ do_sign_certificate_verify(private_key: @key,
347
+ signature_scheme: @signature_scheme,
348
+ context: context,
349
+ handshake_context_end: CT)
350
+ end
351
+
352
+ # @return [String]
353
+ def sign_finished
354
+ digest = CipherSuite.digest(@cipher_suite)
355
+ finished_key = @key_schedule.server_finished_key
356
+ do_sign_finished(digest: digest,
357
+ finished_key: finished_key,
358
+ handshake_context_end: CV)
359
+ end
360
+
361
+ # @return [Boolean]
362
+ def verified_finished?
363
+ digest = CipherSuite.digest(@cipher_suite)
364
+ finished_key = @key_schedule.client_finished_key
365
+ signature = @transcript[CF].verify_data
366
+ do_verified_finished?(digest: digest,
367
+ finished_key: finished_key,
368
+ handshake_context_end: EOED,
369
+ signature: signature)
370
+ end
371
+
372
+ # @return [Boolean]
373
+ def negotiated_tls_1_3?
374
+ ch = @transcript[CH]
375
+ ch_lv = ch.legacy_version
376
+ ch_sv = ch.extensions[Message::ExtensionType::SUPPORTED_VERSIONS]
377
+ &.versions || []
378
+
379
+ ch_lv == Message::ProtocolVersion::TLS_1_2 &&
380
+ ch_sv.include?(Message::ProtocolVersion::TLS_1_3)
381
+ end
382
+
383
+ # @return [TTTLS13::CipherSuite, nil]
384
+ def select_cipher_suite
385
+ @transcript[CH].cipher_suites.find do |cs|
386
+ @settings[:cipher_suites].include?(cs)
387
+ end
388
+ end
389
+
390
+ # @return [TTTLS13::NamedGroup, nil]
391
+ def select_named_group
392
+ groups \
393
+ = @transcript[CH].extensions[Message::ExtensionType::SUPPORTED_GROUPS]
394
+ &.named_group_list || []
395
+
396
+ groups.find do |sg|
397
+ @settings[:supported_groups].include?(sg)
398
+ end
399
+ end
400
+
401
+ # @return [TTTLS13::SignatureScheme, nil]
402
+ def select_signature_scheme
403
+ algorithms \
404
+ = @transcript[CH].extensions[Message::ExtensionType::SIGNATURE_ALGORITHMS]
405
+ &.supported_signature_algorithms || []
406
+
407
+ do_select_signature_algorithms(algorithms, @crt).find do |ss|
408
+ @settings[:signature_algorithms].include?(ss)
409
+ end
410
+ end
411
+
412
+ # @return [Boolean]
413
+ def valid_ch_compression_methods?
414
+ @transcript[CH].legacy_compression_methods == ["\x00"]
415
+ end
416
+
417
+ # @return [Boolean]
418
+ def recognized_server_name?
419
+ server_name \
420
+ = @transcript[CH].extensions[Message::ExtensionType::SERVER_NAME]
421
+ &.server_name
422
+
423
+ return true if server_name.nil?
424
+
425
+ matching_san?(@crt, server_name)
426
+ end
427
+
428
+ # @return [Boolean]
429
+ def valid_ch_key_share?
430
+ ks = @transcript[CH].extensions[Message::ExtensionType::KEY_SHARE]
431
+ ks_groups = ks&.key_share_entry&.map(&:group) || []
432
+ sg = @transcript[CH].extensions[Message::ExtensionType::SUPPORTED_GROUPS]
433
+ sp_groups = sg&.named_group_list || []
434
+
435
+ ks_groups.uniq == ks_groups && (ks_groups - sp_groups).empty?
436
+ end
437
+ end
438
+ # rubocop: enable Metrics/ClassLength
439
+ end
@@ -23,6 +23,8 @@ module TTTLS13
23
23
  super
24
24
  end
25
25
 
26
+ alias super_include? include?
27
+
26
28
  # @param digest [String] name of digest algorithm
27
29
  # @param end_index [Integer]
28
30
  #
@@ -43,6 +45,10 @@ module TTTLS13
43
45
  OpenSSL::Digest.digest(digest, truncated)
44
46
  end
45
47
 
48
+ def include?(key)
49
+ super_include?(key) && !self[key].nil?
50
+ end
51
+
46
52
  private
47
53
 
48
54
  # @param digest [String] name of digest algorithm
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TTTLS13
4
- VERSION = '0.1.4'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/tttls1.3.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'openssl'
4
+ require 'pp'
4
5
  require 'logger'
5
6
 
6
7
  require 'tttls1.3/version'
@@ -9,6 +10,7 @@ require 'tttls1.3/logging'
9
10
  require 'tttls1.3/error'
10
11
  require 'tttls1.3/cipher_suites'
11
12
  require 'tttls1.3/signature_scheme'
13
+ require 'tttls1.3/named_group'
12
14
  require 'tttls1.3/cryptograph'
13
15
  require 'tttls1.3/transcript'
14
16
  require 'tttls1.3/key_schedule'
@@ -16,3 +18,4 @@ require 'tttls1.3/message'
16
18
  require 'tttls1.3/sequence_number'
17
19
  require 'tttls1.3/connection'
18
20
  require 'tttls1.3/client'
21
+ require 'tttls1.3/server'