Skip to content

Commit

Permalink
Merge pull request #459 from rhenium/ky/ssl-set-tmp-dh
Browse files Browse the repository at this point in the history
ssl: add SSLContext#tmp_dh=
  • Loading branch information
rhenium committed Sep 27, 2021
2 parents 5c85b43 + aa43da4 commit e3a4093
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 14 deletions.
3 changes: 3 additions & 0 deletions ext/openssl/extconf.rb
Expand Up @@ -170,6 +170,9 @@ def find_openssl_library
# added in 1.1.1
have_func("EVP_PKEY_check")

# added in 3.0.0
have_func("SSL_set0_tmp_dh_pkey")

Logging::message "=== Checking done. ===\n"

create_header
Expand Down
49 changes: 49 additions & 0 deletions ext/openssl/ossl_ssl.c
Expand Up @@ -987,6 +987,52 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
return v;
}

#ifndef OPENSSL_NO_DH
/*
* call-seq:
* ctx.tmp_dh = pkey
*
* Sets DH parameters used for ephemeral DH key exchange. This is relevant for
* servers only.
*
* +pkey+ is an instance of OpenSSL::PKey::DH. Note that key components
* contained in the key object, if any, are ignored. The server will always
* generate a new key pair for each handshake.
*
* Added in version 3.0. See also the man page SSL_set0_tmp_dh_pkey(3).
*
* Example:
* ctx = OpenSSL::SSL::SSLContext.new
* ctx.tmp_dh = OpenSSL::DH.generate(2048)
* svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx)
* Thread.new { svr.accept }
*/
static VALUE
ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg)
{
SSL_CTX *ctx;
EVP_PKEY *pkey;

rb_check_frozen(self);
GetSSLCTX(self, ctx);
pkey = GetPKeyPtr(arg);

if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH)
rb_raise(eSSLError, "invalid pkey type %s (expected DH)",
OBJ_nid2sn(EVP_PKEY_base_id(pkey)));
#ifdef HAVE_SSL_SET0_TMP_DH_PKEY
if (!SSL_CTX_set0_tmp_dh_pkey(ctx, pkey))
ossl_raise(eSSLError, "SSL_CTX_set0_tmp_dh_pkey");
EVP_PKEY_up_ref(pkey);
#else
if (!SSL_CTX_set_tmp_dh(ctx, EVP_PKEY_get0_DH(pkey)))
ossl_raise(eSSLError, "SSL_CTX_set_tmp_dh");
#endif

return arg;
}
#endif

#if !defined(OPENSSL_NO_EC)
/*
* call-seq:
Expand Down Expand Up @@ -2670,6 +2716,9 @@ Init_ossl_ssl(void)
ossl_sslctx_set_minmax_proto_version, 2);
rb_define_method(cSSLContext, "ciphers", ossl_sslctx_get_ciphers, 0);
rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 1);
#ifndef OPENSSL_NO_DH
rb_define_method(cSSLContext, "tmp_dh=", ossl_sslctx_set_tmp_dh, 1);
#endif
rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1);
rb_define_method(cSSLContext, "security_level", ossl_sslctx_get_security_level, 0);
rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1);
Expand Down
12 changes: 5 additions & 7 deletions lib/openssl/ssl.rb
Expand Up @@ -91,15 +91,17 @@ class SSLContext
DEFAULT_CERT_STORE.set_default_paths
DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL

# A callback invoked when DH parameters are required.
# A callback invoked when DH parameters are required for ephemeral DH key
# exchange.
#
# The callback is invoked with the Session for the key exchange, an
# The callback is invoked with the SSLSocket, a
# flag indicating the use of an export cipher and the keylength
# required.
#
# The callback must return an OpenSSL::PKey::DH instance of the correct
# key length.

#
# <b>Deprecated in version 3.0.</b> Use #tmp_dh= instead.
attr_accessor :tmp_dh_callback

# A callback invoked at connect time to distinguish between multiple
Expand Down Expand Up @@ -432,10 +434,6 @@ def tmp_dh_callback
@context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
end

def tmp_ecdh_callback
@context.tmp_ecdh_callback
end

def session_new_cb
@context.session_new_cb
end
Expand Down
24 changes: 17 additions & 7 deletions test/openssl/test_ssl.rb
Expand Up @@ -1583,13 +1583,11 @@ def test_fallback_scsv
end
end

def test_dh_callback
pend "TLS 1.2 is not supported" unless tls12_supported?

def test_tmp_dh_callback
dh = Fixtures.pkey("dh-1")
called = false
ctx_proc = -> ctx {
ctx.ssl_version = :TLSv1_2
ctx.max_version = :TLS1_2
ctx.ciphers = "DH:!NULL"
ctx.tmp_dh_callback = ->(*args) {
called = true
Expand All @@ -1605,10 +1603,8 @@ def test_dh_callback
end

def test_connect_works_when_setting_dh_callback_to_nil
pend "TLS 1.2 is not supported" unless tls12_supported?

ctx_proc = -> ctx {
ctx.ssl_version = :TLSv1_2
ctx.max_version = :TLS1_2
ctx.ciphers = "DH:!NULL" # use DH
ctx.tmp_dh_callback = nil
}
Expand All @@ -1621,6 +1617,20 @@ def test_connect_works_when_setting_dh_callback_to_nil
end
end

def test_tmp_dh
dh = Fixtures.pkey("dh-1")
ctx_proc = -> ctx {
ctx.max_version = :TLS1_2
ctx.ciphers = "DH:!NULL" # use DH
ctx.tmp_dh = dh
}
start_server(ctx_proc: ctx_proc) do |port|
server_connect(port) { |ssl|
assert_equal dh.to_der, ssl.tmp_key.to_der
}
end
end

def test_ecdh_curves_tls12
pend "EC is disabled" unless defined?(OpenSSL::PKey::EC)

Expand Down

0 comments on commit e3a4093

Please sign in to comment.