diff --git a/lib/cryptcheck.rb b/lib/cryptcheck.rb index 5903ce2..e0352df 100644 --- a/lib/cryptcheck.rb +++ b/lib/cryptcheck.rb @@ -4,7 +4,7 @@ require 'timeout' require 'yaml' module CryptCheck - MAX_ANALYSIS_DURATION = 120 + MAX_ANALYSIS_DURATION = 600 PARALLEL_ANALYSIS = 10 class AnalysisFailure diff --git a/lib/cryptcheck/tls/cert.rb b/lib/cryptcheck/tls/cert.rb index e620837..324dc2d 100644 --- a/lib/cryptcheck/tls/cert.rb +++ b/lib/cryptcheck/tls/cert.rb @@ -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] diff --git a/lib/cryptcheck/tls/cipher.rb b/lib/cryptcheck/tls/cipher.rb index 3638b9a..dc78f5e 100644 --- a/lib/cryptcheck/tls/cipher.rb +++ b/lib/cryptcheck/tls/cipher.rb @@ -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 diff --git a/lib/cryptcheck/tls/fixture.rb b/lib/cryptcheck/tls/fixture.rb index d76c476..0ed4a5d 100644 --- a/lib/cryptcheck/tls/fixture.rb +++ b/lib/cryptcheck/tls/fixture.rb @@ -114,8 +114,6 @@ class ::OpenSSL::PKey::DH :critical when 1024...2048 :error - else - :warning end end] ].freeze diff --git a/lib/cryptcheck/tls/grade.rb b/lib/cryptcheck/tls/grade.rb index e0e22ce..4508aaf 100644 --- a/lib/cryptcheck/tls/grade.rb +++ b/lib/cryptcheck/tls/grade.rb @@ -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+' diff --git a/lib/cryptcheck/tls/https/grade.rb b/lib/cryptcheck/tls/https/grade.rb index b99ebed..5df6ef2 100644 --- a/lib/cryptcheck/tls/https/grade.rb +++ b/lib/cryptcheck/tls/https/grade.rb @@ -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 diff --git a/lib/cryptcheck/tls/method.rb b/lib/cryptcheck/tls/method.rb index 4ff26c2..68694a1 100644 --- a/lib/cryptcheck/tls/method.rb +++ b/lib/cryptcheck/tls/method.rb @@ -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 diff --git a/lib/cryptcheck/tls/server.rb b/lib/cryptcheck/tls/server.rb index b9bc944..b346411 100644 --- a/lib/cryptcheck/tls/server.rb +++ b/lib/cryptcheck/tls/server.rb @@ -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