[前][次][番号順一覧][スレッド一覧]

ruby-changes:39267

From: tenderlove <ko1@a...>
Date: Thu, 23 Jul 2015 03:35:06 +0900 (JST)
Subject: [ruby-changes:39267] tenderlove:r51348 (trunk): * ext/openssl/ossl_ssl.c: add ECDH callback support. [Feature #11356]

tenderlove	2015-07-23 03:34:45 +0900 (Thu, 23 Jul 2015)

  New Revision: 51348

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=51348

  Log:
    * ext/openssl/ossl_ssl.c: add ECDH callback support. [Feature #11356]
    
    * test/openssl/test_pair.rb: test for ECDH callback support

  Modified files:
    trunk/ChangeLog
    trunk/ext/openssl/ossl_ssl.c
    trunk/test/openssl/test_pair.rb
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 51347)
+++ ChangeLog	(revision 51348)
@@ -1,3 +1,9 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Thu Jul 23 03:32:26 2015  Aaron Patterson <tenderlove@r...>
+
+	* ext/openssl/ossl_ssl.c: add ECDH callback support. [Feature #11356]
+
+	* test/openssl/test_pair.rb: test for ECDH callback support
+
 Thu Jul 23 03:29:49 2015  Aaron Patterson <tenderlove@r...>
 
 	* ext/openssl/ossl_ssl.c: add ALPN support. [Feature #9390]
Index: ext/openssl/ossl_ssl.c
===================================================================
--- ext/openssl/ossl_ssl.c	(revision 51347)
+++ ext/openssl/ossl_ssl.c	(revision 51348)
@@ -65,6 +65,7 @@ static VALUE eSSLErrorWaitWritable; https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L65
 #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_tmp_dh_cb(o)     	rb_iv_get((o),"@tmp_dh_callback")
 #define ossl_sslctx_get_sess_id_ctx(o)   	rb_iv_get((o),"@session_id_context")
 
@@ -74,6 +75,7 @@ static const char *ossl_sslctx_attrs[] = https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L75
     "verify_callback", "options", "cert_store", "extra_chain_cert",
     "client_cert_cb", "tmp_dh_callback", "session_id_context",
     "session_get_cb", "session_new_cb", "session_remove_cb",
+    "tmp_ecdh_callback",
 #ifdef HAVE_SSL_SET_TLSEXT_HOST_NAME
     "servername_cb",
 #endif
@@ -89,6 +91,7 @@ static const char *ossl_sslctx_attrs[] = https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L91
 #define ossl_ssl_get_x509(o)         rb_iv_get((o),"@x509")
 #define ossl_ssl_get_key(o)          rb_iv_get((o),"@key")
 #define ossl_ssl_get_tmp_dh(o)       rb_iv_get((o),"@tmp_dh")
+#define ossl_ssl_get_tmp_ecdh(o)     rb_iv_get((o),"@tmp_ecdh")
 
 #define ossl_ssl_set_io(o,v)         rb_iv_set((o),"@io",(v))
 #define ossl_ssl_set_ctx(o,v)        rb_iv_set((o),"@context",(v))
@@ -96,6 +99,7 @@ static const char *ossl_sslctx_attrs[] = https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L99
 #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 const char *ossl_ssl_attr_readers[] = { "io", "context", };
 static const char *ossl_ssl_attrs[] = {
@@ -152,6 +156,7 @@ int ossl_ssl_ex_store_p; https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L156
 int ossl_ssl_ex_ptr_idx;
 int ossl_ssl_ex_client_cert_cb_idx;
 int ossl_ssl_ex_tmp_dh_callback_idx;
+int ossl_ssl_ex_tmp_ecdh_callback_idx;
 
 static void
 ossl_sslctx_free(void *ptr)
@@ -337,6 +342,41 @@ ossl_default_tmp_dh_callback(SSL *ssl, i https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L342
 }
 #endif /* OPENSSL_NO_DH */
 
+#if !defined(OPENSSL_NO_EC)
+static VALUE
+ossl_call_tmp_ecdh_callback(VALUE args)
+{
+    SSL *ssl;
+    VALUE cb, ecdh;
+    EVP_PKEY *pkey;
+
+    GetSSL(rb_ary_entry(args, 0), ssl);
+    cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_tmp_ecdh_callback_idx);
+    if (NIL_P(cb)) return Qfalse;
+    ecdh = rb_apply(cb, rb_intern("call"), args);
+    pkey = GetPKeyPtr(ecdh);
+    if (EVP_PKEY_type(pkey->type) != EVP_PKEY_EC) return Qfalse;
+    ossl_ssl_set_tmp_ecdh(rb_ary_entry(args, 0), ecdh);
+
+    return Qtrue;
+}
+
+static EC_KEY*
+ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength)
+{
+    VALUE args, success, 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));
+
+    success = rb_protect(ossl_call_tmp_ecdh_callback, args, NULL);
+    if (!RTEST(success)) return NULL;
+
+    return GetPKeyPtr(ossl_ssl_get_tmp_ecdh(rb_ssl))->pkey.ec;
+}
+#endif
+
 static int
 ossl_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
 {
@@ -719,6 +759,13 @@ ossl_sslctx_setup(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L759
 	SSL_CTX_set_tmp_dh_callback(ctx, ossl_default_tmp_dh_callback);
     }
 #endif
+
+#if !defined(OPENSSL_NO_EC)
+    if (RTEST(ossl_sslctx_get_tmp_ecdh_cb(self))){
+	SSL_CTX_set_tmp_ecdh_callback(ctx, ossl_tmp_ecdh_callback);
+    }
+#endif
+
     SSL_CTX_set_ex_data(ctx, ossl_ssl_ex_ptr_idx, (void*)self);
 
     val = ossl_sslctx_get_cert_store(self);
@@ -1275,6 +1322,8 @@ ossl_ssl_setup(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L1322
 	SSL_set_ex_data(ssl, ossl_ssl_ex_client_cert_cb_idx, (void*)cb);
 	cb = ossl_sslctx_get_tmp_dh_cb(v_ctx);
 	SSL_set_ex_data(ssl, ossl_ssl_ex_tmp_dh_callback_idx, (void*)cb);
+	cb = ossl_sslctx_get_tmp_ecdh_cb(v_ctx);
+	SSL_set_ex_data(ssl, ossl_ssl_ex_tmp_ecdh_callback_idx, (void*)cb);
 	SSL_set_info_callback(ssl, ssl_info_cb);
     }
 
@@ -1990,6 +2039,8 @@ Init_ossl_ssl(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L2039
 	SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_client_cert_cb_idx",0,0,0);
     ossl_ssl_ex_tmp_dh_callback_idx =
 	SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_tmp_dh_callback_idx",0,0,0);
+    ossl_ssl_ex_tmp_ecdh_callback_idx =
+	SSL_get_ex_new_index(0,(void *)"ossl_ssl_ex_tmp_ecdh_callback_idx",0,0,0);
 
     /* Document-module: OpenSSL::SSL
      *
@@ -2113,6 +2164,18 @@ Init_ossl_ssl(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L2164
     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);
+
+     /*
      * A callback invoked when DH parameters are required.
      *
      * The callback is invoked with the Session for the key exchange, an
Index: test/openssl/test_pair.rb
===================================================================
--- test/openssl/test_pair.rb	(revision 51347)
+++ test/openssl/test_pair.rb	(revision 51348)
@@ -283,6 +283,45 @@ module OpenSSL::TestPairM https://github.com/ruby/ruby/blob/trunk/test/openssl/test_pair.rb#L283
     serv.close if serv && !serv.closed?
   end
 
+  def test_ecdh_callback
+    called = false
+    ctx2 = OpenSSL::SSL::SSLContext.new
+    ctx2.ciphers = "ECDH"
+    ctx2.tmp_ecdh_callback = ->(*args) {
+      called = true
+      OpenSSL::PKey::EC.new "prime256v1"
+    }
+
+    sock1, sock2 = tcp_pair
+
+    s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
+    ctx1 = OpenSSL::SSL::SSLContext.new
+    ctx1.ciphers = "ECDH"
+
+    s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
+    th = Thread.new do
+      begin
+        rv = s1.connect_nonblock(exception: false)
+        case rv
+        when :wait_writable
+          IO.select(nil, [s1], nil, 5)
+        when :wait_readable
+          IO.select([s1], nil, nil, 5)
+        end
+      end until rv == s1
+    end
+
+    accepted = s2.accept
+
+    assert called, 'ecdh callback should be called'
+  ensure
+    s1.close if s1
+    s2.close if s2
+    sock1.close if sock1
+    sock2.close if sock2
+    accepted.close if accepted.respond_to?(:close)
+  end
+
   def test_connect_accept_nonblock_no_exception
     ctx2 = OpenSSL::SSL::SSLContext.new
     ctx2.ciphers = "ADH"

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]