diff --git a/.gitignore b/.gitignore index 590791d..2274823 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ Gemfile.lock .rakeTasks .idea/ /html/ -.rspec /rakefile /output/*.html /db/*.sqlite3 diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..a427908 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--require helpers diff --git a/lib/cryptcheck.rb b/lib/cryptcheck.rb index 5941a90..5956960 100644 --- a/lib/cryptcheck.rb +++ b/lib/cryptcheck.rb @@ -5,9 +5,21 @@ require 'yaml' require 'cryptcheck/tls/fixture' module CryptCheck - MAX_ANALYSIS_DURATION = 600 + MAX_ANALYSIS_DURATION = 120 PARALLEL_ANALYSIS = 10 + class AnalysisFailure + attr_reader :error + + def initialize(error) + @error = error + end + + def to_s + @error.to_s + end + end + autoload :Logger, 'cryptcheck/logger' autoload :Tls, 'cryptcheck/tls' module Tls @@ -50,38 +62,54 @@ module CryptCheck def self.addresses(host) begin ip = IPAddr.new host - [[ip.family, ip.to_s, nil]] + return [[ip.family, ip.to_s, nil]] rescue IPAddr::InvalidAddressError - begin - ::Addrinfo.getaddrinfo(host, nil, nil, :STREAM) - .collect { |a| [a.afamily, a.ip_address, host] } - rescue ::SocketError => e - Logger.error { "Unable to resolv #{host} : #{e}" } - [] - end end + ::Addrinfo.getaddrinfo(host, nil, nil, :STREAM) + .collect { |a| [a.afamily, a.ip_address, host] } end - def self.analyze_addresses(host, addresses, port, on_error = TLS_NOT_AVAILABLE, &block) - begin - ::Timeout::timeout MAX_ANALYSIS_DURATION do - addresses.each { |family, ip, host| return block.call family, ip, host } + def self.analyze_addresses(host, addresses, port, server, grade, *args, **kargs) + first = true + addresses.collect do |family, ip| + first ? (first = false) : Logger.info { '' } + key = [host, ip, port] + a = [host, family, ip, port, *args] + begin + ::Timeout::timeout MAX_ANALYSIS_DURATION do + s = if kargs.empty? + server.new *a + else + server.new *a, **kargs + end + g = grade.new s + Logger.info { '' } + g.display + [key, g] + end + rescue Exception => e + e = "Too long analysis (max #{MAX_ANALYSIS_DURATION.humanize})" if e.message == 'execution expired' + Logger.error e + [key, AnalysisFailure.new(e)] end - rescue ::Exception => e - Logger.error e - end - on_error.call host, port + end.to_h end - def self.analyze(host, port, on_error = Tls::TLS_NOT_AVAILABLE, &block) - analyze_addresses host, addresses(host), port, on_error, &block + def self.analyze(host, port, server, grade, *args, **kargs) + addresses = begin + addresses host + rescue ::SocketError => e + Logger::error e + return AnalysisFailure.new "Unable to resolve #{host}" + end + analyze_addresses host, addresses, port, server, grade, *args, **kargs end def self.analyze_hosts(hosts, template, output, groups: nil, &block) results = {} semaphore = ::Mutex.new ::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item[1] } do |description, host| - #hosts.each do |description, host| + #hosts.each do |description, host| result = block.call host.strip semaphore.synchronize do if results.include? description diff --git a/lib/cryptcheck/ssh/server.rb b/lib/cryptcheck/ssh/server.rb index 0253147..8bfee68 100644 --- a/lib/cryptcheck/ssh/server.rb +++ b/lib/cryptcheck/ssh/server.rb @@ -2,14 +2,6 @@ require 'socket' module CryptCheck module Ssh - class SshNotSupportedServer - attr_reader :host, :port - - def initialize(host, port) - @host, @port = host, port - end - end - class Server TCP_TIMEOUT = 10 class SshNotAvailableException < Exception diff --git a/lib/cryptcheck/tls.rb b/lib/cryptcheck/tls.rb index e085880..ab80d3d 100644 --- a/lib/cryptcheck/tls.rb +++ b/lib/cryptcheck/tls.rb @@ -3,18 +3,8 @@ require 'parallel' module CryptCheck module Tls - TLS_NOT_AVAILABLE = Proc.new { |host, port| - TlsNotSupportedGrade.new TlsNotSupportedServer.new host, port - } - def self.analyze(host, port) - ::CryptCheck.analyze host, port do |family, ip, host| - s = TcpServer.new family, ip, port, hostname: host - g = Grade.new s - Logger.info { '' } - g.display - g - end + ::CryptCheck.analyze host, port, TcpServer, Grade end def self.colorize(cipher) diff --git a/lib/cryptcheck/tls/fixture.rb b/lib/cryptcheck/tls/fixture.rb index bce5452..b86bef8 100644 --- a/lib/cryptcheck/tls/fixture.rb +++ b/lib/cryptcheck/tls/fixture.rb @@ -1,5 +1,18 @@ require 'openssl' +class Integer + def humanize + secs = self + [[60, :second], [60, :minute], [24, :hour], [30, :day], [12, :month]].map { |count, name| + if secs > 0 + secs, n = self.divmod count + n = n.to_i + "#{n} #{name}#{n > 1 ? 's' : ''}" + end + }.compact.reverse.join(' ') + end +end + class ::OpenSSL::PKey::EC def type :ecc diff --git a/lib/cryptcheck/tls/grade.rb b/lib/cryptcheck/tls/grade.rb index 3de740f..04d50e0 100644 --- a/lib/cryptcheck/tls/grade.rb +++ b/lib/cryptcheck/tls/grade.rb @@ -1,13 +1,5 @@ module CryptCheck module Tls - class TlsNotSupportedGrade - attr_reader :server, :score, :grade - - def initialize(server) - @server, @score, @grade = server, -1, 'X' - end - end - class Grade attr_reader :server, :protocol_score, :key_exchange_score, :cipher_strengths_score, :score, :grade, :error, :danger, :warning, :success diff --git a/lib/cryptcheck/tls/https.rb b/lib/cryptcheck/tls/https.rb index 33314f0..a934d71 100644 --- a/lib/cryptcheck/tls/https.rb +++ b/lib/cryptcheck/tls/https.rb @@ -2,13 +2,7 @@ module CryptCheck module Tls module Https def self.analyze(host, port=443) - ::CryptCheck.analyze host, port do |family, ip, host| - s = Server.new family, ip, port, hostname: host - g = Grade.new s - Logger.info { '' } - g.display - g - end + ::CryptCheck.analyze host, port, Server, Grade end def self.analyze_file(input, output) diff --git a/lib/cryptcheck/tls/https/server.rb b/lib/cryptcheck/tls/https/server.rb index 9eb8b65..3dc3b6e 100644 --- a/lib/cryptcheck/tls/https/server.rb +++ b/lib/cryptcheck/tls/https/server.rb @@ -6,7 +6,7 @@ module CryptCheck class Server < Tls::TcpServer attr_reader :hsts - def initialize(family, ip, port = 443, hostname: nil) + def initialize(hostname, family, ip, port=443) super fetch_hsts end diff --git a/lib/cryptcheck/tls/server.rb b/lib/cryptcheck/tls/server.rb index 844e082..3b6baca 100644 --- a/lib/cryptcheck/tls/server.rb +++ b/lib/cryptcheck/tls/server.rb @@ -4,14 +4,6 @@ require 'httparty' module CryptCheck module Tls - class TlsNotSupportedServer - attr_reader :hostname, :port - - def initialize(host, port) - @hostname, @port = host, port - end - end - class Server TCP_TIMEOUT = 10 SSL_TIMEOUT = 2*TCP_TIMEOUT @@ -25,18 +17,18 @@ module CryptCheck end class CipherNotAvailable < TLSException end - class Timeout < TLSException + class Timeout < Exception end - class TLSTimeout < TLSException + class TLSTimeout < Timeout end - class ConnectionError < TLSException + class ConnectionError < Exception end attr_reader :family, :ip, :port, :hostname, :prefered_ciphers, :cert, :cert_valid, :cert_trusted, :dh - def initialize(family, ip, port, hostname: nil) - @family, @ip, @port, @hostname = family, ip, port, hostname - @dh = [] + def initialize(hostname, family, ip, port) + @hostname, @family, @ip, @port = hostname, family, ip, port + @dh = [] Logger.info { name.colorize :blue } extract_cert Logger.info { '' } @@ -128,27 +120,21 @@ module CryptCheck def connect(&block) socket = ::Socket.new @family, sock_type sockaddr = ::Socket.sockaddr_in @port, @ip - Logger.trace { "Connecting to #{name}" } + Logger.trace { "Connecting to #{@ip}:#{@port}" } begin status = socket.connect_nonblock sockaddr - Logger.trace { "Connecting to #{name} status : #{status}" } + Logger.trace { "Connecting to #{@ip}:#{@port} status : #{status}" } raise ConnectionError, status unless status == 0 - Logger.trace { "Connected to #{name}" } + Logger.trace { "Connected to #{@ip}:#{@port}" } block_given? ? block.call(socket) : nil rescue ::IO::WaitReadable - Logger.trace { "Waiting for read to #{name}" } - raise Timeout unless IO.select [socket], nil, nil, TCP_TIMEOUT + Logger.trace { "Waiting for read to #{@ip}:#{@port}" } + raise Timeout, "Timeout when connect to #{@ip}:#{@port} (max #{TCP_TIMEOUT.humanize})" unless IO.select [socket], nil, nil, TCP_TIMEOUT retry rescue ::IO::WaitWritable - Logger.trace { "Waiting for write to #{name}" } - raise Timeout unless IO.select nil, [socket], nil, TCP_TIMEOUT + Logger.trace { "Waiting for write to #{@ip}:#{@port}" } + raise Timeout, "Timeout when connect to #{@ip}:#{@port} (max #{TCP_TIMEOUT.humanize})" unless IO.select nil, [socket], nil, TCP_TIMEOUT retry - rescue => e - case e.message - when /^Connection refused/ - raise TLSNotAvailableException, e - end - raise ensure socket.close end @@ -164,14 +150,14 @@ module CryptCheck return block_given? ? block.call(ssl_socket) : nil rescue ::IO::WaitReadable Logger.trace { "Waiting for SSL read to #{name}" } - raise TLSTimeout unless IO.select [socket], nil, nil, SSL_TIMEOUT + raise TLSTimeout, "Timeout when TLS connect to #{@ip}:#{@port} (max #{SSL_TIMEOUT.humanize})" unless IO.select [ssl_socket], nil, nil, SSL_TIMEOUT retry rescue ::IO::WaitWritable Logger.trace { "Waiting for SSL write to #{name}" } - raise TLSTimeout unless IO.select nil, [socket], nil, SSL_TIMEOUT + raise TLSTimeout, "Timeout when TLS connect to #{@ip}:#{@port} (max #{SSL_TIMEOUT.humanize})" unless IO.select nil, [ssl_socket], nil, SSL_TIMEOUT retry rescue ::OpenSSL::SSL::SSLError => e - case e.message + case e when /state=SSLv2 read server hello A$/, /state=SSLv3 read server hello A: wrong version number$/ raise MethodNotAvailable, e @@ -179,13 +165,11 @@ module CryptCheck /state=SSLv3 read server hello A: sslv3 alert handshake failure$/ raise CipherNotAvailable, e end - raise TLSException, e rescue => e - case e.message + case e when /^Connection reset by peer$/ raise MethodNotAvailable, e end - raise TLSException, e ensure ssl_socket.close end @@ -248,7 +232,7 @@ module CryptCheck dh = ssl_client method, [cipher] { |s| s.tmp_key } @dh << dh if dh cipher = Cipher.new method, cipher, dh - dh = dh ? " (#{'DH'.colorize :green} : #{Tls.key_to_s dh})" : '' + dh = dh ? " (#{'DH'.colorize :green} : #{Tls.key_to_s dh})" : '' Logger.info { "#{Tls.colorize method} / #{cipher.colorize} : Supported#{dh}" } cipher rescue TLSException => e diff --git a/lib/cryptcheck/tls/smtp.rb b/lib/cryptcheck/tls/smtp.rb index 3f7d433..0788ee6 100644 --- a/lib/cryptcheck/tls/smtp.rb +++ b/lib/cryptcheck/tls/smtp.rb @@ -2,20 +2,13 @@ module CryptCheck module Tls module Smtp def self.analyze(host, port=25, domain: nil) - domain ||= host - ::CryptCheck.analyze host, port do |family, ip, host| - s = Server.new family, ip, port, hostname: host, domain: domain - g = Grade.new s - Logger.info { '' } - g.display - g - end + ::CryptCheck.analyze host, port, Server, Grade, domain: domain end def self.analyze_domain(domain) - srv = Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::MX).sort_by(&:preference).first - hostname = srv ? srv.exchange.to_s : domain - self.analyze hostname, domain: domain + srv = Resolv::DNS.new.getresources(domain, Resolv::DNS::Resource::IN::MX).sort_by &:preference + hosts = srv.empty? ? [domain] : srv.collect { |s| s.exchange.to_s } + hosts.collect { |h| self.analyze h, domain: domain }.flatten(1) end def self.analyze_file(input, output) diff --git a/lib/cryptcheck/tls/smtp/server.rb b/lib/cryptcheck/tls/smtp/server.rb index b47a992..a6c1dd0 100644 --- a/lib/cryptcheck/tls/smtp/server.rb +++ b/lib/cryptcheck/tls/smtp/server.rb @@ -4,9 +4,9 @@ module CryptCheck class Server < Tls::TcpServer attr_reader :domain - def initialize(family, ip, port, hostname: nil, domain:) + def initialize(hostname, family, ip, port, domain:) @domain = domain - super family, ip, port, hostname: hostname + super end def ssl_connect(socket, context, method, &block) diff --git a/lib/cryptcheck/tls/xmpp/server.rb b/lib/cryptcheck/tls/xmpp/server.rb index 15e5dbb..7e59a48 100644 --- a/lib/cryptcheck/tls/xmpp/server.rb +++ b/lib/cryptcheck/tls/xmpp/server.rb @@ -8,7 +8,7 @@ module CryptCheck class Server < Tls::TcpServer attr_reader :domain - def initialize(family, ip, port=nil, hostname: nil, domain: nil, type: :s2s) + def initialize(hostname, family, ip, port=nil, domain: nil, type: :s2s) domain ||= hostname @type, @domain = type, domain port = case type diff --git a/spec/cryptcheck/tls_spec.rb b/spec/cryptcheck/tls_spec.rb new file mode 100644 index 0000000..5dbd9fc --- /dev/null +++ b/spec/cryptcheck/tls_spec.rb @@ -0,0 +1,136 @@ +describe CryptCheck::Tls do + describe '#analyze' do + it 'return 1 grade with IPv4' do + grades = server(host: '127.0.0.1') do + CryptCheck::Tls.analyze '127.0.0.1', 5000 + end + + expect(grades.size).to be 1 + expect_grade grades, '127.0.0.1', '127.0.0.1', 5000, :ipv4 + end + + it 'return 1 grade with IPv6' do + grades = server(host: '::1') do + CryptCheck::Tls.analyze '::1', 5000 + end + + expect(grades.size).to be 1 + expect_grade grades, '::1', '::1', 5000, :ipv6 + end + + it 'return 2 grades with hostname (IPv4 & IPv6)' do + addresses = %w(127.0.0.1 ::1) + allow(Addrinfo).to receive(:getaddrinfo).with('localhost', nil, nil, :STREAM) do + addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } + end + + grades = server(host: '::') do + CryptCheck::Tls.analyze 'localhost', 5000 + end + + expect_grade grades, 'localhost', '127.0.0.1', 5000, :ipv4 + expect_grade grades, 'localhost', '::1', 5000, :ipv6 + end + + it 'return error if DNS resolution problem' do + allow(Addrinfo).to receive(:getaddrinfo).with('localhost', nil, nil, :STREAM) + .and_raise SocketError, 'getaddrinfo: Name or service not known' + + grades = server do + CryptCheck::Tls.analyze 'localhost', 5000 + end + + expect(grades).to be_a CryptCheck::AnalysisFailure + expect(grades.to_s).to eq 'Unable to resolve localhost' + end + + it 'return error if analysis too long' do + stub_const 'CryptCheck::MAX_ANALYSIS_DURATION', 1 + allow(CryptCheck::Tls::Server).to receive(:new) { sleep 2 } + + grades = server do + CryptCheck::Tls.analyze 'localhost', 5000 + end + + expect_grade_error grades, 'localhost', '127.0.0.1', 5000, + 'Too long analysis (max 1 second)' + end + + it 'return error if unable to connect' do + addresses = %w(127.0.0.1 ::1) + allow(Addrinfo).to receive(:getaddrinfo).with('localhost', nil, nil, :STREAM) do + addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } + end + + grades = server(host: '::1') do + CryptCheck::Tls.analyze 'localhost', 5000 + end + + expect_grade_error grades, 'localhost', '127.0.0.1', 5000, + 'Connection refused - connect(2) for 127.0.0.1:5000' + expect_grade grades, 'localhost', '::1', 5000, :ipv6 + end + + it 'return error if TCP timeout' do + stub_const 'CryptCheck::Tls::Server::TCP_TIMEOUT', 1 + addresses = %w(127.0.0.1 ::1) + allow(Addrinfo).to receive(:getaddrinfo).with('localhost', nil, nil, :STREAM) do + addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } + end + original = IO.method :select + allow(IO).to receive(:select) do |*args, &block| + socket = [args[0]&.first, args[1]&.first].compact.first + next nil if socket.is_a?(Socket) && (socket.local_address.afamily == Socket::AF_INET) + original.call *args, &block + end + + grades = server(host: '::') do + CryptCheck::Tls.analyze 'localhost', 5000 + end + + expect_grade_error grades, 'localhost', '127.0.0.1', 5000, + 'Timeout when connect to 127.0.0.1:5000 (max 1 second)' + expect_grade grades, 'localhost', '::1', 5000, :ipv6 + end + + it 'return error if TLS timeout' do + stub_const 'CryptCheck::Tls::Server::SSL_TIMEOUT', 1 + addresses = %w(127.0.0.1 ::1) + allow(Addrinfo).to receive(:getaddrinfo).with('localhost', nil, nil, :STREAM) do + addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } + end + original = IO.method :select + allow(IO).to receive(:select) do |*args, &block| + socket = [args[0]&.first, args[1]&.first].compact.first + next nil if socket.is_a?(OpenSSL::SSL::SSLSocket) && (socket.io.local_address.afamily == Socket::AF_INET) + original.call *args, &block + end + + grades = server(host: '::') do + CryptCheck::Tls.analyze 'localhost', 5000 + end + + expect_grade_error grades, 'localhost', '127.0.0.1', 5000, + 'Timeout when TLS connect to 127.0.0.1:5000 (max 1 second)' + expect_grade grades, 'localhost', '::1', 5000, :ipv6 + end + + it 'return error if plain server' do + stub_const 'CryptCheck::Tls::Server::SSL_TIMEOUT', 1 + addresses = %w(127.0.0.1 ::1) + allow(Addrinfo).to receive(:getaddrinfo).with('localhost', nil, nil, :STREAM) do + addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } + end + + grades = plain_server(host: '127.0.0.1') do + server(host: '::1') do + CryptCheck::Tls.analyze 'localhost', 5000 + end + end + + expect_grade_error grades, 'localhost', '127.0.0.1', 5000, + 'Timeout when TLS connect to 127.0.0.1:5000 (max 1 second)' + expect_grade grades, 'localhost', '::1', 5000, :ipv6 + end + end +end diff --git a/spec/helpers.rb b/spec/helpers.rb new file mode 100644 index 0000000..5470430 --- /dev/null +++ b/spec/helpers.rb @@ -0,0 +1,162 @@ +$:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib' +require 'rubygems' +require 'bundler/setup' +require 'cryptcheck' + +CryptCheck::Logger.level = ENV['LOG'] || :none + +module Helpers + OpenSSL::PKey::EC.send :alias_method, :private?, :private_key? + + def key(name) + open(File.join(File.dirname(__FILE__), 'resources', "#{name}.pem"), 'r') { |f| OpenSSL::PKey.read f } + end + + def dh(name) + open(File.join(File.dirname(__FILE__), 'resources', "dh-#{name}.pem"), 'r') { |f| OpenSSL::PKey::DH.new f } + end + + def certificate(key, domain) + cert = OpenSSL::X509::Certificate.new + cert.version = 2 + cert.serial = rand 2**(20*8-1) .. 2**(20*8) + cert.not_before = Time.now + cert.not_after = cert.not_before + 60*60 + + cert.public_key = case key + when OpenSSL::PKey::EC + curve = key.group.curve_name + public = OpenSSL::PKey::EC.new curve + public.public_key = key.public_key + public + else + key.public_key + end + + name = OpenSSL::X509::Name.parse "CN=#{domain}" + cert.subject = name + cert.issuer = name + + extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert + extension_factory.subject_certificate = cert + extension_factory.issuer_certificate = cert + + cert.add_extension extension_factory.create_extension 'basicConstraints', 'CA:TRUE', true + cert.add_extension extension_factory.create_extension 'keyUsage', 'keyEncipherment, dataEncipherment, digitalSignature,nonRepudiation,keyCertSign' + cert.add_extension extension_factory.create_extension 'extendedKeyUsage', 'serverAuth, clientAuth' + cert.add_extension extension_factory.create_extension 'subjectKeyIdentifier', 'hash' + cert.add_extension extension_factory.create_extension 'authorityKeyIdentifier', 'keyid:always' + cert.add_extension extension_factory.create_extension 'subjectAltName', "DNS:#{domain}" + + cert.sign key, OpenSSL::Digest::SHA512.new + end + + def server(key: 'rsa-1024', domain: 'localhost', # Key & certificate + host: '127.0.0.1', port: 5000, # Binding + version: :TLSv1_2, ciphers: 'AES128-SHA', # TLS version and ciphers + dh: 1024, ecdh: 'secp256r1') # DHE & ECDHE + key = key key + cert = certificate key, domain + + context = OpenSSL::SSL::SSLContext.new version + context.cert = cert + context.key = key + context.ciphers = ciphers + + if dh + dh = dh dh + context.tmp_dh_callback = proc { dh } + end + if ecdh + ecdh = key ecdh + context.tmp_ecdh_callback = proc { ecdh } + end + + IO.pipe do |stop_pipe_r, stop_pipe_w| + threads = [] + + mutex = Mutex.new + started = ConditionVariable.new + + threads << Thread.start do + tcp_server = TCPServer.new host, port + ssl_server = OpenSSL::SSL::SSLServer.new tcp_server, context + + mutex.synchronize { started.signal } + + loop do + readable, = IO.select [ssl_server, stop_pipe_r] + break if readable.include? stop_pipe_r + begin + ssl_server.accept + rescue + end + end + ssl_server.close + tcp_server.close + end + + mutex.synchronize { started.wait mutex } + begin + yield + ensure + stop_pipe_w.close + threads.each &:join + end + end + end + + def plain_server(host: '127.0.0.1', port: 5000) + IO.pipe do |stop_pipe_r, stop_pipe_w| + threads = [] + + mutex = Mutex.new + started = ConditionVariable.new + + threads << Thread.start do + tcp_server = TCPServer.new host, port + mutex.synchronize { started.signal } + + loop do + readable, = IO.select [tcp_server, stop_pipe_r] + break if readable.include? stop_pipe_r + begin + tcp_server.accept + rescue + end + end + tcp_server.close + end + + mutex.synchronize { started.wait mutex } + begin + yield + ensure + stop_pipe_w.close + threads.each &:join + end + end + end + + def expect_grade(grades, host, ip, port, family) + server = grades[[host, ip, port]].server + expect(server).to be_a CryptCheck::Tls::Server + expect(server.hostname).to eq host + expect(server.ip).to eq ip + expect(server.port).to eq port + expect(server.family).to eq case family + when :ipv4 then Socket::AF_INET + when :ipv6 then Socket::AF_INET6 + end + end + + def expect_grade_error(grades, host, ip, port, error) + server = grades[[host, ip, port]] + expect(server).to be_a CryptCheck::AnalysisFailure + expect(server.to_s).to eq error + end +end + +RSpec.configure do |c| + c.include Helpers +end diff --git a/spec/resources/dh-1024.pem b/spec/resources/dh-1024.pem new file mode 100644 index 0000000..b46ed48 --- /dev/null +++ b/spec/resources/dh-1024.pem @@ -0,0 +1,5 @@ +-----BEGIN DH PARAMETERS----- +MIGHAoGBANBrEWPccAuXK3fq8VtE1KeDmY1vk1dJ94ht8UPEqJTdsQEtAS2g7UKm +s49RRb7mG4JOVGWy1FWi32FrZTDUxInOP7k0wz8oqQSNBJWeAZjzETd1bjYutoSx +F1DMUlw650faSS2dSXalOXyRfY+2dqR9sa7FQNlOztYFCrtwXMb7AgEC +-----END DH PARAMETERS----- diff --git a/spec/resources/rsa-1024.pem b/spec/resources/rsa-1024.pem new file mode 100644 index 0000000..ee6f108 --- /dev/null +++ b/spec/resources/rsa-1024.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC0xBo+MgjnYqvszjUpslonvcQVI1TG7yxlGCWqpvN0a3zdgBpV +lpXv7q/821jUtlLc2BhNohRXuoejc2oiG7IOv7Md204NnoTQbxLo6gehnMyo86il +Q7KNAAW4tam79xNgOfdkkV0d80AfG148j+N6jDZCOoZ3dFwH4a6vcSWRgQIDAQAB +AoGBAJ7j+MVOqbDpdIG0R9qc4M4p6Z9C7RPny7gY35L/KOPeT2VLYtp0gNrjjWHP +VGe002U3tTUYEJWEahFsM5BDk+ASqyzesPD5lWzi6QSO3cIkvNSYLdBezNprcPk4 +PEy1pX+IXrRFeDXE/wncovuYP2STF18SSP7YgCMBAAwgeZAhAkEA7xLuNz6Qt7HK +euShzsvmzNUIaoBXa9qiOWoIb7aHa/uK87SwXpy6iV85TdWowD34JPnPiRx6FSPk +4rOXYBq0lQJBAMGQYF/ItKUGYnwj7z2Q7N3/Pz5fTyoqzQI7Nza8aCEabFNzAdMv +nZ2ROyWC/qXZ1osgPuwTBBfu9ty7GH2p4j0CQQCk+jJLCzDAor7waV/Dne+qQAQr +wl8RfXFfH22s8Y+oE5CCtpjS4WLUM1MPBDcMWncnxP/TRUR13CwxyO7YEfW1AkBv +VRqJnUiB7sUwv/54O+Zx3cFDn9BJ4apfES411nJSL/+ElA7FqIqQuZr6fXj4be5f +wWFPqbReC72Dwj1Y8iDFAkAXpo8CtvqtxQYdbIh0Jmdj2xHWppkbBs9dT/qVAOdO +RIA5UKKyyweZc+6ZFbAMeouhHGljcL73zOZt5V4YloT7 +-----END RSA PRIVATE KEY----- diff --git a/spec/resources/secp256r1.pem b/spec/resources/secp256r1.pem new file mode 100644 index 0000000..9d88784 --- /dev/null +++ b/spec/resources/secp256r1.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIIg6KuMLLVhcR7IIU+joH9npRN5eVYfBQo6pRL56xUCuoAoGCCqGSM49 +AwEHoUQDQgAE+h78G/a32+1ICT/euHP0Z5INER9Rh1nJNyn0HUSR0yWCistpoX1K +yCHpVwb0SAqB/6WrwOFKnrKIdI/HX1edGQ== +-----END EC PRIVATE KEY----- diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index 3b9cf57..0000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'webmock/rspec' - -# This file was generated by the `rspec --init` command. Conventionally, all -# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. -# The generated `.rspec` file contains `--require spec_helper` which will cause this -# file to always be loaded, without a need to explicitly require it in any files. -# -# Given that it is always loaded, you are encouraged to keep this file as -# light-weight as possible. Requiring heavyweight dependencies from this file -# will add to the boot time of your test suite on EVERY test run, even for an -# individual file that may not need all of that loaded. Instead, consider making -# a separate helper file that requires the additional dependencies and performs -# the additional setup, and require it from the spec files that actually need it. -# -# The `.rspec` file also contains a few flags that are not defaults but that -# users commonly want. -# -# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration -RSpec.configure do |config| - # rspec-expectations config goes here. You can use an alternate - # assertion/expectation library such as wrong or the stdlib/minitest - # assertions if you prefer. - config.expect_with :rspec do |expectations| - # This option will default to `true` in RSpec 4. It makes the `description` - # and `failure_message` of custom matchers include text for helper methods - # defined using `chain`, e.g.: - # be_bigger_than(2).and_smaller_than(4).description - # # => "be bigger than 2 and smaller than 4" - # ...rather than: - # # => "be bigger than 2" - expectations.include_chain_clauses_in_custom_matcher_descriptions = true - end - - # rspec-mocks config goes here. You can use an alternate test double - # library (such as bogus or mocha) by changing the `mock_with` option here. - config.mock_with :rspec do |mocks| - # Prevents you from mocking or stubbing a method that does not exist on - # a real object. This is generally recommended, and will default to - # `true` in RSpec 4. - mocks.verify_partial_doubles = true - end - -# The settings below are suggested to provide a good initial experience -# with RSpec, but feel free to customize to your heart's content. -=begin - # These two settings work together to allow you to limit a spec run - # to individual examples or groups you care about by tagging them with - # `:focus` metadata. When nothing is tagged with `:focus`, all examples - # get run. - config.filter_run :focus - config.run_all_when_everything_filtered = true - - # Limits the available syntax to the non-monkey patched syntax that is recommended. - # For more details, see: - # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax - # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ - # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching - config.disable_monkey_patching! - - # This setting enables warnings. It's recommended, but in some cases may - # be too noisy due to issues in dependencies. - config.warnings = true - - # Many RSpec users commonly either run the entire suite or an individual - # file, and it's useful to allow more verbose output when running an - # individual spec file. - if config.files_to_run.one? - # Use the documentation formatter for detailed output, - # unless a formatter has already been configured - # (e.g. via a command-line flag). - config.default_formatter = 'doc' - end - - # Print the 10 slowest examples and example groups at the - # end of the spec run, to help surface which specs are running - # particularly slow. - config.profile_examples = 10 - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = :random - - # Seed global randomization in this process using the `--seed` CLI option. - # Setting this allows you to use `--seed` to deterministically reproduce - # test failures related to randomization by passing the same `--seed` value - # as the one that triggered the failure. - Kernel.srand config.seed -=end -end