aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/test.yml32
-rw-r--r--Gemfile3
-rw-r--r--History.md40
-rw-r--r--README.md55
-rw-r--r--Rakefile49
-rw-r--r--ext/openssl/extconf.rb43
-rw-r--r--ext/openssl/ossl.c268
-rw-r--r--ext/openssl/ossl.h11
-rw-r--r--ext/openssl/ossl_bn.c2
-rw-r--r--ext/openssl/ossl_cipher.c2
-rw-r--r--ext/openssl/ossl_config.c2
-rw-r--r--ext/openssl/ossl_digest.c2
-rw-r--r--ext/openssl/ossl_engine.c2
-rw-r--r--ext/openssl/ossl_hmac.c2
-rw-r--r--ext/openssl/ossl_kdf.c2
-rw-r--r--ext/openssl/ossl_ns_spki.c2
-rw-r--r--ext/openssl/ossl_ocsp.c12
-rw-r--r--ext/openssl/ossl_pkcs12.c2
-rw-r--r--ext/openssl/ossl_pkcs7.c6
-rw-r--r--ext/openssl/ossl_pkey.c261
-rw-r--r--ext/openssl/ossl_pkey_dh.c29
-rw-r--r--ext/openssl/ossl_pkey_dsa.c65
-rw-r--r--ext/openssl/ossl_pkey_ec.c74
-rw-r--r--ext/openssl/ossl_pkey_rsa.c81
-rw-r--r--ext/openssl/ossl_provider.c211
-rw-r--r--ext/openssl/ossl_provider.h5
-rw-r--r--ext/openssl/ossl_ssl.c148
-rw-r--r--ext/openssl/ossl_ssl_session.c2
-rw-r--r--ext/openssl/ossl_ts.c6
-rw-r--r--ext/openssl/ossl_x509attr.c2
-rw-r--r--ext/openssl/ossl_x509cert.c2
-rw-r--r--ext/openssl/ossl_x509crl.c2
-rw-r--r--ext/openssl/ossl_x509ext.c20
-rw-r--r--ext/openssl/ossl_x509name.c2
-rw-r--r--ext/openssl/ossl_x509req.c2
-rw-r--r--ext/openssl/ossl_x509revoked.c2
-rw-r--r--ext/openssl/ossl_x509store.c17
-rw-r--r--lib/openssl/buffering.rb7
-rw-r--r--lib/openssl/digest.rb6
-rw-r--r--lib/openssl/ssl.rb20
-rw-r--r--lib/openssl/version.rb2
-rw-r--r--openssl.gemspec20
-rw-r--r--test/openssl/envutil.rb287
-rw-r--r--test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl19
-rw-r--r--test/openssl/test_cipher.rb2
-rw-r--r--test/openssl/test_config.rb12
-rw-r--r--test/openssl/test_digest.rb2
-rw-r--r--test/openssl/test_engine.rb38
-rw-r--r--test/openssl/test_fips.rb40
-rw-r--r--test/openssl/test_ns_spki.rb6
-rw-r--r--test/openssl/test_ossl.rb13
-rw-r--r--test/openssl/test_pkcs12.rb4
-rw-r--r--test/openssl/test_pkey.rb74
-rw-r--r--test/openssl/test_pkey_dsa.rb2
-rw-r--r--test/openssl/test_pkey_ec.rb29
-rw-r--r--test/openssl/test_pkey_rsa.rb2
-rw-r--r--test/openssl/test_provider.rb67
-rw-r--r--test/openssl/test_ssl.rb37
-rw-r--r--test/openssl/test_ssl_session.rb2
-rw-r--r--test/openssl/test_x509ext.rb19
-rw-r--r--test/openssl/utils.rb53
61 files changed, 1404 insertions, 827 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index fc4e847c..075bac8e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -7,7 +7,7 @@ jobs:
uses: ruby/actions/.github/workflows/ruby_versions.yml@master
with:
engine: cruby-truffleruby
- min_version: 2.6
+ min_version: 2.7
test:
needs: ruby-versions
name: >-
@@ -86,6 +86,12 @@ jobs:
- libressl-3.7.3 # EOL
- libressl-3.8.4
- libressl-3.9.1
+ fips-enabled: [ false ]
+ include:
+ - { os: ubuntu-latest, ruby: "3.0", openssl: openssl-3.0.10, fips-enabled: true, append-configure: 'enable-fips', name-extra: 'fips' }
+ - { os: ubuntu-latest, ruby: "3.0", openssl: openssl-3.1.2, fips-enabled: true, append-configure: 'enable-fips', name-extra: 'fips' }
+ - { os: ubuntu-latest, ruby: "3.0", openssl: openssl-head, git: 'git://git.openssl.org/openssl.git', branch: 'master' }
+ - { os: ubuntu-latest, ruby: "3.0", openssl: openssl-head, git: 'git://git.openssl.org/openssl.git', branch: 'master', fips-enabled: true, append-configure: 'enable-fips', name-extra: 'fips' }
steps:
- name: repo checkout
uses: actions/checkout@v4
@@ -123,6 +129,22 @@ jobs:
make -j4
make install_sw
+ - name: prepare openssl fips
+ run: make install_fips
+ working-directory: tmp/build-openssl/${{ matrix.openssl }}
+ if: matrix.fips-enabled
+
+ - name: set the open installed directory
+ run: >
+ sed -e "s|OPENSSL_DIR|$HOME/.openssl/${{ matrix.openssl }}|"
+ test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl >
+ test/openssl/fixtures/ssl/openssl_fips.cnf
+ if: matrix.fips-enabled
+
+ - name: set openssl config file path for fips.
+ run: echo "OPENSSL_CONF=$(pwd)/test/openssl/fixtures/ssl/openssl_fips.cnf" >> $GITHUB_ENV
+ if: matrix.fips-enabled
+
- name: load ruby
uses: ruby/setup-ruby@v1
with:
@@ -145,3 +167,11 @@ jobs:
- name: test
run: rake test TESTOPTS="-v --no-show-detail-immediately"
timeout-minutes: 5
+ if: ${{ !matrix.fips-enabled }}
+
+ # Run only the passing tests on the FIPS module as a temporary workaround.
+ # TODO Fix other tests, and run all the tests on FIPS module.
+ - name: test on fips module
+ run: |
+ rake test_fips TESTOPTS="-v --no-show-detail-immediately"
+ if: matrix.fips-enabled
diff --git a/Gemfile b/Gemfile
index 89c37972..cdd030ac 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,4 +6,7 @@ group :development do
gem "rake"
gem "rake-compiler"
gem "test-unit", "~> 3.0", ">= 3.4.6"
+ gem "test-unit-ruby-core"
+ # In the case of Ruby whose rdoc is not a default gem.
+ gem "rdoc"
end
diff --git a/History.md b/History.md
index 1e0df7dd..bd7b1ec1 100644
--- a/History.md
+++ b/History.md
@@ -1,3 +1,43 @@
+Version 3.2.0
+=============
+
+Compatibility
+-------------
+
+* Ruby >= 2.7
+ - Support for Ruby 2.6 has been removed. Note that Ruby 2.6 reached the
+ end-of-life in 2022-04.
+ [[GitHub #639]](https://github.com/ruby/openssl/pull/639)
+* OpenSSL >= 1.0.2 or LibreSSL >= 3.1
+
+Notable changes
+---------------
+
+* Add a stub gemspec for JRuby, which depends on the `jruby-openssl` gem.
+ [[GitHub #598]](https://github.com/ruby/openssl/pull/598)
+* Add support for the FIPS module in OpenSSL 3.0/3.1.
+ [[GitHub #608]](https://github.com/ruby/openssl/pull/608)
+* Rework `OpenSSL::PKey` routines for loading DER or PEM encoded keys for better
+ compatibility with OpenSSL 3.0/3.1 with the FIPS module.
+ [[GitHub #615]](https://github.com/ruby/openssl/pull/615)
+ [[GitHub #669]](https://github.com/ruby/openssl/pull/669)
+* Add `OpenSSL::Provider` module for loading and unloading OpenSSL 3 providers.
+ [[GitHub #635]](https://github.com/ruby/openssl/pull/635)
+* Add `OpenSSL::PKey.new_raw_private_key`, `.new_raw_public_key`,
+ `OpenSSL::PKey::PKey#raw_private_key`, and `#raw_public_key` for public key
+ algorithms that use "raw private/public key", such as X25519 and Ed25519.
+ [[GitHub #646]](https://github.com/ruby/openssl/pull/646)
+* Improve OpenSSL error messages to include additional information when
+ it is available in OpenSSL's error queue.
+ [[GitHub #648]](https://github.com/ruby/openssl/pull/648)
+* Change `OpenSSL::SSL::SSLContext#ca_file=` and `#ca_path=` to raise
+ `OpenSSL::SSL::SSLError` instead of printing a warning message.
+ [[GitHub #659]](https://github.com/ruby/openssl/pull/659)
+* Allow `OpenSSL::X509::ExtensionFactory#create_extension` to take OIDs in the
+ dotted-decimal notation.
+ [[GitHub #141]](https://github.com/ruby/openssl/pull/141)
+
+
Version 3.1.0
=============
diff --git a/README.md b/README.md
index a23135ee..b57758b2 100644
--- a/README.md
+++ b/README.md
@@ -2,26 +2,53 @@
[![Actions Status](https://github.com/ruby/openssl/workflows/CI/badge.svg)](https://github.com/ruby/openssl/actions?workflow=CI)
+**OpenSSL for Ruby** provides access to SSL/TLS and general-purpose
+cryptography based on the OpenSSL library.
-OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the
-OpenSSL library.
+OpenSSL for Ruby is sometimes referred to as **openssl** in all lowercase
+or **Ruby/OpenSSL** for disambiguation.
+
+## Compatibility and maintenance policy
+
+OpenSSL for Ruby is released as a RubyGems gem. At the same time, it is part of
+the standard library of Ruby. This is called a [default gem].
+
+Each stable branch of OpenSSL for Ruby will remain supported as long as it is
+included as a default gem in [supported Ruby branches][Ruby Maintenance Branches].
+
+|Version|Maintenance status |Ruby compatibility|OpenSSL compatibility |
+|-------|-------------------------------|------------------|--------------------------------------------|
+|3.2.x |normal maintenance (Ruby 3.3) |Ruby 2.7+ |OpenSSL 1.0.2-3.1 (current) or LibreSSL 3.1+|
+|3.1.x |normal maintenance (Ruby 3.2) |Ruby 2.6+ |OpenSSL 1.0.2-3.1 (current) or LibreSSL 3.1+|
+|3.0.x |normal maintenance (Ruby 3.1) |Ruby 2.6+ |OpenSSL 1.0.2-3.1 (current) or LibreSSL 3.1+|
+|2.2.x |security maintenance (Ruby 3.0)|Ruby 2.3+ |OpenSSL 1.0.1-1.1.1 or LibreSSL 2.9+ |
+|2.1.x |end-of-life (Ruby 2.5-2.7) |Ruby 2.3+ |OpenSSL 1.0.1-1.1.1 or LibreSSL 2.5+ |
+|2.0.x |end-of-life (Ruby 2.4) |Ruby 2.3+ |OpenSSL 0.9.8-1.1.1 or LibreSSL 2.3+ |
+
+[default gem]: https://docs.ruby-lang.org/en/master/standard_library_rdoc.html
+[Ruby Maintenance Branches]: https://www.ruby-lang.org/en/downloads/branches/
## Installation
-The openssl gem is available at [rubygems.org](https://rubygems.org/gems/openssl).
-You can install with:
+> **Note**
+> The openssl gem is included with Ruby by default, but you may wish to upgrade
+> it to a newer version available at
+> [rubygems.org](https://rubygems.org/gems/openssl).
+
+To upgrade it, you can use RubyGems:
```
gem install openssl
```
-You may need to specify the path where OpenSSL is installed.
+In some cases, it may be necessary to specify the path to the installation
+directory of the OpenSSL library.
```
gem install openssl -- --with-openssl-dir=/opt/openssl
```
-Alternatively, you can install the gem with `bundler`:
+Alternatively, you can install the gem with Bundler:
```ruby
# Gemfile
@@ -30,7 +57,7 @@ gem 'openssl'
gem 'openssl', git: 'https://github.com/ruby/openssl'
```
-After doing `bundle install`, you should have the gem installed in your bundle.
+After running `bundle install`, you should have the gem installed in your bundle.
## Usage
@@ -40,15 +67,6 @@ Once installed, you can require "openssl" in your application.
require "openssl"
```
-**NOTE**: If you are using Ruby 2.3 (and not Bundler), you **must** activate
-the gem version of openssl, otherwise the default gem packaged with the Ruby
-installation will be used:
-
-```ruby
-gem "openssl"
-require "openssl"
-```
-
## Documentation
See https://ruby.github.io/openssl/.
@@ -57,10 +75,9 @@ See https://ruby.github.io/openssl/.
Please read our [CONTRIBUTING.md] for instructions.
+[CONTRIBUTING.md]: https://github.com/ruby/openssl/tree/master/CONTRIBUTING.md
+
## Security
Security issues should be reported to ruby-core by following the process
described on ["Security at ruby-lang.org"](https://www.ruby-lang.org/en/security/).
-
-
-[CONTRIBUTING.md]: https://github.com/ruby/openssl/tree/master/CONTRIBUTING.md
diff --git a/Rakefile b/Rakefile
index 475e428f..c673a8bc 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,6 +5,8 @@ require 'bundler/gem_tasks'
begin
require 'rake/extensiontask'
Rake::ExtensionTask.new('openssl')
+ # Run the debug_compiler task before the compile task.
+ Rake::Task['compile'].prerequisites.unshift :debug_compiler
rescue LoadError
warn "rake-compiler not installed. Run 'bundle install' to " \
"install testing dependency gems."
@@ -16,14 +18,59 @@ Rake::TestTask.new do |t|
t.warning = true
end
+desc 'Run tests for fips'
+task :test_fips do
+ ENV['TEST_RUBY_OPENSSL_FIPS_ENABLED'] = 'true'
+ Rake::Task['test_fips_internal'].invoke
+end
+
+Rake::TestTask.new(:test_fips_internal) do |t|
+ t.libs << 'test/openssl'
+ t.test_files = FileList[
+ 'test/openssl/test_fips.rb',
+ 'test/openssl/test_pkey.rb',
+ 'test/openssl/test_pkey_ec.rb',
+ ]
+ t.warning = true
+end
+
RDoc::Task.new do |rdoc|
rdoc.main = "README.md"
rdoc.rdoc_files.include("*.md", "lib/**/*.rb", "ext/**/*.c")
end
task :test => [:compile, :debug]
+task :test_fips => [:compile, :debug]
+
+# Print Ruby and compiler info for debugging purpose.
+task :debug_compiler do
+ ruby '-v'
+ compiler = RbConfig::CONFIG['CC']
+ case compiler
+ when 'gcc', 'clang'
+ sh "#{compiler} --version"
+ else
+ Rake.rake_output_message "Compiler: #{RbConfig::CONFIG['CC']}"
+ end
+end
+
task :debug do
- ruby "-I./lib -ropenssl -ve'puts OpenSSL::OPENSSL_VERSION, OpenSSL::OPENSSL_LIBRARY_VERSION'"
+ ruby_code = <<~'EOF'
+ openssl_version_number_str = OpenSSL::OPENSSL_VERSION_NUMBER.to_s(16)
+ libressl_version_number_str = (defined? OpenSSL::LIBRESSL_VERSION_NUMBER) ?
+ OpenSSL::LIBRESSL_VERSION_NUMBER.to_s(16) : "undefined"
+ providers_str = (defined? OpenSSL::Provider) ?
+ OpenSSL::Provider.provider_names.join(", ") : "undefined"
+ puts <<~MESSAGE
+ OpenSSL::OPENSSL_VERSION: #{OpenSSL::OPENSSL_VERSION}
+ OpenSSL::OPENSSL_LIBRARY_VERSION: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+ OpenSSL::OPENSSL_VERSION_NUMBER: #{openssl_version_number_str}
+ OpenSSL::LIBRESSL_VERSION_NUMBER: #{libressl_version_number_str}
+ FIPS enabled: #{OpenSSL.fips_mode}
+ Providers: #{providers_str}
+ MESSAGE
+ EOF
+ ruby %Q(-I./lib -ropenssl -ve'#{ruby_code}')
end
task :default => :test
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb
index bc3e4d3a..56f4a1c3 100644
--- a/ext/openssl/extconf.rb
+++ b/ext/openssl/extconf.rb
@@ -13,20 +13,41 @@
require "mkmf"
-dir_config_given = dir_config("openssl").any?
+ssl_dirs = nil
+if defined?(::TruffleRuby)
+ # Always respect the openssl prefix chosen by truffle/openssl-prefix
+ require 'truffle/openssl-prefix'
+ ssl_dirs = dir_config("openssl", ENV["OPENSSL_PREFIX"])
+else
+ ssl_dirs = dir_config("openssl")
+end
+dir_config_given = ssl_dirs.any?
+
+_, ssl_ldir = ssl_dirs
+if ssl_ldir&.split(File::PATH_SEPARATOR)&.none? { |dir| File.directory?(dir) }
+ # According to the `mkmf.rb#dir_config`, the `--with-openssl-dir=<dir>` uses
+ # the value of the `File.basename(RbConfig::MAKEFILE_CONFIG["libdir"])` as a
+ # loaded library directory name.
+ ruby_ldir_name = File.basename(RbConfig::MAKEFILE_CONFIG["libdir"])
+
+ raise "OpenSSL library directory could not be found in '#{ssl_ldir}'. " \
+ "You might want to fix this error in one of the following ways.\n" \
+ " * Recompile OpenSSL by configuring it with --libdir=#{ruby_ldir_name} " \
+ " to specify the OpenSSL library directory.\n" \
+ " * Recompile Ruby by configuring it with --libdir=<dir> to specify the " \
+ "Ruby library directory.\n" \
+ " * Compile this openssl gem with --with-openssl-include=<dir> and " \
+ "--with-openssl-lib=<dir> options to specify the OpenSSL include and " \
+ "library directories."
+end
+
dir_config("kerberos")
Logging::message "=== OpenSSL for Ruby configurator ===\n"
-##
-# Adds -DOSSL_DEBUG for compilation and some more targets when GCC is used
-# To turn it on, use: --with-debug or --enable-debug
-#
-if with_config("debug") or enable_config("debug")
- $defs.push("-DOSSL_DEBUG")
-end
$defs.push("-D""OPENSSL_SUPPRESS_DEPRECATED")
+have_func("rb_io_descriptor")
have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1
Logging::message "=== Checking for system dependent stuff... ===\n"
@@ -191,6 +212,12 @@ have_func("EVP_PKEY_dup(NULL)", evp_h)
Logging::message "=== Checking done. ===\n"
+# Append flags from environment variables.
+extcflags = ENV["RUBY_OPENSSL_EXTCFLAGS"]
+append_cflags(extcflags.split) if extcflags
+extldflags = ENV["RUBY_OPENSSL_EXTLDFLAGS"]
+append_ldflags(extldflags.split) if extldflags
+
create_header
create_makefile("openssl")
Logging::message "Done.\n"
diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c
index 6c532aca..00eded55 100644
--- a/ext/openssl/ossl.c
+++ b/ext/openssl/ossl.c
@@ -207,7 +207,7 @@ ossl_pem_passwd_cb(char *buf, int max_len, int flag, void *pwd_)
while (1) {
/*
- * when the flag is nonzero, this passphrase
+ * when the flag is nonzero, this password
* will be used to perform encryption; otherwise it will
* be used to perform decryption.
*/
@@ -272,23 +272,28 @@ VALUE
ossl_make_error(VALUE exc, VALUE str)
{
unsigned long e;
+ const char *data;
+ int flags;
- e = ERR_peek_last_error();
+ if (NIL_P(str))
+ str = rb_str_new(NULL, 0);
+
+#ifdef HAVE_ERR_GET_ERROR_ALL
+ e = ERR_peek_last_error_all(NULL, NULL, NULL, &data, &flags);
+#else
+ e = ERR_peek_last_error_line_data(NULL, NULL, &data, &flags);
+#endif
if (e) {
- const char *msg = ERR_reason_error_string(e);
+ const char *msg = ERR_reason_error_string(e);
- if (NIL_P(str)) {
- if (msg) str = rb_str_new_cstr(msg);
- }
- else {
- if (RSTRING_LEN(str)) rb_str_cat2(str, ": ");
- rb_str_cat2(str, msg ? msg : "(null)");
- }
- ossl_clear_error();
+ if (RSTRING_LEN(str)) rb_str_cat_cstr(str, ": ");
+ rb_str_cat_cstr(str, msg ? msg : "(null)");
+ if (flags & ERR_TXT_STRING && data)
+ rb_str_catf(str, " (%s)", data);
+ ossl_clear_error();
}
- if (NIL_P(str)) str = rb_str_new(0, 0);
- return rb_exc_new3(exc, str);
+ return rb_exc_new_str(exc, str);
}
void
@@ -369,22 +374,6 @@ ossl_get_errors(VALUE _)
*/
VALUE dOSSL;
-#if !defined(HAVE_VA_ARGS_MACRO)
-void
-ossl_debug(const char *fmt, ...)
-{
- va_list args;
-
- if (dOSSL == Qtrue) {
- fprintf(stderr, "OSSL_DEBUG: ");
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- fprintf(stderr, " [CONTEXT N/A]\n");
- }
-}
-#endif
-
/*
* call-seq:
* OpenSSL.debug -> true | false
@@ -418,7 +407,11 @@ static VALUE
ossl_fips_mode_get(VALUE self)
{
-#ifdef OPENSSL_FIPS
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+ VALUE enabled;
+ enabled = EVP_default_properties_is_fips_enabled(NULL) ? Qtrue : Qfalse;
+ return enabled;
+#elif defined(OPENSSL_FIPS)
VALUE enabled;
enabled = FIPS_mode() ? Qtrue : Qfalse;
return enabled;
@@ -442,8 +435,18 @@ ossl_fips_mode_get(VALUE self)
static VALUE
ossl_fips_mode_set(VALUE self, VALUE enabled)
{
-
-#ifdef OPENSSL_FIPS
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+ if (RTEST(enabled)) {
+ if (!EVP_default_properties_enable_fips(NULL, 1)) {
+ ossl_raise(eOSSLError, "Turning on FIPS mode failed");
+ }
+ } else {
+ if (!EVP_default_properties_enable_fips(NULL, 0)) {
+ ossl_raise(eOSSLError, "Turning off FIPS mode failed");
+ }
+ }
+ return enabled;
+#elif defined(OPENSSL_FIPS)
if (RTEST(enabled)) {
int mode = FIPS_mode();
if(!mode && !FIPS_mode_set(1)) /* turning on twice leads to an error */
@@ -460,75 +463,6 @@ ossl_fips_mode_set(VALUE self, VALUE enabled)
#endif
}
-#if defined(OSSL_DEBUG)
-#if !defined(LIBRESSL_VERSION_NUMBER) && \
- (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \
- defined(CRYPTO_malloc_debug_init))
-/*
- * call-seq:
- * OpenSSL.mem_check_start -> nil
- *
- * Calls CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON). Starts tracking memory
- * allocations. See also OpenSSL.print_mem_leaks.
- *
- * This is available only when built with a capable OpenSSL and --enable-debug
- * configure option.
- */
-static VALUE
-mem_check_start(VALUE self)
-{
- CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
- return Qnil;
-}
-
-/*
- * call-seq:
- * OpenSSL.print_mem_leaks -> true | false
- *
- * For debugging the Ruby/OpenSSL library. Calls CRYPTO_mem_leaks_fp(stderr).
- * Prints detected memory leaks to standard error. This cleans the global state
- * up thus you cannot use any methods of the library after calling this.
- *
- * Returns +true+ if leaks detected, +false+ otherwise.
- *
- * This is available only when built with a capable OpenSSL and --enable-debug
- * configure option.
- *
- * === Example
- * OpenSSL.mem_check_start
- * NOT_GCED = OpenSSL::PKey::RSA.new(256)
- *
- * END {
- * GC.start
- * OpenSSL.print_mem_leaks # will print the leakage
- * }
- */
-static VALUE
-print_mem_leaks(VALUE self)
-{
-#if OPENSSL_VERSION_NUMBER >= 0x10100000
- int ret;
-#endif
-
-#ifndef HAVE_RB_EXT_RACTOR_SAFE
- // for Ruby 2.x
- void ossl_bn_ctx_free(void); // ossl_bn.c
- ossl_bn_ctx_free();
-#endif
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000
- ret = CRYPTO_mem_leaks_fp(stderr);
- if (ret < 0)
- ossl_raise(eOSSLError, "CRYPTO_mem_leaks_fp");
- return ret ? Qfalse : Qtrue;
-#else
- CRYPTO_mem_leaks_fp(stderr);
- return Qnil;
-#endif
-}
-#endif
-#endif
-
#if !defined(HAVE_OPENSSL_110_THREADING_API)
/**
* Stores locks needed for OpenSSL thread safety
@@ -671,23 +605,21 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
*
* key = OpenSSL::PKey::RSA.new 2048
*
- * open 'private_key.pem', 'w' do |io| io.write key.to_pem end
- * open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end
+ * File.write 'private_key.pem', key.private_to_pem
+ * File.write 'public_key.pem', key.public_to_pem
*
* === Exporting a Key
*
* Keys saved to disk without encryption are not secure as anyone who gets
* ahold of the key may use it unless it is encrypted. In order to securely
- * export a key you may export it with a pass phrase.
+ * export a key you may export it with a password.
*
* cipher = OpenSSL::Cipher.new 'aes-256-cbc'
- * pass_phrase = 'my secure pass phrase goes here'
+ * password = 'my secure password goes here'
*
- * key_secure = key.export cipher, pass_phrase
+ * key_secure = key.private_to_pem cipher, password
*
- * open 'private.secure.pem', 'w' do |io|
- * io.write key_secure
- * end
+ * File.write 'private.secure.pem', key_secure
*
* OpenSSL::Cipher.ciphers returns a list of available ciphers.
*
@@ -707,13 +639,13 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
*
* === Loading an Encrypted Key
*
- * OpenSSL will prompt you for your pass phrase when loading an encrypted key.
- * If you will not be able to type in the pass phrase you may provide it when
+ * OpenSSL will prompt you for your password when loading an encrypted key.
+ * If you will not be able to type in the password you may provide it when
* loading the key:
*
* key4_pem = File.read 'private.secure.pem'
- * pass_phrase = 'my secure pass phrase goes here'
- * key4 = OpenSSL::PKey.read key4_pem, pass_phrase
+ * password = 'my secure password goes here'
+ * key4 = OpenSSL::PKey.read key4_pem, password
*
* == RSA Encryption
*
@@ -829,45 +761,6 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
* decrypted = cipher.update encrypted
* decrypted << cipher.final
*
- * == PKCS #5 Password-based Encryption
- *
- * PKCS #5 is a password-based encryption standard documented at
- * RFC2898[http://www.ietf.org/rfc/rfc2898.txt]. It allows a short password or
- * passphrase to be used to create a secure encryption key. If possible, PBKDF2
- * as described above should be used if the circumstances allow it.
- *
- * PKCS #5 uses a Cipher, a pass phrase and a salt to generate an encryption
- * key.
- *
- * pass_phrase = 'my secure pass phrase goes here'
- * salt = '8 octets'
- *
- * === Encryption
- *
- * First set up the cipher for encryption
- *
- * encryptor = OpenSSL::Cipher.new 'aes-256-cbc'
- * encryptor.encrypt
- * encryptor.pkcs5_keyivgen pass_phrase, salt
- *
- * Then pass the data you want to encrypt through
- *
- * encrypted = encryptor.update 'top secret document'
- * encrypted << encryptor.final
- *
- * === Decryption
- *
- * Use a new Cipher instance set up for decryption
- *
- * decryptor = OpenSSL::Cipher.new 'aes-256-cbc'
- * decryptor.decrypt
- * decryptor.pkcs5_keyivgen pass_phrase, salt
- *
- * Then pass the data you want to decrypt through
- *
- * plain = decryptor.update encrypted
- * plain << decryptor.final
- *
* == X509 Certificates
*
* === Creating a Certificate
@@ -945,12 +838,12 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
* not readable by other users.
*
* ca_key = OpenSSL::PKey::RSA.new 2048
- * pass_phrase = 'my secure pass phrase goes here'
+ * password = 'my secure password goes here'
*
- * cipher = OpenSSL::Cipher.new 'aes-256-cbc'
+ * cipher = 'aes-256-cbc'
*
* open 'ca_key.pem', 'w', 0400 do |io|
- * io.write ca_key.export(cipher, pass_phrase)
+ * io.write ca_key.private_to_pem(cipher, password)
* end
*
* === CA Certificate
@@ -1170,8 +1063,8 @@ Init_openssl(void)
/*
* Init main module
*/
- mOSSL = rb_define_module("OpenSSL");
rb_global_variable(&mOSSL);
+ mOSSL = rb_define_module("OpenSSL");
rb_define_singleton_method(mOSSL, "fixed_length_secure_compare", ossl_crypto_fixed_length_secure_compare, 2);
/*
@@ -1190,15 +1083,35 @@ Init_openssl(void)
/*
* Version number of OpenSSL the ruby OpenSSL extension was built with
- * (base 16)
+ * (base 16). The formats are below.
+ *
+ * [OpenSSL 3] <tt>0xMNN00PP0 (major minor 00 patch 0)</tt>
+ * [OpenSSL before 3] <tt>0xMNNFFPPS (major minor fix patch status)</tt>
+ * [LibreSSL] <tt>0x20000000 (fixed value)</tt>
+ *
+ * See also the man page OPENSSL_VERSION_NUMBER(3).
*/
rb_define_const(mOSSL, "OPENSSL_VERSION_NUMBER", INT2NUM(OPENSSL_VERSION_NUMBER));
+#if defined(LIBRESSL_VERSION_NUMBER)
+ /*
+ * Version number of LibreSSL the ruby OpenSSL extension was built with
+ * (base 16). The format is <tt>0xMNNFF00f (major minor fix 00
+ * status)</tt>. This constant is only defined in LibreSSL cases.
+ *
+ * See also the man page LIBRESSL_VERSION_NUMBER(3).
+ */
+ rb_define_const(mOSSL, "LIBRESSL_VERSION_NUMBER", INT2NUM(LIBRESSL_VERSION_NUMBER));
+#endif
+
/*
* Boolean indicating whether OpenSSL is FIPS-capable or not
*/
rb_define_const(mOSSL, "OPENSSL_FIPS",
-#ifdef OPENSSL_FIPS
+/* OpenSSL 3 is FIPS-capable even when it is installed without fips option */
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+ Qtrue
+#elif defined(OPENSSL_FIPS)
Qtrue
#else
Qfalse
@@ -1208,12 +1121,12 @@ Init_openssl(void)
rb_define_module_function(mOSSL, "fips_mode", ossl_fips_mode_get, 0);
rb_define_module_function(mOSSL, "fips_mode=", ossl_fips_mode_set, 1);
+ rb_global_variable(&eOSSLError);
/*
* Generic error,
* common for all classes under OpenSSL module
*/
eOSSLError = rb_define_class_under(mOSSL,"OpenSSLError",rb_eStandardError);
- rb_global_variable(&eOSSLError);
/*
* Init debug core
@@ -1254,42 +1167,7 @@ Init_openssl(void)
Init_ossl_x509();
Init_ossl_ocsp();
Init_ossl_engine();
+ Init_ossl_provider();
Init_ossl_asn1();
Init_ossl_kdf();
-
-#if defined(OSSL_DEBUG)
- /*
- * For debugging Ruby/OpenSSL. Enable only when built with --enable-debug
- */
-#if !defined(LIBRESSL_VERSION_NUMBER) && \
- (OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(OPENSSL_NO_CRYPTO_MDEBUG) || \
- defined(CRYPTO_malloc_debug_init))
- rb_define_module_function(mOSSL, "mem_check_start", mem_check_start, 0);
- rb_define_module_function(mOSSL, "print_mem_leaks", print_mem_leaks, 0);
-
-#if defined(CRYPTO_malloc_debug_init) /* <= 1.0.2 */
- CRYPTO_malloc_debug_init();
-#endif
-
-#if defined(V_CRYPTO_MDEBUG_ALL) /* <= 1.0.2 */
- CRYPTO_set_mem_debug_options(V_CRYPTO_MDEBUG_ALL);
-#endif
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000 /* <= 1.0.2 */
- {
- int i;
- /*
- * See crypto/ex_data.c; call def_get_class() immediately to avoid
- * allocations. 15 is the maximum number that is used as the class index
- * in OpenSSL 1.0.2.
- */
- for (i = 0; i <= 15; i++) {
- if (CRYPTO_get_ex_new_index(i, 0, (void *)"ossl-mdebug-dummy", 0, 0, 0) < 0)
- rb_raise(rb_eRuntimeError, "CRYPTO_get_ex_new_index for "
- "class index %d failed", i);
- }
- }
-#endif
-#endif
-#endif
}
diff --git a/ext/openssl/ossl.h b/ext/openssl/ossl.h
index facb80aa..68d42b71 100644
--- a/ext/openssl/ossl.h
+++ b/ext/openssl/ossl.h
@@ -62,6 +62,10 @@
# define OSSL_USE_ENGINE
#endif
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+# define OSSL_USE_PROVIDER
+#endif
+
/*
* Common Module
*/
@@ -157,7 +161,6 @@ VALUE ossl_to_der_if_possible(VALUE);
*/
extern VALUE dOSSL;
-#if defined(HAVE_VA_ARGS_MACRO)
#define OSSL_Debug(...) do { \
if (dOSSL == Qtrue) { \
fprintf(stderr, "OSSL_DEBUG: "); \
@@ -166,11 +169,6 @@ extern VALUE dOSSL;
} \
} while (0)
-#else
-void ossl_debug(const char *, ...);
-#define OSSL_Debug ossl_debug
-#endif
-
/*
* Include all parts
*/
@@ -194,6 +192,7 @@ void ossl_debug(const char *, ...);
#endif
#include "ossl_x509.h"
#include "ossl_engine.h"
+#include "ossl_provider.h"
#include "ossl_kdf.h"
void Init_openssl(void);
diff --git a/ext/openssl/ossl_bn.c b/ext/openssl/ossl_bn.c
index bf2bac36..ce0d3ec7 100644
--- a/ext/openssl/ossl_bn.c
+++ b/ext/openssl/ossl_bn.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_bn_type = {
{
0, ossl_bn_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_cipher.c b/ext/openssl/ossl_cipher.c
index 046d5a8e..506a0e71 100644
--- a/ext/openssl/ossl_cipher.c
+++ b/ext/openssl/ossl_cipher.c
@@ -42,7 +42,7 @@ static const rb_data_type_t ossl_cipher_type = {
{
0, ossl_cipher_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_config.c b/ext/openssl/ossl_config.c
index 0bac0274..0e598b4d 100644
--- a/ext/openssl/ossl_config.c
+++ b/ext/openssl/ossl_config.c
@@ -22,7 +22,7 @@ static const rb_data_type_t ossl_config_type = {
{
0, nconf_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
CONF *
diff --git a/ext/openssl/ossl_digest.c b/ext/openssl/ossl_digest.c
index fc326ec1..16aeeb81 100644
--- a/ext/openssl/ossl_digest.c
+++ b/ext/openssl/ossl_digest.c
@@ -35,7 +35,7 @@ static const rb_data_type_t ossl_digest_type = {
{
0, ossl_digest_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_engine.c b/ext/openssl/ossl_engine.c
index 1abde7f7..9e86321d 100644
--- a/ext/openssl/ossl_engine.c
+++ b/ext/openssl/ossl_engine.c
@@ -78,7 +78,7 @@ static const rb_data_type_t ossl_engine_type = {
{
0, ossl_engine_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_hmac.c b/ext/openssl/ossl_hmac.c
index 1a5f471a..c485ba7e 100644
--- a/ext/openssl/ossl_hmac.c
+++ b/ext/openssl/ossl_hmac.c
@@ -42,7 +42,7 @@ static const rb_data_type_t ossl_hmac_type = {
{
0, ossl_hmac_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_kdf.c b/ext/openssl/ossl_kdf.c
index 0d25a730..48b161d4 100644
--- a/ext/openssl/ossl_kdf.c
+++ b/ext/openssl/ossl_kdf.c
@@ -21,7 +21,7 @@ static VALUE mKDF, eKDF;
* (https://tools.ietf.org/html/rfc2898#section-5.2).
*
* === Parameters
- * pass :: The passphrase.
+ * pass :: The password.
* salt :: The salt. Salts prevent attacks based on dictionaries of common
* passwords and attacks based on rainbow tables. It is a public
* value that can be safely stored along with the password (e.g.
diff --git a/ext/openssl/ossl_ns_spki.c b/ext/openssl/ossl_ns_spki.c
index 9b114736..9bed1f33 100644
--- a/ext/openssl/ossl_ns_spki.c
+++ b/ext/openssl/ossl_ns_spki.c
@@ -50,7 +50,7 @@ static const rb_data_type_t ossl_netscape_spki_type = {
{
0, ossl_netscape_spki_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_ocsp.c b/ext/openssl/ossl_ocsp.c
index 9c8d768d..df986bb3 100644
--- a/ext/openssl/ossl_ocsp.c
+++ b/ext/openssl/ossl_ocsp.c
@@ -86,7 +86,7 @@ static const rb_data_type_t ossl_ocsp_request_type = {
{
0, ossl_ocsp_request_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -100,7 +100,7 @@ static const rb_data_type_t ossl_ocsp_response_type = {
{
0, ossl_ocsp_response_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -114,7 +114,7 @@ static const rb_data_type_t ossl_ocsp_basicresp_type = {
{
0, ossl_ocsp_basicresp_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -128,7 +128,7 @@ static const rb_data_type_t ossl_ocsp_singleresp_type = {
{
0, ossl_ocsp_singleresp_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -142,7 +142,7 @@ static const rb_data_type_t ossl_ocsp_certid_type = {
{
0, ossl_ocsp_certid_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -1701,7 +1701,7 @@ Init_ossl_ocsp(void)
* require 'net/http'
*
* http_response =
- * Net::HTTP.start ocsp_uri.hostname, ocsp.port do |http|
+ * Net::HTTP.start ocsp_uri.hostname, ocsp_uri.port do |http|
* http.post ocsp_uri.path, request.to_der,
* 'content-type' => 'application/ocsp-request'
* end
diff --git a/ext/openssl/ossl_pkcs12.c b/ext/openssl/ossl_pkcs12.c
index fb947df1..164b2da4 100644
--- a/ext/openssl/ossl_pkcs12.c
+++ b/ext/openssl/ossl_pkcs12.c
@@ -44,7 +44,7 @@ static const rb_data_type_t ossl_pkcs12_type = {
{
0, ossl_pkcs12_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_pkcs7.c b/ext/openssl/ossl_pkcs7.c
index c8f7924e..aeeb4bf5 100644
--- a/ext/openssl/ossl_pkcs7.c
+++ b/ext/openssl/ossl_pkcs7.c
@@ -65,7 +65,7 @@ const rb_data_type_t ossl_pkcs7_type = {
{
0, ossl_pkcs7_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -79,7 +79,7 @@ static const rb_data_type_t ossl_pkcs7_signer_info_type = {
{
0, ossl_pkcs7_signer_info_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -93,7 +93,7 @@ static const rb_data_type_t ossl_pkcs7_recip_info_type = {
{
0, ossl_pkcs7_recip_info_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_pkey.c b/ext/openssl/ossl_pkey.c
index 47625667..013412c2 100644
--- a/ext/openssl/ossl_pkey.c
+++ b/ext/openssl/ossl_pkey.c
@@ -35,7 +35,7 @@ const rb_data_type_t ossl_evp_pkey_type = {
{
0, ossl_evp_pkey_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -82,31 +82,62 @@ ossl_pkey_new(EVP_PKEY *pkey)
#if OSSL_OPENSSL_PREREQ(3, 0, 0)
# include <openssl/decoder.h>
-EVP_PKEY *
-ossl_pkey_read_generic(BIO *bio, VALUE pass)
+static EVP_PKEY *
+ossl_pkey_read(BIO *bio, const char *input_type, int selection, VALUE pass)
{
void *ppass = (void *)pass;
OSSL_DECODER_CTX *dctx;
EVP_PKEY *pkey = NULL;
int pos = 0, pos2;
- dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL, NULL, 0, NULL, NULL);
+ dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, input_type, NULL, NULL,
+ selection, NULL, NULL);
if (!dctx)
goto out;
- if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb, ppass) != 1)
- goto out;
-
- /* First check DER */
- if (OSSL_DECODER_from_bio(dctx, bio) == 1)
+ if (OSSL_DECODER_CTX_set_pem_password_cb(dctx, ossl_pem_passwd_cb,
+ ppass) != 1)
goto out;
+ while (1) {
+ if (OSSL_DECODER_from_bio(dctx, bio) == 1)
+ goto out;
+ if (BIO_eof(bio))
+ break;
+ pos2 = BIO_tell(bio);
+ if (pos2 < 0 || pos2 <= pos)
+ break;
+ ossl_clear_error();
+ pos = pos2;
+ }
+ out:
OSSL_BIO_reset(bio);
+ OSSL_DECODER_CTX_free(dctx);
+ return pkey;
+}
- /* Then check PEM; multiple OSSL_DECODER_from_bio() calls may be needed */
- if (OSSL_DECODER_CTX_set_input_type(dctx, "PEM") != 1)
- goto out;
+EVP_PKEY *
+ossl_pkey_read_generic(BIO *bio, VALUE pass)
+{
+ EVP_PKEY *pkey = NULL;
+ /* First check DER, then check PEM. */
+ const char *input_types[] = {"DER", "PEM"};
+ int input_type_num = (int)(sizeof(input_types) / sizeof(char *));
/*
- * First check for private key formats. This is to keep compatibility with
- * ruby/openssl < 3.0 which decoded the following as a private key.
+ * Non-zero selections to try to decode.
+ *
+ * See EVP_PKEY_fromdata(3) - Selections to see all the selections.
+ *
+ * This is a workaround for the decoder failing to decode or returning
+ * bogus keys with selection 0, if a key management provider is different
+ * from a decoder provider. The workaround is to avoid using selection 0.
+ *
+ * Affected OpenSSL versions: >= 3.1.0, <= 3.1.2, or >= 3.0.0, <= 3.0.10
+ * Fixed OpenSSL versions: 3.2, next release of the 3.1.z and 3.0.z
+ *
+ * See https://github.com/openssl/openssl/pull/21519 for details.
+ *
+ * First check for private key formats (EVP_PKEY_KEYPAIR). This is to keep
+ * compatibility with ruby/openssl < 3.0 which decoded the following as a
+ * private key.
*
* $ openssl ecparam -name prime256v1 -genkey -outform PEM
* -----BEGIN EC PARAMETERS-----
@@ -124,36 +155,28 @@ ossl_pkey_read_generic(BIO *bio, VALUE pass)
*
* Note that normally, the input is supposed to contain a single decodable
* PEM block only, so this special handling should not create a new problem.
+ *
+ * Note that we need to create the OSSL_DECODER_CTX variable each time when
+ * we use the different selection as a workaround.
+ * See https://github.com/openssl/openssl/issues/20657 for details.
*/
- OSSL_DECODER_CTX_set_selection(dctx, EVP_PKEY_KEYPAIR);
- while (1) {
- if (OSSL_DECODER_from_bio(dctx, bio) == 1)
- goto out;
- if (BIO_eof(bio))
- break;
- pos2 = BIO_tell(bio);
- if (pos2 < 0 || pos2 <= pos)
- break;
- ossl_clear_error();
- pos = pos2;
- }
-
- OSSL_BIO_reset(bio);
- OSSL_DECODER_CTX_set_selection(dctx, 0);
- while (1) {
- if (OSSL_DECODER_from_bio(dctx, bio) == 1)
- goto out;
- if (BIO_eof(bio))
- break;
- pos2 = BIO_tell(bio);
- if (pos2 < 0 || pos2 <= pos)
- break;
- ossl_clear_error();
- pos = pos2;
+ int selections[] = {
+ EVP_PKEY_KEYPAIR,
+ EVP_PKEY_KEY_PARAMETERS,
+ EVP_PKEY_PUBLIC_KEY
+ };
+ int selection_num = (int)(sizeof(selections) / sizeof(int));
+ int i, j;
+
+ for (i = 0; i < input_type_num; i++) {
+ for (j = 0; j < selection_num; j++) {
+ pkey = ossl_pkey_read(bio, input_types[i], selections[j], pass);
+ if (pkey) {
+ goto out;
+ }
+ }
}
-
out:
- OSSL_DECODER_CTX_free(dctx);
return pkey;
}
#else
@@ -260,9 +283,9 @@ struct pkey_blocking_generate_arg {
EVP_PKEY_CTX *ctx;
EVP_PKEY *pkey;
int state;
- int yield: 1;
- int genparam: 1;
- int interrupted: 1;
+ unsigned int yield: 1;
+ unsigned int genparam: 1;
+ unsigned int interrupted: 1;
};
static VALUE
@@ -612,6 +635,72 @@ ossl_pkey_initialize_copy(VALUE self, VALUE other)
}
#endif
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+/*
+ * call-seq:
+ * OpenSSL::PKey.new_raw_private_key(algo, string) -> PKey
+ *
+ * See the OpenSSL documentation for EVP_PKEY_new_raw_private_key()
+ */
+
+static VALUE
+ossl_pkey_new_raw_private_key(VALUE self, VALUE type, VALUE key)
+{
+ EVP_PKEY *pkey;
+ const EVP_PKEY_ASN1_METHOD *ameth;
+ int pkey_id;
+ size_t keylen;
+
+ StringValue(type);
+ StringValue(key);
+ ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
+ if (!ameth)
+ ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
+ EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
+
+ keylen = RSTRING_LEN(key);
+
+ pkey = EVP_PKEY_new_raw_private_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
+ if (!pkey)
+ ossl_raise(ePKeyError, "EVP_PKEY_new_raw_private_key");
+
+ return ossl_pkey_new(pkey);
+}
+#endif
+
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+/*
+ * call-seq:
+ * OpenSSL::PKey.new_raw_public_key(algo, string) -> PKey
+ *
+ * See the OpenSSL documentation for EVP_PKEY_new_raw_public_key()
+ */
+
+static VALUE
+ossl_pkey_new_raw_public_key(VALUE self, VALUE type, VALUE key)
+{
+ EVP_PKEY *pkey;
+ const EVP_PKEY_ASN1_METHOD *ameth;
+ int pkey_id;
+ size_t keylen;
+
+ StringValue(type);
+ StringValue(key);
+ ameth = EVP_PKEY_asn1_find_str(NULL, RSTRING_PTR(type), RSTRING_LENINT(type));
+ if (!ameth)
+ ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", type);
+ EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
+
+ keylen = RSTRING_LEN(key);
+
+ pkey = EVP_PKEY_new_raw_public_key(pkey_id, NULL, (unsigned char *)RSTRING_PTR(key), keylen);
+ if (!pkey)
+ ossl_raise(ePKeyError, "EVP_PKEY_new_raw_public_key");
+
+ return ossl_pkey_new(pkey);
+}
+#endif
+
/*
* call-seq:
* pkey.oid -> string
@@ -793,6 +882,18 @@ ossl_pkey_private_to_der(int argc, VALUE *argv, VALUE self)
*
* Serializes the private key to PEM-encoded PKCS #8 format. See #private_to_der
* for more details.
+ *
+ * An unencrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN PRIVATE KEY-----
+ * [...]
+ * -----END PRIVATE KEY-----
+ *
+ * An encrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN ENCRYPTED PRIVATE KEY-----
+ * [...]
+ * -----END ENCRYPTED PRIVATE KEY-----
*/
static VALUE
ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
@@ -800,6 +901,35 @@ ossl_pkey_private_to_pem(int argc, VALUE *argv, VALUE self)
return do_pkcs8_export(argc, argv, self, 0);
}
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+/*
+ * call-seq:
+ * pkey.raw_private_key => string
+ *
+ * See the OpenSSL documentation for EVP_PKEY_get_raw_private_key()
+ */
+
+static VALUE
+ossl_pkey_raw_private_key(VALUE self)
+{
+ EVP_PKEY *pkey;
+ VALUE str;
+ size_t len;
+
+ GetPKey(self, pkey);
+ if (EVP_PKEY_get_raw_private_key(pkey, NULL, &len) != 1)
+ ossl_raise(ePKeyError, "EVP_PKEY_get_raw_private_key");
+ str = rb_str_new(NULL, len);
+
+ if (EVP_PKEY_get_raw_private_key(pkey, (unsigned char *)RSTRING_PTR(str), &len) != 1)
+ ossl_raise(ePKeyError, "EVP_PKEY_get_raw_private_key");
+
+ rb_str_set_len(str, len);
+
+ return str;
+}
+#endif
+
VALUE
ossl_pkey_export_spki(VALUE self, int to_der)
{
@@ -842,6 +972,12 @@ ossl_pkey_public_to_der(VALUE self)
* pkey.public_to_pem -> string
*
* Serializes the public key to PEM-encoded X.509 SubjectPublicKeyInfo format.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ * [...]
+ * -----END PUBLIC KEY-----
*/
static VALUE
ossl_pkey_public_to_pem(VALUE self)
@@ -849,6 +985,35 @@ ossl_pkey_public_to_pem(VALUE self)
return ossl_pkey_export_spki(self, 0);
}
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+/*
+ * call-seq:
+ * pkey.raw_public_key => string
+ *
+ * See the OpenSSL documentation for EVP_PKEY_get_raw_public_key()
+ */
+
+static VALUE
+ossl_pkey_raw_public_key(VALUE self)
+{
+ EVP_PKEY *pkey;
+ VALUE str;
+ size_t len;
+
+ GetPKey(self, pkey);
+ if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1)
+ ossl_raise(ePKeyError, "EVP_PKEY_get_raw_public_key");
+ str = rb_str_new(NULL, len);
+
+ if (EVP_PKEY_get_raw_public_key(pkey, (unsigned char *)RSTRING_PTR(str), &len) != 1)
+ ossl_raise(ePKeyError, "EVP_PKEY_get_raw_public_key");
+
+ rb_str_set_len(str, len);
+
+ return str;
+}
+#endif
+
/*
* call-seq:
* pkey.compare?(another_pkey) -> true | false
@@ -1586,6 +1751,10 @@ Init_ossl_pkey(void)
rb_define_module_function(mPKey, "read", ossl_pkey_new_from_data, -1);
rb_define_module_function(mPKey, "generate_parameters", ossl_pkey_s_generate_parameters, -1);
rb_define_module_function(mPKey, "generate_key", ossl_pkey_s_generate_key, -1);
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+ rb_define_module_function(mPKey, "new_raw_private_key", ossl_pkey_new_raw_private_key, 2);
+ rb_define_module_function(mPKey, "new_raw_public_key", ossl_pkey_new_raw_public_key, 2);
+#endif
rb_define_alloc_func(cPKey, ossl_pkey_alloc);
rb_define_method(cPKey, "initialize", ossl_pkey_initialize, 0);
@@ -1601,6 +1770,10 @@ Init_ossl_pkey(void)
rb_define_method(cPKey, "private_to_pem", ossl_pkey_private_to_pem, -1);
rb_define_method(cPKey, "public_to_der", ossl_pkey_public_to_der, 0);
rb_define_method(cPKey, "public_to_pem", ossl_pkey_public_to_pem, 0);
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+ rb_define_method(cPKey, "raw_private_key", ossl_pkey_raw_private_key, 0);
+ rb_define_method(cPKey, "raw_public_key", ossl_pkey_raw_public_key, 0);
+#endif
rb_define_method(cPKey, "compare?", ossl_pkey_compare, 1);
rb_define_method(cPKey, "sign", ossl_pkey_sign, -1);
diff --git a/ext/openssl/ossl_pkey_dh.c b/ext/openssl/ossl_pkey_dh.c
index 83c41378..a231814a 100644
--- a/ext/openssl/ossl_pkey_dh.c
+++ b/ext/openssl/ossl_pkey_dh.c
@@ -216,9 +216,20 @@ ossl_dh_is_private(VALUE self)
* dh.to_pem -> aString
* dh.to_s -> aString
*
- * Encodes this DH to its PEM encoding. Note that any existing per-session
- * public/private keys will *not* get encoded, just the Diffie-Hellman
- * parameters will be encoded.
+ * Serializes the DH parameters to a PEM-encoding.
+ *
+ * Note that any existing per-session public/private keys will *not* get
+ * encoded, just the Diffie-Hellman parameters will be encoded.
+ *
+ * PEM-encoded parameters will look like:
+ *
+ * -----BEGIN DH PARAMETERS-----
+ * [...]
+ * -----END DH PARAMETERS-----
+ *
+ * See also #public_to_pem (X.509 SubjectPublicKeyInfo) and
+ * #private_to_pem (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) for
+ * serialization with the private or public key components.
*/
static VALUE
ossl_dh_export(VALUE self)
@@ -244,10 +255,14 @@ ossl_dh_export(VALUE self)
* call-seq:
* dh.to_der -> aString
*
- * Encodes this DH to its DER encoding. Note that any existing per-session
- * public/private keys will *not* get encoded, just the Diffie-Hellman
- * parameters will be encoded.
-
+ * Serializes the DH parameters to a DER-encoding
+ *
+ * Note that any existing per-session public/private keys will *not* get
+ * encoded, just the Diffie-Hellman parameters will be encoded.
+ *
+ * See also #public_to_der (X.509 SubjectPublicKeyInfo) and
+ * #private_to_der (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) for
+ * serialization with the private or public key components.
*/
static VALUE
ossl_dh_to_der(VALUE self)
diff --git a/ext/openssl/ossl_pkey_dsa.c b/ext/openssl/ossl_pkey_dsa.c
index b097f8c9..058ce738 100644
--- a/ext/openssl/ossl_pkey_dsa.c
+++ b/ext/openssl/ossl_pkey_dsa.c
@@ -211,16 +211,58 @@ ossl_dsa_is_private(VALUE self)
* dsa.to_pem([cipher, password]) -> aString
* dsa.to_s([cipher, password]) -> aString
*
- * Encodes this DSA to its PEM encoding.
+ * Serializes a private or public key to a PEM-encoding.
*
- * === Parameters
- * * _cipher_ is an OpenSSL::Cipher.
- * * _password_ is a string containing your password.
+ * [When the key contains public components only]
*
- * === Examples
- * DSA.to_pem -> aString
- * DSA.to_pem(cipher, 'mypassword') -> aString
+ * Serializes it into an X.509 SubjectPublicKeyInfo.
+ * The parameters _cipher_ and _password_ are ignored.
*
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ * [...]
+ * -----END PUBLIC KEY-----
+ *
+ * Consider using #public_to_pem instead. This serializes the key into an
+ * X.509 SubjectPublicKeyInfo regardless of whether it is a public key
+ * or a private key.
+ *
+ * [When the key contains private components, and no parameters are given]
+ *
+ * Serializes it into a traditional \OpenSSL DSAPrivateKey.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN DSA PRIVATE KEY-----
+ * [...]
+ * -----END DSA PRIVATE KEY-----
+ *
+ * [When the key contains private components, and _cipher_ and _password_ are given]
+ *
+ * Serializes it into a traditional \OpenSSL DSAPrivateKey and encrypts it in
+ * OpenSSL's traditional PEM encryption format.
+ * _cipher_ must be a cipher name understood by OpenSSL::Cipher.new or an
+ * instance of OpenSSL::Cipher.
+ *
+ * An encrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN DSA PRIVATE KEY-----
+ * Proc-Type: 4,ENCRYPTED
+ * DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0
+ *
+ * [...]
+ * -----END DSA PRIVATE KEY-----
+ *
+ * Note that this format uses MD5 to derive the encryption key, and hence
+ * will not be available on FIPS-compliant systems.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the traditional, non-standard \OpenSSL format
+ * is required.
+ *
+ * Consider using #public_to_pem (X.509 SubjectPublicKeyInfo) or #private_to_pem
+ * (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.
*/
static VALUE
ossl_dsa_export(int argc, VALUE *argv, VALUE self)
@@ -238,8 +280,15 @@ ossl_dsa_export(int argc, VALUE *argv, VALUE self)
* call-seq:
* dsa.to_der -> aString
*
- * Encodes this DSA to its DER encoding.
+ * Serializes a private or public key to a DER-encoding.
+ *
+ * See #to_pem for details.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the traditional, non-standard \OpenSSL format
+ * is required.
*
+ * Consider using #public_to_der or #private_to_der instead.
*/
static VALUE
ossl_dsa_to_der(VALUE self)
diff --git a/ext/openssl/ossl_pkey_ec.c b/ext/openssl/ossl_pkey_ec.c
index 92842f95..4b3a1fd0 100644
--- a/ext/openssl/ossl_pkey_ec.c
+++ b/ext/openssl/ossl_pkey_ec.c
@@ -400,13 +400,61 @@ static VALUE ossl_ec_key_is_private(VALUE self)
/*
* call-seq:
- * key.export([cipher, pass_phrase]) => String
- * key.to_pem([cipher, pass_phrase]) => String
+ * key.export([cipher, password]) => String
+ * key.to_pem([cipher, password]) => String
*
- * Outputs the EC key in PEM encoding. If _cipher_ and _pass_phrase_ are given
- * they will be used to encrypt the key. _cipher_ must be an OpenSSL::Cipher
- * instance. Note that encryption will only be effective for a private key,
- * public keys will always be encoded in plain text.
+ * Serializes a private or public key to a PEM-encoding.
+ *
+ * [When the key contains public components only]
+ *
+ * Serializes it into an X.509 SubjectPublicKeyInfo.
+ * The parameters _cipher_ and _password_ are ignored.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ * [...]
+ * -----END PUBLIC KEY-----
+ *
+ * Consider using #public_to_pem instead. This serializes the key into an
+ * X.509 SubjectPublicKeyInfo regardless of whether it is a public key
+ * or a private key.
+ *
+ * [When the key contains private components, and no parameters are given]
+ *
+ * Serializes it into a SEC 1/RFC 5915 ECPrivateKey.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN EC PRIVATE KEY-----
+ * [...]
+ * -----END EC PRIVATE KEY-----
+ *
+ * [When the key contains private components, and _cipher_ and _password_ are given]
+ *
+ * Serializes it into a SEC 1/RFC 5915 ECPrivateKey
+ * and encrypts it in OpenSSL's traditional PEM encryption format.
+ * _cipher_ must be a cipher name understood by OpenSSL::Cipher.new or an
+ * instance of OpenSSL::Cipher.
+ *
+ * An encrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN EC PRIVATE KEY-----
+ * Proc-Type: 4,ENCRYPTED
+ * DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0
+ *
+ * [...]
+ * -----END EC PRIVATE KEY-----
+ *
+ * Note that this format uses MD5 to derive the encryption key, and hence
+ * will not be available on FIPS-compliant systems.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the SEC 1/RFC 5915 ECPrivateKey format is
+ * required.
+ *
+ * Consider using #public_to_pem (X.509 SubjectPublicKeyInfo) or #private_to_pem
+ * (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.
*/
static VALUE
ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
@@ -426,7 +474,15 @@ ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
* call-seq:
* key.to_der => String
*
- * See the OpenSSL documentation for i2d_ECPrivateKey_bio()
+ * Serializes a private or public key to a DER-encoding.
+ *
+ * See #to_pem for details.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the SEC 1/RFC 5915 ECPrivateKey format is
+ * required.
+ *
+ * Consider using #public_to_der or #private_to_der instead.
*/
static VALUE
ossl_ec_key_to_der(VALUE self)
@@ -530,7 +586,7 @@ static const rb_data_type_t ossl_ec_group_type = {
{
0, ossl_ec_group_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -1115,7 +1171,7 @@ static const rb_data_type_t ossl_ec_point_type = {
{
0, ossl_ec_point_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_pkey_rsa.c b/ext/openssl/ossl_pkey_rsa.c
index 072adabe..389f76f3 100644
--- a/ext/openssl/ossl_pkey_rsa.c
+++ b/ext/openssl/ossl_pkey_rsa.c
@@ -50,8 +50,8 @@ VALUE eRSAError;
/*
* call-seq:
* RSA.new -> rsa
- * RSA.new(encoded_key [, passphrase]) -> rsa
- * RSA.new(encoded_key) { passphrase } -> rsa
+ * RSA.new(encoded_key [, password ]) -> rsa
+ * RSA.new(encoded_key) { password } -> rsa
* RSA.new(size [, exponent]) -> rsa
*
* Generates or loads an \RSA keypair.
@@ -61,9 +61,9 @@ VALUE eRSAError;
* #set_crt_params.
*
* If called with a String, tries to parse as DER or PEM encoding of an \RSA key.
- * Note that, if _passphrase_ is not specified but the key is encrypted with a
- * passphrase, \OpenSSL will prompt for it.
- * See also OpenSSL::PKey.read which can parse keys of any kinds.
+ * Note that if _password_ is not specified, but the key is encrypted with a
+ * password, \OpenSSL will prompt for it.
+ * See also OpenSSL::PKey.read which can parse keys of any kind.
*
* If called with a number, generates a new key pair. This form works as an
* alias of RSA.generate.
@@ -71,7 +71,7 @@ VALUE eRSAError;
* Examples:
* OpenSSL::PKey::RSA.new 2048
* OpenSSL::PKey::RSA.new File.read 'rsa.pem'
- * OpenSSL::PKey::RSA.new File.read('rsa.pem'), 'my pass phrase'
+ * OpenSSL::PKey::RSA.new File.read('rsa.pem'), 'my password'
*/
static VALUE
ossl_rsa_initialize(int argc, VALUE *argv, VALUE self)
@@ -217,13 +217,61 @@ can_export_rsaprivatekey(VALUE self)
/*
* call-seq:
- * rsa.export([cipher, pass_phrase]) => PEM-format String
- * rsa.to_pem([cipher, pass_phrase]) => PEM-format String
- * rsa.to_s([cipher, pass_phrase]) => PEM-format String
+ * rsa.export([cipher, password]) => PEM-format String
+ * rsa.to_pem([cipher, password]) => PEM-format String
+ * rsa.to_s([cipher, password]) => PEM-format String
*
- * Outputs this keypair in PEM encoding. If _cipher_ and _pass_phrase_ are
- * given they will be used to encrypt the key. _cipher_ must be an
- * OpenSSL::Cipher instance.
+ * Serializes a private or public key to a PEM-encoding.
+ *
+ * [When the key contains public components only]
+ *
+ * Serializes it into an X.509 SubjectPublicKeyInfo.
+ * The parameters _cipher_ and _password_ are ignored.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN PUBLIC KEY-----
+ * [...]
+ * -----END PUBLIC KEY-----
+ *
+ * Consider using #public_to_pem instead. This serializes the key into an
+ * X.509 SubjectPublicKeyInfo regardless of whether the key is a public key
+ * or a private key.
+ *
+ * [When the key contains private components, and no parameters are given]
+ *
+ * Serializes it into a PKCS #1 RSAPrivateKey.
+ *
+ * A PEM-encoded key will look like:
+ *
+ * -----BEGIN RSA PRIVATE KEY-----
+ * [...]
+ * -----END RSA PRIVATE KEY-----
+ *
+ * [When the key contains private components, and _cipher_ and _password_ are given]
+ *
+ * Serializes it into a PKCS #1 RSAPrivateKey
+ * and encrypts it in OpenSSL's traditional PEM encryption format.
+ * _cipher_ must be a cipher name understood by OpenSSL::Cipher.new or an
+ * instance of OpenSSL::Cipher.
+ *
+ * An encrypted PEM-encoded key will look like:
+ *
+ * -----BEGIN RSA PRIVATE KEY-----
+ * Proc-Type: 4,ENCRYPTED
+ * DEK-Info: AES-128-CBC,733F5302505B34701FC41F5C0746E4C0
+ *
+ * [...]
+ * -----END RSA PRIVATE KEY-----
+ *
+ * Note that this format uses MD5 to derive the encryption key, and hence
+ * will not be available on FIPS-compliant systems.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the PKCS #1 RSAPrivateKey format is required.
+ *
+ * Consider using #public_to_pem (X.509 SubjectPublicKeyInfo) or #private_to_pem
+ * (PKCS #8 PrivateKeyInfo or EncryptedPrivateKeyInfo) instead.
*/
static VALUE
ossl_rsa_export(int argc, VALUE *argv, VALUE self)
@@ -238,7 +286,14 @@ ossl_rsa_export(int argc, VALUE *argv, VALUE self)
* call-seq:
* rsa.to_der => DER-format String
*
- * Outputs this keypair in DER encoding.
+ * Serializes a private or public key to a DER-encoding.
+ *
+ * See #to_pem for details.
+ *
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the PKCS #1 RSAPrivateKey format is required.
+ *
+ * Consider using #public_to_der or #private_to_der instead.
*/
static VALUE
ossl_rsa_to_der(VALUE self)
diff --git a/ext/openssl/ossl_provider.c b/ext/openssl/ossl_provider.c
new file mode 100644
index 00000000..981c6ccd
--- /dev/null
+++ b/ext/openssl/ossl_provider.c
@@ -0,0 +1,211 @@
+/*
+ * This program is licensed under the same licence as Ruby.
+ * (See the file 'LICENCE'.)
+ */
+#include "ossl.h"
+
+#ifdef OSSL_USE_PROVIDER
+# include <openssl/provider.h>
+
+#define NewProvider(klass) \
+ TypedData_Wrap_Struct((klass), &ossl_provider_type, 0)
+#define SetProvider(obj, provider) do { \
+ if (!(provider)) { \
+ ossl_raise(rb_eRuntimeError, "Provider wasn't initialized."); \
+ } \
+ RTYPEDDATA_DATA(obj) = (provider); \
+} while(0)
+#define GetProvider(obj, provider) do { \
+ TypedData_Get_Struct((obj), OSSL_PROVIDER, &ossl_provider_type, (provider)); \
+ if (!(provider)) { \
+ ossl_raise(rb_eRuntimeError, "PROVIDER wasn't initialized."); \
+ } \
+} while (0)
+
+static const rb_data_type_t ossl_provider_type = {
+ "OpenSSL/Provider",
+ {
+ 0,
+ },
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
+};
+
+/*
+ * Classes
+ */
+/* Document-class: OpenSSL::Provider
+ *
+ * This class is the access to openssl's Provider
+ * See also, https://www.openssl.org/docs/manmaster/man7/provider.html
+ */
+static VALUE cProvider;
+/* Document-class: OpenSSL::Provider::ProviderError
+ *
+ * This is the generic exception for OpenSSL::Provider related errors
+ */
+static VALUE eProviderError;
+
+/*
+ * call-seq:
+ * OpenSSL::Provider.load(name) -> provider
+ *
+ * This method loads and initializes a provider
+ */
+static VALUE
+ossl_provider_s_load(VALUE klass, VALUE name)
+{
+ OSSL_PROVIDER *provider = NULL;
+ VALUE obj;
+
+ const char *provider_name_ptr = StringValueCStr(name);
+
+ provider = OSSL_PROVIDER_load(NULL, provider_name_ptr);
+ if (provider == NULL) {
+ ossl_raise(eProviderError, "Failed to load %s provider", provider_name_ptr);
+ }
+ obj = NewProvider(klass);
+ SetProvider(obj, provider);
+
+ return obj;
+}
+
+struct ary_with_state { VALUE ary; int state; };
+struct rb_push_provider_name_args { OSSL_PROVIDER *prov; VALUE ary; };
+
+static VALUE
+rb_push_provider_name(VALUE rb_push_provider_name_args)
+{
+ struct rb_push_provider_name_args *args = (struct rb_push_provider_name_args *)rb_push_provider_name_args;
+
+ VALUE name = rb_str_new2(OSSL_PROVIDER_get0_name(args->prov));
+ return rb_ary_push(args->ary, name);
+}
+
+static int
+push_provider(OSSL_PROVIDER *prov, void *cbdata)
+{
+ struct ary_with_state *ary_with_state = (struct ary_with_state *)cbdata;
+ struct rb_push_provider_name_args args = { prov, ary_with_state->ary };
+
+ rb_protect(rb_push_provider_name, (VALUE)&args, &ary_with_state->state);
+ if (ary_with_state->state) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/*
+ * call-seq:
+ * OpenSSL::Provider.provider_names -> [provider_name, ...]
+ *
+ * Returns an array of currently loaded provider names.
+ */
+static VALUE
+ossl_provider_s_provider_names(VALUE klass)
+{
+ VALUE ary = rb_ary_new();
+ struct ary_with_state cbdata = { ary, 0 };
+
+ int result = OSSL_PROVIDER_do_all(NULL, &push_provider, (void*)&cbdata);
+ if (result != 1 ) {
+ if (cbdata.state) {
+ rb_jump_tag(cbdata.state);
+ } else {
+ ossl_raise(eProviderError, "Failed to load provider names");
+ }
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * provider.unload -> true
+ *
+ * This method unloads this provider.
+ *
+ * if provider unload fails or already unloaded, it raises OpenSSL::Provider::ProviderError
+ */
+static VALUE
+ossl_provider_unload(VALUE self)
+{
+ OSSL_PROVIDER *prov;
+ if (RTYPEDDATA_DATA(self) == NULL) {
+ ossl_raise(eProviderError, "Provider already unloaded.");
+ }
+ GetProvider(self, prov);
+
+ int result = OSSL_PROVIDER_unload(prov);
+
+ if (result != 1) {
+ ossl_raise(eProviderError, "Failed to unload provider");
+ }
+ RTYPEDDATA_DATA(self) = NULL;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * provider.name -> string
+ *
+ * Get the name of this provider.
+ *
+ * if this provider is already unloaded, it raises OpenSSL::Provider::ProviderError
+ */
+static VALUE
+ossl_provider_get_name(VALUE self)
+{
+ OSSL_PROVIDER *prov;
+ if (RTYPEDDATA_DATA(self) == NULL) {
+ ossl_raise(eProviderError, "Provider already unloaded.");
+ }
+ GetProvider(self, prov);
+
+ return rb_str_new2(OSSL_PROVIDER_get0_name(prov));
+}
+
+/*
+ * call-seq:
+ * provider.inspect -> string
+ *
+ * Pretty prints this provider.
+ */
+static VALUE
+ossl_provider_inspect(VALUE self)
+{
+ OSSL_PROVIDER *prov;
+ if (RTYPEDDATA_DATA(self) == NULL ) {
+ return rb_sprintf("#<%"PRIsVALUE" unloaded provider>", rb_obj_class(self));
+ }
+ GetProvider(self, prov);
+
+ return rb_sprintf("#<%"PRIsVALUE" name=\"%s\">",
+ rb_obj_class(self), OSSL_PROVIDER_get0_name(prov));
+}
+
+void
+Init_ossl_provider(void)
+{
+#if 0
+ mOSSL = rb_define_module("OpenSSL");
+ eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
+#endif
+
+ cProvider = rb_define_class_under(mOSSL, "Provider", rb_cObject);
+ eProviderError = rb_define_class_under(cProvider, "ProviderError", eOSSLError);
+
+ rb_undef_alloc_func(cProvider);
+ rb_define_singleton_method(cProvider, "load", ossl_provider_s_load, 1);
+ rb_define_singleton_method(cProvider, "provider_names", ossl_provider_s_provider_names, 0);
+
+ rb_define_method(cProvider, "unload", ossl_provider_unload, 0);
+ rb_define_method(cProvider, "name", ossl_provider_get_name, 0);
+ rb_define_method(cProvider, "inspect", ossl_provider_inspect, 0);
+}
+#else
+void
+Init_ossl_provider(void)
+{
+}
+#endif
diff --git a/ext/openssl/ossl_provider.h b/ext/openssl/ossl_provider.h
new file mode 100644
index 00000000..1d69cb1e
--- /dev/null
+++ b/ext/openssl/ossl_provider.h
@@ -0,0 +1,5 @@
+#if !defined(OSSL_PROVIDER_H)
+#define OSSL_PROVIDER_H
+
+void Init_ossl_provider(void);
+#endif
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index f6399266..236d455f 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -77,7 +77,7 @@ static const rb_data_type_t ossl_sslctx_type = {
{
ossl_sslctx_mark, ossl_sslctx_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -885,9 +885,9 @@ ossl_sslctx_setup(VALUE self)
if (ca_path && !SSL_CTX_load_verify_dir(ctx, ca_path))
ossl_raise(eSSLError, "SSL_CTX_load_verify_dir");
#else
- if(ca_file || ca_path){
- if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
- rb_warning("can't set verify locations");
+ if (ca_file || ca_path) {
+ if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
+ ossl_raise(eSSLError, "SSL_CTX_load_verify_locations");
}
#endif
@@ -1553,6 +1553,10 @@ ossl_ssl_mark(void *ptr)
{
SSL *ssl = ptr;
rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx));
+
+ // Note: this reference is stored as @verify_callback so we don't need to mark it.
+ // However we do need to ensure GC compaction won't move it, hence why
+ // we call rb_gc_mark here.
rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx));
}
@@ -1567,7 +1571,7 @@ const rb_data_type_t ossl_ssl_type = {
{
ossl_ssl_mark, ossl_ssl_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -1646,6 +1650,8 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
SSL_set_ex_data(ssl, ossl_ssl_ex_ptr_idx, (void *)self);
SSL_set_info_callback(ssl, ssl_info_cb);
verify_cb = rb_attr_get(v_ctx, id_i_verify_callback);
+ // We don't need to trigger a write barrier because it's already
+ // an instance variable of this object.
SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)verify_cb);
rb_call_super(0, NULL);
@@ -1653,6 +1659,17 @@ ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
return self;
}
+#ifndef HAVE_RB_IO_DESCRIPTOR
+static int
+io_descriptor_fallback(VALUE io)
+{
+ rb_io_t *fptr;
+ GetOpenFile(io, fptr);
+ return fptr->fd;
+}
+#define rb_io_descriptor io_descriptor_fallback
+#endif
+
static VALUE
ossl_ssl_setup(VALUE self)
{
@@ -1668,8 +1685,8 @@ ossl_ssl_setup(VALUE self)
GetOpenFile(io, fptr);
rb_io_check_readable(fptr);
rb_io_check_writable(fptr);
- if (!SSL_set_fd(ssl, TO_SOCKET(fptr->fd)))
- ossl_raise(eSSLError, "SSL_set_fd");
+ if (!SSL_set_fd(ssl, TO_SOCKET(rb_io_descriptor(io))))
+ ossl_raise(eSSLError, "SSL_set_fd");
return Qtrue;
}
@@ -1709,21 +1726,25 @@ no_exception_p(VALUE opts)
#endif
static void
-io_wait_writable(rb_io_t *fptr)
+io_wait_writable(VALUE io)
{
#ifdef HAVE_RB_IO_MAYBE_WAIT
- rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT);
+ rb_io_maybe_wait_writable(errno, io, RUBY_IO_TIMEOUT_DEFAULT);
#else
+ rb_io_t *fptr;
+ GetOpenFile(io, fptr);
rb_io_wait_writable(fptr->fd);
#endif
}
static void
-io_wait_readable(rb_io_t *fptr)
+io_wait_readable(VALUE io)
{
#ifdef HAVE_RB_IO_MAYBE_WAIT
- rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT);
+ rb_io_maybe_wait_readable(errno, io, RUBY_IO_TIMEOUT_DEFAULT);
#else
+ rb_io_t *fptr;
+ GetOpenFile(io, fptr);
rb_io_wait_readable(fptr->fd);
#endif
}
@@ -1732,75 +1753,74 @@ static VALUE
ossl_start_ssl(VALUE self, int (*func)(SSL *), const char *funcname, VALUE opts)
{
SSL *ssl;
- rb_io_t *fptr;
int ret, ret2;
VALUE cb_state;
int nonblock = opts != Qfalse;
-#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
- unsigned long err;
-#endif
rb_ivar_set(self, ID_callback_state, Qnil);
GetSSL(self, ssl);
- GetOpenFile(rb_attr_get(self, id_i_io), fptr);
- for(;;){
- ret = func(ssl);
+ VALUE io = rb_attr_get(self, id_i_io);
+ for (;;) {
+ ret = func(ssl);
- cb_state = rb_attr_get(self, ID_callback_state);
+ cb_state = rb_attr_get(self, ID_callback_state);
if (!NIL_P(cb_state)) {
- /* must cleanup OpenSSL error stack before re-raising */
- ossl_clear_error();
- rb_jump_tag(NUM2INT(cb_state));
- }
+ /* must cleanup OpenSSL error stack before re-raising */
+ ossl_clear_error();
+ rb_jump_tag(NUM2INT(cb_state));
+ }
- if (ret > 0)
- break;
+ if (ret > 0)
+ break;
- switch((ret2 = ssl_get_error(ssl, ret))){
- case SSL_ERROR_WANT_WRITE:
+ switch ((ret2 = ssl_get_error(ssl, ret))) {
+ case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
- io_wait_writable(fptr);
+ io_wait_writable(io);
continue;
- case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
- io_wait_readable(fptr);
+ io_wait_readable(io);
continue;
- case SSL_ERROR_SYSCALL:
+ case SSL_ERROR_SYSCALL:
#ifdef __APPLE__
/* See ossl_ssl_write_internal() */
if (errno == EPROTOTYPE)
continue;
#endif
- if (errno) rb_sys_fail(funcname);
- ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s",
- funcname, ret2, errno, peeraddr_ip_str(self), SSL_state_string_long(ssl));
-
+ if (errno) rb_sys_fail(funcname);
+ /* fallthrough */
+ default: {
+ VALUE error_append = Qnil;
#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
- case SSL_ERROR_SSL:
- err = ERR_peek_last_error();
- if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
- ERR_GET_REASON(err) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
- const char *err_msg = ERR_reason_error_string(err),
- *verify_msg = X509_verify_cert_error_string(SSL_get_verify_result(ssl));
- if (!err_msg)
- err_msg = "(null)";
- if (!verify_msg)
- verify_msg = "(null)";
- ossl_clear_error(); /* let ossl_raise() not append message */
- ossl_raise(eSSLError, "%s returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s: %s (%s)",
- funcname, ret2, errno, peeraddr_ip_str(self), SSL_state_string_long(ssl),
- err_msg, verify_msg);
- }
+ unsigned long err = ERR_peek_last_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
+ ERR_GET_REASON(err) == SSL_R_CERTIFICATE_VERIFY_FAILED) {
+ const char *err_msg = ERR_reason_error_string(err),
+ *verify_msg = X509_verify_cert_error_string(SSL_get_verify_result(ssl));
+ if (!err_msg)
+ err_msg = "(null)";
+ if (!verify_msg)
+ verify_msg = "(null)";
+ ossl_clear_error(); /* let ossl_raise() not append message */
+ error_append = rb_sprintf(": %s (%s)", err_msg, verify_msg);
+ }
#endif
- /* fallthrough */
- default:
- ossl_raise(eSSLError, "%s returned=%d errno=%d peeraddr=%"PRIsVALUE" state=%s",
- funcname, ret2, errno, peeraddr_ip_str(self), SSL_state_string_long(ssl));
- }
+ 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),
+ SSL_state_string_long(ssl),
+ error_append);
+ }
+ }
}
return self;
@@ -1906,8 +1926,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
SSL *ssl;
int ilen;
VALUE len, str;
- rb_io_t *fptr;
- VALUE io, opts = Qnil;
+ VALUE opts = Qnil;
if (nonblock) {
rb_scan_args(argc, argv, "11:", &len, &str, &opts);
@@ -1932,8 +1951,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
if (ilen == 0)
return str;
- io = rb_attr_get(self, id_i_io);
- GetOpenFile(io, fptr);
+ VALUE io = rb_attr_get(self, id_i_io);
rb_str_locktmp(str);
for (;;) {
@@ -1953,7 +1971,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
}
- io_wait_writable(fptr);
+ io_wait_writable(io);
continue;
case SSL_ERROR_WANT_READ:
if (nonblock) {
@@ -1961,7 +1979,7 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
}
- io_wait_readable(fptr);
+ io_wait_readable(io);
continue;
case SSL_ERROR_SYSCALL:
if (!ERR_peek_error()) {
@@ -2027,14 +2045,14 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
SSL *ssl;
rb_io_t *fptr;
int num, nonblock = opts != Qfalse;
- VALUE tmp, io;
+ VALUE tmp;
GetSSL(self, ssl);
if (!ssl_started(ssl))
rb_raise(eSSLError, "SSL session is not started yet");
tmp = rb_str_new_frozen(StringValue(str));
- io = rb_attr_get(self, id_i_io);
+ VALUE io = rb_attr_get(self, id_i_io);
GetOpenFile(io, fptr);
/* SSL_write(3ssl) manpage states num == 0 is undefined */
@@ -2050,12 +2068,12 @@ ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
case SSL_ERROR_WANT_WRITE:
if (no_exception_p(opts)) { return sym_wait_writable; }
write_would_block(nonblock);
- io_wait_writable(fptr);
+ io_wait_writable(io);
continue;
case SSL_ERROR_WANT_READ:
if (no_exception_p(opts)) { return sym_wait_readable; }
read_would_block(nonblock);
- io_wait_readable(fptr);
+ io_wait_readable(io);
continue;
case SSL_ERROR_SYSCALL:
#ifdef __APPLE__
diff --git a/ext/openssl/ossl_ssl_session.c b/ext/openssl/ossl_ssl_session.c
index 139a474b..c5df902c 100644
--- a/ext/openssl/ossl_ssl_session.c
+++ b/ext/openssl/ossl_ssl_session.c
@@ -19,7 +19,7 @@ const rb_data_type_t ossl_ssl_session_type = {
{
0, ossl_ssl_session_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE ossl_ssl_session_alloc(VALUE klass)
diff --git a/ext/openssl/ossl_ts.c b/ext/openssl/ossl_ts.c
index b33ff10c..f698bdc7 100644
--- a/ext/openssl/ossl_ts.c
+++ b/ext/openssl/ossl_ts.c
@@ -83,7 +83,7 @@ static const rb_data_type_t ossl_ts_req_type = {
{
0, ossl_ts_req_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -97,7 +97,7 @@ static const rb_data_type_t ossl_ts_resp_type = {
{
0, ossl_ts_resp_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static void
@@ -111,7 +111,7 @@ static const rb_data_type_t ossl_ts_token_info_type = {
{
0, ossl_ts_token_info_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
diff --git a/ext/openssl/ossl_x509attr.c b/ext/openssl/ossl_x509attr.c
index 60846cfe..d1d8bb5e 100644
--- a/ext/openssl/ossl_x509attr.c
+++ b/ext/openssl/ossl_x509attr.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509attr_type = {
{
0, ossl_x509attr_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509cert.c b/ext/openssl/ossl_x509cert.c
index 94435416..aa6b9bb7 100644
--- a/ext/openssl/ossl_x509cert.c
+++ b/ext/openssl/ossl_x509cert.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509_type = {
{
0, ossl_x509_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509crl.c b/ext/openssl/ossl_x509crl.c
index 6c1d9153..80e29f9d 100644
--- a/ext/openssl/ossl_x509crl.c
+++ b/ext/openssl/ossl_x509crl.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509crl_type = {
{
0, ossl_x509crl_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509ext.c b/ext/openssl/ossl_x509ext.c
index e54102c7..192d09bd 100644
--- a/ext/openssl/ossl_x509ext.c
+++ b/ext/openssl/ossl_x509ext.c
@@ -55,7 +55,7 @@ static const rb_data_type_t ossl_x509ext_type = {
{
0, ossl_x509ext_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -108,7 +108,7 @@ static const rb_data_type_t ossl_x509extfactory_type = {
{
0, ossl_x509extfactory_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -209,15 +209,16 @@ ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self)
int nid;
VALUE rconf;
CONF *conf;
+ const char *oid_cstr = NULL;
rb_scan_args(argc, argv, "21", &oid, &value, &critical);
- StringValueCStr(oid);
StringValue(value);
if(NIL_P(critical)) critical = Qfalse;
- nid = OBJ_ln2nid(RSTRING_PTR(oid));
- if(!nid) nid = OBJ_sn2nid(RSTRING_PTR(oid));
- if(!nid) ossl_raise(eX509ExtError, "unknown OID `%"PRIsVALUE"'", oid);
+ oid_cstr = StringValueCStr(oid);
+ nid = OBJ_ln2nid(oid_cstr);
+ if (nid != NID_undef)
+ oid_cstr = OBJ_nid2sn(nid);
valstr = rb_str_new2(RTEST(critical) ? "critical," : "");
rb_str_append(valstr, value);
@@ -228,7 +229,12 @@ ossl_x509extfactory_create_ext(int argc, VALUE *argv, VALUE self)
rconf = rb_iv_get(self, "@config");
conf = NIL_P(rconf) ? NULL : GetConfig(rconf);
X509V3_set_nconf(ctx, conf);
- ext = X509V3_EXT_nconf_nid(conf, ctx, nid, RSTRING_PTR(valstr));
+
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_IS_LIBRESSL
+ ext = X509V3_EXT_nconf(conf, ctx, oid_cstr, RSTRING_PTR(valstr));
+#else
+ ext = X509V3_EXT_nconf(conf, ctx, (char *)oid_cstr, RSTRING_PTR(valstr));
+#endif
X509V3_set_ctx_nodb(ctx);
if (!ext){
ossl_raise(eX509ExtError, "%"PRIsVALUE" = %"PRIsVALUE, oid, valstr);
diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c
index 13a2b2c0..9591912f 100644
--- a/ext/openssl/ossl_x509name.c
+++ b/ext/openssl/ossl_x509name.c
@@ -46,7 +46,7 @@ static const rb_data_type_t ossl_x509name_type = {
{
0, ossl_x509name_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509req.c b/ext/openssl/ossl_x509req.c
index 77a7d3f2..f0581851 100644
--- a/ext/openssl/ossl_x509req.c
+++ b/ext/openssl/ossl_x509req.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509req_type = {
{
0, ossl_x509req_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509revoked.c b/ext/openssl/ossl_x509revoked.c
index 10b8aa4a..108447c8 100644
--- a/ext/openssl/ossl_x509revoked.c
+++ b/ext/openssl/ossl_x509revoked.c
@@ -41,7 +41,7 @@ static const rb_data_type_t ossl_x509rev_type = {
{
0, ossl_x509rev_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
diff --git a/ext/openssl/ossl_x509store.c b/ext/openssl/ossl_x509store.c
index 7c546187..f27381ca 100644
--- a/ext/openssl/ossl_x509store.c
+++ b/ext/openssl/ossl_x509store.c
@@ -116,6 +116,9 @@ static void
ossl_x509store_mark(void *ptr)
{
X509_STORE *store = ptr;
+ // Note: this reference is stored as @verify_callback so we don't need to mark it.
+ // However we do need to ensure GC compaction won't move it, hence why
+ // we call rb_gc_mark here.
rb_gc_mark((VALUE)X509_STORE_get_ex_data(store, store_ex_verify_cb_idx));
}
@@ -130,7 +133,7 @@ static const rb_data_type_t ossl_x509store_type = {
{
ossl_x509store_mark, ossl_x509store_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
/*
@@ -187,8 +190,9 @@ ossl_x509store_set_vfy_cb(VALUE self, VALUE cb)
X509_STORE *store;
GetX509Store(self, store);
- X509_STORE_set_ex_data(store, store_ex_verify_cb_idx, (void *)cb);
rb_iv_set(self, "@verify_callback", cb);
+ // We don't need to trigger a write barrier because `rb_iv_set` did it.
+ X509_STORE_set_ex_data(store, store_ex_verify_cb_idx, (void *)cb);
return cb;
}
@@ -507,6 +511,9 @@ static void
ossl_x509stctx_mark(void *ptr)
{
X509_STORE_CTX *ctx = ptr;
+ // Note: this reference is stored as @verify_callback so we don't need to mark it.
+ // However we do need to ensure GC compaction won't move it, hence why
+ // we call rb_gc_mark here.
rb_gc_mark((VALUE)X509_STORE_CTX_get_ex_data(ctx, stctx_ex_verify_cb_idx));
}
@@ -526,7 +533,7 @@ static const rb_data_type_t ossl_x509stctx_type = {
{
ossl_x509stctx_mark, ossl_x509stctx_free,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -614,8 +621,8 @@ ossl_x509stctx_verify(VALUE self)
X509_STORE_CTX *ctx;
GetX509StCtx(self, ctx);
- X509_STORE_CTX_set_ex_data(ctx, stctx_ex_verify_cb_idx,
- (void *)rb_iv_get(self, "@verify_callback"));
+ VALUE cb = rb_iv_get(self, "@verify_callback");
+ X509_STORE_CTX_set_ex_data(ctx, stctx_ex_verify_cb_idx, (void *)cb);
switch (X509_verify_cert(ctx)) {
case 1:
diff --git a/lib/openssl/buffering.rb b/lib/openssl/buffering.rb
index aa4adee3..4df90746 100644
--- a/lib/openssl/buffering.rb
+++ b/lib/openssl/buffering.rb
@@ -93,9 +93,7 @@ module OpenSSL::Buffering
nil
else
size = @rbuffer.size unless size
- ret = @rbuffer[0, size]
- @rbuffer[0, size] = ""
- ret
+ @rbuffer.slice!(0, size)
end
end
@@ -106,8 +104,7 @@ module OpenSSL::Buffering
#
# Get the next 8bit byte from `ssl`. Returns `nil` on EOF
def getbyte
- byte = read(1)
- byte && byte.unpack1("C")
+ read(1)&.ord
end
##
diff --git a/lib/openssl/digest.rb b/lib/openssl/digest.rb
index 2ff8398e..0f35ddad 100644
--- a/lib/openssl/digest.rb
+++ b/lib/openssl/digest.rb
@@ -18,13 +18,9 @@ module OpenSSL
# Return the hash value computed with _name_ Digest. _name_ is either the
# long name or short name of a supported digest algorithm.
#
- # === Examples
+ # === Example
#
# OpenSSL::Digest.digest("SHA256", "abc")
- #
- # which is equivalent to:
- #
- # OpenSSL::Digest.digest('SHA256', "abc")
def self.digest(name, data)
super(data, name)
diff --git a/lib/openssl/ssl.rb b/lib/openssl/ssl.rb
index ea8bb2a1..e557b8b4 100644
--- a/lib/openssl/ssl.rb
+++ b/lib/openssl/ssl.rb
@@ -34,21 +34,21 @@ module OpenSSL
}
if defined?(OpenSSL::PKey::DH)
- DEFAULT_2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+ DH_ffdhe2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
-----BEGIN DH PARAMETERS-----
-MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
-JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
-VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
-YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
-1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
-7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
+MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----
_end_of_pem_
- private_constant :DEFAULT_2048
+ private_constant :DH_ffdhe2048
DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
warn "using default DH parameters." if $VERBOSE
- DEFAULT_2048
+ DH_ffdhe2048
}
end
@@ -494,7 +494,7 @@ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
unless ctx.session_id_context
# see #6137 - session id may not exceed 32 bytes
prng = ::Random.new($0.hash)
- session_id = prng.bytes(16).unpack('H*')[0]
+ session_id = prng.bytes(16).unpack1('H*')
@ctx.session_id_context = session_id
end
@start_immediately = true
diff --git a/lib/openssl/version.rb b/lib/openssl/version.rb
index 4163f550..9315a793 100644
--- a/lib/openssl/version.rb
+++ b/lib/openssl/version.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module OpenSSL
- VERSION = "3.1.0"
+ VERSION = "3.2.0"
end
diff --git a/openssl.gemspec b/openssl.gemspec
index 8d83b691..2765f554 100644
--- a/openssl.gemspec
+++ b/openssl.gemspec
@@ -1,21 +1,27 @@
Gem::Specification.new do |spec|
spec.name = "openssl"
- spec.version = "3.1.0"
+ spec.version = "3.2.0"
spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"]
spec.email = ["ruby-core@ruby-lang.org"]
- spec.summary = %q{OpenSSL provides SSL, TLS and general purpose cryptography.}
- spec.description = %q{It wraps the OpenSSL library.}
+ spec.summary = %q{SSL/TLS and general-purpose cryptography for Ruby}
+ spec.description = %q{OpenSSL for Ruby provides access to SSL/TLS and general-purpose cryptography based on the OpenSSL library.}
spec.homepage = "https://github.com/ruby/openssl"
spec.license = "Ruby"
- spec.files = Dir["lib/**/*.rb", "ext/**/*.{c,h,rb}", "*.md", "BSDL", "LICENSE.txt"]
- spec.require_paths = ["lib"]
- spec.extensions = ["ext/openssl/extconf.rb"]
+ if Gem::Platform === spec.platform and spec.platform =~ 'java' or RUBY_ENGINE == 'jruby'
+ spec.platform = "java"
+ spec.files = []
+ spec.add_runtime_dependency('jruby-openssl', '~> 0.14')
+ else
+ spec.files = Dir["lib/**/*.rb", "ext/**/*.{c,h,rb}", "*.md", "BSDL", "LICENSE.txt"]
+ spec.require_paths = ["lib"]
+ spec.extensions = ["ext/openssl/extconf.rb"]
+ end
spec.extra_rdoc_files = Dir["*.md"]
spec.rdoc_options = ["--main", "README.md"]
- spec.required_ruby_version = ">= 2.6.0"
+ spec.required_ruby_version = ">= 2.7.0"
spec.metadata["msys2_mingw_dependencies"] = "openssl"
end
diff --git a/test/openssl/envutil.rb b/test/openssl/envutil.rb
deleted file mode 100644
index 1ab36ff3..00000000
--- a/test/openssl/envutil.rb
+++ /dev/null
@@ -1,287 +0,0 @@
-# -*- coding: us-ascii -*-
-require "timeout"
-require "rbconfig"
-require "pp"
-
-module EnvUtil
- def rubybin
- ENV["RUBY"] || RbConfig.ruby
- end
- module_function :rubybin
-
- LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
-
- def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false,
- encoding: nil, timeout: 10, reprieve: 1,
- stdout_filter: nil, stderr_filter: nil,
- rubybin: EnvUtil.rubybin,
- **opt)
- in_c, in_p = IO.pipe
- out_p, out_c = IO.pipe if capture_stdout
- err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout
- opt[:in] = in_c
- opt[:out] = out_c if capture_stdout
- opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr
- if encoding
- out_p.set_encoding(encoding) if out_p
- err_p.set_encoding(encoding) if err_p
- end
- c = "C"
- child_env = {}
- LANG_ENVS.each {|lc| child_env[lc] = c}
- if Array === args and Hash === args.first
- child_env.update(args.shift)
- end
- args = [args] if args.kind_of?(String)
- pid = spawn(child_env, rubybin, *args, **opt)
- in_c.close
- out_c.close if capture_stdout
- err_c.close if capture_stderr && capture_stderr != :merge_to_stdout
- if block_given?
- return yield in_p, out_p, err_p, pid
- else
- th_stdout = Thread.new { out_p.read } if capture_stdout
- th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout
- in_p.write stdin_data.to_str unless stdin_data.empty?
- in_p.close
- if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
- stdout = th_stdout.value if capture_stdout
- stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout
- else
- signal = /mswin|mingw/ =~ RUBY_PLATFORM ? :KILL : :TERM
- case pgroup = opt[:pgroup]
- when 0, true
- pgroup = -pid
- when nil, false
- pgroup = pid
- end
- begin
- Process.kill signal, pgroup
- Timeout.timeout((reprieve unless signal == :KILL)) do
- Process.wait(pid)
- end
- rescue Errno::ESRCH
- break
- rescue Timeout::Error
- raise if signal == :KILL
- signal = :KILL
- else
- break
- end while true
- bt = caller_locations
- raise Timeout::Error, "execution of #{bt.shift.label} expired", bt.map(&:to_s)
- end
- out_p.close if capture_stdout
- err_p.close if capture_stderr && capture_stderr != :merge_to_stdout
- Process.wait pid
- status = $?
- stdout = stdout_filter.call(stdout) if stdout_filter
- stderr = stderr_filter.call(stderr) if stderr_filter
- return stdout, stderr, status
- end
- ensure
- [th_stdout, th_stderr].each do |th|
- th.kill if th
- end
- [in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
- io.close if io && !io.closed?
- end
- [th_stdout, th_stderr].each do |th|
- th.join if th
- end
- end
- module_function :invoke_ruby
-
- def verbose_warning
- class << (stderr = "".dup)
- alias write <<
- end
- stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
- yield stderr
- return $stderr
- ensure
- stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
- end
- module_function :verbose_warning
-
- def suppress_warning
- verbose, $VERBOSE = $VERBOSE, nil
- yield
- ensure
- $VERBOSE = verbose
- end
- module_function :suppress_warning
-
- if /darwin/ =~ RUBY_PLATFORM
- DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports")
- DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'
- def self.diagnostic_reports(signame, cmd, pid, now)
- return unless %w[ABRT QUIT SEGV ILL].include?(signame)
- cmd = File.basename(cmd)
- path = DIAGNOSTIC_REPORTS_PATH
- timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT
- pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash"
- first = true
- 30.times do
- first ? (first = false) : sleep(0.1)
- Dir.glob(pat) do |name|
- log = File.read(name) rescue next
- if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log
- File.unlink(name)
- File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil
- return log
- end
- end
- end
- nil
- end
- else
- def self.diagnostic_reports(signame, cmd, pid, now)
- end
- end
-end
-
-module Test
- module Unit
- module Assertions
- FailDesc = proc do |status, message = "", out = ""|
- pid = status.pid
- now = Time.now
- faildesc = proc do
- if signo = status.termsig
- signame = Signal.signame(signo)
- sigdesc = "signal #{signo}"
- end
- log = EnvUtil.diagnostic_reports(signame, EnvUtil.rubybin, pid, now)
- if signame
- sigdesc = "SIG#{signame} (#{sigdesc})"
- end
- if status.coredump?
- sigdesc << " (core dumped)"
- end
- full_message = ''
- if message and !message.empty?
- full_message << message << "\n"
- end
- full_message << "pid #{pid} killed by #{sigdesc}"
- if out and !out.empty?
- full_message << "\n#{out.gsub(/^/, '| ')}"
- full_message << "\n" if /\n\z/ !~ full_message
- end
- if log
- full_message << "\n#{log.gsub(/^/, '| ')}"
- end
- full_message
- end
- faildesc
- end
-
- ABORT_SIGNALS = Signal.list.values_at(*%w"ILL ABRT BUS SEGV")
-
- def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **opt)
- unless file and line
- loc, = caller_locations(1,1)
- file ||= loc.path
- line ||= loc.lineno
- end
- line -= 6 # lines until src
- src = <<eom
-# -*- coding: #{src.encoding}; -*-
- require 'test/unit';include Test::Unit::Assertions
- END {
- puts [Marshal.dump($!)].pack('m')#, "assertions=\#{self._assertions}"
- exit
- }
- def pend(msg = nil) $stdout.syswrite [Marshal.dump(msg.to_s)].pack("m"); exit! 0 end
-#{src}
- class Test::Unit::Runner
- @@stop_auto_run = true
- end
-eom
- args = args.dup
- args.insert((Hash === args.first ? 1 : 0), "--disable=gems", *$:.map {|l| "-I#{l}"})
- stdout, stderr, status = EnvUtil.invoke_ruby(args, src, true, true, **opt)
- abort = status.coredump? || (status.signaled? && ABORT_SIGNALS.include?(status.termsig))
- assert(!abort, FailDesc[status, nil, stderr])
- #self._assertions += stdout[/^assertions=(\d+)/, 1].to_i
- begin
- res = Marshal.load(stdout.unpack("m")[0])
- rescue => marshal_error
- ignore_stderr = nil
- end
- if res.is_a?(String)
- pend res
- elsif res
- if bt = res.backtrace
- bt.each do |l|
- l.sub!(/\A-:(\d+)/){"#{file}:#{line + $1.to_i}"}
- end
- bt.concat(caller)
- else
- res.set_backtrace(caller)
- end
- raise res
- end
-
- # really is it succeed?
- unless ignore_stderr
- # the body of assert_separately must not output anything to detect error
- assert_equal("", stderr, "assert_separately failed with error message")
- end
- assert_equal(0, status, "assert_separately failed: '#{stderr}'")
- raise marshal_error if marshal_error
- end
-
- def assert_warning(pat, msg = nil)
- stderr = EnvUtil.verbose_warning {
- yield
- }
- if Regexp === pat
- assert_match pat, stderr, msg
- else
- assert_equal pat, stderr, msg
- end
- end
-
- def message msg = nil, ending = ".", &default
- proc {
- msg = msg.call.chomp(".") if Proc === msg
- custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
- "#{custom_message}#{default.call}#{ending}"
- }
- end
-
- # threads should respond to shift method.
- # Array can be used.
- def assert_join_threads(threads, message = nil)
- errs = []
- values = []
- while th = threads.shift
- begin
- values << th.value
- rescue Exception
- errs << [th, $!]
- end
- end
- if !errs.empty?
- msg = "exceptions on #{errs.length} threads:\n" +
- errs.map {|t, err|
- "#{t.inspect}:\n" +
- err.backtrace.map.with_index {|line, i|
- if i == 0
- "#{line}: #{err.message} (#{err.class})"
- else
- "\tfrom #{line}"
- end
- }.join("\n")
- }.join("\n---\n")
- if message
- msg = "#{message}\n#{msg}"
- end
- raise Test::Unit::AssertionFailedError, msg
- end
- values
- end
- end
- end
-end
diff --git a/test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl b/test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl
new file mode 100644
index 00000000..be0768d5
--- /dev/null
+++ b/test/openssl/fixtures/ssl/openssl_fips.cnf.tmpl
@@ -0,0 +1,19 @@
+config_diagnostics = 1
+openssl_conf = openssl_init
+
+# It seems that the .include needs an absolute path.
+.include OPENSSL_DIR/ssl/fipsmodule.cnf
+
+[openssl_init]
+providers = provider_sect
+alg_section = algorithm_sect
+
+[provider_sect]
+fips = fips_sect
+base = base_sect
+
+[base_sect]
+activate = 1
+
+[algorithm_sect]
+default_properties = fips=yes
diff --git a/test/openssl/test_cipher.rb b/test/openssl/test_cipher.rb
index 3c19b76e..41885fd5 100644
--- a/test/openssl/test_cipher.rb
+++ b/test/openssl/test_cipher.rb
@@ -205,7 +205,7 @@ class OpenSSL::TestCipher < OpenSSL::TestCase
assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct2) }
end if has_cipher?("aes-128-ccm") &&
OpenSSL::Cipher.new("aes-128-ccm").authenticated? &&
- OpenSSL::OPENSSL_VERSION_NUMBER >= 0x1010103f # version >= 1.1.1c
+ openssl?(1, 1, 1, 0x03, 0xf) # version >= 1.1.1c
def test_aes_gcm
# GCM spec Appendix B Test Case 4
diff --git a/test/openssl/test_config.rb b/test/openssl/test_config.rb
index 24a215a4..6dbb9c61 100644
--- a/test/openssl/test_config.rb
+++ b/test/openssl/test_config.rb
@@ -91,22 +91,19 @@ __EOC__
assert_equal('123baz456bar798', c['dollar']['qux'])
assert_equal('123baz456bar798.123baz456bar798', c['dollar']['quxx'])
- excn = assert_raise(OpenSSL::ConfigError) do
+ assert_raise_with_message(OpenSSL::ConfigError, /error in line 1: variable has no value/) do
OpenSSL::Config.parse("foo = $bar")
end
- assert_equal("error in line 1: variable has no value", excn.message)
- excn = assert_raise(OpenSSL::ConfigError) do
+ assert_raise_with_message(OpenSSL::ConfigError, /error in line 1: no close brace/) do
OpenSSL::Config.parse("foo = $(bar")
end
- assert_equal("error in line 1: no close brace", excn.message)
- excn = assert_raise(OpenSSL::ConfigError) do
+ assert_raise_with_message(OpenSSL::ConfigError, /error in line 1: missing equal sign/) do
OpenSSL::Config.parse("f o =b ar # no space in key")
end
- assert_equal("error in line 1: missing equal sign", excn.message)
- excn = assert_raise(OpenSSL::ConfigError) do
+ assert_raise_with_message(OpenSSL::ConfigError, /error in line 7: missing close square bracket/) do
OpenSSL::Config.parse(<<__EOC__)
# comment 1 # comments
@@ -117,7 +114,6 @@ __EOC__
[third # section not terminated
__EOC__
end
- assert_equal("error in line 7: missing close square bracket", excn.message)
end
def test_s_parse_include
diff --git a/test/openssl/test_digest.rb b/test/openssl/test_digest.rb
index 84c128c1..b0b5b9be 100644
--- a/test/openssl/test_digest.rb
+++ b/test/openssl/test_digest.rb
@@ -67,7 +67,7 @@ class OpenSSL::TestDigest < OpenSSL::TestCase
end
def encode16(str)
- str.unpack("H*").first
+ str.unpack1("H*")
end
def test_sha2
diff --git a/test/openssl/test_engine.rb b/test/openssl/test_engine.rb
index 1ede6ed0..b6025f91 100644
--- a/test/openssl/test_engine.rb
+++ b/test/openssl/test_engine.rb
@@ -26,7 +26,7 @@ class OpenSSL::TestEngine < OpenSSL::TestCase
with_openssl <<-'end;'
orig = OpenSSL::Engine.engines
pend "'openssl' is already loaded" if orig.any? { |e| e.id == "openssl" }
- engine = get_engine
+ engine = OpenSSL::Engine.by_id("openssl")
assert_not_nil(engine)
assert_equal(1, OpenSSL::Engine.engines.size - orig.size)
end;
@@ -34,7 +34,7 @@ class OpenSSL::TestEngine < OpenSSL::TestCase
def test_openssl_engine_id_name_inspect
with_openssl <<-'end;'
- engine = get_engine
+ engine = OpenSSL::Engine.by_id("openssl")
assert_equal("openssl", engine.id)
assert_not_nil(engine.name)
assert_not_nil(engine.inspect)
@@ -43,7 +43,7 @@ class OpenSSL::TestEngine < OpenSSL::TestCase
def test_openssl_engine_digest_sha1
with_openssl <<-'end;'
- engine = get_engine
+ engine = OpenSSL::Engine.by_id("openssl")
digest = engine.digest("SHA1")
assert_not_nil(digest)
data = "test"
@@ -59,12 +59,21 @@ class OpenSSL::TestEngine < OpenSSL::TestCase
end
with_openssl(<<-'end;', ignore_stderr: true)
- engine = get_engine
+ engine = OpenSSL::Engine.by_id("openssl")
algo = "RC4"
data = "a" * 1000
key = OpenSSL::Random.random_bytes(16)
- encrypted = crypt_data(data, key, :encrypt) { engine.cipher(algo) }
- decrypted = crypt_data(encrypted, key, :decrypt) { OpenSSL::Cipher.new(algo) }
+
+ cipher = engine.cipher(algo)
+ cipher.encrypt
+ cipher.key = key
+ encrypted = cipher.update(data) + cipher.final
+
+ cipher = OpenSSL::Cipher.new(algo)
+ cipher.decrypt
+ cipher.key = key
+ decrypted = cipher.update(encrypted) + cipher.final
+
assert_equal(data, decrypted)
end;
end
@@ -73,25 +82,10 @@ class OpenSSL::TestEngine < OpenSSL::TestCase
# this is required because OpenSSL::Engine methods change global state
def with_openssl(code, **opts)
- assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;", **opts)
- require #{__FILE__.dump}
- include OpenSSL::TestEngine::Utils
+ assert_separately(["-ropenssl"], <<~"end;", **opts)
#{code}
end;
end
-
- module Utils
- def get_engine
- OpenSSL::Engine.by_id("openssl")
- end
-
- def crypt_data(data, key, mode)
- cipher = yield
- cipher.send mode
- cipher.key = key
- cipher.update(data) + cipher.final
- end
- end
end
end
diff --git a/test/openssl/test_fips.rb b/test/openssl/test_fips.rb
index 8cd474f9..4a3dd43a 100644
--- a/test/openssl/test_fips.rb
+++ b/test/openssl/test_fips.rb
@@ -4,22 +4,46 @@ require_relative 'utils'
if defined?(OpenSSL)
class OpenSSL::TestFIPS < OpenSSL::TestCase
+ def test_fips_mode_get_is_true_on_fips_mode_enabled
+ unless ENV["TEST_RUBY_OPENSSL_FIPS_ENABLED"]
+ omit "Only for FIPS mode environment"
+ end
+
+ assert_separately(["-ropenssl"], <<~"end;")
+ assert OpenSSL.fips_mode == true, ".fips_mode should return true on FIPS mode enabled"
+ end;
+ end
+
+ def test_fips_mode_get_is_false_on_fips_mode_disabled
+ if ENV["TEST_RUBY_OPENSSL_FIPS_ENABLED"]
+ omit "Only for non-FIPS mode environment"
+ end
+
+ assert_separately(["-ropenssl"], <<~"end;")
+ message = ".fips_mode should return false on FIPS mode disabled. " \
+ "If you run the test on FIPS mode, please set " \
+ "TEST_RUBY_OPENSSL_FIPS_ENABLED=true"
+ assert OpenSSL.fips_mode == false, message
+ end;
+ end
+
def test_fips_mode_is_reentrant
- OpenSSL.fips_mode = false
- OpenSSL.fips_mode = false
+ assert_separately(["-ropenssl"], <<~"end;")
+ OpenSSL.fips_mode = false
+ OpenSSL.fips_mode = false
+ end;
end
- def test_fips_mode_get
- return unless OpenSSL::OPENSSL_FIPS
- assert_separately([{ "OSSL_MDEBUG" => nil }, "-ropenssl"], <<~"end;")
- require #{__FILE__.dump}
+ def test_fips_mode_get_with_fips_mode_set
+ omit('OpenSSL is not FIPS-capable') unless OpenSSL::OPENSSL_FIPS
+ assert_separately(["-ropenssl"], <<~"end;")
begin
OpenSSL.fips_mode = true
- assert OpenSSL.fips_mode == true, ".fips_mode returns true when .fips_mode=true"
+ assert OpenSSL.fips_mode == true, ".fips_mode should return true when .fips_mode=true"
OpenSSL.fips_mode = false
- assert OpenSSL.fips_mode == false, ".fips_mode returns false when .fips_mode=false"
+ assert OpenSSL.fips_mode == false, ".fips_mode should return false when .fips_mode=false"
rescue OpenSSL::OpenSSLError
pend "Could not set FIPS mode (OpenSSL::OpenSSLError: \#$!); skipping"
end
diff --git a/test/openssl/test_ns_spki.rb b/test/openssl/test_ns_spki.rb
index 383931b9..d76fc9e5 100644
--- a/test/openssl/test_ns_spki.rb
+++ b/test/openssl/test_ns_spki.rb
@@ -38,13 +38,13 @@ class OpenSSL::TestNSSPI < OpenSSL::TestCase
def test_decode_data
spki = OpenSSL::Netscape::SPKI.new(@b64)
assert_equal(@b64, spki.to_pem)
- assert_equal(@b64.unpack("m").first, spki.to_der)
+ assert_equal(@b64.unpack1("m"), spki.to_der)
assert_equal("MozillaIsMyFriend", spki.challenge)
assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
- spki = OpenSSL::Netscape::SPKI.new(@b64.unpack("m").first)
+ spki = OpenSSL::Netscape::SPKI.new(@b64.unpack1("m"))
assert_equal(@b64, spki.to_pem)
- assert_equal(@b64.unpack("m").first, spki.to_der)
+ assert_equal(@b64.unpack1("m"), spki.to_der)
assert_equal("MozillaIsMyFriend", spki.challenge)
assert_equal(OpenSSL::PKey::RSA, spki.public_key.class)
end
diff --git a/test/openssl/test_ossl.rb b/test/openssl/test_ossl.rb
index e1d86bd4..979669a0 100644
--- a/test/openssl/test_ossl.rb
+++ b/test/openssl/test_ossl.rb
@@ -60,6 +60,19 @@ class OpenSSL::OSSL < OpenSSL::SSLTestCase
assert_operator(a_b_time, :<, a_c_time * 10, "fixed_length_secure_compare timing test failed")
assert_operator(a_c_time, :<, a_b_time * 10, "fixed_length_secure_compare timing test failed")
end
+
+ def test_error_data
+ # X509V3_EXT_nconf_nid() called from OpenSSL::X509::ExtensionFactory#create_ext is a function
+ # that uses ERR_raise_data() to append additional information about the error.
+ #
+ # The generated message should look like:
+ # "subjectAltName = IP:not.a.valid.ip.address: bad ip address (value=not.a.valid.ip.address)"
+ # "subjectAltName = IP:not.a.valid.ip.address: error in extension (name=subjectAltName, value=IP:not.a.valid.ip.address)"
+ ef = OpenSSL::X509::ExtensionFactory.new
+ assert_raise_with_message(OpenSSL::X509::ExtensionError, /value=(IP:)?not.a.valid.ip.address\)/) {
+ ef.create_ext("subjectAltName", "IP:not.a.valid.ip.address")
+ }
+ end
end
end
diff --git a/test/openssl/test_pkcs12.rb b/test/openssl/test_pkcs12.rb
index ec676743..e6b91b52 100644
--- a/test/openssl/test_pkcs12.rb
+++ b/test/openssl/test_pkcs12.rb
@@ -181,7 +181,7 @@ module OpenSSL
def test_new_with_no_keys
# generated with:
# openssl pkcs12 -certpbe PBE-SHA1-3DES -in <@mycert> -nokeys -export
- str = <<~EOF.unpack("m").first
+ str = <<~EOF.unpack1("m")
MIIGJAIBAzCCBeoGCSqGSIb3DQEHAaCCBdsEggXXMIIF0zCCBc8GCSqGSIb3
DQEHBqCCBcAwggW8AgEAMIIFtQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMw
DgQIjv5c3OHvnBgCAggAgIIFiMJa8Z/w7errRvCQPXh9dGQz3eJaFq3S2gXD
@@ -230,7 +230,7 @@ AA==
def test_new_with_no_certs
# generated with:
# openssl pkcs12 -inkey fixtures/openssl/pkey/rsa-1.pem -nocerts -export
- str = <<~EOF.unpack("m").first
+ str = <<~EOF.unpack1("m")
MIIJ7wIBAzCCCbUGCSqGSIb3DQEHAaCCCaYEggmiMIIJnjCCCZoGCSqGSIb3
DQEHAaCCCYsEggmHMIIJgzCCCX8GCyqGSIb3DQEMCgECoIIJbjCCCWowHAYK
KoZIhvcNAQwBAzAOBAjX5nN8jyRKwQICCAAEgglIBIRLHfiY1mNHpl3FdX6+
diff --git a/test/openssl/test_pkey.rb b/test/openssl/test_pkey.rb
index 2b99e8f3..aee0546f 100644
--- a/test/openssl/test_pkey.rb
+++ b/test/openssl/test_pkey.rb
@@ -82,6 +82,9 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
end
def test_ed25519
+ # Ed25519 is not FIPS-approved.
+ omit_on_fips
+
# Test vector from RFC 8032 Section 7.1 TEST 2
priv_pem = <<~EOF
-----BEGIN PRIVATE KEY-----
@@ -96,9 +99,11 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
begin
priv = OpenSSL::PKey.read(priv_pem)
pub = OpenSSL::PKey.read(pub_pem)
- rescue OpenSSL::PKey::PKeyError
+ rescue OpenSSL::PKey::PKeyError => e
# OpenSSL < 1.1.1
- pend "Ed25519 is not implemented"
+ pend "Ed25519 is not implemented" unless openssl?(1, 1, 1)
+
+ raise e
end
assert_instance_of OpenSSL::PKey::PKey, priv
assert_instance_of OpenSSL::PKey::PKey, pub
@@ -106,6 +111,19 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
assert_equal pub_pem, priv.public_to_pem
assert_equal pub_pem, pub.public_to_pem
+ begin
+ assert_equal "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
+ priv.raw_private_key.unpack1("H*")
+ assert_equal OpenSSL::PKey.new_raw_private_key("ED25519", priv.raw_private_key).private_to_pem,
+ priv.private_to_pem
+ assert_equal "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
+ priv.raw_public_key.unpack1("H*")
+ assert_equal OpenSSL::PKey.new_raw_public_key("ED25519", priv.raw_public_key).public_to_pem,
+ pub.public_to_pem
+ rescue NoMethodError
+ pend "running OpenSSL version does not have raw public key support"
+ end
+
sig = [<<~EOF.gsub(/[^0-9a-f]/, "")].pack("H*")
92a009a9f0d4cab8720e820b5f642540
a2b27b5416503f8fb3762223ebdb69da
@@ -126,6 +144,32 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
assert_raise(OpenSSL::PKey::PKeyError) { priv.derive(pub) }
end
+ def test_ed25519_not_approved_on_fips
+ omit_on_non_fips
+ # Ed25519 is technically allowed in the OpenSSL 3.0 code as a kind of bug.
+ # So, we need to omit OpenSSL 3.0.
+ #
+ # See OpenSSL providers/fips/fipsprov.c PROV_NAMES_ED25519 entries with
+ # FIPS_DEFAULT_PROPERTIES on openssl-3.0 branch and
+ # FIPS_UNAPPROVED_PROPERTIES on openssl-3.1 branch.
+ #
+ # See also
+ # https://github.com/openssl/openssl/issues/20758#issuecomment-1639658102
+ # for details.
+ unless openssl?(3, 1, 0, 0)
+ omit 'Ed25519 is allowed in the OpenSSL 3.0 FIPS code as a kind of bug'
+ end
+
+ priv_pem = <<~EOF
+ -----BEGIN PRIVATE KEY-----
+ MC4CAQAwBQYDK2VwBCIEIEzNCJso/5banbbDRuwRTg9bijGfNaumJNqM9u1PuKb7
+ -----END PRIVATE KEY-----
+ EOF
+ assert_raise(OpenSSL::PKey::PKeyError) do
+ OpenSSL::PKey.read(priv_pem)
+ end
+ end
+
def test_x25519
# Test vector from RFC 7748 Section 6.1
alice_pem = <<~EOF
@@ -150,6 +194,32 @@ class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
assert_equal alice_pem, alice.private_to_pem
assert_equal bob_pem, bob.public_to_pem
assert_equal [shared_secret].pack("H*"), alice.derive(bob)
+ begin
+ alice_private = OpenSSL::PKey.new_raw_private_key("X25519", alice.raw_private_key)
+ bob_public = OpenSSL::PKey.new_raw_public_key("X25519", bob.raw_public_key)
+ alice_private_raw = alice.raw_private_key.unpack1("H*")
+ bob_public_raw = bob.raw_public_key.unpack1("H*")
+ rescue NoMethodError
+ # OpenSSL < 1.1.1
+ pend "running OpenSSL version does not have raw public key support"
+ end
+ assert_equal alice_private.private_to_pem,
+ alice.private_to_pem
+ assert_equal bob_public.public_to_pem,
+ bob.public_to_pem
+ assert_equal "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
+ alice_private_raw
+ assert_equal "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
+ bob_public_raw
+ end
+
+ def raw_initialize
+ pend "Ed25519 is not implemented" unless openssl?(1, 1, 1) # >= v1.1.1
+
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("foo123", "xxx") }
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_private_key("ED25519", "xxx") }
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_public_key("foo123", "xxx") }
+ assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.new_raw_public_key("ED25519", "xxx") }
end
def test_compare?
diff --git a/test/openssl/test_pkey_dsa.rb b/test/openssl/test_pkey_dsa.rb
index d1059093..3f64a80e 100644
--- a/test/openssl/test_pkey_dsa.rb
+++ b/test/openssl/test_pkey_dsa.rb
@@ -58,7 +58,7 @@ class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
signature = dsa512.sign("SHA256", data)
assert_equal true, dsa512.verify("SHA256", signature, data)
- signature0 = (<<~'end;').unpack("m")[0]
+ signature0 = (<<~'end;').unpack1("m")
MCwCFH5h40plgU5Fh0Z4wvEEpz0eE9SnAhRPbkRB8ggsN/vsSEYMXvJwjGg/
6g==
end;
diff --git a/test/openssl/test_pkey_ec.rb b/test/openssl/test_pkey_ec.rb
index e5fef940..2cb8e287 100644
--- a/test/openssl/test_pkey_ec.rb
+++ b/test/openssl/test_pkey_ec.rb
@@ -5,20 +5,6 @@ if defined?(OpenSSL)
class OpenSSL::TestEC < OpenSSL::PKeyTestCase
def test_ec_key
- builtin_curves = OpenSSL::PKey::EC.builtin_curves
- assert_not_empty builtin_curves
-
- builtin_curves.each do |curve_name, comment|
- # Oakley curves and X25519 are not suitable for signing and causes
- # FIPS-selftest failure on some environment, so skip for now.
- next if ["Oakley", "X25519"].any? { |n| curve_name.start_with?(n) }
-
- key = OpenSSL::PKey::EC.generate(curve_name)
- assert_predicate key, :private?
- assert_predicate key, :public?
- assert_nothing_raised { key.check_key }
- end
-
key1 = OpenSSL::PKey::EC.generate("prime256v1")
# PKey is immutable in OpenSSL >= 3.0; constructing an empty EC object is
@@ -49,6 +35,17 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
end
end
+ def test_builtin_curves
+ builtin_curves = OpenSSL::PKey::EC.builtin_curves
+ assert_not_empty builtin_curves
+ assert_equal 2, builtin_curves[0].size
+ assert_kind_of String, builtin_curves[0][0]
+ assert_kind_of String, builtin_curves[0][1]
+
+ builtin_curve_names = builtin_curves.map { |name, comment| name }
+ assert_include builtin_curve_names, "prime256v1"
+ end
+
def test_generate
assert_raise(OpenSSL::PKey::ECError) { OpenSSL::PKey::EC.generate("non-existent") }
g = OpenSSL::PKey::EC::Group.new("prime256v1")
@@ -110,7 +107,7 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
signature = p256.sign("SHA256", data)
assert_equal true, p256.verify("SHA256", signature, data)
- signature0 = (<<~'end;').unpack("m")[0]
+ signature0 = (<<~'end;').unpack1("m")
MEQCIEOTY/hD7eI8a0qlzxkIt8LLZ8uwiaSfVbjX2dPAvN11AiAQdCYx56Fq
QdBp1B4sxJoA8jvODMMklMyBKVmudboA6A==
end;
@@ -232,6 +229,8 @@ class OpenSSL::TestEC < OpenSSL::PKeyTestCase
end
def test_ECPrivateKey_encrypted
+ omit_on_fips
+
p256 = Fixtures.pkey("p256")
# key = abcdef
pem = <<~EOF
diff --git a/test/openssl/test_pkey_rsa.rb b/test/openssl/test_pkey_rsa.rb
index b0ae5784..61c55c60 100644
--- a/test/openssl/test_pkey_rsa.rb
+++ b/test/openssl/test_pkey_rsa.rb
@@ -83,7 +83,7 @@ class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
signature = rsa1024.sign("SHA256", data)
assert_equal true, rsa1024.verify("SHA256", signature, data)
- signature0 = (<<~'end;').unpack("m")[0]
+ signature0 = (<<~'end;').unpack1("m")
oLCgbprPvfhM4pjFQiDTFeWI9Sk+Og7Nh9TmIZ/xSxf2CGXQrptlwo7NQ28+
WA6YQo8jPH4hSuyWIM4Gz4qRYiYRkl5TDMUYob94zm8Si1HxEiS9354tzvqS
zS8MLW2BtNPuTubMxTItHGTnOzo9sUg0LAHVFt8kHG2NfKAw/gQ=
diff --git a/test/openssl/test_provider.rb b/test/openssl/test_provider.rb
new file mode 100644
index 00000000..d0e66785
--- /dev/null
+++ b/test/openssl/test_provider.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+require_relative 'utils'
+if defined?(OpenSSL) && defined?(OpenSSL::Provider) && !OpenSSL.fips_mode
+
+class OpenSSL::TestProvider < OpenSSL::TestCase
+ def test_openssl_provider_name_inspect
+ with_openssl <<-'end;'
+ provider = OpenSSL::Provider.load("default")
+ assert_equal("default", provider.name)
+ assert_not_nil(provider.inspect)
+ end;
+ end
+
+ def test_openssl_provider_names
+ with_openssl <<-'end;'
+ legacy_provider = OpenSSL::Provider.load("legacy")
+ assert_equal(2, OpenSSL::Provider.provider_names.size)
+ assert_includes(OpenSSL::Provider.provider_names, "legacy")
+
+ assert_equal(true, legacy_provider.unload)
+ assert_equal(1, OpenSSL::Provider.provider_names.size)
+ assert_not_includes(OpenSSL::Provider.provider_names, "legacy")
+ end;
+ end
+
+ def test_unloaded_openssl_provider
+ with_openssl <<-'end;'
+ default_provider = OpenSSL::Provider.load("default")
+ assert_equal(true, default_provider.unload)
+ assert_raise(OpenSSL::Provider::ProviderError) { default_provider.name }
+ assert_raise(OpenSSL::Provider::ProviderError) { default_provider.unload }
+ end;
+ end
+
+ def test_openssl_legacy_provider
+ with_openssl(<<-'end;')
+ OpenSSL::Provider.load("legacy")
+ algo = "RC4"
+ data = "a" * 1000
+ key = OpenSSL::Random.random_bytes(16)
+
+ # default provider does not support RC4
+ cipher = OpenSSL::Cipher.new(algo)
+ cipher.encrypt
+ cipher.key = key
+ encrypted = cipher.update(data) + cipher.final
+
+ other_cipher = OpenSSL::Cipher.new(algo)
+ other_cipher.decrypt
+ other_cipher.key = key
+ decrypted = other_cipher.update(encrypted) + other_cipher.final
+
+ assert_equal(data, decrypted)
+ end;
+ end
+
+ private
+
+ # this is required because OpenSSL::Provider methods change global state
+ def with_openssl(code, **opts)
+ assert_separately(["-ropenssl"], <<~"end;", **opts)
+ #{code}
+ end;
+ end
+end
+
+end
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 91801557..07dc9a34 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -481,6 +481,40 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
}
end
+ def test_ca_file
+ start_server(ignore_listener_error: true) { |port|
+ # X509_STORE is shared; setting ca_file to SSLContext affects store
+ store = OpenSSL::X509::Store.new
+ assert_equal false, store.verify(@svr_cert)
+
+ ctx = Tempfile.create("ca_cert.pem") { |f|
+ f.puts(@ca_cert.to_pem)
+ f.close
+
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ ctx.cert_store = store
+ ctx.ca_file = f.path
+ ctx.setup
+ ctx
+ }
+ assert_nothing_raised {
+ server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
+ }
+ assert_equal true, store.verify(@svr_cert)
+ }
+ end
+
+ def test_ca_file_not_found
+ path = Tempfile.create("ca_cert.pem") { |f| f.path }
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ca_file = path
+ # OpenSSL >= 1.1.0: /no certificate or crl found/
+ assert_raise(OpenSSL::SSL::SSLError) {
+ ctx.setup
+ }
+ end
+
def test_finished_messages
server_finished = nil
server_peer_finished = nil
@@ -1043,9 +1077,6 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase
end
def test_connect_certificate_verify_failed_exception_message
- # Won't fix on the 3.0 branch
- return if openssl?(3, 1, 0)
-
start_server(ignore_listener_error: true) { |port|
ctx = OpenSSL::SSL::SSLContext.new
ctx.set_params
diff --git a/test/openssl/test_ssl_session.rb b/test/openssl/test_ssl_session.rb
index b243201e..89cf672a 100644
--- a/test/openssl/test_ssl_session.rb
+++ b/test/openssl/test_ssl_session.rb
@@ -22,7 +22,7 @@ class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase
assert_match(/\A-----BEGIN SSL SESSION PARAMETERS-----/, pem)
assert_match(/-----END SSL SESSION PARAMETERS-----\Z/, pem)
pem.gsub!(/-----(BEGIN|END) SSL SESSION PARAMETERS-----/, '').gsub!(/[\r\n]+/m, '')
- assert_equal(session.to_der, pem.unpack('m*')[0])
+ assert_equal(session.to_der, pem.unpack1('m'))
assert_not_nil(session.to_text)
}
end
diff --git a/test/openssl/test_x509ext.rb b/test/openssl/test_x509ext.rb
index 7ad010d1..838780d3 100644
--- a/test/openssl/test_x509ext.rb
+++ b/test/openssl/test_x509ext.rb
@@ -70,6 +70,25 @@ class OpenSSL::TestX509Extension < OpenSSL::TestCase
assert_match(%r{http://cps.example.com}, cp.value)
end
+ def test_factory_create_extension_sn_ln
+ ef = OpenSSL::X509::ExtensionFactory.new
+ bc_sn = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2")
+ bc_ln = ef.create_extension("X509v3 Basic Constraints", "critical, CA:TRUE, pathlen:2")
+ assert_equal(@basic_constraints.to_der, bc_sn.to_der)
+ assert_equal(@basic_constraints.to_der, bc_ln.to_der)
+ end
+
+ def test_factory_create_extension_oid
+ ef = OpenSSL::X509::ExtensionFactory.new
+ ef.config = OpenSSL::Config.parse(<<~_end_of_cnf_)
+ [basic_constraints]
+ cA = BOOLEAN:TRUE
+ pathLenConstraint = INTEGER:2
+ _end_of_cnf_
+ bc_oid = ef.create_extension("2.5.29.19", "ASN1:SEQUENCE:basic_constraints", true)
+ assert_equal(@basic_constraints.to_der, bc_oid.to_der)
+ end
+
def test_dup
ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der)
assert_equal(@basic_constraints.to_der, ext.to_der)
diff --git a/test/openssl/utils.rb b/test/openssl/utils.rb
index 4ebcb983..cd70d488 100644
--- a/test/openssl/utils.rb
+++ b/test/openssl/utils.rb
@@ -1,38 +1,13 @@
# frozen_string_literal: true
begin
require "openssl"
-
- # Disable FIPS mode for tests for installations
- # where FIPS mode would be enabled by default.
- # Has no effect on all other installations.
- OpenSSL.fips_mode=false
rescue LoadError
end
-# Compile OpenSSL with crypto-mdebug and run this test suite with OSSL_MDEBUG=1
-# environment variable to enable memory leak check.
-if ENV["OSSL_MDEBUG"] == "1"
- if OpenSSL.respond_to?(:print_mem_leaks)
- OpenSSL.mem_check_start
-
- END {
- GC.start
- case OpenSSL.print_mem_leaks
- when nil
- warn "mdebug: check what is printed"
- when true
- raise "mdebug: memory leaks detected"
- end
- }
- else
- warn "OSSL_MDEBUG=1 is specified but OpenSSL is not built with crypto-mdebug"
- end
-end
-
require "test/unit"
+require "core_assertions"
require "tempfile"
require "socket"
-require "envutil"
if defined?(OpenSSL)
@@ -131,11 +106,12 @@ module OpenSSL::TestUtils
end
end
- def openssl?(major = nil, minor = nil, fix = nil, patch = 0)
+ def openssl?(major = nil, minor = nil, fix = nil, patch = 0, status = 0)
return false if OpenSSL::OPENSSL_VERSION.include?("LibreSSL")
return true unless major
OpenSSL::OPENSSL_VERSION_NUMBER >=
- major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10
+ major * 0x10000000 + minor * 0x100000 + fix * 0x1000 + patch * 0x10 +
+ status * 0x1
end
def libressl?(major = nil, minor = nil, fix = nil)
@@ -148,6 +124,7 @@ end
class OpenSSL::TestCase < Test::Unit::TestCase
include OpenSSL::TestUtils
extend OpenSSL::TestUtils
+ include Test::Unit::CoreAssertions
def setup
if ENV["OSSL_GC_STRESS"] == "1"
@@ -162,6 +139,26 @@ class OpenSSL::TestCase < Test::Unit::TestCase
# OpenSSL error stack must be empty
assert_equal([], OpenSSL.errors)
end
+
+ # Omit the tests in FIPS.
+ #
+ # For example, the password based encryption used in the PEM format uses MD5
+ # for deriving the encryption key from the password, and MD5 is not
+ # FIPS-approved.
+ #
+ # See https://github.com/openssl/openssl/discussions/21830#discussioncomment-6865636
+ # for details.
+ def omit_on_fips
+ return unless OpenSSL.fips_mode
+
+ omit 'An encryption used in the test is not FIPS-approved'
+ end
+
+ def omit_on_non_fips
+ return if OpenSSL.fips_mode
+
+ omit "Only for OpenSSL FIPS"
+ end
end
class OpenSSL::SSLTestCase < OpenSSL::TestCase