summaryrefslogtreecommitdiff
path: root/test/openssl/test_ssl.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/openssl/test_ssl.rb')
-rw-r--r--test/openssl/test_ssl.rb889
1 files changed, 659 insertions, 230 deletions
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index f011e881e9..e4fd581079 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -15,11 +15,16 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
+ def test_ctx_setup
+ ctx = OpenSSL::SSL::SSLContext.new
+ assert_equal true, ctx.setup
+ assert_predicate ctx, :frozen?
+ assert_equal nil, ctx.setup
+ end
+
def test_ctx_options
ctx = OpenSSL::SSL::SSLContext.new
- assert (OpenSSL::SSL::OP_ALL & ctx.options) == OpenSSL::SSL::OP_ALL,
- "OP_ALL is set by default"
ctx.options = 4
assert_equal 4, ctx.options & 4
if ctx.options != 4
@@ -33,6 +38,30 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
assert_equal nil, ctx.setup
end
+ def test_ctx_options_config
+ omit "LibreSSL and AWS-LC do not support OPENSSL_CONF" if libressl? || aws_lc?
+
+ Tempfile.create("openssl.cnf") { |f|
+ f.puts(<<~EOF)
+ openssl_conf = default_conf
+ [default_conf]
+ ssl_conf = ssl_sect
+ [ssl_sect]
+ system_default = ssl_default_sect
+ [ssl_default_sect]
+ Options = -SessionTicket
+ EOF
+ f.close
+
+ assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl"], <<~"end;")
+ ctx = OpenSSL::SSL::SSLContext.new
+ assert_equal OpenSSL::SSL::OP_NO_TICKET, ctx.options & OpenSSL::SSL::OP_NO_TICKET
+ ctx.set_params
+ assert_equal OpenSSL::SSL::OP_NO_TICKET, ctx.options & OpenSSL::SSL::OP_NO_TICKET
+ end;
+ }
+ end
+
def test_ssl_with_server_cert
ctx_proc = -> ctx {
ctx.cert = @svr_cert
@@ -201,6 +230,34 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
+ def test_extra_chain_cert_auto_chain
+ start_server { |port|
+ server_connect(port) { |ssl|
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ assert_equal @svr_cert.to_der, ssl.peer_cert.to_der
+ assert_equal [@svr_cert], ssl.peer_cert_chain
+ }
+ }
+
+ # AWS-LC enables SSL_MODE_NO_AUTO_CHAIN by default
+ unless aws_lc?
+ ctx_proc = -> ctx {
+ # Sanity check: start_server won't set extra_chain_cert
+ assert_nil ctx.extra_chain_cert
+ ctx.cert_store = OpenSSL::X509::Store.new.tap { |store|
+ store.add_cert(@ca_cert)
+ }
+ }
+ start_server(ctx_proc: ctx_proc) { |port|
+ server_connect(port) { |ssl|
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ assert_equal @svr_cert.to_der, ssl.peer_cert.to_der
+ assert_equal [@svr_cert, @ca_cert], ssl.peer_cert_chain
+ }
+ }
+ end
+ end
+
def test_sysread_and_syswrite
start_server { |port|
server_connect(port) { |ssl|
@@ -213,6 +270,11 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl.syswrite(str)
assert_same buf, ssl.sysread(str.size, buf)
assert_equal(str, buf)
+
+ obj = Object.new
+ obj.define_singleton_method(:to_str) { str }
+ ssl.syswrite(obj)
+ assert_equal(str, ssl.sysread(str.bytesize))
}
}
end
@@ -226,11 +288,16 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl.syswrite(str)
assert_equal(str, ssl.sysread(str.bytesize))
- ssl.timeout = 1
- assert_raise(IO::TimeoutError) {ssl.read(1)}
+ ssl.timeout = 0.1
+ assert_raise(IO::TimeoutError) { ssl.sysread(1) }
ssl.syswrite(str)
assert_equal(str, ssl.sysread(str.bytesize))
+
+ buf = "orig".b
+ assert_raise(IO::TimeoutError) { ssl.sysread(1, buf) }
+ assert_equal("orig", buf)
+ assert_nothing_raised { buf.clear }
end
end
end
@@ -288,6 +355,22 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
+ def test_sync_close_initialize_opt
+ start_server do |port|
+ begin
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, sync_close: true)
+ assert_equal true, ssl.sync_close
+ ssl.connect
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ ssl.close
+ assert_predicate sock, :closed?
+ ensure
+ sock&.close
+ end
+ end
+ end
+
def test_copy_stream
start_server do |port|
server_connect(port) do |ssl|
@@ -314,27 +397,27 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
empty_store = OpenSSL::X509::Store.new
# Valid certificate, SSL_VERIFY_PEER
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.cert_store = populated_store
assert_nothing_raised {
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
- ctx.cert_store = populated_store
server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
}
# Invalid certificate, SSL_VERIFY_NONE
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ ctx.cert_store = empty_store
assert_nothing_raised {
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
- ctx.cert_store = empty_store
server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
}
# Invalid certificate, SSL_VERIFY_PEER
- assert_handshake_error {
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
- ctx.cert_store = empty_store
- server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.cert_store = empty_store
+ assert_raise(OpenSSL::SSL::SSLError) {
+ server_connect(port, ctx)
}
}
end
@@ -362,10 +445,15 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_client_auth_success
vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
- start_server(verify_mode: vflag,
- ctx_proc: proc { |ctx|
- ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
- }) { |port|
+ ctx_proc = proc { |ctx|
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ ctx.cert_store = store
+ # LibreSSL doesn't support client_cert_cb in TLS 1.3
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?
+ }
+ start_server(verify_mode: vflag, ctx_proc: ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = @cli_key
ctx.cert = @cli_cert
@@ -407,9 +495,13 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_client_ca
- pend "LibreSSL 3.2 has broken client CA support" if libressl?(3, 2, 0)
+ pend "LibreSSL doesn't support certificate_authorities" if libressl?
ctx_proc = Proc.new do |ctx|
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ ctx.cert_store = store
ctx.client_ca = [@ca_cert]
end
@@ -475,7 +567,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl.sync_close = true
begin
assert_raise(OpenSSL::SSL::SSLError){ ssl.connect }
- assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result)
+ assert_equal(OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, ssl.verify_result)
ensure
ssl.close
end
@@ -579,12 +671,9 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
start_server(accept_proc: proc { |server|
server_finished = server.finished_message
server_peer_finished = server.peer_finished_message
- }, ctx_proc: proc { |ctx|
- ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
}) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
- ctx.max_version = :TLS1_2 if libressl?(3, 2, 0) && !libressl?(3, 3, 0)
server_connect(port, ctx) { |ssl|
ssl.puts "abc"; ssl.gets
@@ -612,8 +701,12 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_post_connect_check_with_anon_ciphers
+ # DH missing the q value on unknown named parameters is not FIPS-approved.
+ omit_on_fips
+ omit "AWS-LC does not support DHE ciphersuites" if aws_lc?
+
ctx_proc = -> ctx {
- ctx.ssl_version = :TLSv1_2
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
ctx.ciphers = "aNULL"
ctx.tmp_dh = Fixtures.pkey("dh-1")
ctx.security_level = 0
@@ -621,7 +714,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
start_server(ctx_proc: ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
- ctx.ssl_version = :TLSv1_2
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
ctx.ciphers = "aNULL"
ctx.security_level = 0
server_connect(port, ctx) { |ssl|
@@ -765,11 +858,6 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
# buzz.example.net, respectively). ...
assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com'))
-
- # LibreSSL 3.5.0+ doesn't support other wildcard certificates
- # (it isn't required to, as RFC states MAY, not MUST)
- return if libressl?(3, 5, 0)
-
assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com'))
assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
@@ -853,11 +941,17 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def create_cert_with_san(san)
- ef = OpenSSL::X509::ExtensionFactory.new
cert = OpenSSL::X509::Certificate.new
cert.subject = OpenSSL::X509::Name.parse("/DC=some/DC=site/CN=Some Site")
- ext = ef.create_ext('subjectAltName', san)
- cert.add_extension(ext)
+ v = OpenSSL::ASN1::Sequence(san.split(",").map { |item|
+ type, value = item.split(":", 2)
+ case type
+ when "DNS" then OpenSSL::ASN1::IA5String(value, 2, :IMPLICIT)
+ when "IP" then OpenSSL::ASN1::OctetString(IPAddr.new(value).hton, 7, :IMPLICIT)
+ else raise "unsupported"
+ end
+ })
+ cert.add_extension(OpenSSL::X509::Extension.new("subjectAltName", v))
cert
end
@@ -894,7 +988,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_keylog_cb
- pend "Keylog callback is not supported" if !openssl?(1, 1, 1) || libressl?
+ omit "Keylog callback is not supported" if libressl?
prefix = 'CLIENT_RANDOM'
context = OpenSSL::SSL::SSLContext.new
@@ -914,30 +1008,28 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
- if tls13_supported?
- prefixes = [
- 'SERVER_HANDSHAKE_TRAFFIC_SECRET',
- 'EXPORTER_SECRET',
- 'SERVER_TRAFFIC_SECRET_0',
- 'CLIENT_HANDSHAKE_TRAFFIC_SECRET',
- 'CLIENT_TRAFFIC_SECRET_0',
- ]
- context = OpenSSL::SSL::SSLContext.new
- context.min_version = context.max_version = OpenSSL::SSL::TLS1_3_VERSION
- cb_called = false
- context.keylog_cb = proc do |_sock, line|
- cb_called = true
- assert_not_nil(prefixes.delete(line.split.first))
- end
+ prefixes = [
+ 'SERVER_HANDSHAKE_TRAFFIC_SECRET',
+ 'EXPORTER_SECRET',
+ 'SERVER_TRAFFIC_SECRET_0',
+ 'CLIENT_HANDSHAKE_TRAFFIC_SECRET',
+ 'CLIENT_TRAFFIC_SECRET_0',
+ ]
+ context = OpenSSL::SSL::SSLContext.new
+ context.min_version = context.max_version = OpenSSL::SSL::TLS1_3_VERSION
+ cb_called = false
+ context.keylog_cb = proc do |_sock, line|
+ cb_called = true
+ assert_not_nil(prefixes.delete(line.split.first))
+ end
- start_server do |port|
- server_connect(port, context) do |ssl|
- ssl.puts "abc"
- assert_equal("abc\n", ssl.gets)
- assert_equal(true, cb_called)
- end
- assert_equal(0, prefixes.size)
+ start_server do |port|
+ server_connect(port, context) do |ssl|
+ ssl.puts "abc"
+ assert_equal("abc\n", ssl.gets)
+ assert_equal(true, cb_called)
end
+ assert_equal(0, prefixes.size)
end
end
@@ -988,36 +1080,46 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
- def test_servername_cb_raises_an_exception_on_unknown_objects
- hostname = 'example.org'
-
- ctx2 = OpenSSL::SSL::SSLContext.new
- ctx2.cert = @svr_cert
- ctx2.key = @svr_key
- ctx2.servername_cb = lambda { |args| Object.new }
-
+ def test_servername_cb_exception
sock1, sock2 = socketpair
+ t = Thread.new {
+ s1 = OpenSSL::SSL::SSLSocket.new(sock1)
+ s1.hostname = "localhost"
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /unrecognized.name/i) {
+ s1.connect
+ }
+ }
+
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.servername_cb = lambda { |args| raise RuntimeError, "foo" }
s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
+ assert_raise_with_message(RuntimeError, "foo") { s2.accept }
+ assert t.join
+ ensure
+ sock1.close
+ sock2.close
+ t.kill.join
+ end
- ctx1 = OpenSSL::SSL::SSLContext.new
+ def test_servername_cb_raises_an_exception_on_unknown_objects
+ sock1, sock2 = socketpair
- s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
- s1.hostname = hostname
t = Thread.new {
- assert_raise(OpenSSL::SSL::SSLError) do
- s1.connect
- end
+ s1 = OpenSSL::SSL::SSLSocket.new(sock1)
+ s1.hostname = "localhost"
+ assert_raise(OpenSSL::SSL::SSLError) { s1.connect }
}
- assert_raise(ArgumentError) do
- s2.accept
- end
-
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.servername_cb = lambda { |args| Object.new }
+ s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
+ assert_raise(ArgumentError) { s2.accept }
assert t.join
ensure
- sock1.close if sock1
- sock2.close if sock2
+ sock1.close
+ sock2.close
+ t.kill.join
end
def test_accept_errors_include_peeraddr
@@ -1047,13 +1149,11 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_verify_hostname_on_connect
ctx_proc = proc { |ctx|
- san = "DNS:a.example.com,DNS:*.b.example.com"
- san += ",DNS:c*.example.com,DNS:d.*.example.com" unless libressl?(3, 2, 2)
exts = [
["keyUsage", "keyEncipherment,digitalSignature", true],
- ["subjectAltName", san],
+ ["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \
+ "DNS:c*.example.com,DNS:d.*.example.com"],
]
-
ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key)
ctx.key = @svr_key
}
@@ -1075,7 +1175,6 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
["cx.example.com", true],
["d.x.example.com", false],
].each do |name, expected_ok|
- next if name.start_with?('cx') if libressl?(3, 2, 2)
begin
sock = TCPSocket.new("127.0.0.1", port)
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
@@ -1084,7 +1183,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl.connect
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
else
- assert_handshake_error { ssl.connect }
+ assert_raise(OpenSSL::SSL::SSLError) { ssl.connect }
end
ensure
ssl.close if ssl
@@ -1122,7 +1221,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
sock = TCPSocket.new("127.0.0.1", port)
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.hostname = "b.example.com"
- assert_handshake_error { ssl.connect }
+ assert_raise(OpenSSL::SSL::SSLError) { ssl.connect }
assert_equal false, verify_callback_ok
assert_equal OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH, verify_callback_err
ensure
@@ -1135,9 +1234,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
start_server(ignore_listener_error: true) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.set_params
- # OpenSSL <= 1.1.0: "self signed certificate in certificate chain"
- # OpenSSL >= 3.0.0: "self-signed certificate in certificate chain"
- assert_raise_with_message(OpenSSL::SSL::SSLError, /self.signed/) {
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /unable to get local issuer certificate/) {
server_connect(port, ctx)
}
}
@@ -1179,34 +1276,33 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
OpenSSL::SSL::TLS1_VERSION,
OpenSSL::SSL::TLS1_1_VERSION,
OpenSSL::SSL::TLS1_2_VERSION,
- # OpenSSL 1.1.1
- defined?(OpenSSL::SSL::TLS1_3_VERSION) && OpenSSL::SSL::TLS1_3_VERSION,
- ].compact
+ OpenSSL::SSL::TLS1_3_VERSION,
+ ]
- # Prepare for testing & do sanity check
supported = []
- possible_versions.each do |ver|
- catch(:unsupported) {
- ctx_proc = proc { |ctx|
- begin
- ctx.min_version = ctx.max_version = ver
- rescue ArgumentError, OpenSSL::SSL::SSLError
- throw :unsupported
- end
+ ctx_proc = proc { |ctx|
+ # The default security level is 1 in OpenSSL <= 3.1, 2 in OpenSSL >= 3.2
+ # In OpenSSL >= 3.0, TLS 1.1 or older is disabled at level 1
+ ctx.security_level = 0
+ # Explicitly reset them to avoid influenced by OPENSSL_CONF
+ ctx.min_version = ctx.max_version = nil
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+ possible_versions.each do |ver|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.security_level = 0
+ ctx.min_version = ctx.max_version = ver
+ server_connect(port, ctx) { |ssl|
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
- start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
- begin
- server_connect(port) { |ssl|
- ssl.puts "abc"; assert_equal "abc\n", ssl.gets
- }
- rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET
- else
- supported << ver
- end
- end
- }
+ supported << ver
+ rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET
+ end
end
- assert_not_empty supported
+
+ # Sanity check: in our test suite we assume these are always supported
+ assert_include(supported, OpenSSL::SSL::TLS1_2_VERSION)
+ assert_include(supported, OpenSSL::SSL::TLS1_3_VERSION)
supported
end
@@ -1224,7 +1320,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.set_params(cert_store: store, verify_hostname: false)
- assert_handshake_error { server_connect(port, ctx) { } }
+ assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx) }
}
end
end
@@ -1240,18 +1336,20 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
OpenSSL::SSL::TLS1_VERSION => { name: "TLSv1", method: "TLSv1" },
OpenSSL::SSL::TLS1_1_VERSION => { name: "TLSv1.1", method: "TLSv1_1" },
OpenSSL::SSL::TLS1_2_VERSION => { name: "TLSv1.2", method: "TLSv1_2" },
- # OpenSSL 1.1.1
- defined?(OpenSSL::SSL::TLS1_3_VERSION) && OpenSSL::SSL::TLS1_3_VERSION =>
- { name: "TLSv1.3", method: nil },
+ OpenSSL::SSL::TLS1_3_VERSION => { name: "TLSv1.3", method: nil },
}
# Server enables a single version
supported.each do |ver|
- ctx_proc = proc { |ctx| ctx.min_version = ctx.max_version = ver }
+ ctx_proc = proc { |ctx|
+ ctx.security_level = 0
+ ctx.min_version = ctx.max_version = ver
+ }
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
supported.each do |cver|
# Client enables a single version
ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.security_level = 0
ctx1.min_version = ctx1.max_version = cver
if ver == cver
server_connect(port, ctx1) { |ssl|
@@ -1259,13 +1357,14 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
else
- assert_handshake_error { server_connect(port, ctx1) { } }
+ assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx1) }
end
# There is no version-specific SSL methods for TLS 1.3
if cver <= OpenSSL::SSL::TLS1_2_VERSION
# Client enables a single version using #ssl_version=
ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.security_level = 0
ctx2.ssl_version = vmap[cver][:method]
if ver == cver
server_connect(port, ctx2) { |ssl|
@@ -1273,13 +1372,14 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
else
- assert_handshake_error { server_connect(port, ctx2) { } }
+ assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx2) }
end
end
end
# Client enables all supported versions
ctx3 = OpenSSL::SSL::SSLContext.new
+ ctx3.security_level = 0
ctx3.min_version = ctx3.max_version = nil
server_connect(port, ctx3) { |ssl|
assert_equal vmap[ver][:name], ssl.ssl_version
@@ -1294,12 +1394,17 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
# Server sets min_version (earliest is disabled)
sver = supported[1]
- ctx_proc = proc { |ctx| ctx.min_version = sver }
+ ctx_proc = proc { |ctx|
+ ctx.security_level = 0
+ ctx.min_version = sver
+ }
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
supported.each do |cver|
# Client sets min_version
ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.security_level = 0
ctx1.min_version = cver
+ ctx1.max_version = 0
server_connect(port, ctx1) { |ssl|
assert_equal vmap[supported.last][:name], ssl.ssl_version
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
@@ -1307,6 +1412,8 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
# Client sets max_version
ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.security_level = 0
+ ctx2.min_version = 0
ctx2.max_version = cver
if cver >= sver
server_connect(port, ctx2) { |ssl|
@@ -1314,14 +1421,18 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
else
- assert_handshake_error { server_connect(port, ctx2) { } }
+ assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx2) }
end
end
}
# Server sets max_version (latest is disabled)
sver = supported[-2]
- ctx_proc = proc { |ctx| ctx.max_version = sver }
+ ctx_proc = proc { |ctx|
+ ctx.security_level = 0
+ ctx.min_version = 0
+ ctx.max_version = sver
+ }
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
supported.each do |cver|
# Client sets min_version
@@ -1333,11 +1444,13 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
else
- assert_handshake_error { server_connect(port, ctx1) { } }
+ assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx1) }
end
# Client sets max_version
ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.security_level = 0
+ ctx2.min_version = 0
ctx2.max_version = cver
server_connect(port, ctx2) { |ssl|
if cver >= sver
@@ -1351,15 +1464,106 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
+ def test_minmax_version_system_default
+ omit "LibreSSL and AWS-LC do not support OPENSSL_CONF" if libressl? || aws_lc?
+
+ Tempfile.create("openssl.cnf") { |f|
+ f.puts(<<~EOF)
+ openssl_conf = default_conf
+ [default_conf]
+ ssl_conf = ssl_sect
+ [ssl_sect]
+ system_default = ssl_default_sect
+ [ssl_default_sect]
+ MaxProtocol = TLSv1.2
+ EOF
+ f.close
+
+ start_server(ignore_listener_error: true) do |port|
+ assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl", "-", port.to_s], <<~"end;")
+ sock = TCPSocket.new("127.0.0.1", ARGV[0].to_i)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ ssl.connect
+ assert_equal("TLSv1.2", ssl.ssl_version)
+ ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
+ ssl.close
+ end;
+
+ assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl", "-", port.to_s], <<~"end;")
+ sock = TCPSocket.new("127.0.0.1", ARGV[0].to_i)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
+ ctx.max_version = nil
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ ssl.connect
+ assert_equal("TLSv1.3", ssl.ssl_version)
+ ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
+ ssl.close
+ end;
+ end
+ }
+ end
+
+ def test_respect_system_default_min
+ omit "LibreSSL and AWS-LC do not support OPENSSL_CONF" if libressl? || aws_lc?
+
+ Tempfile.create("openssl.cnf") { |f|
+ f.puts(<<~EOF)
+ openssl_conf = default_conf
+ [default_conf]
+ ssl_conf = ssl_sect
+ [ssl_sect]
+ system_default = ssl_default_sect
+ [ssl_default_sect]
+ MinProtocol = TLSv1.3
+ EOF
+ f.close
+
+ ctx_proc = proc { |ctx|
+ ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+ assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl", "-", port.to_s], <<~"end;")
+ sock = TCPSocket.new("127.0.0.1", ARGV[0].to_i)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ assert_raise(OpenSSL::SSL::SSLError) do
+ ssl.connect
+ end
+ ssl.close
+ end;
+ end
+
+ ctx_proc = proc { |ctx|
+ ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+ assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl", "-", port.to_s], <<~"end;")
+ sock = TCPSocket.new("127.0.0.1", ARGV[0].to_i)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.sync_close = true
+ ssl.connect
+ assert_equal("TLSv1.3", ssl.ssl_version)
+ ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
+ ssl.close
+ end;
+ end
+ }
+ end
+
def test_options_disable_versions
# It's recommended to use SSLContext#{min,max}_version= instead in real
# applications. The purpose of this test case is to check that SSL options
# are properly propagated to OpenSSL library.
supported = check_supported_protocol_versions
- if !defined?(OpenSSL::SSL::TLS1_3_VERSION) ||
- !supported.include?(OpenSSL::SSL::TLS1_2_VERSION) ||
- !supported.include?(OpenSSL::SSL::TLS1_3_VERSION) ||
- !defined?(OpenSSL::SSL::OP_NO_TLSv1_3) # LibreSSL < 3.4
+ if !supported.include?(OpenSSL::SSL::TLS1_2_VERSION) ||
+ !supported.include?(OpenSSL::SSL::TLS1_3_VERSION)
pend "this test case requires both TLS 1.2 and TLS 1.3 to be supported " \
"and enabled by default"
end
@@ -1374,7 +1578,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
# Client only supports TLS 1.2
ctx1 = OpenSSL::SSL::SSLContext.new
ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION
- assert_handshake_error { server_connect(port, ctx1) { } }
+ assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx1) }
# Client only supports TLS 1.3
ctx2 = OpenSSL::SSL::SSLContext.new
@@ -1390,7 +1594,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
# Client doesn't support TLS 1.2
ctx1 = OpenSSL::SSL::SSLContext.new
ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_2
- assert_handshake_error { server_connect(port, ctx1) { } }
+ assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx1) }
# Client supports TLS 1.2 by default
ctx2 = OpenSSL::SSL::SSLContext.new
@@ -1414,7 +1618,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
num_handshakes = 0
renegotiation_cb = Proc.new { |ssl| num_handshakes += 1 }
ctx_proc = Proc.new { |ctx| ctx.renegotiation_cb = renegotiation_cb }
- start_server_version(:SSLv23, ctx_proc) { |port|
+ start_server(ctx_proc: ctx_proc) { |port|
server_connect(port) { |ssl|
assert_equal(1, num_handshakes)
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
@@ -1430,7 +1634,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
ctx.alpn_protocols = advertised
}
- start_server_version(:SSLv23, ctx_proc) { |port|
+ start_server(ctx_proc: ctx_proc) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.alpn_protocols = advertised
server_connect(port, ctx) { |ssl|
@@ -1472,9 +1676,10 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
advertised = ["http/1.1", "spdy/2"]
ctx_proc = proc { |ctx| ctx.npn_protocols = advertised }
- start_server_version(:TLSv1_2, ctx_proc) { |port|
+ start_server(ctx_proc: ctx_proc) { |port|
selector = lambda { |which|
ctx = OpenSSL::SSL::SSLContext.new
+ ctx.max_version = :TLS1_2
ctx.npn_select_cb = -> (protocols) { protocols.send(which) }
server_connect(port, ctx) { |ssl|
assert_equal(advertised.send(which), ssl.npn_protocol)
@@ -1494,9 +1699,10 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
yield "spdy/2"
end
ctx_proc = Proc.new { |ctx| ctx.npn_protocols = advertised }
- start_server_version(:TLSv1_2, ctx_proc) { |port|
+ start_server(ctx_proc: ctx_proc) { |port|
selector = lambda { |selected, which|
ctx = OpenSSL::SSL::SSLContext.new
+ ctx.max_version = :TLS1_2
ctx.npn_select_cb = -> (protocols) { protocols.to_a.send(which) }
server_connect(port, ctx) { |ssl|
assert_equal(selected, ssl.npn_protocol)
@@ -1511,8 +1717,9 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
- start_server_version(:TLSv1_2, ctx_proc) { |port|
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
ctx = OpenSSL::SSL::SSLContext.new
+ ctx.max_version = :TLS1_2
ctx.npn_select_cb = -> (protocols) { raise RuntimeError.new }
assert_raise(RuntimeError) { server_connect(port, ctx) }
}
@@ -1521,22 +1728,22 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_npn_advertised_protocol_too_long
return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
- ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["a" * 256] }
- start_server_version(:TLSv1_2, ctx_proc) { |port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.npn_select_cb = -> (protocols) { protocols.first }
- assert_handshake_error { server_connect(port, ctx) }
- }
+ ctx = OpenSSL::SSL::SSLContext.new
+ assert_raise(OpenSSL::SSL::SSLError) do
+ ctx.npn_protocols = ["a" * 256]
+ ctx.setup
+ end
end
def test_npn_selected_protocol_too_long
return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
- start_server_version(:TLSv1_2, ctx_proc) { |port|
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
ctx = OpenSSL::SSL::SSLContext.new
+ ctx.max_version = :TLS1_2
ctx.npn_select_cb = -> (protocols) { "a" * 256 }
- assert_handshake_error { server_connect(port, ctx) }
+ assert_raise(OpenSSL::SSL::SSLError) { server_connect(port, ctx) }
}
end
@@ -1568,14 +1775,17 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_get_ephemeral_key
+ # kRSA is not FIPS-approved.
+ omit_on_fips
+
# kRSA
ctx_proc1 = proc { |ctx|
- ctx.ssl_version = :TLSv1_2
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
ctx.ciphers = "kRSA"
}
start_server(ctx_proc: ctx_proc1, ignore_listener_error: true) do |port|
ctx = OpenSSL::SSL::SSLContext.new
- ctx.ssl_version = :TLSv1_2
+ ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
ctx.ciphers = "kRSA"
begin
server_connect(port, ctx) { |ssl| assert_nil ssl.tmp_key }
@@ -1586,30 +1796,27 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
# DHE
- # TODO: How to test this with TLS 1.3?
- ctx_proc2 = proc { |ctx|
- ctx.ssl_version = :TLSv1_2
- ctx.ciphers = "EDH"
- ctx.tmp_dh = Fixtures.pkey("dh-1")
- }
- start_server(ctx_proc: ctx_proc2) do |port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ssl_version = :TLSv1_2
- ctx.ciphers = "EDH"
- server_connect(port, ctx) { |ssl|
- assert_instance_of OpenSSL::PKey::DH, ssl.tmp_key
- }
+ # OpenSSL 3.0 added support for named FFDHE groups in TLS 1.3
+ # LibreSSL does not support named FFDHE groups currently
+ # AWS-LC does not support DHE ciphersuites
+ if openssl?(3, 0, 0)
+ start_server do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.groups = "ffdhe3072"
+ server_connect(port, ctx) { |ssl|
+ assert_instance_of OpenSSL::PKey::DH, ssl.tmp_key
+ assert_equal 3072, ssl.tmp_key.p.num_bits
+ ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+ }
+ end
end
# ECDHE
ctx_proc3 = proc { |ctx|
- ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
- ctx.ecdh_curves = "P-256"
+ ctx.groups = "P-256"
}
start_server(ctx_proc: ctx_proc3) do |port|
- ctx = OpenSSL::SSL::SSLContext.new
- ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
- server_connect(port, ctx) { |ssl|
+ server_connect(port) { |ssl|
assert_instance_of OpenSSL::PKey::EC, ssl.tmp_key
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
@@ -1618,11 +1825,11 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_fallback_scsv
supported = check_supported_protocol_versions
- return unless supported.include?(OpenSSL::SSL::TLS1_1_VERSION) &&
- supported.include?(OpenSSL::SSL::TLS1_2_VERSION)
+ unless supported.include?(OpenSSL::SSL::TLS1_1_VERSION)
+ omit "TLS 1.1 support is required to run this test case"
+ end
- pend "Fallback SCSV is not supported" unless \
- OpenSSL::SSL::SSLContext.method_defined?(:enable_fallback_scsv)
+ omit "Fallback SCSV is not supported" if libressl?
start_server do |port|
ctx = OpenSSL::SSL::SSLContext.new
@@ -1633,11 +1840,15 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
ctx_proc = proc { |ctx|
+ ctx.security_level = 0
+ ctx.min_version = 0
ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
}
start_server(ctx_proc: ctx_proc) do |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.enable_fallback_scsv
+ ctx.security_level = 0
+ ctx.min_version = 0
ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
# Here is OK too
# TLS1.2 not supported, fallback to TLS1.1 and signaling the fallback
@@ -1655,19 +1866,24 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
# Otherwise, this test fails when using openssl 1.1.1 (or later) that supports TLS1.3.
# TODO: We may need another test for TLS1.3 because it seems to have a different mechanism.
ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.security_level = 0
+ ctx1.min_version = 0
ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION
s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
ctx2 = OpenSSL::SSL::SSLContext.new
ctx2.enable_fallback_scsv
+ ctx2.security_level = 0
+ ctx2.min_version = 0
ctx2.max_version = OpenSSL::SSL::TLS1_1_VERSION
s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
+ # AWS-LC has slightly different error messages in all-caps.
t = Thread.new {
- assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) {
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback|INAPPROPRIATE_FALLBACK/) {
s2.connect
}
}
- assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) {
+ assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback|INAPPROPRIATE_FALLBACK/) {
s1.accept
}
t.join
@@ -1678,6 +1894,10 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_tmp_dh_callback
+ # DH missing the q value on unknown named parameters is not FIPS-approved.
+ omit_on_fips
+ omit "AWS-LC does not support DHE ciphersuites" if aws_lc?
+
dh = Fixtures.pkey("dh-1")
called = false
ctx_proc = -> ctx {
@@ -1689,7 +1909,9 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
}
start_server(ctx_proc: ctx_proc) do |port|
- server_connect(port) { |ssl|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.groups = "P-256" # Exclude RFC 7919 groups
+ server_connect(port, ctx) { |ssl|
assert called, "dh callback should be called"
assert_equal dh.to_der, ssl.tmp_key.to_der
}
@@ -1697,11 +1919,6 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_ciphersuites_method_tls_connection
- ssl_ctx = OpenSSL::SSL::SSLContext.new
- if !tls13_supported? || !ssl_ctx.respond_to?(:ciphersuites=)
- pend 'TLS 1.3 not supported'
- end
-
csuite = ['TLS_AES_128_GCM_SHA256', 'TLSv1.3', 128, 128]
inputs = [csuite[0], [csuite[0]], [csuite]]
@@ -1713,11 +1930,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
server_connect(port, cli_ctx) do |ssl|
assert_equal('TLSv1.3', ssl.ssl_version)
- if libressl?(3, 4, 0) && !libressl?(3, 5, 0)
- assert_equal("AEAD-AES128-GCM-SHA256", ssl.cipher[0])
- else
- assert_equal(csuite[0], ssl.cipher[0])
- end
+ assert_equal(csuite[0], ssl.cipher[0])
ssl.puts('abc'); assert_equal("abc\n", ssl.gets)
end
end
@@ -1726,26 +1939,21 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_ciphersuites_method_nil_argument
ssl_ctx = OpenSSL::SSL::SSLContext.new
- pend 'ciphersuites= method is missing' unless ssl_ctx.respond_to?(:ciphersuites=)
-
assert_nothing_raised { ssl_ctx.ciphersuites = nil }
end
def test_ciphersuites_method_frozen_object
ssl_ctx = OpenSSL::SSL::SSLContext.new
- pend 'ciphersuites= method is missing' unless ssl_ctx.respond_to?(:ciphersuites=)
-
ssl_ctx.freeze
assert_raise(FrozenError) { ssl_ctx.ciphersuites = 'TLS_AES_256_GCM_SHA384' }
end
def test_ciphersuites_method_bogus_csuite
ssl_ctx = OpenSSL::SSL::SSLContext.new
- pend 'ciphersuites= method is missing' unless ssl_ctx.respond_to?(:ciphersuites=)
-
+ # AWS-LC has slightly different error messages in all-caps.
assert_raise_with_message(
OpenSSL::SSL::SSLError,
- /SSL_CTX_set_ciphersuites: no cipher match/i
+ /SSL_CTX_set_ciphersuites: (no cipher match|NO_CIPHER_MATCH)/i
) { ssl_ctx.ciphersuites = 'BOGUS' }
end
@@ -1781,34 +1989,184 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_ciphers_method_bogus_csuite
- omit "Old #{OpenSSL::OPENSSL_LIBRARY_VERSION}" if
- year = OpenSSL::OPENSSL_LIBRARY_VERSION[/\A OpenSSL\s+[01]\..*\s\K\d+\z/x] and
- year.to_i <= 2018
-
ssl_ctx = OpenSSL::SSL::SSLContext.new
+ # AWS-LC has slightly different error messages in all-caps.
assert_raise_with_message(
OpenSSL::SSL::SSLError,
- /SSL_CTX_set_cipher_list: no cipher match/i
+ /SSL_CTX_set_cipher_list: (no cipher match|NO_CIPHER_MATCH)/i
) { ssl_ctx.ciphers = 'BOGUS' }
end
+ def test_sigalgs
+ omit "SSL_CTX_set1_sigalgs_list() not supported" if libressl?
+
+ svr_exts = [
+ ["keyUsage", "keyEncipherment,digitalSignature", true],
+ ["subjectAltName", "DNS:localhost", false],
+ ]
+ ecdsa_key = Fixtures.pkey("p256")
+ ecdsa_cert = issue_cert(@svr, ecdsa_key, 10, svr_exts, @ca_cert, @ca_key)
+
+ ctx_proc = -> ctx {
+ # Unset values set by start_server
+ ctx.cert = ctx.key = ctx.extra_chain_cert = nil
+ ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA
+ ctx.add_certificate(ecdsa_cert, ecdsa_key, [@ca_cert]) # ECDSA
+ }
+ start_server(ctx_proc: ctx_proc) do |port|
+ ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.sigalgs = "rsa_pss_rsae_sha256"
+ server_connect(port, ctx1) { |ssl|
+ assert_kind_of(OpenSSL::PKey::RSA, ssl.peer_cert.public_key)
+ ssl.puts("abc"); ssl.gets
+ }
+
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.sigalgs = "ed25519:ecdsa_secp256r1_sha256"
+ server_connect(port, ctx2) { |ssl|
+ assert_kind_of(OpenSSL::PKey::EC, ssl.peer_cert.public_key)
+ ssl.puts("abc"); ssl.gets
+ }
+ end
+
+ # Frozen
+ ssl_ctx = OpenSSL::SSL::SSLContext.new
+ ssl_ctx.freeze
+ assert_raise(FrozenError) { ssl_ctx.sigalgs = "ECDSA+SHA256:RSA+SHA256" }
+
+ # Bogus
+ ssl_ctx = OpenSSL::SSL::SSLContext.new
+ assert_raise(TypeError) { ssl_ctx.sigalgs = nil }
+ assert_raise(OpenSSL::SSL::SSLError) { ssl_ctx.sigalgs = "BOGUS" }
+ end
+
+ def test_client_sigalgs
+ omit "SSL_CTX_set1_client_sigalgs_list() not supported" if libressl? || aws_lc?
+
+ cli_exts = [
+ ["keyUsage", "keyEncipherment,digitalSignature", true],
+ ["subjectAltName", "DNS:localhost", false],
+ ]
+ ecdsa_key = Fixtures.pkey("p256")
+ ecdsa_cert = issue_cert(@cli, ecdsa_key, 10, cli_exts, @ca_cert, @ca_key)
+
+ ctx_proc = -> ctx {
+ store = OpenSSL::X509::Store.new
+ store.add_cert(@ca_cert)
+ store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
+ ctx.cert_store = store
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+ ctx.client_sigalgs = "ECDSA+SHA256"
+ }
+ start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+ ctx1 = OpenSSL::SSL::SSLContext.new
+ ctx1.add_certificate(@cli_cert, @cli_key) # RSA
+ assert_handshake_error {
+ server_connect(port, ctx1) { |ssl|
+ ssl.puts("abc"); ssl.gets
+ }
+ }
+
+ ctx2 = OpenSSL::SSL::SSLContext.new
+ ctx2.add_certificate(ecdsa_cert, ecdsa_key) # ECDSA
+ server_connect(port, ctx2) { |ssl|
+ ssl.puts("abc"); ssl.gets
+ }
+ end
+ end
+
+ def test_get_sigalg
+ # SSL_get0_signature_name() not supported
+ # SSL_get0_peer_signature_name() not supported
+ return unless openssl?(3, 5, 0)
+
+ server_proc = -> (ctx, ssl) {
+ assert_equal('rsa_pss_rsae_sha256', ssl.sigalg)
+ assert_nil(ssl.peer_sigalg)
+
+ readwrite_loop(ctx, ssl)
+ }
+ start_server(server_proc: server_proc) do |port|
+ cli_ctx = OpenSSL::SSL::SSLContext.new
+ server_connect(port, cli_ctx) do |ssl|
+ assert_nil(ssl.sigalg)
+ assert_equal('rsa_pss_rsae_sha256', ssl.peer_sigalg)
+ ssl.puts "abc"; ssl.gets
+ end
+ end
+ end
+
+ def test_pqc_sigalg
+ # PQC algorithm ML-DSA (FIPS 204) is supported on OpenSSL 3.5 or later.
+ return unless openssl?(3, 5, 0)
+
+ mldsa = Fixtures.pkey("mldsa65-1")
+ mldsa_ca_key = Fixtures.pkey("mldsa65-2")
+ mldsa_ca_cert = issue_cert(@ca, mldsa_ca_key, 1, @ca_exts, nil, nil,
+ digest: nil)
+ mldsa_cert = issue_cert(@svr, mldsa, 60, [], mldsa_ca_cert, mldsa_ca_key,
+ digest: nil)
+ rsa = Fixtures.pkey("rsa-1")
+ rsa_cert = issue_cert(@svr, rsa, 61, [], @ca_cert, @ca_key)
+ ctx_proc = -> ctx {
+ # Unset values set by start_server
+ ctx.cert = ctx.key = ctx.extra_chain_cert = nil
+ ctx.sigalgs = "rsa_pss_rsae_sha256:mldsa65"
+ ctx.add_certificate(mldsa_cert, mldsa)
+ ctx.add_certificate(rsa_cert, rsa)
+ }
+
+ server_proc = -> (ctx, ssl) {
+ assert_equal('mldsa65', ssl.sigalg)
+
+ readwrite_loop(ctx, ssl)
+ }
+ start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ # Set signature algorithm because while OpenSSL may use ML-DSA by
+ # default, the system OpenSSL configuration affects the used signature
+ # algorithm.
+ ctx.sigalgs = 'mldsa65'
+ server_connect(port, ctx) { |ssl|
+ assert_equal('mldsa65', ssl.peer_sigalg)
+ ssl.puts "abc"; ssl.gets
+ }
+ end
+
+ server_proc = -> (ctx, ssl) {
+ assert_equal('rsa_pss_rsae_sha256', ssl.sigalg)
+
+ readwrite_loop(ctx, ssl)
+ }
+ start_server(ctx_proc: ctx_proc, server_proc: server_proc) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.sigalgs = 'rsa_pss_rsae_sha256'
+ server_connect(port, ctx) { |ssl|
+ assert_equal('rsa_pss_rsae_sha256', ssl.peer_sigalg)
+ ssl.puts "abc"; ssl.gets
+ }
+ end
+ end
+
def test_connect_works_when_setting_dh_callback_to_nil
+ omit "AWS-LC does not support DHE ciphersuites" if aws_lc?
+
ctx_proc = -> ctx {
ctx.max_version = :TLS1_2
ctx.ciphers = "DH:!NULL" # use DH
ctx.tmp_dh_callback = nil
}
start_server(ctx_proc: ctx_proc) do |port|
- EnvUtil.suppress_warning { # uses default callback
- assert_nothing_raised {
- server_connect(port) { }
- }
- }
+ assert_nothing_raised { server_connect(port) { } }
end
end
def test_tmp_dh
+ # DH missing the q value on unknown named parameters is not FIPS-approved.
+ omit_on_fips
+ omit "AWS-LC does not support DHE ciphersuites" if aws_lc?
+
dh = Fixtures.pkey("dh-1")
ctx_proc = -> ctx {
ctx.max_version = :TLS1_2
@@ -1816,92 +2174,133 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
ctx.tmp_dh = dh
}
start_server(ctx_proc: ctx_proc) do |port|
- server_connect(port) { |ssl|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.groups = "P-256" # Exclude RFC 7919 groups
+ server_connect(port, ctx) { |ssl|
assert_equal dh.to_der, ssl.tmp_key.to_der
}
end
end
- def test_ecdh_curves_tls12
+ def test_set_groups_tls12
ctx_proc = -> ctx {
# Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3
ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
ctx.ciphers = "kEECDH"
- ctx.ecdh_curves = "P-384:P-521"
+ ctx.groups = "P-384:P-521"
}
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
# Test 1: Client=P-256:P-384, Server=P-384:P-521 --> P-384
ctx = OpenSSL::SSL::SSLContext.new
- ctx.ecdh_curves = "P-256:P-384"
+ ctx.groups = "P-256:P-384"
server_connect(port, ctx) { |ssl|
cs = ssl.cipher[0]
assert_match (/\AECDH/), cs
+ # SSL_get0_group_name() is supported on OpenSSL 3.2 or later.
+ assert_equal "secp384r1", ssl.group if openssl?(3, 2, 0)
assert_equal "secp384r1", ssl.tmp_key.group.curve_name
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
# Test 2: Client=P-256, Server=P-521:P-384 --> Fail
ctx = OpenSSL::SSL::SSLContext.new
- ctx.ecdh_curves = "P-256"
+ ctx.groups = "P-256"
assert_raise(OpenSSL::SSL::SSLError) {
server_connect(port, ctx) { }
}
# Test 3: Client=P-521:P-384, Server=P-521:P-384 --> P-521
ctx = OpenSSL::SSL::SSLContext.new
- ctx.ecdh_curves = "P-521:P-384"
+ ctx.groups = "P-521:P-384"
server_connect(port, ctx) { |ssl|
assert_equal "secp521r1", ssl.tmp_key.group.curve_name
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
+
+ # Test 4: #ecdh_curves= alias
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ecdh_curves = "P-256:P-384"
+ server_connect(port, ctx) { |ssl|
+ assert_equal "secp384r1", ssl.tmp_key.group.curve_name
+ }
end
end
- def test_ecdh_curves_tls13
- pend "TLS 1.3 not supported" unless tls13_supported?
-
+ def test_set_groups_tls13
ctx_proc = -> ctx {
# Assume TLS 1.3 is enabled and chosen by default
- ctx.ecdh_curves = "P-384:P-521"
+ ctx.groups = "P-384:P-521"
}
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
ctx = OpenSSL::SSL::SSLContext.new
- ctx.ecdh_curves = "P-256:P-384" # disable P-521
+ ctx.groups = "P-256:P-384" # disable P-521
server_connect(port, ctx) { |ssl|
assert_equal "TLSv1.3", ssl.ssl_version
+ # SSL_get0_group_name() is supported on OpenSSL 3.2 or later.
+ assert_equal "secp384r1", ssl.group if openssl?(3, 2, 0)
assert_equal "secp384r1", ssl.tmp_key.group.curve_name
ssl.puts "abc"; assert_equal "abc\n", ssl.gets
}
end
end
+ def test_pqc_group
+ # PQC algorithm ML-KEM (FIPS 203) is supported on OpenSSL 3.5 or later.
+ return unless openssl?(3, 5, 0)
+
+ [
+ 'X25519MLKEM768',
+ 'SecP256r1MLKEM768',
+ 'SecP384r1MLKEM1024'
+ ].each do |group|
+ ctx_proc = -> ctx {
+ ctx.groups = group
+ }
+ start_server(ctx_proc: ctx_proc) do |port|
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.groups = group
+ server_connect(port, ctx) { |ssl|
+ assert_equal(group, ssl.group)
+ ssl.puts "abc"; ssl.gets
+ }
+ end
+ end
+ end
+
def test_security_level
ctx = OpenSSL::SSL::SSLContext.new
- begin
- ctx.security_level = 1
- rescue NotImplementedError
+ ctx.security_level = 1
+ if aws_lc? # AWS-LC does not support security levels.
assert_equal(0, ctx.security_level)
return
end
assert_equal(1, ctx.security_level)
- dsa512 = Fixtures.pkey("dsa512")
- dsa512_cert = issue_cert(@svr, dsa512, 50, [], @ca_cert, @ca_key)
- rsa1024 = Fixtures.pkey("rsa1024")
- rsa1024_cert = issue_cert(@svr, rsa1024, 51, [], @ca_cert, @ca_key)
+ # See SSL_CTX_set_security_level(3). Definitions of security levels may
+ # change in future OpenSSL versions. As of OpenSSL 1.1.0:
+ # - Level 1 requires 160-bit ECC keys or 1024-bit RSA keys.
+ # - Level 2 requires 224-bit ECC keys or 2048-bit RSA keys.
+ begin
+ ec112 = OpenSSL::PKey::EC.generate("secp112r1")
+ ec112_cert = issue_cert(@svr, ec112, 50, [], @ca_cert, @ca_key)
+ ec192 = OpenSSL::PKey::EC.generate("prime192v1")
+ ec192_cert = issue_cert(@svr, ec192, 51, [], @ca_cert, @ca_key)
+ rescue OpenSSL::PKey::PKeyError
+ # Distro-provided OpenSSL may refuse to generate small keys
+ return
+ end
assert_raise(OpenSSL::SSL::SSLError) {
- # 512 bit DSA key is rejected because it offers < 80 bits of security
- ctx.add_certificate(dsa512_cert, dsa512)
+ ctx.add_certificate(ec112_cert, ec112)
}
assert_nothing_raised {
- ctx.add_certificate(rsa1024_cert, rsa1024)
+ ctx.add_certificate(ec192_cert, ec192)
}
ctx.security_level = 2
assert_raise(OpenSSL::SSL::SSLError) {
# < 112 bits of security
- ctx.add_certificate(rsa1024_cert, rsa1024)
+ ctx.add_certificate(ec192_cert, ec192)
}
end
@@ -1957,22 +2356,52 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
end
- private
+ # OpenSSL::Buffering requires $/ accessible from non-main Ractors (Ruby 4.0)
+ # https://bugs.ruby-lang.org/issues/21109
+ #
+ # Hangs on Windows
+ # https://bugs.ruby-lang.org/issues/21537
+ if respond_to?(:ractor) && RUBY_VERSION >= "4.0" && RUBY_PLATFORM !~ /mswin|mingw/
+ ractor
+ def test_ractor_client
+ start_server { |port|
+ s = Ractor.new(port, @ca_cert) { |port, ca_cert|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.cert_store = OpenSSL::X509::Store.new.tap { |store|
+ store.add_cert(ca_cert)
+ }
+ begin
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.connect
+ ssl.puts("abc")
+ ssl.gets
+ ensure
+ ssl.close
+ sock.close
+ end
+ }.value
+ assert_equal("abc\n", s)
+ }
+ end
- def start_server_version(version, ctx_proc = nil,
- server_proc = method(:readwrite_loop), &blk)
- ctx_wrap = Proc.new { |ctx|
- ctx.ssl_version = version
- ctx_proc.call(ctx) if ctx_proc
- }
- start_server(
- ctx_proc: ctx_wrap,
- server_proc: server_proc,
- ignore_listener_error: true,
- &blk
- )
+ ractor
+ def test_ractor_set_params
+ # We cannot actually test default stores in the test suite as it depends
+ # on the environment, but at least check that it does not raise an
+ # exception
+ ok = Ractor.new {
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.set_params
+ ctx.cert_store.kind_of?(OpenSSL::X509::Store)
+ }.value
+ assert(ok, "ctx.cert_store is an instance of OpenSSL::X509::Store")
+ end
end
+ private
+
def server_connect(port, ctx = nil)
sock = TCPSocket.new("127.0.0.1", port)
ssl = ctx ? OpenSSL::SSL::SSLSocket.new(sock, ctx) : OpenSSL::SSL::SSLSocket.new(sock)