You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

helpers.rb 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. $:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib'
  2. require 'rubygems'
  3. require 'bundler/setup'
  4. Bundler.require :default, :development
  5. require 'cryptcheck'
  6. Dir['./spec/**/support/**/*.rb'].sort.each { |f| require f }
  7. require 'simplecov'
  8. SimpleCov.start do
  9. add_filter 'spec/'
  10. end
  11. CryptCheck::Logger.level = ENV['LOG'] || :none
  12. module Helpers
  13. DEFAULT_METHODS = %i(TLSv1_2)
  14. DEFAULT_CIPHERS = %i(ECDHE-ECDSA-AES128-GCM-SHA256)
  15. DEFAULT_CURVES = %i(prime256v1)
  16. DEFAULT_DH = [:rsa, 4096]
  17. DEFAULT_MATERIAL = [[:ecdsa, :prime256v1]]
  18. DEFAULT_CHAIN = %w(intermediate ca)
  19. DEFAULT_HOST = 'localhost'
  20. DEFAULT_PORT = 5000
  21. def key(type, name=nil)
  22. name = if name
  23. "#{type}-#{name}"
  24. else
  25. type
  26. end
  27. OpenSSL::PKey.read File.read "spec/resources/#{name}.pem"
  28. end
  29. def cert(type, name=nil)
  30. name = if name
  31. "#{type}-#{name}"
  32. else
  33. type
  34. end
  35. OpenSSL::X509::Certificate.new File.read "spec/resources/#{name}.crt"
  36. end
  37. def dh(name)
  38. OpenSSL::PKey::DH.new File.read "spec/resources/dh-#{name}.pem"
  39. end
  40. def serv(server, process)
  41. IO.pipe do |stop_pipe_r, stop_pipe_w|
  42. threads = []
  43. mutex = Mutex.new
  44. started = ConditionVariable.new
  45. threads << Thread.start do
  46. mutex.synchronize { started.signal }
  47. loop do
  48. readable, = IO.select [server, stop_pipe_r]
  49. break if readable.include? stop_pipe_r
  50. begin
  51. socket = server.accept
  52. begin
  53. process.call socket if process
  54. ensure
  55. socket.close
  56. end
  57. rescue
  58. end
  59. end
  60. server.close
  61. end
  62. mutex.synchronize { started.wait mutex }
  63. begin
  64. yield if block_given?
  65. ensure
  66. stop_pipe_w.close
  67. threads.each &:join
  68. end
  69. end
  70. end
  71. def context(certs, keys, chain=[],
  72. methods: DEFAULT_METHODS, ciphers: DEFAULT_CIPHERS,
  73. dh:, curves: DEFAULT_CURVES, server_preference: true)
  74. # Can't find a way to support SSLv2 with others
  75. context = if methods == :SSLv2
  76. OpenSSL::SSL::SSLContext.new :SSLv2
  77. else
  78. context = OpenSSL::SSL::SSLContext.new
  79. context.options |= OpenSSL::SSL::OP_NO_SSLv2 unless methods.include? :SSLv2
  80. context.options |= OpenSSL::SSL::OP_NO_SSLv3 unless methods.include? :SSLv3
  81. context.options |= OpenSSL::SSL::OP_NO_TLSv1 unless methods.include? :TLSv1
  82. context.options |= OpenSSL::SSL::OP_NO_TLSv1_1 unless methods.include? :TLSv1_1
  83. context.options |= OpenSSL::SSL::OP_NO_TLSv1_2 unless methods.include? :TLSv1_2
  84. context
  85. end
  86. context.options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if server_preference
  87. context.certs = certs
  88. context.keys = keys
  89. context.extra_chain_cert = chain unless chain.empty?
  90. context.ciphers = ciphers.join ':'
  91. if methods != :SSLv2
  92. context.tmp_dh_callback = proc { dh } if dh
  93. context.ecdh_curves = curves.join ':' if curves
  94. end
  95. context
  96. end
  97. def tls_serv(host: DEFAULT_HOST, port: DEFAULT_PORT,
  98. material: DEFAULT_MATERIAL, chain: DEFAULT_CHAIN,
  99. methods: DEFAULT_METHODS, ciphers: DEFAULT_CIPHERS,
  100. dh: nil, curves: DEFAULT_CURVES, server_preference: true,
  101. process: nil, &block)
  102. keys = material.collect { |m| key *m }
  103. certs = material.collect { |m| cert *m }
  104. chain = chain.collect { |c| cert c }
  105. dh = dh dh if dh
  106. context = context certs, keys, chain,
  107. methods: methods, ciphers: ciphers,
  108. dh: dh, curves: curves,
  109. server_preference: server_preference
  110. tcp_server = TCPServer.new host, port
  111. tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
  112. begin
  113. serv tls_server, process, &block
  114. ensure
  115. tls_server.close
  116. tcp_server.close
  117. end
  118. end
  119. def plain_serv(host='127.0.0.1', port=5000, process: nil, &block)
  120. tcp_server = TCPServer.new host, port
  121. begin
  122. serv tcp_server, process, &block
  123. ensure
  124. tcp_server.close
  125. end
  126. end
  127. def starttls_serv(key: DEFAULT_KEY, domain: 'localhost', # Key & certificate
  128. version: DEFAULT_METHOD, ciphers: DEFAULT_CIPHERS, # TLS version and ciphers
  129. dh: DEFAULT_DH_SIZE, ecdh: DEFAULT_ECC_CURVE, # DHE & ECDHE
  130. host: '127.0.0.1', port: 5000, # Binding
  131. plain_process: nil, process: nil, &block)
  132. context = context(key: key, domain: domain, version: version, ciphers: ciphers, dh: dh, ecdh: ecdh)
  133. tcp_server = TCPServer.new host, port
  134. tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
  135. tls_server.start_immediately = false
  136. internal_process = proc do |socket|
  137. accept = false
  138. accept = plain_process.call socket if plain_process
  139. if accept
  140. tls_socket = socket.accept
  141. begin
  142. process.call tls_socket if process
  143. ensure
  144. socket.close
  145. end
  146. end
  147. end
  148. begin
  149. serv tls_server, internal_process, &block
  150. ensure
  151. tls_server.close
  152. tcp_server.close
  153. end
  154. end
  155. def grade(grades, host, ip, port)
  156. grades[[host, ip, port]]
  157. end
  158. def expect_grade(grades, host, ip, port, family)
  159. grade = grade grades, host, ip, port
  160. expect(grade).to be_a CryptCheck::Tls::Grade
  161. server = grade.server
  162. expect(server).to be_a CryptCheck::Tls::Server
  163. expect(server.hostname).to eq host
  164. expect(server.ip).to eq ip
  165. expect(server.port).to eq port
  166. expect(server.family).to eq case family
  167. when :ipv4
  168. Socket::AF_INET
  169. when :ipv6
  170. Socket::AF_INET6
  171. end
  172. [grade, server]
  173. end
  174. def expect_grade_error(grades, host, ip, port, error)
  175. server = grades[[host, ip, port]]
  176. expect(server).to be_a CryptCheck::AnalysisFailure
  177. expect(server.to_s).to eq error
  178. end
  179. end
  180. RSpec.configure do |c|
  181. c.include Helpers
  182. end