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.rb762
1 files changed, 593 insertions, 169 deletions
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 8a94ec9924..e4fd581079 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -39,7 +39,7 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_ctx_options_config
- omit "LibreSSL does not support OPENSSL_CONF" if libressl?
+ omit "LibreSSL and AWS-LC do not support OPENSSL_CONF" if libressl? || aws_lc?
Tempfile.create("openssl.cnf") { |f|
f.puts(<<~EOF)
@@ -230,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|
@@ -242,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
@@ -322,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|
@@ -348,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
@@ -396,11 +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|
- # LibreSSL doesn't support client_cert_cb in TLS 1.3
- ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?
- }) { |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
@@ -445,6 +498,10 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
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
@@ -510,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
@@ -644,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
@@ -653,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|
@@ -797,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?
-
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(
@@ -885,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
@@ -1018,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
@@ -1111,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
@@ -1149,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
@@ -1162,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)
}
}
@@ -1207,32 +1277,32 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
OpenSSL::SSL::TLS1_1_VERSION,
OpenSSL::SSL::TLS1_2_VERSION,
OpenSSL::SSL::TLS1_3_VERSION,
- ].compact
+ ]
- # 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
@@ -1250,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
@@ -1271,11 +1341,15 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
# 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|
@@ -1283,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|
@@ -1297,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
@@ -1318,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
@@ -1331,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|
@@ -1338,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
@@ -1357,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
@@ -1375,6 +1464,99 @@ 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
@@ -1396,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
@@ -1412,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
@@ -1436,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
@@ -1452,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|
@@ -1494,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)
@@ -1516,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)
@@ -1533,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) }
}
@@ -1543,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
@@ -1590,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 }
@@ -1608,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
}
@@ -1640,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
@@ -1655,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
@@ -1677,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
@@ -1700,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 {
@@ -1711,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
}
@@ -1750,9 +1950,10 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
def test_ciphersuites_method_bogus_csuite
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_ciphersuites: no cipher match/i
+ /SSL_CTX_set_ciphersuites: (no cipher match|NO_CIPHER_MATCH)/i
) { ssl_ctx.ciphersuites = 'BOGUS' }
end
@@ -1788,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
@@ -1823,90 +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
+ 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
@@ -1962,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)