summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKazuki Yamaguchi <[email protected]>2025-06-03 15:00:43 +0900
committergit <[email protected]>2025-06-20 17:58:38 +0000
commit112ba7064718aa8e486ff1d953946ad5a5480f8c (patch)
tree9637cfe8cbfe9489bd62b205961122fe23a75cd7
parent0d75dd1f47bd93427ecd29c13ce0729d92f8a858 (diff)
[ruby/openssl] ssl: add SSLContext#sigalgs= and #client_sigalgs=
Add methods for setting supported signature algorithms, corresponding to SSL_CTX_set1_sigalgs_list() and SSL_CTX_set1_client_sigalgs_list(), respectively. https://github.com/ruby/openssl/commit/6bbe58c492 Co-authored-by: Markus Jung <[email protected]>
-rw-r--r--ext/openssl/extconf.rb5
-rw-r--r--ext/openssl/ossl_ssl.c63
-rw-r--r--test/openssl/test_ssl.rb78
3 files changed, 146 insertions, 0 deletions
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index 5bb045e895..6eb401cf55 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -135,6 +135,11 @@ ssl_h = "openssl/ssl.h".freeze
# compile options
have_func("RAND_egd()", "openssl/rand.h")
+# added in OpenSSL 1.0.2, not in LibreSSL yet
+have_func("SSL_CTX_set1_sigalgs_list(NULL, NULL)", ssl_h)
+# added in OpenSSL 1.0.2, not in LibreSSL or AWS-LC yet
+have_func("SSL_CTX_set1_client_sigalgs_list(NULL, NULL)", ssl_h)
+
# added in 1.1.0, currently not in LibreSSL
have_func("EVP_PBE_scrypt(\"\", 0, (unsigned char *)\"\", 0, 0, 0, 0, 0, NULL, 0)", evp_h)
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 4b197fbf19..30fbb3bbd1 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -1079,6 +1079,63 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
return v;
}
+#ifdef HAVE_SSL_CTX_SET1_SIGALGS_LIST
+/*
+ * call-seq:
+ * ctx.sigalgs = "sigalg1:sigalg2:..."
+ *
+ * Sets the list of "supported signature algorithms" for this context.
+ *
+ * For a TLS client, the list is used in the "signature_algorithms" extension
+ * in the ClientHello message. For a server, the list is used by OpenSSL to
+ * determine the set of shared signature algorithms. OpenSSL will pick the most
+ * appropriate one from it.
+ *
+ * See also #client_sigalgs= for the client authentication equivalent.
+ */
+static VALUE
+ossl_sslctx_set_sigalgs(VALUE self, VALUE v)
+{
+ SSL_CTX *ctx;
+
+ rb_check_frozen(self);
+ GetSSLCTX(self, ctx);
+
+ if (!SSL_CTX_set1_sigalgs_list(ctx, StringValueCStr(v)))
+ ossl_raise(eSSLError, "SSL_CTX_set1_sigalgs_list");
+
+ return v;
+}
+#endif
+
+#ifdef HAVE_SSL_CTX_SET1_CLIENT_SIGALGS_LIST
+/*
+ * call-seq:
+ * ctx.client_sigalgs = "sigalg1:sigalg2:..."
+ *
+ * Sets the list of "supported signature algorithms" for client authentication
+ * for this context.
+ *
+ * For a TLS server, the list is sent to the client as part of the
+ * CertificateRequest message.
+ *
+ * See also #sigalgs= for the server authentication equivalent.
+ */
+static VALUE
+ossl_sslctx_set_client_sigalgs(VALUE self, VALUE v)
+{
+ SSL_CTX *ctx;
+
+ rb_check_frozen(self);
+ GetSSLCTX(self, ctx);
+
+ if (!SSL_CTX_set1_client_sigalgs_list(ctx, StringValueCStr(v)))
+ ossl_raise(eSSLError, "SSL_CTX_set1_client_sigalgs_list");
+
+ return v;
+}
+#endif
+
#ifndef OPENSSL_NO_DH
/*
* call-seq:
@@ -2892,6 +2949,12 @@ Init_ossl_ssl(void)
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
rb_define_method(cSSLContext, "ciphersuites=", ossl_sslctx_set_ciphersuites, 1);
+#ifdef HAVE_SSL_CTX_SET1_SIGALGS_LIST // Not in LibreSSL yet
+ rb_define_method(cSSLContext, "sigalgs=", ossl_sslctx_set_sigalgs, 1);
+#endif
+#ifdef HAVE_SSL_CTX_SET1_CLIENT_SIGALGS_LIST // Not in LibreSSL or AWS-LC yet
+ rb_define_method(cSSLContext, "client_sigalgs=", ossl_sslctx_set_client_sigalgs, 1);
+#endif
#ifndef OPENSSL_NO_DH
rb_define_method(cSSLContext, "tmp_dh=", ossl_sslctx_set_tmp_dh, 1);
#endif
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 4642063f45..61c26b5dd5 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -1968,6 +1968,84 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
) { 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_connect_works_when_setting_dh_callback_to_nil
omit "AWS-LC does not support DHE ciphersuites" if aws_lc?