Move checks in children again

new-scoring
aeris 6 years ago
parent fd7db31e18
commit c64c154b50
  1. 2
      lib/cryptcheck.rb
  2. 12
      lib/cryptcheck/tls/cert.rb
  3. 103
      lib/cryptcheck/tls/cipher.rb
  4. 2
      lib/cryptcheck/tls/fixture.rb
  5. 57
      lib/cryptcheck/tls/grade.rb
  6. 8
      lib/cryptcheck/tls/https/grade.rb
  7. 18
      lib/cryptcheck/tls/method.rb
  8. 42
      lib/cryptcheck/tls/server.rb

@ -4,7 +4,7 @@ require 'timeout'
require 'yaml'
module CryptCheck
MAX_ANALYSIS_DURATION = 120
MAX_ANALYSIS_DURATION = 600
PARALLEL_ANALYSIS = 10
class AnalysisFailure

@ -109,9 +109,15 @@ module CryptCheck
include ::CryptCheck::State
CHECKS = [:weak_sign, -> (s) do
not (SIGNATURE_ALGORITHMS_X509[s.signature_algorithm] & WEAK_SIGN).empty?
end, :critical].freeze
CHECKS = WEAK_SIGN.collect do |level, hashes|
hashes.collect do |hash|
["#{hash}_sign".to_sym, -> (s) { s.send "#{hash}?" }, level]
end
end.flatten(1).freeze
def checks
CHECKS
end
def children
[self.key]

@ -36,11 +36,10 @@ module CryptCheck
ccm: %w(CCM)
}
attr_reader :method, :name, :states, :status
attr_reader :method, :name
def initialize(method, name)
@method, @name = method, name
fetch_states
end
extend Enumerable
@ -102,70 +101,33 @@ module CryptCheck
size <= 64
end
CHECKS = [
[:psk, Proc.new { |s| s.psk? }, :critical],
[:srp, Proc.new { |s| s.srp? }, :critical],
[:dss, Proc.new { |s| s.dss? }, :critical],
[:anonymous, Proc.new { |s| s.anonymous? }, :critical],
[:null, Proc.new { |s| s.null? }, :critical],
[:export, Proc.new { |s| s.export? }, :critical],
[:des, Proc.new { |s| s.des? }, :critical],
[:md5, Proc.new { |s| s.md5? }, :critical],
[:rc4, Proc.new { |s| s.rc4? }, :error],
[:sweet32, Proc.new { |s| s.sweet32? }, :error],
#[:cbc, Proc.new { |s| s.cbc? }, :warning],
[:dhe, Proc.new { |s| s.dhe? }, :warning],
[:no_pfs, Proc.new { |s| not s.pfs? }, :warning],
[:pfs, Proc.new { |s| s.pfs? }, :good],
[:ecdhe, Proc.new { |s| s.ecdhe? }, :good],
[:aead, Proc.new { |s| s.aead? }, :good],
]
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
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 ' '
"#{@method} #{@name.colorize @status} [#{states}]"
states = self.states.collect { |k, vs| vs.collect { |v| v.to_s.colorize k } }.flatten.join ' '
"#{@method} #{@name.colorize self.status} [#{states}]"
when :short
@name.colorize @status
@name.colorize self.status
end
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]
compare = error_a <=> error_b
next compare unless compare == 0
def <=>(other)
compare = State.compare self, other
return compare unless compare == 0
size_a, size_b = a.size, b.size
compare = size_b <=> size_a
next compare unless compare == 0
size_a, size_b = a.size, b.size
compare = size_b <=> size_a
return compare unless compare == 0
dh_a, dh_b = a.dh, b.dh
next -1 if not dh_a and dh_b
next 1 if dh_a and not dh_b
next a.name <=> b.name if not dh_a and not dh_b
dh_a, dh_b = a.dh, b.dh
return -1 if not dh_a and dh_b
return 1 if dh_a and not dh_b
return a.name <=> b.name if not dh_a and not dh_b
compare = b.dh.size <=> a.dh.size
next compare unless compare == 0
compare = b.dh.size <=> a.dh.size
return compare unless compare == 0
a.name <=> b.name
end
a.name <=> b.name
end
def self.list(cipher_suite = 'ALL:COMPLEMENTOFALL', method: :TLSv1_2)
@ -265,8 +227,33 @@ module CryptCheck
end
end
include State
CHECKS = [
[:dss, -> (c) { c.dss? }, :critical],
[:anonymous, -> (c) { c.anonymous? }, :critical],
[:null, -> (c) { c.null? }, :critical],
[:export, -> (c) { c.export? }, :critical],
[:des, -> (c) { c.des? }, :critical],
[:md5, -> (c) { c.md5? }, :critical],
[:rc4, -> (c) { c.rc4? }, :error],
[:sweet32, -> (c) { c.sweet32? }, :error],
[:no_pfs, -> (c) { not c.pfs? }, :warning],
[:pfs, -> (c) { c.pfs? }, :good],
[:dhe, -> (c) { c.dhe? }, :warning],
[:ecdhe, -> (c) { c.ecdhe? }, :good],
[:aead, -> (c) { c.aead? }, :good]
].freeze
def checks
CHECKS
end
def <=>(other)
status = Status.compare self, other
status = State.compare self, other
return status if status != 0
@name <=> other.name
end
@ -275,8 +262,8 @@ module CryptCheck
SUPPORTED = Method.collect do |m|
context = ::OpenSSL::SSL::SSLContext.new m.to_sym
context.ciphers = ALL
[m, context.ciphers.collect { |c| Cipher.new m, c.first }.sort ]
ciphers = context.ciphers.collect { |c| Cipher.new m, c.first }
[m, ciphers.sort]
end.to_h.freeze
end
end

@ -114,8 +114,6 @@ class ::OpenSSL::PKey::DH
:critical
when 1024...2048
:error
else
:warning
end
end]
].freeze

@ -1,12 +1,12 @@
module CryptCheck
module Tls
class Grade
attr_reader :server, :grade, :states
attr_reader :server, :grade, :status
def initialize(server)
@server = server
@status = @server.status
@checks = checks
@states = calculate_states
@grade = calculate_grade
end
@ -30,39 +30,70 @@ module CryptCheck
Logger.info { "Grade : #{self.grade.colorize color }" }
Logger.info { '' }
Status.each do |color|
states = @states[color]
State.each do |color|
states = @status[color]
Logger.info { "#{color.to_s.capitalize} : #{states.collect { |s| s.to_s.colorize color }.join ' '}" } unless states.empty?
end
end
private
CHECKS = {
critical: %i(
mdc2_sign md2_sign md4_sign md5_sign sha_sign sha1_sign
weak_key
weak_dh
sslv2 sslv3
),
error: %i(
weak_key
weak_dh
),
warning: %i(
weak_key
weak_dh
dhe
),
good: %i(
tls12
),
perfect: %i(
tls12_only
),
best: %i(
)
}.freeze
def checks
end
def calculate_grade
case
when !@states[:critical].empty?
when !@status[:critical].empty?
return 'G'
when !@states[:error].empty?
when !@status[:error].empty?
return 'F'
when !@states[:warning].empty?
when !@status[:warning].empty?
return 'E'
end
goods = @checks.select { |c| c.last == :good }.collect &:first
unless goods.empty?
return 'D' if @states[:good].empty?
return 'C' if @states[:good] != goods
return 'D' if @status[:good].empty?
return 'C' if @status[:good] != goods
end
perfects = @checks.select { |c| c.last == :perfect }.collect &:first
unless perfects.empty?
return 'C+' if @states[:perfect].empty?
return 'B' if @states[:perfect] != perfects
return 'C+' if @status[:perfect].empty?
return 'B' if @status[:perfect] != perfects
end
bests = @checks.select { |c| c.last == :best }.collect &:first
unless bests.empty?
return 'B+' if @states[:best].empty?
return 'A' if @states[:best] != bests
return 'B+' if @status[:best].empty?
return 'A' if @status[:best] != bests
end
'A+'

@ -2,14 +2,6 @@ module CryptCheck
module Tls
module Https
class Grade < Tls::Grade
def checks
super + [
[:hsts, Proc.new { |s| s.hsts? }, :good],
[:hsts_long, Proc.new { |s| s.hsts_long? }, :perfect],
#[:must_staple, Proc.new { |s| s.must_staple? }, :best],
]
end
end
end
end

@ -33,13 +33,17 @@ module CryptCheck
EXISTING.find_index(self) <=> EXISTING.find_index(other)
end
# def eql?(other)
# self.to_sym.eql? other.to_sym
# end
#
# def equal?(other)
# self.to_sym.equal? other.to_sym
# end
include State
CHECKS = [
[:sslv2, -> (s) { s == :SSLv2 }, :critical],
[:sslv3, -> (s) { s == :SSLv3 }, :critical],
[:tlsv1_2, -> (s) { s == :TLSv1_2 }, :good]
]
def checks
CHECKS
end
end
end
end

@ -33,34 +33,18 @@ module CryptCheck
tlsv1_2? and not ssl? and not tlsv1? and not tlsv1_1?
end
def pfs?
uniq_supported_ciphers.any? { |c| c.pfs? }
end
def pfs_only?
uniq_supported_ciphers.all? { |c| c.pfs? }
end
def ecdhe?
uniq_supported_ciphers.any? { |c| c.ecdhe? }
end
def ecdhe_only?
uniq_supported_ciphers.all? { |c| c.ecdhe? }
end
def aead?
uniq_supported_ciphers.any? { |c| c.aead? }
end
def aead_only?
uniq_supported_ciphers.all? { |c| c.aead? }
end
def sweet32?
uniq_supported_ciphers.any? { |c| c.sweet32? }
end
def fallback_scsv?
@fallback_scsv
end
@ -72,31 +56,9 @@ module CryptCheck
include State
CHECKS = [
# Protocols
[:ssl, -> (s) { s.ssl? }, :critical],
[:tls12, -> (s) { s.tlsv1_2? }, :good],
[:tls12_only, -> (s) { s.tlsv1_2_only? }, :perfect],
# Ciphers
[:dss, -> (s) { s.dss? }, :critical],
[:anonymous, -> (s) { s.anonymous? }, :critical],
[:null, -> (s) { s.null? }, :critical],
[:export, -> (s) { s.export? }, :critical],
[:des, -> (s) { s.des? }, :critical],
[:md5, -> (s) { s.md5? }, :critical],
[:rc4, -> (s) { s.rc4? }, :error],
[:sweet32, -> (s) { s.sweet32? }, :error],
[:no_pfs, -> (s) { not s.pfs_only? }, :warning],
[:pfs, -> (s) { s.pfs? }, :good],
[:tlsv1_2_only, -> (s) { s.tlsv1_2_only? }, :perfect],
[:pfs_only, -> (s) { s.pfs_only? }, :perfect],
[:no_ecdhe, -> (s) { not s.ecdhe? }, :warning],
[:ecdhe, -> (s) { s.ecdhe? }, :good],
[:ecdhe_only, -> (s) { s.ecdhe_only? }, :perfect],
[:aead, -> (s) { s.aead? }, :good],
#[:aead_only, -> (s) { s.aead_only? }, :best],
].freeze
@ -112,7 +74,7 @@ module CryptCheck
end
def children
@certs + @dh
@certs + @dh + @supported_methods + uniq_supported_ciphers
end
include Engine

Loading…
Cancel
Save