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/