@@ -4,7 +4,6 @@ Gemfile.lock | |||
.rakeTasks | |||
.idea/ | |||
/html/ | |||
.rspec | |||
/rakefile | |||
/output/*.html | |||
/db/*.sqlite3 |
@@ -0,0 +1,2 @@ | |||
--color | |||
--require helpers |
@@ -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 |
@@ -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 |
@@ -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) |
@@ -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 |
@@ -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 | |||
@@ -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) |
@@ -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 |
@@ -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 |
@@ -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) |
@@ -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) |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -0,0 +1,5 @@ | |||
-----BEGIN DH PARAMETERS----- | |||
MIGHAoGBANBrEWPccAuXK3fq8VtE1KeDmY1vk1dJ94ht8UPEqJTdsQEtAS2g7UKm | |||
s49RRb7mG4JOVGWy1FWi32FrZTDUxInOP7k0wz8oqQSNBJWeAZjzETd1bjYutoSx | |||
F1DMUlw650faSS2dSXalOXyRfY+2dqR9sa7FQNlOztYFCrtwXMb7AgEC | |||
-----END DH PARAMETERS----- |
@@ -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----- |
@@ -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----- |
@@ -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 |