Skip to content

Commit e3a4093

Browse files
authored
Merge pull request #459 from rhenium/ky/ssl-set-tmp-dh
ssl: add SSLContext#tmp_dh=
2 parents 5c85b43 + aa43da4 commit e3a4093

File tree

4 files changed

+74
-14
lines changed

4 files changed

+74
-14
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-7
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
@@ -432,10 +434,6 @@ def tmp_dh_callback
432434
@context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
433435
end
434436

435-
def tmp_ecdh_callback
436-
@context.tmp_ecdh_callback
437-
end
438-
439437
def session_new_cb
440438
@context.session_new_cb
441439
end

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)