-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathextensions.rb
218 lines (193 loc) · 7.92 KB
/
extensions.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# encoding: ascii-8bit
# frozen_string_literal: true
# signature_algorithms_cert.rb needs signature_algorithms.rb so that `sort`
Dir[File.dirname(__FILE__) + '/extension/*.rb'].sort.each { |f| require f }
module TTTLS13
using Refinements
module Message
# rubocop: disable Metrics/ClassLength
class Extensions < Hash
# @param extensions [Array of TTTLS13::Message::Extension::$Object]
#
# @example
# Extensions.new([SupportedVersions.new, ServerName.new('example.com')]
def initialize(extensions = [])
extensions.each do |ex|
super[ex.extension_type] = ex
end
end
alias super_fetch fetch
# NOTE:
# "pre_shared_key" MUST be the last extension in the ClientHello
#
# @return [String]
def serialize
except_ch_psk = values.reject do |ex|
ex.extension_type == ExtensionType::PRE_SHARED_KEY &&
ex.msg_type == HandshakeType::CLIENT_HELLO
end
binary = except_ch_psk.map(&:serialize).join
psk = super_fetch(ExtensionType::PRE_SHARED_KEY, nil)
binary += psk.serialize if psk&.msg_type == HandshakeType::CLIENT_HELLO
binary.prefix_uint16_length
end
# @param binary [String]
# @param msg_type [TTTLS13::Message::HandshakeType]
#
# @raise [TTTLS13::Error::ErrorAlerts]
#
# @return [TTTLS13::Message::Extensions]
# rubocop: disable Metrics/CyclomaticComplexity
# rubocop: disable Metrics/PerceivedComplexity
def self.deserialize(binary, msg_type)
raise Error::ErrorAlerts, :internal_error if binary.nil?
exs = Extensions.new
i = 0
while i < binary.length
raise Error::ErrorAlerts, :decode_error if i + 4 > binary.length
extension_type = binary.slice(i, 2)
i += 2
ex_len = Convert.bin2i(binary.slice(i, 2))
i += 2
raise Error::ErrorAlerts, :decode_error if i + ex_len > binary.length
ex_bin = binary.slice(i, ex_len)
ex = deserialize_extension(ex_bin, extension_type, msg_type)
if ex.nil?
# ignore unparsable binary, but only transcript
ex = Extension::UnknownExtension.new(extension_type: extension_type,
extension_data: ex_bin)
end
# There MUST NOT be more than one extension of the same type in a
# given extension block.
raise Error::ErrorAlerts, :unsupported_extension \
if exs.include?(extension_type)
exs[extension_type] = ex
i += ex_len
end
raise Error::ErrorAlerts, :decode_error unless i == binary.length
exs
end
# rubocop: enable Metrics/CyclomaticComplexity
# rubocop: enable Metrics/PerceivedComplexity
# @param key [TTTLS13::Message::ExtensionType]
# @param default
#
# @return [TTTLS13::Message::Extension::$Object]
def fetch(key, default = nil)
return nil if super_fetch(key, nil).is_a?(Extension::UnknownExtension)
super_fetch(key, default)
end
def [](key)
fetch(key)
end
# @param ex [TTTLS13::Message::Extension::$Object]
#
# @return [TTTLS13::Message::Extension::$Object]
def <<(ex)
store(ex.extension_type, ex)
end
# removing and replacing extensions from EncodedClientHelloInner
# with a single "ech_outer_extensions"
#
# for example
# - before
# - self.keys: [A B C D E]
# - param : [D B]
# - after remove_and_replace!
# - self.keys: [A C E B D]
# - return : [A C E ech_outer_extensions[B D]]
# @param outer_extensions [Array of TTTLS13::Message::ExtensionType]
#
# @return [TTTLS13::Message::Extensions] for EncodedClientHelloInner
def remove_and_replace!(outer_extensions)
tmp1 = filter { |k, _| !outer_extensions.include?(k) }
tmp2 = filter { |k, _| outer_extensions.include?(k) }
clear
replaced = Message::Extensions.new
tmp1.each_value { |v| self << v; replaced << v }
tmp2.each_value { |v| self << v }
replaced << Message::Extension::ECHOuterExtensions.new(tmp2.keys) \
unless tmp2.keys.empty?
replaced
end
class << self
private
# NOTE:
# deserialize_extension ignores unparsable extension.
# Received unparsable binary, returns nil, doesn't raise
# ErrorAlerts :decode_error.
#
# @param binary [String]
# @param extension_type [TTTLS13::Message::ExtensionType]
# @param msg_type [TTTLS13::Message::HandshakeType]
#
# @raise [TTTLS13::Error::ErrorAlerts]
#
# @return [TTTLS13::Message::Extension::$Object, nil]
# rubocop: disable Metrics/AbcSize
# rubocop: disable Metrics/CyclomaticComplexity
# rubocop: disable Metrics/MethodLength
# rubocop: disable Metrics/PerceivedComplexity
def deserialize_extension(binary, extension_type, msg_type)
raise Error::ErrorAlerts, :internal_error if binary.nil?
case extension_type
when ExtensionType::SERVER_NAME
Extension::ServerName.deserialize(binary)
when ExtensionType::STATUS_REQUEST
if msg_type == HandshakeType::CLIENT_HELLO
Extension::OCSPStatusRequest.deserialize(binary)
elsif msg_type == HandshakeType::CERTIFICATE
Extension::OCSPResponse.deserialize(binary)
else
Extension::UnknownExtension.deserialize(binary, extension_type)
end
when ExtensionType::SUPPORTED_GROUPS
Extension::SupportedGroups.deserialize(binary)
when ExtensionType::SIGNATURE_ALGORITHMS
Extension::SignatureAlgorithms.deserialize(binary)
when ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION
Extension::Alpn.deserialize(binary)
when ExtensionType::COMPRESS_CERTIFICATE
Extension::CompressCertificate.deserialize(binary)
when ExtensionType::RECORD_SIZE_LIMIT
Extension::RecordSizeLimit.deserialize(binary)
when ExtensionType::PRE_SHARED_KEY
Extension::PreSharedKey.deserialize(binary, msg_type)
when ExtensionType::EARLY_DATA
Extension::EarlyDataIndication.deserialize(binary, msg_type)
when ExtensionType::SUPPORTED_VERSIONS
Extension::SupportedVersions.deserialize(binary, msg_type)
when ExtensionType::COOKIE
Extension::Cookie.deserialize(binary)
when ExtensionType::PSK_KEY_EXCHANGE_MODES
Extension::PskKeyExchangeModes.deserialize(binary)
when ExtensionType::SIGNATURE_ALGORITHMS_CERT
Extension::SignatureAlgorithmsCert.deserialize(binary)
when ExtensionType::KEY_SHARE
Extension::KeyShare.deserialize(binary, msg_type)
when ExtensionType::ENCRYPTED_CLIENT_HELLO
case msg_type
when HandshakeType::CLIENT_HELLO
Extension::ECHClientHello.deserialize(binary)
when HandshakeType::ENCRYPTED_EXTENSIONS
Extension::ECHEncryptedExtensions.deserialize(binary)
when HandshakeType::HELLO_RETRY_REQUEST
Extension::ECHHelloRetryRequest.deserialize(binary)
else
Extension::UnknownExtension.deserialize(binary, extension_type)
end
when ExtensionType::ECH_OUTER_EXTENSIONS
Extension::ECHOuterExtensions.deserialize(binary)
else
Extension::UnknownExtension.deserialize(binary, extension_type)
end
end
# rubocop: enable Metrics/AbcSize
# rubocop: enable Metrics/CyclomaticComplexity
# rubocop: enable Metrics/MethodLength
# rubocop: enable Metrics/PerceivedComplexity
end
end
# rubocop: enable Metrics/ClassLength
end
end