tttls1.3 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.rubocop.yml +16 -0
- data/.travis.yml +8 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/Rakefile +133 -0
- data/example/helper.rb +17 -0
- data/example/https_client.rb +32 -0
- data/example/https_client_using_0rtt.rb +64 -0
- data/example/https_client_using_hrr.rb +35 -0
- data/example/https_client_using_ticket.rb +56 -0
- data/lib/tttls1.3/cipher_suites.rb +102 -0
- data/lib/tttls1.3/client.rb +745 -0
- data/lib/tttls1.3/connection.rb +380 -0
- data/lib/tttls1.3/cryptograph/aead.rb +118 -0
- data/lib/tttls1.3/cryptograph/passer.rb +22 -0
- data/lib/tttls1.3/cryptograph.rb +3 -0
- data/lib/tttls1.3/error.rb +22 -0
- data/lib/tttls1.3/key_schedule.rb +242 -0
- data/lib/tttls1.3/message/alert.rb +86 -0
- data/lib/tttls1.3/message/application_data.rb +27 -0
- data/lib/tttls1.3/message/certificate.rb +121 -0
- data/lib/tttls1.3/message/certificate_verify.rb +59 -0
- data/lib/tttls1.3/message/change_cipher_spec.rb +26 -0
- data/lib/tttls1.3/message/client_hello.rb +100 -0
- data/lib/tttls1.3/message/encrypted_extensions.rb +65 -0
- data/lib/tttls1.3/message/end_of_early_data.rb +29 -0
- data/lib/tttls1.3/message/extension/alpn.rb +70 -0
- data/lib/tttls1.3/message/extension/cookie.rb +47 -0
- data/lib/tttls1.3/message/extension/early_data_indication.rb +58 -0
- data/lib/tttls1.3/message/extension/key_share.rb +236 -0
- data/lib/tttls1.3/message/extension/pre_shared_key.rb +205 -0
- data/lib/tttls1.3/message/extension/psk_key_exchange_modes.rb +54 -0
- data/lib/tttls1.3/message/extension/record_size_limit.rb +46 -0
- data/lib/tttls1.3/message/extension/server_name.rb +91 -0
- data/lib/tttls1.3/message/extension/signature_algorithms.rb +69 -0
- data/lib/tttls1.3/message/extension/signature_algorithms_cert.rb +25 -0
- data/lib/tttls1.3/message/extension/status_request.rb +106 -0
- data/lib/tttls1.3/message/extension/supported_groups.rb +145 -0
- data/lib/tttls1.3/message/extension/supported_versions.rb +98 -0
- data/lib/tttls1.3/message/extension/unknown_extension.rb +38 -0
- data/lib/tttls1.3/message/extensions.rb +173 -0
- data/lib/tttls1.3/message/finished.rb +44 -0
- data/lib/tttls1.3/message/new_session_ticket.rb +89 -0
- data/lib/tttls1.3/message/record.rb +232 -0
- data/lib/tttls1.3/message/server_hello.rb +116 -0
- data/lib/tttls1.3/message.rb +48 -0
- data/lib/tttls1.3/sequence_number.rb +31 -0
- data/lib/tttls1.3/signature_scheme.rb +31 -0
- data/lib/tttls1.3/transcript.rb +69 -0
- data/lib/tttls1.3/utils.rb +91 -0
- data/lib/tttls1.3/version.rb +5 -0
- data/lib/tttls1.3.rb +16 -0
- data/spec/aead_spec.rb +95 -0
- data/spec/alert_spec.rb +54 -0
- data/spec/alpn_spec.rb +55 -0
- data/spec/application_data_spec.rb +26 -0
- data/spec/certificate_spec.rb +55 -0
- data/spec/certificate_verify_spec.rb +51 -0
- data/spec/change_cipher_spec_spec.rb +26 -0
- data/spec/cipher_suites_spec.rb +39 -0
- data/spec/client_hello_spec.rb +83 -0
- data/spec/client_spec.rb +319 -0
- data/spec/connection_spec.rb +114 -0
- data/spec/cookie_spec.rb +98 -0
- data/spec/early_data_indication_spec.rb +64 -0
- data/spec/encrypted_extensions_spec.rb +94 -0
- data/spec/error_spec.rb +18 -0
- data/spec/extensions_spec.rb +170 -0
- data/spec/finished_spec.rb +55 -0
- data/spec/key_schedule_spec.rb +198 -0
- data/spec/key_share_spec.rb +199 -0
- data/spec/new_session_ticket_spec.rb +80 -0
- data/spec/pre_shared_key_spec.rb +167 -0
- data/spec/psk_key_exchange_modes_spec.rb +45 -0
- data/spec/record_size_limit_spec.rb +61 -0
- data/spec/record_spec.rb +105 -0
- data/spec/server_hello_spec.rb +101 -0
- data/spec/server_name_spec.rb +110 -0
- data/spec/signature_algorithms_cert_spec.rb +73 -0
- data/spec/signature_algorithms_spec.rb +100 -0
- data/spec/spec_helper.rb +872 -0
- data/spec/status_request_spec.rb +73 -0
- data/spec/supported_groups_spec.rb +79 -0
- data/spec/supported_versions_spec.rb +136 -0
- data/spec/transcript_spec.rb +69 -0
- data/spec/unknown_extension_spec.rb +90 -0
- data/spec/utils_spec.rb +215 -0
- data/tttls1.3.gemspec +25 -0
- metadata +197 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
using Refinements
|
6
|
+
module Message
|
7
|
+
class EncryptedExtensions
|
8
|
+
attr_reader :msg_type
|
9
|
+
attr_reader :extensions
|
10
|
+
|
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
|
+
# @param extensions [TTTLS13::Message::Extensions]
|
26
|
+
def initialize(extensions = Extensions.new)
|
27
|
+
@msg_type = HandshakeType::ENCRYPTED_EXTENSIONS
|
28
|
+
@extensions = extensions || Extensions.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [String]
|
32
|
+
def serialize
|
33
|
+
@msg_type + @extensions.serialize.prefix_uint24_length
|
34
|
+
end
|
35
|
+
|
36
|
+
alias fragment serialize
|
37
|
+
|
38
|
+
# @param binary [String]
|
39
|
+
#
|
40
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
41
|
+
#
|
42
|
+
# @return [TTTLS13::Message::EncryptedExtensions]
|
43
|
+
def self.deserialize(binary)
|
44
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
45
|
+
raise Error::ErrorAlerts, :decode_error if binary.length < 6
|
46
|
+
raise Error::ErrorAlerts, :internal_error \
|
47
|
+
unless binary[0] == HandshakeType::ENCRYPTED_EXTENSIONS
|
48
|
+
|
49
|
+
ee_len = Convert.bin2i(binary.slice(1, 3))
|
50
|
+
exs_len = Convert.bin2i(binary.slice(4, 2))
|
51
|
+
extensions = Extensions.deserialize(binary.slice(6, exs_len),
|
52
|
+
HandshakeType::ENCRYPTED_EXTENSIONS)
|
53
|
+
raise Error::ErrorAlerts, :decode_error \
|
54
|
+
unless exs_len + 2 == ee_len && exs_len + 6 == binary.length
|
55
|
+
|
56
|
+
EncryptedExtensions.new(extensions)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Boolean]
|
60
|
+
def any_forbidden_extensions?
|
61
|
+
!(@extensions.keys - ALLOWED_EXTENSIONS).empty?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
module Message
|
6
|
+
class EndOfEarlyData
|
7
|
+
# @return [String]
|
8
|
+
def serialize
|
9
|
+
''
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param binary [String]
|
13
|
+
#
|
14
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
15
|
+
#
|
16
|
+
# @return [TTTLS13::Message::EndOfEarlyData]
|
17
|
+
def self.deserialize(binary)
|
18
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
19
|
+
raise Error::ErrorAlerts, :decode_error unless binary.length == 4
|
20
|
+
raise Error::ErrorAlerts, :unexpected_message \
|
21
|
+
unless binary[0] == HandshakeType::END_OF_EARLY_DATA
|
22
|
+
raise Error::ErrorAlerts, :decode_error \
|
23
|
+
unless binary == "\x05\x00\x00\x00"
|
24
|
+
|
25
|
+
EndOfEarlyData.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
using Refinements
|
6
|
+
module Message
|
7
|
+
module Extension
|
8
|
+
class Alpn
|
9
|
+
attr_reader :extension_type
|
10
|
+
attr_reader :protocol_name_list
|
11
|
+
|
12
|
+
# @param named_group_list [Array of String]
|
13
|
+
#
|
14
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# Alpn.new(['h2', 'http/1.1'])
|
18
|
+
#
|
19
|
+
# https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
|
20
|
+
def initialize(protocol_name_list)
|
21
|
+
@extension_type \
|
22
|
+
= ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
|
23
|
+
@protocol_name_list = protocol_name_list || []
|
24
|
+
raise Error::ErrorAlerts, :internal_error \
|
25
|
+
if @protocol_name_list.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String]
|
29
|
+
def serialize
|
30
|
+
binary = @protocol_name_list.map(&:prefix_uint8_length).join
|
31
|
+
|
32
|
+
@extension_type + binary.prefix_uint16_length.prefix_uint16_length
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param binary [String]
|
36
|
+
#
|
37
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
38
|
+
#
|
39
|
+
# @return [TTTLS13::Message::Extension::Alpn, nil]
|
40
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
41
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
42
|
+
def self.deserialize(binary)
|
43
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
44
|
+
|
45
|
+
return nil if binary.length < 2
|
46
|
+
|
47
|
+
pnlist_len = Convert.bin2i(binary.slice(0, 2))
|
48
|
+
i = 2
|
49
|
+
protocol_name_list = []
|
50
|
+
while i < pnlist_len + 2
|
51
|
+
return nil if i + 1 > binary.length
|
52
|
+
|
53
|
+
pn_len = Convert.bin2i(binary.slice(i, 1))
|
54
|
+
i += 1
|
55
|
+
return nil if i + pn_len > binary.length
|
56
|
+
|
57
|
+
protocol_name_list << binary.slice(i, pn_len)
|
58
|
+
i += pn_len
|
59
|
+
end
|
60
|
+
return nil unless i == binary.length &&
|
61
|
+
pnlist_len + 2 == binary.length
|
62
|
+
|
63
|
+
Alpn.new(protocol_name_list)
|
64
|
+
end
|
65
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
66
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
using Refinements
|
6
|
+
module Message
|
7
|
+
module Extension
|
8
|
+
class Cookie
|
9
|
+
attr_reader :extension_type
|
10
|
+
attr_reader :cookie
|
11
|
+
|
12
|
+
# @param cookie [String]
|
13
|
+
#
|
14
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
15
|
+
def initialize(cookie)
|
16
|
+
@extension_type = ExtensionType::COOKIE
|
17
|
+
@cookie = cookie || ''
|
18
|
+
raise Error::ErrorAlerts, :internal_error \
|
19
|
+
if @cookie.length > 2**16 - 3
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
def serialize
|
24
|
+
@extension_type + @cookie.prefix_uint16_length.prefix_uint16_length
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param binary [String]
|
28
|
+
#
|
29
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
30
|
+
#
|
31
|
+
# @return [TTTLS13::Message::Extensions::Cookie, nil]
|
32
|
+
def self.deserialize(binary)
|
33
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
34
|
+
|
35
|
+
return nil if binary.length < 2
|
36
|
+
|
37
|
+
cookie_len = Convert.bin2i(binary.slice(0, 2))
|
38
|
+
cookie = binary.slice(2, cookie_len)
|
39
|
+
return nil unless cookie_len + 2 == binary.length &&
|
40
|
+
cookie_len <= 2**16 - 3
|
41
|
+
|
42
|
+
Cookie.new(cookie)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
using Refinements
|
6
|
+
module Message
|
7
|
+
module Extension
|
8
|
+
class EarlyDataIndication
|
9
|
+
attr_reader :extension_type
|
10
|
+
attr_reader :max_early_data_size
|
11
|
+
|
12
|
+
# @param max_early_data_size [Integer, nil]
|
13
|
+
#
|
14
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
15
|
+
def initialize(max_early_data_size = nil)
|
16
|
+
@extension_type = ExtensionType::EARLY_DATA
|
17
|
+
@max_early_data_size = max_early_data_size
|
18
|
+
raise Error::ErrorAlerts, :internal_error \
|
19
|
+
unless @max_early_data_size.nil? || @max_early_data_size < 2**32
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
def serialize
|
24
|
+
binary = ''
|
25
|
+
binary = @max_early_data_size.to_uint32 \
|
26
|
+
unless @max_early_data_size.nil?
|
27
|
+
|
28
|
+
@extension_type + binary.prefix_uint16_length
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param binary [String]
|
32
|
+
# @param msg_type [TTTLS13::Message::ContentType]
|
33
|
+
#
|
34
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
35
|
+
#
|
36
|
+
# @return [TTTLS13::Message::Extensions::EarlyDataIndication, nil]
|
37
|
+
def self.deserialize(binary, msg_type)
|
38
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
39
|
+
|
40
|
+
case msg_type
|
41
|
+
when HandshakeType::CLIENT_HELLO, HandshakeType::ENCRYPTED_EXTENSIONS
|
42
|
+
return nil unless binary.empty?
|
43
|
+
|
44
|
+
max_early_data_size = nil
|
45
|
+
when HandshakeType::NEW_SESSION_TICKET
|
46
|
+
return nil unless binary.length == 4
|
47
|
+
|
48
|
+
max_early_data_size = Convert.bin2i(binary)
|
49
|
+
else
|
50
|
+
raise Error::ErrorAlerts, :internal_error
|
51
|
+
end
|
52
|
+
|
53
|
+
EarlyDataIndication.new(max_early_data_size)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TTTLS13
|
5
|
+
using Refinements
|
6
|
+
module Message
|
7
|
+
module Extension
|
8
|
+
# rubocop: disable Metrics/ClassLength
|
9
|
+
class KeyShare
|
10
|
+
attr_reader :extension_type
|
11
|
+
attr_reader :msg_type
|
12
|
+
attr_reader :key_share_entry
|
13
|
+
|
14
|
+
# @param msg_type [TTTLS13::Message::ContentType]
|
15
|
+
# @param key_share_entry [Array of KeyShareEntry]
|
16
|
+
#
|
17
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
18
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
19
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
20
|
+
def initialize(msg_type:, key_share_entry: [])
|
21
|
+
@extension_type = ExtensionType::KEY_SHARE
|
22
|
+
@msg_type = msg_type
|
23
|
+
@key_share_entry = key_share_entry || []
|
24
|
+
raise Error::ErrorAlerts, :internal_error \
|
25
|
+
unless (@msg_type == HandshakeType::CLIENT_HELLO &&
|
26
|
+
@key_share_entry.length >= 0 &&
|
27
|
+
@key_share_entry.all?(&:valid_key_share_client_hello?)) ||
|
28
|
+
(@msg_type == HandshakeType::SERVER_HELLO &&
|
29
|
+
@key_share_entry.length == 1 &&
|
30
|
+
@key_share_entry.first.valid_key_share_server_hello?) ||
|
31
|
+
(@msg_type == HandshakeType::HELLO_RETRY_REQUEST &&
|
32
|
+
@key_share_entry.length == 1 &&
|
33
|
+
@key_share_entry.first.valid_key_share_hello_retry_request?)
|
34
|
+
end
|
35
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
36
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
37
|
+
|
38
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
def serialize
|
42
|
+
case @msg_type
|
43
|
+
when HandshakeType::CLIENT_HELLO
|
44
|
+
binary = @key_share_entry.map(&:serialize).join.prefix_uint16_length
|
45
|
+
when HandshakeType::SERVER_HELLO, HandshakeType::HELLO_RETRY_REQUEST
|
46
|
+
binary = @key_share_entry.first.serialize
|
47
|
+
else
|
48
|
+
raise Error::ErrorAlerts, :internal_error
|
49
|
+
end
|
50
|
+
@extension_type + binary.prefix_uint16_length
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param binary [String]
|
54
|
+
# @param msg_type [TTTLS13::Message::HandshakeType]
|
55
|
+
#
|
56
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
57
|
+
#
|
58
|
+
# @return [TTTLS13::Message::Extensions::KeyShare, nil]
|
59
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
60
|
+
def self.deserialize(binary, msg_type)
|
61
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
62
|
+
|
63
|
+
case msg_type
|
64
|
+
when HandshakeType::CLIENT_HELLO
|
65
|
+
key_share_entry = deserialize_keyshare_ch(binary)
|
66
|
+
return nil \
|
67
|
+
unless key_share_entry.all?(&:valid_key_share_client_hello?)
|
68
|
+
when HandshakeType::SERVER_HELLO
|
69
|
+
key_share_entry = deserialize_keyshare_sh(binary)
|
70
|
+
return nil \
|
71
|
+
unless key_share_entry.first.valid_key_share_server_hello?
|
72
|
+
when HandshakeType::HELLO_RETRY_REQUEST
|
73
|
+
key_share_entry = deserialize_keyshare_hrr(binary)
|
74
|
+
return nil \
|
75
|
+
unless key_share_entry.first.valid_key_share_hello_retry_request?
|
76
|
+
else
|
77
|
+
raise Error::ErrorAlerts, :internal_error
|
78
|
+
end
|
79
|
+
return nil if key_share_entry.nil?
|
80
|
+
|
81
|
+
KeyShare.new(msg_type: msg_type,
|
82
|
+
key_share_entry: key_share_entry)
|
83
|
+
end
|
84
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
85
|
+
|
86
|
+
# @param groups [Array of TTTLS13::Message::Extension::NamedGroup]
|
87
|
+
#
|
88
|
+
# @return [TTTLS13::Message::Extensions::KeyShare]
|
89
|
+
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
90
|
+
def self.gen_ch_key_share(groups)
|
91
|
+
priv_keys = {}
|
92
|
+
kse = groups.map do |group|
|
93
|
+
curve = NamedGroup.curve_name(group)
|
94
|
+
ec = OpenSSL::PKey::EC.new(curve)
|
95
|
+
ec.generate_key!
|
96
|
+
# store private key to do the key-exchange
|
97
|
+
priv_keys.store(group, ec)
|
98
|
+
KeyShareEntry.new(
|
99
|
+
group: group,
|
100
|
+
key_exchange: ec.public_key.to_octet_string(:uncompressed)
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
key_share = KeyShare.new(
|
105
|
+
msg_type: HandshakeType::CLIENT_HELLO,
|
106
|
+
key_share_entry: kse
|
107
|
+
)
|
108
|
+
|
109
|
+
[key_share, priv_keys]
|
110
|
+
end
|
111
|
+
|
112
|
+
class << self
|
113
|
+
private
|
114
|
+
|
115
|
+
# NOTE:
|
116
|
+
# struct {
|
117
|
+
# KeyShareEntry client_shares<0..2^16-1>;
|
118
|
+
# } KeyShareClientHello;
|
119
|
+
#
|
120
|
+
# @param binary [String]
|
121
|
+
#
|
122
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
123
|
+
#
|
124
|
+
# @return [Array of KeyShareEntry, nil]
|
125
|
+
def deserialize_keyshare_ch(binary)
|
126
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
127
|
+
|
128
|
+
return nil if binary.length < 2
|
129
|
+
|
130
|
+
cs_len = Convert.bin2i(binary.slice(0, 2))
|
131
|
+
key_share_entry = []
|
132
|
+
itr = 2
|
133
|
+
while itr < cs_len + 2
|
134
|
+
return nil if itr + 4 > binary.length
|
135
|
+
|
136
|
+
group = binary.slice(itr, 2)
|
137
|
+
itr += 2
|
138
|
+
ke_len = Convert.bin2i(binary.slice(itr, 2))
|
139
|
+
itr += 2
|
140
|
+
key_exchange = binary.slice(itr, ke_len)
|
141
|
+
key_share_entry << KeyShareEntry.new(group: group,
|
142
|
+
key_exchange: key_exchange)
|
143
|
+
itr += ke_len
|
144
|
+
end
|
145
|
+
return nil unless itr == binary.length
|
146
|
+
|
147
|
+
key_share_entry
|
148
|
+
end
|
149
|
+
|
150
|
+
# NOTE:
|
151
|
+
# struct {
|
152
|
+
# KeyShareEntry server_share;
|
153
|
+
# } KeyShareServerHello;
|
154
|
+
#
|
155
|
+
# @param binary [String]
|
156
|
+
#
|
157
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
158
|
+
#
|
159
|
+
# @return [Array of KeyShareEntry, nil]
|
160
|
+
def deserialize_keyshare_sh(binary)
|
161
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
162
|
+
|
163
|
+
return nil if binary.length < 4
|
164
|
+
|
165
|
+
group = binary.slice(0, 2)
|
166
|
+
ke_len = Convert.bin2i(binary.slice(2, 2))
|
167
|
+
key_exchange = binary.slice(4, ke_len)
|
168
|
+
return nil unless ke_len + 4 == binary.length
|
169
|
+
|
170
|
+
[KeyShareEntry.new(group: group, key_exchange: key_exchange)]
|
171
|
+
end
|
172
|
+
|
173
|
+
# NOTE:
|
174
|
+
# struct {
|
175
|
+
# NamedGroup selected_group;
|
176
|
+
# } KeyShareHelloRetryRequest;
|
177
|
+
#
|
178
|
+
# @param binary [String]
|
179
|
+
#
|
180
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
181
|
+
#
|
182
|
+
# @return [Array of KeyShareEntry, nil]
|
183
|
+
def deserialize_keyshare_hrr(binary)
|
184
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
185
|
+
|
186
|
+
return nil unless binary.length == 2
|
187
|
+
|
188
|
+
group = binary.slice(0, 2)
|
189
|
+
[KeyShareEntry.new(group: group)]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
# rubocop: enable Metrics/ClassLength
|
194
|
+
|
195
|
+
class KeyShareEntry
|
196
|
+
attr_reader :group
|
197
|
+
attr_reader :key_exchange
|
198
|
+
|
199
|
+
# @param group [TTTLS13::Message::Extension::NamedGroup]
|
200
|
+
# @param key_exchange [String]
|
201
|
+
#
|
202
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
203
|
+
def initialize(group:, key_exchange: nil)
|
204
|
+
@group = group || ''
|
205
|
+
@key_exchange = key_exchange || ''
|
206
|
+
raise Error::ErrorAlerts, :internal_error unless @group.length == 2
|
207
|
+
end
|
208
|
+
|
209
|
+
# @return [Boolean]
|
210
|
+
def valid_key_share_client_hello?
|
211
|
+
@group.length == 2 && @key_exchange.length.positive?
|
212
|
+
end
|
213
|
+
|
214
|
+
# @return [Boolean]
|
215
|
+
def valid_key_share_server_hello?
|
216
|
+
@group.length == 2 && @key_exchange.length.positive?
|
217
|
+
end
|
218
|
+
|
219
|
+
# @return [Boolean]
|
220
|
+
def valid_key_share_hello_retry_request?
|
221
|
+
@group.length == 2 && @key_exchange.empty?
|
222
|
+
end
|
223
|
+
|
224
|
+
# @return [String]
|
225
|
+
def serialize
|
226
|
+
binary = ''
|
227
|
+
binary += @group
|
228
|
+
# KeyShareHelloRetryRequest doesn't have key_exchange.
|
229
|
+
binary += @key_exchange.prefix_uint16_length \
|
230
|
+
unless @key_exchange.empty?
|
231
|
+
binary
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|