From c5deff8cf79400badb4f3f3c63e3fc13b275746b Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 13 Apr 2016 22:57:40 +0900 Subject: retire tmp_ecdh_callback and add set_elliptic_curves --- ext/openssl/extconf.rb | 2 + ext/openssl/lib/openssl/ssl.rb | 9 +--- ext/openssl/openssl_missing.c | 34 ++++++++++++++ ext/openssl/openssl_missing.h | 4 ++ ext/openssl/ossl_ssl.c | 101 +++++++++++++++++++++-------------------- test/openssl/test_pair.rb | 18 +++----- test/openssl/utils.rb | 2 +- 7 files changed, 102 insertions(+), 68 deletions(-) diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb index 614288138c..330d300127 100644 --- a/ext/openssl/extconf.rb +++ b/ext/openssl/extconf.rb @@ -101,6 +101,7 @@ have_func("PEM_def_callback") have_func("PKCS5_PBKDF2_HMAC") have_func("PKCS5_PBKDF2_HMAC_SHA1") have_func("RAND_egd") +have_func("EC_curve_nist2nid") have_func("X509V3_set_nconf") have_func("X509V3_EXT_nconf_nid") have_func("X509_CRL_add0_revoked") @@ -138,6 +139,7 @@ have_func("TLSv1_2_client_method") have_func("SSL_CTX_set_alpn_select_cb") have_func("SSL_CTX_set_next_proto_select_cb") have_func("SSL_CTX_set_tmp_ecdh_callback") # workaround: 1.1.0 removed this +have_func_or_macro("SSL_CTX_set1_curves_list", "openssl/ssl.h") have_macro("SSL_get_server_tmp_key", ['openssl/ssl.h']) && $defs.push("-DHAVE_SSL_GET_SERVER_TMP_KEY") unless have_func("SSL_set_tlsext_host_name", ['openssl/ssl.h']) have_macro("SSL_set_tlsext_host_name", ['openssl/ssl.h']) && $defs.push("-DHAVE_SSL_SET_TLSEXT_HOST_NAME") diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb index 57519f2c21..3969758279 100644 --- a/ext/openssl/lib/openssl/ssl.rb +++ b/ext/openssl/lib/openssl/ssl.rb @@ -75,9 +75,8 @@ module OpenSSL "verify_callback", "cert_store", "extra_chain_cert", "client_cert_cb", "session_id_context", "tmp_dh_callback", "session_get_cb", "session_new_cb", "session_remove_cb", - "tmp_ecdh_callback", "servername_cb", "npn_protocols", - "alpn_protocols", "alpn_select_cb", - "npn_select_cb"].map { |x| "@#{x}" } + "servername_cb", "npn_protocols", "alpn_protocols", + "alpn_select_cb", "npn_select_cb"].map { |x| "@#{x}" } # A callback invoked when DH parameters are required. # @@ -339,10 +338,6 @@ module OpenSSL @context.tmp_dh_callback || OpenSSL::PKey::DEFAULT_TMP_DH_CALLBACK end - def tmp_ecdh_callback - @context.tmp_ecdh_callback - end - def session_new_cb @context.session_new_cb end diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c index 115f63b2d7..71ce15172e 100644 --- a/ext/openssl/openssl_missing.c +++ b/ext/openssl/openssl_missing.c @@ -552,3 +552,37 @@ SSL_SESSION_cmp(const SSL_SESSION *a, const SSL_SESSION *b) #endif } #endif /* SSL */ + +#if !defined(HAVE_EC_CURVE_NIST2NID) /* new in 1.0.2 */ +static struct { + const char *name; + int nid; +} nist_curves[] = { + {"B-163", NID_sect163r2}, + {"B-233", NID_sect233r1}, + {"B-283", NID_sect283r1}, + {"B-409", NID_sect409r1}, + {"B-571", NID_sect571r1}, + {"K-163", NID_sect163k1}, + {"K-233", NID_sect233k1}, + {"K-283", NID_sect283k1}, + {"K-409", NID_sect409k1}, + {"K-571", NID_sect571k1}, + {"P-192", NID_X9_62_prime192v1}, + {"P-224", NID_secp224r1}, + {"P-256", NID_X9_62_prime256v1}, + {"P-384", NID_secp384r1}, + {"P-521", NID_secp521r1} +}; + +int +EC_curve_nist2nid(const char *name) +{ + size_t i; + for (i = 0; i < (sizeof(nist_curves) / sizeof(nist_curves[0])); i++) { + if (!strcmp(nist_curves[i].name, name)) + return nist_curves[i].nid; + } + return NID_undef; +} +#endif diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h index 9b7eec5f98..f8331880ee 100644 --- a/ext/openssl/openssl_missing.h +++ b/ext/openssl/openssl_missing.h @@ -210,6 +210,10 @@ ASN1_INTEGER *X509_REVOKED_get0_serialNumber(X509_REVOKED *x); int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial); #endif +#if !defined(HAVE_EC_CURVE_NIST2NID) /* new in 1.0.2 */ +int EC_curve_nist2nid(const char *str); +#endif + /*** new in 1.1.0 ***/ /* OCSP */ #if defined(HAVE_OPENSSL_OCSP_H) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 14869f5461..69bbc1e16e 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -62,7 +62,6 @@ static VALUE eSSLErrorWaitWritable; #define ossl_sslctx_get_cert_store(o) rb_iv_get((o),"@cert_store") #define ossl_sslctx_get_extra_cert(o) rb_iv_get((o),"@extra_chain_cert") #define ossl_sslctx_get_client_cert_cb(o) rb_iv_get((o),"@client_cert_cb") -#define ossl_sslctx_get_tmp_ecdh_cb(o) rb_iv_get((o),"@tmp_ecdh_callback") #define ossl_sslctx_get_sess_id_ctx(o) rb_iv_get((o),"@session_id_context") #define ossl_ssl_get_io(o) rb_iv_get((o),"@io") @@ -73,7 +72,6 @@ static VALUE eSSLErrorWaitWritable; #define ossl_ssl_set_x509(o,v) rb_iv_set((o),"@x509",(v)) #define ossl_ssl_set_key(o,v) rb_iv_set((o),"@key",(v)) #define ossl_ssl_set_tmp_dh(o,v) rb_iv_set((o),"@tmp_dh",(v)) -#define ossl_ssl_set_tmp_ecdh(o,v) rb_iv_set((o),"@tmp_ecdh",(v)) static ID ID_callback_state; @@ -126,6 +124,7 @@ static const struct { static int ossl_ssl_ex_vcb_idx; static int ossl_ssl_ex_store_p; static int ossl_ssl_ex_ptr_idx; +static int ossl_ssl_ex_ec_nid_idx; static void ossl_sslctx_free(void *ptr) @@ -271,40 +270,6 @@ ossl_tmp_dh_callback(SSL *ssl, int is_export, int keylength) } #endif /* OPENSSL_NO_DH */ -#if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) -static VALUE -ossl_call_tmp_ecdh_callback(VALUE args) -{ - VALUE cb, ecdh; - EVP_PKEY *pkey; - - cb = rb_funcall(rb_ary_entry(args, 0), rb_intern("tmp_ecdh_callback"), 0); - - if (NIL_P(cb)) return Qfalse; - ecdh = rb_apply(cb, rb_intern("call"), args); - pkey = GetPKeyPtr(ecdh); - if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) return Qfalse; - - return ecdh; -} - -static EC_KEY* -ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength) -{ - VALUE args, ecdh, rb_ssl; - - rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx); - - args = rb_ary_new_from_args(3, rb_ssl, INT2FIX(is_export), INT2FIX(keylength)); - - ecdh = rb_protect(ossl_call_tmp_ecdh_callback, args, NULL); - if (!RTEST(ecdh)) return NULL; - ossl_ssl_set_tmp_ecdh(rb_ssl, ecdh); - - return EVP_PKEY_get0_EC_KEY(GetPKeyPtr(ecdh)); -} -#endif - static int ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { @@ -1011,6 +976,56 @@ ossl_sslctx_set_security_level(VALUE self, VALUE v) return v; } +#ifndef OPENSSL_NO_EC +#if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK) +static EC_KEY * +ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength) +{ + int nid = (int)SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl), ossl_ssl_ex_ec_nid_idx); + if (nid) + return EC_KEY_new_by_curve_name(nid); + else + return NULL; +} +#endif + +/* + * call-seq: + * ctx.set_elliptic_curves("curve1:curve2:curve3") -> self + * + * Sets the list of supported elliptic curves for this context. The curves are + * passed as a string, colon separated list of curve NIDs in NIST curve names. + * For example "P-521:P-384:P-256". + */ +static VALUE +ossl_sslctx_set_elliptic_curves(VALUE self, VALUE str) +{ + SSL_CTX *ctx; + const char *cstr = StringValueCStr(str); + + rb_check_frozen(self); + GetSSLCTX(self, ctx); + if (!ctx) + ossl_raise(eSSLError, "SSL_CTX is not initialized."); + +#if defined(HAVE_SSL_CTX_SET1_CURVES_LIST) /* OpenSSL 1.0.2- */ + if (!SSL_CTX_set1_curves_list(ctx, cstr)) + ossl_raise(eSSLError, "SSL_CTX_set1_curves_list"); +#else + if (strstr(cstr, ":")) + ossl_raise(eSSLError, "only one curve can be specified"); + { + int nid = EC_curve_nist2nid(cstr); + if (nid == NID_undef) + ossl_raise(eSSLError, "unknown curve name"); + SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ec_nid_idx, (void *)(VALUE)nid); + } +#endif + + return self; +} +#endif + /* * call-seq: * ctx.session_add(session) -> true | false @@ -2005,6 +2020,7 @@ Init_ossl_ssl(void) ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_vcb_idx",0,0,0); ossl_ssl_ex_store_p = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_store_p",0,0,0); ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_ptr_idx",0,0,0); + ossl_ssl_ex_ec_nid_idx = SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_ec_nid_idx",0,0,0); /* Document-module: OpenSSL::SSL * @@ -2133,18 +2149,6 @@ Init_ossl_ssl(void) */ rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse); - /* - * A callback invoked when ECDH parameters are required. - * - * The callback is invoked with the Session for the key exchange, an - * flag indicating the use of an export cipher and the keylength - * required. - * - * The callback must return an OpenSSL::PKey::EC instance of the correct - * key length. - */ - rb_attr(cSSLContext, rb_intern("tmp_ecdh_callback"), 1, 1, Qfalse); - /* * Sets the context in which a session can be reused. This allows * sessions for multiple applications to be distinguished, for example, by @@ -2281,6 +2285,7 @@ Init_ossl_ssl(void) rb_define_method(cSSLContext, "ciphers=", ossl_sslctx_set_ciphers, 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); + rb_define_method(cSSLContext, "set_elliptic_curves", ossl_sslctx_set_elliptic_curves, 1); rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0); diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb index eafaee2503..5279f6010f 100644 --- a/test/openssl/test_pair.rb +++ b/test/openssl/test_pair.rb @@ -380,24 +380,17 @@ module OpenSSL::TestPairM accepted.close if accepted.respond_to?(:close) end - def test_ecdh_callback - if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000 - skip "OpenSSL 1.1.0 removed SSL_CTX_set_tmp_ecdh_callback()" - end - called = false + def test_set_elliptic_curves ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.ciphers = "ECDH" + ctx2.ciphers = "ECDH:DH" ctx2.security_level = 0 - ctx2.tmp_ecdh_callback = ->(*args) { - called = true - OpenSSL::PKey::EC.new "prime256v1" - } + ctx2.set_elliptic_curves("P-384") sock1, sock2 = tcp_pair s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) ctx1 = OpenSSL::SSL::SSLContext.new - ctx1.ciphers = "ECDH" + ctx1.ciphers = "ECDH:DH" ctx1.security_level = 0 s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) @@ -415,7 +408,8 @@ module OpenSSL::TestPairM accepted = s2.accept - assert called, 'ecdh callback should be called' + assert accepted.cipher[0].start_with?("AECDH"), "AECDH should be used" + # TODO: how to detect what curve was used? rescue OpenSSL::SSL::SSLError => e if e.message =~ /no cipher match/ skip "ECDH cipher not supported." diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb index 383204c5de..1afee5f88a 100644 --- a/test/openssl/utils.rb +++ b/test/openssl/utils.rb @@ -294,7 +294,7 @@ AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC ctx.cert = @svr_cert ctx.key = @svr_key ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::TEST_KEY_DH1024 } - ctx.tmp_ecdh_callback = proc { OpenSSL::TestUtils::TEST_KEY_EC_P256V1 } + ctx.set_elliptic_curves("P-256") ctx.verify_mode = verify_mode ctx_proc.call(ctx) if ctx_proc -- cgit v1.2.3