From 729e1512dfad3d341ed5916c87a500bce378545b Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Thu, 14 May 2026 00:46:54 +0900 Subject: Merge openssl-4.0.1 The changes can be found at: https://github.com/ruby/openssl/compare/v4.0.0...v4.0.1 --- ext/openssl/History.md | 20 +++++++++ ext/openssl/lib/openssl/version.rb | 2 +- ext/openssl/openssl.gemspec | 2 +- ext/openssl/ossl_cipher.c | 13 +++--- ext/openssl/ossl_ocsp.c | 4 +- ext/openssl/ossl_pkcs7.c | 2 +- ext/openssl/ossl_pkey_ec.c | 2 +- ext/openssl/ossl_ssl.c | 88 ++++++++++++++++++++++++-------------- test/openssl/test_cipher.rb | 67 ++++++++++++++++------------- test/openssl/test_digest.rb | 28 +++++++----- test/openssl/test_fips.rb | 2 +- test/openssl/test_ocsp.rb | 29 +++++++++++++ test/openssl/test_pkcs12.rb | 38 ++++++++++++++-- test/openssl/test_pkey_ec.rb | 9 ++++ test/openssl/test_ssl.rb | 66 +++++++++++++++++++--------- 15 files changed, 263 insertions(+), 109 deletions(-) diff --git a/ext/openssl/History.md b/ext/openssl/History.md index 419237ff16..c78c7e4633 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -1,3 +1,23 @@ +Version 4.0.1 +============= + +Notable changes +--------------- + +* Add `sync_close` keyword argument to `OpenSSL::SSL::SSLSocket.new` as a + short-hand for setting `sync_close` attribute on the created `SSLSocket` + instance. + [[GitHub #955]](https://github.com/ruby/openssl/issues/955) + [[GitHub #996]](https://github.com/ruby/openssl/pull/996) + + +Bug fixes +--------- + +* Fix uninitialized variables in `OpenSSL::OCSP::BasicResponse#status`. + [[GitHub #1004]](https://github.com/ruby/openssl/pull/1004) + + Version 4.0.0 ============= diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb index 88570562e2..45c150be11 100644 --- a/ext/openssl/lib/openssl/version.rb +++ b/ext/openssl/lib/openssl/version.rb @@ -2,5 +2,5 @@ module OpenSSL # The version string of Ruby/OpenSSL. - VERSION = "4.0.0" + VERSION = "4.0.1" end diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index 7072d599d8..c594c6f177 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "4.0.0" + spec.version = "4.0.1" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{SSL/TLS and general-purpose cryptography for Ruby} diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c index db65e99888..f3cd247c8f 100644 --- a/ext/openssl/ossl_cipher.c +++ b/ext/openssl/ossl_cipher.c @@ -401,9 +401,9 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) } out_len = in_len + EVP_MAX_BLOCK_LENGTH; - if (NIL_P(str)) { - str = rb_str_new(0, out_len); - } else { + if (NIL_P(str)) + str = rb_str_buf_new(out_len); + else { StringValue(str); if ((long)rb_str_capacity(str) >= out_len) rb_str_modify(str); @@ -411,9 +411,9 @@ ossl_cipher_update(int argc, VALUE *argv, VALUE self) rb_str_modify_expand(str, out_len - RSTRING_LEN(str)); } - if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len)) - ossl_raise(eCipherError, NULL); - assert(out_len <= RSTRING_LEN(str)); + if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), + &out_len, in, in_len)) + ossl_raise(eCipherError, "EVP_CipherUpdate"); rb_str_set_len(str, out_len); return str; @@ -456,7 +456,6 @@ ossl_cipher_final(VALUE self) ossl_raise(eCipherError, "cipher final failed"); } } - assert(out_len <= RSTRING_LEN(str)); rb_str_set_len(str, out_len); return str; diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c index 93d8bc8567..ddb67fcf07 100644 --- a/ext/openssl/ossl_ocsp.c +++ b/ext/openssl/ossl_ocsp.c @@ -905,8 +905,8 @@ ossl_ocspbres_get_status(VALUE self) int count = OCSP_resp_count(bs); for (int i = 0; i < count; i++) { OCSP_SINGLERESP *single = OCSP_resp_get0(bs, i); - ASN1_TIME *revtime, *thisupd, *nextupd; - int reason; + ASN1_TIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; + int reason = -1; int status = OCSP_single_get0_status(single, &reason, &revtime, &thisupd, &nextupd); if (status < 0) diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c index 6e51fd42b9..ae0d35b723 100644 --- a/ext/openssl/ossl_pkcs7.c +++ b/ext/openssl/ossl_pkcs7.c @@ -1010,7 +1010,7 @@ static VALUE ossl_pkcs7si_get_signed_time(VALUE self) { PKCS7_SIGNER_INFO *p7si; - ASN1_TYPE *asn1obj; + const ASN1_TYPE *asn1obj; GetPKCS7si(self, p7si); diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c index bb19533edf..35f031819d 100644 --- a/ext/openssl/ossl_pkey_ec.c +++ b/ext/openssl/ossl_pkey_ec.c @@ -702,7 +702,7 @@ static VALUE ossl_ec_group_initialize(int argc, VALUE *argv, VALUE self) break; default: - ossl_raise(rb_eArgError, "wrong number of arguments"); + ossl_raise(rb_eArgError, "wrong number of arguments (given %d, expected 1 or 4)", argc); } ASSUME(group); diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 630d46e43f..c6dec32a9e 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -47,7 +47,7 @@ static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode, id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols, id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb, id_i_verify_hostname, id_i_keylog_cb, id_i_tmp_dh_callback; -static ID id_i_io, id_i_context, id_i_hostname; +static ID id_i_io, id_i_context, id_i_hostname, id_i_sync_close; static int ossl_ssl_ex_ptr_idx; static int ossl_sslctx_ex_ptr_idx; @@ -1590,32 +1590,31 @@ ossl_ssl_s_alloc(VALUE klass) } static VALUE -peer_ip_address(VALUE self) +peer_ip_address(VALUE io) { - VALUE remote_address = rb_funcall(rb_attr_get(self, id_i_io), rb_intern("remote_address"), 0); + VALUE remote_address = rb_funcall(io, rb_intern("remote_address"), 0); return rb_funcall(remote_address, rb_intern("inspect_sockaddr"), 0); } static VALUE -fallback_peer_ip_address(VALUE self, VALUE args) +fallback_peer_ip_address(VALUE self, VALUE exc) { return rb_str_new_cstr("(null)"); } static VALUE -peeraddr_ip_str(VALUE self) +peeraddr_ip_str(VALUE io) { - VALUE rb_mErrno = rb_const_get(rb_cObject, rb_intern("Errno")); - VALUE rb_eSystemCallError = rb_const_get(rb_mErrno, rb_intern("SystemCallError")); - - return rb_rescue2(peer_ip_address, self, fallback_peer_ip_address, (VALUE)0, rb_eSystemCallError, NULL); + return rb_rescue2(peer_ip_address, io, fallback_peer_ip_address, Qnil, + rb_eSystemCallError, (VALUE)0); } /* * call-seq: * SSLSocket.new(io) => aSSLSocket * SSLSocket.new(io, ctx) => aSSLSocket + * SSLSocket.new(io, ctx, sync_close:) => aSSLSocket * * Creates a new SSL socket from _io_ which must be a real IO object (not an * IO-like object that responds to read/write). @@ -1623,6 +1622,10 @@ peeraddr_ip_str(VALUE self) * If _ctx_ is provided the SSL Sockets initial params will be taken from * the context. * + * The optional _sync_close_ keyword parameter sets the _sync_close_ instance + * variable. Setting this to +true+ will cause the underlying socket to be + * closed when the SSL/TLS connection is shut down. + * * The OpenSSL::Buffering module provides additional IO methods. * * This method will freeze the SSLContext if one is provided; @@ -1631,6 +1634,10 @@ peeraddr_ip_str(VALUE self) static VALUE ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) { + static ID kw_ids[1]; + VALUE kw_args[1]; + VALUE opts; + VALUE io, v_ctx; SSL *ssl; SSL_CTX *ctx; @@ -1639,9 +1646,18 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self) if (ssl) ossl_raise(eSSLError, "SSL already initialized"); - if (rb_scan_args(argc, argv, "11", &io, &v_ctx) == 1) + if (rb_scan_args(argc, argv, "11:", &io, &v_ctx, &opts) == 1) v_ctx = rb_funcall(cSSLContext, rb_intern("new"), 0); + if (!kw_ids[0]) { + kw_ids[0] = rb_intern_const("sync_close"); + } + + rb_get_kwargs(opts, kw_ids, 0, 1, kw_args); + if (kw_args[0] != Qundef) { + rb_ivar_set(self, id_i_sync_close, kw_args[0]); + } + GetSSLCTX(v_ctx, ctx); rb_ivar_set(self, id_i_context, v_ctx); ossl_sslctx_setup(v_ctx); @@ -1696,11 +1712,15 @@ ossl_ssl_setup(VALUE self) return Qtrue; } +static int +errno_mapped(void) +{ #ifdef _WIN32 -#define ssl_get_error(ssl, ret) (errno = rb_w32_map_errno(WSAGetLastError()), SSL_get_error((ssl), (ret))) + return rb_w32_map_errno(WSAGetLastError()); #else -#define ssl_get_error(ssl, ret) SSL_get_error((ssl), (ret)) + return errno; #endif +} static void write_would_block(int nonblock) @@ -1741,13 +1761,13 @@ static void io_wait_writable(VALUE io) { #ifdef HAVE_RB_IO_MAYBE_WAIT - if (!rb_io_maybe_wait_writable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) { + if (!rb_io_wait(io, INT2NUM(RUBY_IO_WRITABLE), RUBY_IO_TIMEOUT_DEFAULT)) { rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become writable!"); } #else rb_io_t *fptr; GetOpenFile(io, fptr); - rb_io_wait_writable(fptr->fd); + rb_thread_fd_writable(fptr->fd); #endif } @@ -1755,13 +1775,13 @@ static void io_wait_readable(VALUE io) { #ifdef HAVE_RB_IO_MAYBE_WAIT - if (!rb_io_maybe_wait_readable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) { + if (!rb_io_wait(io, INT2NUM(RUBY_IO_READABLE), RUBY_IO_TIMEOUT_DEFAULT)) { rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become readable!"); } #else rb_io_t *fptr; GetOpenFile(io, fptr); - rb_io_wait_readable(fptr->fd); + rb_thread_wait_fd(fptr->fd); #endif } @@ -1769,7 +1789,6 @@ static VALUE ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) { SSL *ssl; - int ret, ret2; VALUE cb_state; int nonblock = opts != Qfalse; @@ -1779,7 +1798,8 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) VALUE io = rb_attr_get(self, id_i_io); for (;;) { - ret = func(ssl); + int ret = func(ssl); + int saved_errno = errno_mapped(); cb_state = rb_attr_get(self, ID_callback_state); if (!NIL_P(cb_state)) { @@ -1791,7 +1811,8 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) if (ret > 0) break; - switch ((ret2 = ssl_get_error(ssl, ret))) { + int code = SSL_get_error(ssl, ret); + switch (code) { case SSL_ERROR_WANT_WRITE: if (no_exception_p(opts)) { return sym_wait_writable; } write_would_block(nonblock); @@ -1805,10 +1826,11 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) case SSL_ERROR_SYSCALL: #ifdef __APPLE__ /* See ossl_ssl_write_internal() */ - if (errno == EPROTOTYPE) + if (saved_errno == EPROTOTYPE) continue; #endif - if (errno) rb_sys_fail(funcname); + if (saved_errno) + rb_exc_raise(rb_syserr_new(saved_errno, funcname)); /* fallthrough */ default: { VALUE error_append = Qnil; @@ -1829,10 +1851,10 @@ ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts) ossl_raise(eSSLError, "%s%s returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s%"PRIsVALUE, funcname, - ret2 == SSL_ERROR_SYSCALL ? " SYSCALL" : "", - ret2, - errno, - peeraddr_ip_str(self), + code == SSL_ERROR_SYSCALL ? " SYSCALL" : "", + code, + saved_errno, + peeraddr_ip_str(io), SSL_state_string_long(ssl), error_append); } @@ -1974,6 +1996,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) for (;;) { rb_str_locktmp(str); int nread = SSL_read(ssl, RSTRING_PTR(str), ilen); + int saved_errno = errno_mapped(); rb_str_unlocktmp(str); cb_state = rb_attr_get(self, ID_callback_state); @@ -1983,7 +2006,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) rb_jump_tag(NUM2INT(cb_state)); } - switch (ssl_get_error(ssl, nread)) { + switch (SSL_get_error(ssl, nread)) { case SSL_ERROR_NONE: rb_str_set_len(str, nread); return str; @@ -2006,8 +2029,8 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock) break; case SSL_ERROR_SYSCALL: if (!ERR_peek_error()) { - if (errno) - rb_sys_fail(0); + if (saved_errno) + rb_exc_raise(rb_syserr_new(saved_errno, "SSL_read")); else { /* * The underlying BIO returned 0. This is actually a @@ -2092,6 +2115,7 @@ ossl_ssl_write_internal_safe(VALUE _args) for (;;) { int nwritten = SSL_write(ssl, RSTRING_PTR(str), num); + int saved_errno = errno_mapped(); cb_state = rb_attr_get(self, ID_callback_state); if (!NIL_P(cb_state)) { @@ -2100,7 +2124,7 @@ ossl_ssl_write_internal_safe(VALUE _args) rb_jump_tag(NUM2INT(cb_state)); } - switch (ssl_get_error(ssl, nwritten)) { + switch (SSL_get_error(ssl, nwritten)) { case SSL_ERROR_NONE: return INT2NUM(nwritten); case SSL_ERROR_WANT_WRITE: @@ -2121,10 +2145,11 @@ ossl_ssl_write_internal_safe(VALUE _args) * make the error handling in line with the socket library. * [Bug #14713] https://bugs.ruby-lang.org/issues/14713 */ - if (errno == EPROTOTYPE) + if (saved_errno == EPROTOTYPE) continue; #endif - if (errno) rb_sys_fail(0); + if (saved_errno) + rb_exc_raise(rb_syserr_new(saved_errno, "SSL_write")); /* fallthrough */ default: ossl_raise(eSSLError, "SSL_write"); @@ -3300,5 +3325,6 @@ Init_ossl_ssl(void) DefIVarID(io); DefIVarID(context); DefIVarID(hostname); + DefIVarID(sync_close); #endif /* !defined(OPENSSL_NO_SOCK) */ } diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb index 93766cfc88..6a405da0a9 100644 --- a/test/openssl/test_cipher.rb +++ b/test/openssl/test_cipher.rb @@ -32,28 +32,28 @@ class OpenSSL::TestCipher < OpenSSL::TestCase salt = "\x01" * 8 num = 2048 pt = "data to be encrypted" - cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt - cipher.pkcs5_keyivgen(pass, salt, num, "MD5") + cipher = OpenSSL::Cipher.new("AES-256-CBC").encrypt + cipher.pkcs5_keyivgen(pass, salt, num, "SHA256") s1 = cipher.update(pt) << cipher.final - d1 = num.times.inject(pass + salt) {|out, _| OpenSSL::Digest.digest('MD5', out) } - d2 = num.times.inject(d1 + pass + salt) {|out, _| OpenSSL::Digest.digest('MD5', out) } - key = (d1 + d2)[0, 24] - iv = (d1 + d2)[24, 8] - cipher = new_encryptor("DES-EDE3-CBC", key: key, iv: iv) + d1 = num.times.inject(pass + salt) {|out, _| OpenSSL::Digest.digest('SHA256', out) } + d2 = num.times.inject(d1 + pass + salt) {|out, _| OpenSSL::Digest.digest('SHA256', out) } + key = (d1 + d2)[0, 32] + iv = (d1 + d2)[32, 16] + cipher = new_encryptor("AES-256-CBC", key: key, iv: iv) s2 = cipher.update(pt) << cipher.final assert_equal s1, s2 - cipher2 = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt - assert_raise(ArgumentError) { cipher2.pkcs5_keyivgen(pass, salt, -1, "MD5") } + cipher2 = OpenSSL::Cipher.new("AES-256-CBC").encrypt + assert_raise(ArgumentError) { cipher2.pkcs5_keyivgen(pass, salt, -1, "SHA256") } end def test_info - cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt - assert_equal "DES-EDE3-CBC", cipher.name - assert_equal 24, cipher.key_len - assert_equal 8, cipher.iv_len + cipher = OpenSSL::Cipher.new("AES-256-CBC").encrypt + assert_equal "AES-256-CBC", cipher.name + assert_equal 32, cipher.key_len + assert_equal 16, cipher.iv_len end def test_dup @@ -80,13 +80,13 @@ class OpenSSL::TestCipher < OpenSSL::TestCase end def test_key_iv_set - cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt - assert_raise(ArgumentError) { cipher.key = "\x01" * 23 } - assert_nothing_raised { cipher.key = "\x01" * 24 } - assert_raise(ArgumentError) { cipher.key = "\x01" * 25 } - assert_raise(ArgumentError) { cipher.iv = "\x01" * 7 } - assert_nothing_raised { cipher.iv = "\x01" * 8 } - assert_raise(ArgumentError) { cipher.iv = "\x01" * 9 } + cipher = OpenSSL::Cipher.new("AES-256-CBC").encrypt + assert_raise(ArgumentError) { cipher.key = "\x01" * 31 } + assert_nothing_raised { cipher.key = "\x01" * 32 } + assert_raise(ArgumentError) { cipher.key = "\x01" * 33 } + assert_raise(ArgumentError) { cipher.iv = "\x01" * 15 } + assert_nothing_raised { cipher.iv = "\x01" * 16 } + assert_raise(ArgumentError) { cipher.iv = "\x01" * 17 } end def test_random_key_iv @@ -109,8 +109,8 @@ class OpenSSL::TestCipher < OpenSSL::TestCase end def test_initialize - cipher = OpenSSL::Cipher.new("DES-EDE3-CBC") - assert_raise(RuntimeError) { cipher.__send__(:initialize, "DES-EDE3-CBC") } + cipher = OpenSSL::Cipher.new("AES-256-CBC") + assert_raise(RuntimeError) { cipher.__send__(:initialize, "AES-256-CBC") } assert_raise(RuntimeError) { OpenSSL::Cipher.allocate.final } assert_raise(OpenSSL::Cipher::CipherError) { OpenSSL::Cipher.new("no such algorithm") @@ -134,13 +134,14 @@ class OpenSSL::TestCipher < OpenSSL::TestCase def test_update_with_buffer cipher = OpenSSL::Cipher.new("aes-128-ecb").encrypt cipher.random_key - expected = cipher.update("data") << cipher.final - assert_equal 16, expected.bytesize + expected = cipher.update("data" * 10) << cipher.final + assert_equal 48, expected.bytesize # Buffer is supplied cipher.reset buf = String.new - assert_same buf, cipher.update("data", buf) + assert_same buf, cipher.update("data" * 10, buf) + assert_equal 32, buf.bytesize assert_equal expected, buf + cipher.final # Buffer is frozen @@ -149,9 +150,9 @@ class OpenSSL::TestCipher < OpenSSL::TestCase # Buffer is a shared string [ruby-core:120141] [Bug #20937] cipher.reset - buf = "x" * 1024 - shared = buf[-("data".bytesize + 32)..-1] - assert_same shared, cipher.update("data", shared) + buf = "x".b * 1024 + shared = buf[-("data".bytesize * 10 + 32)..-1] + assert_same shared, cipher.update("data" * 10, shared) assert_equal expected, shared + cipher.final end @@ -168,12 +169,12 @@ class OpenSSL::TestCipher < OpenSSL::TestCase %w(ecb cbc cfb ofb).each{|mode| c1 = OpenSSL::Cipher.new("aes-256-#{mode}") c1.encrypt - c1.pkcs5_keyivgen("passwd") + c1.pkcs5_keyivgen("passwd", "12345678", 10000, "SHA256") ct = c1.update(pt) + c1.final c2 = OpenSSL::Cipher.new("aes-256-#{mode}") c2.decrypt - c2.pkcs5_keyivgen("passwd") + c2.pkcs5_keyivgen("passwd", "12345678", 10000, "SHA256") assert_equal(pt, c2.update(ct) + c2.final) } end @@ -312,6 +313,9 @@ class OpenSSL::TestCipher < OpenSSL::TestCase end def test_aes_ocb_tag_len + # AES-128-OCB is not FIPS-approved. + omit_on_fips + # RFC 7253 Appendix A; the second sample key = ["000102030405060708090A0B0C0D0E0F"].pack("H*") iv = ["BBAA99887766554433221101"].pack("H*") @@ -346,6 +350,9 @@ class OpenSSL::TestCipher < OpenSSL::TestCase end if has_cipher?("aes-128-ocb") def test_aes_gcm_siv + # AES-128-GCM-SIV is not FIPS-approved. + omit_on_fips + # RFC 8452 Appendix C.1., 8th example key = ["01000000000000000000000000000000"].pack("H*") iv = ["030000000000000000000000"].pack("H*") diff --git a/test/openssl/test_digest.rb b/test/openssl/test_digest.rb index 2ef84cfa4c..91ed247414 100644 --- a/test/openssl/test_digest.rb +++ b/test/openssl/test_digest.rb @@ -6,8 +6,8 @@ if defined?(OpenSSL) class OpenSSL::TestDigest < OpenSSL::TestCase def setup super - @d1 = OpenSSL::Digest.new("MD5") - @d2 = OpenSSL::Digest::MD5.new + @d1 = OpenSSL::Digest.new("SHA256") + @d2 = OpenSSL::Digest::SHA256.new end def test_initialize @@ -17,18 +17,20 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end def test_digest - null_hex = "d41d8cd98f00b204e9800998ecf8427e" + # SHA256 null value calculated by `echo -n "" | sha256sum` + null_hex = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" null_bin = [null_hex].pack("H*") data = "DATA" - hex = "e44f9e348e41cb272efa87387728571b" + # SHA256 DATA value calculated by `echo -n "DATA" | sha256sum` + hex = "c97c29c7a71b392b437ee03fd17f09bb10b75e879466fc0eb757b2c4a78ac938" bin = [hex].pack("H*") assert_equal(null_bin, @d1.digest) assert_equal(null_hex, @d1.hexdigest) @d1 << data assert_equal(bin, @d1.digest) assert_equal(hex, @d1.hexdigest) - assert_equal(bin, OpenSSL::Digest.digest('MD5', data)) - assert_equal(hex, OpenSSL::Digest.hexdigest('MD5', data)) + assert_equal(bin, OpenSSL::Digest.digest('SHA256', data)) + assert_equal(hex, OpenSSL::Digest.hexdigest('SHA256', data)) end def test_eql @@ -38,9 +40,9 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end def test_info - assert_equal("MD5", @d1.name, "name") - assert_equal("MD5", @d2.name, "name") - assert_equal(16, @d1.size, "size") + assert_equal("SHA256", @d1.name, "name") + assert_equal("SHA256", @d2.name, "name") + assert_equal(32, @d1.size, "size") end def test_dup @@ -60,7 +62,10 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end def test_digest_constants - %w{MD5 SHA1 SHA224 SHA256 SHA384 SHA512}.each do |name| + non_fips_names = %w{MD5} + names = %w{SHA1 SHA224 SHA256 SHA384 SHA512} + names = non_fips_names + names unless OpenSSL.fips_mode + names.each do |name| assert_not_nil(OpenSSL::Digest.new(name)) klass = OpenSSL::Digest.const_get(name.tr('-', '_')) assert_not_nil(klass.new) @@ -125,6 +130,9 @@ class OpenSSL::TestDigest < OpenSSL::TestCase end def test_fetched_evp_md + # KECCAK-256 is not FIPS-approved. + omit_on_fips + # Pre-NIST Keccak is an example of a digest algorithm that doesn't have an # NID and requires dynamic allocation of EVP_MD hex = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" diff --git a/test/openssl/test_fips.rb b/test/openssl/test_fips.rb index efc2655e25..683e0011e8 100644 --- a/test/openssl/test_fips.rb +++ b/test/openssl/test_fips.rb @@ -30,7 +30,7 @@ class OpenSSL::TestFIPS < OpenSSL::TestCase def test_fips_mode_is_reentrant return if aws_lc? # AWS-LC's FIPS mode is decided at compile time. - assert_separately(["-ropenssl"], <<~"end;") + assert_ruby_status(["-ropenssl"], <<~"end;") OpenSSL.fips_mode = false OpenSSL.fips_mode = false end; diff --git a/test/openssl/test_ocsp.rb b/test/openssl/test_ocsp.rb index b9b66ad37a..c43ff5cb55 100644 --- a/test/openssl/test_ocsp.rb +++ b/test/openssl/test_ocsp.rb @@ -215,6 +215,35 @@ class OpenSSL::TestOCSP < OpenSSL::TestCase assert_equal bres.to_der, bres.dup.to_der end + def test_basic_response_status_good + bres = OpenSSL::OCSP::BasicResponse.new + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1')) + bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, nil) + bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert]) + + statuses = bres.status + assert_equal 1, statuses.size + status = statuses[0] + assert_equal cid.to_der, status[0].to_der + assert_equal OpenSSL::OCSP::V_CERTSTATUS_GOOD, status[1] + assert_nil status[3] # revtime should be nil for GOOD status + end + + def test_basic_response_status_revoked + bres = OpenSSL::OCSP::BasicResponse.new + now = Time.at(Time.now.to_i) + cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1')) + bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, + OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, now - 400, -300, nil, nil) + bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert]) + + statuses = bres.status + assert_equal 1, statuses.size + status = statuses[0] + assert_equal OpenSSL::OCSP::V_CERTSTATUS_REVOKED, status[1] + assert_equal now - 400, status[3] # revtime should be the revocation time + end + def test_basic_response_response_operations bres = OpenSSL::OCSP::BasicResponse.new now = Time.at(Time.now.to_i) diff --git a/test/openssl/test_pkcs12.rb b/test/openssl/test_pkcs12.rb index 1b5328774e..617c156cbd 100644 --- a/test/openssl/test_pkcs12.rb +++ b/test/openssl/test_pkcs12.rb @@ -3,6 +3,29 @@ require_relative "utils" if defined?(OpenSSL) +# OpenSSL::PKCS12.create calling the PKCS12_create() has the argument mac_iter +# which uses a MAC key using PKCS12KDF which is not FIPS-approved. +# OpenSSL::PKCS12.new with base64-encoded example calling PKCS12_parse() +# verifies the MAC key using PKCS12KDF which is not FIPS-approved. +# +# PBE-SHA1-3DES uses PKCS12KDF which is not FIPS-approved according to the RFC +# 7292 PKCS#12. +# https://datatracker.ietf.org/doc/html/rfc7292#appendix-C +# > The PBES1 encryption scheme defined in PKCS #5 provides a number of +# > algorithm identifiers for deriving keys and IVs; here, we specify a +# > few more, all of which use the procedure detailed in Appendices B.2 +# > and B.3 to construct keys (and IVs, where needed). As is implied by +# > their names, all of the object identifiers below use the hash +# > function SHA-1. +# > ... +# > pbeWithSHAAnd3-KeyTripleDES-CBC OBJECT IDENTIFIER ::= {pkcs-12PbeIds 3} +# +# Note that the pbeWithSHAAnd3-KeyTripleDES-CBC (pkcs12-pbeids 3) in the RFC +# 7292 PKCS#12 means PBE-SHA1-3DES in OpenSSL. PKCS12KDF is used in PKCS#12. +# https://oidref.com/1.2.840.113549.1.12.1.3 +# https://github.com/openssl/openssl/blob/ed57d1e06dca28689190e00d9893e0fd7ecc67c1/crypto/objects/objects.txt#L385 +return if OpenSSL.fips_mode + module OpenSSL class TestPKCS12 < OpenSSL::TestCase DEFAULT_PBE_PKEYS = "PBE-SHA1-3DES" @@ -210,8 +233,13 @@ module OpenSSL end def test_new_with_no_keys - # generated with: - # openssl pkcs12 -certpbe PBE-SHA1-3DES -in <@mycert> -nokeys -export + # Generated with the following steps: + # Print the value of the @mycert such as by `puts @mycert.to_s` and + # save the value as the file `mycert.pem`. + # Run the following commands: + # openssl pkcs12 -certpbe PBE-SHA1-3DES -in <(cat mycert.pem) \ + # -nokeys -export -passout pass:abc123 -out /tmp/p12.out + # base64 -w 60 /tmp/p12.out str = <<~EOF.unpack1("m") MIIGJAIBAzCCBeoGCSqGSIb3DQEHAaCCBdsEggXXMIIF0zCCBc8GCSqGSIb3 DQEHBqCCBcAwggW8AgEAMIIFtQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMw @@ -259,8 +287,10 @@ AA== end def test_new_with_no_certs - # generated with: - # openssl pkcs12 -inkey fixtures/openssl/pkey/rsa-1.pem -nocerts -export + # Generated with the folowing steps: + # openssl pkcs12 -inkey test/openssl/fixtures/pkey/rsa-1.pem \ + # -nocerts -export -passout pass:abc123 -out /tmp/p12.out + # base64 -w 60 /tmp/p12.out str = <<~EOF.unpack1("m") MIIJ7wIBAzCCCbUGCSqGSIb3DQEHAaCCCaYEggmiMIIJnjCCCZoGCSqGSIb3 DQEHAaCCCYsEggmHMIIJgzCCCX8GCyqGSIb3DQEMCgECoIIJbjCCCWowHAYK diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb index 88085bc68c..ec97a747a3 100644 --- a/test/openssl/test_pkey_ec.rb +++ b/test/openssl/test_pkey_ec.rb @@ -345,6 +345,15 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase assert_equal group1.degree, group4.degree end + def test_ec_group_initialize_error_message + # Test that passing 2 arguments raises the helpful error + e = assert_raise(ArgumentError) do + OpenSSL::PKey::EC::Group.new(:GFp, 123) + end + + assert_equal("wrong number of arguments (given 2, expected 1 or 4)", e.message) + end + def test_ec_point group = OpenSSL::PKey::EC::Group.new("prime256v1") key = OpenSSL::PKey::EC.generate(group) diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb index 5d20ccd1f4..ce1b2c1e96 100644 --- a/test/openssl/test_ssl.rb +++ b/test/openssl/test_ssl.rb @@ -355,6 +355,22 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase end end + def test_sync_close_initialize_opt + start_server do |port| + begin + sock = TCPSocket.new("127.0.0.1", port) + ssl = OpenSSL::SSL::SSLSocket.new(sock, sync_close: true) + assert_equal true, ssl.sync_close + ssl.connect + ssl.puts "abc"; assert_equal "abc\n", ssl.gets + ssl.close + assert_predicate sock, :closed? + ensure + sock&.close + end + end + end + def test_copy_stream start_server do |port| server_connect(port) do |ssl| @@ -1064,36 +1080,46 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase end end - def test_servername_cb_raises_an_exception_on_unknown_objects - hostname = 'example.org' - - ctx2 = OpenSSL::SSL::SSLContext.new - ctx2.cert = @svr_cert - ctx2.key = @svr_key - ctx2.servername_cb = lambda { |args| Object.new } - + def test_servername_cb_exception sock1, sock2 = socketpair + t = Thread.new { + s1 = OpenSSL::SSL::SSLSocket.new(sock1) + s1.hostname = "localhost" + assert_raise_with_message(OpenSSL::SSL::SSLError, /unrecognized.name/i) { + s1.connect + } + } + + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.servername_cb = lambda { |args| raise RuntimeError, "foo" } s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) + assert_raise_with_message(RuntimeError, "foo") { s2.accept } + assert t.join + ensure + sock1.close + sock2.close + t.kill.join + end - ctx1 = OpenSSL::SSL::SSLContext.new + def test_servername_cb_raises_an_exception_on_unknown_objects + sock1, sock2 = socketpair - s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1) - s1.hostname = hostname t = Thread.new { - assert_raise(OpenSSL::SSL::SSLError) do - s1.connect - end + s1 = OpenSSL::SSL::SSLSocket.new(sock1) + s1.hostname = "localhost" + assert_raise(OpenSSL::SSL::SSLError) { s1.connect } } - assert_raise(ArgumentError) do - s2.accept - end - + ctx2 = OpenSSL::SSL::SSLContext.new + ctx2.servername_cb = lambda { |args| Object.new } + s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx2) + assert_raise(ArgumentError) { s2.accept } assert t.join ensure - sock1.close if sock1 - sock2.close if sock2 + sock1.close + sock2.close + t.kill.join end def test_accept_errors_include_peeraddr -- cgit v1.2.3