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.3KB

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