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.
- 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
|