@@ -87,7 +87,6 @@ module CryptCheck | |||
server.new *a, **kargs | |||
end | |||
ap s.states | |||
exit | |||
if grade | |||
g = grade.new s | |||
Logger.info { '' } | |||
@@ -5,7 +5,7 @@ module CryptCheck | |||
module Tls | |||
module Engine | |||
TCP_TIMEOUT = 10 | |||
SSL_TIMEOUT = 2*TCP_TIMEOUT | |||
TLS_TIMEOUT = 2*TCP_TIMEOUT | |||
class TLSException < ::StandardError | |||
end | |||
@@ -21,13 +21,25 @@ module CryptCheck | |||
class InappropriateFallback < TLSException | |||
end | |||
class Timeout < ::StandardError | |||
def initialize(ip, port) | |||
@message = "Timeout when connecting to #{ip}:#{port} (max #{TCP_TIMEOUT.humanize})" | |||
end | |||
def to_s | |||
@message | |||
end | |||
end | |||
class TLSTimeout < Timeout | |||
def initialize(ip, port) | |||
@message = "Timeout when TLS connecting to #{ip}:#{port} (max #{TLS_TIMEOUT.humanize})" | |||
end | |||
end | |||
class ConnectionError < ::StandardError | |||
end | |||
attr_reader :certs, :keys, :dh, :supported_methods, :supported_ciphers, :supported_curves, :curves_preference | |||
attr_reader :hostname, :ip, :family, :port, :certs, :keys, :dh, | |||
:supported_methods, :supported_ciphers, | |||
:supported_curves, :curves_preference | |||
def initialize(hostname, ip, family, port) | |||
@hostname, @ip, @family, @port = hostname, ip, family, port | |||
@@ -64,6 +76,7 @@ module CryptCheck | |||
Logger.info { '' } | |||
Logger.info { 'Supported methods' } | |||
@supported_methods = Method.select { |m| supported_method? m } | |||
raise TLSNotAvailableException if @supported_methods.empty? | |||
end | |||
def supported_cipher?(method, cipher) | |||
@@ -310,12 +323,14 @@ module CryptCheck | |||
block_given? ? block.call(socket) : nil | |||
rescue ::IO::WaitReadable | |||
#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 | |||
raise Timeout.new(@ip, @port) unless IO.select [socket], nil, nil, TCP_TIMEOUT | |||
retry | |||
rescue ::IO::WaitWritable | |||
#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 | |||
raise Timeout.new(@ip, @port) unless IO.select nil, [socket], nil, TCP_TIMEOUT | |||
retry | |||
rescue Errno::ECONNREFUSED => e | |||
raise ConnectionError, e | |||
ensure | |||
socket.close | |||
end | |||
@@ -331,11 +346,11 @@ module CryptCheck | |||
return block_given? ? block.call(ssl_socket) : nil | |||
rescue ::OpenSSL::SSL::SSLErrorWaitReadable | |||
#Logger.trace { "Waiting for SSL read to #{name}" } | |||
raise TLSTimeout, "Timeout when TLS connect to #{@ip}:#{@port} (max #{SSL_TIMEOUT.humanize})" unless IO.select [ssl_socket], nil, nil, SSL_TIMEOUT | |||
raise TLSTimeout.new(@ip, @port) unless IO.select [ssl_socket], nil, nil, TLS_TIMEOUT | |||
retry | |||
rescue ::OpenSSL::SSL::SSLErrorWaitWritable | |||
#Logger.trace { "Waiting for SSL write to #{name}" } | |||
raise TLSTimeout, "Timeout when TLS connect to #{@ip}:#{@port} (max #{SSL_TIMEOUT.humanize})" unless IO.select nil, [ssl_socket], nil, SSL_TIMEOUT | |||
raise TLSTimeout.new(@ip, @port) unless IO.select nil, [ssl_socket], nil, TLS_TIMEOUT | |||
retry | |||
rescue ::OpenSSL::SSL::SSLError => e | |||
case e.message | |||
@@ -387,10 +402,15 @@ module CryptCheck | |||
end | |||
Logger.trace { "Try method=#{method} / ciphers=#{ciphers} / curves=#{curves} / scsv=#{fallback}" } | |||
connect do |socket| | |||
ssl_connect socket, ssl_context, method do |ssl_socket| | |||
return block_given? ? block.call(ssl_socket) : ssl_socket | |||
begin | |||
connect do |socket| | |||
ssl_connect socket, ssl_context, method do |ssl_socket| | |||
return block_given? ? block.call(ssl_socket) : ssl_socket | |||
end | |||
end | |||
rescue => e | |||
Logger.trace { "Error occurs : #{e}" } | |||
raise | |||
end | |||
end | |||
@@ -23,24 +23,65 @@ module CryptCheck | |||
class Host | |||
MAX_ANALYSIS_DURATION = 600 | |||
attr_reader :servers | |||
attr_reader :servers, :error | |||
def initialize(hostname, port) | |||
@hostname, @port = hostname, port | |||
def initialize | |||
first = true | |||
@servers = resolve.collect do |args| | |||
_, ip, _, _ = args | |||
first ? (first = false) : Logger.info { '' } | |||
result = begin | |||
server = ::Timeout.timeout MAX_ANALYSIS_DURATION do | |||
server(*args) | |||
end | |||
grade(server) | |||
rescue Engine::TLSException => e | |||
grade server | |||
rescue Engine::TLSException, Engine::ConnectionError, Engine::Timeout => e | |||
AnalysisFailure.new e | |||
rescue ::Timeout::Error | |||
TooLongAnalysis.new | |||
end | |||
[args, result] | |||
[[@hostname, ip, @port], result] | |||
end.to_h | |||
rescue => e | |||
@error = e | |||
end | |||
def to_json | |||
JSON.generate(@servers.collect do |host, result| | |||
hostname, ip, _ = host | |||
json = { | |||
hostname: hostname, | |||
ip: ip, | |||
} | |||
case result | |||
when Grade | |||
json[:result] = result.to_json | |||
else | |||
json[:error] = result.message | |||
end | |||
json | |||
end) | |||
end | |||
private | |||
def resolve | |||
begin | |||
ip = IPAddr.new @hostname | |||
return [[nil, ip.to_s, ip.family]] | |||
rescue IPAddr::InvalidAddressError | |||
end | |||
::Addrinfo.getaddrinfo(@hostname, nil, nil, :STREAM) | |||
.collect { |a| [@hostname, a.ip_address, a.afamily, @port] } | |||
end | |||
def server(*args) | |||
TcpServer.new *args | |||
end | |||
def grade(server) | |||
Grade.new server | |||
end | |||
end | |||
end | |||
@@ -2,24 +2,9 @@ module CryptCheck | |||
module Tls | |||
module Https | |||
class Host < Tls::Host | |||
def initialize(hostname, port=443) | |||
@hostname, @port = hostname, port | |||
super() | |||
end | |||
private | |||
def resolve | |||
begin | |||
ip = IPAddr.new @hostname | |||
return [[nil, ip.to_s, ip.family]] | |||
rescue IPAddr::InvalidAddressError | |||
end | |||
::Addrinfo.getaddrinfo(@hostname, nil, nil, :STREAM) | |||
.collect { |a| [@hostname, a.ip_address, a.afamily] } | |||
end | |||
def server(hostname, ip, family) | |||
Https::Server.new hostname, ip, family, @port | |||
def server(*args) | |||
Https::Server.new *args | |||
end | |||
def grade(server) | |||
@@ -19,7 +19,7 @@ module CryptCheck | |||
{ | |||
follow_redirects: false, | |||
verify: false, | |||
timeout: SSL_TIMEOUT, | |||
timeout: TLS_TIMEOUT, | |||
ssl_version: @supported_methods.first.to_sym, | |||
ciphers: Cipher::ALL | |||
} | |||
@@ -1,135 +0,0 @@ | |||
RSpec.shared_examples :analysis do | |||
describe '#analyze' do | |||
it 'return 1 grade with IPv4' do | |||
grades = server host: '127.0.0.1' do | |||
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 | |||
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 | |||
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 | |||
analyze 'localhost', 5000 | |||
end | |||
expect_grade_error grades, 'localhost', nil, 5000, '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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
analyze 'localhost', 5000 | |||
end | |||
end | |||
expect_grade_error grades, 'localhost', '127.0.0.1', 5000, | |||
'TLS seems not supported on this server' | |||
expect_grade grades, 'localhost', '::1', 5000, :ipv6 | |||
end | |||
end | |||
end |
@@ -0,0 +1,129 @@ | |||
describe CryptCheck::Tls::Host do | |||
def host(*args, **kargs) | |||
do_in_serv *args, **kargs do |host, port| | |||
CryptCheck::Tls::Host.new host, port | |||
end | |||
end | |||
def servers(*args, **kargs) | |||
host(*args, **kargs).servers | |||
end | |||
def error(*args, **kargs) | |||
host(*args, **kargs).error | |||
end | |||
it 'return 1 grade with IPv4' do | |||
servers = servers() | |||
expect(servers.size).to be 1 | |||
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4 | |||
end | |||
it 'return 1 grade with IPv6' do | |||
addresses = [Helpers::DEFAULT_IPv6] | |||
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do | |||
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } | |||
end | |||
servers = servers(host: Helpers::DEFAULT_IPv6) | |||
expect(servers.size).to be 1 | |||
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6 | |||
end | |||
it 'return 2 grades with hostname (IPv4 & IPv6)' do | |||
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6] | |||
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do | |||
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } | |||
end | |||
servers = servers(host: '::') | |||
expect(servers.size).to be 2 | |||
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, :ipv4 | |||
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6 | |||
end | |||
it 'return error if DNS resolution problem' do | |||
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) | |||
.and_raise SocketError, 'getaddrinfo: Name or service not known' | |||
error = error() | |||
expect_error error, ::SocketError, 'getaddrinfo: Name or service not known' | |||
end | |||
it 'return error if analysis too long' do | |||
stub_const 'CryptCheck::Tls::Host::MAX_ANALYSIS_DURATION', 1 | |||
allow_any_instance_of(CryptCheck::Tls::Host).to receive(:server) { sleep 2 } | |||
servers = servers() | |||
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, | |||
'Too long analysis (max 1 second)' | |||
end | |||
it 'return error if unable to connect' do | |||
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6] | |||
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do | |||
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } | |||
end | |||
servers = servers(host: Helpers::DEFAULT_IPv6) | |||
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, | |||
'Connection refused - connect(2) for 127.0.0.1:15000' | |||
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6 | |||
end | |||
it 'return error if TCP timeout' do | |||
stub_const 'CryptCheck::Tls::Engine::TCP_TIMEOUT', 1 | |||
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6] | |||
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, 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 | |||
servers = servers(host: '::') | |||
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, | |||
'Timeout when connecting to 127.0.0.1:15000 (max 1 second)' | |||
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6 | |||
end | |||
it 'return error if TLS timeout' do | |||
stub_const 'CryptCheck::Tls::Engine::TLS_TIMEOUT', 1 | |||
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6] | |||
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, 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 | |||
servers = servers(host: '::') | |||
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, | |||
'Timeout when TLS connecting to 127.0.0.1:15000 (max 1 second)' | |||
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6 | |||
end | |||
it 'return error if plain server' do | |||
stub_const 'CryptCheck::Tls::ENGINE::TLS_TIMEOUT', 1 | |||
addresses = [Helpers::DEFAULT_IPv4, Helpers::DEFAULT_IPv6] | |||
allow(Addrinfo).to receive(:getaddrinfo).with(Helpers::DEFAULT_HOST, nil, nil, :STREAM) do | |||
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) } | |||
end | |||
servers = plain_serv Helpers::DEFAULT_IPv4 do | |||
servers(host: Helpers::DEFAULT_IPv6) | |||
end | |||
expect_grade_error servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv4, Helpers::DEFAULT_PORT, | |||
'TLS seems not supported on this server' | |||
expect_grade servers, Helpers::DEFAULT_HOST, Helpers::DEFAULT_IPv6, Helpers::DEFAULT_PORT, :ipv6 | |||
end | |||
end |
@@ -7,46 +7,8 @@ describe CryptCheck::Tls::Server do | |||
FakeTime.unfreeze | |||
end | |||
default_parameters = { | |||
methods: %i(TLSv1_2), | |||
chain: %w(intermediate ca), | |||
curves: %i(prime256v1), | |||
server_preference: true | |||
}.freeze | |||
default_ecdsa_parameters = default_parameters.merge({ | |||
material: [[:ecdsa, :prime256v1]], | |||
ciphers: %i(ECDHE-ECDSA-AES128-SHA), | |||
curves: %i(prime256v1) | |||
}).freeze | |||
default_rsa_parameters = default_parameters.merge({ | |||
material: [[:rsa, 1024]], | |||
ciphers: %i(ECDHE-RSA-AES128-SHA), | |||
curves: %i(prime256v1), | |||
dh: 1024 | |||
}).freeze | |||
default_mixed_parameters = default_parameters.merge({ | |||
material: [[:ecdsa, :prime256v1], [:rsa, 1024]], | |||
ciphers: %i(ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA), | |||
curves: %i(prime256v1), | |||
dh: 1024 | |||
}).freeze | |||
default_sslv2_parameters = default_parameters.merge({ | |||
methods: :SSLv2, | |||
material: [[:rsa, 1024]], | |||
ciphers: %i(RC4-MD5), | |||
chain: [] | |||
}).freeze | |||
DEFAULT_PARAMETERS = { ecdsa: default_ecdsa_parameters.freeze, | |||
rsa: default_rsa_parameters.freeze, | |||
mixed: default_mixed_parameters.freeze, | |||
sslv2: default_sslv2_parameters.freeze }.freeze | |||
def server(type=:ecdsa, **kargs) | |||
params = DEFAULT_PARAMETERS[type].dup | |||
params.merge!(kargs) if kargs | |||
host, port = '127.0.0.1', 15000 | |||
params.merge!({ host: host, port: port }) | |||
tls_serv **params do | |||
def server(*args, **kargs) | |||
do_in_serv *args, **kargs do |host, port| | |||
CryptCheck::Tls::TcpServer.new 'localhost', host, ::Socket::PF_INET, port | |||
end | |||
end | |||
@@ -21,7 +21,9 @@ module Helpers | |||
DEFAULT_MATERIAL = [[:ecdsa, :prime256v1]] | |||
DEFAULT_CHAIN = %w(intermediate ca) | |||
DEFAULT_HOST = 'localhost' | |||
DEFAULT_PORT = 5000 | |||
DEFAULT_IPv4 = '127.0.0.1' | |||
DEFAULT_IPv6 = '::1' | |||
DEFAULT_PORT = 15000 | |||
def key(type, name=nil) | |||
name = if name | |||
@@ -93,7 +95,7 @@ module Helpers | |||
context = if methods == :SSLv2 | |||
OpenSSL::SSL::SSLContext.new :SSLv2 | |||
else | |||
context = OpenSSL::SSL::SSLContext.new | |||
context = OpenSSL::SSL::SSLContext.new | |||
context.options |= OpenSSL::SSL::OP_NO_SSLv2 unless methods.include? :SSLv2 | |||
context.options |= OpenSSL::SSL::OP_NO_SSLv3 unless methods.include? :SSLv3 | |||
context.options |= OpenSSL::SSL::OP_NO_TLSv1 unless methods.include? :TLSv1 | |||
@@ -107,7 +109,7 @@ module Helpers | |||
context.keys = keys | |||
context.extra_chain_cert = chain unless chain.empty? | |||
context.ciphers = ciphers.join ':' | |||
context.ciphers = ciphers.join ':' | |||
if methods != :SSLv2 | |||
context.tmp_dh_callback = proc { dh } if dh | |||
context.ecdh_curves = curves.join ':' if curves | |||
@@ -116,6 +118,50 @@ module Helpers | |||
context | |||
end | |||
default_parameters = { | |||
methods: %i(TLSv1_2), | |||
chain: %w(intermediate ca), | |||
curves: %i(prime256v1), | |||
server_preference: true | |||
}.freeze | |||
default_ecdsa_parameters = default_parameters.merge({ | |||
material: [[:ecdsa, :prime256v1]], | |||
ciphers: %i(ECDHE-ECDSA-AES128-SHA), | |||
curves: %i(prime256v1) | |||
}).freeze | |||
default_rsa_parameters = default_parameters.merge({ | |||
material: [[:rsa, 1024]], | |||
ciphers: %i(ECDHE-RSA-AES128-SHA), | |||
curves: %i(prime256v1), | |||
dh: 1024 | |||
}).freeze | |||
default_mixed_parameters = default_parameters.merge({ | |||
material: [[:ecdsa, :prime256v1], [:rsa, 1024]], | |||
ciphers: %i(ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA), | |||
curves: %i(prime256v1), | |||
dh: 1024 | |||
}).freeze | |||
default_sslv2_parameters = default_parameters.merge({ | |||
methods: :SSLv2, | |||
material: [[:rsa, 1024]], | |||
ciphers: %i(RC4-MD5), | |||
chain: [] | |||
}).freeze | |||
DEFAULT_PARAMETERS = { ecdsa: default_ecdsa_parameters.freeze, | |||
rsa: default_rsa_parameters.freeze, | |||
mixed: default_mixed_parameters.freeze, | |||
sslv2: default_sslv2_parameters.freeze }.freeze | |||
def do_in_serv(type=:ecdsa, **kargs) | |||
params = DEFAULT_PARAMETERS[type].dup | |||
host, port = Helpers::DEFAULT_HOST, Helpers::DEFAULT_PORT | |||
params.merge!({ host: host, port: port }) | |||
params.merge!(kargs) if kargs | |||
tls_serv **params do | |||
yield host, port if block_given? | |||
end | |||
end | |||
def tls_serv(host: DEFAULT_HOST, port: DEFAULT_PORT, | |||
material: DEFAULT_MATERIAL, chain: DEFAULT_CHAIN, | |||
methods: DEFAULT_METHODS, ciphers: DEFAULT_CIPHERS, | |||
@@ -140,7 +186,7 @@ module Helpers | |||
end | |||
end | |||
def plain_serv(host='127.0.0.1', port=5000, process: nil, &block) | |||
def plain_serv(host=DEFAULT_HOST, port=DEFAULT_PORT, process: nil, &block) | |||
tcp_server = TCPServer.new host, port | |||
begin | |||
serv tcp_server, process, &block | |||
@@ -149,10 +195,10 @@ module Helpers | |||
end | |||
end | |||
def starttls_serv(key: DEFAULT_KEY, domain: 'localhost', # Key & certificate | |||
def starttls_serv(key: DEFAULT_KEY, domain: DEFAULT_HOST, # Key & certificate | |||
version: DEFAULT_METHOD, ciphers: DEFAULT_CIPHERS, # TLS version and ciphers | |||
dh: DEFAULT_DH_SIZE, ecdh: DEFAULT_ECC_CURVE, # DHE & ECDHE | |||
host: '127.0.0.1', port: 5000, # Binding | |||
host: DEFAULT_HOST, port: DEFAULT_PORT, # Binding | |||
plain_process: nil, process: nil, &block) | |||
context = context(key: key, domain: domain, version: version, ciphers: ciphers, dh: dh, ecdh: ecdh) | |||
tcp_server = TCPServer.new host, port | |||
@@ -203,9 +249,14 @@ module Helpers | |||
def expect_grade_error(grades, host, ip, port, error) | |||
server = grades[[host, ip, port]] | |||
expect(server).to be_a CryptCheck::AnalysisFailure | |||
expect(server).to be_a CryptCheck::Tls::AnalysisFailure | |||
expect(server.to_s).to eq error | |||
end | |||
def expect_error(error, type, message) | |||
expect(error).to be_a type | |||
expect(error.message).to eq message | |||
end | |||
end | |||
RSpec.configure do |c| | |||