mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
609103dbb5
Import Ruby/OpenSSL 2.1.0.beta1. The full commit log since v2.0.5 (imported by r59567) can be found at: https://github.com/ruby/openssl/compare/v2.0.5...v2.1.0.beta1 ---------------------------------------------------------------- Antonio Terceiro (1): test/test_ssl: explicitly accept TLS 1.1 in corresponding test Colby Swandale (1): document using secure protocol to fetch git master in Bundler Colton Jenkins (1): Add fips_mode_get to return fips_mode Kazuki Yamaguchi (85): Start preparing for 2.1.0 Remove support for OpenSSL 0.9.8 and 1.0.0 bn: refine tests bn: implement unary {plus,minus} operators for OpenSSL::BN bn: implement OpenSSL::BN#negative? Don't define main() when built with --enable-debug test: let OpenSSL::TestCase include OpenSSL::TestUtils test: prepare test PKey instances on demand Add OpenSSL.print_mem_leaks Enable OSSL_MDEBUG on CI builds ssl: move default DH parameters from OpenSSL::PKey::DH Make exceptions with the same format regardless of OpenSSL.debug ssl: show reason of 'certificate verify error' in exception message ssl: remove OpenSSL::ExtConfig::TLS_DH_anon_WITH_AES_256_GCM_SHA384 ssl: do not confuse different ex_data index registries ssl: assume SSL/SSL_CTX always have a valid reference to the Ruby object Fix RDoc markup ssl: suppress compiler warning ext/openssl/deprecation.rb: remove broken-apple-openssl extconf.rb: print informative message if OpenSSL can't be found Rakefile: compile the extension before test kdf: introduce OpenSSL::KDF module ossl.h: add NUM2UINT64T() macro kdf: add scrypt Expand rb_define_copy_func() macro Expand FPTR_TO_FD() macro Remove SafeGet*() macros cipher: rename GetCipherPtr() to ossl_evp_get_cipherbyname() digest: rename GetDigestPtr() to ossl_evp_get_digestbyname() Add ossl_str_new(), an exception-safe rb_str_new() bio: simplify ossl_membio2str() using ossl_str_new() Remove unused functions and macros Drop support for LibreSSL 2.3 ocsp: add OpenSSL::OCSP::Request#signed? asn1: infinite length -> indefinite length asn1: rearrange tests ssl: remove a needless NULL check in SSL::SSLContext#ciphers ssl: return nil in SSL::SSLSocket#cipher if session is not started asn1: remove an unnecessary function prototype asn1: require tag information when instantiating generic type asn1: initialize 'unused_bits' attribute of BitString with 0 asn1: check for illegal 'unused_bits' value of BitString asn1: disallow NULL to be passed to asn1time_to_time() asn1: avoid truncating OID in OpenSSL::ASN1::ObjectId#oid asn1: allow constructed encoding with definite length form asn1: prohibit indefinite length form for primitive encoding asn1: allow tag number to be >= 32 for universal tag class asn1: use ossl_asn1_tag() asn1: clean up OpenSSL::ASN1::Constructive#to_der asn1: harmonize OpenSSL::ASN1::*#to_der asn1: prevent EOC octets from being in the middle of the content asn1: do not treat EOC octets as part of content octets x509name: add 'loc' and 'set' kwargs to OpenSSL::X509::Name#add_entry ssl: do not call session_remove_cb during GC Backport "Merge branch 'topic/test-memory-leak'" to maint cipher: update the documentation for Cipher#auth_tag= Rakefile: let sync:to_ruby know about test/openssl/fixtures test: fix formatting test/utils: remove OpenSSL::TestUtils.silent test/utils: add SSLTestCase#tls12_supported? test/utils: have start_server yield only the port number test/utils: do not set ecdh_curves in start_server test/utils: let server_loop close socket test/utils: improve error handling in start_server test/utils: add OpenSSL::TestUtils.openssl? and .libressl? test/utils: do not use DSA certificates in SSL tests test/test_ssl: remove test_invalid_shutdown_by_gc test/test_ssl: move test_multibyte_read_write to test_pair test/test_ssl_session: rearrange tests test/test_pair, test/test_ssl: fix for TLS 1.3 ssl: remove useless call to rb_thread_wait_fd() ssl: fix NPN support ssl: mark OpenSSL::SSL::SSLContext::DEFAULT_{1024,2048} as private ssl: use 2048-bit group in the default tmp_dh_cb ssl: ensure that SSL option flags are non-negative ssl: update OpenSSL::SSL::OP_* flags ssl: prefer TLS_method() over SSLv23_method() ssl: add SSLContext#min_version= and #max_version= ssl: rework SSLContext#ssl_version= test/test_x509name: change script encoding to ASCII-8BIT x509name: refactor OpenSSL::X509::Name#to_s x509name: add OpenSSL::X509::Name#to_utf8 x509name: add OpenSSL::X509::Name#inspect x509name: update regexp in OpenSSL::X509::Name.parse Ruby/OpenSSL 2.1.0.beta1 Marcus Stollsteimer (1): Fix rdoc for core Integer class nobu (4): [DOC] {read,write}_nonblock with exception: false [DOC] keyword argument _exception_ [DOC] mark up literals Revert r57690 except for read_nonblock git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59734 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
308 lines
8.6 KiB
Ruby
308 lines
8.6 KiB
Ruby
# frozen_string_literal: false
|
|
begin
|
|
require "openssl"
|
|
|
|
# Disable FIPS mode for tests for installations
|
|
# where FIPS mode would be enabled by default.
|
|
# Has no effect on all other installations.
|
|
OpenSSL.fips_mode=false
|
|
rescue LoadError
|
|
end
|
|
|
|
# Compile OpenSSL with crypto-mdebug and run this test suite with OSSL_MDEBUG=1
|
|
# environment variable to enable memory leak check.
|
|
if ENV["OSSL_MDEBUG"] == "1"
|
|
if OpenSSL.respond_to?(:print_mem_leaks)
|
|
OpenSSL.mem_check_start
|
|
|
|
END {
|
|
GC.start
|
|
case OpenSSL.print_mem_leaks
|
|
when nil
|
|
warn "mdebug: check what is printed"
|
|
when true
|
|
raise "mdebug: memory leaks detected"
|
|
end
|
|
}
|
|
else
|
|
warn "OSSL_MDEBUG=1 is specified but OpenSSL is not built with crypto-mdebug"
|
|
end
|
|
end
|
|
|
|
require "test/unit"
|
|
require "tempfile"
|
|
require "socket"
|
|
require "envutil"
|
|
|
|
if defined?(OpenSSL)
|
|
|
|
module OpenSSL::TestUtils
|
|
module Fixtures
|
|
module_function
|
|
|
|
def pkey(name)
|
|
OpenSSL::PKey.read(read_file("pkey", name))
|
|
end
|
|
|
|
def pkey_dh(name)
|
|
# DH parameters can be read by OpenSSL::PKey.read atm
|
|
OpenSSL::PKey::DH.new(read_file("pkey", name))
|
|
end
|
|
|
|
def read_file(category, name)
|
|
@file_cache ||= {}
|
|
@file_cache[[category, name]] ||=
|
|
File.read(File.join(__dir__, "fixtures", category, name + ".pem"))
|
|
end
|
|
end
|
|
|
|
module_function
|
|
|
|
def issue_cert(dn, key, serial, extensions, issuer, issuer_key,
|
|
not_before: nil, not_after: nil, digest: "sha256")
|
|
cert = OpenSSL::X509::Certificate.new
|
|
issuer = cert unless issuer
|
|
issuer_key = key unless issuer_key
|
|
cert.version = 2
|
|
cert.serial = serial
|
|
cert.subject = dn
|
|
cert.issuer = issuer.subject
|
|
cert.public_key = key.public_key
|
|
now = Time.now
|
|
cert.not_before = not_before || now - 3600
|
|
cert.not_after = not_after || now + 3600
|
|
ef = OpenSSL::X509::ExtensionFactory.new
|
|
ef.subject_certificate = cert
|
|
ef.issuer_certificate = issuer
|
|
extensions.each{|oid, value, critical|
|
|
cert.add_extension(ef.create_extension(oid, value, critical))
|
|
}
|
|
cert.sign(issuer_key, digest)
|
|
cert
|
|
end
|
|
|
|
def issue_crl(revoke_info, serial, lastup, nextup, extensions,
|
|
issuer, issuer_key, digest)
|
|
crl = OpenSSL::X509::CRL.new
|
|
crl.issuer = issuer.subject
|
|
crl.version = 1
|
|
crl.last_update = lastup
|
|
crl.next_update = nextup
|
|
revoke_info.each{|rserial, time, reason_code|
|
|
revoked = OpenSSL::X509::Revoked.new
|
|
revoked.serial = rserial
|
|
revoked.time = time
|
|
enum = OpenSSL::ASN1::Enumerated(reason_code)
|
|
ext = OpenSSL::X509::Extension.new("CRLReason", enum)
|
|
revoked.add_extension(ext)
|
|
crl.add_revoked(revoked)
|
|
}
|
|
ef = OpenSSL::X509::ExtensionFactory.new
|
|
ef.issuer_certificate = issuer
|
|
ef.crl = crl
|
|
crlnum = OpenSSL::ASN1::Integer(serial)
|
|
crl.add_extension(OpenSSL::X509::Extension.new("crlNumber", crlnum))
|
|
extensions.each{|oid, value, critical|
|
|
crl.add_extension(ef.create_extension(oid, value, critical))
|
|
}
|
|
crl.sign(issuer_key, digest)
|
|
crl
|
|
end
|
|
|
|
def get_subject_key_id(cert)
|
|
asn1_cert = OpenSSL::ASN1.decode(cert)
|
|
tbscert = asn1_cert.value[0]
|
|
pkinfo = tbscert.value[6]
|
|
publickey = pkinfo.value[1]
|
|
pkvalue = publickey.value
|
|
OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase
|
|
end
|
|
|
|
def openssl?(major = nil, minor = nil, fix = nil, patch = 0)
|
|
return false if OpenSSL::OPENSSL_VERSION.include?("LibreSSL")
|
|
return true unless major
|
|
OpenSSL::OPENSSL_VERSION_NUMBER >=
|
|
major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10
|
|
end
|
|
|
|
def libressl?(major = nil, minor = nil, fix = nil)
|
|
version = OpenSSL::OPENSSL_VERSION.scan(/LibreSSL (\d+)\.(\d+)\.(\d+).*/)[0]
|
|
return false unless version
|
|
!major || (version.map(&:to_i) <=> [major, minor, fix]) >= 0
|
|
end
|
|
end
|
|
|
|
class OpenSSL::TestCase < Test::Unit::TestCase
|
|
include OpenSSL::TestUtils
|
|
extend OpenSSL::TestUtils
|
|
|
|
def setup
|
|
if ENV["OSSL_GC_STRESS"] == "1"
|
|
GC.stress = true
|
|
end
|
|
end
|
|
|
|
def teardown
|
|
if ENV["OSSL_GC_STRESS"] == "1"
|
|
GC.stress = false
|
|
end
|
|
# OpenSSL error stack must be empty
|
|
assert_equal([], OpenSSL.errors)
|
|
end
|
|
end
|
|
|
|
class OpenSSL::SSLTestCase < OpenSSL::TestCase
|
|
RUBY = EnvUtil.rubybin
|
|
ITERATIONS = ($0 == __FILE__) ? 100 : 10
|
|
|
|
def setup
|
|
super
|
|
@ca_key = Fixtures.pkey("rsa2048")
|
|
@svr_key = Fixtures.pkey("rsa1024")
|
|
@cli_key = Fixtures.pkey("rsa2048")
|
|
@ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
|
|
@svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
|
|
@cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
|
|
ca_exts = [
|
|
["basicConstraints","CA:TRUE",true],
|
|
["keyUsage","cRLSign,keyCertSign",true],
|
|
]
|
|
ee_exts = [
|
|
["keyUsage","keyEncipherment,digitalSignature",true],
|
|
]
|
|
@ca_cert = issue_cert(@ca, @ca_key, 1, ca_exts, nil, nil)
|
|
@svr_cert = issue_cert(@svr, @svr_key, 2, ee_exts, @ca_cert, @ca_key)
|
|
@cli_cert = issue_cert(@cli, @cli_key, 3, ee_exts, @ca_cert, @ca_key)
|
|
@server = nil
|
|
end
|
|
|
|
def tls12_supported?
|
|
ctx = OpenSSL::SSL::SSLContext.new
|
|
ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
|
|
true
|
|
rescue
|
|
end
|
|
|
|
def readwrite_loop(ctx, ssl)
|
|
while line = ssl.gets
|
|
ssl.write(line)
|
|
end
|
|
end
|
|
|
|
def start_server(verify_mode: OpenSSL::SSL::VERIFY_NONE, start_immediately: true,
|
|
ctx_proc: nil, server_proc: method(:readwrite_loop),
|
|
ignore_listener_error: false, &block)
|
|
IO.pipe {|stop_pipe_r, stop_pipe_w|
|
|
store = OpenSSL::X509::Store.new
|
|
store.add_cert(@ca_cert)
|
|
store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
|
|
ctx = OpenSSL::SSL::SSLContext.new
|
|
ctx.cert_store = store
|
|
ctx.cert = @svr_cert
|
|
ctx.key = @svr_key
|
|
ctx.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") }
|
|
ctx.verify_mode = verify_mode
|
|
ctx_proc.call(ctx) if ctx_proc
|
|
|
|
Socket.do_not_reverse_lookup = true
|
|
tcps = TCPServer.new("127.0.0.1", 0)
|
|
port = tcps.connect_address.ip_port
|
|
|
|
ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
|
|
ssls.start_immediately = start_immediately
|
|
|
|
threads = []
|
|
begin
|
|
server_thread = Thread.new do
|
|
begin
|
|
loop do
|
|
begin
|
|
readable, = IO.select([ssls, stop_pipe_r])
|
|
break if readable.include? stop_pipe_r
|
|
ssl = ssls.accept
|
|
rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL,
|
|
Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
|
|
retry if ignore_listener_error
|
|
raise
|
|
end
|
|
|
|
th = Thread.new do
|
|
begin
|
|
server_proc.call(ctx, ssl)
|
|
ensure
|
|
ssl.close
|
|
end
|
|
true
|
|
end
|
|
threads << th
|
|
end
|
|
ensure
|
|
tcps.close
|
|
end
|
|
end
|
|
|
|
client_thread = Thread.new do
|
|
begin
|
|
block.call(port)
|
|
ensure
|
|
# Stop accepting new connection
|
|
stop_pipe_w.close
|
|
server_thread.join
|
|
end
|
|
end
|
|
threads.unshift client_thread
|
|
ensure
|
|
# Terminate existing connections. If a thread did 'pend', re-raise it.
|
|
pend = nil
|
|
threads.each { |th|
|
|
begin
|
|
th.join(10) or
|
|
th.raise(RuntimeError, "[start_server] thread did not exit in 10 secs")
|
|
rescue (defined?(MiniTest::Skip) ? MiniTest::Skip : Test::Unit::PendedError)
|
|
# MiniTest::Skip is for the Ruby tree
|
|
pend = $!
|
|
rescue Exception
|
|
end
|
|
}
|
|
raise pend if pend
|
|
assert_join_threads(threads)
|
|
end
|
|
}
|
|
end
|
|
end
|
|
|
|
class OpenSSL::PKeyTestCase < OpenSSL::TestCase
|
|
def check_component(base, test, keys)
|
|
keys.each { |comp|
|
|
assert_equal base.send(comp), test.send(comp)
|
|
}
|
|
end
|
|
|
|
def dup_public(key)
|
|
case key
|
|
when OpenSSL::PKey::RSA
|
|
rsa = OpenSSL::PKey::RSA.new
|
|
rsa.set_key(key.n, key.e, nil)
|
|
rsa
|
|
when OpenSSL::PKey::DSA
|
|
dsa = OpenSSL::PKey::DSA.new
|
|
dsa.set_pqg(key.p, key.q, key.g)
|
|
dsa.set_key(key.pub_key, nil)
|
|
dsa
|
|
when OpenSSL::PKey::DH
|
|
dh = OpenSSL::PKey::DH.new
|
|
dh.set_pqg(key.p, nil, key.g)
|
|
dh
|
|
else
|
|
if defined?(OpenSSL::PKey::EC) && OpenSSL::PKey::EC === key
|
|
ec = OpenSSL::PKey::EC.new(key.group)
|
|
ec.public_key = key.public_key
|
|
ec
|
|
else
|
|
raise "unknown key type"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|