Fetch curves preference

new-scoring
aeris 2017-01-07 16:21:16 +01:00
parent d92523e4f2
commit a97668a5f8
5 changed files with 101 additions and 55 deletions

View File

@ -1,9 +1,10 @@
module CryptCheck
module Status
LEVELS = %i(critical error warning good perfect best).freeze
PROBLEMS = %i(critical error warning).freeze
class Status
LEVELS = %i(best perfect good warning error critical).freeze
PROBLEMS = %i(warning error critical).freeze
extend Enumerable
def self.each(&block)
LEVELS.each &block
end
@ -22,17 +23,25 @@ module CryptCheck
self.min PROBLEMS, statuses
end
def self.sort(statuses)
statuses.sort { |a, b| self.compare a, b }
end
def self.compare(a, b)
LEVELS.find_index(a.status) <=> LEVELS.find_index(b.status)
end
private
def self.convert(statuses)
statuses = [ statuses ] unless statuses.respond_to? :first
first = statuses.first
statuses = [statuses] unless statuses.respond_to? :first
first = statuses.first
statuses = statuses.collect &:status if first.respond_to? :status
statuses
end
def self.min(levels, statuses)
return nil if statuses.empty?
(levels & statuses).first
(levels & statuses).last
end
end
end

View File

@ -93,7 +93,7 @@ module CryptCheck
end
def ecc?
ecdsa? or ecdhe?
ecdsa? or ecdhe? or ecdh?
end
def sweet32?
@ -116,14 +116,7 @@ module CryptCheck
[:sweet32, Proc.new { |s| s.sweet32? }, :error],
#[: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],
[:dhe, Proc.new { |s| s.dhe? }, :warning],
[:no_pfs, Proc.new { |s| not s.pfs? }, :warning],
[:pfs, Proc.new { |s| s.pfs? }, :good],
@ -135,16 +128,16 @@ module CryptCheck
@states = Status.collect { |s| [s, []] }.to_h
CHECKS.each do |name, check, status|
result = check.call self
states[status ? status : result] << name if result
@states[status ? status : result] << name if result
end
@status = Status[@states.reject { |_, v| v.empty? }.keys]
statuses = @states.reject { |_, v| v.empty? }.keys
@status = Status[statuses]
end
def to_s(type = :long)
case type
when :long
states = @states.collect { |k, vs| vs.collect { |v| v.to_s.colorize k }}.flatten.join ' '
states = @states.collect { |k, vs| vs.collect { |v| v.to_s.colorize k } }.flatten.join ' '
"#{@method} #{@name.colorize @status} [#{states}]"
when :short
@name.colorize @status
@ -152,6 +145,7 @@ module CryptCheck
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]
@ -271,12 +265,18 @@ module CryptCheck
end
end
ALL = 'ALL:COMPLEMENTOFALL'
def <=>(other)
status = Status.compare self, other
return status if status != 0
@name <=> other.name
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
[m, context.ciphers.collect { |c| Cipher.new m, c.first }.sort ]
end.to_h.freeze
end
end
end

View File

@ -17,7 +17,7 @@ module CryptCheck
SUPPORTED = %w(secp256k1 sect283k1 sect283r1 secp384r1
sect409k1 sect409r1 secp521r1 sect571k1 sect571r1
prime192v1 prime256v1
brainpoolP256r1 brainpoolP384r1 brainpoolP512r1).collect { |c| self.new c }
brainpoolP256r1 brainpoolP384r1 brainpoolP512r1).collect { |c| self.new c }.freeze
extend Enumerable

View File

@ -13,8 +13,8 @@ module CryptCheck
@name = name
end
EXISTING = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
SUPPORTED = (EXISTING & ::OpenSSL::SSL::SSLContext::METHODS).collect { |m| self.new m }
EXISTING = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2).freeze
SUPPORTED = (EXISTING & ::OpenSSL::SSL::SSLContext::METHODS).collect { |m| self.new m }.freeze
def to_s
colors = case @name
@ -25,6 +25,10 @@ module CryptCheck
end
@name.to_s.colorize colors
end
def <=>(other)
EXISTING.find_index(@name) <=> EXISTING.find_index(other.name)
end
end
end
end

View File

@ -40,14 +40,16 @@ module CryptCheck
fetch_supported_methods
fetch_supported_ciphers
fetch_ciphers_preferences
fetch_ecdsa_certs
fetch_supported_curves
fetch_ciphers_preferences
fetch_curves_preference
# verify_certs
check_fallback_scsv
exit
end
def supported_method?(method)
@ -87,6 +89,37 @@ module CryptCheck
end.to_h
end
def fetch_ciphers_preferences
Logger.info { '' }
Logger.info { 'Cipher suite preferences' }
@preferences = @supported_ciphers.collect do |method, ciphers|
ciphers = ciphers.keys
preferences = if ciphers.size < 2
Logger.info { method.to_s + ' : ' + 'not applicable'.colorize(:unknown) }
nil
else
a, b, _ = ciphers
ab = ssl_client(method, [a, b]).cipher.first
ba = ssl_client(method, [b, a]).cipher.first
if ab != ba
Logger.info { method.to_s + ' : ' + 'client preference'.colorize(:warning) }
:client
else
sort = -> (a, b) do
connection = ssl_client method, [a, b]
cipher = connection.cipher.first
cipher == a.name ? -1 : 1
end
preferences = ciphers.sort &sort
Logger.info { method.to_s + ' : ' + preferences.collect { |c| c.to_s :short }.join(', ') }
preferences
end
end
[method, preferences]
end.to_h
end
def fetch_ecdsa_certs
@ecdsa_certs = {}
@ -152,6 +185,7 @@ module CryptCheck
begin
ssl_client method, ecdh, curves: curve
Logger.info { "ECC curve #{curve} : supported" }
true
rescue TLSException
Logger.debug { "ECC curve #{curve} : not supported" }
false
@ -162,34 +196,33 @@ module CryptCheck
end
end
def fetch_ciphers_preferences
Logger.info { '' }
Logger.info { 'Server preferences' }
def fetch_curves_preference
@curves_preference = if @supported_curves.size < 2
Logger.info { 'Curves preference : ' + 'not applicable'.colorize(:unknown) }
nil
else
method, cipher = @supported_ciphers.collect do |method, ciphers|
cipher = ciphers.keys.detect { |c| c.ecdh? or c.ecdhe? }
[method, cipher]
end.detect { |n| !n.nil? }
@preferences = @supported_ciphers.collect do |method, ciphers|
ciphers = ciphers.keys
if ciphers.size < 2
Logger.info { "Preference not applicable for #{method}" }
else
a, b, _ = ciphers
ab = ssl_client(method, [a, b]).cipher.first
ba = ssl_client(method, [b, a]).cipher.first
if ab != ba
Logger.info { 'Server use client preference for '.colorize(:warning) + method.to_s }
:client
else
sort = -> (a, b) do
connection = ssl_client method, [a, b]
cipher = connection.cipher.first
cipher == a.name ? -1 : 1
end
preferences = ciphers.sort &sort
Logger.info { "Cipher preference for #{method} is #{preferences.collect { |c| c.to_s :short }.join ':'}" }
preferences
end
end
[method, preferences]
end.to_h
a, b, _ = @supported_curves
ab = ssl_client(method, cipher, curves: [a, b]).tmp_key.curve
ba = ssl_client(method, cipher, curves: [b, a]).tmp_key.curve
if ab != ba
Logger.info { 'Curves preference : ' + 'client preference'.colorize(:warning) }
:client
else
sort = -> (a, b) do
connection = ssl_client method, cipher, curves: [a, b]
curve = connection.tmp_key.curve
curve == a.name ? -1 : 1
end
preferences = @supported_curves.sort &sort
Logger.info { 'Curves preference : ' + preferences.collect { |c| c.to_s }.join(', ') }
preferences
end
end
end
def check_fallback_scsv
@ -224,7 +257,7 @@ module CryptCheck
method = method.name
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{method.to_s.downcase}?
@supported_methods.detect { |m| m.name == method }
@supported_methods.detect { |m| m.name == method }
end
RUBY_EVAL
end