aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2016-04-13 22:57:40 +0900
committerKazuki Yamaguchi <k@rhe.jp>2016-04-21 00:46:34 +0900
commitc5deff8cf79400badb4f3f3c63e3fc13b275746b (patch)
tree4ebe2ceb7638f36585cdbb5a9f26e02a86a0ce11
parent2b51d449be34963b8e56439cbaac8d75dd0666a1 (diff)
downloadruby-c5deff8cf79400badb4f3f3c63e3fc13b275746b.tar.gz
retire tmp_ecdh_callback and add set_elliptic_curves
-rw-r--r--ext/openssl/extconf.rb2
-rw-r--r--ext/openssl/lib/openssl/ssl.rb9
-rw-r--r--ext/openssl/openssl_missing.c34
-rw-r--r--ext/openssl/openssl_missing.h4
-rw-r--r--ext/openssl/ossl_ssl.c101
-rw-r--r--test/openssl/test_pair.rb18
-rw-r--r--test/openssl/utils.rb2
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
*
@@ -2134,18 +2150,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
* name.
@@ -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