tttls1.3 0.2.9 → 0.2.14
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 +4 -4
- data/.github/workflows/ci.yml +32 -0
- data/.rubocop.yml +9 -2
- data/Gemfile +1 -1
- data/README.md +5 -1
- data/Rakefile +66 -7
- data/example/helper.rb +6 -8
- data/example/https_client.rb +1 -1
- data/example/https_client_using_0rtt.rb +3 -3
- data/example/https_client_using_hrr.rb +1 -1
- data/example/https_client_using_hrr_and_ticket.rb +2 -2
- data/example/https_client_using_status_request.rb +31 -0
- data/example/https_client_using_ticket.rb +2 -2
- data/example/https_server.rb +6 -5
- data/interop/client_spec.rb +8 -8
- data/interop/helper.rb +10 -2
- data/interop/server_spec.rb +14 -10
- data/lib/tttls1.3.rb +1 -0
- data/lib/tttls1.3/client.rb +97 -12
- data/lib/tttls1.3/connection.rb +45 -12
- data/lib/tttls1.3/cryptograph.rb +1 -1
- data/lib/tttls1.3/cryptograph/aead.rb +20 -7
- data/lib/tttls1.3/message.rb +1 -1
- data/lib/tttls1.3/message/alert.rb +2 -2
- data/lib/tttls1.3/message/extension/status_request.rb +73 -17
- data/lib/tttls1.3/message/extensions.rb +35 -12
- data/lib/tttls1.3/server.rb +40 -13
- data/lib/tttls1.3/utils.rb +15 -0
- data/lib/tttls1.3/version.rb +1 -1
- data/spec/extensions_spec.rb +16 -0
- data/spec/fixtures/rsa_rsa.crt +15 -15
- data/spec/fixtures/rsa_rsa.key +25 -25
- data/spec/fixtures/rsa_rsa_ocsp.crt +18 -0
- data/spec/fixtures/rsa_rsa_ocsp.key +27 -0
- data/spec/server_hello_spec.rb +1 -1
- data/spec/spec_helper.rb +35 -1
- data/spec/status_request_spec.rb +77 -10
- data/tttls1.3.gemspec +1 -1
- metadata +14 -10
- data/.travis.yml +0 -18
- data/interop/Dockerfile +0 -28
@@ -8,7 +8,7 @@ module TTTLS13
|
|
8
8
|
FATAL = "\x02"
|
9
9
|
end
|
10
10
|
|
11
|
-
# rubocop: disable Layout/
|
11
|
+
# rubocop: disable Layout/HashAlignment
|
12
12
|
ALERT_DESCRIPTION = {
|
13
13
|
close_notify: "\x00",
|
14
14
|
unexpected_message: "\x0a",
|
@@ -38,7 +38,7 @@ module TTTLS13
|
|
38
38
|
certificate_required: "\x74",
|
39
39
|
no_application_protocol: "\x78"
|
40
40
|
}.freeze
|
41
|
-
# rubocop: enable Layout/
|
41
|
+
# rubocop: enable Layout/HashAlignment
|
42
42
|
|
43
43
|
class Alert
|
44
44
|
attr_reader :level
|
@@ -9,23 +9,20 @@ module TTTLS13
|
|
9
9
|
OCSP = "\x01"
|
10
10
|
end
|
11
11
|
|
12
|
-
class
|
12
|
+
class OCSPStatusRequest
|
13
13
|
attr_reader :extension_type
|
14
14
|
attr_reader :responder_id_list
|
15
15
|
attr_reader :request_extensions
|
16
16
|
|
17
|
-
# @param responder_id_list [Array of
|
18
|
-
# @param request_extensions [
|
17
|
+
# @param responder_id_list [Array of OpenSSL::ASN1::ASN1Data]
|
18
|
+
# @param request_extensions [Array of OpenSSL::ASN1::ASN1Data]
|
19
19
|
#
|
20
20
|
# @example
|
21
|
-
#
|
22
|
-
|
23
|
-
# request_extensions: []
|
24
|
-
# )
|
25
|
-
def initialize(responder_id_list: [], request_extensions: '')
|
21
|
+
# OCSPStatusRequest.new
|
22
|
+
def initialize(responder_id_list: [], request_extensions: [])
|
26
23
|
@extension_type = ExtensionType::STATUS_REQUEST
|
27
24
|
@responder_id_list = responder_id_list || []
|
28
|
-
@request_extensions = request_extensions ||
|
25
|
+
@request_extensions = request_extensions || []
|
29
26
|
end
|
30
27
|
|
31
28
|
# @return [String]
|
@@ -34,9 +31,9 @@ module TTTLS13
|
|
34
31
|
binary += CertificateStatusType::OCSP
|
35
32
|
binary += @responder_id_list.length.to_uint16
|
36
33
|
binary += @responder_id_list.map do |id|
|
37
|
-
id.
|
34
|
+
id.to_der.prefix_uint16_length
|
38
35
|
end.join
|
39
|
-
binary += @request_extensions.prefix_uint16_length
|
36
|
+
binary += @request_extensions.map(&:to_der).join.prefix_uint16_length
|
40
37
|
|
41
38
|
@extension_type + binary.prefix_uint16_length
|
42
39
|
end
|
@@ -45,8 +42,9 @@ module TTTLS13
|
|
45
42
|
#
|
46
43
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
47
44
|
#
|
48
|
-
# @return [TTTLS13::Message::Extension::
|
45
|
+
# @return [TTTLS13::Message::Extension::OCSPStatusRequest, nil]
|
49
46
|
# rubocop: disable Metrics/CyclomaticComplexity
|
47
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
50
48
|
def self.deserialize(binary)
|
51
49
|
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
52
50
|
return nil if binary.length < 5 ||
|
@@ -64,14 +62,20 @@ module TTTLS13
|
|
64
62
|
|
65
63
|
re_len = Convert.bin2i(binary.slice(i, 2))
|
66
64
|
i += 2
|
67
|
-
|
65
|
+
exs_bin = binary.slice(i, re_len)
|
66
|
+
begin
|
67
|
+
request_extensions = OpenSSL::ASN1.decode_all(exs_bin)
|
68
|
+
rescue OpenSSL::ASN1::ASN1Error
|
69
|
+
return nil
|
70
|
+
end
|
68
71
|
i += re_len
|
69
72
|
return nil unless i == binary.length
|
70
73
|
|
71
|
-
|
72
|
-
|
74
|
+
OCSPStatusRequest.new(responder_id_list: responder_id_list,
|
75
|
+
request_extensions: request_extensions)
|
73
76
|
end
|
74
77
|
# rubocop: enable Metrics/CyclomaticComplexity
|
78
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
75
79
|
|
76
80
|
class << self
|
77
81
|
private
|
@@ -80,7 +84,7 @@ module TTTLS13
|
|
80
84
|
#
|
81
85
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
82
86
|
#
|
83
|
-
# @return [Array of
|
87
|
+
# @return [Array of ASN1Data, nil] received unparsable binary, nil
|
84
88
|
def deserialize_request_ids(binary)
|
85
89
|
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
86
90
|
|
@@ -92,7 +96,11 @@ module TTTLS13
|
|
92
96
|
id_len = Convert.bin2i(binary.slice(i, 2))
|
93
97
|
i += 2
|
94
98
|
id = binary.slice(i, id_len)
|
95
|
-
|
99
|
+
begin
|
100
|
+
request_ids += OpenSSL::ASN1.decode(id)
|
101
|
+
rescue OpenSSL::ASN1::ASN1Error
|
102
|
+
return nil
|
103
|
+
end
|
96
104
|
i += id_len
|
97
105
|
end
|
98
106
|
return nil if i != binary.length
|
@@ -101,6 +109,54 @@ module TTTLS13
|
|
101
109
|
end
|
102
110
|
end
|
103
111
|
end
|
112
|
+
|
113
|
+
class OCSPResponse
|
114
|
+
attr_reader :extension_type
|
115
|
+
attr_reader :ocsp_response
|
116
|
+
|
117
|
+
# @param ocsp_response [OpenSSL::OCSP::Response]
|
118
|
+
#
|
119
|
+
# @example
|
120
|
+
# OCSPResponse.new(
|
121
|
+
# OpenSSL::OCSP::Response.create(status, basic_resp)
|
122
|
+
# )
|
123
|
+
def initialize(ocsp_response)
|
124
|
+
@extension_type = ExtensionType::STATUS_REQUEST
|
125
|
+
@ocsp_response = ocsp_response
|
126
|
+
end
|
127
|
+
|
128
|
+
# @return [String]
|
129
|
+
def serialize
|
130
|
+
binary = ''
|
131
|
+
binary += CertificateStatusType::OCSP
|
132
|
+
binary += @ocsp_response.to_der.prefix_uint24_length
|
133
|
+
|
134
|
+
@extension_type + binary.prefix_uint16_length
|
135
|
+
end
|
136
|
+
|
137
|
+
# @param binary [String]
|
138
|
+
#
|
139
|
+
# @raise [TTTLS13::Error::ErrorAlerts]
|
140
|
+
#
|
141
|
+
# @return [TTTLS13::Message::Extension::OCSPResponse, nil]
|
142
|
+
def self.deserialize(binary)
|
143
|
+
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
144
|
+
return nil if binary.length < 4 ||
|
145
|
+
binary[0] != CertificateStatusType::OCSP
|
146
|
+
|
147
|
+
res_len = Convert.bin2i(binary.slice(1, 3))
|
148
|
+
res = binary.slice(4, res_len)
|
149
|
+
ocsp_response = nil
|
150
|
+
begin
|
151
|
+
ocsp_response = OpenSSL::OCSP::Response.new(res)
|
152
|
+
rescue OpenSSL::OCSP::OCSPError
|
153
|
+
return nil
|
154
|
+
end
|
155
|
+
return nil if 4 + res_len != binary.length
|
156
|
+
|
157
|
+
OCSPResponse.new(ocsp_response)
|
158
|
+
end
|
159
|
+
end
|
104
160
|
end
|
105
161
|
end
|
106
162
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
# signature_algorithms_cert.rb needs signature_algorithms.rb so that `sort`
|
5
|
+
Dir[File.dirname(__FILE__) + '/extension/*.rb'].sort.each { |f| require f }
|
5
6
|
|
6
7
|
module TTTLS13
|
7
8
|
using Refinements
|
@@ -43,10 +44,11 @@ module TTTLS13
|
|
43
44
|
#
|
44
45
|
# @return [TTTLS13::Message::Extensions]
|
45
46
|
# rubocop: disable Metrics/CyclomaticComplexity
|
47
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
46
48
|
def self.deserialize(binary, msg_type)
|
47
49
|
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
48
50
|
|
49
|
-
|
51
|
+
exs = Extensions.new
|
50
52
|
i = 0
|
51
53
|
while i < binary.length
|
52
54
|
raise Error::ErrorAlerts, :decode_error if i + 4 > binary.length
|
@@ -65,32 +67,41 @@ module TTTLS13
|
|
65
67
|
ex = Extension::UnknownExtension.new(extension_type: extension_type,
|
66
68
|
extension_data: ex_bin)
|
67
69
|
end
|
68
|
-
|
70
|
+
|
71
|
+
# There MUST NOT be more than one extension of the same type in a
|
72
|
+
# given extension block.
|
73
|
+
raise Error::ErrorAlerts, :unsupported_extension \
|
74
|
+
if exs.include?(extension_type)
|
75
|
+
|
76
|
+
exs[extension_type] = ex
|
69
77
|
i += ex_len
|
70
78
|
end
|
71
79
|
raise Error::ErrorAlerts, :decode_error unless i == binary.length
|
72
80
|
|
73
|
-
|
81
|
+
exs
|
74
82
|
end
|
75
83
|
# rubocop: enable Metrics/CyclomaticComplexity
|
84
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
76
85
|
|
77
86
|
# @param key [TTTLS13::Message::ExtensionType]
|
87
|
+
# @param default
|
78
88
|
#
|
79
89
|
# @return [TTTLS13::Message::Extension::$Object]
|
80
|
-
def
|
90
|
+
def fetch(key, default = nil)
|
81
91
|
return nil if super_fetch(key, nil).is_a?(Extension::UnknownExtension)
|
82
92
|
|
83
|
-
super_fetch(key,
|
93
|
+
super_fetch(key, default)
|
84
94
|
end
|
85
95
|
|
86
|
-
|
87
|
-
|
96
|
+
def [](key)
|
97
|
+
fetch(key)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param ex [TTTLS13::Message::Extension::$Object]
|
88
101
|
#
|
89
102
|
# @return [TTTLS13::Message::Extension::$Object]
|
90
|
-
def
|
91
|
-
|
92
|
-
|
93
|
-
super_fetch(key, default)
|
103
|
+
def <<(ex)
|
104
|
+
store(ex.extension_type, ex)
|
94
105
|
end
|
95
106
|
|
96
107
|
class << self
|
@@ -109,12 +120,23 @@ module TTTLS13
|
|
109
120
|
#
|
110
121
|
# @return [TTTLS13::Message::Extension::$Object, nil]
|
111
122
|
# rubocop: disable Metrics/CyclomaticComplexity
|
123
|
+
# rubocop: disable Metrics/MethodLength
|
112
124
|
def deserialize_extension(binary, extension_type, msg_type)
|
113
125
|
raise Error::ErrorAlerts, :internal_error if binary.nil?
|
114
126
|
|
115
127
|
case extension_type
|
116
128
|
when ExtensionType::SERVER_NAME
|
117
129
|
Extension::ServerName.deserialize(binary)
|
130
|
+
when ExtensionType::STATUS_REQUEST
|
131
|
+
if msg_type == HandshakeType::CLIENT_HELLO
|
132
|
+
return Extension::OCSPStatusRequest.deserialize(binary)
|
133
|
+
end
|
134
|
+
|
135
|
+
if msg_type == HandshakeType::CERTIFICATE
|
136
|
+
return Extension::OCSPResponse.deserialize(binary)
|
137
|
+
end
|
138
|
+
|
139
|
+
Extension::UnknownExtension.deserialize(binary, extension_type)
|
118
140
|
when ExtensionType::SUPPORTED_GROUPS
|
119
141
|
Extension::SupportedGroups.deserialize(binary)
|
120
142
|
when ExtensionType::SIGNATURE_ALGORITHMS
|
@@ -142,6 +164,7 @@ module TTTLS13
|
|
142
164
|
end
|
143
165
|
end
|
144
166
|
# rubocop: enable Metrics/CyclomaticComplexity
|
167
|
+
# rubocop: enable Metrics/MethodLength
|
145
168
|
end
|
146
169
|
end
|
147
170
|
end
|
data/lib/tttls1.3/server.rb
CHANGED
@@ -46,11 +46,13 @@ module TTTLS13
|
|
46
46
|
|
47
47
|
DEFAULT_SERVER_SETTINGS = {
|
48
48
|
crt_file: nil,
|
49
|
+
chain_files: nil,
|
49
50
|
key_file: nil,
|
50
51
|
cipher_suites: DEFAULT_SP_CIPHER_SUITES,
|
51
52
|
signature_algorithms: DEFAULT_SP_SIGNATURE_ALGORITHMS,
|
52
53
|
supported_groups: DEFAULT_SP_NAMED_GROUP_LIST,
|
53
54
|
alpn: nil,
|
55
|
+
process_ocsp_response: nil,
|
54
56
|
compatibility_mode: true,
|
55
57
|
loglevel: Logger::WARN
|
56
58
|
}.freeze
|
@@ -75,6 +77,14 @@ module TTTLS13
|
|
75
77
|
klass = @crt.public_key.class
|
76
78
|
@key = klass.new(File.read(@settings[:key_file]))
|
77
79
|
raise Error::ConfigError unless @crt.check_private_key(@key)
|
80
|
+
|
81
|
+
@chain = @settings[:chain_files]&.map do |f|
|
82
|
+
OpenSSL::X509::Certificate.new(File.read(f))
|
83
|
+
end
|
84
|
+
@chain ||= []
|
85
|
+
([@crt] + @chain).each_cons(2) do |cert, sign|
|
86
|
+
raise Error::ConfigError unless cert.verify(sign.public_key)
|
87
|
+
end
|
78
88
|
end
|
79
89
|
|
80
90
|
# NOTE:
|
@@ -227,10 +237,14 @@ module TTTLS13
|
|
227
237
|
|
228
238
|
ch = transcript[CH]
|
229
239
|
rsl = @send_record_size \
|
230
|
-
|
240
|
+
if ch.extensions.include?(Message::ExtensionType::RECORD_SIZE_LIMIT)
|
231
241
|
ee = transcript[EE] = gen_encrypted_extensions(ch, @alpn, rsl)
|
232
242
|
# TODO: [Send CertificateRequest]
|
233
|
-
|
243
|
+
|
244
|
+
# status_request
|
245
|
+
ocsp_response = fetch_ocsp_response \
|
246
|
+
if ch.extensions.include?(Message::ExtensionType::STATUS_REQUEST)
|
247
|
+
ct = transcript[CT] = gen_certificate(@crt, @chain, ocsp_response)
|
234
248
|
digest = CipherSuite.digest(@cipher_suite)
|
235
249
|
cv = transcript[CV] = gen_certificate_verify(
|
236
250
|
@key,
|
@@ -341,7 +355,7 @@ module TTTLS13
|
|
341
355
|
#
|
342
356
|
# @return [TTTLS13::Message::ServerHello]
|
343
357
|
def send_hello_retry_request(ch1, cipher_suite)
|
344
|
-
exs =
|
358
|
+
exs = Message::Extensions.new
|
345
359
|
# supported_versions
|
346
360
|
exs << Message::Extension::SupportedVersions.new(
|
347
361
|
msg_type: Message::HandshakeType::SERVER_HELLO
|
@@ -363,7 +377,7 @@ module TTTLS13
|
|
363
377
|
random: Message::HRR_RANDOM,
|
364
378
|
legacy_session_id_echo: ch1.legacy_session_id,
|
365
379
|
cipher_suite: cipher_suite,
|
366
|
-
extensions:
|
380
|
+
extensions: exs
|
367
381
|
)
|
368
382
|
send_handshakes(Message::ContentType::HANDSHAKE, [sh],
|
369
383
|
Cryptograph::Passer.new)
|
@@ -394,11 +408,18 @@ module TTTLS13
|
|
394
408
|
end
|
395
409
|
|
396
410
|
# @param crt [OpenSSL::X509::Certificate]
|
411
|
+
# @param chain [Array of OpenSSL::X509::Certificate]
|
412
|
+
# @param ocsp_response [OpenSSL::OCSP::Response]
|
397
413
|
#
|
398
414
|
# @return [TTTLS13::Message::Certificate, nil]
|
399
|
-
def gen_certificate(crt)
|
400
|
-
|
401
|
-
|
415
|
+
def gen_certificate(crt, chain = [], ocsp_response = nil)
|
416
|
+
exs = Message::Extensions.new
|
417
|
+
# status_request
|
418
|
+
exs << Message::Extension::OCSPResponse.new(ocsp_response) \
|
419
|
+
unless ocsp_response.nil?
|
420
|
+
ces = [Message::CertificateEntry.new(crt, exs)] \
|
421
|
+
+ (chain || []).map { |c| Message::CertificateEntry.new(c) }
|
422
|
+
Message::Certificate.new(certificate_list: ces)
|
402
423
|
end
|
403
424
|
|
404
425
|
# @param key [OpenSSL::PKey::PKey]
|
@@ -433,7 +454,7 @@ module TTTLS13
|
|
433
454
|
# @return [TTTLS13::Message::Extensions]
|
434
455
|
# @return [OpenSSL::PKey::EC.$Object]
|
435
456
|
def gen_sh_extensions(named_group)
|
436
|
-
exs =
|
457
|
+
exs = Message::Extensions.new
|
437
458
|
# supported_versions: only TLS 1.3
|
438
459
|
exs << Message::Extension::SupportedVersions.new(
|
439
460
|
msg_type: Message::HandshakeType::SERVER_HELLO
|
@@ -444,7 +465,7 @@ module TTTLS13
|
|
444
465
|
= Message::Extension::KeyShare.gen_sh_key_share(named_group)
|
445
466
|
exs << key_share
|
446
467
|
|
447
|
-
[
|
468
|
+
[exs, priv_key]
|
448
469
|
end
|
449
470
|
|
450
471
|
# @param ch [TTTLS13::Message::ClientHello]
|
@@ -453,15 +474,16 @@ module TTTLS13
|
|
453
474
|
#
|
454
475
|
# @return [TTTLS13::Message::Extensions]
|
455
476
|
def gen_ee_extensions(ch, alpn, record_size_limit)
|
456
|
-
exs =
|
477
|
+
exs = Message::Extensions.new
|
457
478
|
|
458
479
|
# server_name
|
459
480
|
exs << Message::Extension::ServerName.new('') \
|
460
481
|
if ch.extensions.include?(Message::ExtensionType::SERVER_NAME)
|
461
482
|
|
462
483
|
# supported_groups
|
463
|
-
exs
|
464
|
-
|
484
|
+
exs << Message::Extension::SupportedGroups.new(
|
485
|
+
@settings[:supported_groups]
|
486
|
+
)
|
465
487
|
|
466
488
|
# alpn
|
467
489
|
exs << Message::Extension::Alpn.new([alpn]) unless alpn.nil?
|
@@ -470,7 +492,7 @@ module TTTLS13
|
|
470
492
|
exs << Message::Extension::RecordSizeLimit.new(record_size_limit) \
|
471
493
|
unless record_size_limit.nil?
|
472
494
|
|
473
|
-
|
495
|
+
exs
|
474
496
|
end
|
475
497
|
|
476
498
|
# @param key [OpenSSL::PKey::PKey]
|
@@ -532,6 +554,11 @@ module TTTLS13
|
|
532
554
|
|
533
555
|
matching_san?(crt, server_name)
|
534
556
|
end
|
557
|
+
|
558
|
+
# @return [OpenSSL::OCSP::Response, nil]
|
559
|
+
def fetch_ocsp_response
|
560
|
+
@settings[:process_ocsp_response]&.call
|
561
|
+
end
|
535
562
|
end
|
536
563
|
# rubocop: enable Metrics/ClassLength
|
537
564
|
end
|
data/lib/tttls1.3/utils.rb
CHANGED
@@ -61,6 +61,21 @@ module TTTLS13
|
|
61
61
|
length.to_uint64 + self
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
65
|
+
refine OpenSSL::X509::Certificate do
|
66
|
+
unless method_defined?(:ocsp_uris)
|
67
|
+
define_method(:ocsp_uris) do
|
68
|
+
aia = extensions.find { |ex| ex.oid == 'authorityInfoAccess' }
|
69
|
+
return nil if aia.nil?
|
70
|
+
|
71
|
+
ostr = OpenSSL::ASN1.decode(aia.to_der).value.last
|
72
|
+
ocsp = OpenSSL::ASN1.decode(ostr.value)
|
73
|
+
.map(&:value)
|
74
|
+
.select { |des| des.first.value == 'OCSP' }
|
75
|
+
ocsp&.map { |o| o[1].value }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
64
79
|
end
|
65
80
|
|
66
81
|
module Convert
|
data/lib/tttls1.3/version.rb
CHANGED
data/spec/extensions_spec.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require_relative 'spec_helper'
|
5
|
+
using Refinements
|
5
6
|
|
6
7
|
RSpec.describe Extensions do
|
7
8
|
context 'empty extensions' do
|
@@ -167,4 +168,19 @@ RSpec.describe Extensions do
|
|
167
168
|
expect(extensions).to include ExtensionType::RECORD_SIZE_LIMIT
|
168
169
|
end
|
169
170
|
end
|
171
|
+
|
172
|
+
context 'duplicated extension_type' do
|
173
|
+
let(:server_name) do
|
174
|
+
ServerName.new('example.com')
|
175
|
+
end
|
176
|
+
|
177
|
+
let(:testbinary) do
|
178
|
+
server_name.serialize * 2
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should raise error, if extension_type get duplicated' do
|
182
|
+
expect { Extensions.deserialize(testbinary, HandshakeType::CLIENT_HELLO) }
|
183
|
+
.to raise_error(ErrorAlerts)
|
184
|
+
end
|
185
|
+
end
|
170
186
|
end
|