Skip to content

Commit 328d0dc

Browse files
authored
Merge pull request #895 from rhenium/ky/ssl-sigalgs
ssl: add SSLContext#sigalgs= and #client_sigalgs=
2 parents 6b38b0f + 6bbe58c commit 328d0dc

File tree

3 files changed

+158
-7
lines changed

3 files changed

+158
-7
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: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -999,11 +999,10 @@ static VALUE
999999
build_cipher_string(VALUE v)
10001000
{
10011001
VALUE str, elem;
1002-
int i;
10031002

10041003
if (RB_TYPE_P(v, T_ARRAY)) {
10051004
str = rb_str_new(0, 0);
1006-
for (i = 0; i < RARRAY_LEN(v); i++) {
1005+
for (long i = 0; i < RARRAY_LEN(v); i++) {
10071006
elem = rb_ary_entry(v, i);
10081007
if (RB_TYPE_P(elem, T_ARRAY)) elem = rb_ary_entry(elem, 0);
10091008
elem = rb_String(elem);
@@ -1024,9 +1023,14 @@ build_cipher_string(VALUE v)
10241023
* ctx.ciphers = [name, ...]
10251024
* ctx.ciphers = [[name, version, bits, alg_bits], ...]
10261025
*
1027-
* Sets the list of available cipher suites for this context. Note in a server
1028-
* context some ciphers require the appropriate certificates. For example, an
1029-
* RSA cipher suite can only be chosen when an RSA certificate is available.
1026+
* Sets the list of available cipher suites for TLS 1.2 and below for this
1027+
* context.
1028+
*
1029+
* Note in a server context some ciphers require the appropriate certificates.
1030+
* For example, an RSA cipher suite can only be chosen when an RSA certificate
1031+
* is available.
1032+
*
1033+
* This method does not affect TLS 1.3 connections. See also #ciphersuites=.
10301034
*/
10311035
static VALUE
10321036
ossl_sslctx_set_ciphers(VALUE self, VALUE v)
@@ -1035,6 +1039,7 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
10351039
VALUE str;
10361040

10371041
rb_check_frozen(self);
1042+
// Assigning nil is a no-op for compatibility
10381043
if (NIL_P(v))
10391044
return v;
10401045

@@ -1051,9 +1056,8 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
10511056
* call-seq:
10521057
* ctx.ciphersuites = "cipher1:cipher2:..."
10531058
* ctx.ciphersuites = [name, ...]
1054-
* ctx.ciphersuites = [[name, version, bits, alg_bits], ...]
10551059
*
1056-
* Sets the list of available TLSv1.3 cipher suites for this context.
1060+
* Sets the list of available TLS 1.3 cipher suites for this context.
10571061
*/
10581062
static VALUE
10591063
ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
@@ -1062,6 +1066,7 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
10621066
VALUE str;
10631067

10641068
rb_check_frozen(self);
1069+
// Assigning nil is a no-op for compatibility
10651070
if (NIL_P(v))
10661071
return v;
10671072

@@ -1074,6 +1079,63 @@ ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
10741079
return v;
10751080
}
10761081

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+
10771139
#ifndef OPENSSL_NO_DH
10781140
/*
10791141
* call-seq:
@@ -2887,6 +2949,12 @@ Init_ossl_ssl(void)
28872949
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
28882950
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
28892951
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
28902958
#ifndef OPENSSL_NO_DH
28912959
rb_define_method(cSSLContext, "tmp_dh=", ossl_sslctx_set_tmp_dh, 1);
28922960
#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)