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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +35 -13
- data/Rakefile +2 -4
- data/example/helper.rb +30 -7
- data/example/https_client.rb +3 -20
- data/example/https_client_using_0rtt.rb +10 -24
- data/example/https_client_using_hrr.rb +3 -20
- data/example/https_client_using_ticket.rb +3 -20
- data/example/https_server.rb +43 -0
- data/interop/client_spec.rb +111 -22
- data/interop/helper.rb +1 -0
- data/interop/server_spec.rb +182 -0
- data/lib/tttls1.3/client.rb +115 -98
- data/lib/tttls1.3/connection.rb +119 -32
- data/lib/tttls1.3/message/certificate.rb +18 -0
- data/lib/tttls1.3/message/client_hello.rb +38 -0
- data/lib/tttls1.3/message/encrypted_extensions.rb +20 -16
- data/lib/tttls1.3/message/extension/key_share.rb +24 -2
- data/lib/tttls1.3/message/extension/supported_groups.rb +0 -87
- data/lib/tttls1.3/message/extensions.rb +1 -27
- data/lib/tttls1.3/message/new_session_ticket.rb +14 -0
- data/lib/tttls1.3/message/record.rb +23 -20
- data/lib/tttls1.3/message/server_hello.rb +27 -0
- data/lib/tttls1.3/message.rb +35 -2
- data/lib/tttls1.3/named_group.rb +89 -0
- data/lib/tttls1.3/server.rb +439 -0
- data/lib/tttls1.3/transcript.rb +6 -0
- data/lib/tttls1.3/version.rb +1 -1
- data/lib/tttls1.3.rb +3 -0
- data/spec/certificate_spec.rb +28 -1
- data/spec/client_spec.rb +14 -10
- data/spec/connection_spec.rb +43 -13
- data/spec/encrypted_extensions_spec.rb +4 -4
- data/spec/fixtures/rsa_ca.crt +29 -0
- data/spec/fixtures/rsa_ca.key +51 -0
- data/spec/fixtures/rsa_rsa.crt +23 -0
- data/spec/fixtures/rsa_rsa.key +27 -0
- data/spec/fixtures/rsa_secp256r1.crt +19 -0
- data/spec/fixtures/rsa_secp256r1.key +5 -0
- data/spec/fixtures/rsa_secp384r1.crt +19 -0
- data/spec/fixtures/rsa_secp384r1.key +6 -0
- data/spec/fixtures/rsa_secp521r1.crt +20 -0
- data/spec/fixtures/rsa_secp521r1.key +7 -0
- data/spec/server_spec.rb +186 -0
- data/spec/spec_helper.rb +43 -0
- metadata +28 -2
data/lib/tttls1.3/connection.rb
CHANGED
@@ -30,10 +30,13 @@ module TTTLS13
|
|
30
30
|
# @raise [TTTLS13::Error::ConfigError]
|
31
31
|
#
|
32
32
|
# @return [String]
|
33
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
34
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
33
35
|
def read
|
34
36
|
# secure channel has not established yet
|
35
37
|
raise Error::ConfigError \
|
36
|
-
unless @endpoint == :client && @state == ClientState::CONNECTED
|
38
|
+
unless (@endpoint == :client && @state == ClientState::CONNECTED) ||
|
39
|
+
(@endpoint == :server && @state == ServerState::CONNECTED)
|
37
40
|
return '' if @state == EOF
|
38
41
|
|
39
42
|
message = nil
|
@@ -49,6 +52,8 @@ module TTTLS13
|
|
49
52
|
|
50
53
|
message.fragment
|
51
54
|
end
|
55
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
56
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
52
57
|
|
53
58
|
# @return [Boolean]
|
54
59
|
def eof?
|
@@ -61,7 +66,8 @@ module TTTLS13
|
|
61
66
|
def write(binary)
|
62
67
|
# secure channel has not established yet
|
63
68
|
raise Error::ConfigError \
|
64
|
-
unless @endpoint == :client && @state == ClientState::CONNECTED
|
69
|
+
unless (@endpoint == :client && @state == ClientState::CONNECTED) ||
|
70
|
+
(@endpoint == :server && @state == ServerState::CONNECTED)
|
65
71
|
|
66
72
|
ap = Message::ApplicationData.new(binary)
|
67
73
|
send_application_data(ap, @write_cipher)
|
@@ -144,7 +150,7 @@ module TTTLS13
|
|
144
150
|
|
145
151
|
# @param record [TTTLS13::Message::Record]
|
146
152
|
def send_record(record)
|
147
|
-
logger.debug(
|
153
|
+
logger.debug("send \n" + record.pretty_inspect)
|
148
154
|
@socket.write(record.serialize(@send_record_size))
|
149
155
|
end
|
150
156
|
|
@@ -164,7 +170,7 @@ module TTTLS13
|
|
164
170
|
messages = record.messages
|
165
171
|
break unless messages.empty?
|
166
172
|
when Message::ContentType::CCS
|
167
|
-
terminate(:unexpected_message) unless
|
173
|
+
terminate(:unexpected_message) unless receivable_ccs?
|
168
174
|
next
|
169
175
|
when Message::ContentType::ALERT
|
170
176
|
handle_received_alert(record.messages.first)
|
@@ -205,23 +211,69 @@ module TTTLS13
|
|
205
211
|
terminate(:unexpected_message)
|
206
212
|
end
|
207
213
|
|
208
|
-
logger.debug(
|
214
|
+
logger.debug("receive \n" + record.pretty_inspect)
|
209
215
|
record
|
210
216
|
end
|
211
217
|
|
212
218
|
# @param digest [String] name of digest algorithm
|
219
|
+
# @param transcript [TTTLS13::Transcript]
|
213
220
|
#
|
214
221
|
# @return [String]
|
215
|
-
def do_sign_psk_binder(digest)
|
222
|
+
def do_sign_psk_binder(digest, transcript)
|
216
223
|
# TODO: ext binder
|
217
224
|
secret = @key_schedule.binder_key_res
|
218
225
|
hash_len = OpenSSL::Digest.new(digest).digest_length
|
219
226
|
# transcript-hash (CH1 + HRR +) truncated-CH
|
220
|
-
hash =
|
227
|
+
hash = transcript.truncate_hash(digest, CH, hash_len + 3)
|
221
228
|
OpenSSL::HMAC.digest(digest, secret, hash)
|
222
229
|
end
|
223
230
|
|
224
|
-
# @param
|
231
|
+
# @param private_key [OpenSSL::PKey::PKey]
|
232
|
+
# @param signature_scheme [TTTLS13::SignatureScheme]
|
233
|
+
# @param context [String]
|
234
|
+
# @param handshake_context_end [Integer]
|
235
|
+
#
|
236
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
237
|
+
#
|
238
|
+
# @return [String]
|
239
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
240
|
+
def do_sign_certificate_verify(private_key:, signature_scheme:, context:,
|
241
|
+
handshake_context_end:)
|
242
|
+
digest = CipherSuite.digest(@cipher_suite)
|
243
|
+
hash = @transcript.hash(digest, handshake_context_end)
|
244
|
+
content = "\x20" * 64 + context + "\x00" + hash
|
245
|
+
|
246
|
+
# RSA signatures MUST use an RSASSA-PSS algorithm, regardless of whether
|
247
|
+
# RSASSA-PKCS1-v1_5 algorithms appear in "signature_algorithms".
|
248
|
+
case signature_scheme
|
249
|
+
when SignatureScheme::RSA_PKCS1_SHA256,
|
250
|
+
SignatureScheme::RSA_PSS_RSAE_SHA256,
|
251
|
+
SignatureScheme::RSA_PSS_PSS_SHA256
|
252
|
+
private_key.sign_pss('SHA256', content, salt_length: :digest,
|
253
|
+
mgf1_hash: 'SHA256')
|
254
|
+
when SignatureScheme::RSA_PKCS1_SHA384,
|
255
|
+
SignatureScheme::RSA_PSS_RSAE_SHA384,
|
256
|
+
SignatureScheme::RSA_PSS_PSS_SHA384
|
257
|
+
private_key.sign_pss('SHA384', content, salt_length: :digest,
|
258
|
+
mgf1_hash: 'SHA384')
|
259
|
+
when SignatureScheme::RSA_PKCS1_SHA512,
|
260
|
+
SignatureScheme::RSA_PSS_RSAE_SHA512,
|
261
|
+
SignatureScheme::RSA_PSS_PSS_SHA512
|
262
|
+
private_key.sign_pss('SHA512', content, salt_length: :digest,
|
263
|
+
mgf1_hash: 'SHA512')
|
264
|
+
when SignatureScheme::ECDSA_SECP256R1_SHA256
|
265
|
+
private_key.sign('SHA256', content)
|
266
|
+
when SignatureScheme::ECDSA_SECP384R1_SHA384
|
267
|
+
private_key.sign('SHA384', content)
|
268
|
+
when SignatureScheme::ECDSA_SECP521R1_SHA512
|
269
|
+
private_key.sign('SHA512', content)
|
270
|
+
else # TODO: ED25519, ED448
|
271
|
+
terminate(:internal_error)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
275
|
+
|
276
|
+
# @param public_key [OpenSSL::PKey::PKey]
|
225
277
|
# @param signature_scheme [TTTLS13::SignatureScheme]
|
226
278
|
# @param signature [String]
|
227
279
|
# @param context [String]
|
@@ -231,13 +283,12 @@ module TTTLS13
|
|
231
283
|
#
|
232
284
|
# @return [Boolean]
|
233
285
|
# rubocop: disable Metrics/CyclomaticComplexity
|
234
|
-
def
|
235
|
-
|
236
|
-
|
286
|
+
def do_verified_certificate_verify?(public_key:, signature_scheme:,
|
287
|
+
signature:, context:,
|
288
|
+
handshake_context_end:)
|
237
289
|
digest = CipherSuite.digest(@cipher_suite)
|
238
290
|
hash = @transcript.hash(digest, handshake_context_end)
|
239
291
|
content = "\x20" * 64 + context + "\x00" + hash
|
240
|
-
public_key = OpenSSL::X509::Certificate.new(certificate_pem).public_key
|
241
292
|
|
242
293
|
# RSA signatures MUST use an RSASSA-PSS algorithm, regardless of whether
|
243
294
|
# RSASSA-PKCS1-v1_5 algorithms appear in "signature_algorithms".
|
@@ -285,8 +336,8 @@ module TTTLS13
|
|
285
336
|
# @param signature [String]
|
286
337
|
#
|
287
338
|
# @return [Boolean]
|
288
|
-
def
|
289
|
-
|
339
|
+
def do_verified_finished?(digest:, finished_key:, handshake_context_end:,
|
340
|
+
signature:)
|
290
341
|
do_sign_finished(
|
291
342
|
digest: digest,
|
292
343
|
finished_key: finished_key,
|
@@ -296,11 +347,11 @@ module TTTLS13
|
|
296
347
|
|
297
348
|
# @param key_exchange [String]
|
298
349
|
# @param priv_key [OpenSSL::PKey::$Object]
|
299
|
-
# @param group [TTTLS13::
|
350
|
+
# @param group [TTTLS13::NamedGroup]
|
300
351
|
#
|
301
352
|
# @return [String]
|
302
353
|
def gen_shared_secret(key_exchange, priv_key, group)
|
303
|
-
curve =
|
354
|
+
curve = NamedGroup.curve_name(group)
|
304
355
|
terminate(:internal_error) if curve.nil?
|
305
356
|
|
306
357
|
pub_key = OpenSSL::PKey::EC::Point.new(
|
@@ -315,7 +366,7 @@ module TTTLS13
|
|
315
366
|
#
|
316
367
|
# Received ccs before the first ClientHello message or after the peer's
|
317
368
|
# Finished message, peer MUST abort.
|
318
|
-
def
|
369
|
+
def receivable_ccs?
|
319
370
|
return false unless @transcript.include?(CH)
|
320
371
|
return false if @endpoint == :client && @transcript.include?(SF)
|
321
372
|
return false if @endpoint == :server && @transcript.include?(CF)
|
@@ -352,23 +403,13 @@ module TTTLS13
|
|
352
403
|
# @param hostname [String]
|
353
404
|
#
|
354
405
|
# @return [Boolean]
|
355
|
-
|
356
|
-
# rubocop: disable Metrics/CyclomaticComplexity
|
357
|
-
def certified_certificate?(certificate_list, ca_file = nil, hostname = nil)
|
406
|
+
def trusted_certificate?(certificate_list, ca_file = nil, hostname = nil)
|
358
407
|
cert_bin = certificate_list.first.cert_data
|
359
408
|
cert = OpenSSL::X509::Certificate.new(cert_bin)
|
360
409
|
|
361
410
|
# not support CN matching, only support SAN matching
|
362
|
-
|
363
|
-
|
364
|
-
return false if san.nil?
|
365
|
-
|
366
|
-
ostr = OpenSSL::ASN1.decode(san.to_der).value.last
|
367
|
-
san_match = OpenSSL::ASN1.decode(ostr.value).map(&:value)
|
368
|
-
.map { |s| s.gsub('.', '\.').gsub('*', '.*') }
|
369
|
-
.any? { |s| hostname.match(/#{s}/) }
|
370
|
-
return false unless san_match
|
371
|
-
end
|
411
|
+
return false if !hostname.nil? && !matching_san?(cert, hostname)
|
412
|
+
|
372
413
|
store = OpenSSL::X509::Store.new
|
373
414
|
store.set_default_paths
|
374
415
|
store.add_file(ca_file) unless ca_file.nil?
|
@@ -380,8 +421,54 @@ module TTTLS13
|
|
380
421
|
now = Time.now
|
381
422
|
ctx.verify && cert.not_before < now && now < cert.not_after
|
382
423
|
end
|
383
|
-
|
384
|
-
#
|
424
|
+
|
425
|
+
# @param cert [OpenSSL::X509::Certificate]
|
426
|
+
# @param name [String]
|
427
|
+
#
|
428
|
+
# @return [Boolean]
|
429
|
+
def matching_san?(cert, name)
|
430
|
+
san = cert.extensions.find { |ex| ex.oid == 'subjectAltName' }
|
431
|
+
return false if san.nil?
|
432
|
+
|
433
|
+
ostr = OpenSSL::ASN1.decode(san.to_der).value.last
|
434
|
+
matching = OpenSSL::ASN1.decode(ostr.value).map(&:value)
|
435
|
+
.map { |s| s.gsub('.', '\.').gsub('*', '.*') }
|
436
|
+
.any? { |s| name.match(/#{s}/) }
|
437
|
+
|
438
|
+
matching
|
439
|
+
end
|
440
|
+
|
441
|
+
# @param signature_algorithms [Array of SignatureAlgorithms]
|
442
|
+
# @param crt [OpenSSL::X509::Certificate]
|
443
|
+
#
|
444
|
+
# @return [Array of TTTLS13::Message::Extension::SignatureAlgorithms]
|
445
|
+
def do_select_signature_algorithms(signature_algorithms, crt)
|
446
|
+
spki = OpenSSL::Netscape::SPKI.new
|
447
|
+
spki.public_key = crt.public_key
|
448
|
+
oid_str = spki.to_text.split("\n")
|
449
|
+
.find { |l| l.include?('Public Key Algorithm:') }
|
450
|
+
signature_algorithms.select do |sa|
|
451
|
+
case sa
|
452
|
+
when SignatureScheme::ECDSA_SECP256R1_SHA256,
|
453
|
+
SignatureScheme::ECDSA_SECP384R1_SHA384,
|
454
|
+
SignatureScheme::ECDSA_SECP521R1_SHA512
|
455
|
+
oid_str.include?('id-ecPublicKey')
|
456
|
+
when SignatureScheme::RSA_PSS_PSS_SHA256,
|
457
|
+
SignatureScheme::RSA_PSS_PSS_SHA384,
|
458
|
+
SignatureScheme::RSA_PSS_PSS_SHA512
|
459
|
+
oid_str.include?('rsassaPss')
|
460
|
+
when SignatureScheme::RSA_PSS_RSAE_SHA256,
|
461
|
+
SignatureScheme::RSA_PSS_RSAE_SHA384,
|
462
|
+
SignatureScheme::RSA_PSS_RSAE_SHA512
|
463
|
+
oid_str.include?('rsaEncryption')
|
464
|
+
else
|
465
|
+
# RSASSA-PKCS1-v1_5 algorithms refer solely to signatures which appear
|
466
|
+
# in certificates and are not defined for use in signed TLS handshake
|
467
|
+
# messages
|
468
|
+
false
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
385
472
|
end
|
386
473
|
# rubocop: enable Metrics/ClassLength
|
387
474
|
end
|
@@ -4,6 +4,13 @@
|
|
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_CT_EXTENSIONS = [
|
9
|
+
ExtensionType::STATUS_REQUEST,
|
10
|
+
ExtensionType::SIGNED_CERTIFICATE_TIMESTAMP
|
11
|
+
].freeze
|
12
|
+
private_constant :APPEARABLE_CT_EXTENSIONS
|
13
|
+
|
7
14
|
class Certificate
|
8
15
|
attr_reader :msg_type
|
9
16
|
attr_reader :certificate_request_context
|
@@ -58,6 +65,17 @@ module TTTLS13
|
|
58
65
|
)
|
59
66
|
end
|
60
67
|
|
68
|
+
# @return [Boolean]
|
69
|
+
def only_appearable_extensions?
|
70
|
+
cl_exs = @certificate_list.map do |e|
|
71
|
+
e.instance_variable_get(:@extensions).keys
|
72
|
+
end
|
73
|
+
exs = cl_exs.uniq.flatten - APPEARABLE_CT_EXTENSIONS
|
74
|
+
return true if exs.empty?
|
75
|
+
|
76
|
+
!(exs - DEFINED_EXTENSIONS).empty?
|
77
|
+
end
|
78
|
+
|
61
79
|
class << self
|
62
80
|
private
|
63
81
|
|
@@ -4,6 +4,36 @@
|
|
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_CH_EXTENSIONS = [
|
9
|
+
ExtensionType::SERVER_NAME,
|
10
|
+
ExtensionType::MAX_FRAGMENT_LENGTH,
|
11
|
+
ExtensionType::STATUS_REQUEST,
|
12
|
+
ExtensionType::SUPPORTED_GROUPS,
|
13
|
+
ExtensionType::SIGNATURE_ALGORITHMS,
|
14
|
+
ExtensionType::USE_SRTP,
|
15
|
+
ExtensionType::HEARTBEAT,
|
16
|
+
ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION,
|
17
|
+
ExtensionType::SIGNED_CERTIFICATE_TIMESTAMP,
|
18
|
+
ExtensionType::CLIENT_CERTIFICATE_TYPE,
|
19
|
+
ExtensionType::SERVER_CERTIFICATE_TYPE,
|
20
|
+
ExtensionType::PADDING,
|
21
|
+
ExtensionType::RECORD_SIZE_LIMIT,
|
22
|
+
ExtensionType::PWD_PROTECT,
|
23
|
+
ExtensionType::PWD_CLEAR,
|
24
|
+
ExtensionType::PASSWORD_SALT,
|
25
|
+
ExtensionType::PRE_SHARED_KEY,
|
26
|
+
ExtensionType::EARLY_DATA,
|
27
|
+
ExtensionType::SUPPORTED_VERSIONS,
|
28
|
+
ExtensionType::COOKIE,
|
29
|
+
ExtensionType::PSK_KEY_EXCHANGE_MODES,
|
30
|
+
ExtensionType::CERTIFICATE_AUTHORITIES,
|
31
|
+
ExtensionType::POST_HANDSHAKE_AUTH,
|
32
|
+
ExtensionType::SIGNATURE_ALGORITHMS_CERT,
|
33
|
+
ExtensionType::KEY_SHARE
|
34
|
+
].freeze
|
35
|
+
private_constant :APPEARABLE_CH_EXTENSIONS
|
36
|
+
|
7
37
|
class ClientHello
|
8
38
|
attr_reader :msg_type
|
9
39
|
attr_reader :legacy_version
|
@@ -95,6 +125,14 @@ module TTTLS13
|
|
95
125
|
end
|
96
126
|
# rubocop: enable Metrics/AbcSize
|
97
127
|
# rubocop: enable Metrics/MethodLength
|
128
|
+
|
129
|
+
# @return [Boolean]
|
130
|
+
def only_appearable_extensions?
|
131
|
+
exs = @extensions.keys - APPEARABLE_CH_EXTENSIONS
|
132
|
+
return true if exs.empty?
|
133
|
+
|
134
|
+
!(exs - DEFINED_EXTENSIONS).empty?
|
135
|
+
end
|
98
136
|
end
|
99
137
|
end
|
100
138
|
end
|
@@ -4,24 +4,25 @@
|
|
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_EE_EXTENSIONS = [
|
9
|
+
ExtensionType::SERVER_NAME,
|
10
|
+
ExtensionType::MAX_FRAGMENT_LENGTH,
|
11
|
+
ExtensionType::SUPPORTED_GROUPS,
|
12
|
+
ExtensionType::USE_SRTP,
|
13
|
+
ExtensionType::HEARTBEAT,
|
14
|
+
ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION,
|
15
|
+
ExtensionType::CLIENT_CERTIFICATE_TYPE,
|
16
|
+
ExtensionType::SERVER_CERTIFICATE_TYPE,
|
17
|
+
ExtensionType::RECORD_SIZE_LIMIT,
|
18
|
+
ExtensionType::EARLY_DATA
|
19
|
+
].freeze
|
20
|
+
private_constant :APPEARABLE_EE_EXTENSIONS
|
21
|
+
|
7
22
|
class EncryptedExtensions
|
8
23
|
attr_reader :msg_type
|
9
24
|
attr_reader :extensions
|
10
25
|
|
11
|
-
# https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1
|
12
|
-
ALLOWED_EXTENSIONS = [
|
13
|
-
ExtensionType::SERVER_NAME,
|
14
|
-
ExtensionType::MAX_FRAGMENT_LENGTH,
|
15
|
-
ExtensionType::SUPPORTED_GROUPS,
|
16
|
-
ExtensionType::USE_SRTP,
|
17
|
-
ExtensionType::HEARTBEAT,
|
18
|
-
ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION,
|
19
|
-
ExtensionType::CLIENT_CERTIFICATE_TYPE,
|
20
|
-
ExtensionType::SERVER_CERTIFICATE_TYPE,
|
21
|
-
ExtensionType::RECORD_SIZE_LIMIT,
|
22
|
-
ExtensionType::EARLY_DATA
|
23
|
-
].freeze
|
24
|
-
|
25
26
|
# @param extensions [TTTLS13::Message::Extensions]
|
26
27
|
def initialize(extensions = Extensions.new)
|
27
28
|
@msg_type = HandshakeType::ENCRYPTED_EXTENSIONS
|
@@ -57,8 +58,11 @@ module TTTLS13
|
|
57
58
|
end
|
58
59
|
|
59
60
|
# @return [Boolean]
|
60
|
-
def
|
61
|
-
|
61
|
+
def only_appearable_extensions?
|
62
|
+
exs = @extensions.keys - APPEARABLE_EE_EXTENSIONS
|
63
|
+
return true if exs.empty?
|
64
|
+
|
65
|
+
!(exs - DEFINED_EXTENSIONS).empty?
|
62
66
|
end
|
63
67
|
end
|
64
68
|
end
|
@@ -83,7 +83,7 @@ module TTTLS13
|
|
83
83
|
end
|
84
84
|
# rubocop: enable Metrics/CyclomaticComplexity
|
85
85
|
|
86
|
-
# @param groups [Array of TTTLS13::
|
86
|
+
# @param groups [Array of TTTLS13::NamedGroup]
|
87
87
|
#
|
88
88
|
# @return [TTTLS13::Message::Extensions::KeyShare]
|
89
89
|
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
@@ -109,6 +109,28 @@ module TTTLS13
|
|
109
109
|
[key_share, priv_keys]
|
110
110
|
end
|
111
111
|
|
112
|
+
# @param groups [TTTLS13::NamedGroup]
|
113
|
+
#
|
114
|
+
# @return [TTTLS13::Message::Extensions::KeyShare]
|
115
|
+
# @return [OpenSSL::PKey::EC.$Object]
|
116
|
+
def self.gen_sh_key_share(group)
|
117
|
+
curve = NamedGroup.curve_name(group)
|
118
|
+
ec = OpenSSL::PKey::EC.new(curve)
|
119
|
+
ec.generate_key!
|
120
|
+
|
121
|
+
key_share = KeyShare.new(
|
122
|
+
msg_type: HandshakeType::SERVER_HELLO,
|
123
|
+
key_share_entry: [
|
124
|
+
KeyShareEntry.new(
|
125
|
+
group: group,
|
126
|
+
key_exchange: ec.public_key.to_octet_string(:uncompressed)
|
127
|
+
)
|
128
|
+
]
|
129
|
+
)
|
130
|
+
|
131
|
+
[key_share, ec]
|
132
|
+
end
|
133
|
+
|
112
134
|
class << self
|
113
135
|
private
|
114
136
|
|
@@ -196,7 +218,7 @@ module TTTLS13
|
|
196
218
|
attr_reader :group
|
197
219
|
attr_reader :key_exchange
|
198
220
|
|
199
|
-
# @param group [TTTLS13::
|
221
|
+
# @param group [TTTLS13::NamedGroup]
|
200
222
|
# @param key_exchange [String]
|
201
223
|
#
|
202
224
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
@@ -5,93 +5,6 @@ module TTTLS13
|
|
5
5
|
using Refinements
|
6
6
|
module Message
|
7
7
|
module Extension
|
8
|
-
module NamedGroup
|
9
|
-
SECP256R1 = "\x00\x17"
|
10
|
-
SECP384R1 = "\x00\x18"
|
11
|
-
SECP521R1 = "\x00\x19"
|
12
|
-
# X25519 = "\x00\x1d" # UNSUPPORTED
|
13
|
-
# X448 = "\x00\x1e" # UNSUPPORTED
|
14
|
-
# FFDHE2048 = "\x01\x00" # UNSUPPORTED
|
15
|
-
# FFDHE3072 = "\x01\x01" # UNSUPPORTED
|
16
|
-
# FFDHE4096 = "\x01\x02" # UNSUPPORTED
|
17
|
-
# FFDHE6144 = "\x01\x03" # UNSUPPORTED
|
18
|
-
# FFDHE8192 = "\x01\x04" # UNSUPPORTED
|
19
|
-
# ffdhe_private_use "\x01\xfc" ~ "\x01\xff"
|
20
|
-
# ecdhe_private_use "\xfe\x00" ~ "\xfe\xff"
|
21
|
-
|
22
|
-
class << self
|
23
|
-
# NOTE:
|
24
|
-
# For secp256r1, secp384r1, and secp521r1
|
25
|
-
#
|
26
|
-
# struct {
|
27
|
-
# uint8 legacy_form = 4;
|
28
|
-
# opaque X[coordinate_length];
|
29
|
-
# opaque Y[coordinate_length];
|
30
|
-
# } UncompressedPointRepresentation;
|
31
|
-
#
|
32
|
-
# @param group [TTTLS13::Message::Extension::NamedGroup]
|
33
|
-
#
|
34
|
-
# @raise [TTTLS13::Error::ErrorAlerts]
|
35
|
-
#
|
36
|
-
# @return [Integer]
|
37
|
-
def key_exchange_len(group)
|
38
|
-
case group
|
39
|
-
when SECP256R1
|
40
|
-
65
|
41
|
-
when SECP384R1
|
42
|
-
97
|
43
|
-
when SECP521R1
|
44
|
-
133
|
45
|
-
# NOTE:
|
46
|
-
# not supported other NamedGroup
|
47
|
-
# when X25519
|
48
|
-
# 32
|
49
|
-
# when X448
|
50
|
-
# 56
|
51
|
-
# when FFDHE2048
|
52
|
-
# 256
|
53
|
-
# when FFDHE4096
|
54
|
-
# 512
|
55
|
-
# when FFDHE6144
|
56
|
-
# 768
|
57
|
-
# when FFDHE8192
|
58
|
-
# 1024
|
59
|
-
else
|
60
|
-
raise Error::ErrorAlerts, :internal_error
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# NOTE:
|
65
|
-
# SECG | ANSI X9.62 | NIST
|
66
|
-
# ------------+---------------+-------------
|
67
|
-
# secp256r1 | prime256v1 | NIST P-256
|
68
|
-
# secp384r1 | | NIST P-384
|
69
|
-
# secp521r1 | | NIST P-521
|
70
|
-
#
|
71
|
-
# https://tools.ietf.org/html/rfc4492#appendix-A
|
72
|
-
#
|
73
|
-
# @param groups [Array of TTTLS13::Message::Extension::NamedGroup]
|
74
|
-
#
|
75
|
-
# @raise [TTTLS13::Error::ErrorAlerts]
|
76
|
-
#
|
77
|
-
# @return [String] EC_builtin_curves
|
78
|
-
def curve_name(group)
|
79
|
-
case group
|
80
|
-
when NamedGroup::SECP256R1
|
81
|
-
'prime256v1'
|
82
|
-
when NamedGroup::SECP384R1
|
83
|
-
'secp384r1'
|
84
|
-
when NamedGroup::SECP521R1
|
85
|
-
'secp521r1'
|
86
|
-
else
|
87
|
-
# NOTE:
|
88
|
-
# not supported other NamedGroup
|
89
|
-
raise Error::ErrorAlerts, :internal_error
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
8
|
class SupportedGroups
|
96
9
|
attr_reader :extension_type
|
97
10
|
attr_reader :named_group_list
|
@@ -6,32 +6,6 @@ Dir[File.dirname(__FILE__) + '/extension/*.rb'].each { |f| require f }
|
|
6
6
|
module TTTLS13
|
7
7
|
using Refinements
|
8
8
|
module Message
|
9
|
-
module ExtensionType
|
10
|
-
SERVER_NAME = "\x00\x00"
|
11
|
-
MAX_FRAGMENT_LENGTH = "\x00\x01"
|
12
|
-
STATUS_REQUEST = "\x00\x05"
|
13
|
-
SUPPORTED_GROUPS = "\x00\x0a"
|
14
|
-
SIGNATURE_ALGORITHMS = "\x00\x0d"
|
15
|
-
USE_SRTP = "\x00\x0e"
|
16
|
-
HEARTBEAT = "\x00\x0f"
|
17
|
-
APPLICATION_LAYER_PROTOCOL_NEGOTIATION = "\x00\x10"
|
18
|
-
SIGNED_CERTIFICATE_TIMESTAMP = "\x00\x12"
|
19
|
-
CLIENT_CERTIFICATE_TYPE = "\x00\x13"
|
20
|
-
SERVER_CERTIFICATE_TYPE = "\x00\x14"
|
21
|
-
PADDING = "\x00\x15"
|
22
|
-
RECORD_SIZE_LIMIT = "\x00\x1c"
|
23
|
-
PRE_SHARED_KEY = "\x00\x29"
|
24
|
-
EARLY_DATA = "\x00\x2a"
|
25
|
-
SUPPORTED_VERSIONS = "\x00\x2b"
|
26
|
-
COOKIE = "\x00\x2c"
|
27
|
-
PSK_KEY_EXCHANGE_MODES = "\x00\x2d"
|
28
|
-
CERTIFICATE_AUTHORITIES = "\x00\x2f"
|
29
|
-
OID_FILTERS = "\x00\x30"
|
30
|
-
POST_HANDSHAKE_AUTH = "\x00\x31"
|
31
|
-
SIGNATURE_ALGORITHMS_CERT = "\x00\x32"
|
32
|
-
KEY_SHARE = "\x00\x33"
|
33
|
-
end
|
34
|
-
|
35
9
|
class Extensions < Hash
|
36
10
|
# @param extensions [Array of TTTLS13::Message::Extension::$Object]
|
37
11
|
#
|
@@ -145,7 +119,7 @@ module TTTLS13
|
|
145
119
|
when ExtensionType::SIGNATURE_ALGORITHMS
|
146
120
|
Extension::SignatureAlgorithms.deserialize(binary)
|
147
121
|
when ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
|
148
|
-
Extension::Alpn
|
122
|
+
Extension::Alpn.deserialize(binary)
|
149
123
|
when ExtensionType::RECORD_SIZE_LIMIT
|
150
124
|
Extension::RecordSizeLimit.deserialize(binary)
|
151
125
|
when ExtensionType::PRE_SHARED_KEY
|
@@ -4,6 +4,12 @@
|
|
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_NST_EXTENSIONS = [
|
9
|
+
ExtensionType::EARLY_DATA
|
10
|
+
].freeze
|
11
|
+
private_constant :APPEARABLE_NST_EXTENSIONS
|
12
|
+
|
7
13
|
class NewSessionTicket
|
8
14
|
attr_reader :msg_type
|
9
15
|
attr_reader :ticket_lifetime
|
@@ -84,6 +90,14 @@ module TTTLS13
|
|
84
90
|
extensions: extensions)
|
85
91
|
end
|
86
92
|
# rubocop: enable Metrics/AbcSize
|
93
|
+
|
94
|
+
# @return [Boolean]
|
95
|
+
def only_appearable_extensions?
|
96
|
+
exs = @extensions.keys - APPEARABLE_NST_EXTENSIONS
|
97
|
+
return true if exs.empty?
|
98
|
+
|
99
|
+
!(exs - DEFINED_EXTENSIONS).empty?
|
100
|
+
end
|
87
101
|
end
|
88
102
|
end
|
89
103
|
end
|
@@ -108,28 +108,31 @@ module TTTLS13
|
|
108
108
|
#
|
109
109
|
# @return [TTTLS13::Message::ContentType]
|
110
110
|
def messages_type
|
111
|
-
types = @messages.map
|
111
|
+
types = @messages.map do |m|
|
112
|
+
if [Message::ClientHello,
|
113
|
+
Message::ServerHello,
|
114
|
+
Message::EncryptedExtensions,
|
115
|
+
Message::Certificate,
|
116
|
+
Message::CertificateVerify,
|
117
|
+
Message::Finished,
|
118
|
+
Message::EndOfEarlyData,
|
119
|
+
Message::NewSessionTicket].include?(m.class)
|
120
|
+
ContentType::HANDSHAKE
|
121
|
+
elsif m.class == ChangeCipherSpec
|
122
|
+
ContentType::CCS
|
123
|
+
elsif m.class == Message::ApplicationData
|
124
|
+
ContentType::APPLICATION_DATA
|
125
|
+
elsif m.class == Message::Alert
|
126
|
+
ContentType::ALERT
|
127
|
+
else
|
128
|
+
raise Error::ErrorAlerts, :internal_error
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
types.uniq!
|
112
133
|
raise Error::ErrorAlerts, :internal_error unless types.length == 1
|
113
134
|
|
114
|
-
|
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
|
135
|
+
types.first
|
133
136
|
end
|
134
137
|
|
135
138
|
class << self
|