Skip to content

ssl: add SSLContext#sigalgs= and #client_sigalgs= #895

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ext/openssl/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ def find_openssl_library
# 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)

Expand Down
78 changes: 73 additions & 5 deletions ext/openssl/ossl_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1024,9 +1024,14 @@ build_cipher_string(VALUE v)
* ctx.ciphers = [name, ...]
* ctx.ciphers = [[name, version, bits, alg_bits], ...]
*
* Sets the list of available cipher suites for this context. Note in a server
* context some ciphers require the appropriate certificates. For example, an
* RSA cipher suite can only be chosen when an RSA certificate is available.
* Sets the list of available cipher suites for TLS 1.2 and below for this
* context.
*
* Note in a server context some ciphers require the appropriate certificates.
* For example, an RSA cipher suite can only be chosen when an RSA certificate
* is available.
*
* This method does not affect TLS 1.3 connections. See also #ciphersuites=.
*/
static VALUE
ossl_sslctx_set_ciphers(VALUE self, VALUE v)
Expand All @@ -1051,9 +1056,8 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
* call-seq:
* ctx.ciphersuites = "cipher1:cipher2:..."
* ctx.ciphersuites = [name, ...]
* ctx.ciphersuites = [[name, version, bits, alg_bits], ...]
*
* Sets the list of available TLSv1.3 cipher suites for this context.
* Sets the list of available TLS 1.3 cipher suites for this context.
*/
static VALUE
ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
Expand All @@ -1062,6 +1066,7 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
VALUE str;

rb_check_frozen(self);
// This does not make sense, but it is allowed for compatibility
if (NIL_P(v))
return v;

Expand All @@ -1074,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 messsage.
* 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 used in the "signature_algorithms" extension
* in 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:
Expand Down Expand Up @@ -2887,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
Expand Down
78 changes: 78 additions & 0 deletions test/openssl/test_ssl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1968,6 +1968,84 @@ def test_ciphers_method_bogus_csuite
) { 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?

Expand Down