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
data/interop/helper.rb
CHANGED
@@ -12,6 +12,14 @@ include TTTLS13::Message::Extension
|
|
12
12
|
include TTTLS13::Error
|
13
13
|
# rubocop: enable Style/MixinUsage
|
14
14
|
|
15
|
-
def wait_to_listen(port)
|
16
|
-
|
15
|
+
def wait_to_listen(host, port)
|
16
|
+
loop do
|
17
|
+
s = TCPSocket.open(host, port) # check by TCP handshake
|
18
|
+
rescue # rubocop: disable Style/RescueStandardError
|
19
|
+
sleep(0.2)
|
20
|
+
next
|
21
|
+
else
|
22
|
+
s.close
|
23
|
+
break
|
24
|
+
end
|
17
25
|
end
|
data/interop/server_spec.rb
CHANGED
@@ -9,14 +9,13 @@ PORT = 4433
|
|
9
9
|
tcpserver = TCPServer.open(PORT)
|
10
10
|
|
11
11
|
RSpec.describe Server do
|
12
|
-
# testcases
|
13
12
|
# normal [Boolean] Is this nominal scenarios?
|
14
13
|
# opt [String] openssl s_client options
|
15
14
|
# crt [String] server crt file path
|
16
15
|
# key [String] server key file path
|
17
16
|
# settings [Hash] TTTLS13::Client settins
|
18
|
-
|
19
|
-
|
17
|
+
# rubocop: disable Layout/LineLength
|
18
|
+
testcases = [
|
20
19
|
[
|
21
20
|
true,
|
22
21
|
'-groups P-256:P-384:P-521 -ciphersuites TLS_AES_256_GCM_SHA384',
|
@@ -172,20 +171,24 @@ RSpec.describe Server do
|
|
172
171
|
FIXTURES_DIR + '/rsa_rsa.key',
|
173
172
|
compatibility_mode: false
|
174
173
|
]
|
175
|
-
|
176
|
-
|
174
|
+
]
|
175
|
+
# rubocop: enable Layout/LineLength
|
176
|
+
testcases.each do |normal, opt, crt, key, settings|
|
177
177
|
context 'server interop' do
|
178
178
|
let(:server) do
|
179
|
-
|
179
|
+
loop do
|
180
|
+
@socket = tcpserver.accept
|
181
|
+
break unless @socket.eof?
|
182
|
+
end
|
180
183
|
settings[:crt_file] = crt
|
181
184
|
settings[:key_file] = key
|
182
|
-
Server.new(@socket, settings)
|
185
|
+
Server.new(@socket, **settings)
|
183
186
|
end
|
184
187
|
|
185
188
|
let(:client) do
|
186
|
-
wait_to_listen(PORT)
|
187
|
-
|
188
189
|
ip = Socket.ip_address_list.find(&:ipv4_private?).ip_address
|
190
|
+
wait_to_listen(ip, PORT)
|
191
|
+
|
189
192
|
cmd = 'echo -n ping | openssl s_client ' \
|
190
193
|
+ "-connect local:#{PORT} " \
|
191
194
|
+ '-tls1_3 ' \
|
@@ -195,7 +198,7 @@ RSpec.describe Server do
|
|
195
198
|
+ opt
|
196
199
|
'docker run ' \
|
197
200
|
+ "--volume #{FIXTURES_DIR}:/tmp " \
|
198
|
-
+ "--add-host=local:#{ip}
|
201
|
+
+ "--add-host=local:#{ip} thekuwayama/openssl " \
|
199
202
|
+ "sh -c \"#{cmd}\" 2>&1 >/dev/null"
|
200
203
|
end
|
201
204
|
|
@@ -216,6 +219,7 @@ RSpec.describe Server do
|
|
216
219
|
it "should NOT accept request from openssl s_client ...#{opt}" do
|
217
220
|
spawn(client)
|
218
221
|
expect { server.accept }.to raise_error ErrorAlerts
|
222
|
+
expect { server.close }.to_not raise_error
|
219
223
|
end
|
220
224
|
end
|
221
225
|
end
|
data/lib/tttls1.3.rb
CHANGED
data/lib/tttls1.3/client.rb
CHANGED
@@ -59,6 +59,8 @@ module TTTLS13
|
|
59
59
|
ticket_age_add: nil,
|
60
60
|
ticket_timestamp: nil,
|
61
61
|
record_size_limit: nil,
|
62
|
+
check_certificate_status: false,
|
63
|
+
process_certificate_status: nil,
|
62
64
|
compatibility_mode: true,
|
63
65
|
loglevel: Logger::WARN
|
64
66
|
}.freeze
|
@@ -300,7 +302,8 @@ module TTTLS13
|
|
300
302
|
message = recv_message(receivable_ccs: true, cipher: hs_rcipher)
|
301
303
|
if message.msg_type == Message::HandshakeType::CERTIFICATE
|
302
304
|
ct = transcript[CT] = message
|
303
|
-
|
305
|
+
alert = check_invalid_certificate(ct, transcript[CH])
|
306
|
+
terminate(alert) unless alert.nil?
|
304
307
|
|
305
308
|
@state = ClientState::WAIT_CV
|
306
309
|
elsif message.msg_type == Message::HandshakeType::CERTIFICATE_REQUEST
|
@@ -314,7 +317,8 @@ module TTTLS13
|
|
314
317
|
logger.debug('ClientState::WAIT_CERT')
|
315
318
|
|
316
319
|
ct = transcript[CT] = recv_certificate(hs_rcipher)
|
317
|
-
|
320
|
+
alert = check_invalid_certificate(ct, transcript[CH])
|
321
|
+
terminate(alert) unless alert.nil?
|
318
322
|
|
319
323
|
@state = ClientState::WAIT_CV
|
320
324
|
when ClientState::WAIT_CV
|
@@ -392,6 +396,53 @@ module TTTLS13
|
|
392
396
|
@succeed_early_data
|
393
397
|
end
|
394
398
|
|
399
|
+
# @param res [OpenSSL::OCSP::Response]
|
400
|
+
# @param cert [OpenSSL::X509::Certificate]
|
401
|
+
# @param chain [Array of OpenSSL::X509::Certificate, nil]
|
402
|
+
#
|
403
|
+
# @return [Boolean]
|
404
|
+
#
|
405
|
+
# @example
|
406
|
+
# m = Client.method(:softfail_check_certificate_status)
|
407
|
+
# Client.new(
|
408
|
+
# socket,
|
409
|
+
# hostname,
|
410
|
+
# check_certificate_status: true,
|
411
|
+
# process_certificate_status: m
|
412
|
+
# )
|
413
|
+
def self.softfail_check_certificate_status(res, cert, chain)
|
414
|
+
ocsp_response = res
|
415
|
+
cid = OpenSSL::OCSP::CertificateId.new(cert, chain.first)
|
416
|
+
|
417
|
+
# When NOT received OCSPResponse in TLS handshake, this method will
|
418
|
+
# send OCSPRequest. If ocsp_uri is NOT presented in Certificate, return
|
419
|
+
# true. Also, if it sends OCSPRequest and does NOT receive a HTTPresponse
|
420
|
+
# within 2 seconds, return true.
|
421
|
+
if ocsp_response.nil?
|
422
|
+
uri = cert.ocsp_uris&.find { |u| URI::DEFAULT_PARSER.make_regexp =~ u }
|
423
|
+
return true if uri.nil?
|
424
|
+
|
425
|
+
begin
|
426
|
+
# send OCSP::Request
|
427
|
+
ocsp_request = gen_ocsp_request(cid)
|
428
|
+
Timeout.timeout(2) do
|
429
|
+
ocsp_response = send_ocsp_request(ocsp_request, uri)
|
430
|
+
end
|
431
|
+
|
432
|
+
# check nonce of OCSP::Response
|
433
|
+
check_nonce = ocsp_request.check_nonce(ocsp_response.basic)
|
434
|
+
return true unless [-1, 1].include?(check_nonce)
|
435
|
+
rescue StandardError
|
436
|
+
return true
|
437
|
+
end
|
438
|
+
end
|
439
|
+
return true \
|
440
|
+
if ocsp_response.status != OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL
|
441
|
+
|
442
|
+
status = ocsp_response.basic.status.find { |s| s.first.cmp(cid) }
|
443
|
+
status[1] != OpenSSL::OCSP::V_CERTSTATUS_REVOKED
|
444
|
+
end
|
445
|
+
|
395
446
|
private
|
396
447
|
|
397
448
|
# @return [Boolean]
|
@@ -423,6 +474,9 @@ module TTTLS13
|
|
423
474
|
rsl = @settings[:record_size_limit]
|
424
475
|
return false if !rsl.nil? && (rsl < 64 || rsl > 2**14 + 1)
|
425
476
|
|
477
|
+
return false if @settings[:check_certificate_status] &&
|
478
|
+
@settings[:process_certificate_status].nil?
|
479
|
+
|
426
480
|
true
|
427
481
|
end
|
428
482
|
# rubocop: enable Metrics/AbcSize
|
@@ -471,9 +525,10 @@ module TTTLS13
|
|
471
525
|
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
472
526
|
# rubocop: disable Metrics/AbcSize
|
473
527
|
# rubocop: disable Metrics/CyclomaticComplexity
|
528
|
+
# rubocop: disable Metrics/MethodLength
|
474
529
|
# rubocop: disable Metrics/PerceivedComplexity
|
475
530
|
def gen_ch_extensions
|
476
|
-
exs =
|
531
|
+
exs = Message::Extensions.new
|
477
532
|
# server_name
|
478
533
|
exs << Message::Extension::ServerName.new(@hostname)
|
479
534
|
|
@@ -519,10 +574,15 @@ module TTTLS13
|
|
519
574
|
exs << Message::Extension::Alpn.new(@settings[:alpn].reject(&:empty?)) \
|
520
575
|
if !@settings[:alpn].nil? && !@settings[:alpn].empty?
|
521
576
|
|
522
|
-
|
577
|
+
# status_request
|
578
|
+
exs << Message::Extension::OCSPStatusRequest.new \
|
579
|
+
if @settings[:check_certificate_status]
|
580
|
+
|
581
|
+
[exs, priv_keys]
|
523
582
|
end
|
524
583
|
# rubocop: enable Metrics/AbcSize
|
525
584
|
# rubocop: enable Metrics/CyclomaticComplexity
|
585
|
+
# rubocop: enable Metrics/MethodLength
|
526
586
|
# rubocop: enable Metrics/PerceivedComplexity
|
527
587
|
|
528
588
|
# @param extensions [TTTLS13::Message::Extensions]
|
@@ -611,7 +671,7 @@ module TTTLS13
|
|
611
671
|
# @return [TTTLS13::Message::Extensions]
|
612
672
|
# @return [Hash of NamedGroup => OpenSSL::PKey::EC.$Object]
|
613
673
|
def gen_newch_extensions(ch1, hrr)
|
614
|
-
exs =
|
674
|
+
exs = Message::Extensions.new
|
615
675
|
# key_share
|
616
676
|
if hrr.extensions.include?(Message::ExtensionType::KEY_SHARE)
|
617
677
|
group = hrr.extensions[Message::ExtensionType::KEY_SHARE]
|
@@ -633,7 +693,7 @@ module TTTLS13
|
|
633
693
|
if hrr.extensions.include?(Message::ExtensionType::COOKIE)
|
634
694
|
|
635
695
|
# early_data
|
636
|
-
new_exs = ch1.extensions.merge(
|
696
|
+
new_exs = ch1.extensions.merge(exs)
|
637
697
|
new_exs.delete(Message::ExtensionType::EARLY_DATA)
|
638
698
|
|
639
699
|
[new_exs, priv_keys]
|
@@ -753,16 +813,32 @@ module TTTLS13
|
|
753
813
|
|
754
814
|
# @param ct [TTTLS13::Message::Certificate]
|
755
815
|
# @param ch [TTTLS13::Message::ClientHello]
|
756
|
-
|
757
|
-
|
816
|
+
#
|
817
|
+
# @return [Symbol, nil] return key of ALERT_DESCRIPTION, if invalid
|
818
|
+
def check_invalid_certificate(ct, ch)
|
819
|
+
return :illegal_parameter unless ct.appearable_extensions?
|
758
820
|
|
759
|
-
|
821
|
+
return :unsupported_extension \
|
760
822
|
unless ct.certificate_list.map(&:extensions)
|
761
823
|
.all? { |e| (e.keys - ch.extensions.keys).empty? }
|
762
824
|
|
763
|
-
|
764
|
-
|
765
|
-
|
825
|
+
return :certificate_unknown unless trusted_certificate?(
|
826
|
+
ct.certificate_list,
|
827
|
+
@settings[:ca_file],
|
828
|
+
@hostname
|
829
|
+
)
|
830
|
+
|
831
|
+
if @settings[:check_certificate_status]
|
832
|
+
ee = ct.certificate_list.first
|
833
|
+
ocsp_response = ee.extensions[Message::ExtensionType::STATUS_REQUEST]
|
834
|
+
&.ocsp_response
|
835
|
+
cert = ee.cert_data
|
836
|
+
chain = ct.certificate_list[1..]&.map(&:cert_data)
|
837
|
+
return :bad_certificate_status_response \
|
838
|
+
unless satisfactory_certificate_status?(ocsp_response, cert, chain)
|
839
|
+
end
|
840
|
+
|
841
|
+
nil
|
766
842
|
end
|
767
843
|
|
768
844
|
# @param ct [TTTLS13::Message::Certificate]
|
@@ -784,6 +860,15 @@ module TTTLS13
|
|
784
860
|
)
|
785
861
|
end
|
786
862
|
|
863
|
+
# @param ocsp_response [OpenSSL::OCSP::Response]
|
864
|
+
# @param cert [OpenSSL::X509::Certificate]
|
865
|
+
# @param chain [Array of OpenSSL::X509::Certificate, nil]
|
866
|
+
#
|
867
|
+
# @return [Boolean]
|
868
|
+
def satisfactory_certificate_status?(ocsp_response, cert, chain)
|
869
|
+
@settings[:process_certificate_status]&.call(ocsp_response, cert, chain)
|
870
|
+
end
|
871
|
+
|
787
872
|
# @param nst [TTTLS13::Message::NewSessionTicket]
|
788
873
|
#
|
789
874
|
# @raise [TTTLS13::Error::ErrorAlerts]
|
data/lib/tttls1.3/connection.rb
CHANGED
@@ -254,7 +254,7 @@ module TTTLS13
|
|
254
254
|
end
|
255
255
|
# rubocop: enable Metrics/CyclomaticComplexity
|
256
256
|
|
257
|
-
# @param
|
257
|
+
# @param cipher [TTTLS13::Cryptograph::Aead, Passer]
|
258
258
|
#
|
259
259
|
# @return [TTTLS13::Message::Record]
|
260
260
|
def recv_record(cipher)
|
@@ -273,7 +273,7 @@ module TTTLS13
|
|
273
273
|
|
274
274
|
# Received a protected ccs, peer MUST abort the handshake.
|
275
275
|
if record.type == Message::ContentType::APPLICATION_DATA &&
|
276
|
-
record.messages.
|
276
|
+
record.messages.any? { |m| m.is_a?(Message::ChangeCipherSpec) }
|
277
277
|
terminate(:unexpected_message)
|
278
278
|
end
|
279
279
|
|
@@ -474,8 +474,10 @@ module TTTLS13
|
|
474
474
|
#
|
475
475
|
# @return [Boolean]
|
476
476
|
def trusted_certificate?(certificate_list, ca_file = nil, hostname = nil)
|
477
|
-
|
478
|
-
|
477
|
+
chain = certificate_list.map(&:cert_data).map do |c|
|
478
|
+
OpenSSL::X509::Certificate.new(c)
|
479
|
+
end
|
480
|
+
cert = chain.shift
|
479
481
|
|
480
482
|
# not support CN matching, only support SAN matching
|
481
483
|
return false if !hostname.nil? && !matching_san?(cert, hostname)
|
@@ -483,9 +485,6 @@ module TTTLS13
|
|
483
485
|
store = OpenSSL::X509::Store.new
|
484
486
|
store.set_default_paths
|
485
487
|
store.add_file(ca_file) unless ca_file.nil?
|
486
|
-
chain = certificate_list[1..].map(&:cert_data).map do |c|
|
487
|
-
OpenSSL::X509::Certificate.new(c)
|
488
|
-
end
|
489
488
|
# TODO: parse authorityInfoAccess::CA Issuers
|
490
489
|
ctx = OpenSSL::X509::StoreContext.new(store, cert, chain)
|
491
490
|
now = Time.now
|
@@ -515,22 +514,22 @@ module TTTLS13
|
|
515
514
|
def do_select_signature_algorithms(signature_algorithms, crt)
|
516
515
|
spki = OpenSSL::Netscape::SPKI.new
|
517
516
|
spki.public_key = crt.public_key
|
518
|
-
|
519
|
-
|
517
|
+
pka = OpenSSL::ASN1.decode(spki.to_der)
|
518
|
+
.value.first.value.first.value.first.value.first.value
|
520
519
|
signature_algorithms.select do |sa|
|
521
520
|
case sa
|
522
521
|
when SignatureScheme::ECDSA_SECP256R1_SHA256,
|
523
522
|
SignatureScheme::ECDSA_SECP384R1_SHA384,
|
524
523
|
SignatureScheme::ECDSA_SECP521R1_SHA512
|
525
|
-
|
524
|
+
pka == 'id-ecPublicKey'
|
526
525
|
when SignatureScheme::RSA_PSS_PSS_SHA256,
|
527
526
|
SignatureScheme::RSA_PSS_PSS_SHA384,
|
528
527
|
SignatureScheme::RSA_PSS_PSS_SHA512
|
529
|
-
|
528
|
+
pka == 'rsassaPss'
|
530
529
|
when SignatureScheme::RSA_PSS_RSAE_SHA256,
|
531
530
|
SignatureScheme::RSA_PSS_RSAE_SHA384,
|
532
531
|
SignatureScheme::RSA_PSS_RSAE_SHA512
|
533
|
-
|
532
|
+
pka == 'rsaEncryption'
|
534
533
|
else
|
535
534
|
# RSASSA-PKCS1-v1_5 algorithms refer solely to signatures which appear
|
536
535
|
# in certificates and are not defined for use in signed TLS handshake
|
@@ -539,6 +538,40 @@ module TTTLS13
|
|
539
538
|
end
|
540
539
|
end
|
541
540
|
end
|
541
|
+
|
542
|
+
class << self
|
543
|
+
# @param cid [OpenSSL::OCSP::CertificateId]
|
544
|
+
#
|
545
|
+
# @return [OpenSSL::OCSP::Request]
|
546
|
+
def gen_ocsp_request(cid)
|
547
|
+
ocsp_request = OpenSSL::OCSP::Request.new
|
548
|
+
ocsp_request.add_certid(cid)
|
549
|
+
ocsp_request.add_nonce
|
550
|
+
ocsp_request
|
551
|
+
end
|
552
|
+
|
553
|
+
# @param ocsp_request [OpenSSL::OCSP::Request]
|
554
|
+
# @param uri_string [String]
|
555
|
+
#
|
556
|
+
# @raise [Net::OpenTimeout, OpenSSL::OCSP::OCSPError, URI::$Exception]
|
557
|
+
#
|
558
|
+
# @return [OpenSSL::OCSP::Response, n
|
559
|
+
def send_ocsp_request(ocsp_request, uri_string)
|
560
|
+
# send HTTP POST
|
561
|
+
uri = URI.parse(uri_string)
|
562
|
+
path = uri.path
|
563
|
+
path = '/' if path.nil? || path.empty?
|
564
|
+
http_response = Net::HTTP.start(uri.host, uri.port) do |http|
|
565
|
+
http.post(
|
566
|
+
path,
|
567
|
+
ocsp_request.to_der,
|
568
|
+
'content-type' => 'application/ocsp-request'
|
569
|
+
)
|
570
|
+
end
|
571
|
+
|
572
|
+
OpenSSL::OCSP::Response.new(http_response.body)
|
573
|
+
end
|
574
|
+
end
|
542
575
|
end
|
543
576
|
# rubocop: enable Metrics/ClassLength
|
544
577
|
end
|
data/lib/tttls1.3/cryptograph.rb
CHANGED
@@ -44,8 +44,7 @@ module TTTLS13
|
|
44
44
|
#
|
45
45
|
# @return [String]
|
46
46
|
def encrypt(content, type)
|
47
|
-
reset_cipher
|
48
|
-
cipher = @cipher.encrypt
|
47
|
+
cipher = reset_cipher
|
49
48
|
plaintext = content + type + "\x00" * @length_of_padding
|
50
49
|
cipher.auth_data = additional_data(plaintext.length)
|
51
50
|
encrypted_data = cipher.update(plaintext) + cipher.final
|
@@ -66,8 +65,7 @@ module TTTLS13
|
|
66
65
|
# @return [String]
|
67
66
|
# @return [TTTLS13::Message::ContentType]
|
68
67
|
def decrypt(encrypted_record, auth_data)
|
69
|
-
|
70
|
-
decipher = @cipher.decrypt
|
68
|
+
decipher = reset_decipher
|
71
69
|
auth_tag = encrypted_record[-@auth_tag_len..-1]
|
72
70
|
decipher.auth_tag = auth_tag
|
73
71
|
decipher.auth_data = auth_data # record header of TLSCiphertext
|
@@ -105,11 +103,26 @@ module TTTLS13
|
|
105
103
|
+ ciphertext_len.to_uint16
|
106
104
|
end
|
107
105
|
|
106
|
+
# @return [OpenSSL::Cipher]
|
108
107
|
def reset_cipher
|
109
|
-
@cipher.
|
110
|
-
|
108
|
+
cipher = @cipher.encrypt
|
109
|
+
cipher.reset
|
110
|
+
cipher.key = @write_key
|
111
111
|
iv_len = CipherSuite.iv_len(@cipher_suite)
|
112
|
-
|
112
|
+
cipher.iv = @sequence_number.xor(@write_iv, iv_len)
|
113
|
+
|
114
|
+
cipher
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return [OpenSSL::Cipher]
|
118
|
+
def reset_decipher
|
119
|
+
decipher = @cipher.decrypt
|
120
|
+
decipher.reset
|
121
|
+
decipher.key = @write_key
|
122
|
+
iv_len = CipherSuite.iv_len(@cipher_suite)
|
123
|
+
decipher.iv = @sequence_number.xor(@write_iv, iv_len)
|
124
|
+
|
125
|
+
decipher
|
113
126
|
end
|
114
127
|
|
115
128
|
# @param clear [String]
|
data/lib/tttls1.3/message.rb
CHANGED