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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. $:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib'
  2. require 'rubygems'
  3. require 'bundler/setup'
  4. require 'cryptcheck'
  5. Dir['./spec/**/support/**/*.rb'].sort.each { |f| require f }
  6. CryptCheck::Logger.level = ENV['LOG'] || :none
  7. module Helpers
  8. DEFAULT_KEY = 'rsa-1024'
  9. DEFAULT_METHOD = :TLSv1_2
  10. DEFAULT_CIPHERS = %w(AES128-SHA)
  11. DEFAULT_ECC_CURVE = 'secp256k1'
  12. DEFAULT_DH_SIZE = 1024
  13. OpenSSL::PKey::EC.send :alias_method, :private?, :private_key?
  14. def key(name)
  15. open(File.join(File.dirname(__FILE__), 'resources', "#{name}.pem"), 'r') { |f| OpenSSL::PKey.read f }
  16. end
  17. def dh(name)
  18. open(File.join(File.dirname(__FILE__), 'resources', "dh-#{name}.pem"), 'r') { |f| OpenSSL::PKey::DH.new f }
  19. end
  20. def certificate(key, domain)
  21. cert = OpenSSL::X509::Certificate.new
  22. cert.version = 2
  23. cert.serial = rand 2**(20*8-1) .. 2**(20*8)
  24. cert.not_before = Time.now
  25. cert.not_after = cert.not_before + 60*60
  26. cert.public_key = case key
  27. when OpenSSL::PKey::EC
  28. curve = key.group.curve_name
  29. public = OpenSSL::PKey::EC.new curve
  30. public.public_key = key.public_key
  31. public
  32. else
  33. key.public_key
  34. end
  35. name = OpenSSL::X509::Name.parse "CN=#{domain}"
  36. cert.subject = name
  37. cert.issuer = name
  38. extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert
  39. extension_factory.subject_certificate = cert
  40. extension_factory.issuer_certificate = cert
  41. cert.add_extension extension_factory.create_extension 'basicConstraints', 'CA:TRUE', true
  42. cert.add_extension extension_factory.create_extension 'keyUsage', 'keyEncipherment, dataEncipherment, digitalSignature,nonRepudiation,keyCertSign'
  43. cert.add_extension extension_factory.create_extension 'extendedKeyUsage', 'serverAuth, clientAuth'
  44. cert.add_extension extension_factory.create_extension 'subjectKeyIdentifier', 'hash'
  45. cert.add_extension extension_factory.create_extension 'authorityKeyIdentifier', 'keyid:always'
  46. cert.add_extension extension_factory.create_extension 'subjectAltName', "DNS:#{domain}"
  47. cert.sign key, OpenSSL::Digest::SHA512.new
  48. end
  49. def serv(server, process, &block)
  50. IO.pipe do |stop_pipe_r, stop_pipe_w|
  51. threads = []
  52. mutex = Mutex.new
  53. started = ConditionVariable.new
  54. threads << Thread.start do
  55. mutex.synchronize { started.signal }
  56. loop do
  57. readable, = IO.select [server, stop_pipe_r]
  58. break if readable.include? stop_pipe_r
  59. begin
  60. socket = server.accept
  61. begin
  62. process.call socket if process
  63. ensure
  64. socket.close
  65. end
  66. rescue
  67. end
  68. end
  69. server.close
  70. end
  71. mutex.synchronize { started.wait mutex }
  72. begin
  73. block.call if block
  74. ensure
  75. stop_pipe_w.close
  76. threads.each &:join
  77. end
  78. end
  79. end
  80. def context(key: DEFAULT_KEY, domain: 'localhost', # Key & certificate
  81. version: DEFAULT_METHOD, ciphers: DEFAULT_CIPHERS, # TLS version and ciphers
  82. dh: DEFAULT_DH_SIZE, ecdh: DEFAULT_ECC_CURVE) # DHE & ECDHE
  83. key = key key
  84. cert = certificate key, domain
  85. context = OpenSSL::SSL::SSLContext.new version
  86. context.cert = cert
  87. context.key = key
  88. context.ciphers = ciphers
  89. if dh
  90. dh = dh dh
  91. context.tmp_dh_callback = proc { dh }
  92. end
  93. context.ecdh_curves = ecdh if ecdh
  94. context
  95. end
  96. def tls_serv(key: DEFAULT_KEY, domain: 'localhost', # Key & certificate
  97. version: DEFAULT_METHOD, ciphers: DEFAULT_CIPHERS, # TLS version and ciphers
  98. dh: DEFAULT_DH_SIZE, ecdh: DEFAULT_ECC_CURVE, # DHE & ECDHE
  99. host: '127.0.0.1', port: 5000, # Binding
  100. process: nil, &block)
  101. context = context(key: key, domain: domain, version: version, ciphers: ciphers, dh: dh, ecdh: ecdh)
  102. tcp_server = TCPServer.new host, port
  103. tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
  104. begin
  105. serv tls_server, process, &block
  106. ensure
  107. tls_server.close
  108. tcp_server.close
  109. end
  110. end
  111. def plain_serv(host: '127.0.0.1', port: 5000, process: nil, &block)
  112. tcp_server = TCPServer.new host, port
  113. begin
  114. serv tcp_server, process, &block
  115. ensure
  116. tcp_server.close
  117. end
  118. end
  119. def starttls_serv(key: DEFAULT_KEY, domain: 'localhost', # Key & certificate
  120. version: DEFAULT_METHOD, ciphers: DEFAULT_CIPHERS, # TLS version and ciphers
  121. dh: DEFAULT_DH_SIZE, ecdh: DEFAULT_ECC_CURVE, # DHE & ECDHE
  122. host: '127.0.0.1', port: 5000, # Binding
  123. plain_process: nil, process: nil, &block)
  124. context = context(key: key, domain: domain, version: version, ciphers: ciphers, dh: dh, ecdh: ecdh)
  125. tcp_server = TCPServer.new host, port
  126. tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
  127. tls_server.start_immediately = false
  128. internal_process = proc do |socket|
  129. accept = false
  130. accept = plain_process.call socket if plain_process
  131. if accept
  132. tls_socket = socket.accept
  133. begin
  134. process.call tls_socket if process
  135. ensure
  136. socket.close
  137. end
  138. end
  139. end
  140. begin
  141. serv tls_server, internal_process, &block
  142. ensure
  143. tls_server.close
  144. tcp_server.close
  145. end
  146. end
  147. def grade(grades, host, ip, port)
  148. grades[[host, ip, port]]
  149. end
  150. def expect_grade(grades, host, ip, port, family)
  151. grade = grade grades, host, ip, port
  152. expect(grade).to be_a CryptCheck::Tls::Grade
  153. server = grade.server
  154. expect(server).to be_a CryptCheck::Tls::Server
  155. expect(server.hostname).to eq host
  156. expect(server.ip).to eq ip
  157. expect(server.port).to eq port
  158. expect(server.family).to eq case family
  159. when :ipv4
  160. Socket::AF_INET
  161. when :ipv6
  162. Socket::AF_INET6
  163. end
  164. [grade, server]
  165. end
  166. def expect_grade_error(grades, host, ip, port, error)
  167. server = grades[[host, ip, port]]
  168. expect(server).to be_a CryptCheck::AnalysisFailure
  169. expect(server.to_s).to eq error
  170. end
  171. end
  172. RSpec.configure do |c|
  173. c.include Helpers
  174. end