From 5a4faaaeb198b8cede59f323c7b6ed47e029c6db Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 14 Dec 2025 22:40:45 +0900 Subject: [PATCH 1/7] Merge `root_box_data` into `root_box` * Make invariant `root_box` an array consist of only `root_box_data`. * Remove the unnecessary initializer list that is just overwritten in `initialize_root_box()` and missing `classext_cow_classes`. * Shrink the scope using another local `root_box`. * Make the data type constants static. --- box.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/box.c b/box.c index 7907e0ff632a1e..150a75700c9313 100644 --- a/box.c +++ b/box.c @@ -31,16 +31,8 @@ VALUE rb_cBox = 0; VALUE rb_cBoxEntry = 0; VALUE rb_mBoxLoader = 0; -static rb_box_t root_box_data = { - /* Initialize values lazily in Init_Box() */ - (VALUE)NULL, 0, - (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, (VALUE)NULL, - (struct st_table *)NULL, (struct st_table *)NULL, (VALUE)NULL, (VALUE)NULL, - false, false -}; - -static rb_box_t * root_box = &root_box_data; -static rb_box_t * main_box = 0; +static rb_box_t root_box[1]; /* Initialize in initialize_root_box() */ +static rb_box_t *main_box; static char *tmp_dir; static bool tmp_dir_has_dirsep; @@ -290,7 +282,7 @@ box_entry_memsize(const void *ptr) rb_st_memsize(box->loading_table); } -const rb_data_type_t rb_box_data_type = { +static const rb_data_type_t rb_box_data_type = { "Ruby::Box::Entry", { rb_box_entry_mark, @@ -301,7 +293,7 @@ const rb_data_type_t rb_box_data_type = { 0, 0, RUBY_TYPED_FREE_IMMEDIATELY // TODO: enable RUBY_TYPED_WB_PROTECTED when inserting write barriers }; -const rb_data_type_t rb_root_box_data_type = { +static const rb_data_type_t rb_root_box_data_type = { "Ruby::Box::Root", { rb_box_entry_mark, @@ -838,8 +830,6 @@ rb_box_require_relative(VALUE box, VALUE fname) static void initialize_root_box(void) { - VALUE root_box, entry; - ID id_box_entry; rb_vm_t *vm = GET_VM(); rb_box_t *root = (rb_box_t *)rb_root_box(); @@ -864,6 +854,8 @@ initialize_root_box(void) vm->root_box = root; if (rb_box_available()) { + VALUE root_box, entry; + ID id_box_entry; CONST_ID(id_box_entry, "__box_entry__"); root_box = rb_obj_alloc(rb_cBox); From ee6ba41bfdc82110468598467cd10ce850b44f0c Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 14 Dec 2025 19:55:28 +0900 Subject: [PATCH 2/7] [ruby/openssl] Freeze more constants for Ractor compatibility https://github.com/ruby/openssl/commit/695126f582 --- ext/openssl/ossl.c | 9 +++++++-- ext/openssl/ossl_asn1.c | 18 ++++++++++-------- ext/openssl/ossl_x509.c | 3 ++- ext/openssl/ossl_x509name.c | 1 + 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 9c63d9451aa59d..b2edafd876812c 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -1010,12 +1010,17 @@ Init_openssl(void) /* * Version of OpenSSL the ruby OpenSSL extension was built with */ - rb_define_const(mOSSL, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT)); + rb_define_const(mOSSL, "OPENSSL_VERSION", + rb_obj_freeze(rb_str_new_cstr(OPENSSL_VERSION_TEXT))); /* * Version of OpenSSL the ruby OpenSSL extension is running with */ - rb_define_const(mOSSL, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION))); + rb_define_const( + mOSSL, + "OPENSSL_LIBRARY_VERSION", + rb_obj_freeze(rb_str_new_cstr(OpenSSL_version(OPENSSL_VERSION))) + ); /* * Version number of OpenSSL the ruby OpenSSL extension was built with diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 628140a75e3ea4..71a87f04636c98 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -1397,8 +1397,6 @@ void Init_ossl_asn1(void) { #undef rb_intern - VALUE ary; - int i; sym_UNIVERSAL = ID2SYM(rb_intern_const("UNIVERSAL")); sym_CONTEXT_SPECIFIC = ID2SYM(rb_intern_const("CONTEXT_SPECIFIC")); @@ -1548,17 +1546,20 @@ Init_ossl_asn1(void) rb_define_module_function(mASN1, "traverse", ossl_asn1_traverse, 1); rb_define_module_function(mASN1, "decode", ossl_asn1_decode, 1); rb_define_module_function(mASN1, "decode_all", ossl_asn1_decode_all, 1); - ary = rb_ary_new(); + VALUE ary = rb_ary_new_capa(ossl_asn1_info_size); + for (int i = 0; i < ossl_asn1_info_size; i++) { + const char *name = ossl_asn1_info[i].name; + if (name[0] == '[') + continue; + rb_define_const(mASN1, name, INT2NUM(i)); + rb_ary_store(ary, i, rb_obj_freeze(rb_str_new_cstr(name))); + } + rb_obj_freeze(ary); /* * Array storing tag names at the tag's index. */ rb_define_const(mASN1, "UNIVERSAL_TAG_NAME", ary); - for(i = 0; i < ossl_asn1_info_size; i++){ - if(ossl_asn1_info[i].name[0] == '[') continue; - rb_define_const(mASN1, ossl_asn1_info[i].name, INT2NUM(i)); - rb_ary_store(ary, i, rb_str_new2(ossl_asn1_info[i].name)); - } /* Document-class: OpenSSL::ASN1::ASN1Data * @@ -1880,6 +1881,7 @@ do{\ rb_hash_aset(class_tag_map, cASN1GeneralString, INT2NUM(V_ASN1_GENERALSTRING)); rb_hash_aset(class_tag_map, cASN1UniversalString, INT2NUM(V_ASN1_UNIVERSALSTRING)); rb_hash_aset(class_tag_map, cASN1BMPString, INT2NUM(V_ASN1_BMPSTRING)); + rb_obj_freeze(class_tag_map); id_each = rb_intern_const("each"); } diff --git a/ext/openssl/ossl_x509.c b/ext/openssl/ossl_x509.c index e341ca1fbb4c1c..bc3914fda20d39 100644 --- a/ext/openssl/ossl_x509.c +++ b/ext/openssl/ossl_x509.c @@ -13,7 +13,8 @@ VALUE mX509; #define DefX509Const(x) rb_define_const(mX509, #x, INT2NUM(X509_##x)) #define DefX509Default(x,i) \ - rb_define_const(mX509, "DEFAULT_" #x, rb_str_new2(X509_get_default_##i())) + rb_define_const(mX509, "DEFAULT_" #x, \ + rb_obj_freeze(rb_str_new_cstr(X509_get_default_##i()))) ASN1_TIME * ossl_x509_time_adjust(ASN1_TIME *s, VALUE time) diff --git a/ext/openssl/ossl_x509name.c b/ext/openssl/ossl_x509name.c index b91c92c1ff9670..5b3c3f7261dc26 100644 --- a/ext/openssl/ossl_x509name.c +++ b/ext/openssl/ossl_x509name.c @@ -534,6 +534,7 @@ Init_ossl_x509name(void) rb_hash_aset(hash, rb_str_new2("DC"), ia5str); rb_hash_aset(hash, rb_str_new2("domainComponent"), ia5str); rb_hash_aset(hash, rb_str_new2("emailAddress"), ia5str); + rb_obj_freeze(hash); /* * The default object type template for name entries. From f06eb75646e7a8d17d9c41988207a2a29a3b006c Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Wed, 9 Apr 2025 04:01:58 +0900 Subject: [PATCH 3/7] [ruby/openssl] ossl.c: improve docs for constants and methods under ::OpenSSL https://github.com/ruby/openssl/commit/b0de8ba9bd --- ext/openssl/lib/openssl.rb | 8 ++- ext/openssl/lib/openssl/digest.rb | 2 +- ext/openssl/lib/openssl/version.rb | 1 + ext/openssl/ossl.c | 103 ++++++++++++++++++----------- 4 files changed, 71 insertions(+), 43 deletions(-) diff --git a/ext/openssl/lib/openssl.rb b/ext/openssl/lib/openssl.rb index 41927f32ae8238..98fa8d39f2aad6 100644 --- a/ext/openssl/lib/openssl.rb +++ b/ext/openssl/lib/openssl.rb @@ -23,12 +23,16 @@ require_relative 'openssl/x509' module OpenSSL - # call-seq: - # OpenSSL.secure_compare(string, string) -> boolean + # :call-seq: + # OpenSSL.secure_compare(string, string) -> true or false # # Constant time memory comparison. Inputs are hashed using SHA-256 to mask # the length of the secret. Returns +true+ if the strings are identical, # +false+ otherwise. + # + # This method is expensive due to the SHA-256 hashing. In most cases, where + # the input lengths are known to be equal or are not sensitive, + # OpenSSL.fixed_length_secure_compare should be used instead. def self.secure_compare(a, b) hashed_a = OpenSSL::Digest.digest('SHA256', a) hashed_b = OpenSSL::Digest.digest('SHA256', b) diff --git a/ext/openssl/lib/openssl/digest.rb b/ext/openssl/lib/openssl/digest.rb index 5cda1e931c8b44..46ddfd6021b7ea 100644 --- a/ext/openssl/lib/openssl/digest.rb +++ b/ext/openssl/lib/openssl/digest.rb @@ -57,7 +57,7 @@ class Digest < Digest; end # :nodoc: # OpenSSL::Digest("MD5") # # => OpenSSL::Digest::MD5 # - # Digest("Foo") + # OpenSSL::Digest("Foo") # # => NameError: wrong constant name Foo def Digest(name) diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb index 784f8885066091..6ca62f428352a5 100644 --- a/ext/openssl/lib/openssl/version.rb +++ b/ext/openssl/lib/openssl/version.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true module OpenSSL + # The version string of Ruby/OpenSSL. VERSION = "4.0.0.pre" end diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index b2edafd876812c..98127fcba02314 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -388,10 +388,14 @@ osslerror_detailed_message(int argc, VALUE *argv, VALUE self) * call-seq: * OpenSSL.errors -> [String...] * - * See any remaining errors held in queue. + * Returns any remaining errors held in the \OpenSSL thread-local error queue + * and clears the queue. This should normally return an empty array. * - * Any errors you see here are probably due to a bug in Ruby's OpenSSL - * implementation. + * This is intended for debugging Ruby/OpenSSL. If you see any errors here, + * it likely indicates a bug in the extension. Please file an issue at + * https://github.com/ruby/openssl. + * + * For debugging your program, OpenSSL.debug= may be useful. */ static VALUE ossl_get_errors(VALUE _) @@ -415,6 +419,8 @@ VALUE dOSSL; /* * call-seq: * OpenSSL.debug -> true | false + * + * Returns whether Ruby/OpenSSL's debug mode is currently enabled. */ static VALUE ossl_debug_get(VALUE self) @@ -424,9 +430,9 @@ ossl_debug_get(VALUE self) /* * call-seq: - * OpenSSL.debug = boolean -> boolean + * OpenSSL.debug = boolean * - * Turns on or off debug mode. With debug mode, all errors added to the OpenSSL + * Turns on or off debug mode. With debug mode, all errors added to the \OpenSSL * error queue will be printed to stderr. */ static VALUE @@ -440,6 +446,8 @@ ossl_debug_set(VALUE self, VALUE val) /* * call-seq: * OpenSSL.fips_mode -> true | false + * + * Returns whether the FIPS mode is currently enabled. */ static VALUE ossl_fips_mode_get(VALUE self) @@ -460,10 +468,10 @@ ossl_fips_mode_get(VALUE self) /* * call-seq: - * OpenSSL.fips_mode = boolean -> boolean + * OpenSSL.fips_mode = boolean * * Turns FIPS mode on or off. Turning on FIPS mode will obviously only have an - * effect for FIPS-capable installations of the OpenSSL library. Trying to do + * effect for FIPS-capable installations of the \OpenSSL library. Trying to do * so otherwise will result in an error. * * === Examples @@ -503,13 +511,13 @@ ossl_fips_mode_set(VALUE self, VALUE enabled) /* * call-seq: - * OpenSSL.fixed_length_secure_compare(string, string) -> boolean + * OpenSSL.fixed_length_secure_compare(string, string) -> true or false * * Constant time memory comparison for fixed length strings, such as results - * of HMAC calculations. + * of \HMAC calculations. * * Returns +true+ if the strings are identical, +false+ if they are of the same - * length but not identical. If the length is different, +ArgumentError+ is + * length but not identical. If the length is different, ArgumentError is * raised. */ static VALUE @@ -531,7 +539,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) } /* - * OpenSSL provides SSL, TLS and general purpose cryptography. It wraps the + * OpenSSL provides \SSL, TLS and general purpose cryptography. It wraps the * OpenSSL[https://www.openssl.org/] library. * * = Examples @@ -586,7 +594,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * === Loading an Encrypted Key * - * OpenSSL will prompt you for your password when loading an encrypted key. + * \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: * @@ -649,7 +657,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * == PBKDF2 Password-based Encryption * - * If supported by the underlying OpenSSL version used, Password-based + * If supported by the underlying \OpenSSL version used, Password-based * Encryption should use the features of PKCS5. If not supported or if * required by legacy applications, the older, less secure methods specified * in RFC 2898 are also supported (see below). @@ -708,7 +716,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * decrypted = cipher.update encrypted * decrypted << cipher.final * - * == X509 Certificates + * == \X509 Certificates * * === Creating a Certificate * @@ -745,7 +753,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * extension_factory.create_extension('subjectKeyIdentifier', 'hash') * * The list of supported extensions (and in some cases their possible values) - * can be derived from the "objects.h" file in the OpenSSL source code. + * can be derived from the "objects.h" file in the \OpenSSL source code. * * === Signing a Certificate * @@ -899,23 +907,23 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * io.write csr_cert.to_pem * end * - * == SSL and TLS Connections + * == \SSL and TLS Connections * - * Using our created key and certificate we can create an SSL or TLS connection. - * An SSLContext is used to set up an SSL session. + * Using our created key and certificate we can create an \SSL or TLS + * connection. An OpenSSL::SSL::SSLContext is used to set up an \SSL session. * * context = OpenSSL::SSL::SSLContext.new * - * === SSL Server + * === \SSL Server * - * An SSL server requires the certificate and private key to communicate + * An \SSL server requires the certificate and private key to communicate * securely with its clients: * * context.cert = cert * context.key = key * - * Then create an SSLServer with a TCP server socket and the context. Use the - * SSLServer like an ordinary TCP server. + * Then create an OpenSSL::SSL::SSLServer with a TCP server socket and the + * context. Use the SSLServer like an ordinary TCP server. * * require 'socket' * @@ -934,14 +942,15 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * ssl_connection.close * end * - * === SSL client + * === \SSL client * - * An SSL client is created with a TCP socket and the context. - * SSLSocket#connect must be called to initiate the SSL handshake and start - * encryption. A key and certificate are not required for the client socket. + * An \SSL client is created with a TCP socket and the context. + * OpenSSL::SSL::SSLSocket#connect must be called to initiate the \SSL handshake + * and start encryption. A key and certificate are not required for the client + * socket. * - * Note that SSLSocket#close doesn't close the underlying socket by default. Set - * SSLSocket#sync_close to true if you want. + * Note that OpenSSL::SSL::SSLSocket#close doesn't close the underlying socket + * by default. Set OpenSSL::SSL::SSLSocket#sync_close to true if you want. * * require 'socket' * @@ -957,7 +966,7 @@ ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2) * * === Peer Verification * - * An unverified SSL connection does not provide much security. For enhanced + * An unverified \SSL connection does not provide much security. For enhanced * security the client or server can verify the certificate of its peer. * * The client can be modified to verify the server's certificate against the @@ -1008,13 +1017,14 @@ Init_openssl(void) rb_define_singleton_method(mOSSL, "fixed_length_secure_compare", ossl_crypto_fixed_length_secure_compare, 2); /* - * Version of OpenSSL the ruby OpenSSL extension was built with + * \OpenSSL library version string used to compile the Ruby/OpenSSL + * extension. This may differ from the version used at runtime. */ rb_define_const(mOSSL, "OPENSSL_VERSION", rb_obj_freeze(rb_str_new_cstr(OPENSSL_VERSION_TEXT))); /* - * Version of OpenSSL the ruby OpenSSL extension is running with + * \OpenSSL library version string currently used at runtime. */ rb_define_const( mOSSL, @@ -1023,12 +1033,18 @@ Init_openssl(void) ); /* - * Version number of OpenSSL the ruby OpenSSL extension was built with - * (base 16). The formats are below. + * \OpenSSL library version number used to compile the Ruby/OpenSSL + * extension. This may differ from the version used at runtime. * - * [OpenSSL 3] 0xMNN00PP0 (major minor 00 patch 0) - * [OpenSSL before 3] 0xMNNFFPPS (major minor fix patch status) - * [LibreSSL] 0x20000000 (fixed value) + * The version number is encoded into a single integer value. The number + * follows the format: + * + * [\OpenSSL 3.0.0 or later] + * 0xMNN00PP0 (major minor 00 patch 0) + * [\OpenSSL 1.1.1 or earlier] + * 0xMNNFFPPS (major minor fix patch status) + * [LibreSSL] + * 0x20000000 (a fixed value) * * See also the man page OPENSSL_VERSION_NUMBER(3). */ @@ -1036,9 +1052,12 @@ Init_openssl(void) #if defined(LIBRESSL_VERSION_NUMBER) /* - * Version number of LibreSSL the ruby OpenSSL extension was built with - * (base 16). The format is 0xMNNFF00f (major minor fix 00 - * status). This constant is only defined in LibreSSL cases. + * LibreSSL library version number used to compile the Ruby/OpenSSL + * extension. This may differ from the version used at runtime. + * + * This constant is only defined if the extension was compiled against + * LibreSSL. The number follows the format: + * 0xMNNFF00f (major minor fix 00 status). * * See also the man page LIBRESSL_VERSION_NUMBER(3). */ @@ -1046,7 +1065,11 @@ Init_openssl(void) #endif /* - * Boolean indicating whether OpenSSL is FIPS-capable or not + * Boolean indicating whether the \OpenSSL library is FIPS-capable or not. + * Always true for \OpenSSL 3.0 and later. + * + * This is obsolete and will be removed in the future. + * See also OpenSSL.fips_mode. */ rb_define_const(mOSSL, "OPENSSL_FIPS", /* OpenSSL 3 is FIPS-capable even when it is installed without fips option */ From f0793731853c0e130f798e9dc5c736b2fa1b72b7 Mon Sep 17 00:00:00 2001 From: Kazuki Yamaguchi Date: Sun, 14 Dec 2025 19:10:04 +0900 Subject: [PATCH 4/7] [ruby/openssl] Ruby/OpenSSL 4.0.0 https://github.com/ruby/openssl/commit/5af1edab18 --- ext/openssl/History.md | 85 ++++++++++++++++++++++++++++++ ext/openssl/lib/openssl/version.rb | 2 +- ext/openssl/openssl.gemspec | 2 +- 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/ext/openssl/History.md b/ext/openssl/History.md index 32a2c0b2fb6ea7..419237ff167f9a 100644 --- a/ext/openssl/History.md +++ b/ext/openssl/History.md @@ -1,3 +1,88 @@ +Version 4.0.0 +============= + +Compatibility +------------- + +* Ruby >= 2.7 +* OpenSSL >= 1.1.1, LibreSSL >= 3.9, and AWS-LC 1.66.0 + - Removed support for OpenSSL 1.0.2-1.1.0 and LibreSSL 3.1-3.8. + [[GitHub #835]](https://github.com/ruby/openssl/issues/835) + - Added support for AWS-LC. + [[GitHub #833]](https://github.com/ruby/openssl/issues/833) + + +Notable changes +--------------- + +* `OpenSSL::SSL` + - Reduce overhead when writing to `OpenSSL::SSL::SSLSocket`. `#syswrite` no + longer creates a temporary String object. + [[GitHub #831]](https://github.com/ruby/openssl/pull/831) + - Make `OpenSSL::SSL::SSLContext#min_version=` and `#max_version=` wrap the + corresponding OpenSSL APIs directly, and remove the fallback to SSL options. + [[GitHub #849]](https://github.com/ruby/openssl/pull/849) + - Add `OpenSSL::SSL::SSLContext#sigalgs=` and `#client_sigalgs=` for + specifying signature algorithms to use for connections. + [[GitHub #895]](https://github.com/ruby/openssl/pull/895) + - Rename `OpenSSL::SSL::SSLContext#ecdh_curves=` to `#groups=` following + the underlying OpenSSL API rename. This method is no longer specific to + ECDHE. The old method remains as an alias. + [[GitHub #900]](https://github.com/ruby/openssl/pull/900) + - Add `OpenSSL::SSL::SSLSocket#sigalg`, `#peer_sigalg`, and `#group` for + getting the signature algorithm and the key agreement group used in the + current connection. + [[GitHub #908]](https://github.com/ruby/openssl/pull/908) + - Enable `SSL_CTX_set_dh_auto()` for servers by default. + [[GitHub #924]](https://github.com/ruby/openssl/pull/924) + - Improve Ractor compatibility. Note that the internal-use constant + `OpenSSL::SSL::SSLContext::DEFAULT_PARAMS` is now frozen. + [[GitHub #925]](https://github.com/ruby/openssl/pull/925) +* `OpenSSL::PKey` + - Remove `OpenSSL::PKey::EC::Point#mul` support with array arguments. The + underlying OpenSSL API has been removed, and the method has been deprecated + since ruby/openssl v3.0.0. + [[GitHub #843]](https://github.com/ruby/openssl/pull/843) + - `OpenSSL::PKey::{RSA,DSA,DH}#params` uses `nil` to indicate missing fields + instead of the number `0`. + [[GitHub #774]](https://github.com/ruby/openssl/pull/774) + - Unify `OpenSSL::PKey::PKeyError` classes. The former subclasses + `OpenSSL::PKey::DHError`, `OpenSSL::PKey::DSAError`, + `OpenSSL::PKey::ECError`, and `OpenSSL::PKey::RSAError` have been merged + into a single class. + [[GitHub #929]](https://github.com/ruby/openssl/pull/929) +* `OpenSSL::Cipher` + - `OpenSSL::Cipher#encrypt` and `#decrypt` no longer accept arguments. + Passing passwords has been deprecated since Ruby 1.8.2 (released in 2004). + [[GitHub #887]](https://github.com/ruby/openssl/pull/887) + - `OpenSSL::Cipher#final` raises `OpenSSL::Cipher::AuthTagError` when the + integrity check fails for AEAD ciphers. `OpenSSL::Cipher::AuthTagError` is a + new subclass of `OpenSSL::Cipher::CipherError`, which was previously raised. + [[GitHub #939]](https://github.com/ruby/openssl/pull/939) + - `OpenSSL::Cipher.new` now raises `OpenSSL::Cipher::CipherError` instead of + `RuntimeError` when OpenSSL does not recognize the algorithm. + [[GitHub #958]](https://github.com/ruby/openssl/pull/958) + - Add support for "fetched" cipher algorithms with OpenSSL 3.0 or later. + [[GitHub #958]](https://github.com/ruby/openssl/pull/958) +* `OpenSSL::Digest` + - `OpenSSL::Digest.new` now raises `OpenSSL::Digest::DigestError` instead of + `RuntimeError` when OpenSSL does not recognize the algorithm. + [[GitHub #958]](https://github.com/ruby/openssl/pull/958) + - Add support for "fetched" digest algorithms with OpenSSL 3.0 or later. + [[GitHub #958]](https://github.com/ruby/openssl/pull/958) +* `OpenSSL::ASN1.decode` now assumes a 1950-2049 year range for `UTCTime` + according to RFC 5280. It previously used a 1969-2068 range. The encoder + has always used the 1950-2049 range. + [[GitHub #909]](https://github.com/ruby/openssl/pull/909) +* `OpenSSL::OpenSSLError`, the base class for all ruby/openssl errors, carry + an additional attribute `#errors` to keep the content of OpenSSL's error + queue. Also, add `#detailed_message` for Ruby 3.2 or later. + [[GitHub #976]](https://github.com/ruby/openssl/pull/976) +* `OpenSSL::PKCS7.new` raises `OpenSSL::PKCS7::PKCS7Error` instead of + `ArgumentError` on error to be consistent with other constructors. + [[GitHub #983]](https://github.com/ruby/openssl/pull/983) + + Version 3.3.2 ============= diff --git a/ext/openssl/lib/openssl/version.rb b/ext/openssl/lib/openssl/version.rb index 6ca62f428352a5..88570562e255c3 100644 --- a/ext/openssl/lib/openssl/version.rb +++ b/ext/openssl/lib/openssl/version.rb @@ -2,5 +2,5 @@ module OpenSSL # The version string of Ruby/OpenSSL. - VERSION = "4.0.0.pre" + VERSION = "4.0.0" end diff --git a/ext/openssl/openssl.gemspec b/ext/openssl/openssl.gemspec index 061a9c5a6ab626..7072d599d86d69 100644 --- a/ext/openssl/openssl.gemspec +++ b/ext/openssl/openssl.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "openssl" - spec.version = "4.0.0.pre" + spec.version = "4.0.0" spec.authors = ["Martin Bosslet", "SHIBATA Hiroshi", "Zachary Scott", "Kazuki Yamaguchi"] spec.email = ["ruby-core@ruby-lang.org"] spec.summary = %q{SSL/TLS and general-purpose cryptography for Ruby} From 35209caef2b46de3a0625c2c40330ab3c6ebb5a3 Mon Sep 17 00:00:00 2001 From: git Date: Mon, 15 Dec 2025 09:51:22 +0000 Subject: [PATCH 5/7] Update default gems list at f0793731853c0e130f798e9dc5c736 [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 947d3d5eeee93f..1389c892f435d0 100644 --- a/NEWS.md +++ b/NEWS.md @@ -286,7 +286,7 @@ The following default gems are updated. * ipaddr 1.2.8 * json 2.18.0 * net-http 0.8.0 -* openssl 4.0.0.pre +* openssl 4.0.0 * optparse 0.8.1 * pp 0.6.3 * prism 1.6.0 From bbc10ed0cdcd2fe9d7d09a9dcc5f036bc4425aef Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 15 Dec 2025 12:10:49 +0100 Subject: [PATCH 6/7] Add NEWS entry for Array#rfind and Array#find --- NEWS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS.md b/NEWS.md index 1389c892f435d0..cd038f08d0d563 100644 --- a/NEWS.md +++ b/NEWS.md @@ -82,6 +82,11 @@ Note: We're only listing outstanding class updates. * A deprecated behavior, process creation by `Kernel#open` with a leading `|`, was removed. [[Feature #19630]] +* Array + + * `Array#rfind` has been added as a more efficient alternative to `array.reverse_each.find` [[Feature #21678]] + * `Array#find` has been added as a more efficient override of `Enumerable#find` [[Feature #21678]] + * Binding * `Binding#local_variables` does no longer include numbered parameters. @@ -478,4 +483,5 @@ A lot of work has gone into making Ractors more stable, performant, and usable. [Feature #21550]: https://bugs.ruby-lang.org/issues/21550 [Feature #21557]: https://bugs.ruby-lang.org/issues/21557 [Bug #21654]: https://bugs.ruby-lang.org/issues/21654 +[Feature #21678]: https://bugs.ruby-lang.org/issues/21678 [Feature #21701]: https://bugs.ruby-lang.org/issues/21701 From eceab2f44c631ceda13656728ae23b0750941001 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Sun, 14 Dec 2025 20:39:10 -0500 Subject: [PATCH 7/7] [ruby/prism] Escape error location is incorrect for some regex When you have a regular expression that has a named capture that has an escape sequence in the named capture, and that escape sequence is a unicode escape sequence with an invalid surrogate pair, the error was attached to the owned string as opposed to a location on the shared source. https://github.com/ruby/prism/commit/793a7a6a0a --- prism/prism.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/prism/prism.c b/prism/prism.c index 677e65056f8f74..06e692c95ea696 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -8613,7 +8613,7 @@ escape_hexadecimal_digit(const uint8_t value) { * validated. */ static inline uint32_t -escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length) { +escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length, const pm_location_t *error_location) { uint32_t value = 0; for (size_t index = 0; index < length; index++) { if (index != 0) value <<= 4; @@ -8623,7 +8623,11 @@ escape_unicode(pm_parser_t *parser, const uint8_t *string, size_t length) { // Here we're going to verify that the value is actually a valid Unicode // codepoint and not a surrogate pair. if (value >= 0xD800 && value <= 0xDFFF) { - pm_parser_err(parser, string, string + length, PM_ERR_ESCAPE_INVALID_UNICODE); + if (error_location != NULL) { + pm_parser_err(parser, error_location->start, error_location->end, PM_ERR_ESCAPE_INVALID_UNICODE); + } else { + pm_parser_err(parser, string, string + length, PM_ERR_ESCAPE_INVALID_UNICODE); + } return 0xFFFD; } @@ -8923,7 +8927,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre extra_codepoints_start = unicode_start; } - uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length); + uint32_t value = escape_unicode(parser, unicode_start, hexadecimal_length, NULL); escape_write_unicode(parser, buffer, flags, unicode_start, parser->current.end, value); parser->current.end += pm_strspn_inline_whitespace(parser->current.end, parser->end - parser->current.end); @@ -8964,7 +8968,7 @@ escape_read(pm_parser_t *parser, pm_buffer_t *buffer, pm_buffer_t *regular_expre PM_PARSER_ERR_FORMAT(parser, start, parser->current.end, PM_ERR_ESCAPE_INVALID_UNICODE_SHORT, 2, start); } } else if (length == 4) { - uint32_t value = escape_unicode(parser, parser->current.end, 4); + uint32_t value = escape_unicode(parser, parser->current.end, 4, NULL); if (flags & PM_ESCAPE_FLAG_REGEXP) { pm_buffer_append_bytes(regular_expression_buffer, start, (size_t) (parser->current.end + 4 - start)); @@ -20368,7 +20372,7 @@ pm_named_capture_escape_octal(pm_buffer_t *unescaped, const uint8_t *cursor, con } static inline const uint8_t * -pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end) { +pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *cursor, const uint8_t *end, const pm_location_t *error_location) { const uint8_t *start = cursor - 1; cursor++; @@ -20379,7 +20383,7 @@ pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, con if (*cursor != '{') { size_t length = pm_strspn_hexadecimal_digit(cursor, MIN(end - cursor, 4)); - uint32_t value = escape_unicode(parser, cursor, length); + uint32_t value = escape_unicode(parser, cursor, length, error_location); if (!pm_buffer_append_unicode_codepoint(unescaped, value)) { pm_buffer_append_string(unescaped, (const char *) start, (size_t) ((cursor + length) - start)); @@ -20402,7 +20406,7 @@ pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, con if (length == 0) { break; } - uint32_t value = escape_unicode(parser, cursor, length); + uint32_t value = escape_unicode(parser, cursor, length, error_location); (void) pm_buffer_append_unicode_codepoint(unescaped, value); cursor += length; @@ -20412,7 +20416,7 @@ pm_named_capture_escape_unicode(pm_parser_t *parser, pm_buffer_t *unescaped, con } static void -pm_named_capture_escape(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *source, const size_t length, const uint8_t *cursor) { +pm_named_capture_escape(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8_t *source, const size_t length, const uint8_t *cursor, const pm_location_t *error_location) { const uint8_t *end = source + length; pm_buffer_append_string(unescaped, (const char *) source, (size_t) (cursor - source)); @@ -20430,7 +20434,7 @@ pm_named_capture_escape(pm_parser_t *parser, pm_buffer_t *unescaped, const uint8 cursor = pm_named_capture_escape_octal(unescaped, cursor, end); break; case 'u': - cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end); + cursor = pm_named_capture_escape_unicode(parser, unescaped, cursor, end, error_location); break; default: pm_buffer_append_byte(unescaped, '\\'); @@ -20473,7 +20477,7 @@ parse_regular_expression_named_capture(const pm_string_t *capture, void *data) { // unescaped, which is what we need. const uint8_t *cursor = pm_memchr(source, '\\', length, parser->encoding_changed, parser->encoding); if (PRISM_UNLIKELY(cursor != NULL)) { - pm_named_capture_escape(parser, &unescaped, source, length, cursor); + pm_named_capture_escape(parser, &unescaped, source, length, cursor, callback_data->shared ? NULL : &call->receiver->location); source = (const uint8_t *) pm_buffer_value(&unescaped); length = pm_buffer_length(&unescaped); }