PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/test/openssl/utils.rb

http://github.com/ruby/ruby
Ruby | 397 lines | 337 code | 49 blank | 11 comment | 24 complexity | 64272c7e47c996f8725ea98ff516e966 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0
  1. # frozen_string_literal: true
  2. begin
  3. require "openssl"
  4. # Disable FIPS mode for tests for installations
  5. # where FIPS mode would be enabled by default.
  6. # Has no effect on all other installations.
  7. OpenSSL.fips_mode=false
  8. rescue LoadError
  9. end
  10. # Compile OpenSSL with crypto-mdebug and run this test suite with OSSL_MDEBUG=1
  11. # environment variable to enable memory leak check.
  12. if ENV["OSSL_MDEBUG"] == "1"
  13. if OpenSSL.respond_to?(:print_mem_leaks)
  14. OpenSSL.mem_check_start
  15. END {
  16. GC.start
  17. case OpenSSL.print_mem_leaks
  18. when nil
  19. warn "mdebug: check what is printed"
  20. when true
  21. raise "mdebug: memory leaks detected"
  22. end
  23. }
  24. else
  25. warn "OSSL_MDEBUG=1 is specified but OpenSSL is not built with crypto-mdebug"
  26. end
  27. end
  28. require "test/unit"
  29. require "tempfile"
  30. require "socket"
  31. require "envutil"
  32. if defined?(OpenSSL)
  33. module OpenSSL::TestUtils
  34. module Fixtures
  35. module_function
  36. def pkey(name)
  37. OpenSSL::PKey.read(read_file("pkey", name))
  38. rescue OpenSSL::PKey::PKeyError
  39. # TODO: DH parameters can be read by OpenSSL::PKey.read atm
  40. OpenSSL::PKey::DH.new(read_file("pkey", name))
  41. end
  42. def read_file(category, name)
  43. @file_cache ||= {}
  44. @file_cache[[category, name]] ||=
  45. File.read(File.join(__dir__, "fixtures", category, name + ".pem"))
  46. end
  47. def file_path(category, name)
  48. File.join(__dir__, "fixtures", category, name)
  49. end
  50. end
  51. module_function
  52. def generate_cert(dn, key, serial, issuer,
  53. not_before: nil, not_after: nil)
  54. cert = OpenSSL::X509::Certificate.new
  55. issuer = cert unless issuer
  56. cert.version = 2
  57. cert.serial = serial
  58. cert.subject = dn
  59. cert.issuer = issuer.subject
  60. cert.public_key = key
  61. now = Time.now
  62. cert.not_before = not_before || now - 3600
  63. cert.not_after = not_after || now + 3600
  64. cert
  65. end
  66. def issue_cert(dn, key, serial, extensions, issuer, issuer_key,
  67. not_before: nil, not_after: nil, digest: "sha256")
  68. cert = generate_cert(dn, key, serial, issuer,
  69. not_before: not_before, not_after: not_after)
  70. issuer = cert unless issuer
  71. issuer_key = key unless issuer_key
  72. ef = OpenSSL::X509::ExtensionFactory.new
  73. ef.subject_certificate = cert
  74. ef.issuer_certificate = issuer
  75. extensions.each{|oid, value, critical|
  76. cert.add_extension(ef.create_extension(oid, value, critical))
  77. }
  78. cert.sign(issuer_key, digest)
  79. cert
  80. end
  81. def issue_crl(revoke_info, serial, lastup, nextup, extensions,
  82. issuer, issuer_key, digest)
  83. crl = OpenSSL::X509::CRL.new
  84. crl.issuer = issuer.subject
  85. crl.version = 1
  86. crl.last_update = lastup
  87. crl.next_update = nextup
  88. revoke_info.each{|rserial, time, reason_code|
  89. revoked = OpenSSL::X509::Revoked.new
  90. revoked.serial = rserial
  91. revoked.time = time
  92. enum = OpenSSL::ASN1::Enumerated(reason_code)
  93. ext = OpenSSL::X509::Extension.new("CRLReason", enum)
  94. revoked.add_extension(ext)
  95. crl.add_revoked(revoked)
  96. }
  97. ef = OpenSSL::X509::ExtensionFactory.new
  98. ef.issuer_certificate = issuer
  99. ef.crl = crl
  100. crlnum = OpenSSL::ASN1::Integer(serial)
  101. crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum))
  102. extensions.each{|oid, value, critical|
  103. crl.add_extension(ef.create_extension(oid, value, critical))
  104. }
  105. crl.sign(issuer_key, digest)
  106. crl
  107. end
  108. def get_subject_key_id(cert, hex: true)
  109. asn1_cert = OpenSSL::ASN1.decode(cert)
  110. tbscert = asn1_cert.value[0]
  111. pkinfo = tbscert.value[6]
  112. publickey = pkinfo.value[1]
  113. pkvalue = publickey.value
  114. digest = OpenSSL::Digest::SHA1.digest(pkvalue)
  115. if hex
  116. digest.unpack("H2"*20).join(":").upcase
  117. else
  118. digest
  119. end
  120. end
  121. def openssl?(major = nil, minor = nil, fix = nil, patch = 0)
  122. return false if OpenSSL::OPENSSL_VERSION.include?("LibreSSL")
  123. return true unless major
  124. OpenSSL::OPENSSL_VERSION_NUMBER >=
  125. major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10
  126. end
  127. def libressl?(major = nil, minor = nil, fix = nil)
  128. version = OpenSSL::OPENSSL_VERSION.scan(/LibreSSL (\d+)\.(\d+)\.(\d+).*/)[0]
  129. return false unless version
  130. !major || (version.map(&:to_i) <=> [major, minor, fix]) >= 0
  131. end
  132. end
  133. class OpenSSL::TestCase < Test::Unit::TestCase
  134. include OpenSSL::TestUtils
  135. extend OpenSSL::TestUtils
  136. def setup
  137. if ENV["OSSL_GC_STRESS"] == "1"
  138. GC.stress = true
  139. end
  140. end
  141. def teardown
  142. if ENV["OSSL_GC_STRESS"] == "1"
  143. GC.stress = false
  144. end
  145. # OpenSSL error stack must be empty
  146. assert_equal([], OpenSSL.errors)
  147. end
  148. end
  149. class OpenSSL::SSLTestCase < OpenSSL::TestCase
  150. RUBY = EnvUtil.rubybin
  151. ITERATIONS = ($0 == __FILE__) ? 100 : 10
  152. def setup
  153. super
  154. @ca_key = Fixtures.pkey("rsa-1")
  155. @svr_key = Fixtures.pkey("rsa-2")
  156. @cli_key = Fixtures.pkey("rsa-3")
  157. @ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
  158. @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
  159. @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
  160. ca_exts = [
  161. ["basicConstraints","CA:TRUE",true],
  162. ["keyUsage","cRLSign,keyCertSign",true],
  163. ]
  164. ee_exts = [
  165. ["keyUsage","keyEncipherment,digitalSignature",true],
  166. ]
  167. @ca_cert = issue_cert(@ca, @ca_key, 1, ca_exts, nil, nil)
  168. @svr_cert = issue_cert(@svr, @svr_key, 2, ee_exts, @ca_cert, @ca_key)
  169. @cli_cert = issue_cert(@cli, @cli_key, 3, ee_exts, @ca_cert, @ca_key)
  170. @server = nil
  171. end
  172. def tls12_supported?
  173. ctx = OpenSSL::SSL::SSLContext.new
  174. ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
  175. true
  176. rescue
  177. end
  178. def readwrite_loop(ctx, ssl)
  179. while line = ssl.gets
  180. ssl.write(line)
  181. end
  182. end
  183. def start_server(verify_mode: OpenSSL::SSL::VERIFY_NONE, start_immediately: true,
  184. ctx_proc: nil, server_proc: method(:readwrite_loop),
  185. accept_proc: proc{},
  186. ignore_listener_error: false, &block)
  187. IO.pipe {|stop_pipe_r, stop_pipe_w|
  188. store = OpenSSL::X509::Store.new
  189. store.add_cert(@ca_cert)
  190. store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
  191. ctx = OpenSSL::SSL::SSLContext.new
  192. ctx.cert_store = store
  193. ctx.cert = @svr_cert
  194. ctx.key = @svr_key
  195. ctx.tmp_dh_callback = proc { Fixtures.pkey("dh-1") }
  196. ctx.verify_mode = verify_mode
  197. ctx_proc.call(ctx) if ctx_proc
  198. Socket.do_not_reverse_lookup = true
  199. tcps = TCPServer.new("127.0.0.1", 0)
  200. port = tcps.connect_address.ip_port
  201. ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
  202. ssls.start_immediately = start_immediately
  203. threads = []
  204. begin
  205. server_thread = Thread.new do
  206. if Thread.method_defined?(:report_on_exception=) # Ruby >= 2.4
  207. Thread.current.report_on_exception = false
  208. end
  209. begin
  210. loop do
  211. begin
  212. readable, = IO.select([ssls, stop_pipe_r])
  213. break if readable.include? stop_pipe_r
  214. ssl = ssls.accept
  215. accept_proc.call(ssl)
  216. rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL,
  217. Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
  218. retry if ignore_listener_error
  219. raise
  220. end
  221. th = Thread.new do
  222. if Thread.method_defined?(:report_on_exception=)
  223. Thread.current.report_on_exception = false
  224. end
  225. begin
  226. server_proc.call(ctx, ssl)
  227. ensure
  228. ssl.close
  229. end
  230. true
  231. end
  232. threads << th
  233. end
  234. ensure
  235. tcps.close
  236. end
  237. end
  238. client_thread = Thread.new do
  239. if Thread.method_defined?(:report_on_exception=)
  240. Thread.current.report_on_exception = false
  241. end
  242. begin
  243. block.call(port)
  244. ensure
  245. # Stop accepting new connection
  246. stop_pipe_w.close
  247. server_thread.join
  248. end
  249. end
  250. threads.unshift client_thread
  251. ensure
  252. # Terminate existing connections. If a thread did 'pend', re-raise it.
  253. pend = nil
  254. threads.each { |th|
  255. begin
  256. timeout = EnvUtil.apply_timeout_scale(30)
  257. th.join(timeout) or
  258. th.raise(RuntimeError, "[start_server] thread did not exit in #{timeout} secs")
  259. rescue (defined?(MiniTest::Skip) ? MiniTest::Skip : Test::Unit::PendedError)
  260. # MiniTest::Skip is for the Ruby tree
  261. pend = $!
  262. rescue Exception
  263. end
  264. }
  265. raise pend if pend
  266. assert_join_threads(threads)
  267. end
  268. }
  269. end
  270. end
  271. class OpenSSL::PKeyTestCase < OpenSSL::TestCase
  272. def check_component(base, test, keys)
  273. keys.each { |comp|
  274. assert_equal base.send(comp), test.send(comp)
  275. }
  276. end
  277. def dup_public(key)
  278. case key
  279. when OpenSSL::PKey::RSA
  280. rsa = OpenSSL::PKey::RSA.new
  281. rsa.set_key(key.n, key.e, nil)
  282. rsa
  283. when OpenSSL::PKey::DSA
  284. dsa = OpenSSL::PKey::DSA.new
  285. dsa.set_pqg(key.p, key.q, key.g)
  286. dsa.set_key(key.pub_key, nil)
  287. dsa
  288. when OpenSSL::PKey::DH
  289. dh = OpenSSL::PKey::DH.new
  290. dh.set_pqg(key.p, nil, key.g)
  291. dh
  292. else
  293. if defined?(OpenSSL::PKey::EC) && OpenSSL::PKey::EC === key
  294. ec = OpenSSL::PKey::EC.new(key.group)
  295. ec.public_key = key.public_key
  296. ec
  297. else
  298. raise "unknown key type"
  299. end
  300. end
  301. end
  302. end
  303. module OpenSSL::Certs
  304. include OpenSSL::TestUtils
  305. module_function
  306. def ca_cert
  307. ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Timestamp Root CA")
  308. ca_exts = [
  309. ["basicConstraints","CA:TRUE,pathlen:1",true],
  310. ["keyUsage","keyCertSign, cRLSign",true],
  311. ["subjectKeyIdentifier","hash",false],
  312. ["authorityKeyIdentifier","keyid:always",false],
  313. ]
  314. OpenSSL::TestUtils.issue_cert(ca, Fixtures.pkey("rsa2048"), 1, ca_exts, nil, nil)
  315. end
  316. def ts_cert_direct(key, ca_cert)
  317. dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Server Direct")
  318. exts = [
  319. ["basicConstraints","CA:FALSE",true],
  320. ["keyUsage","digitalSignature, nonRepudiation", true],
  321. ["subjectKeyIdentifier", "hash",false],
  322. ["authorityKeyIdentifier","keyid,issuer", false],
  323. ["extendedKeyUsage", "timeStamping", true]
  324. ]
  325. OpenSSL::TestUtils.issue_cert(dn, key, 2, exts, ca_cert, Fixtures.pkey("rsa2048"))
  326. end
  327. def intermediate_cert(key, ca_cert)
  328. dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Timestamp Intermediate CA")
  329. exts = [
  330. ["basicConstraints","CA:TRUE,pathlen:0",true],
  331. ["keyUsage","keyCertSign, cRLSign",true],
  332. ["subjectKeyIdentifier","hash",false],
  333. ["authorityKeyIdentifier","keyid:always",false],
  334. ]
  335. OpenSSL::TestUtils.issue_cert(dn, key, 3, exts, ca_cert, Fixtures.pkey("rsa2048"))
  336. end
  337. def ts_cert_ee(key, intermediate, im_key)
  338. dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Server End Entity")
  339. exts = [
  340. ["keyUsage","digitalSignature, nonRepudiation", true],
  341. ["subjectKeyIdentifier", "hash",false],
  342. ["authorityKeyIdentifier","keyid,issuer", false],
  343. ["extendedKeyUsage", "timeStamping", true]
  344. ]
  345. OpenSSL::TestUtils.issue_cert(dn, key, 4, exts, intermediate, im_key)
  346. end
  347. end
  348. end