diff options
author | rhe <rhe@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-11-25 14:12:08 +0000 |
---|---|---|
committer | rhe <rhe@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-11-25 14:12:08 +0000 |
commit | a55320b0933cbcfd05d427fe3712bc519c713deb (patch) | |
tree | edcad6f717f8d75a70bdcfbf4ac47ea21e5c2fe0 | |
parent | 55953e374db2193ffb6ca84e2cb83d480ecd68ac (diff) | |
download | ruby-a55320b0933cbcfd05d427fe3712bc519c713deb.tar.gz |
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
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60907 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
32 files changed, 1020 insertions, 116 deletions
diff --git a/ext/openssl/History.md b/ext/openssl/History.md index 4e12682c64..f83f523dcc 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -1,18 +1,21 @@ -Version 2.1.0.beta1 +Version 2.1.0.beta2 =================== Notable changes --------------- -* Support for OpenSSL versions before 1.0.1 is removed. +* Support for OpenSSL versions before 1.0.1 and LibreSSL versions before 2.5 + is removed. [[GitHub #86]](https://github.com/ruby/openssl/pull/86) * OpenSSL::BN#negative?, #+@, and #-@ are added. * OpenSSL::SSL::SSLSocket#connect raises a more informative exception when certificate verification fails. [[GitHub #99]](https://github.com/ruby/openssl/pull/99) -* OpenSSL::KDF module is newly added. Support for scrypt is added. +* OpenSSL::KDF module is newly added. In addition to PBKDF2-HMAC that has moved + from OpenSSL::PKCS5, scrypt and HKDF are supported. [[GitHub #109]](https://github.com/ruby/openssl/pull/109) -* OpenSSL.fips_mode is added. We have had the setter, but not the getter. + [[GitHub #173]](https://github.com/ruby/openssl/pull/173) +* OpenSSL.fips_mode is added. We had the setter, but not the getter. [[GitHub #125]](https://github.com/ruby/openssl/pull/125) * OpenSSL::OCSP::Request#signed? is added. * OpenSSL::ASN1 handles the indefinite length form better. OpenSSL::ASN1.decode @@ -22,11 +25,31 @@ Notable changes * OpenSSL::X509::Name#add_entry now accepts two additional keyword arguments 'loc' and 'set'. [[GitHub #94]](https://github.com/ruby/openssl/issues/94) -* OpenSSL::SSL::SSLContext#min_version= and #max_version= are added. +* OpenSSL::SSL::SSLContext#min_version= and #max_version= are added to replace + #ssl_version= that was built on top of the deprecated OpenSSL C API. Use of + that method and the constant OpenSSL::SSL::SSLContext::METHODS is now + deprecated. [[GitHub #142]](https://github.com/ruby/openssl/pull/142) * OpenSSL::X509::Name#to_utf8 is added. [[GitHub #26]](https://github.com/ruby/openssl/issues/26) [[GitHub #143]](https://github.com/ruby/openssl/pull/143) +* OpenSSL::X509::{Extension,Attribute,Certificate,CRL,Revoked,Request} can be + compared with == operator. + [[GitHub #161]](https://github.com/ruby/openssl/pull/161) +* TLS Fallback Signaling Cipher Suite Value (SCSV) support is added. + [[GitHub #165]](https://github.com/ruby/openssl/pull/165) +* Build failure with OpenSSL 1.1 built with no-deprecated is fixed. + [[GitHub #160]](https://github.com/ruby/openssl/pull/160) +* OpenSSL::Buffering#write accepts an arbitrary number of arguments. + [[Feature #9323]](https://bugs.ruby-lang.org/issues/9323) + [[GitHub #162]](https://github.com/ruby/openssl/pull/162) +* OpenSSL::PKey::RSA#sign_pss and #verify_pss are added. They perform RSA-PSS + signature and verification. + [[GitHub #75]](https://github.com/ruby/openssl/issues/75) + [[GitHub #76]](https://github.com/ruby/openssl/pull/76) + [[GitHub #169]](https://github.com/ruby/openssl/pull/169) +* OpenSSL::SSL::SSLContext#add_certificate is added. + [[GitHub #167]](https://github.com/ruby/openssl/pull/167) Version 2.0.6 @@ -201,7 +224,7 @@ Notable changes - A new option 'verify_hostname' is added to OpenSSL::SSL::SSLContext. When it is enabled, and the SNI hostname is also set, the hostname verification on the server certificate is automatically performed. It is now enabled by - OpenSSL::SSL::Context#set_params. + OpenSSL::SSL::SSLContext#set_params. [[GH ruby/openssl#60]](https://github.com/ruby/openssl/pull/60) Removals diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb index f328c0007d..935f61f0ef 100644 --- a/ext/openssl/lib/openssl/buffering.rb +++ b/ext/openssl/lib/openssl/buffering.rb @@ -340,9 +340,10 @@ module OpenSSL::Buffering # converted using +.to_s+ method. Returns the number of bytes written. def write(*s) - s = s.size == 1 ? s[0] : s.join("") - do_write(s) - s.bytesize + s.inject(0) do |written, str| + do_write(str) + written + str.bytesize + end end ## diff --git a/ext/openssl/lib/openssl/x509.rb b/ext/openssl/lib/openssl/x509.rb index 6d31b98c68..98358f90da 100644 --- a/ext/openssl/lib/openssl/x509.rb +++ b/ext/openssl/lib/openssl/x509.rb @@ -41,6 +41,11 @@ module OpenSSL end class Extension + def ==(other) + return false unless Extension === other + to_der == other.to_der + end + def to_s # "oid = critical, value" str = self.oid str << " = " @@ -160,6 +165,13 @@ module OpenSSL end end + class Attribute + def ==(other) + return false unless Attribute === other + to_der == other.to_der + end + end + class StoreContext def cleanup warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE @@ -178,5 +190,26 @@ module OpenSSL } end end + + class CRL + def ==(other) + return false unless CRL === other + to_der == other.to_der + end + end + + class Revoked + def ==(other) + return false unless Revoked === other + to_der == other.to_der + end + end + + class Request + def ==(other) + return false unless Request === other + to_der == other.to_der + end + end end end diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index a8b21a561b..b60b5358d9 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,16 +1,16 @@ # -*- encoding: utf-8 -*- -# stub: openssl 2.1.0.beta1 ruby lib +# stub: openssl 2.1.0.beta2 ruby lib # stub: ext/openssl/extconf.rb Gem::Specification.new do |s| s.name = "openssl".freeze - s.version = "2.1.0.beta1" + s.version = "2.1.0.beta2" s.required_rubygems_version = Gem::Requirement.new("> 1.3.1".freeze) if s.respond_to? :required_rubygems_version= s.metadata = { "msys2_mingw_dependencies" => "openssl" } if s.respond_to? :metadata= s.require_paths = ["lib".freeze] s.authors = ["Martin Bosslet".freeze, "SHIBATA Hiroshi".freeze, "Zachary Scott".freeze, "Kazuki Yamaguchi".freeze] - s.date = "2017-09-24" + s.date = "2017-11-25" s.description = "It wraps the OpenSSL library.".freeze s.email = ["ruby-core@ruby-lang.org".freeze] s.extensions = ["ext/openssl/extconf.rb".freeze] @@ -20,7 +20,7 @@ Gem::Specification.new do |s| s.licenses = ["Ruby".freeze] s.rdoc_options = ["--main".freeze, "README.md".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze) - s.rubygems_version = "2.6.13".freeze + s.rubygems_version = "2.7.2".freeze s.summary = "OpenSSL provides SSL, TLS and general purpose cryptography.".freeze if s.respond_to? :specification_version then diff --git a/ext/openssl/openssl_missing.h b/ext/openssl/openssl_missing.h index cc31f6ace7..debd25adea 100644 --- a/ext/openssl/openssl_missing.h +++ b/ext/openssl/openssl_missing.h @@ -209,6 +209,10 @@ IMPL_PKEY_GETTER(EC_KEY, ec) # 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) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 93ecc7d414..245385e7da 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -1109,25 +1109,14 @@ Init_openssl(void) /* * Init all digests, ciphers */ - /* CRYPTO_malloc_init(); */ - /* ENGINE_load_builtin_engines(); */ +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 + if (!OPENSSL_init_ssl(0, NULL)) + rb_raise(rb_eRuntimeError, "OPENSSL_init_ssl"); +#else OpenSSL_add_ssl_algorithms(); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); SSL_load_error_strings(); - - /* - * FIXME: - * On unload do: - */ -#if 0 - CONF_modules_unload(1); - destroy_ui_method(); - EVP_cleanup(); - ENGINE_cleanup(); - CRYPTO_cleanup_all_ex_data(); - ERR_remove_state(0); - ERR_free_strings(); #endif /* @@ -1149,7 +1138,11 @@ Init_openssl(void) /* * Version of OpenSSL the ruby OpenSSL extension is running with */ +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 + rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION))); +#else rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION))); +#endif /* * Version number of OpenSSL the ruby OpenSSL extension was built with diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index f08889b22e..5a15839cb4 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -35,6 +35,11 @@ #if !defined(OPENSSL_NO_OCSP) # include <openssl/ocsp.h> #endif +#include <openssl/bn.h> +#include <openssl/rsa.h> +#include <openssl/dsa.h> +#include <openssl/evp.h> +#include <openssl/dh.h> /* * Common Module diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c index d337d50961..4666ce6c2f 100644 --- a/ext/openssl/ossl_bn.c +++ b/ext/openssl/ossl_bn.c @@ -979,20 +979,20 @@ static VALUE ossl_bn_hash(VALUE self) { BIGNUM *bn; - VALUE hash; + VALUE tmp, hash; unsigned char *buf; int len; GetBN(self, bn); len = BN_num_bytes(bn); - buf = xmalloc(len); + buf = ALLOCV(tmp, len); if (BN_bn2bin(bn, buf) != len) { - xfree(buf); - ossl_raise(eBNError, NULL); + ALLOCV_END(tmp); + ossl_raise(eBNError, "BN_bn2bin"); } hash = ST2FIX(rb_memhash(buf, len)); - xfree(buf); + ALLOCV_END(tmp); return hash; } diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index bfa76c1aab..3038a76687 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -508,7 +508,7 @@ ossl_cipher_set_iv(VALUE self, VALUE iv) StringValue(iv); GetCipher(self, ctx); - if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) + if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) iv_len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); if (!iv_len) iv_len = EVP_CIPHER_CTX_iv_length(ctx); @@ -535,7 +535,7 @@ ossl_cipher_is_authenticated(VALUE self) GetCipher(self, ctx); - return (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; + return (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) ? Qtrue : Qfalse; } /* @@ -569,6 +569,8 @@ ossl_cipher_set_auth_data(VALUE self, VALUE data) in_len = RSTRING_LEN(data); GetCipher(self, ctx); + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) + ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!ossl_cipher_update_long(ctx, NULL, &out_len, in, in_len)) ossl_raise(eCipherError, "couldn't set additional authenticated data"); @@ -606,7 +608,7 @@ ossl_cipher_get_auth_tag(int argc, VALUE *argv, VALUE self) GetCipher(self, ctx); - if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "authentication tag not supported by this cipher"); ret = rb_str_new(NULL, tag_len); @@ -641,7 +643,7 @@ ossl_cipher_set_auth_tag(VALUE self, VALUE vtag) tag_len = RSTRING_LENINT(vtag); GetCipher(self, ctx); - if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "authentication tag not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, tag)) @@ -668,7 +670,7 @@ ossl_cipher_set_auth_tag_len(VALUE self, VALUE vlen) EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); - if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "AEAD not supported by this cipher"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len, NULL)) @@ -695,7 +697,7 @@ ossl_cipher_set_iv_length(VALUE self, VALUE iv_length) EVP_CIPHER_CTX *ctx; GetCipher(self, ctx); - if (!(EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER)) + if (!(EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER)) ossl_raise(eCipherError, "cipher does not support AEAD"); if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, len, NULL)) @@ -786,7 +788,7 @@ ossl_cipher_iv_length(VALUE self) int len = 0; GetCipher(self, ctx); - if (EVP_CIPHER_CTX_flags(ctx) & EVP_CIPH_FLAG_AEAD_CIPHER) + if (EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(ctx)) & EVP_CIPH_FLAG_AEAD_CIPHER) len = (int)(VALUE)EVP_CIPHER_CTX_get_app_data(ctx); if (!len) len = EVP_CIPHER_CTX_iv_length(ctx); diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c index d69b5dcac5..5ca0d4ca3f 100644 --- a/ext/openssl/ossl_engine.c +++ b/ext/openssl/ossl_engine.c @@ -46,13 +46,25 @@ VALUE eEngineError; /* * Private */ -#define OSSL_ENGINE_LOAD_IF_MATCH(x) \ +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 +#define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \ do{\ - if(!strcmp(#x, RSTRING_PTR(name))){\ - ENGINE_load_##x();\ + if(!strcmp(#engine_name, RSTRING_PTR(name))){\ + if (OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_##x, NULL))\ + return Qtrue;\ + else\ + ossl_raise(eEngineError, "OPENSSL_init_crypto"); \ + }\ +}while(0) +#else +#define OSSL_ENGINE_LOAD_IF_MATCH(engine_name, x) \ +do{\ + if(!strcmp(#engine_name, RSTRING_PTR(name))){\ + ENGINE_load_##engine_name();\ return Qtrue;\ }\ }while(0) +#endif static void ossl_engine_free(void *engine) @@ -94,55 +106,55 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) StringValueCStr(name); #ifndef OPENSSL_NO_STATIC_ENGINE #if HAVE_ENGINE_LOAD_DYNAMIC - OSSL_ENGINE_LOAD_IF_MATCH(dynamic); + OSSL_ENGINE_LOAD_IF_MATCH(dynamic, DYNAMIC); #endif #if HAVE_ENGINE_LOAD_4758CCA - OSSL_ENGINE_LOAD_IF_MATCH(4758cca); + OSSL_ENGINE_LOAD_IF_MATCH(4758cca, 4758CCA); #endif #if HAVE_ENGINE_LOAD_AEP - OSSL_ENGINE_LOAD_IF_MATCH(aep); + OSSL_ENGINE_LOAD_IF_MATCH(aep, AEP); #endif #if HAVE_ENGINE_LOAD_ATALLA - OSSL_ENGINE_LOAD_IF_MATCH(atalla); + OSSL_ENGINE_LOAD_IF_MATCH(atalla, ATALLA); #endif #if HAVE_ENGINE_LOAD_CHIL - OSSL_ENGINE_LOAD_IF_MATCH(chil); + OSSL_ENGINE_LOAD_IF_MATCH(chil, CHIL); #endif #if HAVE_ENGINE_LOAD_CSWIFT - OSSL_ENGINE_LOAD_IF_MATCH(cswift); + OSSL_ENGINE_LOAD_IF_MATCH(cswift, CSWIFT); #endif #if HAVE_ENGINE_LOAD_NURON - OSSL_ENGINE_LOAD_IF_MATCH(nuron); + OSSL_ENGINE_LOAD_IF_MATCH(nuron, NURON); #endif #if HAVE_ENGINE_LOAD_SUREWARE - OSSL_ENGINE_LOAD_IF_MATCH(sureware); + OSSL_ENGINE_LOAD_IF_MATCH(sureware, SUREWARE); #endif #if HAVE_ENGINE_LOAD_UBSEC - OSSL_ENGINE_LOAD_IF_MATCH(ubsec); + OSSL_ENGINE_LOAD_IF_MATCH(ubsec, UBSEC); #endif #if HAVE_ENGINE_LOAD_PADLOCK - OSSL_ENGINE_LOAD_IF_MATCH(padlock); + OSSL_ENGINE_LOAD_IF_MATCH(padlock, PADLOCK); #endif #if HAVE_ENGINE_LOAD_CAPI - OSSL_ENGINE_LOAD_IF_MATCH(capi); + OSSL_ENGINE_LOAD_IF_MATCH(capi, CAPI); #endif #if HAVE_ENGINE_LOAD_GMP - OSSL_ENGINE_LOAD_IF_MATCH(gmp); + OSSL_ENGINE_LOAD_IF_MATCH(gmp, GMP); #endif #if HAVE_ENGINE_LOAD_GOST - OSSL_ENGINE_LOAD_IF_MATCH(gost); + OSSL_ENGINE_LOAD_IF_MATCH(gost, GOST); #endif #if HAVE_ENGINE_LOAD_CRYPTODEV - OSSL_ENGINE_LOAD_IF_MATCH(cryptodev); + OSSL_ENGINE_LOAD_IF_MATCH(cryptodev, CRYPTODEV); #endif #if HAVE_ENGINE_LOAD_AESNI - OSSL_ENGINE_LOAD_IF_MATCH(aesni); + OSSL_ENGINE_LOAD_IF_MATCH(aesni, AESNI); #endif #endif #ifdef HAVE_ENGINE_LOAD_OPENBSD_DEV_CRYPTO - OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto); + OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto, OPENBSD_DEV_CRYPTO); #endif - OSSL_ENGINE_LOAD_IF_MATCH(openssl); + OSSL_ENGINE_LOAD_IF_MATCH(openssl, OPENSSL); rb_warning("no such builtin loader for `%"PRIsVALUE"'", name); return Qnil; #endif /* HAVE_ENGINE_LOAD_BUILTIN_ENGINES */ @@ -160,7 +172,9 @@ ossl_engine_s_load(int argc, VALUE *argv, VALUE klass) static VALUE ossl_engine_s_cleanup(VALUE self) { +#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000 ENGINE_cleanup(); +#endif return Qnil; } diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c index 9fa42e174a..ee124718b5 100644 --- a/ext/openssl/ossl_kdf.c +++ b/ext/openssl/ossl_kdf.c @@ -3,6 +3,9 @@ * Copyright (C) 2007, 2017 Ruby/OpenSSL Project Authors */ #include "ossl.h" +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) +# include <openssl/kdf.h> +#endif static VALUE mKDF, eKDF; @@ -138,6 +141,97 @@ kdf_scrypt(int argc, VALUE *argv, VALUE self) } #endif +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) +/* + * call-seq: + * KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String + * + * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in + * {RFC 5869}[https://tools.ietf.org/html/rfc5869]. + * + * New in OpenSSL 1.1.0. + * + * === Parameters + * _ikm_:: + * The input keying material. + * _salt_:: + * The salt. + * _info_:: + * The context and application specific information. + * _length_:: + * The output length in octets. Must be <= <tt>255 * HashLen</tt>, where + * HashLen is the length of the hash function output in octets. + * _hash_:: + * The hash function. + */ +static VALUE +kdf_hkdf(int argc, VALUE *argv, VALUE self) +{ + VALUE ikm, salt, info, opts, kwargs[4], str; + static ID kwargs_ids[4]; + int saltlen, ikmlen, infolen; + size_t len; + const EVP_MD *md; + EVP_PKEY_CTX *pctx; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt"); + kwargs_ids[1] = rb_intern_const("info"); + kwargs_ids[2] = rb_intern_const("length"); + kwargs_ids[3] = rb_intern_const("hash"); + } + rb_scan_args(argc, argv, "1:", &ikm, &opts); + rb_get_kwargs(opts, kwargs_ids, 4, 0, kwargs); + + StringValue(ikm); + ikmlen = RSTRING_LENINT(ikm); + salt = StringValue(kwargs[0]); + saltlen = RSTRING_LENINT(salt); + info = StringValue(kwargs[1]); + infolen = RSTRING_LENINT(info); + len = (size_t)NUM2LONG(kwargs[2]); + if (len > LONG_MAX) + rb_raise(rb_eArgError, "length must be non-negative"); + md = ossl_evp_get_digestbyname(kwargs[3]); + + str = rb_str_new(NULL, (long)len); + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (!pctx) + ossl_raise(eKDF, "EVP_PKEY_CTX_new_id"); + if (EVP_PKEY_derive_init(pctx) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_derive_init"); + } + if (EVP_PKEY_CTX_set_hkdf_md(pctx, md) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_md"); + } + if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, (unsigned char *)RSTRING_PTR(salt), + saltlen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_salt"); + } + if (EVP_PKEY_CTX_set1_hkdf_key(pctx, (unsigned char *)RSTRING_PTR(ikm), + ikmlen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_key"); + } + if (EVP_PKEY_CTX_add1_hkdf_info(pctx, (unsigned char *)RSTRING_PTR(info), + infolen) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_CTX_set_hkdf_info"); + } + if (EVP_PKEY_derive(pctx, (unsigned char *)RSTRING_PTR(str), &len) <= 0) { + EVP_PKEY_CTX_free(pctx); + ossl_raise(eKDF, "EVP_PKEY_derive"); + } + rb_str_set_len(str, (long)len); + EVP_PKEY_CTX_free(pctx); + + return str; +} +#endif + void Init_ossl_kdf(void) { @@ -162,6 +256,7 @@ Init_ossl_kdf(void) * * PKCS #5 PBKDF2 (Password-Based Key Derivation Function 2) in * combination with HMAC * * scrypt + * * HKDF * * == Examples * === Generating a 128 bit key for a Cipher (e.g. AES) @@ -218,4 +313,7 @@ Init_ossl_kdf(void) #if defined(HAVE_EVP_PBE_SCRYPT) rb_define_module_function(mKDF, "scrypt", kdf_scrypt, -1); #endif +#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER) + rb_define_module_function(mKDF, "hkdf", kdf_hkdf, -1); +#endif } diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c index f17b9509c6..6f61e61bf5 100644 --- a/ext/openssl/ossl_ns_spki.c +++ b/ext/openssl/ossl_ns_spki.c @@ -208,12 +208,13 @@ static VALUE ossl_spki_set_public_key(VALUE self, VALUE key) { NETSCAPE_SPKI *spki; + EVP_PKEY *pkey; GetSPKI(self, spki); - if (!NETSCAPE_SPKI_set_pubkey(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */ - ossl_raise(eSPKIError, NULL); - } - + pkey = GetPKeyPtr(key); + ossl_pkey_check_public_key(pkey); + if (!NETSCAPE_SPKI_set_pubkey(spki, pkey)) + ossl_raise(eSPKIError, "NETSCAPE_SPKI_set_pubkey"); return key; } @@ -307,17 +308,20 @@ static VALUE ossl_spki_verify(VALUE self, VALUE key) { NETSCAPE_SPKI *spki; + EVP_PKEY *pkey; GetSPKI(self, spki); - switch (NETSCAPE_SPKI_verify(spki, GetPKeyPtr(key))) { /* NO NEED TO DUP */ - case 0: + pkey = GetPKeyPtr(key); + ossl_pkey_check_public_key(pkey); + switch (NETSCAPE_SPKI_verify(spki, pkey)) { + case 0: + ossl_clear_error(); return Qfalse; - case 1: + case 1: return Qtrue; - default: - ossl_raise(eSPKIError, NULL); + default: + ossl_raise(eSPKIError, "NETSCAPE_SPKI_verify"); } - return Qnil; /* dummy */ } /* Document-class: OpenSSL::Netscape::SPKI diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 23e2115409..2b96ece575 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -163,8 +163,8 @@ ossl_pkey_new_from_data(int argc, VALUE *argv, VALUE self) 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) 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 digest, VALUE sig, VALUE data) 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); diff --git a/ext/openssl/ossl_pkey.h b/ext/openssl/ossl_pkey.h index a87472ad09..2b17bf5368 100644 --- a/ext/openssl/ossl_pkey.h +++ b/ext/openssl/ossl_pkey.h @@ -44,6 +44,7 @@ int ossl_generate_cb_2(int p, int n, BN_GENCB *cb); 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); diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c index 26397bd021..4800fb2710 100644 --- a/ext/openssl/ossl_pkey_rsa.c +++ b/ext/openssl/ossl_pkey_rsa.c @@ -538,6 +538,196 @@ ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self) /* * call-seq: + * rsa.sign_pss(digest, data, salt_length:, mgf1_hash:) -> String + * + * Signs _data_ using the Probabilistic Signature Scheme (RSA-PSS) and returns + * the calculated signature. + * + * RSAError will be raised if an error occurs. + * + * See #verify_pss for the verification operation. + * + * === Parameters + * _digest_:: + * A String containing the message digest algorithm name. + * _data_:: + * A String. The data to be signed. + * _salt_length_:: + * The length in octets of the salt. Two special values are reserved: + * +:digest+ means the digest length, and +:max+ means the maximum possible + * length for the combination of the private key and the selected message + * digest algorithm. + * _mgf1_hash_:: + * The hash algorithm used in MGF1 (the currently supported mask generation + * function (MGF)). + * + * === Example + * data = "Sign me!" + * pkey = OpenSSL::PKey::RSA.new(2048) + * signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256") + * pub_key = pkey.public_key + * puts pub_key.verify_pss("SHA256", signature, data, + * salt_length: :auto, mgf1_hash: "SHA256") # => true + */ +static VALUE +ossl_rsa_sign_pss(int argc, VALUE *argv, VALUE self) +{ + VALUE digest, data, options, kwargs[2], signature; + static ID kwargs_ids[2]; + EVP_PKEY *pkey; + EVP_PKEY_CTX *pkey_ctx; + const EVP_MD *md, *mgf1md; + EVP_MD_CTX *md_ctx; + size_t buf_len; + int salt_len; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); + } + rb_scan_args(argc, argv, "2:", &digest, &data, &options); + rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); + if (kwargs[0] == ID2SYM(rb_intern("max"))) + salt_len = -2; /* RSA_PSS_SALTLEN_MAX_SIGN */ + else if (kwargs[0] == ID2SYM(rb_intern("digest"))) + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + else + salt_len = NUM2INT(kwargs[0]); + mgf1md = ossl_evp_get_digestbyname(kwargs[1]); + + pkey = GetPrivPKeyPtr(self); + buf_len = EVP_PKEY_size(pkey); + md = ossl_evp_get_digestbyname(digest); + StringValue(data); + signature = rb_str_new(NULL, (long)buf_len); + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) + goto err; + + if (EVP_DigestSignInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) + goto err; + + if (EVP_DigestSignUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) + goto err; + + if (EVP_DigestSignFinal(md_ctx, (unsigned char *)RSTRING_PTR(signature), &buf_len) != 1) + goto err; + + rb_str_set_len(signature, (long)buf_len); + + EVP_MD_CTX_free(md_ctx); + return signature; + + err: + EVP_MD_CTX_free(md_ctx); + ossl_raise(eRSAError, NULL); +} + +/* + * call-seq: + * rsa.verify_pss(digest, signature, data, salt_length:, mgf1_hash:) -> true | false + * + * Verifies _data_ using the Probabilistic Signature Scheme (RSA-PSS). + * + * The return value is +true+ if the signature is valid, +false+ otherwise. + * RSAError will be raised if an error occurs. + * + * See #sign_pss for the signing operation and an example code. + * + * === Parameters + * _digest_:: + * A String containing the message digest algorithm name. + * _data_:: + * A String. The data to be signed. + * _salt_length_:: + * The length in octets of the salt. Two special values are reserved: + * +:digest+ means the digest length, and +:auto+ means automatically + * determining the length based on the signature. + * _mgf1_hash_:: + * The hash algorithm used in MGF1. + */ +static VALUE +ossl_rsa_verify_pss(int argc, VALUE *argv, VALUE self) +{ + VALUE digest, signature, data, options, kwargs[2]; + static ID kwargs_ids[2]; + EVP_PKEY *pkey; + EVP_PKEY_CTX *pkey_ctx; + const EVP_MD *md, *mgf1md; + EVP_MD_CTX *md_ctx; + int result, salt_len; + + if (!kwargs_ids[0]) { + kwargs_ids[0] = rb_intern_const("salt_length"); + kwargs_ids[1] = rb_intern_const("mgf1_hash"); + } + rb_scan_args(argc, argv, "3:", &digest, &signature, &data, &options); + rb_get_kwargs(options, kwargs_ids, 2, 0, kwargs); + if (kwargs[0] == ID2SYM(rb_intern("auto"))) + salt_len = -2; /* RSA_PSS_SALTLEN_AUTO */ + else if (kwargs[0] == ID2SYM(rb_intern("digest"))) + salt_len = -1; /* RSA_PSS_SALTLEN_DIGEST */ + else + salt_len = NUM2INT(kwargs[0]); + mgf1md = ossl_evp_get_digestbyname(kwargs[1]); + + GetPKey(self, pkey); + md = ossl_evp_get_digestbyname(digest); + StringValue(signature); + StringValue(data); + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) + goto err; + + if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, md, NULL, pkey) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len) != 1) + goto err; + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1md) != 1) + goto err; + + if (EVP_DigestVerifyUpdate(md_ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1) + goto err; + + result = EVP_DigestVerifyFinal(md_ctx, + (unsigned char *)RSTRING_PTR(signature), + RSTRING_LEN(signature)); + + switch (result) { + case 0: + ossl_clear_error(); + EVP_MD_CTX_free(md_ctx); + return Qfalse; + case 1: + EVP_MD_CTX_free(md_ctx); + return Qtrue; + default: + goto err; + } + + err: + EVP_MD_CTX_free(md_ctx); + ossl_raise(eRSAError, NULL); +} + +/* + * call-seq: * rsa.params => hash * * THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!! @@ -731,6 +921,8 @@ Init_ossl_rsa(void) rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, -1); rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, -1); rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, -1); + rb_define_method(cRSA, "sign_pss", ossl_rsa_sign_pss, -1); + rb_define_method(cRSA, "verify_pss", ossl_rsa_verify_pss, -1); DEF_OSSL_PKEY_BN(cRSA, rsa, n); DEF_OSSL_PKEY_BN(cRSA, rsa, e); diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 32ae2df557..8cdb9737b5 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -32,7 +32,7 @@ VALUE cSSLSocket; 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) 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_callback_args *args) 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) 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) 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) 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) 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) 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) 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 self, VALUE value) 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) 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) /* * 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) /* * 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) 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"); diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c index 003a9c1949..40542c4a78 100644 --- a/ext/openssl/ossl_x509cert.c +++ b/ext/openssl/ossl_x509cert.c @@ -440,7 +440,7 @@ ossl_x509_set_not_before(VALUE self, VALUE time) GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); - if (!X509_set_notBefore(x509, asn1time)) { + if (!X509_set1_notBefore(x509, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CertError, "X509_set_notBefore"); } @@ -479,7 +479,7 @@ ossl_x509_set_not_after(VALUE self, VALUE time) GetX509(self, x509); asn1time = ossl_x509_time_adjust(NULL, time); - if (!X509_set_notAfter(x509, asn1time)) { + if (!X509_set1_notAfter(x509, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CertError, "X509_set_notAfter"); } @@ -508,18 +508,19 @@ ossl_x509_get_public_key(VALUE self) /* * call-seq: - * cert.public_key = key => key + * cert.public_key = key */ static VALUE ossl_x509_set_public_key(VALUE self, VALUE key) { X509 *x509; + EVP_PKEY *pkey; GetX509(self, x509); - if (!X509_set_pubkey(x509, GetPKeyPtr(key))) { /* DUPs pkey */ - ossl_raise(eX509CertError, NULL); - } - + pkey = GetPKeyPtr(key); + ossl_pkey_check_public_key(pkey); + if (!X509_set_pubkey(x509, pkey)) + ossl_raise(eX509CertError, "X509_set_pubkey"); return key; } @@ -557,9 +558,9 @@ ossl_x509_verify(VALUE self, VALUE key) X509 *x509; EVP_PKEY *pkey; - pkey = GetPKeyPtr(key); /* NO NEED TO DUP */ GetX509(self, x509); - + pkey = GetPKeyPtr(key); + ossl_pkey_check_public_key(pkey); switch (X509_verify(x509, pkey)) { case 1: return Qtrue; @@ -684,6 +685,26 @@ ossl_x509_inspect(VALUE self) } /* + * call-seq: + * cert1 == cert2 -> true | false + * + * Compares the two certificates. Note that this takes into account all fields, + * not just the issuer name and the serial number. + */ +static VALUE +ossl_x509_eq(VALUE self, VALUE other) +{ + X509 *a, *b; + + GetX509(self, a); + if (!rb_obj_is_kind_of(other, cX509Cert)) + return Qfalse; + GetX509(other, b); + + return !X509_cmp(a, b) ? Qtrue : Qfalse; +} + +/* * INIT */ void @@ -821,4 +842,5 @@ Init_ossl_x509cert(void) rb_define_method(cX509Cert, "extensions=", ossl_x509_set_extensions, 1); rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1); rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0); + rb_define_method(cX509Cert, "==", ossl_x509_eq, 1); } diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c index 5ecd7ea0b2..b0badf45c4 100644 --- a/ext/openssl/ossl_x509crl.c +++ b/ext/openssl/ossl_x509crl.c @@ -226,7 +226,7 @@ ossl_x509crl_set_last_update(VALUE self, VALUE time) GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); - if (!X509_CRL_set_lastUpdate(crl, asn1time)) { + if (!X509_CRL_set1_lastUpdate(crl, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CRLError, "X509_CRL_set_lastUpdate"); } @@ -257,7 +257,7 @@ ossl_x509crl_set_next_update(VALUE self, VALUE time) GetX509CRL(self, crl); asn1time = ossl_x509_time_adjust(NULL, time); - if (!X509_CRL_set_nextUpdate(crl, asn1time)) { + if (!X509_CRL_set1_nextUpdate(crl, asn1time)) { ASN1_TIME_free(asn1time); ossl_raise(eX509CRLError, "X509_CRL_set_nextUpdate"); } @@ -359,9 +359,12 @@ static VALUE ossl_x509crl_verify(VALUE self, VALUE key) { X509_CRL *crl; + EVP_PKEY *pkey; GetX509CRL(self, crl); - switch (X509_CRL_verify(crl, GetPKeyPtr(key))) { + pkey = GetPKeyPtr(key); + ossl_pkey_check_public_key(pkey); + switch (X509_CRL_verify(crl, pkey)) { case 1: return Qtrue; case 0: diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c index 9f20dba311..2c20042a92 100644 --- a/ext/openssl/ossl_x509req.c +++ b/ext/openssl/ossl_x509req.c @@ -293,11 +293,10 @@ ossl_x509req_set_public_key(VALUE self, VALUE key) EVP_PKEY *pkey; GetX509Req(self, req); - pkey = GetPKeyPtr(key); /* NO NEED TO DUP */ - if (!X509_REQ_set_pubkey(req, pkey)) { - ossl_raise(eX509ReqError, NULL); - } - + pkey = GetPKeyPtr(key); + ossl_pkey_check_public_key(pkey); + if (!X509_REQ_set_pubkey(req, pkey)) + ossl_raise(eX509ReqError, "X509_REQ_set_pubkey"); return key; } @@ -328,7 +327,8 @@ ossl_x509req_verify(VALUE self, VALUE key) EVP_PKEY *pkey; GetX509Req(self, req); - pkey = GetPKeyPtr(key); /* NO NEED TO DUP */ + pkey = GetPKeyPtr(key); + ossl_pkey_check_public_key(pkey); switch (X509_REQ_verify(req, pkey)) { case 1: return Qtrue; diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c index 85489efdb2..5fe6853430 100644 --- a/ext/openssl/ossl_x509revoked.c +++ b/ext/openssl/ossl_x509revoked.c @@ -249,6 +249,26 @@ ossl_x509revoked_add_extension(VALUE self, VALUE ext) return ext; } +static VALUE +ossl_x509revoked_to_der(VALUE self) +{ + X509_REVOKED *rev; + VALUE str; + int len; + unsigned char *p; + + GetX509Rev(self, rev); + len = i2d_X509_REVOKED(rev, NULL); + if (len <= 0) + ossl_raise(eX509RevError, "i2d_X509_REVOKED"); + str = rb_str_new(NULL, len); + p = (unsigned char *)RSTRING_PTR(str); + if (i2d_X509_REVOKED(rev, &p) <= 0) + ossl_raise(eX509RevError, "i2d_X509_REVOKED"); + ossl_str_adjust(str, p); + return str; +} + /* * INIT */ @@ -276,4 +296,5 @@ Init_ossl_x509revoked(void) rb_define_method(cX509Rev, "extensions", ossl_x509revoked_get_extensions, 0); rb_define_method(cX509Rev, "extensions=", ossl_x509revoked_set_extensions, 1); rb_define_method(cX509Rev, "add_extension", ossl_x509revoked_add_extension, 1); + rb_define_method(cX509Rev, "to_der", ossl_x509revoked_to_der, 0); } diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index d87dedf73c..56061741bc 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -295,6 +295,13 @@ class OpenSSL::TestCipher < OpenSSL::TestCase assert_equal tag1, tag2 end + def test_non_aead_cipher_set_auth_data + assert_raise(OpenSSL::Cipher::CipherError) { + cipher = OpenSSL::Cipher.new("aes-128-cfb").encrypt + cipher.auth_data = "123" + } + end + private def new_encryptor(algo, **kwargs) diff --git a/test/openssl/test_kdf.rb b/test/openssl/test_kdf.rb index d91fa3cf5d..5e1db80c5f 100644 --- a/test/openssl/test_kdf.rb +++ b/test/openssl/test_kdf.rb @@ -131,6 +131,48 @@ class OpenSSL::TestKDF < OpenSSL::TestCase assert_equal(expected, OpenSSL::KDF.scrypt(pass, salt: salt, N: n, r: r, p: p, length: dklen)) end + def test_hkdf_rfc5869_test_case_1 + pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0 + hash = "sha256" + ikm = B("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") + salt = B("000102030405060708090a0b0c") + info = B("f0f1f2f3f4f5f6f7f8f9") + l = 42 + + okm = B("3cb25f25faacd57a90434f64d0362f2a" \ + "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" \ + "34007208d5b887185865") + assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash)) + end + + def test_hkdf_rfc5869_test_case_3 + pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0 + hash = "sha256" + ikm = B("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b") + salt = B("") + info = B("") + l = 42 + + okm = B("8da4e775a563c18f715f802a063c5a31" \ + "b8a11f5c5ee1879ec3454e5f3c738d2d" \ + "9d201395faa4b61a96c8") + assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash)) + end + + def test_hkdf_rfc5869_test_case_4 + pend "HKDF is not implemented" unless OpenSSL::KDF.respond_to?(:hkdf) # OpenSSL >= 1.1.0 + hash = "sha1" + ikm = B("0b0b0b0b0b0b0b0b0b0b0b") + salt = B("000102030405060708090a0b0c") + info = B("f0f1f2f3f4f5f6f7f8f9") + l = 42 + + okm = B("085a01ea1b10f36933068b56efa5ad81" \ + "a4f14b822f5b091568a9cdd4f155fda2" \ + "c22e422478d305f3f896") + assert_equal(okm, OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: l, hash: hash)) + end + private def B(ary) diff --git a/test/openssl/test_ocsp.rb b/test/openssl/test_ocsp.rb index 9d48496de5..50ad6c31f5 100644 --- a/test/openssl/test_ocsp.rb +++ b/test/openssl/test_ocsp.rb @@ -122,12 +122,12 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase assert_equal true, req.verify([@cert], store, OpenSSL::OCSP::NOINTERN) ret = req.verify([@cert], store) - if ret || openssl?(1, 0, 2) || libressl?(2, 4, 2) + if ret || openssl?(1, 0, 2) assert_equal true, ret else # RT2560; OCSP_request_verify() does not find signer cert from 'certs' when # OCSP_NOINTERN is not specified. - # fixed by OpenSSL 1.0.1j, 1.0.2 and LibreSSL 2.4.2 + # fixed by OpenSSL 1.0.1j, 1.0.2 pend "RT2560: ocsp_req_find_signer" end @@ -262,11 +262,6 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, nil, []) bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, Time.now + 100, nil, nil) - if bres.responses[2].check_validity # thisUpdate is in future; must fail - # LibreSSL bug; skip for now - pend "OCSP_check_validity() is broken" - end - single1 = bres.responses[0] assert_equal false, single1.check_validity assert_equal false, single1.check_validity(30) @@ -275,6 +270,8 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase assert_equal true, single2.check_validity assert_equal true, single2.check_validity(0, 500) assert_equal false, single2.check_validity(0, 200) + single3 = bres.responses[2] + assert_equal false, single3.check_validity end def test_response diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb index 55b62321b8..29d5c9bbb1 100644 --- a/test/openssl/test_pair.rb +++ b/test/openssl/test_pair.rb @@ -362,6 +362,15 @@ module OpenSSL::TestPairM } end + def test_write_multiple_arguments + ssl_pair {|s1, s2| + str1 = "foo"; str2 = "bar" + assert_equal 6, s1.write(str1, str2) + s1.close + assert_equal "foobar", s2.read + } + end + def test_partial_tls_record_read_nonblock ssl_pair { |s1, s2| # the beginning of a TLS record diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb index 49ab379251..d9bea1a622 100644 --- a/test/openssl/test_pkey_rsa.rb +++ b/test/openssl/test_pkey_rsa.rb @@ -113,6 +113,39 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase } end + def test_sign_verify_pss + key = Fixtures.pkey("rsa1024") + data = "Sign me!" + invalid_data = "Sign me?" + + signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA1") + assert_equal 128, signature.bytesize + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1") + assert_equal false, + key.verify_pss("SHA256", signature, invalid_data, salt_length: 20, mgf1_hash: "SHA1") + + signature = key.sign_pss("SHA256", data, salt_length: :digest, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: 32, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1") + assert_equal false, + key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1") + + signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: 94, mgf1_hash: "SHA1") + assert_equal true, + key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1") + + assert_raise(OpenSSL::PKey::RSAError) { + key.sign_pss("SHA256", data, salt_length: 95, mgf1_hash: "SHA1") + } + end + def test_RSAPrivateKey rsa1024 = Fixtures.pkey("rsa1024") asn1 = OpenSSL::ASN1::Sequence([ diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 4f3df9dd1d..b3f3143f4f 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -54,6 +54,87 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase } 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 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 return end assert_equal(1, ctx.security_level) - # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = Fixtures.pkey("dsa512") } - # ctx.key = Fixtures.pkey("rsa1024") - # ctx.security_level = 2 - # assert_raise(OpenSSL::SSL::SSLError) { ctx.key = Fixtures.pkey("rsa1024") } - pend "FIXME: SSLContext#key= currently does not raise because SSL_CTX_use_certificate() is delayed" + + dsa512 = Fixtures.pkey("dsa512") + dsa512_cert = issue_cert(@svr, dsa512, 50, [], @ca_cert, @ca_key) + rsa1024 = Fixtures.pkey("rsa1024") + rsa1024_cert = issue_cert(@svr, rsa1024, 51, [], @ca_cert, @ca_key) + + assert_raise(OpenSSL::SSL::SSLError) { + # 512 bit DSA key is rejected because it offers < 80 bits of security + ctx.add_certificate(dsa512_cert, dsa512) + } + assert_nothing_raised { + ctx.add_certificate(rsa1024_cert, rsa1024) + } + ctx.security_level = 2 + assert_raise(OpenSSL::SSL::SSLError) { + # < 112 bits of security + ctx.add_certificate(rsa1024_cert, rsa1024) + } end def test_dup diff --git a/test/openssl/test_x509attr.rb b/test/openssl/test_x509attr.rb index 108162f407..c6c48e86ab 100644 --- a/test/openssl/test_x509attr.rb +++ b/test/openssl/test_x509attr.rb @@ -62,6 +62,23 @@ class OpenSSL::TestX509Attribute < OpenSSL::TestCase 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 diff --git a/test/openssl/test_x509cert.rb b/test/openssl/test_x509cert.rb index 289994d17b..40a5b0ad74 100644 --- a/test/openssl/test_x509cert.rb +++ b/test/openssl/test_x509cert.rb @@ -169,6 +169,26 @@ class OpenSSL::TestX509Certificate < OpenSSL::TestCase } end + def test_eq + now = Time.now + cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil, + not_before: now, not_after: now + 3600) + cert1 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024, + not_before: now, not_after: now + 3600) + cert2 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024, + not_before: now, not_after: now + 3600) + cert3 = issue_cert(@ee1, @rsa2048, 3, [], cacert, @rsa1024, + not_before: now, not_after: now + 3600) + cert4 = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024, + digest: "sha512", not_before: now, not_after: now + 3600) + + assert_equal false, cert1 == 12345 + assert_equal true, cert1 == cert2 + assert_equal false, cert1 == cert3 + assert_equal false, cert1 == cert4 + assert_equal false, cert3 == cert4 + end + private def certificate_error_returns_false diff --git a/test/openssl/test_x509crl.rb b/test/openssl/test_x509crl.rb index 1914a651cf..03fdf64dd4 100644 --- a/test/openssl/test_x509crl.rb +++ b/test/openssl/test_x509crl.rb @@ -197,6 +197,58 @@ class OpenSSL::TestX509CRL < OpenSSL::TestCase assert_equal(false, crl.verify(@dsa512)) end + def test_revoked_to_der + # revokedCertificates SEQUENCE OF SEQUENCE { + # userCertificate CertificateSerialNumber, + # revocationDate Time, + # crlEntryExtensions Extensions OPTIONAL + # -- if present, version MUST be v2 + # } OPTIONAL, + + now = Time.utc(2000, 1, 1) + rev1 = OpenSSL::X509::Revoked.new + rev1.serial = 123 + rev1.time = now + ext = OpenSSL::X509::Extension.new("CRLReason", OpenSSL::ASN1::Enumerated(1)) + rev1.extensions = [ext] + asn1 = OpenSSL::ASN1::Sequence([ + OpenSSL::ASN1::Integer(123), + OpenSSL::ASN1::UTCTime(now), + OpenSSL::ASN1::Sequence([ext.to_der]) + ]) + + assert_equal asn1.to_der, rev1.to_der + end + + def test_eq + now = Time.now + + cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil) + crl1 = issue_crl([], 1, now, now + 3600, [], cacert, @rsa1024, "sha256") + rev1 = OpenSSL::X509::Revoked.new.tap { |rev| + rev.serial = 1 + rev.time = now + } + crl1.add_revoked(rev1) + crl2 = OpenSSL::X509::CRL.new(crl1.to_der) + + # CRL + assert_equal false, crl1 == 12345 + assert_equal true, crl1 == crl2 + rev2 = OpenSSL::X509::Revoked.new.tap { |rev| + rev.serial = 2 + rev.time = now + } + crl2.add_revoked(rev2) + assert_equal false, crl1 == crl2 + + # Revoked + assert_equal false, rev1 == 12345 + assert_equal true, rev1 == crl2.revoked[0] + assert_equal false, rev1 == crl2.revoked[1] + assert_equal true, rev2 == crl2.revoked[1] + end + private def crl_error_returns_false diff --git a/test/openssl/test_x509ext.rb b/test/openssl/test_x509ext.rb index f384a25e8c..91ce202fec 100644 --- a/test/openssl/test_x509ext.rb +++ b/test/openssl/test_x509ext.rb @@ -75,6 +75,17 @@ class OpenSSL::TestX509Extension < OpenSSL::TestCase assert_equal(@basic_constraints.to_der, ext.to_der) assert_equal(ext.to_der, ext.dup.to_der) end + + def test_eq + ext1 = OpenSSL::X509::Extension.new(@basic_constraints.to_der) + ef = OpenSSL::X509::ExtensionFactory.new + ext2 = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2") + ext3 = ef.create_extension("basicConstraints", "critical, CA:TRUE") + + assert_equal false, ext1 == 12345 + assert_equal true, ext1 == ext2 + assert_equal false, ext1 == ext3 + end end end diff --git a/test/openssl/test_x509req.rb b/test/openssl/test_x509req.rb index a21d45da19..2c447ccdd5 100644 --- a/test/openssl/test_x509req.rb +++ b/test/openssl/test_x509req.rb @@ -141,6 +141,16 @@ class OpenSSL::TestX509Request < OpenSSL::TestCase assert_equal(req.to_der, req.dup.to_der) end + def test_eq + req1 = issue_csr(0, @dn, @rsa1024, "sha1") + req2 = issue_csr(0, @dn, @rsa1024, "sha1") + req3 = issue_csr(0, @dn, @rsa1024, "sha256") + + assert_equal false, req1 == 12345 + assert_equal true, req1 == req2 + assert_equal false, req1 == req3 + end + private def request_error_returns_false diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb index f59d53b0d1..a359949061 100644 --- a/test/openssl/utils.rb +++ b/test/openssl/utils.rb @@ -67,7 +67,7 @@ module OpenSSL::TestUtils cert.serial = serial cert.subject = dn cert.issuer = issuer.subject - cert.public_key = key.public_key + cert.public_key = key now = Time.now cert.not_before = not_before || now - 3600 cert.not_after = not_after || now + 3600 |