Browse Source

Refactor TLS server

new-scoring
aeris 2 years ago
parent
commit
f1860ab3ed

+ 2
- 0
lib/cryptcheck.rb View File

@@ -35,7 +35,9 @@ module CryptCheck
autoload :Logger, 'cryptcheck/logger'
autoload :Tls, 'cryptcheck/tls'
module Tls
autoload :Method, 'cryptcheck/tls/method'
autoload :Cipher, 'cryptcheck/tls/cipher'
autoload :Curve, 'cryptcheck/tls/curve'
autoload :Server, 'cryptcheck/tls/server'
autoload :TcpServer, 'cryptcheck/tls/server'
autoload :UdpServer, 'cryptcheck/tls/server'

+ 0
- 10
lib/cryptcheck/tls.rb View File

@@ -7,16 +7,6 @@ module CryptCheck
::CryptCheck.analyze host, port, TcpServer, Grade
end

def self.colorize(cipher)
colors = case
when /^SSL/ =~ cipher
:critical
when :TLSv1_2 == cipher
:good
end
cipher.to_s.colorize colors
end

def self.key_to_s(key)
size, color = case key.type
when :ecc

+ 53
- 0
lib/cryptcheck/tls/cert.rb View File

@@ -0,0 +1,53 @@
module CryptCheck
module Tls
class Cert
SIGNATURE_ALGORITHMS = {
'dsaWithSHA' => %i(sha1 dss),
'dsaWithSHA1' => %i(sha1 dss),
'dsaWithSHA1_2' => %i(sha1 dss),
'dsa_with_SHA224' => %i(sha2 dss),
'dsa_with_SHA256' => %i(sha2 dss),

'mdc2WithRSA' => %i(mdc2 rsa),

'md2WithRSAEncryption' => %i(md2 rsa),

'md4WithRSAEncryption' => %i(md4, rsa),

'md5WithRSA' => %i(md5 rsa),
'md5WithRSAEncryption' => %i(md5 rsa),

'shaWithRSAEncryption' => %i(sha rsa),
'sha1WithRSA' => %i(sha1 rsa),
'sha1WithRSAEncryption' => %i(sha1 rsa),
'sha224WithRSAEncryption' => %i(sha2 rsa),
'sha256WithRSAEncryption' => %i(sha2 rsa),
'sha384WithRSAEncryption' => %i(sha2 rsa),
'sha512WithRSAEncryption' => %i(sha2 rsa),

'ripemd160WithRSA' => %i(ripemd160 rsa),

'ecdsa-with-SHA1' => %i(sha1 ecc),
'ecdsa-with-SHA224' => %i(sha2 ecc),
'ecdsa-with-SHA256' => %i(sha2 ecc),
'ecdsa-with-SHA384' => %i(sha2 ecc),
'ecdsa-with-SHA512' => %i(sha2 ecc),

'id_GostR3411_94_with_GostR3410_2001' => %i(ghost),
'id_GostR3411_94_with_GostR3410_94' => %i(ghost),
'id_GostR3411_94_with_GostR3410_94_cc' => %i(ghost),
'id_GostR3411_94_with_GostR3410_2001_cc' => %i(ghost)
}

%i(md2 mdc2 md4 md5 ripemd160 sha sha1 sha2 rsa dss ecc ghost).each do |name|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}_sig?
@chains.any? do |chain|
SIGNATURE_ALGORITHMS[chain[:cert].signature_algorithm].include? :#{name}
end
end
RUBY_EVAL
end
end
end
end

+ 135
- 115
lib/cryptcheck/tls/cipher.rb View File

@@ -36,11 +36,21 @@ module CryptCheck
ccm: %w(CCM)
}

attr_reader :protocol, :name, :size, :key, :dh
attr_reader :method, :name, :states, :status

def initialize(protocol, cipher, dh=nil, key=nil)
@protocol, @dh, @key = protocol, dh, key
@name, _, @size = cipher
def initialize(method, name)
@method, @name = method, name
fetch_states
end

extend Enumerable

def self.each(&block)
SUPPORTED.each &block
end

def self.[](method)
SUPPORTED[method]
end

TYPES.each do |name, ciphers|
@@ -57,12 +67,15 @@ module CryptCheck
def self.cbc?(cipher)
!aead? cipher
end

def cbc?
!aead?
end
def aead?(cipher)

def self.aead?(cipher)
gcm?(cipher) or ccm?(cipher)
end

def aead?
gcm? or ccm?
end
@@ -79,24 +92,14 @@ module CryptCheck
dhe? or ecdhe?
end

def sweet32?
enc = params[:enc]
return false unless enc # No encryption
block = enc[2]
return false unless block # No block encryption
block <= 64
def ecc?
ecdsa? or ecdhe?
end

def sweet32?
enc = params[:enc]
return false unless enc # No encryption
block = enc[2]
return false unless block # No block encryption
block <= 64
end

def colorize
@name.colorize self.score
size = self.block_size
return false unless size # Not block encryption
size <= 64
end

CHECKS = [
@@ -114,13 +117,13 @@ module CryptCheck

#[:cbc, Proc.new { |s| s.cbc? }, :warning],
#[:dhe, Proc.new { |s| s.dhe? }, :warning],
[:weak_dh, Proc.new do |s|
dh = s.dh
next nil unless dh
status = dh.status
next status if %i(critical error warning).include? status
nil
end ],
# [:weak_dh, Proc.new do |s|
# dh = s.dh
# next nil unless dh
# status = dh.status
# next status if %i(critical error warning).include? status
# nil
# end],
[:no_pfs, Proc.new { |s| not s.pfs? }, :warning],

[:pfs, Proc.new { |s| s.pfs? }, :good],
@@ -128,25 +131,22 @@ module CryptCheck
[:aead, Proc.new { |s| s.aead? }, :good],
]

def states
return @states if @states
@states = { critical: [], error: [], warning: [], good: [], perfect: [], best: [] }
def fetch_states
@states = Status.collect { |s| [s, []] }.to_h
CHECKS.each do |name, check, status|
result = check.call self
states[status ? status : result] << name if result
end
@states

@status = Status[@states.reject { |_, v| v.empty? }.keys]
end

def score
%i(critical error warning good perfect best).each do |s|
return s unless self.states[s].empty?
end
:none
def to_s
states = @states.collect { |k, vs| vs.collect { |v| v.to_s.colorize k }}.flatten.join ' '
"#{@method} #{@name.colorize @status} [#{states}]"
end

PRIORITY = { good: 1, none: 2, warning: 3, error: 4, critical: 5 }

def self.sort(ciphers)
ciphers.sort do |a, b|
error_a, error_b = PRIORITY[a.score], PRIORITY[b.score]
@@ -169,89 +169,109 @@ module CryptCheck
end
end

def self.list(cipher_suite = 'ALL:COMPLEMENTOFALL', protocol: :TLSv1_2)
context = OpenSSL::SSL::SSLContext.new protocol
def self.list(cipher_suite = 'ALL:COMPLEMENTOFALL', method: :TLSv1_2)
context = OpenSSL::SSL::SSLContext.new method
context.ciphers = cipher_suite
ciphers = context.ciphers.collect { |c| self.new protocol, c }
ciphers = context.ciphers.collect { |c| self.new method, c }
self.sort ciphers
end

def params
key_exchange = case
when ecdhe? || ecdh?
[:ecdh, dh]
when dhe? || dh?
[:dh, dh]
when dss?
[:dss, key]
else
[:rsa, key]
end
authentication = case
when ecdsa?
[:ecdsa, key]
when rsa?
[:rsa, key]
when dss?
[:dss, key]
when anonymous?
nil
else
[:rsa, key]
end
encryption = case
when chacha20?
:chacha20
when aes?
:aes
when camellia?
:camellia
when seed?
:seed
when idea?
:idea
when des3?
:'3des'
when des?
:des
when rc4?
:rc4
when rc2?
:rc2
end
mode = case
when gcm?
:gcm
when ccm?
:ccm
when rc4? || chacha20?
nil
else
:cbc
end
b = case encryption
when :rc4
nil
when :'3des', :idea, :rc2
64
when :aes, :camellia, :seed
128
end
encryption = [encryption, size, b, mode] if encryption
mac = case
when poly1305?
[:poly1305, 128]
when sha384?
[:sha384, 384]
when sha256?
[:sha256, 256]
when sha1?
[:sha1, 160]
when md5?
[:md5, 128]
end
{ kex: key_exchange, auth: authentication, enc: encryption, mac: mac, pfs: pfs? }
def kex
case
when ecdhe? || ecdh?
:ecdh
when dhe? || dh?
:dh
when dss?
:dss
else
:rsa
end
end

def auth
case
when ecdsa?
:ecdsa
when rsa?
:rsa
when dss?
:dss
when anonymous?
nil
else
:rsa
end
end

def encryption
case
when chacha20?
:chacha20
when aes?
:aes
when camellia?
:camellia
when seed?
:seed
when idea?
:idea
when des3?
:'3des'
when des?
:des
when rc4?
:rc4
when rc2?
:rc2
end
end

def mode
case
when gcm?
:gcm
when ccm?
:ccm
when rc4? || chacha20?
nil
else
:cbc
end
end

def block_size
case self.encryption
when :'3des', :idea, :rc2
64
when :aes, :camellia, :seed
128
else
nil
end
end

def hmac
case
when poly1305?
[:poly1305, 128]
when sha384?
[:sha384, 384]
when sha256?
[:sha256, 256]
when sha1?
[:sha1, 160]
when md5?
[:md5, 128]
end
end

ALL = 'ALL:COMPLEMENTOFALL'
SUPPORTED = Method.collect do |m|
context = ::OpenSSL::SSL::SSLContext.new m.name
context.ciphers = ALL
[m, context.ciphers.collect { |c| Cipher.new m, c.first }]
end.to_h
end
end
end

+ 33
- 0
lib/cryptcheck/tls/curve.rb View File

@@ -0,0 +1,33 @@
module CryptCheck
module Tls
class Curve
attr_reader :name

def initialize(name)
@name = name
end

# SUPPORTED = %w(sect163k1 sect163r1 sect163r2 sect193r1
# sect193r2 sect233k1 sect233r1 sect239k1 sect283k1 sect283r1
# sect409k1 sect409r1 sect571k1 sect571r1 secp160k1 secp160r1
# secp160r2 secp192k1 secp192r1 secp224k1 secp224r1 secp256k1
# secp256r1 secp384r1 secp521r1
# prime256v1
# brainpoolP256r1 brainpoolP384r1 brainpoolP512r1)
SUPPORTED = %w(secp256k1 sect283k1 sect283r1 secp384r1
sect409k1 sect409r1 secp521r1 sect571k1 sect571r1
prime192v1 prime256v1
brainpoolP256r1 brainpoolP384r1 brainpoolP512r1).collect { |c| self.new c }

extend Enumerable

def self.each(&block)
SUPPORTED.each &block
end

def to_s
@name
end
end
end
end

+ 4
- 0
lib/cryptcheck/tls/fixture.rb View File

@@ -62,6 +62,10 @@ class ::OpenSSL::PKey::EC
self.group.degree
end

def curve
self.group.curve_name
end

def to_s
"ECC #{self.size} bits"
end

+ 30
- 0
lib/cryptcheck/tls/method.rb View File

@@ -0,0 +1,30 @@
module CryptCheck
module Tls
class Method
extend Enumerable

def self.each(&block)
SUPPORTED.each &block
end

attr_reader :name

def initialize(name)
@name = name
end

EXISTING = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
SUPPORTED = (EXISTING & ::OpenSSL::SSL::SSLContext::METHODS).collect { |m| self.new m }

def to_s
colors = case @name
when *%i(SSLv3 SSLv2)
:critical
when :TLSv1_2
:good
end
@name.to_s.colorize colors
end
end
end
end

+ 160
- 200
lib/cryptcheck/tls/server.rb View File

@@ -5,10 +5,8 @@ require 'httparty'
module CryptCheck
module Tls
class Server
TCP_TIMEOUT = 10
SSL_TIMEOUT = 2*TCP_TIMEOUT
EXISTING_METHODS = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
SUPPORTED_METHODS = ::OpenSSL::SSL::SSLContext::METHODS
TCP_TIMEOUT = 10
SSL_TIMEOUT = 2*TCP_TIMEOUT

class TLSException < ::StandardError
end
@@ -30,96 +28,169 @@ module CryptCheck
class ConnectionError < ::StandardError
end

attr_reader :family, :ip, :port, :hostname, :prefered_ciphers, :cert, :cert_valid, :cert_trusted, :dh

def initialize(hostname, family, ip, port)
@hostname, @family, @ip, @port = hostname, family, ip, port
@dh = []
@chains = []
Logger.info { name.colorize :blue }
fetch_prefered_ciphers
check_supported_cipher
verify_certs
check_fallback_scsv
uniq_dh
end

def cipher_size
supported_ciphers.collect { |c| c.size }.min
end
@name = "#@ip:#@port"
@name += " [#@hostname]" if @hostname

def supported_protocols
@supported_ciphers.keys
end
Logger.info { @name.colorize :blue }

fetch_supported_methods
fetch_supported_ciphers
fetch_ecdsa_certs
fetch_supported_curves

# verify_certs

def supported_ciphers
@supported_ciphers.values.flatten 1
check_fallback_scsv

exit
end

def supported_ciphers_by_protocol(protocol)
@supported_ciphers[protocol]
def supported_method?(method)
ssl_client method
Logger.info { "Supported method : #{method}" }
true
rescue TLSException
Logger.debug { "Method not supported : #{method}" }
false
end

EXISTING_METHODS.each do |method|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{method.to_s.downcase}?
!prefered_ciphers[:#{method}].nil?
end
RUBY_EVAL
def fetch_supported_methods
Logger.info { '' }
@supported_methods = Method.select { |m| supported_method? m }
end

def key_status
Status[@keys]
def supported_cipher?(method, cipher)
connection = ssl_client method, cipher
Logger.info { "Supported cipher #{cipher}" }
connection
rescue TLSException
Logger.debug { "Not supported cipher #{cipher}" }
nil
end

def dh_status
Status[@dh]
def fetch_supported_ciphers
Logger.info { '' }
@supported_ciphers = @supported_methods.collect do |method|
ciphers = Cipher[method].collect do |cipher|
connection = supported_cipher? method, cipher
next nil unless connection
[cipher, connection]
end.compact.to_h
[method, ciphers]
end.to_h
end

SIGNATURE_ALGORITHMS = {
'dsaWithSHA' => %i(sha1 dss),
'dsaWithSHA1' => %i(sha1 dss),
'dsaWithSHA1_2' => %i(sha1 dss),
'dsa_with_SHA224' => %i(sha2 dss),
'dsa_with_SHA256' => %i(sha2 dss),
def fetch_ecdsa_certs
@ecdsa_certs = {}

'mdc2WithRSA' => %i(mdc2 rsa),
@supported_ciphers.each do |method, ciphers|
ecdsa = ciphers.keys.detect &:ecdsa?
next unless ecdsa

'md2WithRSAEncryption' => %i(md2 rsa),
@ecdsa_certs = Curve.collect do |curve|
begin
connection = ssl_client method, ecdsa, curves: curve
cert, chain = connection.peer_cert, connection.peer_cert_chain
[curve, { cert: cert, chain: chain }]
rescue TLSException
nil
end
end.compact.to_h

'md4WithRSAEncryption' => %i(md4, rsa),
break
end
end

'md5WithRSA' => %i(md5 rsa),
'md5WithRSAEncryption' => %i(md5 rsa),
def fetch_supported_curves
Logger.info { '' }

'shaWithRSAEncryption' => %i(sha rsa),
'sha1WithRSA' => %i(sha1 rsa),
'sha1WithRSAEncryption' => %i(sha1 rsa),
'sha224WithRSAEncryption' => %i(sha2 rsa),
'sha256WithRSAEncryption' => %i(sha2 rsa),
'sha384WithRSAEncryption' => %i(sha2 rsa),
'sha512WithRSAEncryption' => %i(sha2 rsa),
ecdsa_curve = @ecdsa_certs.keys.first
if ecdsa_curve
# If we have an ECDSA cipher, we need at least the certificate curve to do handshake,
# but with lowest priority to check for ECHDE and not just ECDSA

@supported_ciphers.each do |method, ciphers|
ecdsa = ciphers.keys.detect &:ecdsa?
next unless ecdsa
@supported_curves = Curve.select do |curve|
next true if curve == ecdsa_curve # ECDSA curve is always supported
begin
connection = ssl_client method, ecdsa, curves: [curve, ecdsa_curve]
# Not too fast !!!
# Handshake will **always** succeed, because ECDSA curve is always supported
# So, need to test for the real curve
dh = connection.tmp_key
curve = dh.curve
supported = curve != ecdsa_curve
if supported
Logger.info { "Supported ECC curve #{curve}" }
else
Logger.debug { "Not supported ECC curve #{curve}" }
end
supported
rescue TLSException
false
end
end
break
end
else
# If we have no ECDSA ciphers, ECC supported are only ECDH ones
# So peak an ECDH cipher and test all curves
@supported_ciphers.each do |method, ciphers|
ecdh = ciphers.keys.detect { |c| c.ecdh? or c.ecdhe? }
next unless ecdh
@supported_curves = Curve.select do |curve|
begin
ssl_client method, ecdh, curves: curve
Logger.info { "Supported ECC curve #{curve}" }
rescue TLSException
Logger.debug { "Not supported ECC curve #{curve}" }
false
end
end
break
end
end
end

'ripemd160WithRSA' => %i(ripemd160 rsa),
def check_fallback_scsv
@fallback_scsv = false
if @supported_methods.size > 1
# We will try to connect to the not better supported method
method = @supported_methods[1]

'ecdsa-with-SHA1' => %i(sha1 ecc),
'ecdsa-with-SHA224' => %i(sha2 ecc),
'ecdsa-with-SHA256' => %i(sha2 ecc),
'ecdsa-with-SHA384' => %i(sha2 ecc),
'ecdsa-with-SHA512' => %i(sha2 ecc),
begin
ssl_client method, fallback: true
rescue InappropriateFallback
@fallback_scsv = true
end
else
@fallback_scsv = nil
end

'id_GostR3411_94_with_GostR3410_2001' => %i(ghost),
'id_GostR3411_94_with_GostR3410_94' => %i(ghost),
'id_GostR3411_94_with_GostR3410_94_cc' => %i(ghost),
'id_GostR3411_94_with_GostR3410_2001_cc' => %i(ghost)
}
Logger.info { '' }
text, color = case @fallback_scsv
when true
['Supported', :good]
when false
['Not supported', :error]
when nil
['Not applicable', :unknown]
end
Logger.info { "Fallback SCSV : #{text.colorize color}" }
end

%i(md2 mdc2 md4 md5 ripemd160 sha sha1 sha2 rsa dss ecc ghost).each do |name|
Method.each do |method|
method = method.name
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}_sig?
@chains.any? do |chain|
SIGNATURE_ALGORITHMS[chain[:cert].signature_algorithm].include? :#{name}
end
def #{method.to_s.downcase}?
@supported_methods.detect { |m| m.name == method }
end
RUBY_EVAL
end
@@ -185,12 +256,6 @@ module CryptCheck
end

private
def name
name = "#@ip:#@port"
name += " [#@hostname]" if @hostname
name
end

def connect(&block)
socket = ::Socket.new @family, sock_type
sockaddr = ::Socket.sockaddr_in @port, @ip
@@ -232,14 +297,16 @@ module CryptCheck
retry
rescue ::OpenSSL::SSL::SSLError => e
case e.message
when /state=SSLv.* read server hello A$/
when /state=SSLv3 read server hello A$/
raise TLSNotAvailableException, e
when /state=SSLv.* read server hello A: wrong version number$/
when /state=SSLv3 read server hello A: wrong version number$/
raise MethodNotAvailable, e
when /state=SSLv3 read server hello A: tlsv1 alert protocol version$/
raise MethodNotAvailable, e
when /state=error: no ciphers available$/,
/state=SSLv.* read server hello A: sslv.* alert handshake failure$/
/state=SSLv3 read server hello A: sslv3 alert handshake failure$/
raise CipherNotAvailable, e
when /state=SSLv.* read server hello A: tlsv.* alert inappropriate fallback$/
when /state=SSLv3 read server hello A: tlsv3 alert inappropriate fallback$/
raise InappropriateFallback, e
end
raise
@@ -254,32 +321,29 @@ module CryptCheck
end
end

# SUPPORTED_CURVES = %w(sect163k1 sect163r1 sect163r2 sect193r1
# sect193r2 sect233k1 sect233r1 sect239k1 sect283k1 sect283r1
# sect409k1 sect409r1 sect571k1 sect571r1 secp160k1 secp160r1
# secp160r2 secp192k1 secp192r1 secp224k1 secp224r1 secp256k1
# secp256r1 secp384r1 secp521r1
# prime256v1
# brainpoolP256r1 brainpoolP384r1 brainpoolP512r1)
SUPPORTED_CURVES = %w(secp256k1 sect283k1 sect283r1 secp384r1
sect409k1 sect409r1 secp521r1 sect571k1 sect571r1
prime192v1 prime256v1
brainpoolP256r1 brainpoolP384r1 brainpoolP512r1)

def ssl_client(method, ciphers = %w(ALL COMPLEMENTOFALL), curves = nil, fallback: false, &block)
def ssl_client(method, ciphers = nil, curves: nil, fallback: false, &block)
method = method.name
ssl_context = ::OpenSSL::SSL::SSLContext.new method
ssl_context.enable_fallback_scsv if fallback
ssl_context.ciphers = ciphers.join ':'

ssl_context.ecdh_curves = curves.join ':' if curves
#ssl_context.ecdh_auto = false
#ecdh = OpenSSL::PKey::EC.new('sect163r1').generate_key
#ssl_context.tmp_ecdh_callback = proc { ecdh }
if ciphers
ciphers = [ciphers] unless ciphers.is_a? Enumerable
ciphers = ciphers.collect(&:name).join ':'
else
ciphers = Cipher::ALL
end
ssl_context.ciphers = ciphers

if curves
curves = [curves] unless curves.is_a? Enumerable
curves = curves.collect(&:name).join ':'
ssl_context.ecdh_curves = curves
end

Logger.trace { "Try method=#{method} / ciphers=#{ciphers} / curves=#{curves} / scsv=#{fallback}" }
connect do |socket|
ssl_connect socket, ssl_context, method do |ssl_socket|
return block_given? ? block.call(ssl_socket) : nil
return block_given? ? block.call(ssl_socket) : ssl_socket
end
end
end
@@ -304,110 +368,6 @@ module CryptCheck
@keys = @chains.collect { |c| c[:key] }
end

def prefered_cipher(method)
cipher = ssl_client(method) { |s| Cipher.new method, s.cipher, s.tmp_key }
Logger.info { "Prefered cipher for #{Tls.colorize method} : #{cipher.colorize}" }
cipher
rescue => e
Logger.debug { "Method #{Tls.colorize method} not supported : #{e}" }
nil
end

def fetch_prefered_ciphers
@prefered_ciphers = {}
EXISTING_METHODS.each do |method|
next unless SUPPORTED_METHODS.include? method
@prefered_ciphers[method] = prefered_cipher method
end
raise TLSNotAvailableException unless @prefered_ciphers.any? { |_, c| !c.nil? }
end

def available_ciphers(method)
context = ::OpenSSL::SSL::SSLContext.new method
context.ciphers = %w(ALL COMPLEMENTOFALL)
context.ciphers
end

def supported_cipher?(method, cipher, curves = nil)
cert, chain, dh = ssl_client(method, [cipher], curves) do |s|
[s.peer_cert, s.peer_cert_chain, s.tmp_key]
end
@chains << [cert, chain]
@dh << dh if dh
p dh.group.curve_name
cipher = Cipher.new method, cipher, dh
dh = dh ? " (#{'PFS'.colorize :good} : #{Tls.key_to_s dh})" : ''

states = cipher.states
text = %i(critical error warning good perfect best).collect do |s|
states[s].collect { |t| t.to_s.colorize s }.join ' '
end.reject &:empty?
text = text.join ' '

Logger.info { "#{Tls.colorize method} / #{cipher.colorize}#{dh} [#{text}]" }

cipher
rescue => e
cipher = Cipher.new method, cipher
Logger.debug { "#{Tls.colorize method} / #{cipher.colorize} : Not supported (#{e})" }
nil
end

def check_supported_cipher
Logger.info { '' }
@supported_ciphers = {}
EXISTING_METHODS.each do |method|
next unless SUPPORTED_METHODS.include? method and @prefered_ciphers[method]
supported_ciphers = []

available_ciphers = available_ciphers method
available_ciphers.each do |c|
cipher = Cipher.new method, c
supported = supported_cipher? method, c.first
if supported
if cipher.ecdhe?
SUPPORTED_CURVES.each do |curve|
supported = supported_cipher? method, c.first, [curve]
supported_ciphers << supported if supported
end
else
supported_ciphers << supported
end
end
end
@supported_ciphers[method] = supported_ciphers
end
end

def check_fallback_scsv
Logger.info { '' }
@fallback_scsv = false

methods = @prefered_ciphers.reject { |_, v| v.nil? }.keys
if methods.size > 1
# We will try to connect to the not better supported method
method = methods[1]

begin
ssl_client method, fallback: true
rescue InappropriateFallback
@fallback_scsv = true
end
else
@fallback_scsv = nil
end

text, color = case @fallback_scsv
when true
['Supported', :good]
when false
['Not supported', :error]
when nil
['Not applicable', :unknown]
end
Logger.info { "Fallback SCSV : #{text.colorize color}" }
end

def verify_trust(chain, cert)
store = ::OpenSSL::X509::Store.new
store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT

Loading…
Cancel
Save