Skip to content

Commit d54774e

Browse files
rheniummj-vivavis
andcommitted
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. Co-authored-by: Markus Jung <[email protected]>
1 parent 7cf0acc commit d54774e

File tree

3 files changed

+148
-1
lines changed

3 files changed

+148
-1
lines changed

ext/openssl/extconf.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ def find_openssl_library
135135
# compile options
136136
have_func("RAND_egd()", "openssl/rand.h")
137137

138+
# added in OpenSSL 1.0.2, not in LibreSSL yet
139+
have_func("SSL_CTX_set1_sigalgs_list(NULL, NULL)", ssl_h)
140+
# added in OpenSSL 1.0.2, not in LibreSSL or AWS-LC yet
141+
have_func("SSL_CTX_set1_client_sigalgs_list(NULL, NULL)", ssl_h)
142+
138143
# added in 1.1.0, currently not in LibreSSL
139144
have_func("EVP_PBE_scrypt(\"\", 0, (unsigned char *)\"\", 0, 0, 0, 0, 0, NULL, 0)", evp_h)
140145

ext/openssl/ossl_ssl.c

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,7 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
10391039
VALUE str;
10401040

10411041
rb_check_frozen(self);
1042+
// Assigning nil is a no-op for compatibility
10421043
if (NIL_P(v))
10431044
return v;
10441045

@@ -1065,7 +1066,7 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
10651066
VALUE str;
10661067

10671068
rb_check_frozen(self);
1068-
// This does not make sense, but it is allowed for compatibility
1069+
// Assigning nil is a no-op for compatibility
10691070
if (NIL_P(v))
10701071
return v;
10711072

@@ -1078,6 +1079,63 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
10781079
return v;
10791080
}
10801081

1082+
#ifdef HAVE_SSL_CTX_SET1_SIGALGS_LIST
1083+
/*
1084+
* call-seq:
1085+
* ctx.sigalgs = "sigalg1:sigalg2:..."
1086+
*
1087+
* Sets the list of "supported signature algorithms" for this context.
1088+
*
1089+
* For a TLS client, the list is used in the "signature_algorithms" extension
1090+
* in the ClientHello message. For a server, the list is used by OpenSSL to
1091+
* determine the set of shared signature algorithms. OpenSSL will pick the most
1092+
* appropriate one from it.
1093+
*
1094+
* See also #client_sigalgs= for the client authentication equivalent.
1095+
*/
1096+
static VALUE
1097+
ossl_sslctx_set_sigalgs(VALUE self, VALUE v)
1098+
{
1099+
SSL_CTX *ctx;
1100+
1101+
rb_check_frozen(self);
1102+
GetSSLCTX(self, ctx);
1103+
1104+
if (!SSL_CTX_set1_sigalgs_list(ctx, StringValueCStr(v)))
1105+
ossl_raise(eSSLError, "SSL_CTX_set1_sigalgs_list");
1106+
1107+
return v;
1108+
}
1109+
#endif
1110+
1111+
#ifdef HAVE_SSL_CTX_SET1_CLIENT_SIGALGS_LIST
1112+
/*
1113+
* call-seq:
1114+
* ctx.client_sigalgs = "sigalg1:sigalg2:..."
1115+
*
1116+
* Sets the list of "supported signature algorithms" for client authentication
1117+
* for this context.
1118+
*
1119+
* For a TLS server, the list is sent to the client as part of the
1120+
* CertificateRequest message.
1121+
*
1122+
* See also #sigalgs= for the server authentication equivalent.
1123+
*/
1124+
static VALUE
1125+
ossl_sslctx_set_client_sigalgs(VALUE self, VALUE v)
1126+
{
1127+
SSL_CTX *ctx;
1128+
1129+
rb_check_frozen(self);
1130+
GetSSLCTX(self, ctx);
1131+
1132+
if (!SSL_CTX_set1_client_sigalgs_list(ctx, StringValueCStr(v)))
1133+
ossl_raise(eSSLError, "SSL_CTX_set1_client_sigalgs_list");
1134+
1135+
return v;
1136+
}
1137+
#endif
1138+
10811139
#ifndef OPENSSL_NO_DH
10821140
/*
10831141
* call-seq:
@@ -2891,6 +2949,12 @@ Init_ossl_ssl(void)
28912949
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
28922950
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
28932951
rb_define_method(cSSLContext, "ciphersuites=", ossl_sslctx_set_ciphersuites, 1);
2952+
#ifdef HAVE_SSL_CTX_SET1_SIGALGS_LIST // Not in LibreSSL yet
2953+
rb_define_method(cSSLContext, "sigalgs=", ossl_sslctx_set_sigalgs, 1);
2954+
#endif
2955+
#ifdef HAVE_SSL_CTX_SET1_CLIENT_SIGALGS_LIST // Not in LibreSSL or AWS-LC yet
2956+
rb_define_method(cSSLContext, "client_sigalgs=", ossl_sslctx_set_client_sigalgs, 1);
2957+
#endif
28942958
#ifndef OPENSSL_NO_DH
28952959
rb_define_method(cSSLContext, "tmp_dh=", ossl_sslctx_set_tmp_dh, 1);
28962960
#endif

test/openssl/test_ssl.rb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,6 +1968,84 @@ def test_ciphers_method_bogus_csuite
19681968
) { ssl_ctx.ciphers = 'BOGUS' }
19691969
end
19701970

1971+
def test_sigalgs
1972+
omit "SSL_CTX_set1_sigalgs_list() not supported" if libressl?
1973+
1974+
svr_exts = [
1975+
["keyUsage", "keyEncipherment,digitalSignature", true],
1976+
["subjectAltName", "DNS:localhost", false],
1977+
]
1978+
ecdsa_key = Fixtures.pkey("p256")
1979+
ecdsa_cert = issue_cert(@svr, ecdsa_key, 10, svr_exts, @ca_cert, @ca_key)
1980+
1981+
ctx_proc = -> ctx {
1982+
# Unset values set by start_server
1983+
ctx.cert = ctx.key = ctx.extra_chain_cert = nil
1984+
ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA
1985+
ctx.add_certificate(ecdsa_cert, ecdsa_key, [@ca_cert]) # ECDSA
1986+
}
1987+
start_server(ctx_proc: ctx_proc) do |port|
1988+
ctx1 = OpenSSL::SSL::SSLContext.new
1989+
ctx1.sigalgs = "rsa_pss_rsae_sha256"
1990+
server_connect(port, ctx1) { |ssl|
1991+
assert_kind_of(OpenSSL::PKey::RSA, ssl.peer_cert.public_key)
1992+
ssl.puts("abc"); ssl.gets
1993+
}
1994+
1995+
ctx2 = OpenSSL::SSL::SSLContext.new
1996+
ctx2.sigalgs = "ed25519:ecdsa_secp256r1_sha256"
1997+
server_connect(port, ctx2) { |ssl|
1998+
assert_kind_of(OpenSSL::PKey::EC, ssl.peer_cert.public_key)
1999+
ssl.puts("abc"); ssl.gets
2000+
}
2001+
end
2002+
2003+
# Frozen
2004+
ssl_ctx = OpenSSL::SSL::SSLContext.new
2005+
ssl_ctx.freeze
2006+
assert_raise(FrozenError) { ssl_ctx.sigalgs = "ECDSA+SHA256:RSA+SHA256" }
2007+
2008+
# Bogus
2009+
ssl_ctx = OpenSSL::SSL::SSLContext.new
2010+
assert_raise(TypeError) { ssl_ctx.sigalgs = nil }
2011+
assert_raise(OpenSSL::SSL::SSLError) { ssl_ctx.sigalgs = "BOGUS" }
2012+
end
2013+
2014+
def test_client_sigalgs
2015+
omit "SSL_CTX_set1_client_sigalgs_list() not supported" if libressl? || aws_lc?
2016+
2017+
cli_exts = [
2018+
["keyUsage", "keyEncipherment,digitalSignature", true],
2019+
["subjectAltName", "DNS:localhost", false],
2020+
]
2021+
ecdsa_key = Fixtures.pkey("p256")
2022+
ecdsa_cert = issue_cert(@cli, ecdsa_key, 10, cli_exts, @ca_cert, @ca_key)
2023+
2024+
ctx_proc = -> ctx {
2025+
store = OpenSSL::X509::Store.new
2026+
store.add_cert(@ca_cert)
2027+
store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
2028+
ctx.cert_store = store
2029+
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
2030+
ctx.client_sigalgs = "ECDSA+SHA256"
2031+
}
2032+
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
2033+
ctx1 = OpenSSL::SSL::SSLContext.new
2034+
ctx1.add_certificate(@cli_cert, @cli_key) # RSA
2035+
assert_handshake_error {
2036+
server_connect(port, ctx1) { |ssl|
2037+
ssl.puts("abc"); ssl.gets
2038+
}
2039+
}
2040+
2041+
ctx2 = OpenSSL::SSL::SSLContext.new
2042+
ctx2.add_certificate(ecdsa_cert, ecdsa_key) # ECDSA
2043+
server_connect(port, ctx2) { |ssl|
2044+
ssl.puts("abc"); ssl.gets
2045+
}
2046+
end
2047+
end
2048+
19712049
def test_connect_works_when_setting_dh_callback_to_nil
19722050
omit "AWS-LC does not support DHE ciphersuites" if aws_lc?
19732051

0 commit comments

Comments
 (0)