From 91d04f991f8b9910efea7bbe5aecb0fea2bbd5fa Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 24 Oct 2021 17:50:18 +0900 Subject: [PATCH 1/8] cipher: update test_ciphers Do not attempt to actually use all algorithms. Not all algorithms listed in OpenSSL::Cipher.ciphers are always available. --- test/openssl/test_cipher.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index 6d18c0c8..b5fdf0b3 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -135,14 +135,11 @@ def test_ctr_if_exists end def test_ciphers - OpenSSL::Cipher.ciphers.each{|name| - next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name - begin - assert_kind_of(OpenSSL::Cipher, OpenSSL::Cipher.new(name)) - rescue OpenSSL::Cipher::CipherError => e - raise unless /wrap/ =~ name and /wrap mode not allowed/ =~ e.message - end - } + ciphers = OpenSSL::Cipher.ciphers + assert_kind_of Array, ciphers + assert_include ciphers, "aes-128-cbc" + assert_include ciphers, "aes128" # alias of aes-128-cbc + assert_include ciphers, "aes-128-gcm" end def test_AES From 6a60c7b2e7b6afe8b8c98d864ef2740094d86e1d Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sat, 11 Dec 2021 16:27:42 +0900 Subject: [PATCH 2/8] hmac: fix wrong usage of EVP_DigestSignFinal() According to the manpage, the "siglen" parameter must be initialized beforehand. --- ext/openssl/ossl_hmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c index f89ff2f9..bfe3a74b 100644 --- a/ext/openssl/ossl_hmac.c +++ b/ext/openssl/ossl_hmac.c @@ -175,7 +175,7 @@ static VALUE ossl_hmac_digest(VALUE self) { EVP_MD_CTX *ctx; - size_t buf_len; + size_t buf_len = EVP_MAX_MD_SIZE; VALUE ret; GetHMAC(self, ctx); @@ -200,7 +200,7 @@ ossl_hmac_hexdigest(VALUE self) { EVP_MD_CTX *ctx; unsigned char buf[EVP_MAX_MD_SIZE]; - size_t buf_len; + size_t buf_len = EVP_MAX_MD_SIZE; VALUE ret; GetHMAC(self, ctx); From 46995816392a79d037df5550b2fb226652c06f42 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sat, 11 Dec 2021 16:30:30 +0900 Subject: [PATCH 3/8] hmac: skip test_dup on OpenSSL 3.0 for now EVP_MD_CTX_copy() doesn't seem to work as intended on HMAC EVP_MD_CTX on OpenSSL 3.0.0 and causes a double free. I haven't found the root problem yet, but let's skip the test case for now. --- test/openssl/test_hmac.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/openssl/test_hmac.rb b/test/openssl/test_hmac.rb index 2f53a813..47cb3718 100644 --- a/test/openssl/test_hmac.rb +++ b/test/openssl/test_hmac.rb @@ -19,6 +19,7 @@ def test_hmac end def test_dup + pend "HMAC#initialize_copy is currently broken on OpenSSL 3.0.0" if openssl?(3, 0, 0) h1 = OpenSSL::HMAC.new("KEY", "MD5") h1.update("DATA") h = h1.dup From 69a27d8de4bd291cb4eb21a4d715b197e7da5a06 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 15 Apr 2021 00:51:58 +0900 Subject: [PATCH 4/8] engine: disable OpenSSL::Engine on OpenSSL 3.0 The entire ENGINE API is deprecated in OpenSSL 3.0 in favor of the new "Provider" concept. OpenSSL::Engine will not be defined when compiled with OpenSSL 3.0. We would need a way to interact with providers from Ruby programs, but since the concept is completely different from the ENGINE API, it will not be through the current OpenSSL::Engine interface. --- ext/openssl/openssl_missing.c | 3 --- ext/openssl/ossl.h | 8 +++++--- ext/openssl/ossl_engine.c | 3 ++- ext/openssl/ossl_pkey.c | 4 ++++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ext/openssl/openssl_missing.c b/ext/openssl/openssl_missing.c index 8b93cba6..4415703d 100644 --- a/ext/openssl/openssl_missing.c +++ b/ext/openssl/openssl_missing.c @@ -10,9 +10,6 @@ #include RUBY_EXTCONF_H #include /* memcpy() */ -#if !defined(OPENSSL_NO_ENGINE) -# include -#endif #include #include "openssl_missing.h" diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h index 3a0ab1e5..4b512689 100644 --- a/ext/openssl/ossl.h +++ b/ext/openssl/ossl.h @@ -18,6 +18,7 @@ #include #include #include + #include #include #include @@ -30,9 +31,6 @@ #include #endif #include -#if !defined(OPENSSL_NO_ENGINE) -# include -#endif #if !defined(OPENSSL_NO_OCSP) # include #endif @@ -54,6 +52,10 @@ (LIBRESSL_VERSION_NUMBER >= (maj << 28) | (min << 20) | (pat << 12)) #endif +#if !defined(OPENSSL_NO_ENGINE) && !OSSL_OPENSSL_PREREQ(3, 0, 0) +# define OSSL_USE_ENGINE +#endif + /* * Common Module */ diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c index 661a1368..1abde7f7 100644 --- a/ext/openssl/ossl_engine.c +++ b/ext/openssl/ossl_engine.c @@ -9,7 +9,8 @@ */ #include "ossl.h" -#if !defined(OPENSSL_NO_ENGINE) +#ifdef OSSL_USE_ENGINE +# include #define NewEngine(klass) \ TypedData_Wrap_Struct((klass), &ossl_engine_type, 0) diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 7030be3c..94760d32 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -9,6 +9,10 @@ */ #include "ossl.h" +#ifdef OSSL_USE_ENGINE +# include +#endif + /* * Classes */ From b1ee2f23b28c2d0b14fd9b4b9fef13e870370746 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 17 Nov 2021 11:39:06 +0900 Subject: [PATCH 5/8] ssl: add constants for new SSL_OP_* flags Add all SSL_OP_* constants defined in OpenSSL 3.0.0 which are not specific to DTLS. --- ext/openssl/ossl_ssl.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 3b425ca7..9a0682a7 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -2941,13 +2941,28 @@ Init_ossl_ssl(void) rb_define_const(mSSL, "VERIFY_CLIENT_ONCE", INT2NUM(SSL_VERIFY_CLIENT_ONCE)); rb_define_const(mSSL, "OP_ALL", ULONG2NUM(SSL_OP_ALL)); +#ifdef SSL_OP_CLEANSE_PLAINTEXT /* OpenSSL 3.0 */ + rb_define_const(mSSL, "OP_CLEANSE_PLAINTEXT", ULONG2NUM(SSL_OP_CLEANSE_PLAINTEXT)); +#endif rb_define_const(mSSL, "OP_LEGACY_SERVER_CONNECT", ULONG2NUM(SSL_OP_LEGACY_SERVER_CONNECT)); +#ifdef SSL_OP_ENABLE_KTLS /* OpenSSL 3.0 */ + rb_define_const(mSSL, "OP_ENABLE_KTLS", ULONG2NUM(SSL_OP_ENABLE_KTLS)); +#endif #ifdef SSL_OP_TLSEXT_PADDING /* OpenSSL 1.0.1h and OpenSSL 1.0.2 */ rb_define_const(mSSL, "OP_TLSEXT_PADDING", ULONG2NUM(SSL_OP_TLSEXT_PADDING)); #endif #ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG /* OpenSSL 1.0.1f and OpenSSL 1.0.2 */ rb_define_const(mSSL, "OP_SAFARI_ECDHE_ECDSA_BUG", ULONG2NUM(SSL_OP_SAFARI_ECDHE_ECDSA_BUG)); #endif +#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF /* OpenSSL 3.0 */ + rb_define_const(mSSL, "OP_IGNORE_UNEXPECTED_EOF", ULONG2NUM(SSL_OP_IGNORE_UNEXPECTED_EOF)); +#endif +#ifdef SSL_OP_ALLOW_CLIENT_RENEGOTIATION /* OpenSSL 3.0 */ + rb_define_const(mSSL, "OP_ALLOW_CLIENT_RENEGOTIATION", ULONG2NUM(SSL_OP_ALLOW_CLIENT_RENEGOTIATION)); +#endif +#ifdef SSL_OP_DISABLE_TLSEXT_CA_NAMES /* OpenSSL 3.0 */ + rb_define_const(mSSL, "OP_DISABLE_TLSEXT_CA_NAMES", ULONG2NUM(SSL_OP_DISABLE_TLSEXT_CA_NAMES)); +#endif #ifdef SSL_OP_ALLOW_NO_DHE_KEX /* OpenSSL 1.1.1 */ rb_define_const(mSSL, "OP_ALLOW_NO_DHE_KEX", ULONG2NUM(SSL_OP_ALLOW_NO_DHE_KEX)); #endif @@ -2959,13 +2974,15 @@ Init_ossl_ssl(void) #ifdef SSL_OP_NO_ENCRYPT_THEN_MAC /* OpenSSL 1.1.1 */ rb_define_const(mSSL, "OP_NO_ENCRYPT_THEN_MAC", ULONG2NUM(SSL_OP_NO_ENCRYPT_THEN_MAC)); #endif - rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE)); - rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG)); -#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */ - rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION)); +#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT /* OpenSSL 1.1.1 */ + rb_define_const(mSSL, "OP_ENABLE_MIDDLEBOX_COMPAT", ULONG2NUM(SSL_OP_ENABLE_MIDDLEBOX_COMPAT)); +#endif +#ifdef SSL_OP_PRIORITIZE_CHACHA /* OpenSSL 1.1.1 */ + rb_define_const(mSSL, "OP_PRIORITIZE_CHACHA", ULONG2NUM(SSL_OP_PRIORITIZE_CHACHA)); +#endif +#ifdef SSL_OP_NO_ANTI_REPLAY /* OpenSSL 1.1.1 */ + rb_define_const(mSSL, "OP_NO_ANTI_REPLAY", ULONG2NUM(SSL_OP_NO_ANTI_REPLAY)); #endif - rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG)); - rb_define_const(mSSL, "OP_NO_SSLv3", ULONG2NUM(SSL_OP_NO_SSLv3)); rb_define_const(mSSL, "OP_NO_TLSv1", ULONG2NUM(SSL_OP_NO_TLSv1)); rb_define_const(mSSL, "OP_NO_TLSv1_1", ULONG2NUM(SSL_OP_NO_TLSv1_1)); @@ -2973,6 +2990,12 @@ Init_ossl_ssl(void) #ifdef SSL_OP_NO_TLSv1_3 /* OpenSSL 1.1.1 */ rb_define_const(mSSL, "OP_NO_TLSv1_3", ULONG2NUM(SSL_OP_NO_TLSv1_3)); #endif + rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE)); + rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG)); +#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */ + rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION)); +#endif + rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG)); /* SSL_OP_* flags for DTLS */ #if 0 From e168df0f3570709bfb38e9a39838bd0a7e78164c Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 12 Dec 2021 00:47:35 +0900 Subject: [PATCH 6/8] ssl: update test_options_disable_versions Use the combination of TLS 1.2 and TLS 1.3 instead of TLS 1.1 and TLS 1.2 so that will the test case will be run on latest platforms. --- test/openssl/test_ssl.rb | 75 +++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 22691292..2abade06 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -1180,46 +1180,51 @@ def test_minmax_version end def test_options_disable_versions - # Note: Use of these OP_* flags has been deprecated since OpenSSL 1.1.0. + # It's recommended to use SSLContext#{min,max}_version= instead in real + # applications. The purpose of this test case is to check that SSL options + # are properly propagated to OpenSSL library. supported = check_supported_protocol_versions + if !defined?(OpenSSL::SSL::TLS1_3_VERSION) || + !supported.include?(OpenSSL::SSL::TLS1_2_VERSION) || + !supported.include?(OpenSSL::SSL::TLS1_3_VERSION) || + !defined?(OpenSSL::SSL::OP_NO_TLSv1_3) # LibreSSL < 3.4 + pend "this test case requires both TLS 1.2 and TLS 1.3 to be supported " \ + "and enabled by default" + end - if supported.include?(OpenSSL::SSL::TLS1_1_VERSION) && - supported.include?(OpenSSL::SSL::TLS1_2_VERSION) - # Server disables ~ TLS 1.1 - ctx_proc = proc { |ctx| - ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 | - OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1 - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| - # Client only supports TLS 1.1 - ctx1 = OpenSSL::SSL::SSLContext.new - ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_1_VERSION - assert_handshake_error { server_connect(port, ctx1) { } } + # Server disables TLS 1.2 and earlier + ctx_proc = proc { |ctx| + ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 | + OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1 | + OpenSSL::SSL::OP_NO_TLSv1_2 + } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + # Client only supports TLS 1.2 + ctx1 = OpenSSL::SSL::SSLContext.new + ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION + assert_handshake_error { server_connect(port, ctx1) { } } - # Client only supports TLS 1.2 - ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.min_version = ctx2.max_version = OpenSSL::SSL::TLS1_2_VERSION - assert_nothing_raised { server_connect(port, ctx2) { } } - } + # Client only supports TLS 1.3 + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.min_version = ctx2.max_version = OpenSSL::SSL::TLS1_3_VERSION + assert_nothing_raised { server_connect(port, ctx2) { } } + } - # Server only supports TLS 1.1 - ctx_proc = proc { |ctx| - ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION - } - start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| - # Client disables TLS 1.1 - ctx1 = OpenSSL::SSL::SSLContext.new - ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_1 - assert_handshake_error { server_connect(port, ctx1) { } } + # Server only supports TLS 1.2 + ctx_proc = proc { |ctx| + ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION + } + start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port| + # Client doesn't support TLS 1.2 + ctx1 = OpenSSL::SSL::SSLContext.new + ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_2 + assert_handshake_error { server_connect(port, ctx1) { } } - # Client disables TLS 1.2 - ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.options |= OpenSSL::SSL::OP_NO_TLSv1_2 - assert_nothing_raised { server_connect(port, ctx2) { } } - } - else - pend "TLS 1.1 and TLS 1.2 must be supported; skipping" - end + # Client supports TLS 1.2 by default + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.options |= OpenSSL::SSL::OP_NO_TLSv1_3 + assert_nothing_raised { server_connect(port, ctx2) { } } + } end def test_ssl_methods_constant From ccdb6f7bfa5f988a07beecedbf2b6205b6ab8492 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sat, 20 Mar 2021 23:16:41 +0900 Subject: [PATCH 7/8] pkey: assume a pkey always has public key components on OpenSSL 3.0 OpenSSL 3.0's EVP_PKEY_get0() returns NULL for provider-backed pkeys. This causes segfault because it was supposed to never return NULL before. We can't check the existence of public key components in this way on OpenSSL 3.0. Let's just skip it for now. --- ext/openssl/ossl_pkey.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 94760d32..09d45d85 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -428,9 +428,19 @@ ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self) return pkey_generate(argc, argv, self, 0); } +/* + * TODO: There is no convenient way to check the presence of public key + * components on OpenSSL 3.0. But since keys are immutable on 3.0, pkeys without + * these should only be created by OpenSSL::PKey.generate_parameters or by + * parsing DER-/PEM-encoded string. We would need another flag for that. + */ void ossl_pkey_check_public_key(const EVP_PKEY *pkey) { +#if OSSL_OPENSSL_PREREQ(3, 0, 0) + if (EVP_PKEY_missing_parameters(pkey)) + ossl_raise(ePKeyError, "parameters missing"); +#else void *ptr; const BIGNUM *n, *e, *pubkey; @@ -466,6 +476,7 @@ ossl_pkey_check_public_key(const EVP_PKEY *pkey) return; } ossl_raise(ePKeyError, "public key missing"); +#endif } EVP_PKEY * From d6535d13d174cd87ae99f3e60e97f7a00e1474e5 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Mon, 12 Apr 2021 10:43:46 +0900 Subject: [PATCH 8/8] pkey: use EVP_PKEY_CTX_new_from_name() on OpenSSL 3.0 Replace EVP_PKEY_CTX_new_id() with the new EVP_PKEY_CTX_new_from_name() which takes the algorithm name in a string instead of in an NID. --- ext/openssl/ossl_pkey.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c index 09d45d85..2a4835a2 100644 --- a/ext/openssl/ossl_pkey.c +++ b/ext/openssl/ossl_pkey.c @@ -315,6 +315,11 @@ pkey_generate(int argc, VALUE *argv, VALUE self, int genparam) ossl_raise(ePKeyError, "EVP_PKEY_CTX_new"); } else { +#if OSSL_OPENSSL_PREREQ(3, 0, 0) + ctx = EVP_PKEY_CTX_new_from_name(NULL, StringValueCStr(alg), NULL); + if (!ctx) + ossl_raise(ePKeyError, "EVP_PKEY_CTX_new_from_name"); +#else const EVP_PKEY_ASN1_METHOD *ameth; ENGINE *tmpeng; int pkey_id; @@ -333,6 +338,7 @@ pkey_generate(int argc, VALUE *argv, VALUE self, int genparam) ctx = EVP_PKEY_CTX_new_id(pkey_id, NULL/* engine */); if (!ctx) ossl_raise(ePKeyError, "EVP_PKEY_CTX_new_id"); +#endif } if (genparam && EVP_PKEY_paramgen_init(ctx) <= 0) {