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

ruby-changes:48791

From: rhe <ko1@a...>
Date: Sat, 25 Nov 2017 23:12:15 +0900 (JST)
Subject: [ruby-changes:48791] rhe:r60907 (trunk): openssl: import v2.1.0.beta2

rhe	2017-11-25 23:12:08 +0900 (Sat, 25 Nov 2017)

  New Revision: 60907

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

  Log:
    openssl: import v2.1.0.beta2
    
    Import Ruby/OpenSSL 2.1.0.beta2. The full commit log since commit
    e72d960db262 which was imported by r60013 can be found at:
    
    https://github.com/ruby/openssl/compare/e72d960db262...v2.1.0.beta2
    
    ----------------------------------------------------------------
    Kazuki Yamaguchi (26):
          bn: use ALLOCV() macro instead of xmalloc()
          appveyor.yml: remove 'openssl version' line
          test/test_ssl_session: skip tests for session_remove_cb
          x509ext: implement X509::Extension#==
          x509attr: implement X509::Attribute#==
          x509cert: implement X509::Certificate#==
          x509revoked: add missing X509::Revoked#to_der
          x509crl, x509revoked: implement X509::{CRL,Revoked}#==
          x509req: implement X509::Request#==
          ssl: extract rb_intern("call")
          cipher: disallow setting AAD for non-AEAD ciphers
          test/test_cipher: fix test_non_aead_cipher_set_auth_data failure
          ssl: fix conflict of options in SSLContext#set_params
          buffering: let #write accept multiple arguments
          pkey: make pkey_check_public_key() non-static
          x509cert, x509crl, x509req, ns_spki: check sanity of public key
          test/envutil: port assert_warning from Ruby trunk
          test/utils: remove a pointless .public_key call in issue_cert
          ssl: add SSLContext#add_certificate
          test/test_ssl: fix test_security_level
          Drop support for LibreSSL 2.4
          kdf: add HKDF support
          test/test_x509cert: fix flaky test
          test/test_x509crl: fix random failure
          History.md: fix a typo
          Ruby/OpenSSL 2.1.0.beta2
    
    Mark Wright (1):
          Fix build failure against OpenSSL 1.1 built with no-deprecated Thanks rhenium for the code review and fixes.
    
    Peter Karman (1):
          Add RSA sign_pss() and verify_pss() methods
    
    aeris (1):
          TLS Fallback Signaling Cipher Suite Value
    
    kazu (1):
          Use caller with length to reduce unused strings

  Modified files:
    trunk/ext/openssl/History.md
    trunk/ext/openssl/lib/openssl/buffering.rb
    trunk/ext/openssl/lib/openssl/x509.rb
    trunk/ext/openssl/openssl.gemspec
    trunk/ext/openssl/openssl_missing.h
    trunk/ext/openssl/ossl.c
    trunk/ext/openssl/ossl.h
    trunk/ext/openssl/ossl_bn.c
    trunk/ext/openssl/ossl_cipher.c
    trunk/ext/openssl/ossl_engine.c
    trunk/ext/openssl/ossl_kdf.c
    trunk/ext/openssl/ossl_ns_spki.c
    trunk/ext/openssl/ossl_pkey.c
    trunk/ext/openssl/ossl_pkey.h
    trunk/ext/openssl/ossl_pkey_rsa.c
    trunk/ext/openssl/ossl_ssl.c
    trunk/ext/openssl/ossl_x509cert.c
    trunk/ext/openssl/ossl_x509crl.c
    trunk/ext/openssl/ossl_x509req.c
    trunk/ext/openssl/ossl_x509revoked.c
    trunk/test/openssl/test_cipher.rb
    trunk/test/openssl/test_kdf.rb
    trunk/test/openssl/test_ocsp.rb
    trunk/test/openssl/test_pair.rb
    trunk/test/openssl/test_pkey_rsa.rb
    trunk/test/openssl/test_ssl.rb
    trunk/test/openssl/test_x509attr.rb
    trunk/test/openssl/test_x509cert.rb
    trunk/test/openssl/test_x509crl.rb
    trunk/test/openssl/test_x509ext.rb
    trunk/test/openssl/test_x509req.rb
    trunk/test/openssl/utils.rb
Index: ext/openssl/ossl_ssl.c
===================================================================
--- ext/openssl/ossl_ssl.c	(revision 60906)
+++ ext/openssl/ossl_ssl.c	(revision 60907)
@@ -32,7 +32,7 @@ VALUE cSSLSocket; https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L32
 static VALUE eSSLErrorWaitReadable;
 static VALUE eSSLErrorWaitWritable;
 
-static ID ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback,
+static ID id_call, ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback,
 	  id_npn_protocols_encoded;
 static VALUE sym_exception, sym_wait_readable, sym_wait_writable;
 
@@ -205,7 +205,7 @@ ossl_call_client_cert_cb(VALUE obj) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L205
     if (NIL_P(cb))
 	return Qnil;
 
-    ary = rb_funcall(cb, rb_intern("call"), 1, obj);
+    ary = rb_funcallv(cb, id_call, 1, &obj);
     Check_Type(ary, T_ARRAY);
     GetX509CertPtr(cert = rb_ary_entry(ary, 0));
     GetPrivPKeyPtr(key = rb_ary_entry(ary, 1));
@@ -248,8 +248,8 @@ ossl_call_tmp_dh_callback(struct tmp_dh_ https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L248
     cb = rb_funcall(args->ssl_obj, args->id, 0);
     if (NIL_P(cb))
 	return NULL;
-    dh = rb_funcall(cb, rb_intern("call"), 3,
-		    args->ssl_obj, INT2NUM(args->is_export), INT2NUM(args->keylength));
+    dh = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export),
+		    INT2NUM(args->keylength));
     pkey = GetPKeyPtr(dh);
     if (EVP_PKEY_base_id(pkey) != args->type)
 	return NULL;
@@ -374,12 +374,12 @@ ossl_call_session_get_cb(VALUE ary) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L374
     cb = rb_funcall(ssl_obj, rb_intern("session_get_cb"), 0);
     if (NIL_P(cb)) return Qnil;
 
-    return rb_funcall(cb, rb_intern("call"), 1, ary);
+    return rb_funcallv(cb, id_call, 1, &ary);
 }
 
 /* this method is currently only called for servers (in OpenSSL <= 0.9.8e) */
 static SSL_SESSION *
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
 ossl_sslctx_session_get_cb(SSL *ssl, const unsigned char *buf, int len, int *copy)
 #else
 ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy)
@@ -420,7 +420,7 @@ ossl_call_session_new_cb(VALUE ary) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L420
     cb = rb_funcall(ssl_obj, rb_intern("session_new_cb"), 0);
     if (NIL_P(cb)) return Qnil;
 
-    return rb_funcall(cb, rb_intern("call"), 1, ary);
+    return rb_funcallv(cb, id_call, 1, &ary);
 }
 
 /* return 1 normal.  return 0 removes the session */
@@ -467,7 +467,7 @@ ossl_call_session_remove_cb(VALUE ary) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L467
     cb = rb_attr_get(sslctx_obj, id_i_session_remove_cb);
     if (NIL_P(cb)) return Qnil;
 
-    return rb_funcall(cb, rb_intern("call"), 1, ary);
+    return rb_funcallv(cb, id_call, 1, &ary);
 }
 
 static void
@@ -533,7 +533,7 @@ ossl_call_servername_cb(VALUE ary) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L533
     cb = rb_attr_get(sslctx_obj, id_i_servername_cb);
     if (NIL_P(cb)) return Qnil;
 
-    ret_obj = rb_funcall(cb, rb_intern("call"), 1, ary);
+    ret_obj = rb_funcallv(cb, id_call, 1, &ary);
     if (rb_obj_is_kind_of(ret_obj, cSSLContext)) {
         SSL *ssl;
         SSL_CTX *ctx2;
@@ -585,7 +585,7 @@ ssl_renegotiation_cb(const SSL *ssl) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L585
     cb = rb_attr_get(sslctx_obj, id_i_renegotiation_cb);
     if (NIL_P(cb)) return;
 
-    (void) rb_funcall(cb, rb_intern("call"), 1, ssl_obj);
+    rb_funcallv(cb, id_call, 1, &ssl_obj);
 }
 
 #if !defined(OPENSSL_NO_NEXTPROTONEG) || \
@@ -635,7 +635,7 @@ npn_select_cb_common_i(VALUE tmp) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L635
 	in += l;
     }
 
-    selected = rb_funcall(args->cb, rb_intern("call"), 1, protocols);
+    selected = rb_funcallv(args->cb, id_call, 1, &protocols);
     StringValue(selected);
     len = RSTRING_LEN(selected);
     if (len < 1 || len >= 256) {
@@ -1193,6 +1193,134 @@ ossl_sslctx_set_security_level(VALUE sel https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L1193
     return value;
 }
 
+#ifdef SSL_MODE_SEND_FALLBACK_SCSV
+/*
+ * call-seq:
+ *    ctx.enable_fallback_scsv() => nil
+ *
+ * Activate TLS_FALLBACK_SCSV for this context.
+ * See RFC 7507.
+ */
+static VALUE
+ossl_sslctx_enable_fallback_scsv(VALUE self)
+{
+    SSL_CTX *ctx;
+
+    GetSSLCTX(self, ctx);
+    SSL_CTX_set_mode(ctx, SSL_MODE_SEND_FALLBACK_SCSV);
+
+    return Qnil;
+}
+#endif
+
+/*
+ * call-seq:
+ *    ctx.add_certificate(certiticate, pkey [, extra_certs]) -> self
+ *
+ * Adds a certificate to the context. _pkey_ must be a corresponding private
+ * key with _certificate_.
+ *
+ * Multiple certificates with different public key type can be added by
+ * repeated calls of this method, and OpenSSL will choose the most appropriate
+ * certificate during the handshake.
+ *
+ * #cert=, #key=, and #extra_chain_cert= are old accessor methods for setting
+ * certificate and internally call this method.
+ *
+ * === Parameters
+ * _certificate_::
+ *   A certificate. An instance of OpenSSL::X509::Certificate.
+ * _pkey_::
+ *   The private key for _certificate_. An instance of OpenSSL::PKey::PKey.
+ * _extra_certs_::
+ *   Optional. An array of OpenSSL::X509::Certificate. When sending a
+ *   certificate chain, the certificates specified by this are sent following
+ *   _certificate_, in the order in the array.
+ *
+ * === Example
+ *   rsa_cert = OpenSSL::X509::Certificate.new(...)
+ *   rsa_pkey = OpenSSL::PKey.read(...)
+ *   ca_intermediate_cert = OpenSSL::X509::Certificate.new(...)
+ *   ctx.add_certificate(rsa_cert, rsa_pkey, [ca_intermediate_cert])
+ *
+ *   ecdsa_cert = ...
+ *   ecdsa_pkey = ...
+ *   another_ca_cert = ...
+ *   ctx.add_certificate(ecdsa_cert, ecdsa_pkey, [another_ca_cert])
+ *
+ * === Note
+ * OpenSSL before the version 1.0.2 could handle only one extra chain across
+ * all key types. Calling this method discards the chain set previously.
+ */
+static VALUE
+ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self)
+{
+    VALUE cert, key, extra_chain_ary;
+    SSL_CTX *ctx;
+    X509 *x509;
+    STACK_OF(X509) *extra_chain = NULL;
+    EVP_PKEY *pkey, *pub_pkey;
+
+    GetSSLCTX(self, ctx);
+    rb_scan_args(argc, argv, "21", &cert, &key, &extra_chain_ary);
+    rb_check_frozen(self);
+    x509 = GetX509CertPtr(cert);
+    pkey = GetPrivPKeyPtr(key);
+
+    /*
+     * The reference counter is bumped, and decremented immediately.
+     * X509_get0_pubkey() is only available in OpenSSL >= 1.1.0.
+     */
+    pub_pkey = X509_get_pubkey(x509);
+    EVP_PKEY_free(pub_pkey);
+    if (!pub_pkey)
+	rb_raise(rb_eArgError, "certificate does not contain public key");
+    if (EVP_PKEY_cmp(pub_pkey, pkey) != 1)
+	rb_raise(rb_eArgError, "public key mismatch");
+
+    if (argc >= 3)
+	extra_chain = ossl_x509_ary2sk(extra_chain_ary);
+
+    if (!SSL_CTX_use_certificate(ctx, x509)) {
+	sk_X509_pop_free(extra_chain, X509_free);
+	ossl_raise(eSSLError, "SSL_CTX_use_certificate");
+    }
+    if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
+	sk_X509_pop_free(extra_chain, X509_free);
+	ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey");
+    }
+
+    if (extra_chain) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000 && !defined(LIBRESSL_VERSION_NUMBER)
+	if (!SSL_CTX_set0_chain(ctx, extra_chain)) {
+	    sk_X509_pop_free(extra_chain, X509_free);
+	    ossl_raise(eSSLError, "SSL_CTX_set0_chain");
+	}
+#else
+	STACK_OF(X509) *orig_extra_chain;
+	X509 *x509_tmp;
+
+	/* First, clear the existing chain */
+	SSL_CTX_get_extra_chain_certs(ctx, &orig_extra_chain);
+	if (orig_extra_chain && sk_X509_num(orig_extra_chain)) {
+	    rb_warning("SSL_CTX_set0_chain() is not available; " \
+		       "clearing previously set certificate chain");
+	    SSL_CTX_clear_extra_chain_certs(ctx);
+	}
+	while ((x509_tmp = sk_X509_shift(extra_chain))) {
+	    /* Transfers ownership */
+	    if (!SSL_CTX_add_extra_chain_cert(ctx, x509_tmp)) {
+		X509_free(x509_tmp);
+		sk_X509_pop_free(extra_chain, X509_free);
+		ossl_raise(eSSLError, "SSL_CTX_add_extra_chain_cert");
+	    }
+	}
+	sk_X509_free(extra_chain);
+#endif
+    }
+    return self;
+}
+
 /*
  *  call-seq:
  *     ctx.session_add(session) -> true | false
@@ -2261,6 +2389,7 @@ Init_ossl_ssl(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L2389
     rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable");
 #endif
 
+    id_call = rb_intern("call");
     ID_callback_state = rb_intern("callback_state");
 
     ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_vcb_idx", 0, 0, 0);
@@ -2324,11 +2453,17 @@ Init_ossl_ssl(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L2453
 
     /*
      * Context certificate
+     *
+     * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
+     * It is recommended to use #add_certificate instead.
      */
     rb_attr(cSSLContext, rb_intern("cert"), 1, 1, Qfalse);
 
     /*
      * Context private key
+     *
+     * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
+     * It is recommended to use #add_certificate instead.
      */
     rb_attr(cSSLContext, rb_intern("key"), 1, 1, Qfalse);
 
@@ -2402,6 +2537,9 @@ Init_ossl_ssl(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L2537
     /*
      * An Array of extra X509 certificates to be added to the certificate
      * chain.
+     *
+     * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
+     * It is recommended to use #add_certificate instead.
      */
     rb_attr(cSSLContext, rb_intern("extra_chain_cert"), 1, 1, Qfalse);
 
@@ -2561,6 +2699,10 @@ Init_ossl_ssl(void) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_ssl.c#L2699
     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);
+#ifdef SSL_MODE_SEND_FALLBACK_SCSV
+    rb_define_method(cSSLContext, "enable_fallback_scsv", ossl_sslctx_enable_fallback_scsv, 0);
+#endif
+    rb_define_method(cSSLContext, "add_certificate", ossl_sslctx_add_certificate, -1);
 
     rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);
     rb_define_alias(cSSLContext, "freeze", "setup");
Index: ext/openssl/ossl_pkey.c
===================================================================
--- ext/openssl/ossl_pkey.c	(revision 60906)
+++ ext/openssl/ossl_pkey.c	(revision 60907)
@@ -163,8 +163,8 @@ ossl_pkey_new_from_data(int argc, VALUE https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_pkey.c#L163
     return ossl_pkey_new(pkey);
 }
 
-static void
-pkey_check_public_key(EVP_PKEY *pkey)
+void
+ossl_pkey_check_public_key(const EVP_PKEY *pkey)
 {
     void *ptr;
     const BIGNUM *n, *e, *pubkey;
@@ -172,7 +172,8 @@ pkey_check_public_key(EVP_PKEY *pkey) https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_pkey.c#L172
     if (EVP_PKEY_missing_parameters(pkey))
 	ossl_raise(ePKeyError, "parameters missing");
 
-    ptr = EVP_PKEY_get0(pkey);
+    /* OpenSSL < 1.1.0 takes non-const pointer */
+    ptr = EVP_PKEY_get0((EVP_PKEY *)pkey);
     switch (EVP_PKEY_base_id(pkey)) {
       case EVP_PKEY_RSA:
 	RSA_get0_key(ptr, &n, &e, NULL);
@@ -352,7 +353,7 @@ ossl_pkey_verify(VALUE self, VALUE diges https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_pkey.c#L353
     int siglen, result;
 
     GetPKey(self, pkey);
-    pkey_check_public_key(pkey);
+    ossl_pkey_check_public_key(pkey);
     md = ossl_evp_get_digestbyname(digest);
     StringValue(sig);
     siglen = RSTRING_LENINT(sig);
Index: ext/openssl/ossl_pkey.h
===================================================================
--- ext/openssl/ossl_pkey.h	(revision 60906)
+++ ext/openssl/ossl_pkey.h	(revision 60907)
@@ -44,6 +44,7 @@ int ossl_generate_cb_2(int p, int n, BN_ https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl_pkey.h#L44
 void ossl_generate_cb_stop(void *ptr);
 
 VALUE ossl_pkey_new(EVP_PKEY *);
+void ossl_pkey_check_public_key(const EVP_PKEY *);
 EVP_PKEY *GetPKeyPtr(VALUE);
 EVP_PKEY *DupPKeyPtr(VALUE);
 EVP_PKEY *GetPrivPKeyPtr(VALUE);
Index: ext/openssl/openssl_missing.h
===================================================================
--- ext/openssl/openssl_missing.h	(revision 60906)
+++ ext/openssl/openssl_missing.h	(revision 60907)
@@ -209,6 +209,10 @@ IMPL_PKEY_GETTER(EC_KEY, ec) https://github.com/ruby/ruby/blob/trunk/ext/openssl/openssl_missing.h#L209
 #  define X509_get0_notAfter(x) X509_get_notAfter(x)
 #  define X509_CRL_get0_lastUpdate(x) X509_CRL_get_lastUpdate(x)
 #  define X509_CRL_get0_nextUpdate(x) X509_CRL_get_nextUpdate(x)
+#  define X509_set1_notBefore(x, t) X509_set_notBefore(x, t)
+#  define X509_set1_notAfter(x, t) X509_set_notAfter(x, t)
+#  define X509_CRL_set1_lastUpdate(x, t) X509_CRL_set_lastUpdate(x, t)
+#  define X509_CRL_set1_nextUpdate(x, t) X509_CRL_set_nextUpdate(x, t)
 #endif
 
 #if !defined(HAVE_SSL_SESSION_GET_PROTOCOL_VERSION)
Index: test/openssl/test_x509attr.rb
===================================================================
--- test/openssl/test_x509attr.rb	(revision 60906)
+++ test/openssl/test_x509attr.rb	(revision 60907)
@@ -62,6 +62,23 @@ class OpenSSL::TestX509Attribute < OpenS https://github.com/ruby/ruby/blob/trunk/test/openssl/test_x509attr.rb#L62
     attr = OpenSSL::X509::Attribute.new("challengePassword", val)
     assert_equal(attr.to_der, attr.dup.to_der)
   end
+
+  def test_eq
+    val1 = OpenSSL::ASN1::Set([
+      OpenSSL::ASN1::UTF8String("abc123")
+    ])
+    attr1 = OpenSSL::X509::Attribute.new("challengePassword", val1)
+    attr2 = OpenSSL::X509::Attribute.new("challengePassword", val1)
+    ef = OpenSSL::X509::ExtensionFactory.new
+    val2 = OpenSSL::ASN1::Set.new([OpenSSL::ASN1::Sequence.new([
+      ef.create_extension("keyUsage", "keyCertSign", true)
+    ])])
+    attr3 = OpenSSL::X509::Attribute.new("extReq", val2)
+
+    assert_equal false, attr1 == 12345
+    assert_equal true, attr1 == attr2
+    assert_equal false, attr1 == attr3
+  end
 end
 
 end
Index: test/openssl/test_ssl.rb
===================================================================
--- test/openssl/test_ssl.rb	(revision 60906)
+++ test/openssl/test_ssl.rb	(revision 60907)
@@ -54,6 +54,87 @@ class OpenSSL::TestSSL < OpenSSL::SSLTes https://github.com/ruby/ruby/blob/trunk/test/openssl/test_ssl.rb#L54
     }
   end
 
+  def test_add_certificate
+    ctx_proc = -> ctx {
+      # Unset values set by start_server
+      ctx.cert = ctx.key = ctx.extra_chain_cert = nil
+      ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA
+    }
+    start_server(ctx_proc: ctx_proc) do |port|
+      server_connect(port) { |ssl|
+        assert_equal @svr_cert.subject, ssl.peer_cert.subject
+        assert_equal [@svr_cert.subject, @ca_cert.subject],
+          ssl.peer_cert_chain.map(&:subject)
+      }
+    end
+  end
+
+  def test_add_certificate_multiple_certs
+    pend "EC is not supported" unless defined?(OpenSSL::PKey::EC)
+    pend "TLS 1.2 is not supported" unless tls12_supported?
+
+    # SSL_CTX_set0_chain() is needed for setting multiple certificate chains
+    add0_chain_supported = openssl?(1, 0, 2)
+
+    if add0_chain_supported
+      ca2_key = Fixtures.pkey("rsa1024")
+      ca2_exts = [
+        ["basicConstraints", "CA:TRUE", true],
+        ["keyUsage", "cRLSign, keyCertSign", true],
+      ]
+      ca2_dn = OpenSSL::X509::Name.parse_rfc2253("CN=CA2")
+      ca2_cert = issue_cert(ca2_dn, ca2_key, 123, ca2_exts, nil, nil)
+    else
+      # Use the same CA as @svr_cert
+      ca2_key = @ca_key; ca2_cert = @ca_cert
+    end
+
+    ecdsa_key = Fixtures.pkey("p256")
+    exts = [
+      ["keyUsage", "digitalSignature", false],
+    ]
+    ecdsa_dn = OpenSSL::X509::Name.parse_rfc2253("CN=localhost2")
+    ecdsa_cert = issue_cert(ecdsa_dn, ecdsa_key, 456, exts, ca2_cert, ca2_key)
+
+    if !add0_chain_supported
+      # Testing the warning emitted when 'extra' chain is replaced
+      tctx = OpenSSL::SSL::SSLContext.new
+      tctx.add_certificate(@svr_cert, @svr_key, [@ca_cert])
+      assert_warning(/set0_chain/) {
+        tctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert])
+      }
+    end
+
+    ctx_proc = -> ctx {
+      # Unset values set by start_server
+      ctx.cert = ctx.key = ctx.extra_chain_cert = nil
+      ctx.ecdh_curves = "P-256" unless openssl?(1, 0, 2)
+      ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA
+      EnvUtil.suppress_warning do # !add0_chain_supported
+        ctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert])
+      end
+    }
+    start_server(ctx_proc: ctx_proc) do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.max_version = :TLS1_2 # TODO: We need this to force certificate type
+      ctx.ciphers = "aECDSA"
+      server_connect(port, ctx) { |ssl|
+        assert_equal ecdsa_cert.subject, ssl.peer_cert.subject
+        assert_equal [ecdsa_cert.subject, ca2_cert.subject],
+          ssl.peer_cert_chain.map(&:subject)
+      }
+
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.max_version = :TLS1_2
+      ctx.ciphers = "aRSA"
+      server_connect(port, ctx) { |ssl|
+        assert_equal @svr_cert.subject, ssl.peer_cert.subject
+        assert_equal [@svr_cert.subject, @ca_cert.subject],
+          ssl.peer_cert_chain.map(&:subject)
+      }
+    end
+  end
+
   def test_sysread_and_syswrite
     start_server { |port|
       server_connect(port) { |ssl|
@@ -1222,6 +1303,59 @@ end https://github.com/ruby/ruby/blob/trunk/test/openssl/test_ssl.rb#L1303
     end
   end
 
+  def test_fallback_scsv
+    pend "Fallback SCSV is not supported" unless OpenSSL::SSL::SSLContext.method_defined?( :enable_fallback_scsv)
+
+    start_server do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+      # Here is OK
+      # TLS1.2 supported and this is what we ask the first time
+      server_connect(port, ctx)
+    end
+
+    ctx_proc = proc { |ctx|
+      ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
+    }
+    start_server(ctx_proc: ctx_proc) do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.enable_fallback_scsv
+      ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
+      # Here is OK too
+      # TLS1.2 not supported, fallback to TLS1.1 and signaling the fallback
+      # Server doesn't support better, so connection OK
+      server_connect(port, ctx)
+    end
+
+    # Here is not OK
+    # TLS1.2 is supported, fallback to TLS1.1 (downgrade attack) and signaling the fallback
+    # Server support better, so refuse the connection
+    sock1, sock2 = socketpair
+    begin
+      ctx1 = OpenSSL::SSL::SSLContext.new
+      s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
+
+      ctx2 = OpenSSL::SSL::SSLContext.new
+      ctx2.enable_fallback_scsv
+      ctx2.max_version = OpenSSL::SSL::TLS1_1_VERSION
+      s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2)
+      t = Thread.new {
+        assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) {
+          s2.connect
+        }
+      }
+
+      assert_raise_with_message(OpenSSL::SSL::SSLError, /inappropriate fallback/) {
+        s1.accept
+      }
+
+      assert t.join
+    ensure
+      sock1.close
+      sock2.close
+    end
+  end
+
   def test_dh_callback
     pend "TLS 1.2 is not supported" unless tls12_supported?
 
@@ -1336,11 +1470,24 @@ end https://github.com/ruby/ruby/blob/trunk/test/openssl/test_ssl.rb#L1470
       return
     end
     assert_equal(1, ctx.security_level)
-    # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = Fixtures.pkey("dsa512") }
-  (... truncated)

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

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