Skip to content

Commit aa43da4

Browse files
committed
ssl: add SSLContext#tmp_dh=
Provide a wrapper of SSL_set0_tmp_dh_pkey()/SSL_CTX_set_tmp_dh(), which sets the DH parameters used for ephemeral DH key exchange. SSLContext#tmp_dh_callback= already exists for this purpose, as a wrapper around SSL_CTX_set_tmp_dh_callback(), but it is considered obsolete and the OpenSSL API is deprecated for future removal. There is no practical use case where an application needs to use different DH parameters nowadays. This was originally introduced to support export grade ciphers. RDoc for #tmp_dh_callback= is updated to recommend the new #tmp_dh=. Note that current versions of OpenSSL support automatic ECDHE curve selection which is enabled by default. SSLContext#tmp_dh= should only be necessary if you must allow ancient clients which don't support ECDHE.
1 parent bef9ea8 commit aa43da4

File tree

4 files changed

+74
-10
lines changed

4 files changed

+74
-10
lines changed

ext/openssl/extconf.rb

+3
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ def find_openssl_library
170170
# added in 1.1.1
171171
have_func("EVP_PKEY_check")
172172

173+
# added in 3.0.0
174+
have_func("SSL_set0_tmp_dh_pkey")
175+
173176
Logging::message "=== Checking done. ===\n"
174177

175178
create_header

ext/openssl/ossl_ssl.c

+49
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,52 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
987987
return v;
988988
}
989989

990+
#ifndef OPENSSL_NO_DH
991+
/*
992+
* call-seq:
993+
* ctx.tmp_dh = pkey
994+
*
995+
* Sets DH parameters used for ephemeral DH key exchange. This is relevant for
996+
* servers only.
997+
*
998+
* +pkey+ is an instance of OpenSSL::PKey::DH. Note that key components
999+
* contained in the key object, if any, are ignored. The server will always
1000+
* generate a new key pair for each handshake.
1001+
*
1002+
* Added in version 3.0. See also the man page SSL_set0_tmp_dh_pkey(3).
1003+
*
1004+
* Example:
1005+
* ctx = OpenSSL::SSL::SSLContext.new
1006+
* ctx.tmp_dh = OpenSSL::DH.generate(2048)
1007+
* svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx)
1008+
* Thread.new { svr.accept }
1009+
*/
1010+
static VALUE
1011+
ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg)
1012+
{
1013+
SSL_CTX *ctx;
1014+
EVP_PKEY *pkey;
1015+
1016+
rb_check_frozen(self);
1017+
GetSSLCTX(self, ctx);
1018+
pkey = GetPKeyPtr(arg);
1019+
1020+
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH)
1021+
rb_raise(eSSLError, "invalid pkey type %s (expected DH)",
1022+
OBJ_nid2sn(EVP_PKEY_base_id(pkey)));
1023+
#ifdef HAVE_SSL_SET0_TMP_DH_PKEY
1024+
if (!SSL_CTX_set0_tmp_dh_pkey(ctx, pkey))
1025+
ossl_raise(eSSLError, "SSL_CTX_set0_tmp_dh_pkey");
1026+
EVP_PKEY_up_ref(pkey);
1027+
#else
1028+
if (!SSL_CTX_set_tmp_dh(ctx, EVP_PKEY_get0_DH(pkey)))
1029+
ossl_raise(eSSLError, "SSL_CTX_set_tmp_dh");
1030+
#endif
1031+
1032+
return arg;
1033+
}
1034+
#endif
1035+
9901036
#if !defined(OPENSSL_NO_EC)
9911037
/*
9921038
* call-seq:
@@ -2670,6 +2716,9 @@ Init_ossl_ssl(void)
26702716
ossl_sslctx_set_minmax_proto_version, 2);
26712717
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
26722718
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
2719+
#ifndef OPENSSL_NO_DH
2720+
rb_define_method(cSSLContext, "tmp_dh=", ossl_sslctx_set_tmp_dh, 1);
2721+
#endif
26732722
rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1);
26742723
rb_define_method(cSSLContext, "security_level", ossl_sslctx_get_security_level, 0);
26752724
rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1);

lib/openssl/ssl.rb

+5-3
Original file line numberDiff line numberDiff line change
@@ -91,15 +91,17 @@ class SSLContext
9191
DEFAULT_CERT_STORE.set_default_paths
9292
DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
9393

94-
# A callback invoked when DH parameters are required.
94+
# A callback invoked when DH parameters are required for ephemeral DH key
95+
# exchange.
9596
#
96-
# The callback is invoked with the Session for the key exchange, an
97+
# The callback is invoked with the SSLSocket, a
9798
# flag indicating the use of an export cipher and the keylength
9899
# required.
99100
#
100101
# The callback must return an OpenSSL::PKey::DH instance of the correct
101102
# key length.
102-
103+
#
104+
# <b>Deprecated in version 3.0.</b> Use #tmp_dh= instead.
103105
attr_accessor :tmp_dh_callback
104106

105107
# A callback invoked at connect time to distinguish between multiple

test/openssl/test_ssl.rb

+17-7
Original file line numberDiff line numberDiff line change
@@ -1583,13 +1583,11 @@ def test_fallback_scsv
15831583
end
15841584
end
15851585

1586-
def test_dh_callback
1587-
pend "TLS 1.2 is not supported" unless tls12_supported?
1588-
1586+
def test_tmp_dh_callback
15891587
dh = Fixtures.pkey("dh-1")
15901588
called = false
15911589
ctx_proc = -> ctx {
1592-
ctx.ssl_version = :TLSv1_2
1590+
ctx.max_version = :TLS1_2
15931591
ctx.ciphers = "DH:!NULL"
15941592
ctx.tmp_dh_callback = ->(*args) {
15951593
called = true
@@ -1605,10 +1603,8 @@ def test_dh_callback
16051603
end
16061604

16071605
def test_connect_works_when_setting_dh_callback_to_nil
1608-
pend "TLS 1.2 is not supported" unless tls12_supported?
1609-
16101606
ctx_proc = -> ctx {
1611-
ctx.ssl_version = :TLSv1_2
1607+
ctx.max_version = :TLS1_2
16121608
ctx.ciphers = "DH:!NULL" # use DH
16131609
ctx.tmp_dh_callback = nil
16141610
}
@@ -1621,6 +1617,20 @@ def test_connect_works_when_setting_dh_callback_to_nil
16211617
end
16221618
end
16231619

1620+
def test_tmp_dh
1621+
dh = Fixtures.pkey("dh-1")
1622+
ctx_proc = -> ctx {
1623+
ctx.max_version = :TLS1_2
1624+
ctx.ciphers = "DH:!NULL" # use DH
1625+
ctx.tmp_dh = dh
1626+
}
1627+
start_server(ctx_proc: ctx_proc) do |port|
1628+
server_connect(port) { |ssl|
1629+
assert_equal dh.to_der, ssl.tmp_key.to_der
1630+
}
1631+
end
1632+
end
1633+
16241634
def test_ecdh_curves_tls12
16251635
pend "EC is disabled" unless defined?(OpenSSL::PKey::EC)
16261636

0 commit comments

Comments
 (0)