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 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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. require 'faketime'
  7. Dir['./spec/**/support/**/*.rb'].sort.each { |f| require f }
  8. require 'simplecov'
  9. SimpleCov.start do
  10. add_filter 'spec/'
  11. end
  12. CryptCheck::Logger.level = ENV['LOG'] || :none
  13. module Helpers
  14. DEFAULT_METHODS = %i(TLSv1_2)
  15. DEFAULT_CIPHERS = %i(ECDHE-ECDSA-AES128-GCM-SHA256)
  16. DEFAULT_CURVES = %i(prime256v1)
  17. DEFAULT_DH = [:rsa, 4096]
  18. DEFAULT_MATERIAL = [[:ecdsa, :prime256v1]]
  19. DEFAULT_CHAIN = %w(intermediate ca)
  20. DEFAULT_HOST = 'localhost'
  21. DEFAULT_IPv4 = '127.0.0.1'
  22. DEFAULT_IPv6 = '::1'
  23. DEFAULT_PORT = 15000
  24. def key(type, name=nil)
  25. name = if name
  26. "#{type}-#{name}"
  27. else
  28. type
  29. end
  30. OpenSSL::PKey.read File.read "spec/resources/#{name}.pem"
  31. end
  32. def cert(type, name=nil)
  33. name = if name
  34. "#{type}-#{name}"
  35. else
  36. type
  37. end
  38. OpenSSL::X509::Certificate.new File.read "spec/resources/#{name}.crt"
  39. end
  40. def chain(chain)
  41. chain.collect { |f| self.cert f }
  42. end
  43. def dh(name)
  44. OpenSSL::PKey::DH.new File.read "spec/resources/dh-#{name}.pem"
  45. end
  46. def serv(server, process)
  47. IO.pipe do |stop_pipe_r, stop_pipe_w|
  48. threads = []
  49. mutex = Mutex.new
  50. started = ConditionVariable.new
  51. threads << Thread.start do
  52. mutex.synchronize { started.signal }
  53. loop do
  54. readable, = IO.select [server, stop_pipe_r]
  55. break if readable.include? stop_pipe_r
  56. begin
  57. socket = server.accept
  58. begin
  59. process.call socket if process
  60. ensure
  61. socket.close
  62. end
  63. rescue
  64. end
  65. end
  66. server.close
  67. end
  68. mutex.synchronize { started.wait mutex }
  69. begin
  70. yield if block_given?
  71. ensure
  72. stop_pipe_w.close
  73. threads.each &:join
  74. end
  75. end
  76. end
  77. def context(certs, keys, chain=[],
  78. methods: DEFAULT_METHODS, ciphers: DEFAULT_CIPHERS,
  79. dh:, curves: DEFAULT_CURVES, server_preference: true)
  80. # Can't find a way to support SSLv2 with others
  81. context = if methods == :SSLv2
  82. OpenSSL::SSL::SSLContext.new :SSLv2
  83. else
  84. context = OpenSSL::SSL::SSLContext.new
  85. context.options |= OpenSSL::SSL::OP_NO_SSLv2 unless methods.include? :SSLv2
  86. context.options |= OpenSSL::SSL::OP_NO_SSLv3 unless methods.include? :SSLv3
  87. context.options |= OpenSSL::SSL::OP_NO_TLSv1 unless methods.include? :TLSv1
  88. context.options |= OpenSSL::SSL::OP_NO_TLSv1_1 unless methods.include? :TLSv1_1
  89. context.options |= OpenSSL::SSL::OP_NO_TLSv1_2 unless methods.include? :TLSv1_2
  90. context
  91. end
  92. context.options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if server_preference
  93. context.certs = certs
  94. context.keys = keys
  95. context.extra_chain_cert = chain unless chain.empty?
  96. context.ciphers = ciphers.join ':'
  97. if methods != :SSLv2
  98. context.tmp_dh_callback = proc { dh } if dh
  99. context.ecdh_curves = curves.join ':' if curves
  100. end
  101. context
  102. end
  103. default_parameters = {
  104. methods: %i(TLSv1_2),
  105. chain: %w(intermediate ca),
  106. curves: %i(prime256v1),
  107. server_preference: true
  108. }.freeze
  109. default_ecdsa_parameters = default_parameters.merge({
  110. material: [[:ecdsa, :prime256v1]],
  111. ciphers: %i(ECDHE-ECDSA-AES128-SHA),
  112. curves: %i(prime256v1)
  113. }).freeze
  114. default_rsa_parameters = default_parameters.merge({
  115. material: [[:rsa, 1024]],
  116. ciphers: %i(ECDHE-RSA-AES128-SHA),
  117. curves: %i(prime256v1),
  118. dh: 1024
  119. }).freeze
  120. default_mixed_parameters = default_parameters.merge({
  121. material: [[:ecdsa, :prime256v1], [:rsa, 1024]],
  122. ciphers: %i(ECDHE-ECDSA-AES128-SHA ECDHE-RSA-AES128-SHA),
  123. curves: %i(prime256v1),
  124. dh: 1024
  125. }).freeze
  126. default_sslv2_parameters = default_parameters.merge({
  127. methods: :SSLv2,
  128. material: [[:rsa, 1024]],
  129. ciphers: %i(RC4-MD5),
  130. chain: []
  131. }).freeze
  132. DEFAULT_PARAMETERS = { ecdsa: default_ecdsa_parameters.freeze,
  133. rsa: default_rsa_parameters.freeze,
  134. mixed: default_mixed_parameters.freeze,
  135. sslv2: default_sslv2_parameters.freeze }.freeze
  136. def do_in_serv(type=:ecdsa, **kargs)
  137. params = DEFAULT_PARAMETERS[type].dup
  138. host, port = Helpers::DEFAULT_HOST, Helpers::DEFAULT_PORT
  139. params.merge!({ host: host, port: port })
  140. params.merge!(kargs) if kargs
  141. tls_serv **params do
  142. yield host, port if block_given?
  143. end
  144. end
  145. def tls_serv(host: DEFAULT_HOST, port: DEFAULT_PORT,
  146. material: DEFAULT_MATERIAL, chain: DEFAULT_CHAIN,
  147. methods: DEFAULT_METHODS, ciphers: DEFAULT_CIPHERS,
  148. dh: nil, curves: DEFAULT_CURVES, server_preference: true,
  149. process: nil, &block)
  150. keys = material.collect { |m| key *m }
  151. certs = material.collect { |m| cert *m }
  152. chain = chain.collect { |c| cert c }
  153. dh = dh dh if dh
  154. context = context certs, keys, chain,
  155. methods: methods, ciphers: ciphers,
  156. dh: dh, curves: curves,
  157. server_preference: server_preference
  158. tcp_server = TCPServer.new host, port
  159. tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
  160. begin
  161. serv tls_server, process, &block
  162. ensure
  163. tls_server.close
  164. tcp_server.close
  165. end
  166. end
  167. def plain_serv(host=DEFAULT_HOST, port=DEFAULT_PORT, process: nil, &block)
  168. tcp_server = TCPServer.new host, port
  169. begin
  170. serv tcp_server, process, &block
  171. ensure
  172. tcp_server.close
  173. end
  174. end
  175. def starttls_serv(key: DEFAULT_KEY, domain: DEFAULT_HOST, # Key & certificate
  176. version: DEFAULT_METHOD, ciphers: DEFAULT_CIPHERS, # TLS version and ciphers
  177. dh: DEFAULT_DH_SIZE, ecdh: DEFAULT_ECC_CURVE, # DHE & ECDHE
  178. host: DEFAULT_HOST, port: DEFAULT_PORT, # Binding
  179. plain_process: nil, process: nil, &block)
  180. context = context(key: key, domain: domain, version: version, ciphers: ciphers, dh: dh, ecdh: ecdh)
  181. tcp_server = TCPServer.new host, port
  182. tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
  183. tls_server.start_immediately = false
  184. internal_process = proc do |socket|
  185. accept = false
  186. accept = plain_process.call socket if plain_process
  187. if accept
  188. tls_socket = socket.accept
  189. begin
  190. process.call tls_socket if process
  191. ensure
  192. socket.close
  193. end
  194. end
  195. end
  196. begin
  197. serv tls_server, internal_process, &block
  198. ensure
  199. tls_server.close
  200. tcp_server.close
  201. end
  202. end
  203. def server(servers, host, ip, port)
  204. servers[[host, ip, port]]
  205. end
  206. def expect_grade(servers, host, ip, port, family)
  207. server = server servers, host, ip, port
  208. expect(server).to be_a CryptCheck::Tls::Server
  209. expect(server.hostname).to eq host
  210. expect(server.ip).to eq ip
  211. expect(server.port).to eq port
  212. expect(server.family).to eq case family
  213. when :ipv4
  214. Socket::AF_INET
  215. when :ipv6
  216. Socket::AF_INET6
  217. end
  218. end
  219. def expect_grade_error(servers, host, ip, port, error)
  220. server = servers[[host, ip, port]]
  221. expect(server).to be_a CryptCheck::Tls::AnalysisFailure
  222. expect(server.to_s).to eq error
  223. end
  224. def expect_error(error, type, message)
  225. expect(error).to be_a type
  226. expect(error.message).to eq message
  227. end
  228. end
  229. RSpec.configure do |c|
  230. c.include Helpers
  231. end