From c75601dad40a58e0523f27f58def8c5c3866c15e Mon Sep 17 00:00:00 2001 From: aeris Date: Sun, 19 Mar 2017 15:06:52 +0100 Subject: [PATCH] Handle corner cases during protocol error --- lib/cryptcheck.rb | 1 - lib/cryptcheck/tls/engine.rb | 38 ++++++-- lib/cryptcheck/tls/host.rb | 51 +++++++++-- lib/cryptcheck/tls/https/host.rb | 19 +--- lib/cryptcheck/tls/https/server.rb | 2 +- spec/cryptcheck/support/analysis.rb | 135 ---------------------------- spec/cryptcheck/tls/host_spec.rb | 129 ++++++++++++++++++++++++++ spec/cryptcheck/tls/server_spec.rb | 42 +-------- spec/helpers.rb | 65 ++++++++++++-- 9 files changed, 267 insertions(+), 215 deletions(-) delete mode 100644 spec/cryptcheck/support/analysis.rb create mode 100644 spec/cryptcheck/tls/host_spec.rb diff --git a/lib/cryptcheck.rb b/lib/cryptcheck.rb index 4f615ea..4d8d4fc 100644 --- a/lib/cryptcheck.rb +++ b/lib/cryptcheck.rb @@ -87,7 +87,6 @@ module CryptCheck server.new *a, **kargs end ap s.states - exit if grade g = grade.new s Logger.info { '' } diff --git a/lib/cryptcheck/tls/engine.rb b/lib/cryptcheck/tls/engine.rb index 17d46d0..7681885 100644 --- a/lib/cryptcheck/tls/engine.rb +++ b/lib/cryptcheck/tls/engine.rb @@ -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 diff --git a/lib/cryptcheck/tls/host.rb b/lib/cryptcheck/tls/host.rb index 434f1f3..2ae2f92 100644 --- a/lib/cryptcheck/tls/host.rb +++ b/lib/cryptcheck/tls/host.rb @@ -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 diff --git a/lib/cryptcheck/tls/https/host.rb b/lib/cryptcheck/tls/https/host.rb index 87508e7..284b612 100644 --- a/lib/cryptcheck/tls/https/host.rb +++ b/lib/cryptcheck/tls/https/host.rb @@ -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) diff --git a/lib/cryptcheck/tls/https/server.rb b/lib/cryptcheck/tls/https/server.rb index 96b0446..fb370fc 100644 --- a/lib/cryptcheck/tls/https/server.rb +++ b/lib/cryptcheck/tls/https/server.rb @@ -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 } diff --git a/spec/cryptcheck/support/analysis.rb b/spec/cryptcheck/support/analysis.rb deleted file mode 100644 index a722e39..0000000 --- a/spec/cryptcheck/support/analysis.rb +++ /dev/null @@ -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 diff --git a/spec/cryptcheck/tls/host_spec.rb b/spec/cryptcheck/tls/host_spec.rb new file mode 100644 index 0000000..9fafa36 --- /dev/null +++ b/spec/cryptcheck/tls/host_spec.rb @@ -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 diff --git a/spec/cryptcheck/tls/server_spec.rb b/spec/cryptcheck/tls/server_spec.rb index 98755a8..f086a40 100644 --- a/spec/cryptcheck/tls/server_spec.rb +++ b/spec/cryptcheck/tls/server_spec.rb @@ -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 diff --git a/spec/helpers.rb b/spec/helpers.rb index 6dbf001..da2d4ee 100644 --- a/spec/helpers.rb +++ b/spec/helpers.rb @@ -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|