File openssl-update.patch of Package ruby2.5

diff -ruN ruby-2.5.9.orig/ext/openssl/depend ruby-2.5.9/ext/openssl/depend
--- ruby-2.5.9.orig/ext/openssl/depend	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/depend	1970-01-01 01:00:00.000000000 +0100
@@ -1,1154 +0,0 @@
-# AUTOGENERATED DEPENDENCIES START
-openssl_missing.o: $(RUBY_EXTCONF_H)
-openssl_missing.o: $(arch_hdrdir)/ruby/config.h
-openssl_missing.o: openssl_missing.c
-openssl_missing.o: openssl_missing.h
-ossl.o: $(RUBY_EXTCONF_H)
-ossl.o: $(arch_hdrdir)/ruby/config.h
-ossl.o: $(hdrdir)/ruby/backward.h
-ossl.o: $(hdrdir)/ruby/defines.h
-ossl.o: $(hdrdir)/ruby/encoding.h
-ossl.o: $(hdrdir)/ruby/intern.h
-ossl.o: $(hdrdir)/ruby/io.h
-ossl.o: $(hdrdir)/ruby/missing.h
-ossl.o: $(hdrdir)/ruby/onigmo.h
-ossl.o: $(hdrdir)/ruby/oniguruma.h
-ossl.o: $(hdrdir)/ruby/ruby.h
-ossl.o: $(hdrdir)/ruby/st.h
-ossl.o: $(hdrdir)/ruby/subst.h
-ossl.o: $(hdrdir)/ruby/thread.h
-ossl.o: $(hdrdir)/ruby/thread_native.h
-ossl.o: $(top_srcdir)/include/ruby.h
-ossl.o: openssl_missing.h
-ossl.o: ossl.c
-ossl.o: ossl.h
-ossl.o: ossl_asn1.h
-ossl.o: ossl_bio.h
-ossl.o: ossl_bn.h
-ossl.o: ossl_cipher.h
-ossl.o: ossl_config.h
-ossl.o: ossl_digest.h
-ossl.o: ossl_engine.h
-ossl.o: ossl_hmac.h
-ossl.o: ossl_kdf.h
-ossl.o: ossl_ns_spki.h
-ossl.o: ossl_ocsp.h
-ossl.o: ossl_pkcs12.h
-ossl.o: ossl_pkcs7.h
-ossl.o: ossl_pkey.h
-ossl.o: ossl_rand.h
-ossl.o: ossl_ssl.h
-ossl.o: ossl_version.h
-ossl.o: ossl_x509.h
-ossl.o: ruby_missing.h
-ossl_asn1.o: $(RUBY_EXTCONF_H)
-ossl_asn1.o: $(arch_hdrdir)/ruby/config.h
-ossl_asn1.o: $(hdrdir)/ruby/backward.h
-ossl_asn1.o: $(hdrdir)/ruby/defines.h
-ossl_asn1.o: $(hdrdir)/ruby/encoding.h
-ossl_asn1.o: $(hdrdir)/ruby/intern.h
-ossl_asn1.o: $(hdrdir)/ruby/io.h
-ossl_asn1.o: $(hdrdir)/ruby/missing.h
-ossl_asn1.o: $(hdrdir)/ruby/onigmo.h
-ossl_asn1.o: $(hdrdir)/ruby/oniguruma.h
-ossl_asn1.o: $(hdrdir)/ruby/ruby.h
-ossl_asn1.o: $(hdrdir)/ruby/st.h
-ossl_asn1.o: $(hdrdir)/ruby/subst.h
-ossl_asn1.o: $(hdrdir)/ruby/thread.h
-ossl_asn1.o: $(top_srcdir)/include/ruby.h
-ossl_asn1.o: openssl_missing.h
-ossl_asn1.o: ossl.h
-ossl_asn1.o: ossl_asn1.c
-ossl_asn1.o: ossl_asn1.h
-ossl_asn1.o: ossl_bio.h
-ossl_asn1.o: ossl_bn.h
-ossl_asn1.o: ossl_cipher.h
-ossl_asn1.o: ossl_config.h
-ossl_asn1.o: ossl_digest.h
-ossl_asn1.o: ossl_engine.h
-ossl_asn1.o: ossl_hmac.h
-ossl_asn1.o: ossl_kdf.h
-ossl_asn1.o: ossl_ns_spki.h
-ossl_asn1.o: ossl_ocsp.h
-ossl_asn1.o: ossl_pkcs12.h
-ossl_asn1.o: ossl_pkcs7.h
-ossl_asn1.o: ossl_pkey.h
-ossl_asn1.o: ossl_rand.h
-ossl_asn1.o: ossl_ssl.h
-ossl_asn1.o: ossl_version.h
-ossl_asn1.o: ossl_x509.h
-ossl_asn1.o: ruby_missing.h
-ossl_bio.o: $(RUBY_EXTCONF_H)
-ossl_bio.o: $(arch_hdrdir)/ruby/config.h
-ossl_bio.o: $(hdrdir)/ruby/backward.h
-ossl_bio.o: $(hdrdir)/ruby/defines.h
-ossl_bio.o: $(hdrdir)/ruby/encoding.h
-ossl_bio.o: $(hdrdir)/ruby/intern.h
-ossl_bio.o: $(hdrdir)/ruby/io.h
-ossl_bio.o: $(hdrdir)/ruby/missing.h
-ossl_bio.o: $(hdrdir)/ruby/onigmo.h
-ossl_bio.o: $(hdrdir)/ruby/oniguruma.h
-ossl_bio.o: $(hdrdir)/ruby/ruby.h
-ossl_bio.o: $(hdrdir)/ruby/st.h
-ossl_bio.o: $(hdrdir)/ruby/subst.h
-ossl_bio.o: $(hdrdir)/ruby/thread.h
-ossl_bio.o: $(top_srcdir)/include/ruby.h
-ossl_bio.o: openssl_missing.h
-ossl_bio.o: ossl.h
-ossl_bio.o: ossl_asn1.h
-ossl_bio.o: ossl_bio.c
-ossl_bio.o: ossl_bio.h
-ossl_bio.o: ossl_bn.h
-ossl_bio.o: ossl_cipher.h
-ossl_bio.o: ossl_config.h
-ossl_bio.o: ossl_digest.h
-ossl_bio.o: ossl_engine.h
-ossl_bio.o: ossl_hmac.h
-ossl_bio.o: ossl_kdf.h
-ossl_bio.o: ossl_ns_spki.h
-ossl_bio.o: ossl_ocsp.h
-ossl_bio.o: ossl_pkcs12.h
-ossl_bio.o: ossl_pkcs7.h
-ossl_bio.o: ossl_pkey.h
-ossl_bio.o: ossl_rand.h
-ossl_bio.o: ossl_ssl.h
-ossl_bio.o: ossl_version.h
-ossl_bio.o: ossl_x509.h
-ossl_bio.o: ruby_missing.h
-ossl_bn.o: $(RUBY_EXTCONF_H)
-ossl_bn.o: $(arch_hdrdir)/ruby/config.h
-ossl_bn.o: $(hdrdir)/ruby/backward.h
-ossl_bn.o: $(hdrdir)/ruby/defines.h
-ossl_bn.o: $(hdrdir)/ruby/encoding.h
-ossl_bn.o: $(hdrdir)/ruby/intern.h
-ossl_bn.o: $(hdrdir)/ruby/io.h
-ossl_bn.o: $(hdrdir)/ruby/missing.h
-ossl_bn.o: $(hdrdir)/ruby/onigmo.h
-ossl_bn.o: $(hdrdir)/ruby/oniguruma.h
-ossl_bn.o: $(hdrdir)/ruby/ruby.h
-ossl_bn.o: $(hdrdir)/ruby/st.h
-ossl_bn.o: $(hdrdir)/ruby/subst.h
-ossl_bn.o: $(hdrdir)/ruby/thread.h
-ossl_bn.o: $(top_srcdir)/include/ruby.h
-ossl_bn.o: openssl_missing.h
-ossl_bn.o: ossl.h
-ossl_bn.o: ossl_asn1.h
-ossl_bn.o: ossl_bio.h
-ossl_bn.o: ossl_bn.c
-ossl_bn.o: ossl_bn.h
-ossl_bn.o: ossl_cipher.h
-ossl_bn.o: ossl_config.h
-ossl_bn.o: ossl_digest.h
-ossl_bn.o: ossl_engine.h
-ossl_bn.o: ossl_hmac.h
-ossl_bn.o: ossl_kdf.h
-ossl_bn.o: ossl_ns_spki.h
-ossl_bn.o: ossl_ocsp.h
-ossl_bn.o: ossl_pkcs12.h
-ossl_bn.o: ossl_pkcs7.h
-ossl_bn.o: ossl_pkey.h
-ossl_bn.o: ossl_rand.h
-ossl_bn.o: ossl_ssl.h
-ossl_bn.o: ossl_version.h
-ossl_bn.o: ossl_x509.h
-ossl_bn.o: ruby_missing.h
-ossl_cipher.o: $(RUBY_EXTCONF_H)
-ossl_cipher.o: $(arch_hdrdir)/ruby/config.h
-ossl_cipher.o: $(hdrdir)/ruby/backward.h
-ossl_cipher.o: $(hdrdir)/ruby/defines.h
-ossl_cipher.o: $(hdrdir)/ruby/encoding.h
-ossl_cipher.o: $(hdrdir)/ruby/intern.h
-ossl_cipher.o: $(hdrdir)/ruby/io.h
-ossl_cipher.o: $(hdrdir)/ruby/missing.h
-ossl_cipher.o: $(hdrdir)/ruby/onigmo.h
-ossl_cipher.o: $(hdrdir)/ruby/oniguruma.h
-ossl_cipher.o: $(hdrdir)/ruby/ruby.h
-ossl_cipher.o: $(hdrdir)/ruby/st.h
-ossl_cipher.o: $(hdrdir)/ruby/subst.h
-ossl_cipher.o: $(hdrdir)/ruby/thread.h
-ossl_cipher.o: $(top_srcdir)/include/ruby.h
-ossl_cipher.o: openssl_missing.h
-ossl_cipher.o: ossl.h
-ossl_cipher.o: ossl_asn1.h
-ossl_cipher.o: ossl_bio.h
-ossl_cipher.o: ossl_bn.h
-ossl_cipher.o: ossl_cipher.c
-ossl_cipher.o: ossl_cipher.h
-ossl_cipher.o: ossl_config.h
-ossl_cipher.o: ossl_digest.h
-ossl_cipher.o: ossl_engine.h
-ossl_cipher.o: ossl_hmac.h
-ossl_cipher.o: ossl_kdf.h
-ossl_cipher.o: ossl_ns_spki.h
-ossl_cipher.o: ossl_ocsp.h
-ossl_cipher.o: ossl_pkcs12.h
-ossl_cipher.o: ossl_pkcs7.h
-ossl_cipher.o: ossl_pkey.h
-ossl_cipher.o: ossl_rand.h
-ossl_cipher.o: ossl_ssl.h
-ossl_cipher.o: ossl_version.h
-ossl_cipher.o: ossl_x509.h
-ossl_cipher.o: ruby_missing.h
-ossl_config.o: $(RUBY_EXTCONF_H)
-ossl_config.o: $(arch_hdrdir)/ruby/config.h
-ossl_config.o: $(hdrdir)/ruby/backward.h
-ossl_config.o: $(hdrdir)/ruby/defines.h
-ossl_config.o: $(hdrdir)/ruby/encoding.h
-ossl_config.o: $(hdrdir)/ruby/intern.h
-ossl_config.o: $(hdrdir)/ruby/io.h
-ossl_config.o: $(hdrdir)/ruby/missing.h
-ossl_config.o: $(hdrdir)/ruby/onigmo.h
-ossl_config.o: $(hdrdir)/ruby/oniguruma.h
-ossl_config.o: $(hdrdir)/ruby/ruby.h
-ossl_config.o: $(hdrdir)/ruby/st.h
-ossl_config.o: $(hdrdir)/ruby/subst.h
-ossl_config.o: $(hdrdir)/ruby/thread.h
-ossl_config.o: $(top_srcdir)/include/ruby.h
-ossl_config.o: openssl_missing.h
-ossl_config.o: ossl.h
-ossl_config.o: ossl_asn1.h
-ossl_config.o: ossl_bio.h
-ossl_config.o: ossl_bn.h
-ossl_config.o: ossl_cipher.h
-ossl_config.o: ossl_config.c
-ossl_config.o: ossl_config.h
-ossl_config.o: ossl_digest.h
-ossl_config.o: ossl_engine.h
-ossl_config.o: ossl_hmac.h
-ossl_config.o: ossl_kdf.h
-ossl_config.o: ossl_ns_spki.h
-ossl_config.o: ossl_ocsp.h
-ossl_config.o: ossl_pkcs12.h
-ossl_config.o: ossl_pkcs7.h
-ossl_config.o: ossl_pkey.h
-ossl_config.o: ossl_rand.h
-ossl_config.o: ossl_ssl.h
-ossl_config.o: ossl_version.h
-ossl_config.o: ossl_x509.h
-ossl_config.o: ruby_missing.h
-ossl_digest.o: $(RUBY_EXTCONF_H)
-ossl_digest.o: $(arch_hdrdir)/ruby/config.h
-ossl_digest.o: $(hdrdir)/ruby/backward.h
-ossl_digest.o: $(hdrdir)/ruby/defines.h
-ossl_digest.o: $(hdrdir)/ruby/encoding.h
-ossl_digest.o: $(hdrdir)/ruby/intern.h
-ossl_digest.o: $(hdrdir)/ruby/io.h
-ossl_digest.o: $(hdrdir)/ruby/missing.h
-ossl_digest.o: $(hdrdir)/ruby/onigmo.h
-ossl_digest.o: $(hdrdir)/ruby/oniguruma.h
-ossl_digest.o: $(hdrdir)/ruby/ruby.h
-ossl_digest.o: $(hdrdir)/ruby/st.h
-ossl_digest.o: $(hdrdir)/ruby/subst.h
-ossl_digest.o: $(hdrdir)/ruby/thread.h
-ossl_digest.o: $(top_srcdir)/include/ruby.h
-ossl_digest.o: openssl_missing.h
-ossl_digest.o: ossl.h
-ossl_digest.o: ossl_asn1.h
-ossl_digest.o: ossl_bio.h
-ossl_digest.o: ossl_bn.h
-ossl_digest.o: ossl_cipher.h
-ossl_digest.o: ossl_config.h
-ossl_digest.o: ossl_digest.c
-ossl_digest.o: ossl_digest.h
-ossl_digest.o: ossl_engine.h
-ossl_digest.o: ossl_hmac.h
-ossl_digest.o: ossl_kdf.h
-ossl_digest.o: ossl_ns_spki.h
-ossl_digest.o: ossl_ocsp.h
-ossl_digest.o: ossl_pkcs12.h
-ossl_digest.o: ossl_pkcs7.h
-ossl_digest.o: ossl_pkey.h
-ossl_digest.o: ossl_rand.h
-ossl_digest.o: ossl_ssl.h
-ossl_digest.o: ossl_version.h
-ossl_digest.o: ossl_x509.h
-ossl_digest.o: ruby_missing.h
-ossl_engine.o: $(RUBY_EXTCONF_H)
-ossl_engine.o: $(arch_hdrdir)/ruby/config.h
-ossl_engine.o: $(hdrdir)/ruby/backward.h
-ossl_engine.o: $(hdrdir)/ruby/defines.h
-ossl_engine.o: $(hdrdir)/ruby/encoding.h
-ossl_engine.o: $(hdrdir)/ruby/intern.h
-ossl_engine.o: $(hdrdir)/ruby/io.h
-ossl_engine.o: $(hdrdir)/ruby/missing.h
-ossl_engine.o: $(hdrdir)/ruby/onigmo.h
-ossl_engine.o: $(hdrdir)/ruby/oniguruma.h
-ossl_engine.o: $(hdrdir)/ruby/ruby.h
-ossl_engine.o: $(hdrdir)/ruby/st.h
-ossl_engine.o: $(hdrdir)/ruby/subst.h
-ossl_engine.o: $(hdrdir)/ruby/thread.h
-ossl_engine.o: $(top_srcdir)/include/ruby.h
-ossl_engine.o: openssl_missing.h
-ossl_engine.o: ossl.h
-ossl_engine.o: ossl_asn1.h
-ossl_engine.o: ossl_bio.h
-ossl_engine.o: ossl_bn.h
-ossl_engine.o: ossl_cipher.h
-ossl_engine.o: ossl_config.h
-ossl_engine.o: ossl_digest.h
-ossl_engine.o: ossl_engine.c
-ossl_engine.o: ossl_engine.h
-ossl_engine.o: ossl_hmac.h
-ossl_engine.o: ossl_kdf.h
-ossl_engine.o: ossl_ns_spki.h
-ossl_engine.o: ossl_ocsp.h
-ossl_engine.o: ossl_pkcs12.h
-ossl_engine.o: ossl_pkcs7.h
-ossl_engine.o: ossl_pkey.h
-ossl_engine.o: ossl_rand.h
-ossl_engine.o: ossl_ssl.h
-ossl_engine.o: ossl_version.h
-ossl_engine.o: ossl_x509.h
-ossl_engine.o: ruby_missing.h
-ossl_hmac.o: $(RUBY_EXTCONF_H)
-ossl_hmac.o: $(arch_hdrdir)/ruby/config.h
-ossl_hmac.o: $(hdrdir)/ruby/backward.h
-ossl_hmac.o: $(hdrdir)/ruby/defines.h
-ossl_hmac.o: $(hdrdir)/ruby/encoding.h
-ossl_hmac.o: $(hdrdir)/ruby/intern.h
-ossl_hmac.o: $(hdrdir)/ruby/io.h
-ossl_hmac.o: $(hdrdir)/ruby/missing.h
-ossl_hmac.o: $(hdrdir)/ruby/onigmo.h
-ossl_hmac.o: $(hdrdir)/ruby/oniguruma.h
-ossl_hmac.o: $(hdrdir)/ruby/ruby.h
-ossl_hmac.o: $(hdrdir)/ruby/st.h
-ossl_hmac.o: $(hdrdir)/ruby/subst.h
-ossl_hmac.o: $(hdrdir)/ruby/thread.h
-ossl_hmac.o: $(top_srcdir)/include/ruby.h
-ossl_hmac.o: openssl_missing.h
-ossl_hmac.o: ossl.h
-ossl_hmac.o: ossl_asn1.h
-ossl_hmac.o: ossl_bio.h
-ossl_hmac.o: ossl_bn.h
-ossl_hmac.o: ossl_cipher.h
-ossl_hmac.o: ossl_config.h
-ossl_hmac.o: ossl_digest.h
-ossl_hmac.o: ossl_engine.h
-ossl_hmac.o: ossl_hmac.c
-ossl_hmac.o: ossl_hmac.h
-ossl_hmac.o: ossl_kdf.h
-ossl_hmac.o: ossl_ns_spki.h
-ossl_hmac.o: ossl_ocsp.h
-ossl_hmac.o: ossl_pkcs12.h
-ossl_hmac.o: ossl_pkcs7.h
-ossl_hmac.o: ossl_pkey.h
-ossl_hmac.o: ossl_rand.h
-ossl_hmac.o: ossl_ssl.h
-ossl_hmac.o: ossl_version.h
-ossl_hmac.o: ossl_x509.h
-ossl_hmac.o: ruby_missing.h
-ossl_kdf.o: $(RUBY_EXTCONF_H)
-ossl_kdf.o: $(arch_hdrdir)/ruby/config.h
-ossl_kdf.o: $(hdrdir)/ruby/backward.h
-ossl_kdf.o: $(hdrdir)/ruby/defines.h
-ossl_kdf.o: $(hdrdir)/ruby/encoding.h
-ossl_kdf.o: $(hdrdir)/ruby/intern.h
-ossl_kdf.o: $(hdrdir)/ruby/io.h
-ossl_kdf.o: $(hdrdir)/ruby/missing.h
-ossl_kdf.o: $(hdrdir)/ruby/onigmo.h
-ossl_kdf.o: $(hdrdir)/ruby/oniguruma.h
-ossl_kdf.o: $(hdrdir)/ruby/ruby.h
-ossl_kdf.o: $(hdrdir)/ruby/st.h
-ossl_kdf.o: $(hdrdir)/ruby/subst.h
-ossl_kdf.o: $(hdrdir)/ruby/thread.h
-ossl_kdf.o: $(top_srcdir)/include/ruby.h
-ossl_kdf.o: openssl_missing.h
-ossl_kdf.o: ossl.h
-ossl_kdf.o: ossl_asn1.h
-ossl_kdf.o: ossl_bio.h
-ossl_kdf.o: ossl_bn.h
-ossl_kdf.o: ossl_cipher.h
-ossl_kdf.o: ossl_config.h
-ossl_kdf.o: ossl_digest.h
-ossl_kdf.o: ossl_engine.h
-ossl_kdf.o: ossl_hmac.h
-ossl_kdf.o: ossl_kdf.c
-ossl_kdf.o: ossl_kdf.h
-ossl_kdf.o: ossl_ns_spki.h
-ossl_kdf.o: ossl_ocsp.h
-ossl_kdf.o: ossl_pkcs12.h
-ossl_kdf.o: ossl_pkcs7.h
-ossl_kdf.o: ossl_pkey.h
-ossl_kdf.o: ossl_rand.h
-ossl_kdf.o: ossl_ssl.h
-ossl_kdf.o: ossl_version.h
-ossl_kdf.o: ossl_x509.h
-ossl_kdf.o: ruby_missing.h
-ossl_ns_spki.o: $(RUBY_EXTCONF_H)
-ossl_ns_spki.o: $(arch_hdrdir)/ruby/config.h
-ossl_ns_spki.o: $(hdrdir)/ruby/backward.h
-ossl_ns_spki.o: $(hdrdir)/ruby/defines.h
-ossl_ns_spki.o: $(hdrdir)/ruby/encoding.h
-ossl_ns_spki.o: $(hdrdir)/ruby/intern.h
-ossl_ns_spki.o: $(hdrdir)/ruby/io.h
-ossl_ns_spki.o: $(hdrdir)/ruby/missing.h
-ossl_ns_spki.o: $(hdrdir)/ruby/onigmo.h
-ossl_ns_spki.o: $(hdrdir)/ruby/oniguruma.h
-ossl_ns_spki.o: $(hdrdir)/ruby/ruby.h
-ossl_ns_spki.o: $(hdrdir)/ruby/st.h
-ossl_ns_spki.o: $(hdrdir)/ruby/subst.h
-ossl_ns_spki.o: $(hdrdir)/ruby/thread.h
-ossl_ns_spki.o: $(top_srcdir)/include/ruby.h
-ossl_ns_spki.o: openssl_missing.h
-ossl_ns_spki.o: ossl.h
-ossl_ns_spki.o: ossl_asn1.h
-ossl_ns_spki.o: ossl_bio.h
-ossl_ns_spki.o: ossl_bn.h
-ossl_ns_spki.o: ossl_cipher.h
-ossl_ns_spki.o: ossl_config.h
-ossl_ns_spki.o: ossl_digest.h
-ossl_ns_spki.o: ossl_engine.h
-ossl_ns_spki.o: ossl_hmac.h
-ossl_ns_spki.o: ossl_kdf.h
-ossl_ns_spki.o: ossl_ns_spki.c
-ossl_ns_spki.o: ossl_ns_spki.h
-ossl_ns_spki.o: ossl_ocsp.h
-ossl_ns_spki.o: ossl_pkcs12.h
-ossl_ns_spki.o: ossl_pkcs7.h
-ossl_ns_spki.o: ossl_pkey.h
-ossl_ns_spki.o: ossl_rand.h
-ossl_ns_spki.o: ossl_ssl.h
-ossl_ns_spki.o: ossl_version.h
-ossl_ns_spki.o: ossl_x509.h
-ossl_ns_spki.o: ruby_missing.h
-ossl_ocsp.o: $(RUBY_EXTCONF_H)
-ossl_ocsp.o: $(arch_hdrdir)/ruby/config.h
-ossl_ocsp.o: $(hdrdir)/ruby/backward.h
-ossl_ocsp.o: $(hdrdir)/ruby/defines.h
-ossl_ocsp.o: $(hdrdir)/ruby/encoding.h
-ossl_ocsp.o: $(hdrdir)/ruby/intern.h
-ossl_ocsp.o: $(hdrdir)/ruby/io.h
-ossl_ocsp.o: $(hdrdir)/ruby/missing.h
-ossl_ocsp.o: $(hdrdir)/ruby/onigmo.h
-ossl_ocsp.o: $(hdrdir)/ruby/oniguruma.h
-ossl_ocsp.o: $(hdrdir)/ruby/ruby.h
-ossl_ocsp.o: $(hdrdir)/ruby/st.h
-ossl_ocsp.o: $(hdrdir)/ruby/subst.h
-ossl_ocsp.o: $(hdrdir)/ruby/thread.h
-ossl_ocsp.o: $(top_srcdir)/include/ruby.h
-ossl_ocsp.o: openssl_missing.h
-ossl_ocsp.o: ossl.h
-ossl_ocsp.o: ossl_asn1.h
-ossl_ocsp.o: ossl_bio.h
-ossl_ocsp.o: ossl_bn.h
-ossl_ocsp.o: ossl_cipher.h
-ossl_ocsp.o: ossl_config.h
-ossl_ocsp.o: ossl_digest.h
-ossl_ocsp.o: ossl_engine.h
-ossl_ocsp.o: ossl_hmac.h
-ossl_ocsp.o: ossl_kdf.h
-ossl_ocsp.o: ossl_ns_spki.h
-ossl_ocsp.o: ossl_ocsp.c
-ossl_ocsp.o: ossl_ocsp.h
-ossl_ocsp.o: ossl_pkcs12.h
-ossl_ocsp.o: ossl_pkcs7.h
-ossl_ocsp.o: ossl_pkey.h
-ossl_ocsp.o: ossl_rand.h
-ossl_ocsp.o: ossl_ssl.h
-ossl_ocsp.o: ossl_version.h
-ossl_ocsp.o: ossl_x509.h
-ossl_ocsp.o: ruby_missing.h
-ossl_pkcs12.o: $(RUBY_EXTCONF_H)
-ossl_pkcs12.o: $(arch_hdrdir)/ruby/config.h
-ossl_pkcs12.o: $(hdrdir)/ruby/backward.h
-ossl_pkcs12.o: $(hdrdir)/ruby/defines.h
-ossl_pkcs12.o: $(hdrdir)/ruby/encoding.h
-ossl_pkcs12.o: $(hdrdir)/ruby/intern.h
-ossl_pkcs12.o: $(hdrdir)/ruby/io.h
-ossl_pkcs12.o: $(hdrdir)/ruby/missing.h
-ossl_pkcs12.o: $(hdrdir)/ruby/onigmo.h
-ossl_pkcs12.o: $(hdrdir)/ruby/oniguruma.h
-ossl_pkcs12.o: $(hdrdir)/ruby/ruby.h
-ossl_pkcs12.o: $(hdrdir)/ruby/st.h
-ossl_pkcs12.o: $(hdrdir)/ruby/subst.h
-ossl_pkcs12.o: $(hdrdir)/ruby/thread.h
-ossl_pkcs12.o: $(top_srcdir)/include/ruby.h
-ossl_pkcs12.o: openssl_missing.h
-ossl_pkcs12.o: ossl.h
-ossl_pkcs12.o: ossl_asn1.h
-ossl_pkcs12.o: ossl_bio.h
-ossl_pkcs12.o: ossl_bn.h
-ossl_pkcs12.o: ossl_cipher.h
-ossl_pkcs12.o: ossl_config.h
-ossl_pkcs12.o: ossl_digest.h
-ossl_pkcs12.o: ossl_engine.h
-ossl_pkcs12.o: ossl_hmac.h
-ossl_pkcs12.o: ossl_kdf.h
-ossl_pkcs12.o: ossl_ns_spki.h
-ossl_pkcs12.o: ossl_ocsp.h
-ossl_pkcs12.o: ossl_pkcs12.c
-ossl_pkcs12.o: ossl_pkcs12.h
-ossl_pkcs12.o: ossl_pkcs7.h
-ossl_pkcs12.o: ossl_pkey.h
-ossl_pkcs12.o: ossl_rand.h
-ossl_pkcs12.o: ossl_ssl.h
-ossl_pkcs12.o: ossl_version.h
-ossl_pkcs12.o: ossl_x509.h
-ossl_pkcs12.o: ruby_missing.h
-ossl_pkcs7.o: $(RUBY_EXTCONF_H)
-ossl_pkcs7.o: $(arch_hdrdir)/ruby/config.h
-ossl_pkcs7.o: $(hdrdir)/ruby/backward.h
-ossl_pkcs7.o: $(hdrdir)/ruby/defines.h
-ossl_pkcs7.o: $(hdrdir)/ruby/encoding.h
-ossl_pkcs7.o: $(hdrdir)/ruby/intern.h
-ossl_pkcs7.o: $(hdrdir)/ruby/io.h
-ossl_pkcs7.o: $(hdrdir)/ruby/missing.h
-ossl_pkcs7.o: $(hdrdir)/ruby/onigmo.h
-ossl_pkcs7.o: $(hdrdir)/ruby/oniguruma.h
-ossl_pkcs7.o: $(hdrdir)/ruby/ruby.h
-ossl_pkcs7.o: $(hdrdir)/ruby/st.h
-ossl_pkcs7.o: $(hdrdir)/ruby/subst.h
-ossl_pkcs7.o: $(hdrdir)/ruby/thread.h
-ossl_pkcs7.o: $(top_srcdir)/include/ruby.h
-ossl_pkcs7.o: openssl_missing.h
-ossl_pkcs7.o: ossl.h
-ossl_pkcs7.o: ossl_asn1.h
-ossl_pkcs7.o: ossl_bio.h
-ossl_pkcs7.o: ossl_bn.h
-ossl_pkcs7.o: ossl_cipher.h
-ossl_pkcs7.o: ossl_config.h
-ossl_pkcs7.o: ossl_digest.h
-ossl_pkcs7.o: ossl_engine.h
-ossl_pkcs7.o: ossl_hmac.h
-ossl_pkcs7.o: ossl_kdf.h
-ossl_pkcs7.o: ossl_ns_spki.h
-ossl_pkcs7.o: ossl_ocsp.h
-ossl_pkcs7.o: ossl_pkcs12.h
-ossl_pkcs7.o: ossl_pkcs7.c
-ossl_pkcs7.o: ossl_pkcs7.h
-ossl_pkcs7.o: ossl_pkey.h
-ossl_pkcs7.o: ossl_rand.h
-ossl_pkcs7.o: ossl_ssl.h
-ossl_pkcs7.o: ossl_version.h
-ossl_pkcs7.o: ossl_x509.h
-ossl_pkcs7.o: ruby_missing.h
-ossl_pkey.o: $(RUBY_EXTCONF_H)
-ossl_pkey.o: $(arch_hdrdir)/ruby/config.h
-ossl_pkey.o: $(hdrdir)/ruby/backward.h
-ossl_pkey.o: $(hdrdir)/ruby/defines.h
-ossl_pkey.o: $(hdrdir)/ruby/encoding.h
-ossl_pkey.o: $(hdrdir)/ruby/intern.h
-ossl_pkey.o: $(hdrdir)/ruby/io.h
-ossl_pkey.o: $(hdrdir)/ruby/missing.h
-ossl_pkey.o: $(hdrdir)/ruby/onigmo.h
-ossl_pkey.o: $(hdrdir)/ruby/oniguruma.h
-ossl_pkey.o: $(hdrdir)/ruby/ruby.h
-ossl_pkey.o: $(hdrdir)/ruby/st.h
-ossl_pkey.o: $(hdrdir)/ruby/subst.h
-ossl_pkey.o: $(hdrdir)/ruby/thread.h
-ossl_pkey.o: $(top_srcdir)/include/ruby.h
-ossl_pkey.o: openssl_missing.h
-ossl_pkey.o: ossl.h
-ossl_pkey.o: ossl_asn1.h
-ossl_pkey.o: ossl_bio.h
-ossl_pkey.o: ossl_bn.h
-ossl_pkey.o: ossl_cipher.h
-ossl_pkey.o: ossl_config.h
-ossl_pkey.o: ossl_digest.h
-ossl_pkey.o: ossl_engine.h
-ossl_pkey.o: ossl_hmac.h
-ossl_pkey.o: ossl_kdf.h
-ossl_pkey.o: ossl_ns_spki.h
-ossl_pkey.o: ossl_ocsp.h
-ossl_pkey.o: ossl_pkcs12.h
-ossl_pkey.o: ossl_pkcs7.h
-ossl_pkey.o: ossl_pkey.c
-ossl_pkey.o: ossl_pkey.h
-ossl_pkey.o: ossl_rand.h
-ossl_pkey.o: ossl_ssl.h
-ossl_pkey.o: ossl_version.h
-ossl_pkey.o: ossl_x509.h
-ossl_pkey.o: ruby_missing.h
-ossl_pkey_dh.o: $(RUBY_EXTCONF_H)
-ossl_pkey_dh.o: $(arch_hdrdir)/ruby/config.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/backward.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/defines.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/encoding.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/intern.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/io.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/missing.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/onigmo.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/oniguruma.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/ruby.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/st.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/subst.h
-ossl_pkey_dh.o: $(hdrdir)/ruby/thread.h
-ossl_pkey_dh.o: $(top_srcdir)/include/ruby.h
-ossl_pkey_dh.o: openssl_missing.h
-ossl_pkey_dh.o: ossl.h
-ossl_pkey_dh.o: ossl_asn1.h
-ossl_pkey_dh.o: ossl_bio.h
-ossl_pkey_dh.o: ossl_bn.h
-ossl_pkey_dh.o: ossl_cipher.h
-ossl_pkey_dh.o: ossl_config.h
-ossl_pkey_dh.o: ossl_digest.h
-ossl_pkey_dh.o: ossl_engine.h
-ossl_pkey_dh.o: ossl_hmac.h
-ossl_pkey_dh.o: ossl_kdf.h
-ossl_pkey_dh.o: ossl_ns_spki.h
-ossl_pkey_dh.o: ossl_ocsp.h
-ossl_pkey_dh.o: ossl_pkcs12.h
-ossl_pkey_dh.o: ossl_pkcs7.h
-ossl_pkey_dh.o: ossl_pkey.h
-ossl_pkey_dh.o: ossl_pkey_dh.c
-ossl_pkey_dh.o: ossl_rand.h
-ossl_pkey_dh.o: ossl_ssl.h
-ossl_pkey_dh.o: ossl_version.h
-ossl_pkey_dh.o: ossl_x509.h
-ossl_pkey_dh.o: ruby_missing.h
-ossl_pkey_dsa.o: $(RUBY_EXTCONF_H)
-ossl_pkey_dsa.o: $(arch_hdrdir)/ruby/config.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/backward.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/defines.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/encoding.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/intern.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/io.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/missing.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/onigmo.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/oniguruma.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/ruby.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/st.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/subst.h
-ossl_pkey_dsa.o: $(hdrdir)/ruby/thread.h
-ossl_pkey_dsa.o: $(top_srcdir)/include/ruby.h
-ossl_pkey_dsa.o: openssl_missing.h
-ossl_pkey_dsa.o: ossl.h
-ossl_pkey_dsa.o: ossl_asn1.h
-ossl_pkey_dsa.o: ossl_bio.h
-ossl_pkey_dsa.o: ossl_bn.h
-ossl_pkey_dsa.o: ossl_cipher.h
-ossl_pkey_dsa.o: ossl_config.h
-ossl_pkey_dsa.o: ossl_digest.h
-ossl_pkey_dsa.o: ossl_engine.h
-ossl_pkey_dsa.o: ossl_hmac.h
-ossl_pkey_dsa.o: ossl_kdf.h
-ossl_pkey_dsa.o: ossl_ns_spki.h
-ossl_pkey_dsa.o: ossl_ocsp.h
-ossl_pkey_dsa.o: ossl_pkcs12.h
-ossl_pkey_dsa.o: ossl_pkcs7.h
-ossl_pkey_dsa.o: ossl_pkey.h
-ossl_pkey_dsa.o: ossl_pkey_dsa.c
-ossl_pkey_dsa.o: ossl_rand.h
-ossl_pkey_dsa.o: ossl_ssl.h
-ossl_pkey_dsa.o: ossl_version.h
-ossl_pkey_dsa.o: ossl_x509.h
-ossl_pkey_dsa.o: ruby_missing.h
-ossl_pkey_ec.o: $(RUBY_EXTCONF_H)
-ossl_pkey_ec.o: $(arch_hdrdir)/ruby/config.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/backward.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/defines.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/encoding.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/intern.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/io.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/missing.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/onigmo.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/oniguruma.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/ruby.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/st.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/subst.h
-ossl_pkey_ec.o: $(hdrdir)/ruby/thread.h
-ossl_pkey_ec.o: $(top_srcdir)/include/ruby.h
-ossl_pkey_ec.o: openssl_missing.h
-ossl_pkey_ec.o: ossl.h
-ossl_pkey_ec.o: ossl_asn1.h
-ossl_pkey_ec.o: ossl_bio.h
-ossl_pkey_ec.o: ossl_bn.h
-ossl_pkey_ec.o: ossl_cipher.h
-ossl_pkey_ec.o: ossl_config.h
-ossl_pkey_ec.o: ossl_digest.h
-ossl_pkey_ec.o: ossl_engine.h
-ossl_pkey_ec.o: ossl_hmac.h
-ossl_pkey_ec.o: ossl_kdf.h
-ossl_pkey_ec.o: ossl_ns_spki.h
-ossl_pkey_ec.o: ossl_ocsp.h
-ossl_pkey_ec.o: ossl_pkcs12.h
-ossl_pkey_ec.o: ossl_pkcs7.h
-ossl_pkey_ec.o: ossl_pkey.h
-ossl_pkey_ec.o: ossl_pkey_ec.c
-ossl_pkey_ec.o: ossl_rand.h
-ossl_pkey_ec.o: ossl_ssl.h
-ossl_pkey_ec.o: ossl_version.h
-ossl_pkey_ec.o: ossl_x509.h
-ossl_pkey_ec.o: ruby_missing.h
-ossl_pkey_rsa.o: $(RUBY_EXTCONF_H)
-ossl_pkey_rsa.o: $(arch_hdrdir)/ruby/config.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/backward.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/defines.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/encoding.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/intern.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/io.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/missing.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/onigmo.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/oniguruma.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/ruby.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/st.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/subst.h
-ossl_pkey_rsa.o: $(hdrdir)/ruby/thread.h
-ossl_pkey_rsa.o: $(top_srcdir)/include/ruby.h
-ossl_pkey_rsa.o: openssl_missing.h
-ossl_pkey_rsa.o: ossl.h
-ossl_pkey_rsa.o: ossl_asn1.h
-ossl_pkey_rsa.o: ossl_bio.h
-ossl_pkey_rsa.o: ossl_bn.h
-ossl_pkey_rsa.o: ossl_cipher.h
-ossl_pkey_rsa.o: ossl_config.h
-ossl_pkey_rsa.o: ossl_digest.h
-ossl_pkey_rsa.o: ossl_engine.h
-ossl_pkey_rsa.o: ossl_hmac.h
-ossl_pkey_rsa.o: ossl_kdf.h
-ossl_pkey_rsa.o: ossl_ns_spki.h
-ossl_pkey_rsa.o: ossl_ocsp.h
-ossl_pkey_rsa.o: ossl_pkcs12.h
-ossl_pkey_rsa.o: ossl_pkcs7.h
-ossl_pkey_rsa.o: ossl_pkey.h
-ossl_pkey_rsa.o: ossl_pkey_rsa.c
-ossl_pkey_rsa.o: ossl_rand.h
-ossl_pkey_rsa.o: ossl_ssl.h
-ossl_pkey_rsa.o: ossl_version.h
-ossl_pkey_rsa.o: ossl_x509.h
-ossl_pkey_rsa.o: ruby_missing.h
-ossl_rand.o: $(RUBY_EXTCONF_H)
-ossl_rand.o: $(arch_hdrdir)/ruby/config.h
-ossl_rand.o: $(hdrdir)/ruby/backward.h
-ossl_rand.o: $(hdrdir)/ruby/defines.h
-ossl_rand.o: $(hdrdir)/ruby/encoding.h
-ossl_rand.o: $(hdrdir)/ruby/intern.h
-ossl_rand.o: $(hdrdir)/ruby/io.h
-ossl_rand.o: $(hdrdir)/ruby/missing.h
-ossl_rand.o: $(hdrdir)/ruby/onigmo.h
-ossl_rand.o: $(hdrdir)/ruby/oniguruma.h
-ossl_rand.o: $(hdrdir)/ruby/ruby.h
-ossl_rand.o: $(hdrdir)/ruby/st.h
-ossl_rand.o: $(hdrdir)/ruby/subst.h
-ossl_rand.o: $(hdrdir)/ruby/thread.h
-ossl_rand.o: $(top_srcdir)/include/ruby.h
-ossl_rand.o: openssl_missing.h
-ossl_rand.o: ossl.h
-ossl_rand.o: ossl_asn1.h
-ossl_rand.o: ossl_bio.h
-ossl_rand.o: ossl_bn.h
-ossl_rand.o: ossl_cipher.h
-ossl_rand.o: ossl_config.h
-ossl_rand.o: ossl_digest.h
-ossl_rand.o: ossl_engine.h
-ossl_rand.o: ossl_hmac.h
-ossl_rand.o: ossl_kdf.h
-ossl_rand.o: ossl_ns_spki.h
-ossl_rand.o: ossl_ocsp.h
-ossl_rand.o: ossl_pkcs12.h
-ossl_rand.o: ossl_pkcs7.h
-ossl_rand.o: ossl_pkey.h
-ossl_rand.o: ossl_rand.c
-ossl_rand.o: ossl_rand.h
-ossl_rand.o: ossl_ssl.h
-ossl_rand.o: ossl_version.h
-ossl_rand.o: ossl_x509.h
-ossl_rand.o: ruby_missing.h
-ossl_ssl.o: $(RUBY_EXTCONF_H)
-ossl_ssl.o: $(arch_hdrdir)/ruby/config.h
-ossl_ssl.o: $(hdrdir)/ruby/backward.h
-ossl_ssl.o: $(hdrdir)/ruby/defines.h
-ossl_ssl.o: $(hdrdir)/ruby/encoding.h
-ossl_ssl.o: $(hdrdir)/ruby/intern.h
-ossl_ssl.o: $(hdrdir)/ruby/io.h
-ossl_ssl.o: $(hdrdir)/ruby/missing.h
-ossl_ssl.o: $(hdrdir)/ruby/onigmo.h
-ossl_ssl.o: $(hdrdir)/ruby/oniguruma.h
-ossl_ssl.o: $(hdrdir)/ruby/ruby.h
-ossl_ssl.o: $(hdrdir)/ruby/st.h
-ossl_ssl.o: $(hdrdir)/ruby/subst.h
-ossl_ssl.o: $(hdrdir)/ruby/thread.h
-ossl_ssl.o: $(top_srcdir)/include/ruby.h
-ossl_ssl.o: openssl_missing.h
-ossl_ssl.o: ossl.h
-ossl_ssl.o: ossl_asn1.h
-ossl_ssl.o: ossl_bio.h
-ossl_ssl.o: ossl_bn.h
-ossl_ssl.o: ossl_cipher.h
-ossl_ssl.o: ossl_config.h
-ossl_ssl.o: ossl_digest.h
-ossl_ssl.o: ossl_engine.h
-ossl_ssl.o: ossl_hmac.h
-ossl_ssl.o: ossl_kdf.h
-ossl_ssl.o: ossl_ns_spki.h
-ossl_ssl.o: ossl_ocsp.h
-ossl_ssl.o: ossl_pkcs12.h
-ossl_ssl.o: ossl_pkcs7.h
-ossl_ssl.o: ossl_pkey.h
-ossl_ssl.o: ossl_rand.h
-ossl_ssl.o: ossl_ssl.c
-ossl_ssl.o: ossl_ssl.h
-ossl_ssl.o: ossl_version.h
-ossl_ssl.o: ossl_x509.h
-ossl_ssl.o: ruby_missing.h
-ossl_ssl_session.o: $(RUBY_EXTCONF_H)
-ossl_ssl_session.o: $(arch_hdrdir)/ruby/config.h
-ossl_ssl_session.o: $(hdrdir)/ruby/backward.h
-ossl_ssl_session.o: $(hdrdir)/ruby/defines.h
-ossl_ssl_session.o: $(hdrdir)/ruby/encoding.h
-ossl_ssl_session.o: $(hdrdir)/ruby/intern.h
-ossl_ssl_session.o: $(hdrdir)/ruby/io.h
-ossl_ssl_session.o: $(hdrdir)/ruby/missing.h
-ossl_ssl_session.o: $(hdrdir)/ruby/onigmo.h
-ossl_ssl_session.o: $(hdrdir)/ruby/oniguruma.h
-ossl_ssl_session.o: $(hdrdir)/ruby/ruby.h
-ossl_ssl_session.o: $(hdrdir)/ruby/st.h
-ossl_ssl_session.o: $(hdrdir)/ruby/subst.h
-ossl_ssl_session.o: $(hdrdir)/ruby/thread.h
-ossl_ssl_session.o: $(top_srcdir)/include/ruby.h
-ossl_ssl_session.o: openssl_missing.h
-ossl_ssl_session.o: ossl.h
-ossl_ssl_session.o: ossl_asn1.h
-ossl_ssl_session.o: ossl_bio.h
-ossl_ssl_session.o: ossl_bn.h
-ossl_ssl_session.o: ossl_cipher.h
-ossl_ssl_session.o: ossl_config.h
-ossl_ssl_session.o: ossl_digest.h
-ossl_ssl_session.o: ossl_engine.h
-ossl_ssl_session.o: ossl_hmac.h
-ossl_ssl_session.o: ossl_kdf.h
-ossl_ssl_session.o: ossl_ns_spki.h
-ossl_ssl_session.o: ossl_ocsp.h
-ossl_ssl_session.o: ossl_pkcs12.h
-ossl_ssl_session.o: ossl_pkcs7.h
-ossl_ssl_session.o: ossl_pkey.h
-ossl_ssl_session.o: ossl_rand.h
-ossl_ssl_session.o: ossl_ssl.h
-ossl_ssl_session.o: ossl_ssl_session.c
-ossl_ssl_session.o: ossl_version.h
-ossl_ssl_session.o: ossl_x509.h
-ossl_ssl_session.o: ruby_missing.h
-ossl_x509.o: $(RUBY_EXTCONF_H)
-ossl_x509.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509.o: $(hdrdir)/ruby/backward.h
-ossl_x509.o: $(hdrdir)/ruby/defines.h
-ossl_x509.o: $(hdrdir)/ruby/encoding.h
-ossl_x509.o: $(hdrdir)/ruby/intern.h
-ossl_x509.o: $(hdrdir)/ruby/io.h
-ossl_x509.o: $(hdrdir)/ruby/missing.h
-ossl_x509.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509.o: $(hdrdir)/ruby/ruby.h
-ossl_x509.o: $(hdrdir)/ruby/st.h
-ossl_x509.o: $(hdrdir)/ruby/subst.h
-ossl_x509.o: $(hdrdir)/ruby/thread.h
-ossl_x509.o: $(top_srcdir)/include/ruby.h
-ossl_x509.o: openssl_missing.h
-ossl_x509.o: ossl.h
-ossl_x509.o: ossl_asn1.h
-ossl_x509.o: ossl_bio.h
-ossl_x509.o: ossl_bn.h
-ossl_x509.o: ossl_cipher.h
-ossl_x509.o: ossl_config.h
-ossl_x509.o: ossl_digest.h
-ossl_x509.o: ossl_engine.h
-ossl_x509.o: ossl_hmac.h
-ossl_x509.o: ossl_kdf.h
-ossl_x509.o: ossl_ns_spki.h
-ossl_x509.o: ossl_ocsp.h
-ossl_x509.o: ossl_pkcs12.h
-ossl_x509.o: ossl_pkcs7.h
-ossl_x509.o: ossl_pkey.h
-ossl_x509.o: ossl_rand.h
-ossl_x509.o: ossl_ssl.h
-ossl_x509.o: ossl_version.h
-ossl_x509.o: ossl_x509.c
-ossl_x509.o: ossl_x509.h
-ossl_x509.o: ruby_missing.h
-ossl_x509attr.o: $(RUBY_EXTCONF_H)
-ossl_x509attr.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509attr.o: $(hdrdir)/ruby/backward.h
-ossl_x509attr.o: $(hdrdir)/ruby/defines.h
-ossl_x509attr.o: $(hdrdir)/ruby/encoding.h
-ossl_x509attr.o: $(hdrdir)/ruby/intern.h
-ossl_x509attr.o: $(hdrdir)/ruby/io.h
-ossl_x509attr.o: $(hdrdir)/ruby/missing.h
-ossl_x509attr.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509attr.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509attr.o: $(hdrdir)/ruby/ruby.h
-ossl_x509attr.o: $(hdrdir)/ruby/st.h
-ossl_x509attr.o: $(hdrdir)/ruby/subst.h
-ossl_x509attr.o: $(hdrdir)/ruby/thread.h
-ossl_x509attr.o: $(top_srcdir)/include/ruby.h
-ossl_x509attr.o: openssl_missing.h
-ossl_x509attr.o: ossl.h
-ossl_x509attr.o: ossl_asn1.h
-ossl_x509attr.o: ossl_bio.h
-ossl_x509attr.o: ossl_bn.h
-ossl_x509attr.o: ossl_cipher.h
-ossl_x509attr.o: ossl_config.h
-ossl_x509attr.o: ossl_digest.h
-ossl_x509attr.o: ossl_engine.h
-ossl_x509attr.o: ossl_hmac.h
-ossl_x509attr.o: ossl_kdf.h
-ossl_x509attr.o: ossl_ns_spki.h
-ossl_x509attr.o: ossl_ocsp.h
-ossl_x509attr.o: ossl_pkcs12.h
-ossl_x509attr.o: ossl_pkcs7.h
-ossl_x509attr.o: ossl_pkey.h
-ossl_x509attr.o: ossl_rand.h
-ossl_x509attr.o: ossl_ssl.h
-ossl_x509attr.o: ossl_version.h
-ossl_x509attr.o: ossl_x509.h
-ossl_x509attr.o: ossl_x509attr.c
-ossl_x509attr.o: ruby_missing.h
-ossl_x509cert.o: $(RUBY_EXTCONF_H)
-ossl_x509cert.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509cert.o: $(hdrdir)/ruby/backward.h
-ossl_x509cert.o: $(hdrdir)/ruby/defines.h
-ossl_x509cert.o: $(hdrdir)/ruby/encoding.h
-ossl_x509cert.o: $(hdrdir)/ruby/intern.h
-ossl_x509cert.o: $(hdrdir)/ruby/io.h
-ossl_x509cert.o: $(hdrdir)/ruby/missing.h
-ossl_x509cert.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509cert.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509cert.o: $(hdrdir)/ruby/ruby.h
-ossl_x509cert.o: $(hdrdir)/ruby/st.h
-ossl_x509cert.o: $(hdrdir)/ruby/subst.h
-ossl_x509cert.o: $(hdrdir)/ruby/thread.h
-ossl_x509cert.o: $(top_srcdir)/include/ruby.h
-ossl_x509cert.o: openssl_missing.h
-ossl_x509cert.o: ossl.h
-ossl_x509cert.o: ossl_asn1.h
-ossl_x509cert.o: ossl_bio.h
-ossl_x509cert.o: ossl_bn.h
-ossl_x509cert.o: ossl_cipher.h
-ossl_x509cert.o: ossl_config.h
-ossl_x509cert.o: ossl_digest.h
-ossl_x509cert.o: ossl_engine.h
-ossl_x509cert.o: ossl_hmac.h
-ossl_x509cert.o: ossl_kdf.h
-ossl_x509cert.o: ossl_ns_spki.h
-ossl_x509cert.o: ossl_ocsp.h
-ossl_x509cert.o: ossl_pkcs12.h
-ossl_x509cert.o: ossl_pkcs7.h
-ossl_x509cert.o: ossl_pkey.h
-ossl_x509cert.o: ossl_rand.h
-ossl_x509cert.o: ossl_ssl.h
-ossl_x509cert.o: ossl_version.h
-ossl_x509cert.o: ossl_x509.h
-ossl_x509cert.o: ossl_x509cert.c
-ossl_x509cert.o: ruby_missing.h
-ossl_x509crl.o: $(RUBY_EXTCONF_H)
-ossl_x509crl.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509crl.o: $(hdrdir)/ruby/backward.h
-ossl_x509crl.o: $(hdrdir)/ruby/defines.h
-ossl_x509crl.o: $(hdrdir)/ruby/encoding.h
-ossl_x509crl.o: $(hdrdir)/ruby/intern.h
-ossl_x509crl.o: $(hdrdir)/ruby/io.h
-ossl_x509crl.o: $(hdrdir)/ruby/missing.h
-ossl_x509crl.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509crl.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509crl.o: $(hdrdir)/ruby/ruby.h
-ossl_x509crl.o: $(hdrdir)/ruby/st.h
-ossl_x509crl.o: $(hdrdir)/ruby/subst.h
-ossl_x509crl.o: $(hdrdir)/ruby/thread.h
-ossl_x509crl.o: $(top_srcdir)/include/ruby.h
-ossl_x509crl.o: openssl_missing.h
-ossl_x509crl.o: ossl.h
-ossl_x509crl.o: ossl_asn1.h
-ossl_x509crl.o: ossl_bio.h
-ossl_x509crl.o: ossl_bn.h
-ossl_x509crl.o: ossl_cipher.h
-ossl_x509crl.o: ossl_config.h
-ossl_x509crl.o: ossl_digest.h
-ossl_x509crl.o: ossl_engine.h
-ossl_x509crl.o: ossl_hmac.h
-ossl_x509crl.o: ossl_kdf.h
-ossl_x509crl.o: ossl_ns_spki.h
-ossl_x509crl.o: ossl_ocsp.h
-ossl_x509crl.o: ossl_pkcs12.h
-ossl_x509crl.o: ossl_pkcs7.h
-ossl_x509crl.o: ossl_pkey.h
-ossl_x509crl.o: ossl_rand.h
-ossl_x509crl.o: ossl_ssl.h
-ossl_x509crl.o: ossl_version.h
-ossl_x509crl.o: ossl_x509.h
-ossl_x509crl.o: ossl_x509crl.c
-ossl_x509crl.o: ruby_missing.h
-ossl_x509ext.o: $(RUBY_EXTCONF_H)
-ossl_x509ext.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509ext.o: $(hdrdir)/ruby/backward.h
-ossl_x509ext.o: $(hdrdir)/ruby/defines.h
-ossl_x509ext.o: $(hdrdir)/ruby/encoding.h
-ossl_x509ext.o: $(hdrdir)/ruby/intern.h
-ossl_x509ext.o: $(hdrdir)/ruby/io.h
-ossl_x509ext.o: $(hdrdir)/ruby/missing.h
-ossl_x509ext.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509ext.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509ext.o: $(hdrdir)/ruby/ruby.h
-ossl_x509ext.o: $(hdrdir)/ruby/st.h
-ossl_x509ext.o: $(hdrdir)/ruby/subst.h
-ossl_x509ext.o: $(hdrdir)/ruby/thread.h
-ossl_x509ext.o: $(top_srcdir)/include/ruby.h
-ossl_x509ext.o: openssl_missing.h
-ossl_x509ext.o: ossl.h
-ossl_x509ext.o: ossl_asn1.h
-ossl_x509ext.o: ossl_bio.h
-ossl_x509ext.o: ossl_bn.h
-ossl_x509ext.o: ossl_cipher.h
-ossl_x509ext.o: ossl_config.h
-ossl_x509ext.o: ossl_digest.h
-ossl_x509ext.o: ossl_engine.h
-ossl_x509ext.o: ossl_hmac.h
-ossl_x509ext.o: ossl_kdf.h
-ossl_x509ext.o: ossl_ns_spki.h
-ossl_x509ext.o: ossl_ocsp.h
-ossl_x509ext.o: ossl_pkcs12.h
-ossl_x509ext.o: ossl_pkcs7.h
-ossl_x509ext.o: ossl_pkey.h
-ossl_x509ext.o: ossl_rand.h
-ossl_x509ext.o: ossl_ssl.h
-ossl_x509ext.o: ossl_version.h
-ossl_x509ext.o: ossl_x509.h
-ossl_x509ext.o: ossl_x509ext.c
-ossl_x509ext.o: ruby_missing.h
-ossl_x509name.o: $(RUBY_EXTCONF_H)
-ossl_x509name.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509name.o: $(hdrdir)/ruby/backward.h
-ossl_x509name.o: $(hdrdir)/ruby/defines.h
-ossl_x509name.o: $(hdrdir)/ruby/encoding.h
-ossl_x509name.o: $(hdrdir)/ruby/intern.h
-ossl_x509name.o: $(hdrdir)/ruby/io.h
-ossl_x509name.o: $(hdrdir)/ruby/missing.h
-ossl_x509name.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509name.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509name.o: $(hdrdir)/ruby/ruby.h
-ossl_x509name.o: $(hdrdir)/ruby/st.h
-ossl_x509name.o: $(hdrdir)/ruby/subst.h
-ossl_x509name.o: $(hdrdir)/ruby/thread.h
-ossl_x509name.o: $(top_srcdir)/include/ruby.h
-ossl_x509name.o: openssl_missing.h
-ossl_x509name.o: ossl.h
-ossl_x509name.o: ossl_asn1.h
-ossl_x509name.o: ossl_bio.h
-ossl_x509name.o: ossl_bn.h
-ossl_x509name.o: ossl_cipher.h
-ossl_x509name.o: ossl_config.h
-ossl_x509name.o: ossl_digest.h
-ossl_x509name.o: ossl_engine.h
-ossl_x509name.o: ossl_hmac.h
-ossl_x509name.o: ossl_kdf.h
-ossl_x509name.o: ossl_ns_spki.h
-ossl_x509name.o: ossl_ocsp.h
-ossl_x509name.o: ossl_pkcs12.h
-ossl_x509name.o: ossl_pkcs7.h
-ossl_x509name.o: ossl_pkey.h
-ossl_x509name.o: ossl_rand.h
-ossl_x509name.o: ossl_ssl.h
-ossl_x509name.o: ossl_version.h
-ossl_x509name.o: ossl_x509.h
-ossl_x509name.o: ossl_x509name.c
-ossl_x509name.o: ruby_missing.h
-ossl_x509req.o: $(RUBY_EXTCONF_H)
-ossl_x509req.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509req.o: $(hdrdir)/ruby/backward.h
-ossl_x509req.o: $(hdrdir)/ruby/defines.h
-ossl_x509req.o: $(hdrdir)/ruby/encoding.h
-ossl_x509req.o: $(hdrdir)/ruby/intern.h
-ossl_x509req.o: $(hdrdir)/ruby/io.h
-ossl_x509req.o: $(hdrdir)/ruby/missing.h
-ossl_x509req.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509req.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509req.o: $(hdrdir)/ruby/ruby.h
-ossl_x509req.o: $(hdrdir)/ruby/st.h
-ossl_x509req.o: $(hdrdir)/ruby/subst.h
-ossl_x509req.o: $(hdrdir)/ruby/thread.h
-ossl_x509req.o: $(top_srcdir)/include/ruby.h
-ossl_x509req.o: openssl_missing.h
-ossl_x509req.o: ossl.h
-ossl_x509req.o: ossl_asn1.h
-ossl_x509req.o: ossl_bio.h
-ossl_x509req.o: ossl_bn.h
-ossl_x509req.o: ossl_cipher.h
-ossl_x509req.o: ossl_config.h
-ossl_x509req.o: ossl_digest.h
-ossl_x509req.o: ossl_engine.h
-ossl_x509req.o: ossl_hmac.h
-ossl_x509req.o: ossl_kdf.h
-ossl_x509req.o: ossl_ns_spki.h
-ossl_x509req.o: ossl_ocsp.h
-ossl_x509req.o: ossl_pkcs12.h
-ossl_x509req.o: ossl_pkcs7.h
-ossl_x509req.o: ossl_pkey.h
-ossl_x509req.o: ossl_rand.h
-ossl_x509req.o: ossl_ssl.h
-ossl_x509req.o: ossl_version.h
-ossl_x509req.o: ossl_x509.h
-ossl_x509req.o: ossl_x509req.c
-ossl_x509req.o: ruby_missing.h
-ossl_x509revoked.o: $(RUBY_EXTCONF_H)
-ossl_x509revoked.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509revoked.o: $(hdrdir)/ruby/backward.h
-ossl_x509revoked.o: $(hdrdir)/ruby/defines.h
-ossl_x509revoked.o: $(hdrdir)/ruby/encoding.h
-ossl_x509revoked.o: $(hdrdir)/ruby/intern.h
-ossl_x509revoked.o: $(hdrdir)/ruby/io.h
-ossl_x509revoked.o: $(hdrdir)/ruby/missing.h
-ossl_x509revoked.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509revoked.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509revoked.o: $(hdrdir)/ruby/ruby.h
-ossl_x509revoked.o: $(hdrdir)/ruby/st.h
-ossl_x509revoked.o: $(hdrdir)/ruby/subst.h
-ossl_x509revoked.o: $(hdrdir)/ruby/thread.h
-ossl_x509revoked.o: $(top_srcdir)/include/ruby.h
-ossl_x509revoked.o: openssl_missing.h
-ossl_x509revoked.o: ossl.h
-ossl_x509revoked.o: ossl_asn1.h
-ossl_x509revoked.o: ossl_bio.h
-ossl_x509revoked.o: ossl_bn.h
-ossl_x509revoked.o: ossl_cipher.h
-ossl_x509revoked.o: ossl_config.h
-ossl_x509revoked.o: ossl_digest.h
-ossl_x509revoked.o: ossl_engine.h
-ossl_x509revoked.o: ossl_hmac.h
-ossl_x509revoked.o: ossl_kdf.h
-ossl_x509revoked.o: ossl_ns_spki.h
-ossl_x509revoked.o: ossl_ocsp.h
-ossl_x509revoked.o: ossl_pkcs12.h
-ossl_x509revoked.o: ossl_pkcs7.h
-ossl_x509revoked.o: ossl_pkey.h
-ossl_x509revoked.o: ossl_rand.h
-ossl_x509revoked.o: ossl_ssl.h
-ossl_x509revoked.o: ossl_version.h
-ossl_x509revoked.o: ossl_x509.h
-ossl_x509revoked.o: ossl_x509revoked.c
-ossl_x509revoked.o: ruby_missing.h
-ossl_x509store.o: $(RUBY_EXTCONF_H)
-ossl_x509store.o: $(arch_hdrdir)/ruby/config.h
-ossl_x509store.o: $(hdrdir)/ruby/backward.h
-ossl_x509store.o: $(hdrdir)/ruby/defines.h
-ossl_x509store.o: $(hdrdir)/ruby/encoding.h
-ossl_x509store.o: $(hdrdir)/ruby/intern.h
-ossl_x509store.o: $(hdrdir)/ruby/io.h
-ossl_x509store.o: $(hdrdir)/ruby/missing.h
-ossl_x509store.o: $(hdrdir)/ruby/onigmo.h
-ossl_x509store.o: $(hdrdir)/ruby/oniguruma.h
-ossl_x509store.o: $(hdrdir)/ruby/ruby.h
-ossl_x509store.o: $(hdrdir)/ruby/st.h
-ossl_x509store.o: $(hdrdir)/ruby/subst.h
-ossl_x509store.o: $(hdrdir)/ruby/thread.h
-ossl_x509store.o: $(top_srcdir)/include/ruby.h
-ossl_x509store.o: openssl_missing.h
-ossl_x509store.o: ossl.h
-ossl_x509store.o: ossl_asn1.h
-ossl_x509store.o: ossl_bio.h
-ossl_x509store.o: ossl_bn.h
-ossl_x509store.o: ossl_cipher.h
-ossl_x509store.o: ossl_config.h
-ossl_x509store.o: ossl_digest.h
-ossl_x509store.o: ossl_engine.h
-ossl_x509store.o: ossl_hmac.h
-ossl_x509store.o: ossl_kdf.h
-ossl_x509store.o: ossl_ns_spki.h
-ossl_x509store.o: ossl_ocsp.h
-ossl_x509store.o: ossl_pkcs12.h
-ossl_x509store.o: ossl_pkcs7.h
-ossl_x509store.o: ossl_pkey.h
-ossl_x509store.o: ossl_rand.h
-ossl_x509store.o: ossl_ssl.h
-ossl_x509store.o: ossl_version.h
-ossl_x509store.o: ossl_x509.h
-ossl_x509store.o: ossl_x509store.c
-ossl_x509store.o: ruby_missing.h
-# AUTOGENERATED DEPENDENCIES END
diff -ruN ruby-2.5.9.orig/ext/openssl/deprecation.rb ruby-2.5.9/ext/openssl/deprecation.rb
--- ruby-2.5.9.orig/ext/openssl/deprecation.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/deprecation.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,23 +0,0 @@
-# frozen_string_literal: false
-module OpenSSL
-  def self.deprecated_warning_flag
-    unless flag = (@deprecated_warning_flag ||= nil)
-      if try_compile("", flag = "-Werror=deprecated-declarations")
-        $warnflags << " #{flag}"
-      else
-        flag = ""
-      end
-      @deprecated_warning_flag = flag
-    end
-    flag
-  end
-
-  def self.check_func(func, header)
-    have_func(func, header, deprecated_warning_flag)
-  end
-
-  def self.check_func_or_macro(func, header)
-    check_func(func, header) or
-      have_macro(func, header) && $defs.push("-DHAVE_#{func.upcase}")
-  end
-end
diff -ruN ruby-2.5.9.orig/ext/openssl/extconf.rb ruby-2.5.9/ext/openssl/extconf.rb
--- ruby-2.5.9.orig/ext/openssl/extconf.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/extconf.rb	2025-01-29 19:08:02.377182194 +0100
@@ -1,5 +1,5 @@
 # -*- coding: us-ascii -*-
-# frozen_string_literal: false
+# frozen_string_literal: true
 =begin
 = Info
   'OpenSSL for Ruby 2' project
@@ -8,27 +8,41 @@
 
 = Licence
   This program is licensed under the same licence as Ruby.
-  (See the file 'LICENCE'.)
+  (See the file 'COPYING'.)
 =end
 
 require "mkmf"
-require File.expand_path('../deprecation', __FILE__)
 
-dir_config("openssl")
+ssl_dirs = dir_config("openssl")
+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"
 
-# Add -Werror=deprecated-declarations to $warnflags if available
-OpenSSL.deprecated_warning_flag
+$defs.push("-D""OPENSSL_SUPPRESS_DEPRECATED")
 
-##
-# 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
+have_func("rb_io_descriptor")
+have_func("rb_io_maybe_wait(0, Qnil, Qnil, Qnil)", "ruby/io.h") # Ruby 3.1
+have_func("rb_io_timeout", "ruby/io.h")
 
 Logging::message "=== Checking for system dependent stuff... ===\n"
 have_library("nsl", "t_open")
@@ -37,8 +51,11 @@
   have_library("ws2_32")
 end
 
-Logging::message "=== Checking for required stuff... ===\n"
-result = pkg_config("openssl") && have_header("openssl/ssl.h")
+if $mingw
+  append_cflags '-D_FORTIFY_SOURCE=2'
+  append_ldflags '-fstack-protector'
+  have_library 'ssp'
+end
 
 def find_openssl_library
   if $mswin || $mingw
@@ -90,84 +107,115 @@
   return false
 end
 
-unless result
-  unless find_openssl_library
-    Logging::message "=== Checking for required stuff failed. ===\n"
-    Logging::message "Makefile wasn't created. Fix the errors above.\n"
-    raise "OpenSSL library could not be found. You might want to use " \
-      "--with-openssl-dir=<dir> option to specify the prefix where OpenSSL " \
-      "is installed."
-  end
+Logging::message "=== Checking for required stuff... ===\n"
+pkg_config_found = !dir_config_given && pkg_config("openssl") && have_header("openssl/ssl.h")
+
+if !pkg_config_found && !find_openssl_library
+  Logging::message "=== Checking for required stuff failed. ===\n"
+  Logging::message "Makefile wasn't created. Fix the errors above.\n"
+  raise "OpenSSL library could not be found. You might want to use " \
+    "--with-openssl-dir=<dir> option to specify the prefix where OpenSSL " \
+    "is installed."
+end
+
+version_ok = if have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h")
+  is_libressl = true
+  checking_for("LibreSSL version >= 3.1.0") {
+    try_static_assert("LIBRESSL_VERSION_NUMBER >= 0x30100000L", "openssl/opensslv.h") }
+else
+  checking_for("OpenSSL version >= 1.0.2") {
+    try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10002000L", "openssl/opensslv.h") }
+end
+unless version_ok
+  raise "OpenSSL >= 1.0.2 or LibreSSL >= 3.1.0 is required"
 end
 
-unless checking_for("OpenSSL version is 1.0.1 or later") {
-    try_static_assert("OPENSSL_VERSION_NUMBER >= 0x10001000L", "openssl/opensslv.h") }
-  raise "OpenSSL >= 1.0.1 or LibreSSL is required"
+# Prevent wincrypt.h from being included, which defines conflicting macro with openssl/x509.h
+if is_libressl && ($mswin || $mingw)
+  $defs.push("-DNOCRYPT")
 end
 
 Logging::message "=== Checking for OpenSSL features... ===\n"
+evp_h = "openssl/evp.h".freeze
+x509_h = "openssl/x509.h".freeze
+ts_h = "openssl/ts.h".freeze
+ssl_h = "openssl/ssl.h".freeze
+
 # compile options
-have_func("RAND_egd")
-engines = %w{builtin_engines openbsd_dev_crypto dynamic 4758cca aep atalla chil
-             cswift nuron sureware ubsec padlock capi gmp gost cryptodev aesni}
+have_func("RAND_egd()", "openssl/rand.h")
+engines = %w{dynamic 4758cca aep atalla chil
+             cswift nuron sureware ubsec padlock capi gmp gost cryptodev}
 engines.each { |name|
-  OpenSSL.check_func_or_macro("ENGINE_load_#{name}", "openssl/engine.h")
+  have_func("ENGINE_load_#{name}()", "openssl/engine.h")
 }
 
-if ($mswin || $mingw) && have_macro("LIBRESSL_VERSION_NUMBER", "openssl/opensslv.h")
-  $defs.push("-DNOCRYPT")
-end
-
-# added in 1.0.2
-have_func("EC_curve_nist2nid")
-have_func("X509_REVOKED_dup")
-have_func("X509_STORE_CTX_get0_store")
-have_func("SSL_CTX_set_alpn_select_cb")
-OpenSSL.check_func_or_macro("SSL_CTX_set1_curves_list", "openssl/ssl.h")
-OpenSSL.check_func_or_macro("SSL_CTX_set_ecdh_auto", "openssl/ssl.h")
-OpenSSL.check_func_or_macro("SSL_get_server_tmp_key", "openssl/ssl.h")
-have_func("SSL_is_server")
+# missing in libressl < 3.5
+have_func("i2d_re_X509_tbs(NULL, NULL)", x509_h)
 
 # added in 1.1.0
-if !have_struct_member("SSL", "ctx", "openssl/ssl.h") ||
-    try_static_assert("LIBRESSL_VERSION_NUMBER >= 0x2070000fL", "openssl/opensslv.h")
+if !have_struct_member("SSL", "ctx", "openssl/ssl.h") || is_libressl
   $defs.push("-DHAVE_OPAQUE_OPENSSL")
 end
-have_func("CRYPTO_lock") || $defs.push("-DHAVE_OPENSSL_110_THREADING_API")
-have_func("BN_GENCB_new")
-have_func("BN_GENCB_free")
-have_func("BN_GENCB_get_arg")
-have_func("EVP_MD_CTX_new")
-have_func("EVP_MD_CTX_free")
-have_func("HMAC_CTX_new")
-have_func("HMAC_CTX_free")
-OpenSSL.check_func("RAND_pseudo_bytes", "openssl/rand.h") # deprecated
-have_func("X509_STORE_get_ex_data")
-have_func("X509_STORE_set_ex_data")
-have_func("X509_CRL_get0_signature")
-have_func("X509_REQ_get0_signature")
-have_func("X509_REVOKED_get0_serialNumber")
-have_func("X509_REVOKED_get0_revocationDate")
-have_func("X509_get0_tbs_sigalg")
-have_func("X509_STORE_CTX_get0_untrusted")
-have_func("X509_STORE_CTX_get0_cert")
-have_func("X509_STORE_CTX_get0_chain")
-have_func("OCSP_SINGLERESP_get0_id")
-have_func("SSL_CTX_get_ciphers")
-have_func("X509_up_ref")
-have_func("X509_CRL_up_ref")
-have_func("X509_STORE_up_ref")
-have_func("SSL_SESSION_up_ref")
-have_func("EVP_PKEY_up_ref")
-OpenSSL.check_func_or_macro("SSL_CTX_set_tmp_ecdh_callback", "openssl/ssl.h") # removed
-OpenSSL.check_func_or_macro("SSL_CTX_set_min_proto_version", "openssl/ssl.h")
-have_func("SSL_CTX_get_security_level")
-have_func("X509_get0_notBefore")
-have_func("SSL_SESSION_get_protocol_version")
-have_func("EVP_PBE_scrypt")
+have_func("EVP_MD_CTX_new()", evp_h)
+have_func("EVP_MD_CTX_free(NULL)", evp_h)
+have_func("EVP_MD_CTX_pkey_ctx(NULL)", evp_h)
+have_func("X509_STORE_get_ex_data(NULL, 0)", x509_h)
+have_func("X509_STORE_set_ex_data(NULL, 0, NULL)", x509_h)
+have_func("X509_STORE_get_ex_new_index(0, NULL, NULL, NULL, NULL)", x509_h)
+have_func("X509_CRL_get0_signature(NULL, NULL, NULL)", x509_h)
+have_func("X509_REQ_get0_signature(NULL, NULL, NULL)", x509_h)
+have_func("X509_REVOKED_get0_serialNumber(NULL)", x509_h)
+have_func("X509_REVOKED_get0_revocationDate(NULL)", x509_h)
+have_func("X509_get0_tbs_sigalg(NULL)", x509_h)
+have_func("X509_STORE_CTX_get0_untrusted(NULL)", x509_h)
+have_func("X509_STORE_CTX_get0_cert(NULL)", x509_h)
+have_func("X509_STORE_CTX_get0_chain(NULL)", x509_h)
+have_func("OCSP_SINGLERESP_get0_id(NULL)", "openssl/ocsp.h")
+have_func("SSL_CTX_get_ciphers(NULL)", ssl_h)
+have_func("X509_up_ref(NULL)", x509_h)
+have_func("X509_CRL_up_ref(NULL)", x509_h)
+have_func("X509_STORE_up_ref(NULL)", x509_h)
+have_func("SSL_SESSION_up_ref(NULL)", ssl_h)
+have_func("EVP_PKEY_up_ref(NULL)", evp_h)
+have_func("SSL_CTX_set_min_proto_version(NULL, 0)", ssl_h)
+have_func("SSL_CTX_get_security_level(NULL)", ssl_h)
+have_func("X509_get0_notBefore(NULL)", x509_h)
+have_func("SSL_SESSION_get_protocol_version(NULL)", ssl_h)
+have_func("TS_STATUS_INFO_get0_status(NULL)", ts_h)
+have_func("TS_STATUS_INFO_get0_text(NULL)", ts_h)
+have_func("TS_STATUS_INFO_get0_failure_info(NULL)", ts_h)
+have_func("TS_VERIFY_CTS_set_certs(NULL, NULL)", ts_h)
+have_func("TS_VERIFY_CTX_set_store(NULL, NULL)", ts_h)
+have_func("TS_VERIFY_CTX_add_flags(NULL, 0)", ts_h)
+have_func("TS_RESP_CTX_set_time_cb(NULL, NULL, NULL)", ts_h)
+have_func("EVP_PBE_scrypt(\"\", 0, (unsigned char *)\"\", 0, 0, 0, 0, 0, NULL, 0)", evp_h)
+have_func("SSL_CTX_set_post_handshake_auth(NULL, 0)", ssl_h)
+have_func("X509_STORE_get0_param(NULL)", x509_h)
+
+# added in 1.1.1
+have_func("EVP_PKEY_check(NULL)", evp_h)
+have_func("EVP_PKEY_new_raw_private_key(0, NULL, (unsigned char *)\"\", 0)", evp_h)
+have_func("SSL_CTX_set_ciphersuites(NULL, \"\")", ssl_h)
+
+# added in 3.0.0
+have_func("SSL_set0_tmp_dh_pkey(NULL, NULL)", ssl_h)
+have_func("ERR_get_error_all(NULL, NULL, NULL, NULL, NULL)", "openssl/err.h")
+have_func("TS_VERIFY_CTX_set_certs(NULL, NULL)", ts_h)
+have_func("SSL_CTX_load_verify_file(NULL, \"\")", ssl_h)
+have_func("BN_check_prime(NULL, NULL, NULL)", "openssl/bn.h")
+have_func("EVP_MD_CTX_get0_md(NULL)", evp_h)
+have_func("EVP_MD_CTX_get_pkey_ctx(NULL)", evp_h)
+have_func("EVP_PKEY_eq(NULL, NULL)", evp_h)
+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 -ruN ruby-2.5.9.orig/ext/openssl/History.md ruby-2.5.9/ext/openssl/History.md
--- ruby-2.5.9.orig/ext/openssl/History.md	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/History.md	1970-01-01 01:00:00.000000000 +0100
@@ -1,339 +0,0 @@
-Version 2.1.2
-=============
-
-Merged changes in 2.0.9.
-
-
-Version 2.1.1
-=============
-
-Merged changes in 2.0.8.
-
-
-Version 2.1.0
-=============
-
-Notable changes
----------------
-
-* Support for OpenSSL versions before 1.0.1 and LibreSSL versions before 2.5
-  is removed.
-  [[GitHub #86]](https://github.com/ruby/openssl/pull/86)
-* OpenSSL::BN#negative?, #+@, and #-@ are added.
-* OpenSSL::SSL::SSLSocket#connect raises a more informative exception when
-  certificate verification fails.
-  [[GitHub #99]](https://github.com/ruby/openssl/pull/99)
-* OpenSSL::KDF module is newly added. In addition to PBKDF2-HMAC that has moved
-  from OpenSSL::PKCS5, scrypt and HKDF are supported.
-  [[GitHub #109]](https://github.com/ruby/openssl/pull/109)
-  [[GitHub #173]](https://github.com/ruby/openssl/pull/173)
-* OpenSSL.fips_mode is added. We had the setter, but not the getter.
-  [[GitHub #125]](https://github.com/ruby/openssl/pull/125)
-* OpenSSL::OCSP::Request#signed? is added.
-* OpenSSL::ASN1 handles the indefinite length form better. OpenSSL::ASN1.decode
-  no longer wrongly treats the end-of-contents octets as part of the content.
-  OpenSSL::ASN1::ASN1Data#infinite_length is renamed to #indefinite_length.
-  [[GitHub #98]](https://github.com/ruby/openssl/pull/98)
-* OpenSSL::X509::Name#add_entry now accepts two additional keyword arguments
-  'loc' and 'set'.
-  [[GitHub #94]](https://github.com/ruby/openssl/issues/94)
-* OpenSSL::SSL::SSLContext#min_version= and #max_version= are added to replace
-  #ssl_version= that was built on top of the deprecated OpenSSL C API. Use of
-  that method and the constant OpenSSL::SSL::SSLContext::METHODS is now
-  deprecated.
-  [[GitHub #142]](https://github.com/ruby/openssl/pull/142)
-* OpenSSL::X509::Name#to_utf8 is added.
-  [[GitHub #26]](https://github.com/ruby/openssl/issues/26)
-  [[GitHub #143]](https://github.com/ruby/openssl/pull/143)
-* OpenSSL::X509::{Extension,Attribute,Certificate,CRL,Revoked,Request} can be
-  compared with == operator.
-  [[GitHub #161]](https://github.com/ruby/openssl/pull/161)
-* TLS Fallback Signaling Cipher Suite Value (SCSV) support is added.
-  [[GitHub #165]](https://github.com/ruby/openssl/pull/165)
-* Build failure with OpenSSL 1.1 built with no-deprecated is fixed.
-  [[GitHub #160]](https://github.com/ruby/openssl/pull/160)
-* OpenSSL::Buffering#write accepts an arbitrary number of arguments.
-  [[Feature #9323]](https://bugs.ruby-lang.org/issues/9323)
-  [[GitHub #162]](https://github.com/ruby/openssl/pull/162)
-* OpenSSL::PKey::RSA#sign_pss and #verify_pss are added. They perform RSA-PSS
-  signature and verification.
-  [[GitHub #75]](https://github.com/ruby/openssl/issues/75)
-  [[GitHub #76]](https://github.com/ruby/openssl/pull/76)
-  [[GitHub #169]](https://github.com/ruby/openssl/pull/169)
-* OpenSSL::SSL::SSLContext#add_certificate is added.
-  [[GitHub #167]](https://github.com/ruby/openssl/pull/167)
-* OpenSSL::PKey::EC::Point#to_octet_string is added.
-  OpenSSL::PKey::EC::Point.new can now take String as the second argument.
-  [[GitHub #177]](https://github.com/ruby/openssl/pull/177)
-
-
-Version 2.0.9
-=============
-
-Security fixes
---------------
-
-* OpenSSL::X509::Name#<=> could incorrectly return 0 (= equal) for non-equal
-  objects. CVE-2018-16395 is assigned for this issue.
-  https://hackerone.com/reports/387250
-
-Bug fixes
----------
-
-* Fixed OpenSSL::PKey::*.{new,generate} immediately aborting if the thread is
-  interrupted.
-  [[Bug #14882]](https://bugs.ruby-lang.org/issues/14882)
-  [[GitHub #205]](https://github.com/ruby/openssl/pull/205)
-* Fixed OpenSSL::X509::Name#to_s failing with OpenSSL::X509::NameError if
-  called against an empty instance.
-  [[GitHub #200]](https://github.com/ruby/openssl/issues/200)
-  [[GitHub #211]](https://github.com/ruby/openssl/pull/211)
-
-
-Version 2.0.8
-=============
-
-Bug fixes
----------
-
-* OpenSSL::Cipher#pkcs5_keyivgen raises an error when a negative iteration
-  count is given.
-  [[GitHub #184]](https://github.com/ruby/openssl/pull/184)
-* Fixed build with LibreSSL 2.7.
-  [[GitHub #192]](https://github.com/ruby/openssl/issues/192)
-  [[GitHub #193]](https://github.com/ruby/openssl/pull/193)
-
-
-Version 2.0.7
-=============
-
-Bug fixes
----------
-
-* OpenSSL::Cipher#auth_data= could segfault if called against a non-AEAD cipher.
-  [[Bug #14024]](https://bugs.ruby-lang.org/issues/14024)
-* OpenSSL::X509::Certificate#public_key= (and similar methods) could segfault
-  when an instance of OpenSSL::PKey::PKey with no public key components is
-  passed.
-  [[Bug #14087]](https://bugs.ruby-lang.org/issues/14087)
-  [[GitHub #168]](https://github.com/ruby/openssl/pull/168)
-
-
-Version 2.0.6
-=============
-
-Bug fixes
----------
-
-* The session_remove_cb set to an OpenSSL::SSL::SSLContext is no longer called
-  during GC.
-* A possible deadlock in OpenSSL::SSL::SSLSocket#sysread is fixed.
-  [[GitHub #139]](https://github.com/ruby/openssl/pull/139)
-* OpenSSL::BN#hash could return an unnormalized fixnum value on Windows.
-  [[Bug #13877]](https://bugs.ruby-lang.org/issues/13877)
-* OpenSSL::SSL::SSLSocket#sysread and #sysread_nonblock set the length of the
-  destination buffer String to 0 on error.
-  [[GitHub #153]](https://github.com/ruby/openssl/pull/153)
-* Possible deadlock is fixed. This happened only when built with older versions
-  of OpenSSL (before 1.1.0) or LibreSSL.
-  [[GitHub #155]](https://github.com/ruby/openssl/pull/155)
-
-
-Version 2.0.5
-=============
-
-Bug fixes
----------
-
-* Reading a PEM/DER-encoded private key or certificate from an IO object did
-  not work properly on mswin platforms.
-  [[ruby/openssl#128]](https://github.com/ruby/openssl/issues/128)
-* Broken length check in the PEM passphrase callback is fixed.
-* It failed to compile when OpenSSL is configured without TLS 1.0 support.
-
-
-Version 2.0.4
-=============
-
-Bug fixes
----------
-
-* It now compiles with LibreSSL without renaming on Windows (mswin).
-* A workaround for the error queue leak of X509_load_cert_crl_file() that
-  causes random errors is added.
-  [[Bug #11033]](https://bugs.ruby-lang.org/issues/11033)
-
-
-Version 2.0.3
-=============
-
-Bug fixes
----------
-
-* OpenSSL::ASN1::Constructive#each which was broken by 2.0.0 is fixed.
-  [[ruby/openssl#96]](https://github.com/ruby/openssl/pull/96)
-* Fixed build with static OpenSSL libraries on Windows.
-  [[Bug #13080]](https://bugs.ruby-lang.org/issues/13080)
-* OpenSSL::X509::Name#eql? which was broken by 2.0.0 is fixed.
-
-
-Version 2.0.2
-=============
-
-Bug fixes
----------
-
-* Fix build with early 0.9.8 series which did not have SSL_CTX_clear_options().
-  [ruby-core:78693]
-
-
-Version 2.0.1
-=============
-
-Bug fixes
----------
-
-* A GC issue around OpenSSL::BN is fixed.
-  [[ruby/openssl#87]](https://github.com/ruby/openssl/issues/87)
-* OpenSSL::ASN1 now parses BER encoding of GeneralizedTime without seconds.
-  [[ruby/openssl#88]](https://github.com/ruby/openssl/pull/88)
-
-
-Version 2.0.0
-=============
-
-This is the first release of openssl gem, formerly a standard library of Ruby,
-ext/openssl. This is the successor of the version included in Ruby 2.3.
-
-Compatibility notes
--------------------
-
-* Support for OpenSSL version 0.9.6 and 0.9.7 is completely removed. openssl gem
-  still works with OpenSSL 0.9.8, but users are strongly encouraged to upgrade
-  to at least 1.0.1, as OpenSSL < 1.0.1 will not receive any security fixes from
-  the OpenSSL development team.
-
-Supported platforms
--------------------
-
-* OpenSSL 1.0.0, 1.0.1, 1.0.2, 1.1.0
-* OpenSSL < 0.9.8 is no longer supported.
-* LibreSSL 2.3, 2.4, 2.5
-* Ruby 2.3, 2.4
-
-Notable changes
----------------
-
-* Add support for OpenSSL 1.1.0.
-  [[Feature #12324]](https://bugs.ruby-lang.org/issues/12324)
-* Add support for LibreSSL
-
-* OpenSSL::Cipher
-
-  - OpenSSL::Cipher#key= and #iv= reject too long inputs. They used to truncate
-    silently. [[Bug #12561]](https://bugs.ruby-lang.org/issues/12561)
-
-  - OpenSSL::Cipher#iv_len= is added. It allows changing IV (nonce) length if
-    using AEAD ciphers.
-    [[Bug #8667]](https://bugs.ruby-lang.org/issues/8667),
-    [[Bug #10420]](https://bugs.ruby-lang.org/issues/10420),
-    [[GH ruby/ruby#569]](https://github.com/ruby/ruby/pull/569),
-    [[GH ruby/openssl#58]](https://github.com/ruby/openssl/pull/58)
-
-  - OpenSSL::Cipher#auth_tag_len= is added. This sets the authentication tag
-    length to be generated by an AEAD cipher.
-
-* OpenSSL::OCSP
-
-  - Accessor methods are added to OpenSSL::OCSP::CertificateId.
-    [[Feature #7181]](https://bugs.ruby-lang.org/issues/7181)
-
-  - OpenSSL::OCSP::Request and BasicResponse can be signed with non-SHA-1 hash
-    algorithm. [[Feature #11552]](https://bugs.ruby-lang.org/issues/11552)
-
-  - OpenSSL::OCSP::CertificateId and BasicResponse can be encoded into DER.
-
-  - A new class OpenSSL::OCSP::SingleResponse is added for convenience.
-
-  - OpenSSL::OCSP::BasicResponse#add_status accepts absolute times. They used to
-    accept only relative seconds from the current time.
-
-* OpenSSL::PKey
-
-  - OpenSSL::PKey::EC follows the general PKey interface.
-    [[Bug #6567]](https://bugs.ruby-lang.org/issues/6567)
-
-  - OpenSSL::PKey.read raises OpenSSL::PKey::PKeyError instead of ArgumentError
-    for consistency with OpenSSL::PKey::{DH,DSA,RSA,EC}#new.
-    [[Bug #11774]](https://bugs.ruby-lang.org/issues/11774),
-    [[GH ruby/openssl#55]](https://github.com/ruby/openssl/pull/55)
-
-  - OpenSSL::PKey::EC::Group retrieved by OpenSSL::PKey::EC#group is no longer
-    linked with the EC key. Modifications to the EC::Group have no effect on the
-    key. [[GH ruby/openssl#71]](https://github.com/ruby/openssl/pull/71)
-
-  - OpenSSL::PKey::EC::Point#to_bn allows specifying the point conversion form
-    by the optional argument.
-
-* OpenSSL::SSL
-
-  - OpenSSL::SSL::SSLSocket#tmp_key is added. A client can call it after the
-    connection is established to retrieve the ephemeral key.
-    [[GH ruby/ruby#1318]](https://github.com/ruby/ruby/pull/1318)
-
-  - The automatic ephemeral ECDH curve selection is enabled by default when
-    built with OpenSSL >= 1.0.2 or LibreSSL.
-
-  - OpenSSL::SSL::SSLContext#security_level= is added. You can set the "security
-    level" of the SSL context. This is effective only when built with OpenSSL
-    1.1.0.
-
-  - A new option 'verify_hostname' is added to OpenSSL::SSL::SSLContext. When it
-    is enabled, and the SNI hostname is also set, the hostname verification on
-    the server certificate is automatically performed. It is now enabled by
-    OpenSSL::SSL::SSLContext#set_params.
-    [[GH ruby/openssl#60]](https://github.com/ruby/openssl/pull/60)
-
-Removals
---------
-
-* OpenSSL::Engine
-
-  - OpenSSL::Engine.cleanup does nothing when built with OpenSSL 1.1.0.
-
-* OpenSSL::SSL
-
-  - OpenSSL::PKey::DH::DEFAULT_512 is removed. Hence servers no longer use
-    512-bit DH group by default. It is considered too weak nowadays.
-    [[Bug #11968]](https://bugs.ruby-lang.org/issues/11968),
-    [[GH ruby/ruby#1196]](https://github.com/ruby/ruby/pull/1196)
-
-  - RC4 cipher suites are removed from OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.
-    RC4 is now considered to be weak.
-    [[GH ruby/openssl#50]](https://github.com/ruby/openssl/pull/50)
-
-Deprecations
-------------
-
-* OpenSSL::PKey
-
-  - OpenSSL::PKey::RSA#n=, #e=, #d=, #p=, #q=, #dmp1=, #dmq1=, #iqmp=,
-    OpenSSL::PKey::DSA#p=, #q=, #g=, #priv_key=, #pub_key=,
-    OpenSSL::PKey::DH#p=, #g=, #priv_key= and #pub_key= are deprecated. They are
-    disabled when built with OpenSSL 1.1.0, due to its API change. Instead,
-    OpenSSL::PKey::RSA#set_key, #set_factors, #set_crt_params,
-    OpenSSL::PKey::DSA#set_pqg, #set_key, OpenSSL::PKey::DH#set_pqg and #set_key
-    are added.
-
-* OpenSSL::Random
-
-  - OpenSSL::Random.pseudo_bytes is deprecated, and not defined when built with
-    OpenSSL 1.1.0. Use OpenSSL::Random.random_bytes instead.
-
-* OpenSSL::SSL
-
-  - OpenSSL::SSL::SSLContext#tmp_ecdh_callback is deprecated, as the underlying
-    API SSL_CTX_set_tmp_ecdh_callback() is removed in OpenSSL 1.1.0. It was
-    first added in Ruby 2.3.0. To specify the curve to be used in ephemeral
-    ECDH, use OpenSSL::SSL::SSLContext#ecdh_curves=. The automatic curve
-    selection is also now enabled by default when built with a capable OpenSSL.
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/bn.rb ruby-2.5.9/ext/openssl/lib/openssl/bn.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/bn.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/bn.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,40 +0,0 @@
-# frozen_string_literal: false
-#--
-#
-# = Ruby-space definitions that completes C-space funcs for BN
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
-# All rights reserved.
-#
-# = Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-module OpenSSL
-  class BN
-    include Comparable
-
-    def pretty_print(q)
-      q.object_group(self) {
-        q.text ' '
-        q.text to_i.to_s
-      }
-    end
-  end # BN
-end # OpenSSL
-
-##
-#--
-# Add double dispatch to Integer
-#++
-class Integer
-  # Casts an Integer as an OpenSSL::BN
-  #
-  # See `man bn` for more info.
-  def to_bn
-    OpenSSL::BN::new(self)
-  end
-end # Integer
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/buffering.rb ruby-2.5.9/ext/openssl/lib/openssl/buffering.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/buffering.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/buffering.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,462 +0,0 @@
-# coding: binary
-# frozen_string_literal: false
-#--
-#= Info
-#  'OpenSSL for Ruby 2' project
-#  Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
-#  All rights reserved.
-#
-#= Licence
-#  This program is licensed under the same licence as Ruby.
-#  (See the file 'LICENCE'.)
-#++
-
-##
-# OpenSSL IO buffering mix-in module.
-#
-# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO.
-#
-# You typically won't use this module directly, you can see it implemented in
-# OpenSSL::SSL::SSLSocket.
-
-module OpenSSL::Buffering
-  include Enumerable
-
-  ##
-  # The "sync mode" of the SSLSocket.
-  #
-  # See IO#sync for full details.
-
-  attr_accessor :sync
-
-  ##
-  # Default size to read from or write to the SSLSocket for buffer operations.
-
-  BLOCK_SIZE = 1024*16
-
-  ##
-  # Creates an instance of OpenSSL's buffering IO module.
-
-  def initialize(*)
-    super
-    @eof = false
-    @rbuffer = ""
-    @sync = @io.sync
-  end
-
-  #
-  # for reading.
-  #
-  private
-
-  ##
-  # Fills the buffer from the underlying SSLSocket
-
-  def fill_rbuff
-    begin
-      @rbuffer << self.sysread(BLOCK_SIZE)
-    rescue Errno::EAGAIN
-      retry
-    rescue EOFError
-      @eof = true
-    end
-  end
-
-  ##
-  # Consumes _size_ bytes from the buffer
-
-  def consume_rbuff(size=nil)
-    if @rbuffer.empty?
-      nil
-    else
-      size = @rbuffer.size unless size
-      ret = @rbuffer[0, size]
-      @rbuffer[0, size] = ""
-      ret
-    end
-  end
-
-  public
-
-  ##
-  # Reads _size_ bytes from the stream.  If _buf_ is provided it must
-  # reference a string which will receive the data.
-  #
-  # See IO#read for full details.
-
-  def read(size=nil, buf=nil)
-    if size == 0
-      if buf
-        buf.clear
-        return buf
-      else
-        return ""
-      end
-    end
-    until @eof
-      break if size && size <= @rbuffer.size
-      fill_rbuff
-    end
-    ret = consume_rbuff(size) || ""
-    if buf
-      buf.replace(ret)
-      ret = buf
-    end
-    (size && ret.empty?) ? nil : ret
-  end
-
-  ##
-  # Reads at most _maxlen_ bytes from the stream.  If _buf_ is provided it
-  # must reference a string which will receive the data.
-  #
-  # See IO#readpartial for full details.
-
-  def readpartial(maxlen, buf=nil)
-    if maxlen == 0
-      if buf
-        buf.clear
-        return buf
-      else
-        return ""
-      end
-    end
-    if @rbuffer.empty?
-      begin
-        return sysread(maxlen, buf)
-      rescue Errno::EAGAIN
-        retry
-      end
-    end
-    ret = consume_rbuff(maxlen)
-    if buf
-      buf.replace(ret)
-      ret = buf
-    end
-    ret
-  end
-
-  ##
-  # Reads at most _maxlen_ bytes in the non-blocking manner.
-  #
-  # When no data can be read without blocking it raises
-  # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
-  #
-  # IO::WaitReadable means SSL needs to read internally so read_nonblock
-  # should be called again when the underlying IO is readable.
-  #
-  # IO::WaitWritable means SSL needs to write internally so read_nonblock
-  # should be called again after the underlying IO is writable.
-  #
-  # OpenSSL::Buffering#read_nonblock needs two rescue clause as follows:
-  #
-  #   # emulates blocking read (readpartial).
-  #   begin
-  #     result = ssl.read_nonblock(maxlen)
-  #   rescue IO::WaitReadable
-  #     IO.select([io])
-  #     retry
-  #   rescue IO::WaitWritable
-  #     IO.select(nil, [io])
-  #     retry
-  #   end
-  #
-  # Note that one reason that read_nonblock writes to the underlying IO is
-  # when the peer requests a new TLS/SSL handshake.  See openssl the FAQ for
-  # more details.  http://www.openssl.org/support/faq.html
-  #
-  # By specifying a keyword argument _exception_ to +false+, you can indicate
-  # that read_nonblock should not raise an IO::Wait*able exception, but
-  # return the symbol +:wait_writable+ or +:wait_readable+ instead. At EOF,
-  # it will return +nil+ instead of raising EOFError.
-
-  def read_nonblock(maxlen, buf=nil, exception: true)
-    if maxlen == 0
-      if buf
-        buf.clear
-        return buf
-      else
-        return ""
-      end
-    end
-    if @rbuffer.empty?
-      return sysread_nonblock(maxlen, buf, exception: exception)
-    end
-    ret = consume_rbuff(maxlen)
-    if buf
-      buf.replace(ret)
-      ret = buf
-    end
-    ret
-  end
-
-  ##
-  # Reads the next "line" from the stream.  Lines are separated by _eol_.  If
-  # _limit_ is provided the result will not be longer than the given number of
-  # bytes.
-  #
-  # _eol_ may be a String or Regexp.
-  #
-  # Unlike IO#gets the line read will not be assigned to +$_+.
-  #
-  # Unlike IO#gets the separator must be provided if a limit is provided.
-
-  def gets(eol=$/, limit=nil)
-    idx = @rbuffer.index(eol)
-    until @eof
-      break if idx
-      fill_rbuff
-      idx = @rbuffer.index(eol)
-    end
-    if eol.is_a?(Regexp)
-      size = idx ? idx+$&.size : nil
-    else
-      size = idx ? idx+eol.size : nil
-    end
-    if size && limit && limit >= 0
-      size = [size, limit].min
-    end
-    consume_rbuff(size)
-  end
-
-  ##
-  # Executes the block for every line in the stream where lines are separated
-  # by _eol_.
-  #
-  # See also #gets
-
-  def each(eol=$/)
-    while line = self.gets(eol)
-      yield line
-    end
-  end
-  alias each_line each
-
-  ##
-  # Reads lines from the stream which are separated by _eol_.
-  #
-  # See also #gets
-
-  def readlines(eol=$/)
-    ary = []
-    while line = self.gets(eol)
-      ary << line
-    end
-    ary
-  end
-
-  ##
-  # Reads a line from the stream which is separated by _eol_.
-  #
-  # Raises EOFError if at end of file.
-
-  def readline(eol=$/)
-    raise EOFError if eof?
-    gets(eol)
-  end
-
-  ##
-  # Reads one character from the stream.  Returns nil if called at end of
-  # file.
-
-  def getc
-    read(1)
-  end
-
-  ##
-  # Calls the given block once for each byte in the stream.
-
-  def each_byte # :yields: byte
-    while c = getc
-      yield(c.ord)
-    end
-  end
-
-  ##
-  # Reads a one-character string from the stream.  Raises an EOFError at end
-  # of file.
-
-  def readchar
-    raise EOFError if eof?
-    getc
-  end
-
-  ##
-  # Pushes character _c_ back onto the stream such that a subsequent buffered
-  # character read will return it.
-  #
-  # Unlike IO#getc multiple bytes may be pushed back onto the stream.
-  #
-  # Has no effect on unbuffered reads (such as #sysread).
-
-  def ungetc(c)
-    @rbuffer[0,0] = c.chr
-  end
-
-  ##
-  # Returns true if the stream is at file which means there is no more data to
-  # be read.
-
-  def eof?
-    fill_rbuff if !@eof && @rbuffer.empty?
-    @eof && @rbuffer.empty?
-  end
-  alias eof eof?
-
-  #
-  # for writing.
-  #
-  private
-
-  ##
-  # Writes _s_ to the buffer.  When the buffer is full or #sync is true the
-  # buffer is flushed to the underlying socket.
-
-  def do_write(s)
-    @wbuffer = "" unless defined? @wbuffer
-    @wbuffer << s
-    @wbuffer.force_encoding(Encoding::BINARY)
-    @sync ||= false
-    if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/)
-      remain = idx ? idx + $/.size : @wbuffer.length
-      nwritten = 0
-      while remain > 0
-        str = @wbuffer[nwritten,remain]
-        begin
-          nwrote = syswrite(str)
-        rescue Errno::EAGAIN
-          retry
-        end
-        remain -= nwrote
-        nwritten += nwrote
-      end
-      @wbuffer[0,nwritten] = ""
-    end
-  end
-
-  public
-
-  ##
-  # Writes _s_ to the stream.  If the argument is not a String it will be
-  # converted using +.to_s+ method.  Returns the number of bytes written.
-
-  def write(*s)
-    s.inject(0) do |written, str|
-      do_write(str)
-      written + str.bytesize
-    end
-  end
-
-  ##
-  # Writes _s_ in the non-blocking manner.
-  #
-  # If there is buffered data, it is flushed first.  This may block.
-  #
-  # write_nonblock returns number of bytes written to the SSL connection.
-  #
-  # When no data can be written without blocking it raises
-  # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
-  #
-  # IO::WaitReadable means SSL needs to read internally so write_nonblock
-  # should be called again after the underlying IO is readable.
-  #
-  # IO::WaitWritable means SSL needs to write internally so write_nonblock
-  # should be called again after underlying IO is writable.
-  #
-  # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
-  #
-  #   # emulates blocking write.
-  #   begin
-  #     result = ssl.write_nonblock(str)
-  #   rescue IO::WaitReadable
-  #     IO.select([io])
-  #     retry
-  #   rescue IO::WaitWritable
-  #     IO.select(nil, [io])
-  #     retry
-  #   end
-  #
-  # Note that one reason that write_nonblock reads from the underlying IO
-  # is when the peer requests a new TLS/SSL handshake.  See the openssl FAQ
-  # for more details.  http://www.openssl.org/support/faq.html
-  #
-  # By specifying a keyword argument _exception_ to +false+, you can indicate
-  # that write_nonblock should not raise an IO::Wait*able exception, but
-  # return the symbol +:wait_writable+ or +:wait_readable+ instead.
-
-  def write_nonblock(s, exception: true)
-    flush
-    syswrite_nonblock(s, exception: exception)
-  end
-
-  ##
-  # Writes _s_ to the stream.  _s_ will be converted to a String using
-  # +.to_s+ method.
-
-  def <<(s)
-    do_write(s)
-    self
-  end
-
-  ##
-  # Writes _args_ to the stream along with a record separator.
-  #
-  # See IO#puts for full details.
-
-  def puts(*args)
-    s = ""
-    if args.empty?
-      s << "\n"
-    end
-    args.each{|arg|
-      s << arg.to_s
-      if $/ && /\n\z/ !~ s
-        s << "\n"
-      end
-    }
-    do_write(s)
-    nil
-  end
-
-  ##
-  # Writes _args_ to the stream.
-  #
-  # See IO#print for full details.
-
-  def print(*args)
-    s = ""
-    args.each{ |arg| s << arg.to_s }
-    do_write(s)
-    nil
-  end
-
-  ##
-  # Formats and writes to the stream converting parameters under control of
-  # the format string.
-  #
-  # See Kernel#sprintf for format string details.
-
-  def printf(s, *args)
-    do_write(s % args)
-    nil
-  end
-
-  ##
-  # Flushes buffered data to the SSLSocket.
-
-  def flush
-    osync = @sync
-    @sync = true
-    do_write ""
-    return self
-  ensure
-    @sync = osync
-  end
-
-  ##
-  # Closes the SSLSocket and flushes any unwritten data.
-
-  def close
-    flush rescue nil
-    sysclose
-  end
-end
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/cipher.rb ruby-2.5.9/ext/openssl/lib/openssl/cipher.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/cipher.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/cipher.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,67 +0,0 @@
-# frozen_string_literal: false
-#--
-# = Ruby-space predefined Cipher subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
-# All rights reserved.
-#
-# = Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-module OpenSSL
-  class Cipher
-    %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name|
-      klass = Class.new(Cipher){
-        define_method(:initialize){|*args|
-          cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" }
-          super(cipher_name.downcase)
-        }
-      }
-      const_set(name, klass)
-    }
-
-    %w(128 192 256).each{|keylen|
-      klass = Class.new(Cipher){
-        define_method(:initialize){|mode = "CBC"|
-          super("aes-#{keylen}-#{mode}".downcase)
-        }
-      }
-      const_set("AES#{keylen}", klass)
-    }
-
-    # call-seq:
-    #   cipher.random_key -> key
-    #
-    # Generate a random key with OpenSSL::Random.random_bytes and sets it to
-    # the cipher, and returns it.
-    #
-    # You must call #encrypt or #decrypt before calling this method.
-    def random_key
-      str = OpenSSL::Random.random_bytes(self.key_len)
-      self.key = str
-    end
-
-    # call-seq:
-    #   cipher.random_iv -> iv
-    #
-    # Generate a random IV with OpenSSL::Random.random_bytes and sets it to the
-    # cipher, and returns it.
-    #
-    # You must call #encrypt or #decrypt before calling this method.
-    def random_iv
-      str = OpenSSL::Random.random_bytes(self.iv_len)
-      self.iv = str
-    end
-
-    # Deprecated.
-    #
-    # This class is only provided for backwards compatibility.
-    # Use OpenSSL::Cipher.
-    class Cipher < Cipher; end
-    deprecate_constant :Cipher
-  end # Cipher
-end # OpenSSL
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/config.rb ruby-2.5.9/ext/openssl/lib/openssl/config.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/config.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/config.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,474 +0,0 @@
-# frozen_string_literal: false
-=begin
-= Ruby-space definitions that completes C-space funcs for Config
-
-= Info
-  Copyright (C) 2010  Hiroshi Nakamura <nahi@ruby-lang.org>
-
-= Licence
-  This program is licensed under the same licence as Ruby.
-  (See the file 'LICENCE'.)
-
-=end
-
-require 'stringio'
-
-module OpenSSL
-  ##
-  # = OpenSSL::Config
-  #
-  # Configuration for the openssl library.
-  #
-  # Many system's installation of openssl library will depend on your system
-  # configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for
-  # the location of the file for your host.
-  #
-  # See also http://www.openssl.org/docs/apps/config.html
-  class Config
-    include Enumerable
-
-    class << self
-
-      ##
-      # Parses a given _string_ as a blob that contains configuration for
-      # OpenSSL.
-      #
-      # If the source of the IO is a file, then consider using #parse_config.
-      def parse(string)
-        c = new()
-        parse_config(StringIO.new(string)).each do |section, hash|
-          c[section] = hash
-        end
-        c
-      end
-
-      ##
-      # load is an alias to ::new
-      alias load new
-
-      ##
-      # Parses the configuration data read from _io_, see also #parse.
-      #
-      # Raises a ConfigError on invalid configuration data.
-      def parse_config(io)
-        begin
-          parse_config_lines(io)
-        rescue ConfigError => e
-          e.message.replace("error in line #{io.lineno}: " + e.message)
-          raise
-        end
-      end
-
-      def get_key_string(data, section, key) # :nodoc:
-        if v = data[section] && data[section][key]
-          return v
-        elsif section == 'ENV'
-          if v = ENV[key]
-            return v
-          end
-        end
-        if v = data['default'] && data['default'][key]
-          return v
-        end
-      end
-
-    private
-
-      def parse_config_lines(io)
-        section = 'default'
-        data = {section => {}}
-        while definition = get_definition(io)
-          definition = clear_comments(definition)
-          next if definition.empty?
-          if definition[0] == ?[
-            if /\[([^\]]*)\]/ =~ definition
-              section = $1.strip
-              data[section] ||= {}
-            else
-              raise ConfigError, "missing close square bracket"
-            end
-          else
-            if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition
-              if $2
-                section = $1
-                key = $2
-              else
-                key = $1
-              end
-              value = unescape_value(data, section, $3)
-              (data[section] ||= {})[key] = value.strip
-            else
-              raise ConfigError, "missing equal sign"
-            end
-          end
-        end
-        data
-      end
-
-      # escape with backslash
-      QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/
-      # escape with backslash and doubled dq
-      QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/
-      # escaped char map
-      ESCAPE_MAP = {
-        "r" => "\r",
-        "n" => "\n",
-        "b" => "\b",
-        "t" => "\t",
-      }
-
-      def unescape_value(data, section, value)
-        scanned = []
-        while m = value.match(/['"\\$]/)
-          scanned << m.pre_match
-          c = m[0]
-          value = m.post_match
-          case c
-          when "'"
-            if m = value.match(QUOTE_REGEXP_SQ)
-              scanned << m[1].gsub(/\\(.)/, '\\1')
-              value = m.post_match
-            else
-              break
-            end
-          when '"'
-            if m = value.match(QUOTE_REGEXP_DQ)
-              scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1')
-              value = m.post_match
-            else
-              break
-            end
-          when "\\"
-            c = value.slice!(0, 1)
-            scanned << (ESCAPE_MAP[c] || c)
-          when "$"
-            ref, value = extract_reference(value)
-            refsec = section
-            if ref.index('::')
-              refsec, ref = ref.split('::', 2)
-            end
-            if v = get_key_string(data, refsec, ref)
-              scanned << v
-            else
-              raise ConfigError, "variable has no value"
-            end
-          else
-            raise 'must not reaced'
-          end
-        end
-        scanned << value
-        scanned.join
-      end
-
-      def extract_reference(value)
-        rest = ''
-        if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/)
-          value = m[1] || m[2]
-          rest = m.post_match
-        elsif [?(, ?{].include?(value[0])
-          raise ConfigError, "no close brace"
-        end
-        if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/)
-          return m[0], m.post_match + rest
-        else
-          raise
-        end
-      end
-
-      def clear_comments(line)
-        # FCOMMENT
-        if m = line.match(/\A([\t\n\f ]*);.*\z/)
-          return m[1]
-        end
-        # COMMENT
-        scanned = []
-        while m = line.match(/[#'"\\]/)
-          scanned << m.pre_match
-          c = m[0]
-          line = m.post_match
-          case c
-          when '#'
-            line = nil
-            break
-          when "'", '"'
-            regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ
-            scanned << c
-            if m = line.match(regexp)
-              scanned << m[0]
-              line = m.post_match
-            else
-              scanned << line
-              line = nil
-              break
-            end
-          when "\\"
-            scanned << c
-            scanned << line.slice!(0, 1)
-          else
-            raise 'must not reaced'
-          end
-        end
-        scanned << line
-        scanned.join
-      end
-
-      def get_definition(io)
-        if line = get_line(io)
-          while /[^\\]\\\z/ =~ line
-            if extra = get_line(io)
-              line += extra
-            else
-              break
-            end
-          end
-          return line.strip
-        end
-      end
-
-      def get_line(io)
-        if line = io.gets
-          line.gsub(/[\r\n]*/, '')
-        end
-      end
-    end
-
-    ##
-    # Creates an instance of OpenSSL's configuration class.
-    #
-    # This can be used in contexts like OpenSSL::X509::ExtensionFactory.config=
-    #
-    # If the optional _filename_ parameter is provided, then it is read in and
-    # parsed via #parse_config.
-    #
-    # This can raise IO exceptions based on the access, or availability of the
-    # file. A ConfigError exception may be raised depending on the validity of
-    # the data being configured.
-    #
-    def initialize(filename = nil)
-      @data = {}
-      if filename
-        File.open(filename.to_s) do |file|
-          Config.parse_config(file).each do |section, hash|
-            self[section] = hash
-          end
-        end
-      end
-    end
-
-    ##
-    # Gets the value of _key_ from the given _section_
-    #
-    # Given the following configurating file being loaded:
-    #
-    #   config = OpenSSL::Config.load('foo.cnf')
-    #     #=> #<OpenSSL::Config sections=["default"]>
-    #   puts config.to_s
-    #     #=> [ default ]
-    #     #   foo=bar
-    #
-    # You can get a specific value from the config if you know the _section_
-    # and _key_ like so:
-    #
-    #   config.get_value('default','foo')
-    #     #=> "bar"
-    #
-    def get_value(section, key)
-      if section.nil?
-        raise TypeError.new('nil not allowed')
-      end
-      section = 'default' if section.empty?
-      get_key_string(section, key)
-    end
-
-    ##
-    #
-    # *Deprecated*
-    #
-    # Use #get_value instead
-    def value(arg1, arg2 = nil) # :nodoc:
-      warn('Config#value is deprecated; use Config#get_value')
-      if arg2.nil?
-        section, key = 'default', arg1
-      else
-        section, key = arg1, arg2
-      end
-      section ||= 'default'
-      section = 'default' if section.empty?
-      get_key_string(section, key)
-    end
-
-    ##
-    # Set the target _key_ with a given _value_ under a specific _section_.
-    #
-    # Given the following configurating file being loaded:
-    #
-    #   config = OpenSSL::Config.load('foo.cnf')
-    #     #=> #<OpenSSL::Config sections=["default"]>
-    #   puts config.to_s
-    #     #=> [ default ]
-    #     #   foo=bar
-    #
-    # You can set the value of _foo_ under the _default_ section to a new
-    # value:
-    #
-    #   config.add_value('default', 'foo', 'buzz')
-    #     #=> "buzz"
-    #   puts config.to_s
-    #     #=> [ default ]
-    #     #   foo=buzz
-    #
-    def add_value(section, key, value)
-      check_modify
-      (@data[section] ||= {})[key] = value
-    end
-
-    ##
-    # Get a specific _section_ from the current configuration
-    #
-    # Given the following configurating file being loaded:
-    #
-    #   config = OpenSSL::Config.load('foo.cnf')
-    #     #=> #<OpenSSL::Config sections=["default"]>
-    #   puts config.to_s
-    #     #=> [ default ]
-    #     #   foo=bar
-    #
-    # You can get a hash of the specific section like so:
-    #
-    #   config['default']
-    #     #=> {"foo"=>"bar"}
-    #
-    def [](section)
-      @data[section] || {}
-    end
-
-    ##
-    # Deprecated
-    #
-    # Use #[] instead
-    def section(name) # :nodoc:
-      warn('Config#section is deprecated; use Config#[]')
-      @data[name] || {}
-    end
-
-    ##
-    # Sets a specific _section_ name with a Hash _pairs_.
-    #
-    # Given the following configuration being created:
-    #
-    #   config = OpenSSL::Config.new
-    #     #=> #<OpenSSL::Config sections=[]>
-    #   config['default'] = {"foo"=>"bar","baz"=>"buz"}
-    #     #=> {"foo"=>"bar", "baz"=>"buz"}
-    #   puts config.to_s
-    #     #=> [ default ]
-    #     #   foo=bar
-    #     #   baz=buz
-    #
-    # It's important to note that this will essentially merge any of the keys
-    # in _pairs_ with the existing _section_. For example:
-    #
-    #   config['default']
-    #     #=> {"foo"=>"bar", "baz"=>"buz"}
-    #   config['default'] = {"foo" => "changed"}
-    #     #=> {"foo"=>"changed"}
-    #   config['default']
-    #     #=> {"foo"=>"changed", "baz"=>"buz"}
-    #
-    def []=(section, pairs)
-      check_modify
-      @data[section] ||= {}
-      pairs.each do |key, value|
-        self.add_value(section, key, value)
-      end
-    end
-
-    ##
-    # Get the names of all sections in the current configuration
-    def sections
-      @data.keys
-    end
-
-    ##
-    # Get the parsable form of the current configuration
-    #
-    # Given the following configuration being created:
-    #
-    #   config = OpenSSL::Config.new
-    #     #=> #<OpenSSL::Config sections=[]>
-    #   config['default'] = {"foo"=>"bar","baz"=>"buz"}
-    #     #=> {"foo"=>"bar", "baz"=>"buz"}
-    #   puts config.to_s
-    #     #=> [ default ]
-    #     #   foo=bar
-    #     #   baz=buz
-    #
-    # You can parse get the serialized configuration using #to_s and then parse
-    # it later:
-    #
-    #   serialized_config = config.to_s
-    #   # much later...
-    #   new_config = OpenSSL::Config.parse(serialized_config)
-    #     #=> #<OpenSSL::Config sections=["default"]>
-    #   puts new_config
-    #     #=> [ default ]
-    #         foo=bar
-    #         baz=buz
-    #
-    def to_s
-      ary = []
-      @data.keys.sort.each do |section|
-        ary << "[ #{section} ]\n"
-        @data[section].keys.each do |key|
-          ary << "#{key}=#{@data[section][key]}\n"
-        end
-        ary << "\n"
-      end
-      ary.join
-    end
-
-    ##
-    # For a block.
-    #
-    # Receive the section and its pairs for the current configuration.
-    #
-    #   config.each do |section, key, value|
-    #     # ...
-    #   end
-    #
-    def each
-      @data.each do |section, hash|
-        hash.each do |key, value|
-          yield [section, key, value]
-        end
-      end
-    end
-
-    ##
-    # String representation of this configuration object, including the class
-    # name and its sections.
-    def inspect
-      "#<#{self.class.name} sections=#{sections.inspect}>"
-    end
-
-  protected
-
-    def data # :nodoc:
-      @data
-    end
-
-  private
-
-    def initialize_copy(other)
-      @data = other.data.dup
-    end
-
-    def check_modify
-      raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen?
-    end
-
-    def get_key_string(section, key)
-      Config.get_key_string(@data, section, key)
-    end
-  end
-end
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/digest.rb ruby-2.5.9/ext/openssl/lib/openssl/digest.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/digest.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/digest.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,75 +0,0 @@
-# frozen_string_literal: false
-#--
-# = Ruby-space predefined Digest subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
-# All rights reserved.
-#
-# = Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-module OpenSSL
-  class Digest
-
-    alg = %w(MD2 MD4 MD5 MDC2 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
-    if OPENSSL_VERSION_NUMBER < 0x10100000
-      alg += %w(DSS DSS1 SHA)
-    end
-
-    # Return the hash value computed with _name_ Digest. _name_ is either the
-    # long name or short name of a supported digest algorithm.
-    #
-    # === Examples
-    #
-    #   OpenSSL::Digest.digest("SHA256", "abc")
-    #
-    # which is equivalent to:
-    #
-    #   OpenSSL::Digest::SHA256.digest("abc")
-
-    def self.digest(name, data)
-      super(data, name)
-    end
-
-    alg.each{|name|
-      klass = Class.new(self) {
-        define_method(:initialize, ->(data = nil) {super(name, data)})
-      }
-      singleton = (class << klass; self; end)
-      singleton.class_eval{
-        define_method(:digest){|data| new.digest(data) }
-        define_method(:hexdigest){|data| new.hexdigest(data) }
-      }
-      const_set(name, klass)
-    }
-
-    # Deprecated.
-    #
-    # This class is only provided for backwards compatibility.
-    # Use OpenSSL::Digest instead.
-    class Digest < Digest; end # :nodoc:
-    deprecate_constant :Digest
-
-  end # Digest
-
-  # Returns a Digest subclass by _name_
-  #
-  #   require 'openssl'
-  #
-  #   OpenSSL::Digest("MD5")
-  #   # => OpenSSL::Digest::MD5
-  #
-  #   Digest("Foo")
-  #   # => NameError: wrong constant name Foo
-
-  def Digest(name)
-    OpenSSL::Digest.const_get(name)
-  end
-
-  module_function :Digest
-
-end # OpenSSL
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/pkcs5.rb ruby-2.5.9/ext/openssl/lib/openssl/pkcs5.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/pkcs5.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/pkcs5.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,22 +0,0 @@
-# frozen_string_literal: false
-#--
-# Ruby/OpenSSL Project
-# Copyright (C) 2017 Ruby/OpenSSL Project Authors
-#++
-
-module OpenSSL
-  module PKCS5
-    module_function
-
-    # OpenSSL::PKCS5.pbkdf2_hmac has been renamed to OpenSSL::KDF.pbkdf2_hmac.
-    # This method is provided for backwards compatibility.
-    def pbkdf2_hmac(pass, salt, iter, keylen, digest)
-      OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
-                               length: keylen, hash: digest)
-    end
-
-    def pbkdf2_hmac_sha1(pass, salt, iter, keylen)
-      pbkdf2_hmac(pass, salt, iter, keylen, "sha1")
-    end
-  end
-end
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/pkey.rb ruby-2.5.9/ext/openssl/lib/openssl/pkey.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/pkey.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/pkey.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,25 +0,0 @@
-# frozen_string_literal: false
-#--
-# Ruby/OpenSSL Project
-# Copyright (C) 2017 Ruby/OpenSSL Project Authors
-#++
-
-module OpenSSL::PKey
-  if defined?(EC)
-  class EC::Point
-    # :call-seq:
-    #    point.to_bn([conversion_form]) -> OpenSSL::BN
-    #
-    # Returns the octet string representation of the EC point as an instance of
-    # OpenSSL::BN.
-    #
-    # If _conversion_form_ is not given, the _point_conversion_form_ attribute
-    # set to the group is used.
-    #
-    # See #to_octet_string for more information.
-    def to_bn(conversion_form = group.point_conversion_form)
-      OpenSSL::BN.new(to_octet_string(conversion_form), 2)
-    end
-  end
-  end
-end
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/ssl.rb ruby-2.5.9/ext/openssl/lib/openssl/ssl.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/ssl.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/ssl.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,503 +0,0 @@
-# frozen_string_literal: false
-=begin
-= Info
-  'OpenSSL for Ruby 2' project
-  Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
-  All rights reserved.
-
-= Licence
-  This program is licensed under the same licence as Ruby.
-  (See the file 'LICENCE'.)
-=end
-
-require "openssl/buffering"
-require "io/nonblock"
-
-module OpenSSL
-  module SSL
-    class SSLContext
-      DEFAULT_PARAMS = { # :nodoc:
-        :min_version => OpenSSL::SSL::TLS1_VERSION,
-        :verify_mode => OpenSSL::SSL::VERIFY_PEER,
-        :verify_hostname => true,
-        :options => -> {
-          opts = OpenSSL::SSL::OP_ALL
-          opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
-          opts |= OpenSSL::SSL::OP_NO_COMPRESSION
-          opts
-        }.call
-      }
-
-      if defined?(OpenSSL::PKey::DH)
-        DEFAULT_2048 = 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==
------END DH PARAMETERS-----
-        _end_of_pem_
-        private_constant :DEFAULT_2048
-
-        DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
-          warn "using default DH parameters." if $VERBOSE
-          DEFAULT_2048
-        }
-      end
-
-      if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") &&
-           OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000)
-        DEFAULT_PARAMS.merge!(
-          ciphers: %w{
-            ECDHE-ECDSA-AES128-GCM-SHA256
-            ECDHE-RSA-AES128-GCM-SHA256
-            ECDHE-ECDSA-AES256-GCM-SHA384
-            ECDHE-RSA-AES256-GCM-SHA384
-            DHE-RSA-AES128-GCM-SHA256
-            DHE-DSS-AES128-GCM-SHA256
-            DHE-RSA-AES256-GCM-SHA384
-            DHE-DSS-AES256-GCM-SHA384
-            ECDHE-ECDSA-AES128-SHA256
-            ECDHE-RSA-AES128-SHA256
-            ECDHE-ECDSA-AES128-SHA
-            ECDHE-RSA-AES128-SHA
-            ECDHE-ECDSA-AES256-SHA384
-            ECDHE-RSA-AES256-SHA384
-            ECDHE-ECDSA-AES256-SHA
-            ECDHE-RSA-AES256-SHA
-            DHE-RSA-AES128-SHA256
-            DHE-RSA-AES256-SHA256
-            DHE-RSA-AES128-SHA
-            DHE-RSA-AES256-SHA
-            DHE-DSS-AES128-SHA256
-            DHE-DSS-AES256-SHA256
-            DHE-DSS-AES128-SHA
-            DHE-DSS-AES256-SHA
-            AES128-GCM-SHA256
-            AES256-GCM-SHA384
-            AES128-SHA256
-            AES256-SHA256
-            AES128-SHA
-            AES256-SHA
-          }.join(":"),
-        )
-      end
-
-      DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
-      DEFAULT_CERT_STORE.set_default_paths
-      DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
-
-      # A callback invoked when DH parameters are required.
-      #
-      # The callback is invoked with the Session for the key exchange, an
-      # flag indicating the use of an export cipher and the keylength
-      # required.
-      #
-      # The callback must return an OpenSSL::PKey::DH instance of the correct
-      # key length.
-
-      attr_accessor :tmp_dh_callback
-
-      # A callback invoked at connect time to distinguish between multiple
-      # server names.
-      #
-      # The callback is invoked with an SSLSocket and a server name.  The
-      # callback must return an SSLContext for the server name or nil.
-      attr_accessor :servername_cb
-
-      # call-seq:
-      #    SSLContext.new           -> ctx
-      #    SSLContext.new(:TLSv1)   -> ctx
-      #    SSLContext.new("SSLv23") -> ctx
-      #
-      # Creates a new SSL context.
-      #
-      # If an argument is given, #ssl_version= is called with the value. Note
-      # that this form is deprecated. New applications should use #min_version=
-      # and #max_version= as necessary.
-      def initialize(version = nil)
-        self.options |= OpenSSL::SSL::OP_ALL
-        self.ssl_version = version if version
-      end
-
-      ##
-      # call-seq:
-      #   ctx.set_params(params = {}) -> params
-      #
-      # Sets saner defaults optimized for the use with HTTP-like protocols.
-      #
-      # If a Hash _params_ is given, the parameters are overridden with it.
-      # The keys in _params_ must be assignment methods on SSLContext.
-      #
-      # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
-      # cert_store are not set then the system default certificate store is
-      # used.
-      def set_params(params={})
-        params = DEFAULT_PARAMS.merge(params)
-        self.options = params.delete(:options) # set before min_version/max_version
-        params.each{|name, value| self.__send__("#{name}=", value) }
-        if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
-          unless self.ca_file or self.ca_path or self.cert_store
-            self.cert_store = DEFAULT_CERT_STORE
-          end
-        end
-        return params
-      end
-
-      # call-seq:
-      #    ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
-      #    ctx.min_version = :TLS1_2
-      #    ctx.min_version = nil
-      #
-      # Sets the lower bound on the supported SSL/TLS protocol version. The
-      # version may be specified by an integer constant named
-      # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
-      #
-      # Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v*
-      # options by #options= once you have called #min_version= or
-      # #max_version=.
-      #
-      # === Example
-      #   ctx = OpenSSL::SSL::SSLContext.new
-      #   ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
-      #   ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
-      #
-      #   sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
-      #   sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
-      def min_version=(version)
-        set_minmax_proto_version(version, @max_proto_version ||= nil)
-        @min_proto_version = version
-      end
-
-      # call-seq:
-      #    ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
-      #    ctx.max_version = :TLS1_2
-      #    ctx.max_version = nil
-      #
-      # Sets the upper bound of the supported SSL/TLS protocol version. See
-      # #min_version= for the possible values.
-      def max_version=(version)
-        set_minmax_proto_version(@min_proto_version ||= nil, version)
-        @max_proto_version = version
-      end
-
-      # call-seq:
-      #    ctx.ssl_version = :TLSv1
-      #    ctx.ssl_version = "SSLv23"
-      #
-      # Sets the SSL/TLS protocol version for the context. This forces
-      # connections to use only the specified protocol version. This is
-      # deprecated and only provided for backwards compatibility. Use
-      # #min_version= and #max_version= instead.
-      #
-      # === History
-      # As the name hints, this used to call the SSL_CTX_set_ssl_version()
-      # function which sets the SSL method used for connections created from
-      # the context. As of Ruby/OpenSSL 2.1, this accessor method is
-      # implemented to call #min_version= and #max_version= instead.
-      def ssl_version=(meth)
-        meth = meth.to_s if meth.is_a?(Symbol)
-        if /(?<type>_client|_server)\z/ =~ meth
-          meth = $`
-          if $VERBOSE
-            warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored"
-          end
-        end
-        version = METHODS_MAP[meth.intern] or
-          raise ArgumentError, "unknown SSL method `%s'" % meth
-        set_minmax_proto_version(version, version)
-        @min_proto_version = @max_proto_version = version
-      end
-
-      METHODS_MAP = {
-        SSLv23: 0,
-        SSLv2: OpenSSL::SSL::SSL2_VERSION,
-        SSLv3: OpenSSL::SSL::SSL3_VERSION,
-        TLSv1: OpenSSL::SSL::TLS1_VERSION,
-        TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
-        TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
-      }.freeze
-      private_constant :METHODS_MAP
-
-      # The list of available SSL/TLS methods. This constant is only provided
-      # for backwards compatibility.
-      METHODS = METHODS_MAP.flat_map { |name,|
-        [name, :"#{name}_client", :"#{name}_server"]
-      }.freeze
-      deprecate_constant :METHODS
-    end
-
-    module SocketForwarder
-      def addr
-        to_io.addr
-      end
-
-      def peeraddr
-        to_io.peeraddr
-      end
-
-      def setsockopt(level, optname, optval)
-        to_io.setsockopt(level, optname, optval)
-      end
-
-      def getsockopt(level, optname)
-        to_io.getsockopt(level, optname)
-      end
-
-      def fcntl(*args)
-        to_io.fcntl(*args)
-      end
-
-      def closed?
-        to_io.closed?
-      end
-
-      def do_not_reverse_lookup=(flag)
-        to_io.do_not_reverse_lookup = flag
-      end
-    end
-
-    def verify_certificate_identity(cert, hostname)
-      should_verify_common_name = true
-      cert.extensions.each{|ext|
-        next if ext.oid != "subjectAltName"
-        ostr = OpenSSL::ASN1.decode(ext.to_der).value.last
-        sequence = OpenSSL::ASN1.decode(ostr.value)
-        sequence.value.each{|san|
-          case san.tag
-          when 2 # dNSName in GeneralName (RFC5280)
-            should_verify_common_name = false
-            return true if verify_hostname(hostname, san.value)
-          when 7 # iPAddress in GeneralName (RFC5280)
-            should_verify_common_name = false
-            # follows GENERAL_NAME_print() in x509v3/v3_alt.c
-            if san.value.size == 4
-              return true if san.value.unpack('C*').join('.') == hostname
-            elsif san.value.size == 16
-              return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname
-            end
-          end
-        }
-      }
-      if should_verify_common_name
-        cert.subject.to_a.each{|oid, value|
-          if oid == "CN"
-            return true if verify_hostname(hostname, value)
-          end
-        }
-      end
-      return false
-    end
-    module_function :verify_certificate_identity
-
-    def verify_hostname(hostname, san) # :nodoc:
-      # RFC 5280, IA5String is limited to the set of ASCII characters
-      return false unless san.ascii_only?
-      return false unless hostname.ascii_only?
-
-      # See RFC 6125, section 6.4.1
-      # Matching is case-insensitive.
-      san_parts = san.downcase.split(".")
-
-      # TODO: this behavior should probably be more strict
-      return san == hostname if san_parts.size < 2
-
-      # Matching is case-insensitive.
-      host_parts = hostname.downcase.split(".")
-
-      # RFC 6125, section 6.4.3, subitem 2.
-      # If the wildcard character is the only character of the left-most
-      # label in the presented identifier, the client SHOULD NOT compare
-      # against anything but the left-most label of the reference
-      # identifier (e.g., *.example.com would match foo.example.com but
-      # not bar.foo.example.com or example.com).
-      return false unless san_parts.size == host_parts.size
-
-      # RFC 6125, section 6.4.3, subitem 1.
-      # The client SHOULD NOT attempt to match a presented identifier in
-      # which the wildcard character comprises a label other than the
-      # left-most label (e.g., do not match bar.*.example.net).
-      return false unless verify_wildcard(host_parts.shift, san_parts.shift)
-
-      san_parts.join(".") == host_parts.join(".")
-    end
-    module_function :verify_hostname
-
-    def verify_wildcard(domain_component, san_component) # :nodoc:
-      parts = san_component.split("*", -1)
-
-      return false if parts.size > 2
-      return san_component == domain_component if parts.size == 1
-
-      # RFC 6125, section 6.4.3, subitem 3.
-      # The client SHOULD NOT attempt to match a presented identifier
-      # where the wildcard character is embedded within an A-label or
-      # U-label of an internationalized domain name.
-      return false if domain_component.start_with?("xn--") && san_component != "*"
-
-      parts[0].length + parts[1].length < domain_component.length &&
-      domain_component.start_with?(parts[0]) &&
-      domain_component.end_with?(parts[1])
-    end
-    module_function :verify_wildcard
-
-    class SSLSocket
-      include Buffering
-      include SocketForwarder
-
-      attr_reader :hostname
-
-      # The underlying IO object.
-      attr_reader :io
-      alias :to_io :io
-
-      # The SSLContext object used in this connection.
-      attr_reader :context
-
-      # Whether to close the underlying socket as well, when the SSL/TLS
-      # connection is shut down. This defaults to +false+.
-      attr_accessor :sync_close
-
-      # call-seq:
-      #    ssl.sysclose => nil
-      #
-      # Sends "close notify" to the peer and tries to shut down the SSL
-      # connection gracefully.
-      #
-      # If sync_close is set to +true+, the underlying IO is also closed.
-      def sysclose
-        return if closed?
-        stop
-        io.close if sync_close
-      end
-
-      # call-seq:
-      #   ssl.post_connection_check(hostname) -> true
-      #
-      # Perform hostname verification following RFC 6125.
-      #
-      # This method MUST be called after calling #connect to ensure that the
-      # hostname of a remote peer has been verified.
-      def post_connection_check(hostname)
-        if peer_cert.nil?
-          msg = "Peer verification enabled, but no certificate received."
-          if using_anon_cipher?
-            msg += " Anonymous cipher suite #{cipher[0]} was negotiated. " \
-                   "Anonymous suites must be disabled to use peer verification."
-          end
-          raise SSLError, msg
-        end
-
-        unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
-          raise SSLError, "hostname \"#{hostname}\" does not match the server certificate"
-        end
-        return true
-      end
-
-      # call-seq:
-      #   ssl.session -> aSession
-      #
-      # Returns the SSLSession object currently used, or nil if the session is
-      # not established.
-      def session
-        SSL::Session.new(self)
-      rescue SSL::Session::SessionError
-        nil
-      end
-
-      private
-
-      def using_anon_cipher?
-        ctx = OpenSSL::SSL::SSLContext.new
-        ctx.ciphers = "aNULL"
-        ctx.ciphers.include?(cipher)
-      end
-
-      def client_cert_cb
-        @context.client_cert_cb
-      end
-
-      def tmp_dh_callback
-        @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
-      end
-
-      def tmp_ecdh_callback
-        @context.tmp_ecdh_callback
-      end
-
-      def session_new_cb
-        @context.session_new_cb
-      end
-
-      def session_get_cb
-        @context.session_get_cb
-      end
-    end
-
-    ##
-    # SSLServer represents a TCP/IP server socket with Secure Sockets Layer.
-    class SSLServer
-      include SocketForwarder
-      # When true then #accept works exactly the same as TCPServer#accept
-      attr_accessor :start_immediately
-
-      # Creates a new instance of SSLServer.
-      # * _srv_ is an instance of TCPServer.
-      # * _ctx_ is an instance of OpenSSL::SSL::SSLContext.
-      def initialize(svr, ctx)
-        @svr = svr
-        @ctx = ctx
-        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]
-          @ctx.session_id_context = session_id
-        end
-        @start_immediately = true
-      end
-
-      # Returns the TCPServer passed to the SSLServer when initialized.
-      def to_io
-        @svr
-      end
-
-      # See TCPServer#listen for details.
-      def listen(backlog=5)
-        @svr.listen(backlog)
-      end
-
-      # See BasicSocket#shutdown for details.
-      def shutdown(how=Socket::SHUT_RDWR)
-        @svr.shutdown(how)
-      end
-
-      # Works similar to TCPServer#accept.
-      def accept
-        # Socket#accept returns [socket, addrinfo].
-        # TCPServer#accept returns a socket.
-        # The following comma strips addrinfo.
-        sock, = @svr.accept
-        begin
-          ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
-          ssl.sync_close = true
-          ssl.accept if @start_immediately
-          ssl
-        rescue Exception => ex
-          if ssl
-            ssl.close
-          else
-            sock.close
-          end
-          raise ex
-        end
-      end
-
-      # See IO#close for details.
-      def close
-        @svr.close
-      end
-    end
-  end
-end
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl/x509.rb ruby-2.5.9/ext/openssl/lib/openssl/x509.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl/x509.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl/x509.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,215 +0,0 @@
-# frozen_string_literal: false
-#--
-# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
-#
-# = Info
-# 'OpenSSL for Ruby 2' project
-# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
-# All rights reserved.
-#
-# = Licence
-# This program is licensed under the same licence as Ruby.
-# (See the file 'LICENCE'.)
-#++
-
-module OpenSSL
-  module X509
-    class ExtensionFactory
-      def create_extension(*arg)
-        if arg.size > 1
-          create_ext(*arg)
-        else
-          send("create_ext_from_"+arg[0].class.name.downcase, arg[0])
-        end
-      end
-
-      def create_ext_from_array(ary)
-        raise ExtensionError, "unexpected array form" if ary.size > 3
-        create_ext(ary[0], ary[1], ary[2])
-      end
-
-      def create_ext_from_string(str) # "oid = critical, value"
-        oid, value = str.split(/=/, 2)
-        oid.strip!
-        value.strip!
-        create_ext(oid, value)
-      end
-
-      def create_ext_from_hash(hash)
-        create_ext(hash["oid"], hash["value"], hash["critical"])
-      end
-    end
-
-    class Extension
-      def ==(other)
-        return false unless Extension === other
-        to_der == other.to_der
-      end
-
-      def to_s # "oid = critical, value"
-        str = self.oid
-        str << " = "
-        str << "critical, " if self.critical?
-        str << self.value.gsub(/\n/, ", ")
-      end
-
-      def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
-        {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?}
-      end
-
-      def to_a
-        [ self.oid, self.value, self.critical? ]
-      end
-    end
-
-    class Name
-      module RFC2253DN
-        Special = ',=+<>#;'
-        HexChar = /[0-9a-fA-F]/
-        HexPair = /#{HexChar}#{HexChar}/
-        HexString = /#{HexPair}+/
-        Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
-        StringChar = /[^\\"#{Special}]/
-        QuoteChar = /[^\\"]/
-        AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
-        AttributeValue = /
-          (?!["#])((?:#{StringChar}|#{Pair})*)|
-          \#(#{HexString})|
-          "((?:#{QuoteChar}|#{Pair})*)"
-        /x
-        TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
-
-        module_function
-
-        def expand_pair(str)
-          return nil unless str
-          return str.gsub(Pair){
-            pair = $&
-            case pair.size
-            when 2 then pair[1,1]
-            when 3 then Integer("0x#{pair[1,2]}").chr
-            else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
-            end
-          }
-        end
-
-        def expand_hexstring(str)
-          return nil unless str
-          der = str.gsub(HexPair){$&.to_i(16).chr }
-          a1 = OpenSSL::ASN1.decode(der)
-          return a1.value, a1.tag
-        end
-
-        def expand_value(str1, str2, str3)
-          value = expand_pair(str1)
-          value, tag = expand_hexstring(str2) unless value
-          value = expand_pair(str3) unless value
-          return value, tag
-        end
-
-        def scan(dn)
-          str = dn
-          ary = []
-          while true
-            if md = TypeAndValue.match(str)
-              remain = md.post_match
-              type = md[1]
-              value, tag = expand_value(md[2], md[3], md[4]) rescue nil
-              if value
-                type_and_value = [type, value]
-                type_and_value.push(tag) if tag
-                ary.unshift(type_and_value)
-                if remain.length > 2 && remain[0] == ?,
-                  str = remain[1..-1]
-                  next
-                elsif remain.length > 2 && remain[0] == ?+
-                  raise OpenSSL::X509::NameError,
-                    "multi-valued RDN is not supported: #{dn}"
-                elsif remain.empty?
-                  break
-                end
-              end
-            end
-            msg_dn = dn[0, dn.length - str.length] + " =>" + str
-            raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
-          end
-          return ary
-        end
-      end
-
-      class << self
-        def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
-          ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
-          self.new(ary, template)
-        end
-
-        def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
-          if str.start_with?("/")
-            # /A=B/C=D format
-            ary = str[1..-1].split("/").map { |i| i.split("=", 2) }
-          else
-            # Comma-separated
-            ary = str.split(",").map { |i| i.strip.split("=", 2) }
-          end
-          self.new(ary, template)
-        end
-
-        alias parse parse_openssl
-      end
-
-      def pretty_print(q)
-        q.object_group(self) {
-          q.text ' '
-          q.text to_s(OpenSSL::X509::Name::RFC2253)
-        }
-      end
-    end
-
-    class Attribute
-      def ==(other)
-        return false unless Attribute === other
-        to_der == other.to_der
-      end
-    end
-
-    class StoreContext
-      def cleanup
-        warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
-      end
-    end
-
-    class Certificate
-      def pretty_print(q)
-        q.object_group(self) {
-          q.breakable
-          q.text 'subject='; q.pp self.subject; q.text ','; q.breakable
-          q.text 'issuer='; q.pp self.issuer; q.text ','; q.breakable
-          q.text 'serial='; q.pp self.serial; q.text ','; q.breakable
-          q.text 'not_before='; q.pp self.not_before; q.text ','; q.breakable
-          q.text 'not_after='; q.pp self.not_after
-        }
-      end
-    end
-
-    class CRL
-      def ==(other)
-        return false unless CRL === other
-        to_der == other.to_der
-      end
-    end
-
-    class Revoked
-      def ==(other)
-        return false unless Revoked === other
-        to_der == other.to_der
-      end
-    end
-
-    class Request
-      def ==(other)
-        return false unless Request === other
-        to_der == other.to_der
-      end
-    end
-  end
-end
diff -ruN ruby-2.5.9.orig/ext/openssl/lib/openssl.rb ruby-2.5.9/ext/openssl/lib/openssl.rb
--- ruby-2.5.9.orig/ext/openssl/lib/openssl.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/lib/openssl.rb	1970-01-01 01:00:00.000000000 +0100
@@ -1,22 +0,0 @@
-# frozen_string_literal: false
-=begin
-= Info
-  'OpenSSL for Ruby 2' project
-  Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
-  All rights reserved.
-
-= Licence
-  This program is licensed under the same licence as Ruby.
-  (See the file 'LICENCE'.)
-=end
-
-require 'openssl.so'
-
-require 'openssl/bn'
-require 'openssl/pkey'
-require 'openssl/cipher'
-require 'openssl/config'
-require 'openssl/digest'
-require 'openssl/x509'
-require 'openssl/ssl'
-require 'openssl/pkcs5'
diff -ruN ruby-2.5.9.orig/ext/openssl/openssl.gemspec ruby-2.5.9/ext/openssl/openssl.gemspec
--- ruby-2.5.9.orig/ext/openssl/openssl.gemspec	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/openssl.gemspec	1970-01-01 01:00:00.000000000 +0100
@@ -1,46 +0,0 @@
-# -*- encoding: utf-8 -*-
-# stub: openssl 2.1.2 ruby lib
-# stub: ext/openssl/extconf.rb
-
-Gem::Specification.new do |s|
-  s.name = "openssl".freeze
-  s.version = "2.1.2"
-
-  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
-  s.metadata = { "msys2_mingw_dependencies" => "openssl" } if s.respond_to? :metadata=
-  s.require_paths = ["lib".freeze]
-  s.authors = ["Martin Bosslet".freeze, "SHIBATA Hiroshi".freeze, "Zachary Scott".freeze, "Kazuki Yamaguchi".freeze]
-  s.date = "2018-10-17"
-  s.description = "It wraps the OpenSSL library.".freeze
-  s.email = ["ruby-core@ruby-lang.org".freeze]
-  s.extensions = ["ext/openssl/extconf.rb".freeze]
-  s.extra_rdoc_files = ["CONTRIBUTING.md".freeze, "README.md".freeze, "History.md".freeze]
-  s.files = ["BSDL".freeze, "CONTRIBUTING.md".freeze, "History.md".freeze, "LICENSE.txt".freeze, "README.md".freeze, "ext/openssl/deprecation.rb".freeze, "ext/openssl/extconf.rb".freeze, "ext/openssl/openssl_missing.c".freeze, "ext/openssl/openssl_missing.h".freeze, "ext/openssl/ossl.c".freeze, "ext/openssl/ossl.h".freeze, "ext/openssl/ossl_asn1.c".freeze, "ext/openssl/ossl_asn1.h".freeze, "ext/openssl/ossl_bio.c".freeze, "ext/openssl/ossl_bio.h".freeze, "ext/openssl/ossl_bn.c".freeze, "ext/openssl/ossl_bn.h".freeze, "ext/openssl/ossl_cipher.c".freeze, "ext/openssl/ossl_cipher.h".freeze, "ext/openssl/ossl_config.c".freeze, "ext/openssl/ossl_config.h".freeze, "ext/openssl/ossl_digest.c".freeze, "ext/openssl/ossl_digest.h".freeze, "ext/openssl/ossl_engine.c".freeze, "ext/openssl/ossl_engine.h".freeze, "ext/openssl/ossl_hmac.c".freeze, "ext/openssl/ossl_hmac.h".freeze, "ext/openssl/ossl_kdf.c".freeze, "ext/openssl/ossl_kdf.h".freeze, "ext/openssl/ossl_ns_spki.c".freeze, "ext/openssl/ossl_ns_spki.h".freeze, "ext/openssl/ossl_ocsp.c".freeze, "ext/openssl/ossl_ocsp.h".freeze, "ext/openssl/ossl_pkcs12.c".freeze, "ext/openssl/ossl_pkcs12.h".freeze, "ext/openssl/ossl_pkcs7.c".freeze, "ext/openssl/ossl_pkcs7.h".freeze, "ext/openssl/ossl_pkey.c".freeze, "ext/openssl/ossl_pkey.h".freeze, "ext/openssl/ossl_pkey_dh.c".freeze, "ext/openssl/ossl_pkey_dsa.c".freeze, "ext/openssl/ossl_pkey_ec.c".freeze, "ext/openssl/ossl_pkey_rsa.c".freeze, "ext/openssl/ossl_rand.c".freeze, "ext/openssl/ossl_rand.h".freeze, "ext/openssl/ossl_ssl.c".freeze, "ext/openssl/ossl_ssl.h".freeze, "ext/openssl/ossl_ssl_session.c".freeze, "ext/openssl/ossl_version.h".freeze, "ext/openssl/ossl_x509.c".freeze, "ext/openssl/ossl_x509.h".freeze, "ext/openssl/ossl_x509attr.c".freeze, "ext/openssl/ossl_x509cert.c".freeze, "ext/openssl/ossl_x509crl.c".freeze, "ext/openssl/ossl_x509ext.c".freeze, "ext/openssl/ossl_x509name.c".freeze, "ext/openssl/ossl_x509req.c".freeze, "ext/openssl/ossl_x509revoked.c".freeze, "ext/openssl/ossl_x509store.c".freeze, "ext/openssl/ruby_missing.h".freeze, "lib/openssl.rb".freeze, "lib/openssl/bn.rb".freeze, "lib/openssl/buffering.rb".freeze, "lib/openssl/cipher.rb".freeze, "lib/openssl/config.rb".freeze, "lib/openssl/digest.rb".freeze, "lib/openssl/pkcs5.rb".freeze, "lib/openssl/pkey.rb".freeze, "lib/openssl/ssl.rb".freeze, "lib/openssl/x509.rb".freeze]
-  s.homepage = "https://github.com/ruby/openssl".freeze
-  s.licenses = ["Ruby".freeze]
-  s.rdoc_options = ["--main".freeze, "README.md".freeze]
-  s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze)
-  s.rubygems_version = "3.0.0.beta1".freeze
-  s.summary = "OpenSSL provides SSL, TLS and general purpose cryptography.".freeze
-
-  if s.respond_to? :specification_version then
-    s.specification_version = 4
-
-    if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
-      s.add_development_dependency(%q<rake>.freeze, [">= 0"])
-      s.add_development_dependency(%q<rake-compiler>.freeze, [">= 0"])
-      s.add_development_dependency(%q<test-unit>.freeze, ["~> 3.0"])
-      s.add_development_dependency(%q<rdoc>.freeze, [">= 0"])
-    else
-      s.add_dependency(%q<rake>.freeze, [">= 0"])
-      s.add_dependency(%q<rake-compiler>.freeze, [">= 0"])
-      s.add_dependency(%q<test-unit>.freeze, ["~> 3.0"])
-      s.add_dependency(%q<rdoc>.freeze, [">= 0"])
-    end
-  else
-    s.add_dependency(%q<rake>.freeze, [">= 0"])
-    s.add_dependency(%q<rake-compiler>.freeze, [">= 0"])
-    s.add_dependency(%q<test-unit>.freeze, ["~> 3.0"])
-    s.add_dependency(%q<rdoc>.freeze, [">= 0"])
-  end
-end
diff -ruN ruby-2.5.9.orig/ext/openssl/openssl_missing.c ruby-2.5.9/ext/openssl/openssl_missing.c
--- ruby-2.5.9.orig/ext/openssl/openssl_missing.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/openssl_missing.c	2025-01-29 19:08:02.377182194 +0100
@@ -5,82 +5,16 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include RUBY_EXTCONF_H
 
 #include <string.h> /* memcpy() */
-#if !defined(OPENSSL_NO_ENGINE)
-# include <openssl/engine.h>
-#endif
-#if !defined(OPENSSL_NO_HMAC)
-# include <openssl/hmac.h>
-#endif
 #include <openssl/x509_vfy.h>
 
 #include "openssl_missing.h"
 
-/* added in 1.0.2 */
-#if !defined(OPENSSL_NO_EC)
-#if !defined(HAVE_EC_CURVE_NIST2NID)
-static struct {
-    const char *name;
-    int nid;
-} nist_curves[] = {
-    {"B-163", NID_sect163r2},
-    {"B-233", NID_sect233r1},
-    {"B-283", NID_sect283r1},
-    {"B-409", NID_sect409r1},
-    {"B-571", NID_sect571r1},
-    {"K-163", NID_sect163k1},
-    {"K-233", NID_sect233k1},
-    {"K-283", NID_sect283k1},
-    {"K-409", NID_sect409k1},
-    {"K-571", NID_sect571k1},
-    {"P-192", NID_X9_62_prime192v1},
-    {"P-224", NID_secp224r1},
-    {"P-256", NID_X9_62_prime256v1},
-    {"P-384", NID_secp384r1},
-    {"P-521", NID_secp521r1}
-};
-
-int
-ossl_EC_curve_nist2nid(const char *name)
-{
-    size_t i;
-    for (i = 0; i < (sizeof(nist_curves) / sizeof(nist_curves[0])); i++) {
-	if (!strcmp(nist_curves[i].name, name))
-	    return nist_curves[i].nid;
-    }
-    return NID_undef;
-}
-#endif
-#endif
-
 /*** added in 1.1.0 ***/
-#if !defined(HAVE_HMAC_CTX_NEW)
-HMAC_CTX *
-ossl_HMAC_CTX_new(void)
-{
-    HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
-    if (!ctx)
-	return NULL;
-    HMAC_CTX_init(ctx);
-    return ctx;
-}
-#endif
-
-#if !defined(HAVE_HMAC_CTX_FREE)
-void
-ossl_HMAC_CTX_free(HMAC_CTX *ctx)
-{
-    if (ctx) {
-	HMAC_CTX_cleanup(ctx);
-	OPENSSL_free(ctx);
-    }
-}
-#endif
-
 #if !defined(HAVE_X509_CRL_GET0_SIGNATURE)
 void
 ossl_X509_CRL_get0_signature(const X509_CRL *crl, const ASN1_BIT_STRING **psig,
diff -ruN ruby-2.5.9.orig/ext/openssl/openssl_missing.h ruby-2.5.9/ext/openssl/openssl_missing.h
--- ruby-2.5.9.orig/ext/openssl/openssl_missing.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/openssl_missing.h	2025-01-29 19:08:02.377182194 +0100
@@ -5,47 +5,14 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_OPENSSL_MISSING_H_)
 #define _OSSL_OPENSSL_MISSING_H_
 
 #include "ruby/config.h"
 
-/* added in 1.0.2 */
-#if !defined(OPENSSL_NO_EC)
-#if !defined(HAVE_EC_CURVE_NIST2NID)
-int ossl_EC_curve_nist2nid(const char *);
-#  define EC_curve_nist2nid ossl_EC_curve_nist2nid
-#endif
-#endif
-
-#if !defined(HAVE_X509_REVOKED_DUP)
-# define X509_REVOKED_dup(rev) (X509_REVOKED *)ASN1_dup((i2d_of_void *)i2d_X509_REVOKED, \
-	(d2i_of_void *)d2i_X509_REVOKED, (char *)(rev))
-#endif
-
-#if !defined(HAVE_X509_STORE_CTX_GET0_STORE)
-#  define X509_STORE_CTX_get0_store(x) ((x)->ctx)
-#endif
-
-#if !defined(HAVE_SSL_IS_SERVER)
-#  define SSL_is_server(s) ((s)->server)
-#endif
-
 /* added in 1.1.0 */
-#if !defined(HAVE_BN_GENCB_NEW)
-#  define BN_GENCB_new() ((BN_GENCB *)OPENSSL_malloc(sizeof(BN_GENCB)))
-#endif
-
-#if !defined(HAVE_BN_GENCB_FREE)
-#  define BN_GENCB_free(cb) OPENSSL_free(cb)
-#endif
-
-#if !defined(HAVE_BN_GENCB_GET_ARG)
-#  define BN_GENCB_get_arg(cb) (cb)->arg
-#endif
-
 #if !defined(HAVE_EVP_MD_CTX_NEW)
 #  define EVP_MD_CTX_new EVP_MD_CTX_create
 #endif
@@ -54,16 +21,6 @@
 #  define EVP_MD_CTX_free EVP_MD_CTX_destroy
 #endif
 
-#if !defined(HAVE_HMAC_CTX_NEW)
-HMAC_CTX *ossl_HMAC_CTX_new(void);
-#  define HMAC_CTX_new ossl_HMAC_CTX_new
-#endif
-
-#if !defined(HAVE_HMAC_CTX_FREE)
-void ossl_HMAC_CTX_free(HMAC_CTX *);
-#  define HMAC_CTX_free ossl_HMAC_CTX_free
-#endif
-
 #if !defined(HAVE_X509_STORE_GET_EX_DATA)
 #  define X509_STORE_get_ex_data(x, idx) \
 	CRYPTO_get_ex_data(&(x)->ex_data, (idx))
@@ -72,6 +29,9 @@
 #if !defined(HAVE_X509_STORE_SET_EX_DATA)
 #  define X509_STORE_set_ex_data(x, idx, data) \
 	CRYPTO_set_ex_data(&(x)->ex_data, (idx), (data))
+#endif
+
+#if !defined(HAVE_X509_STORE_GET_EX_NEW_INDEX) && !defined(X509_STORE_get_ex_new_index)
 #  define X509_STORE_get_ex_new_index(l, p, newf, dupf, freef) \
 	CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_X509_STORE, (l), (p), \
 				(newf), (dupf), (freef))
@@ -185,7 +145,7 @@
 #if !defined(OPENSSL_NO_DH)
 IMPL_PKEY_GETTER(DH, dh)
 IMPL_KEY_ACCESSOR2(DH, key, pub_key, priv_key, (pub_key == obj->pub_key || (obj->priv_key && priv_key == obj->priv_key)))
-IMPL_KEY_ACCESSOR3(DH, pqg, p, q, g, (p == obj->p || obj->q && q == obj->q || g == obj->g))
+IMPL_KEY_ACCESSOR3(DH, pqg, p, q, g, (p == obj->p || (obj->q && q == obj->q) || g == obj->g))
 static inline ENGINE *DH_get0_engine(DH *dh) { return dh->engine; }
 #endif
 
@@ -219,4 +179,60 @@
 #  define SSL_SESSION_get_protocol_version(s) ((s)->ssl_version)
 #endif
 
+#if !defined(HAVE_TS_STATUS_INFO_GET0_STATUS)
+#  define TS_STATUS_INFO_get0_status(a) ((a)->status)
+#endif
+
+#if !defined(HAVE_TS_STATUS_INFO_GET0_TEXT)
+#  define TS_STATUS_INFO_get0_text(a) ((a)->text)
+#endif
+
+#if !defined(HAVE_TS_STATUS_INFO_GET0_FAILURE_INFO)
+#  define TS_STATUS_INFO_get0_failure_info(a) ((a)->failure_info)
+#endif
+
+#if !defined(HAVE_TS_VERIFY_CTS_SET_CERTS)
+#  define TS_VERIFY_CTS_set_certs(ctx, crts) ((ctx)->certs=(crts))
+#endif
+
+#if !defined(HAVE_TS_VERIFY_CTX_SET_STORE)
+#  define TS_VERIFY_CTX_set_store(ctx, str) ((ctx)->store=(str))
+#endif
+
+#if !defined(HAVE_TS_VERIFY_CTX_ADD_FLAGS)
+#  define TS_VERIFY_CTX_add_flags(ctx, f) ((ctx)->flags |= (f))
+#endif
+
+#if !defined(HAVE_TS_RESP_CTX_SET_TIME_CB)
+#   define TS_RESP_CTX_set_time_cb(ctx, callback, dta) do { \
+        (ctx)->time_cb = (callback); \
+        (ctx)->time_cb_data = (dta); \
+    } while (0)
+#endif
+
+/* added in 3.0.0 */
+#if !defined(HAVE_TS_VERIFY_CTX_SET_CERTS)
+#  define TS_VERIFY_CTX_set_certs(ctx, crts) TS_VERIFY_CTS_set_certs(ctx, crts)
+#endif
+
+#ifndef HAVE_EVP_MD_CTX_GET0_MD
+#  define EVP_MD_CTX_get0_md(ctx) EVP_MD_CTX_md(ctx)
+#endif
+
+/*
+ * OpenSSL 1.1.0 added EVP_MD_CTX_pkey_ctx(), and then it was renamed to
+ * EVP_MD_CTX_get_pkey_ctx(x) in OpenSSL 3.0.
+ */
+#ifndef HAVE_EVP_MD_CTX_GET_PKEY_CTX
+# ifdef HAVE_EVP_MD_CTX_PKEY_CTX
+#  define EVP_MD_CTX_get_pkey_ctx(x) EVP_MD_CTX_pkey_ctx(x)
+# else
+#  define EVP_MD_CTX_get_pkey_ctx(x) (x)->pctx
+# endif
+#endif
+
+#ifndef HAVE_EVP_PKEY_EQ
+#  define EVP_PKEY_eq(a, b) EVP_PKEY_cmp(a, b)
+#endif
+
 #endif /* _OSSL_OPENSSL_MISSING_H_ */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_asn1.c ruby-2.5.9/ext/openssl/ossl_asn1.c
--- ruby-2.5.9.orig/ext/openssl/ossl_asn1.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_asn1.c	2025-01-29 19:08:02.377182194 +0100
@@ -5,13 +5,12 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
 static VALUE ossl_asn1_decode0(unsigned char **pp, long length, long *offset,
 			       int depth, int yield, long *num_read);
-static VALUE ossl_asn1_initialize(int argc, VALUE *argv, VALUE self);
 
 /*
  * DATE conversion
@@ -69,6 +68,12 @@
     return rb_funcall2(rb_cTime, rb_intern("utc"), 6, argv);
 }
 
+static VALUE
+asn1time_to_time_i(VALUE arg)
+{
+    return asn1time_to_time((ASN1_TIME *)arg);
+}
+
 void
 ossl_time_split(VALUE time, time_t *sec, int *days)
 {
@@ -136,6 +141,12 @@
     return ai;
 }
 
+static VALUE
+asn1integer_to_num_i(VALUE arg)
+{
+    return asn1integer_to_num((ASN1_INTEGER *)arg);
+}
+
 /********/
 /*
  * ASN1 module
@@ -146,38 +157,33 @@
 #define ossl_asn1_get_tag_class(o)       rb_attr_get((o),sivTAG_CLASS)
 #define ossl_asn1_get_indefinite_length(o) rb_attr_get((o),sivINDEFINITE_LENGTH)
 
-#define ossl_asn1_set_value(o,v)           rb_ivar_set((o),sivVALUE,(v))
-#define ossl_asn1_set_tag(o,v)             rb_ivar_set((o),sivTAG,(v))
-#define ossl_asn1_set_tagging(o,v)         rb_ivar_set((o),sivTAGGING,(v))
-#define ossl_asn1_set_tag_class(o,v)       rb_ivar_set((o),sivTAG_CLASS,(v))
 #define ossl_asn1_set_indefinite_length(o,v) rb_ivar_set((o),sivINDEFINITE_LENGTH,(v))
 
 VALUE mASN1;
 VALUE eASN1Error;
 
 VALUE cASN1Data;
-VALUE cASN1Primitive;
-VALUE cASN1Constructive;
+static VALUE cASN1Primitive;
+static VALUE cASN1Constructive;
 
-VALUE cASN1EndOfContent;
-VALUE cASN1Boolean;                           /* BOOLEAN           */
-VALUE cASN1Integer, cASN1Enumerated;          /* INTEGER           */
-VALUE cASN1BitString;                         /* BIT STRING        */
-VALUE cASN1OctetString, cASN1UTF8String;      /* STRINGs           */
-VALUE cASN1NumericString, cASN1PrintableString;
-VALUE cASN1T61String, cASN1VideotexString;
-VALUE cASN1IA5String, cASN1GraphicString;
-VALUE cASN1ISO64String, cASN1GeneralString;
-VALUE cASN1UniversalString, cASN1BMPString;
-VALUE cASN1Null;                              /* NULL              */
-VALUE cASN1ObjectId;                          /* OBJECT IDENTIFIER */
-VALUE cASN1UTCTime, cASN1GeneralizedTime;     /* TIME              */
-VALUE cASN1Sequence, cASN1Set;                /* CONSTRUCTIVE      */
+static VALUE cASN1EndOfContent;
+static VALUE cASN1Boolean;                           /* BOOLEAN           */
+static VALUE cASN1Integer, cASN1Enumerated;          /* INTEGER           */
+static VALUE cASN1BitString;                         /* BIT STRING        */
+static VALUE cASN1OctetString, cASN1UTF8String;      /* STRINGs           */
+static VALUE cASN1NumericString, cASN1PrintableString;
+static VALUE cASN1T61String, cASN1VideotexString;
+static VALUE cASN1IA5String, cASN1GraphicString;
+static VALUE cASN1ISO64String, cASN1GeneralString;
+static VALUE cASN1UniversalString, cASN1BMPString;
+static VALUE cASN1Null;                              /* NULL              */
+static VALUE cASN1ObjectId;                          /* OBJECT IDENTIFIER */
+static VALUE cASN1UTCTime, cASN1GeneralizedTime;     /* TIME              */
+static VALUE cASN1Sequence, cASN1Set;                /* CONSTRUCTIVE      */
 
 static VALUE sym_IMPLICIT, sym_EXPLICIT;
 static VALUE sym_UNIVERSAL, sym_APPLICATION, sym_CONTEXT_SPECIFIC, sym_PRIVATE;
 static ID sivVALUE, sivTAG, sivTAG_CLASS, sivTAGGING, sivINDEFINITE_LENGTH, sivUNUSED_BITS;
-static ID id_each;
 
 /*
  * Ruby to ASN1 converters
@@ -325,7 +331,7 @@
     p = der;
     if(!(ai = d2i_ASN1_INTEGER(NULL, &p, length)))
 	ossl_raise(eASN1Error, NULL);
-    ret = rb_protect((VALUE (*)(VALUE))asn1integer_to_num,
+    ret = rb_protect(asn1integer_to_num_i,
 		     (VALUE)ai, &status);
     ASN1_INTEGER_free(ai);
     if(status) rb_jump_tag(status);
@@ -365,7 +371,7 @@
     p = der;
     if(!(ai = d2i_ASN1_ENUMERATED(NULL, &p, length)))
 	ossl_raise(eASN1Error, NULL);
-    ret = rb_protect((VALUE (*)(VALUE))asn1integer_to_num,
+    ret = rb_protect(asn1integer_to_num_i,
 		     (VALUE)ai, &status);
     ASN1_ENUMERATED_free(ai);
     if(status) rb_jump_tag(status);
@@ -427,7 +433,7 @@
     p = der;
     if(!(time = d2i_ASN1_TIME(NULL, &p, length)))
 	ossl_raise(eASN1Error, NULL);
-    ret = rb_protect((VALUE (*)(VALUE))asn1time_to_time,
+    ret = rb_protect(asn1time_to_time_i,
 		     (VALUE)time, &status);
     ASN1_TIME_free(time);
     if(status) rb_jump_tag(status);
@@ -491,13 +497,14 @@
 
 static int ossl_asn1_default_tag(VALUE obj);
 
-ASN1_TYPE*
+static ASN1_TYPE *
 ossl_asn1_get_asn1type(VALUE obj)
 {
     ASN1_TYPE *ret;
     VALUE value, rflag;
     void *ptr;
-    void (*free_func)();
+    typedef void free_func_type(void *);
+    free_func_type *free_func;
     int tag;
 
     tag = ossl_asn1_default_tag(obj);
@@ -510,16 +517,16 @@
     case V_ASN1_INTEGER:         /* FALLTHROUGH */
     case V_ASN1_ENUMERATED:
 	ptr = obj_to_asn1int(value);
-	free_func = ASN1_INTEGER_free;
+	free_func = (free_func_type *)ASN1_INTEGER_free;
 	break;
     case V_ASN1_BIT_STRING:
         rflag = rb_attr_get(obj, sivUNUSED_BITS);
 	ptr = obj_to_asn1bstr(value, NUM2INT(rflag));
-	free_func = ASN1_BIT_STRING_free;
+	free_func = (free_func_type *)ASN1_BIT_STRING_free;
 	break;
     case V_ASN1_NULL:
 	ptr = obj_to_asn1null(value);
-	free_func = ASN1_NULL_free;
+	free_func = (free_func_type *)ASN1_NULL_free;
 	break;
     case V_ASN1_OCTET_STRING:    /* FALLTHROUGH */
     case V_ASN1_UTF8STRING:      /* FALLTHROUGH */
@@ -534,24 +541,24 @@
     case V_ASN1_UNIVERSALSTRING: /* FALLTHROUGH */
     case V_ASN1_BMPSTRING:
 	ptr = obj_to_asn1str(value);
-	free_func = ASN1_STRING_free;
+	free_func = (free_func_type *)ASN1_STRING_free;
 	break;
     case V_ASN1_OBJECT:
 	ptr = obj_to_asn1obj(value);
-	free_func = ASN1_OBJECT_free;
+	free_func = (free_func_type *)ASN1_OBJECT_free;
 	break;
     case V_ASN1_UTCTIME:
 	ptr = obj_to_asn1utime(value);
-	free_func = ASN1_TIME_free;
+	free_func = (free_func_type *)ASN1_TIME_free;
 	break;
     case V_ASN1_GENERALIZEDTIME:
 	ptr = obj_to_asn1gtime(value);
-	free_func = ASN1_TIME_free;
+	free_func = (free_func_type *)ASN1_TIME_free;
 	break;
     case V_ASN1_SET:             /* FALLTHROUGH */
     case V_ASN1_SEQUENCE:
 	ptr = obj_to_asn1derstr(obj);
-	free_func = ASN1_STRING_free;
+	free_func = (free_func_type *)ASN1_STRING_free;
 	break;
     default:
 	ossl_raise(eASN1Error, "unsupported ASN.1 type");
@@ -625,35 +632,6 @@
 	return sym_UNIVERSAL;
 }
 
-/*
- * call-seq:
- *    OpenSSL::ASN1::ASN1Data.new(value, tag, tag_class) => ASN1Data
- *
- * _value_: Please have a look at Constructive and Primitive to see how Ruby
- * types are mapped to ASN.1 types and vice versa.
- *
- * _tag_: An Integer indicating the tag number.
- *
- * _tag_class_: A Symbol indicating the tag class. Please cf. ASN1 for
- * possible values.
- *
- * == Example
- *   asn1_int = OpenSSL::ASN1Data.new(42, 2, :UNIVERSAL) # => Same as OpenSSL::ASN1::Integer.new(42)
- *   tagged_int = OpenSSL::ASN1Data.new(42, 0, :CONTEXT_SPECIFIC) # implicitly 0-tagged INTEGER
- */
-static VALUE
-ossl_asn1data_initialize(VALUE self, VALUE value, VALUE tag, VALUE tag_class)
-{
-    if(!SYMBOL_P(tag_class))
-	ossl_raise(eASN1Error, "invalid tag class");
-    ossl_asn1_set_tag(self, tag);
-    ossl_asn1_set_value(self, value);
-    ossl_asn1_set_tag_class(self, tag_class);
-    ossl_asn1_set_indefinite_length(self, Qfalse);
-
-    return self;
-}
-
 static VALUE
 to_der_internal(VALUE self, int constructed, int indef_len, VALUE body)
 {
@@ -782,20 +760,19 @@
     if (tc == sym_UNIVERSAL &&
 	tag < ossl_asn1_info_size && ossl_asn1_info[tag].klass) {
 	VALUE klass = *ossl_asn1_info[tag].klass;
-	VALUE args[4];
-	args[0] = value;
-	args[1] = INT2NUM(tag);
-	args[2] = Qnil;
-	args[3] = tc;
-	asn1data = rb_obj_alloc(klass);
-	ossl_asn1_initialize(4, args, asn1data);
+	if (tag == V_ASN1_EOC)
+	    asn1data = rb_funcall(cASN1EndOfContent, rb_intern("new"), 0);
+	else {
+	    VALUE args[4] = { value, INT2NUM(tag), Qnil, tc };
+	    asn1data = rb_funcallv_public(klass, rb_intern("new"), 4, args);
+	}
 	if(tag == V_ASN1_BIT_STRING){
 	    rb_ivar_set(asn1data, sivUNUSED_BITS, LONG2NUM(flag));
 	}
     }
     else {
-	asn1data = rb_obj_alloc(cASN1Data);
-	ossl_asn1data_initialize(asn1data, value, INT2NUM(tag), tc);
+        VALUE args[3] = { value, INT2NUM(tag), tc };
+        asn1data = rb_funcallv_public(cASN1Data, rb_intern("new"), 3, args);
     }
 
     return asn1data;
@@ -829,20 +806,20 @@
     }
 
     if (tc == sym_UNIVERSAL) {
-	VALUE args[4];
-	if (tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET)
-	    asn1data = rb_obj_alloc(*ossl_asn1_info[tag].klass);
-	else
-	    asn1data = rb_obj_alloc(cASN1Constructive);
-	args[0] = ary;
-	args[1] = INT2NUM(tag);
-	args[2] = Qnil;
-	args[3] = tc;
-	ossl_asn1_initialize(4, args, asn1data);
+        if (tag == V_ASN1_SEQUENCE) {
+            VALUE args[4] = { ary, INT2NUM(tag), Qnil, tc };
+            asn1data = rb_funcallv_public(cASN1Sequence, rb_intern("new"), 4, args);
+        } else if (tag == V_ASN1_SET) {
+            VALUE args[4] = { ary, INT2NUM(tag), Qnil, tc };
+            asn1data = rb_funcallv_public(cASN1Set, rb_intern("new"), 4, args);
+        } else {
+            VALUE args[4] = { ary, INT2NUM(tag), Qnil, tc };
+            asn1data = rb_funcallv_public(cASN1Constructive, rb_intern("new"), 4, args);
+        }
     }
     else {
-	asn1data = rb_obj_alloc(cASN1Data);
-	ossl_asn1data_initialize(asn1data, ary, INT2NUM(tag), tc);
+        VALUE args[3] = {ary, INT2NUM(tag), tc};
+        asn1data = rb_funcallv_public(cASN1Data, rb_intern("new"), 3, args);
     }
 
     if (indefinite)
@@ -1035,83 +1012,6 @@
     return ary;
 }
 
-/*
- * call-seq:
- *    OpenSSL::ASN1::Primitive.new(value [, tag, tagging, tag_class ]) => Primitive
- *
- * _value_: is mandatory.
- *
- * _tag_: optional, may be specified for tagged values. If no _tag_ is
- * specified, the UNIVERSAL tag corresponding to the Primitive sub-class
- * is used by default.
- *
- * _tagging_: may be used as an encoding hint to encode a value either
- * explicitly or implicitly, see ASN1 for possible values.
- *
- * _tag_class_: if _tag_ and _tagging_ are +nil+ then this is set to
- * +:UNIVERSAL+ by default. If either _tag_ or _tagging_ are set then
- * +:CONTEXT_SPECIFIC+ is used as the default. For possible values please
- * cf. ASN1.
- *
- * == Example
- *   int = OpenSSL::ASN1::Integer.new(42)
- *   zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :IMPLICIT)
- *   private_explicit_zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :EXPLICIT, :PRIVATE)
- */
-static VALUE
-ossl_asn1_initialize(int argc, VALUE *argv, VALUE self)
-{
-    VALUE value, tag, tagging, tag_class;
-    int default_tag;
-
-    rb_scan_args(argc, argv, "13", &value, &tag, &tagging, &tag_class);
-    default_tag = ossl_asn1_default_tag(self);
-
-    if (default_tag == -1 || argc > 1) {
-	if(NIL_P(tag))
-	    ossl_raise(eASN1Error, "must specify tag number");
-	if(!NIL_P(tagging) && !SYMBOL_P(tagging))
-	    ossl_raise(eASN1Error, "invalid tagging method");
-	if(NIL_P(tag_class)) {
-	    if (NIL_P(tagging))
-		tag_class = sym_UNIVERSAL;
-	    else
-		tag_class = sym_CONTEXT_SPECIFIC;
-	}
-	if(!SYMBOL_P(tag_class))
-	    ossl_raise(eASN1Error, "invalid tag class");
-    }
-    else{
-	tag = INT2NUM(default_tag);
-	tagging = Qnil;
-	tag_class = sym_UNIVERSAL;
-    }
-    ossl_asn1_set_tag(self, tag);
-    ossl_asn1_set_value(self, value);
-    ossl_asn1_set_tagging(self, tagging);
-    ossl_asn1_set_tag_class(self, tag_class);
-    ossl_asn1_set_indefinite_length(self, Qfalse);
-    if (default_tag == V_ASN1_BIT_STRING)
-	rb_ivar_set(self, sivUNUSED_BITS, INT2FIX(0));
-
-    return self;
-}
-
-static VALUE
-ossl_asn1eoc_initialize(VALUE self) {
-    VALUE tag, tagging, tag_class, value;
-    tag = INT2FIX(0);
-    tagging = Qnil;
-    tag_class = sym_UNIVERSAL;
-    value = rb_str_new("", 0);
-    ossl_asn1_set_tag(self, tag);
-    ossl_asn1_set_value(self, value);
-    ossl_asn1_set_tagging(self, tagging);
-    ossl_asn1_set_tag_class(self, tag_class);
-    ossl_asn1_set_indefinite_length(self, Qfalse);
-    return self;
-}
-
 static VALUE
 ossl_asn1eoc_to_der(VALUE self)
 {
@@ -1150,9 +1050,12 @@
 	rb_jump_tag(state);
     }
     p0 = p1 = (unsigned char *)RSTRING_PTR(str);
-    i2d_ASN1_TYPE(asn1, &p0);
+    if (i2d_ASN1_TYPE(asn1, &p0) < 0) {
+        ASN1_TYPE_free(asn1);
+        ossl_raise(eASN1Error, "i2d_ASN1_TYPE");
+    }
     ASN1_TYPE_free(asn1);
-    assert(p0 - p1 == alllen);
+    ossl_str_adjust(str, p0);
 
     /* Strip header since to_der_internal() wants only the payload */
     j = ASN1_get_object((const unsigned char **)&p1, &bodylen, &tag, &tc, alllen);
@@ -1202,27 +1105,6 @@
 
 /*
  * call-seq:
- *    asn1_ary.each { |asn1| block } => asn1_ary
- *
- * Calls the given block once for each element in self, passing that element
- * as parameter _asn1_. If no block is given, an enumerator is returned
- * instead.
- *
- * == Example
- *   asn1_ary.each do |asn1|
- *     puts asn1
- *   end
- */
-static VALUE
-ossl_asn1cons_each(VALUE self)
-{
-    rb_block_call(ossl_asn1_get_value(self), id_each, 0, 0, 0, 0);
-
-    return self;
-}
-
-/*
- * call-seq:
  *    OpenSSL::ASN1::ObjectId.register(object_id, short_name, long_name)
  *
  * This adds a new ObjectId to the internal tables. Where _object_id_ is the
@@ -1329,9 +1211,28 @@
     return str;
 }
 
+/*
+ *  call-seq:
+ *     oid == other_oid => true or false
+ *
+ *  Returns +true+ if _other_oid_ is the same as _oid_.
+ */
+static VALUE
+ossl_asn1obj_eq(VALUE self, VALUE other)
+{
+    VALUE oid1, oid2;
+
+    if (!rb_obj_is_kind_of(other, cASN1ObjectId))
+        return Qfalse;
+
+    oid1 = ossl_asn1obj_get_oid(self);
+    oid2 = ossl_asn1obj_get_oid(other);
+    return rb_str_equal(oid1, oid2);
+}
+
 #define OSSL_ASN1_IMPL_FACTORY_METHOD(klass) \
 static VALUE ossl_asn1_##klass(int argc, VALUE *argv, VALUE self)\
-{ return rb_funcall3(cASN1##klass, rb_intern("new"), argc, argv); }
+{ return rb_funcallv_public(cASN1##klass, rb_intern("new"), argc, argv); }
 
 OSSL_ASN1_IMPL_FACTORY_METHOD(Boolean)
 OSSL_ASN1_IMPL_FACTORY_METHOD(Integer)
@@ -1486,7 +1387,7 @@
      *
      * An Array that stores the name of a given tag number. These names are
      * the same as the name of the tag constant that is additionally defined,
-     * e.g. UNIVERSAL_TAG_NAME[2] = "INTEGER" and OpenSSL::ASN1::INTEGER = 2.
+     * e.g. <tt>UNIVERSAL_TAG_NAME[2] = "INTEGER"</tt> and <tt>OpenSSL::ASN1::INTEGER = 2</tt>.
      *
      * == Example usage
      *
@@ -1617,42 +1518,6 @@
      *   puts int2.value # => 1
      */
     cASN1Data = rb_define_class_under(mASN1, "ASN1Data", rb_cObject);
-    /*
-     * Carries the value of a ASN.1 type.
-     * Please confer Constructive and Primitive for the mappings between
-     * ASN.1 data types and Ruby classes.
-     */
-    rb_attr(cASN1Data, rb_intern("value"), 1, 1, 0);
-    /*
-     * An Integer representing the tag number of this ASN1Data. Never +nil+.
-     */
-    rb_attr(cASN1Data, rb_intern("tag"), 1, 1, 0);
-    /*
-     * A Symbol representing the tag class of this ASN1Data. Never +nil+.
-     * See ASN1Data for possible values.
-     */
-    rb_attr(cASN1Data, rb_intern("tag_class"), 1, 1, 0);
-    /*
-     * Never +nil+. A boolean value indicating whether the encoding uses
-     * indefinite length (in the case of parsing) or whether an indefinite
-     * length form shall be used (in the encoding case).
-     * In DER, every value uses definite length form. But in scenarios where
-     * large amounts of data need to be transferred it might be desirable to
-     * have some kind of streaming support available.
-     * For example, huge OCTET STRINGs are preferably sent in smaller-sized
-     * chunks, each at a time.
-     * This is possible in BER by setting the length bytes of an encoding
-     * to zero and by this indicating that the following value will be
-     * sent in chunks. Indefinite length encodings are always constructed.
-     * The end of such a stream of chunks is indicated by sending a EOC
-     * (End of Content) tag. SETs and SEQUENCEs may use an indefinite length
-     * encoding, but also primitive types such as e.g. OCTET STRINGS or
-     * BIT STRINGS may leverage this functionality (cf. ITU-T X.690).
-     */
-    rb_attr(cASN1Data, rb_intern("indefinite_length"), 1, 1, 0);
-    rb_define_alias(cASN1Data, "infinite_length", "indefinite_length");
-    rb_define_alias(cASN1Data, "infinite_length=", "indefinite_length=");
-    rb_define_method(cASN1Data, "initialize", ossl_asn1data_initialize, 3);
     rb_define_method(cASN1Data, "to_der", ossl_asn1data_to_der, 0);
 
     /* Document-class: OpenSSL::ASN1::Primitive
@@ -1720,16 +1585,6 @@
      *   prim_zero_tagged_explicit = <class>.new(value, 0, :EXPLICIT)
      */
     cASN1Primitive = rb_define_class_under(mASN1, "Primitive", cASN1Data);
-    /*
-     * May be used as a hint for encoding a value either implicitly or
-     * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+.
-     * _tagging_ is not set when a ASN.1 structure is parsed using
-     * OpenSSL::ASN1.decode.
-     */
-    rb_attr(cASN1Primitive, rb_intern("tagging"), 1, 1, Qtrue);
-    rb_undef_method(cASN1Primitive, "indefinite_length=");
-    rb_undef_method(cASN1Primitive, "infinite_length=");
-    rb_define_method(cASN1Primitive, "initialize", ossl_asn1_initialize, -1);
     rb_define_method(cASN1Primitive, "to_der", ossl_asn1prim_to_der, 0);
 
     /* Document-class: OpenSSL::ASN1::Constructive
@@ -1760,17 +1615,7 @@
      *   set = OpenSSL::ASN1::Set.new( [ int, str ] )
      */
     cASN1Constructive = rb_define_class_under(mASN1,"Constructive", cASN1Data);
-    rb_include_module(cASN1Constructive, rb_mEnumerable);
-    /*
-     * May be used as a hint for encoding a value either implicitly or
-     * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+.
-     * _tagging_ is not set when a ASN.1 structure is parsed using
-     * OpenSSL::ASN1.decode.
-     */
-    rb_attr(cASN1Constructive, rb_intern("tagging"), 1, 1, Qtrue);
-    rb_define_method(cASN1Constructive, "initialize", ossl_asn1_initialize, -1);
     rb_define_method(cASN1Constructive, "to_der", ossl_asn1cons_to_der, 0);
-    rb_define_method(cASN1Constructive, "each", ossl_asn1cons_each, 0);
 
 #define OSSL_ASN1_DEFINE_CLASS(name, super) \
 do{\
@@ -1818,13 +1663,11 @@
     rb_define_method(cASN1ObjectId, "oid", ossl_asn1obj_get_oid, 0);
     rb_define_alias(cASN1ObjectId, "short_name", "sn");
     rb_define_alias(cASN1ObjectId, "long_name", "ln");
-    rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, 0);
+    rb_define_method(cASN1ObjectId, "==", ossl_asn1obj_eq, 1);
 
-    rb_define_method(cASN1EndOfContent, "initialize", ossl_asn1eoc_initialize, 0);
     rb_define_method(cASN1EndOfContent, "to_der", ossl_asn1eoc_to_der, 0);
 
     class_tag_map = rb_hash_new();
-    rb_gc_register_mark_object(class_tag_map);
     rb_hash_aset(class_tag_map, cASN1EndOfContent, INT2NUM(V_ASN1_EOC));
     rb_hash_aset(class_tag_map, cASN1Boolean, INT2NUM(V_ASN1_BOOLEAN));
     rb_hash_aset(class_tag_map, cASN1Integer, INT2NUM(V_ASN1_INTEGER));
@@ -1848,7 +1691,5 @@
     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_global_variable(&class_tag_map);
-
-    id_each = rb_intern_const("each");
+    rb_define_const(mASN1, "CLASS_TAG_MAP", class_tag_map);
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_asn1.h ruby-2.5.9/ext/openssl/ossl_asn1.h
--- ruby-2.5.9.orig/ext/openssl/ossl_asn1.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_asn1.h	2025-01-29 19:08:02.377182194 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_ASN1_H_)
 #define _OSSL_ASN1_H_
@@ -38,24 +38,6 @@
 extern VALUE eASN1Error;
 
 extern VALUE cASN1Data;
-extern VALUE cASN1Primitive;
-extern VALUE cASN1Constructive;
-
-extern VALUE cASN1Boolean;                           /* BOOLEAN           */
-extern VALUE cASN1Integer, cASN1Enumerated;          /* INTEGER           */
-extern VALUE cASN1BitString;                         /* BIT STRING        */
-extern VALUE cASN1OctetString, cASN1UTF8String;      /* STRINGs           */
-extern VALUE cASN1NumericString, cASN1PrintableString;
-extern VALUE cASN1T61String, cASN1VideotexString;
-extern VALUE cASN1IA5String, cASN1GraphicString;
-extern VALUE cASN1ISO64String, cASN1GeneralString;
-extern VALUE cASN1UniversalString, cASN1BMPString;
-extern VALUE cASN1Null;                              /* NULL              */
-extern VALUE cASN1ObjectId;                          /* OBJECT IDENTIFIER */
-extern VALUE cASN1UTCTime, cASN1GeneralizedTime;     /* TIME              */
-extern VALUE cASN1Sequence, cASN1Set;                /* CONSTRUCTIVE      */
-
-ASN1_TYPE *ossl_asn1_get_asn1type(VALUE);
 
 void Init_ossl_asn1(void);
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_bio.c ruby-2.5.9/ext/openssl/ossl_bio.c
--- ruby-2.5.9.orig/ext/openssl/ossl_bio.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_bio.c	2025-01-29 19:08:02.377182194 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_bio.h ruby-2.5.9/ext/openssl/ossl_bio.h
--- ruby-2.5.9.orig/ext/openssl/ossl_bio.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_bio.h	2025-01-29 19:08:02.377421257 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_BIO_H_)
 #define _OSSL_BIO_H_
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_bn.c ruby-2.5.9/ext/openssl/ossl_bn.c
--- ruby-2.5.9.orig/ext/openssl/ossl_bn.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_bn.c	2025-01-29 19:08:02.377421257 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 /* modified by Michal Rokos <m.rokos@sh.cvut.cz> */
 #include "ossl.h"
@@ -37,7 +37,7 @@
     {
 	0, ossl_bn_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
 };
 
 /*
@@ -49,7 +49,7 @@
  *
  * Generic Error for all of OpenSSL::BN (big num)
  */
-VALUE eBNError;
+static VALUE eBNError;
 
 /*
  * Public
@@ -150,12 +150,58 @@
 /*
  * Private
  */
-/*
- * BN_CTX - is used in more difficult math. ops
- * (Why just 1? Because Ruby itself isn't thread safe,
- *  we don't need to care about threads)
- */
-BN_CTX *ossl_bn_ctx;
+
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+static void
+ossl_bn_ctx_free(void *ptr)
+{
+    BN_CTX *ctx = (BN_CTX *)ptr;
+    BN_CTX_free(ctx);
+}
+
+static struct rb_ractor_local_storage_type ossl_bn_ctx_key_type = {
+    NULL, // mark
+    ossl_bn_ctx_free,
+};
+
+static rb_ractor_local_key_t ossl_bn_ctx_key;
+
+BN_CTX *
+ossl_bn_ctx_get(void)
+{
+    // stored in ractor local storage
+
+    BN_CTX *ctx = rb_ractor_local_storage_ptr(ossl_bn_ctx_key);
+    if (!ctx) {
+        if (!(ctx = BN_CTX_new())) {
+            ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX");
+        }
+        rb_ractor_local_storage_ptr_set(ossl_bn_ctx_key, ctx);
+    }
+    return ctx;
+}
+#else
+// for ruby 2.x
+static BN_CTX *gv_ossl_bn_ctx;
+
+BN_CTX *
+ossl_bn_ctx_get(void)
+{
+    if (gv_ossl_bn_ctx == NULL) {
+        if (!(gv_ossl_bn_ctx = BN_CTX_new())) {
+            ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX");
+        }
+    }
+    return gv_ossl_bn_ctx;
+}
+
+void
+ossl_bn_ctx_free(void)
+{
+    BN_CTX_free(gv_ossl_bn_ctx);
+    gv_ossl_bn_ctx = NULL;
+}
+#endif
 
 static VALUE
 ossl_bn_alloc(VALUE klass)
@@ -173,13 +219,29 @@
 
 /*
  * call-seq:
- *    OpenSSL::BN.new => aBN
- *    OpenSSL::BN.new(bn) => aBN
- *    OpenSSL::BN.new(integer) => aBN
- *    OpenSSL::BN.new(string) => aBN
- *    OpenSSL::BN.new(string, 0 | 2 | 10 | 16) => aBN
+ *    OpenSSL::BN.new(bn) -> aBN
+ *    OpenSSL::BN.new(integer) -> aBN
+ *    OpenSSL::BN.new(string, base = 10) -> aBN
+ *
+ * Construct a new \OpenSSL BIGNUM object.
+ *
+ * If +bn+ is an Integer or OpenSSL::BN, a new instance of OpenSSL::BN
+ * representing the same value is returned. See also Integer#to_bn for the
+ * short-hand.
  *
- * Construct a new OpenSSL BIGNUM object.
+ * If a String is given, the content will be parsed according to +base+.
+ *
+ * +string+::
+ *   The string to be parsed.
+ * +base+::
+ *   The format. Must be one of the following:
+ *   - +0+  - MPI format. See the man page BN_mpi2bn(3) for details.
+ *   - +2+  - Variable-length and big-endian binary encoding of a positive
+ *     number.
+ *   - +10+ - Decimal number representation, with a leading '-' for a negative
+ *     number.
+ *   - +16+ - Hexadecimal number representation, with a leading '-' for a
+ *     negative number.
  */
 static VALUE
 ossl_bn_initialize(int argc, VALUE *argv, VALUE self)
@@ -187,11 +249,17 @@
     BIGNUM *bn;
     VALUE str, bs;
     int base = 10;
+    char *ptr;
 
     if (rb_scan_args(argc, argv, "11", &str, &bs) == 2) {
 	base = NUM2INT(bs);
     }
 
+    if (NIL_P(str)) {
+        ossl_raise(rb_eArgError, "invalid argument");
+    }
+
+    rb_check_frozen(self);
     if (RB_INTEGER_TYPE_P(str)) {
 	GetBN(self, bn);
 	integer_to_bnptr(str, bn);
@@ -213,12 +281,14 @@
     GetBN(self, bn);
     switch (base) {
     case 0:
-	if (!BN_mpi2bn((unsigned char *)StringValuePtr(str), RSTRING_LENINT(str), bn)) {
+        ptr = StringValuePtr(str);
+        if (!BN_mpi2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) {
 	    ossl_raise(eBNError, NULL);
 	}
 	break;
     case 2:
-	if (!BN_bin2bn((unsigned char *)StringValuePtr(str), RSTRING_LENINT(str), bn)) {
+        ptr = StringValuePtr(str);
+        if (!BN_bin2bn((unsigned char *)ptr, RSTRING_LENINT(str), bn)) {
 	    ossl_raise(eBNError, NULL);
 	}
 	break;
@@ -240,16 +310,21 @@
 
 /*
  * call-seq:
- *    bn.to_s => string
- *    bn.to_s(base) => string
+ *    bn.to_s(base = 10) -> string
  *
- * === Parameters
- * * _base_ - Integer
- *   Valid values:
- *   * 0 - MPI
- *   * 2 - binary
- *   * 10 - the default
- *   * 16 - hex
+ * Returns the string representation of the bignum.
+ *
+ * BN.new can parse the encoded string to convert back into an OpenSSL::BN.
+ *
+ * +base+::
+ *   The format. Must be one of the following:
+ *   - +0+  - MPI format. See the man page BN_bn2mpi(3) for details.
+ *   - +2+  - Variable-length and big-endian binary encoding. The sign of
+ *     the bignum is ignored.
+ *   - +10+ - Decimal number representation, with a leading '-' for a negative
+ *     bignum.
+ *   - +16+ - Hexadecimal number representation, with a leading '-' for a
+ *     negative bignum.
  */
 static VALUE
 ossl_bn_to_s(int argc, VALUE *argv, VALUE self)
@@ -397,7 +472,7 @@
 	if (!(result = BN_new())) {			\
 	    ossl_raise(eBNError, NULL);			\
 	}						\
-	if (!BN_##func(result, bn, ossl_bn_ctx)) {	\
+	if (BN_##func(result, bn, ossl_bn_ctx) <= 0) {	\
 	    BN_free(result);				\
 	    ossl_raise(eBNError, NULL);			\
 	}						\
@@ -423,7 +498,7 @@
 	if (!(result = BN_new())) {			\
 	    ossl_raise(eBNError, NULL);			\
 	}						\
-	if (!BN_##func(result, bn1, bn2)) {		\
+	if (BN_##func(result, bn1, bn2) <= 0) {		\
 	    BN_free(result);				\
 	    ossl_raise(eBNError, NULL);			\
 	}						\
@@ -456,7 +531,7 @@
 	if (!(result = BN_new())) {				\
 	    ossl_raise(eBNError, NULL);				\
 	}							\
-	if (!BN_##func(result, bn1, bn2, ossl_bn_ctx)) {	\
+	if (BN_##func(result, bn1, bn2, ossl_bn_ctx) <= 0) {	\
 	    BN_free(result);					\
 	    ossl_raise(eBNError, NULL);				\
 	}							\
@@ -499,12 +574,33 @@
  */
 BIGNUM_2c(mod_sqr)
 
+#define BIGNUM_2cr(func)					\
+    static VALUE						\
+    ossl_bn_##func(VALUE self, VALUE other)			\
+    {								\
+	BIGNUM *bn1, *bn2 = GetBNPtr(other), *result;		\
+	VALUE obj;						\
+	GetBN(self, bn1);					\
+	obj = NewBN(rb_obj_class(self));			\
+	if (!(result = BN_##func(NULL, bn1, bn2, ossl_bn_ctx)))	\
+	    ossl_raise(eBNError, NULL);				\
+	SetBN(obj, result);					\
+	return obj;						\
+    }
+
+/*
+ * Document-method: OpenSSL::BN#mod_sqrt
+ * call-seq:
+ *   bn.mod_sqrt(bn2) => aBN
+ */
+BIGNUM_2cr(mod_sqrt)
+
 /*
  * Document-method: OpenSSL::BN#mod_inverse
  * call-seq:
- *   bn.mod_inverse(bn2) => aBN
+ *    bn.mod_inverse(bn2) => aBN
  */
-BIGNUM_2c(mod_inverse)
+BIGNUM_2cr(mod_inverse)
 
 /*
  * call-seq:
@@ -553,7 +649,7 @@
 	if (!(result = BN_new())) {				\
 	    ossl_raise(eBNError, NULL);				\
 	}							\
-	if (!BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx)) {	\
+	if (BN_##func(result, bn1, bn2, bn3, ossl_bn_ctx) <= 0) { \
 	    BN_free(result);					\
 	    ossl_raise(eBNError, NULL);				\
 	}							\
@@ -594,8 +690,9 @@
     ossl_bn_##func(VALUE self, VALUE bit)		\
     {							\
 	BIGNUM *bn;					\
+	rb_check_frozen(self);				\
 	GetBN(self, bn);				\
-	if (!BN_##func(bn, NUM2INT(bit))) {		\
+	if (BN_##func(bn, NUM2INT(bit)) <= 0) {		\
 	    ossl_raise(eBNError, NULL);			\
 	}						\
 	return self;					\
@@ -655,7 +752,7 @@
 	if (!(result = BN_new())) {			\
 		ossl_raise(eBNError, NULL);		\
 	}						\
-	if (!BN_##func(result, bn, b)) {		\
+	if (BN_##func(result, bn, b) <= 0) {		\
 		BN_free(result);			\
 		ossl_raise(eBNError, NULL);		\
 	}						\
@@ -683,9 +780,10 @@
     {							\
 	BIGNUM *bn;					\
 	int b;						\
+	rb_check_frozen(self);				\
 	b = NUM2INT(bits);				\
 	GetBN(self, bn);				\
-	if (!BN_##func(bn, bn, b))			\
+	if (BN_##func(bn, bn, b) <= 0)			\
 		ossl_raise(eBNError, NULL);		\
 	return self;					\
     }
@@ -704,78 +802,64 @@
  */
 BIGNUM_SELF_SHIFT(rshift)
 
-#define BIGNUM_RAND(func)					\
-    static VALUE						\
-    ossl_bn_s_##func(int argc, VALUE *argv, VALUE klass)	\
-    {								\
-	BIGNUM *result;						\
-	int bottom = 0, top = 0, b;				\
-	VALUE bits, fill, odd, obj;				\
-								\
-	switch (rb_scan_args(argc, argv, "12", &bits, &fill, &odd)) {	\
-	case 3:							\
-	    bottom = (odd == Qtrue) ? 1 : 0;			\
-	    /* FALLTHROUGH */					\
-	case 2:							\
-	    top = NUM2INT(fill);				\
-	}							\
-	b = NUM2INT(bits);					\
-	obj = NewBN(klass);					\
-	if (!(result = BN_new())) {				\
-	    ossl_raise(eBNError, NULL);				\
-	}							\
-	if (!BN_##func(result, b, top, bottom)) {		\
-	    BN_free(result);					\
-	    ossl_raise(eBNError, NULL);				\
-	}							\
-	SetBN(obj, result);					\
-	return obj;						\
-    }
-
-/*
- * Document-method: OpenSSL::BN.rand
- *   BN.rand(bits [, fill [, odd]]) -> aBN
- */
-BIGNUM_RAND(rand)
-
 /*
- * Document-method: OpenSSL::BN.pseudo_rand
- *   BN.pseudo_rand(bits [, fill [, odd]]) -> aBN
- */
-BIGNUM_RAND(pseudo_rand)
-
-#define BIGNUM_RAND_RANGE(func)					\
-    static VALUE						\
-    ossl_bn_s_##func##_range(VALUE klass, VALUE range)		\
-    {								\
-	BIGNUM *bn = GetBNPtr(range), *result;			\
-	VALUE obj = NewBN(klass);				\
-	if (!(result = BN_new())) {				\
-	    ossl_raise(eBNError, NULL);				\
-	}							\
-	if (!BN_##func##_range(result, bn)) {			\
-	    BN_free(result);					\
-	    ossl_raise(eBNError, NULL);				\
-	}							\
-	SetBN(obj, result);					\
-	return obj;						\
-    }
-
-/*
- * Document-method: OpenSSL::BN.rand_range
  * call-seq:
- *   BN.rand_range(range) -> aBN
+ *    BN.rand(bits [, fill [, odd]]) -> aBN
+ *
+ * Generates a cryptographically strong pseudo-random number of +bits+.
  *
+ * See also the man page BN_rand(3).
  */
-BIGNUM_RAND_RANGE(rand)
+static VALUE
+ossl_bn_s_rand(int argc, VALUE *argv, VALUE klass)
+{
+    BIGNUM *result;
+    int bottom = 0, top = 0, b;
+    VALUE bits, fill, odd, obj;
+
+    switch (rb_scan_args(argc, argv, "12", &bits, &fill, &odd)) {
+      case 3:
+        bottom = (odd == Qtrue) ? 1 : 0;
+        /* FALLTHROUGH */
+      case 2:
+        top = NUM2INT(fill);
+    }
+    b = NUM2INT(bits);
+    obj = NewBN(klass);
+    if (!(result = BN_new())) {
+        ossl_raise(eBNError, "BN_new");
+    }
+    if (BN_rand(result, b, top, bottom) <= 0) {
+        BN_free(result);
+        ossl_raise(eBNError, "BN_rand");
+    }
+    SetBN(obj, result);
+    return obj;
+}
 
 /*
- * Document-method: OpenSSL::BN.pseudo_rand_range
  * call-seq:
- *   BN.pseudo_rand_range(range) -> aBN
+ *    BN.rand_range(range) -> aBN
+ *
+ * Generates a cryptographically strong pseudo-random number in the range
+ * 0...+range+.
  *
+ * See also the man page BN_rand_range(3).
  */
-BIGNUM_RAND_RANGE(pseudo_rand)
+static VALUE
+ossl_bn_s_rand_range(VALUE klass, VALUE range)
+{
+    BIGNUM *bn = GetBNPtr(range), *result;
+    VALUE obj = NewBN(klass);
+    if (!(result = BN_new()))
+        ossl_raise(eBNError, "BN_new");
+    if (BN_rand_range(result, bn) <= 0) {
+        BN_free(result);
+        ossl_raise(eBNError, "BN_rand_range");
+    }
+    SetBN(obj, result);
+    return obj;
+}
 
 /*
  * call-seq:
@@ -870,7 +954,17 @@
 static VALUE
 ossl_bn_uplus(VALUE self)
 {
-    return self;
+    VALUE obj;
+    BIGNUM *bn1, *bn2;
+
+    GetBN(self, bn1);
+    obj = NewBN(cBN);
+    bn2 = BN_dup(bn1);
+    if (!bn2)
+	ossl_raise(eBNError, "BN_dup");
+    SetBN(obj, bn2);
+
+    return obj;
 }
 
 /*
@@ -894,6 +988,24 @@
     return obj;
 }
 
+/*
+ * call-seq:
+ *   bn.abs -> aBN
+ */
+static VALUE
+ossl_bn_abs(VALUE self)
+{
+    BIGNUM *bn1;
+
+    GetBN(self, bn1);
+    if (BN_is_negative(bn1)) {
+        return ossl_bn_uminus(self);
+    }
+    else {
+        return ossl_bn_uplus(self);
+    }
+}
+
 #define BIGNUM_CMP(func)				\
     static VALUE					\
     ossl_bn_##func(VALUE self, VALUE other)		\
@@ -1002,34 +1114,29 @@
  *    bn.prime? => true | false
  *    bn.prime?(checks) => true | false
  *
- * Performs a Miller-Rabin probabilistic primality test with _checks_
- * iterations. If _checks_ is not specified, a number of iterations is used
- * that yields a false positive rate of at most 2^-80 for random input.
+ * Performs a Miller-Rabin probabilistic primality test for +bn+.
  *
- * === Parameters
- * * _checks_ - integer
+ * <b>+checks+ parameter is deprecated in version 3.0.</b> It has no effect.
  */
 static VALUE
 ossl_bn_is_prime(int argc, VALUE *argv, VALUE self)
 {
     BIGNUM *bn;
-    VALUE vchecks;
-    int checks = BN_prime_checks;
+    int ret;
 
-    if (rb_scan_args(argc, argv, "01", &vchecks) == 1) {
-	checks = NUM2INT(vchecks);
-    }
+    rb_check_arity(argc, 0, 1);
     GetBN(self, bn);
-    switch (BN_is_prime_ex(bn, checks, ossl_bn_ctx, NULL)) {
-    case 1:
-	return Qtrue;
-    case 0:
-	return Qfalse;
-    default:
-	ossl_raise(eBNError, NULL);
-    }
-    /* not reachable */
-    return Qnil;
+
+#ifdef HAVE_BN_CHECK_PRIME
+    ret = BN_check_prime(bn, ossl_bn_ctx, NULL);
+    if (ret < 0)
+        ossl_raise(eBNError, "BN_check_prime");
+#else
+    ret = BN_is_prime_fasttest_ex(bn, BN_prime_checks, ossl_bn_ctx, 1, NULL);
+    if (ret < 0)
+        ossl_raise(eBNError, "BN_is_prime_fasttest_ex");
+#endif
+    return ret ? Qtrue : Qfalse;
 }
 
 /*
@@ -1038,39 +1145,53 @@
  *    bn.prime_fasttest?(checks) => true | false
  *    bn.prime_fasttest?(checks, trial_div) => true | false
  *
- * Performs a Miller-Rabin primality test. This is same as #prime? except this
- * first attempts trial divisions with some small primes.
+ * Performs a Miller-Rabin probabilistic primality test for +bn+.
  *
- * === Parameters
- * * _checks_ - integer
- * * _trial_div_ - boolean
+ * <b>Deprecated in version 3.0.</b> Use #prime? instead.
+ *
+ * +checks+ and +trial_div+ parameters no longer have any effect.
  */
 static VALUE
 ossl_bn_is_prime_fasttest(int argc, VALUE *argv, VALUE self)
 {
+    rb_check_arity(argc, 0, 2);
+    return ossl_bn_is_prime(0, argv, self);
+}
+
+/*
+ * call-seq:
+ *    bn.get_flags(flags) => flags
+ *
+ * Returns the flags on the BN object.
+ * The argument is used as a bit mask.
+ *
+ * === Parameters
+ * * _flags_ - integer
+ */
+static VALUE
+ossl_bn_get_flags(VALUE self, VALUE arg)
+{
     BIGNUM *bn;
-    VALUE vchecks, vtrivdiv;
-    int checks = BN_prime_checks, do_trial_division = 1;
+    GetBN(self, bn);
 
-    rb_scan_args(argc, argv, "02", &vchecks, &vtrivdiv);
+    return INT2NUM(BN_get_flags(bn, NUM2INT(arg)));
+}
 
-    if (!NIL_P(vchecks)) {
-	checks = NUM2INT(vchecks);
-    }
+/*
+ * call-seq:
+ *    bn.set_flags(flags) => nil
+ *
+ * Enables the flags on the BN object.
+ * Currently, the flags argument can contain zero of OpenSSL::BN::CONSTTIME.
+ */
+static VALUE
+ossl_bn_set_flags(VALUE self, VALUE arg)
+{
+    BIGNUM *bn;
     GetBN(self, bn);
-    /* handle true/false */
-    if (vtrivdiv == Qfalse) {
-	do_trial_division = 0;
-    }
-    switch (BN_is_prime_fasttest_ex(bn, checks, ossl_bn_ctx, do_trial_division, NULL)) {
-    case 1:
-	return Qtrue;
-    case 0:
-	return Qfalse;
-    default:
-	ossl_raise(eBNError, NULL);
-    }
-    /* not reachable */
+
+    rb_check_frozen(self);
+    BN_set_flags(bn, NUM2INT(arg));
     return Qnil;
 }
 
@@ -1086,9 +1207,11 @@
     eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
 #endif
 
-    if (!(ossl_bn_ctx = BN_CTX_new())) {
-	ossl_raise(rb_eRuntimeError, "Cannot init BN_CTX");
-    }
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+    ossl_bn_ctx_key = rb_ractor_local_storage_ptr_newkey(&ossl_bn_ctx_key_type);
+#else
+    ossl_bn_ctx_get();
+#endif
 
     eBNError = rb_define_class_under(mOSSL, "BNError", eOSSLError);
 
@@ -1108,6 +1231,7 @@
 
     rb_define_method(cBN, "+@", ossl_bn_uplus, 0);
     rb_define_method(cBN, "-@", ossl_bn_uminus, 0);
+    rb_define_method(cBN, "abs", ossl_bn_abs, 0);
 
     rb_define_method(cBN, "+", ossl_bn_add, 1);
     rb_define_method(cBN, "-", ossl_bn_sub, 1);
@@ -1121,6 +1245,7 @@
     rb_define_method(cBN, "mod_sub", ossl_bn_mod_sub, 2);
     rb_define_method(cBN, "mod_mul", ossl_bn_mod_mul, 2);
     rb_define_method(cBN, "mod_sqr", ossl_bn_mod_sqr, 1);
+    rb_define_method(cBN, "mod_sqrt", ossl_bn_mod_sqrt, 1);
     rb_define_method(cBN, "**", ossl_bn_exp, 1);
     rb_define_method(cBN, "mod_exp", ossl_bn_mod_exp, 2);
     rb_define_method(cBN, "gcd", ossl_bn_gcd, 1);
@@ -1151,9 +1276,9 @@
      * get_word */
 
     rb_define_singleton_method(cBN, "rand", ossl_bn_s_rand, -1);
-    rb_define_singleton_method(cBN, "pseudo_rand", ossl_bn_s_pseudo_rand, -1);
     rb_define_singleton_method(cBN, "rand_range", ossl_bn_s_rand_range, 1);
-    rb_define_singleton_method(cBN, "pseudo_rand_range", ossl_bn_s_pseudo_rand_range, 1);
+    rb_define_alias(rb_singleton_class(cBN), "pseudo_rand", "rand");
+    rb_define_alias(rb_singleton_class(cBN), "pseudo_rand_range", "rand_range");
 
     rb_define_singleton_method(cBN, "generate_prime", ossl_bn_s_generate_prime, -1);
     rb_define_method(cBN, "prime?", ossl_bn_is_prime, -1);
@@ -1170,6 +1295,23 @@
     /* lshift1 - DON'T IMPL. */
     /* rshift1 - DON'T IMPL. */
 
+    rb_define_method(cBN, "get_flags", ossl_bn_get_flags, 1);
+    rb_define_method(cBN, "set_flags", ossl_bn_set_flags, 1);
+
+#ifdef BN_FLG_CONSTTIME
+    rb_define_const(cBN, "CONSTTIME", INT2NUM(BN_FLG_CONSTTIME));
+#endif
+    /* BN_FLG_MALLOCED and BN_FLG_STATIC_DATA seems for C programming.
+     * Allowing them leads to memory leak.
+     * So, for now, they are not exported
+#ifdef BN_FLG_MALLOCED
+    rb_define_const(cBN, "MALLOCED", INT2NUM(BN_FLG_MALLOCED));
+#endif
+#ifdef BN_FLG_STATIC_DATA
+    rb_define_const(cBN, "STATIC_DATA", INT2NUM(BN_FLG_STATIC_DATA));
+#endif
+    */
+
     /*
      * bn2bin
      * bin2bn
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_bn.h ruby-2.5.9/ext/openssl/ossl_bn.h
--- ruby-2.5.9.orig/ext/openssl/ossl_bn.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_bn.h	2025-01-29 19:08:02.377421257 +0100
@@ -5,15 +5,15 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_BN_H_)
 #define _OSSL_BN_H_
 
 extern VALUE cBN;
-extern VALUE eBNError;
 
-extern BN_CTX *ossl_bn_ctx;
+BN_CTX *ossl_bn_ctx_get(void);
+#define ossl_bn_ctx ossl_bn_ctx_get()
 
 #define GetBNPtr(obj) ossl_bn_value_ptr(&(obj))
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl.c ruby-2.5.9/ext/openssl/ossl.c
--- ruby-2.5.9.orig/ext/openssl/ossl.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl.c	2025-01-29 19:08:02.377182194 +0100
@@ -5,17 +5,23 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 #include <stdarg.h> /* for ossl_raise */
-#include <ruby/thread_native.h> /* for OpenSSL < 1.1.0 locks */
+
+/* OpenSSL >= 1.1.0 and LibreSSL >= 2.9.0 */
+#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER >= 0x10100000
+# define HAVE_OPENSSL_110_THREADING_API
+#else
+# include <ruby/thread_native.h>
+#endif
 
 /*
  * Data Conversion
  */
 #define OSSL_IMPL_ARY2SK(name, type, expected_class, dup)	\
-STACK_OF(type) *						\
+VALUE								\
 ossl_##name##_ary2sk0(VALUE ary)				\
 {								\
     STACK_OF(type) *sk;						\
@@ -37,7 +43,7 @@
 	x = dup(val); /* NEED TO DUP */				\
 	sk_##type##_push(sk, x);				\
     }								\
-    return sk;							\
+    return (VALUE)sk;						\
 }								\
 								\
 STACK_OF(type) *						\
@@ -201,7 +207,7 @@
 
     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.
 	 */
@@ -262,31 +268,32 @@
 /*
  * Errors
  */
-static VALUE
-ossl_make_error(VALUE exc, const char *fmt, va_list args)
+VALUE
+ossl_make_error(VALUE exc, VALUE str)
 {
-    VALUE str = Qnil;
     unsigned long e;
+    const char *data;
+    int flags;
 
-    if (fmt) {
-	str = rb_vsprintf(fmt, args);
-    }
-    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
@@ -294,37 +301,48 @@
 {
     va_list args;
     VALUE err;
-    va_start(args, fmt);
-    err = ossl_make_error(exc, fmt, args);
-    va_end(args);
-    rb_exc_raise(err);
+
+    if (fmt) {
+	va_start(args, fmt);
+	err = rb_vsprintf(fmt, args);
+	va_end(args);
+    }
+    else {
+	err = Qnil;
+    }
+
+    rb_exc_raise(ossl_make_error(exc, err));
 }
 
 void
 ossl_clear_error(void)
 {
     if (dOSSL == Qtrue) {
-	unsigned long e;
-	const char *file, *data, *errstr;
-	int line, flags;
-
-	while ((e = ERR_get_error_line_data(&file, &line, &data, &flags))) {
-	    errstr = ERR_error_string(e, NULL);
-	    if (!errstr)
-		errstr = "(null)";
-
-	    if (flags & ERR_TXT_STRING) {
-		if (!data)
-		    data = "(null)";
-		rb_warn("error on stack: %s (%s)", errstr, data);
-	    }
-	    else {
-		rb_warn("error on stack: %s", errstr);
-	    }
-	}
+        unsigned long e;
+        const char *file, *data, *func, *lib, *reason;
+        char append[256] = "";
+        int line, flags;
+
+#ifdef HAVE_ERR_GET_ERROR_ALL
+        while ((e = ERR_get_error_all(&file, &line, &func, &data, &flags))) {
+#else
+        while ((e = ERR_get_error_line_data(&file, &line, &data, &flags))) {
+            func = ERR_func_error_string(e);
+#endif
+            lib = ERR_lib_error_string(e);
+            reason = ERR_reason_error_string(e);
+
+            if (flags & ERR_TXT_STRING) {
+                if (!data)
+                    data = "(null)";
+                snprintf(append, sizeof(append), " (%s)", data);
+            }
+            rb_warn("error on stack: error:%08lX:%s:%s:%s%s", e, lib ? lib : "",
+                    func ? func : "", reason ? reason : "", append);
+        }
     }
     else {
-	ERR_clear_error();
+        ERR_clear_error();
     }
 }
 
@@ -337,8 +355,8 @@
  * Any errors you see here are probably due to a bug in Ruby's OpenSSL
  * implementation.
  */
-VALUE
-ossl_get_errors(void)
+static VALUE
+ossl_get_errors(VALUE _)
 {
     VALUE ary;
     long e;
@@ -356,22 +374,6 @@
  */
 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
@@ -386,7 +388,7 @@
  * call-seq:
  *   OpenSSL.debug = boolean -> boolean
  *
- * Turns on or off debug mode. With debug mode, all erros 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
@@ -398,14 +400,18 @@
 }
 
 /*
- * call-seq
+ * call-seq:
  *   OpenSSL.fips_mode -> true | false
  */
 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;
@@ -429,8 +435,18 @@
 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 */
@@ -447,72 +463,6 @@
 #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
-
-    BN_CTX_free(ossl_bn_ctx);
-    ossl_bn_ctx = NULL;
-
-#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
@@ -605,6 +555,35 @@
 #endif /* !HAVE_OPENSSL_110_THREADING_API */
 
 /*
+ * call-seq:
+ *   OpenSSL.fixed_length_secure_compare(string, string) -> boolean
+ *
+ * Constant time memory comparison for fixed length strings, such as results
+ * 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
+ * raised.
+ */
+static VALUE
+ossl_crypto_fixed_length_secure_compare(VALUE dummy, VALUE str1, VALUE str2)
+{
+    const unsigned char *p1 = (const unsigned char *)StringValuePtr(str1);
+    const unsigned char *p2 = (const unsigned char *)StringValuePtr(str2);
+    long len1 = RSTRING_LEN(str1);
+    long len2 = RSTRING_LEN(str2);
+
+    if (len1 != len2) {
+        ossl_raise(rb_eArgError, "inputs must be of equal length");
+    }
+
+    switch (CRYPTO_memcmp(p1, p2, len1)) {
+        case 0:	return Qtrue;
+        default: return Qfalse;
+    }
+}
+
+/*
  * OpenSSL provides SSL, TLS and general purpose cryptography.  It wraps the
  * OpenSSL[https://www.openssl.org/] library.
  *
@@ -626,23 +605,21 @@
  *
  *   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-128-CBC'
- *   pass_phrase = 'my secure pass phrase goes here'
+ *   cipher = OpenSSL::Cipher.new 'aes-256-cbc'
+ *   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.
  *
@@ -650,25 +627,25 @@
  *
  * A key can also be loaded from a file.
  *
- *   key2 = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
+ *   key2 = OpenSSL::PKey.read File.read 'private_key.pem'
  *   key2.public? # => true
  *   key2.private? # => true
  *
  * or
  *
- *   key3 = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
+ *   key3 = OpenSSL::PKey.read File.read 'public_key.pem'
  *   key3.public? # => true
  *   key3.private? # => false
  *
  * === 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::RSA.new key4_pem, pass_phrase
+ *   password = 'my secure password goes here'
+ *   key4 = OpenSSL::PKey.read key4_pem, password
  *
  * == RSA Encryption
  *
@@ -710,16 +687,14 @@
  * To sign a document, a cryptographically secure hash of the document is
  * computed first, which is then signed using the private key.
  *
- *   digest = OpenSSL::Digest::SHA256.new
- *   signature = key.sign digest, document
+ *   signature = key.sign 'SHA256', document
  *
  * To validate the signature, again a hash of the document is computed and
  * the signature is decrypted using the public key. The result is then
  * compared to the hash just computed, if they are equal the signature was
  * valid.
  *
- *   digest = OpenSSL::Digest::SHA256.new
- *   if key.verify digest, signature, document
+ *   if key.verify 'SHA256', signature, document
  *     puts 'Valid'
  *   else
  *     puts 'Invalid'
@@ -745,7 +720,7 @@
  * using PBKDF2. PKCS #5 v2.0 recommends at least 8 bytes for the salt,
  * the number of iterations largely depends on the hardware being used.
  *
- *   cipher = OpenSSL::Cipher.new 'AES-128-CBC'
+ *   cipher = OpenSSL::Cipher.new 'aes-256-cbc'
  *   cipher.encrypt
  *   iv = cipher.random_iv
  *
@@ -753,7 +728,7 @@
  *   salt = OpenSSL::Random.random_bytes 16
  *   iter = 20000
  *   key_len = cipher.key_len
- *   digest = OpenSSL::Digest::SHA256.new
+ *   digest = OpenSSL::Digest.new('SHA256')
  *
  *   key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
  *   cipher.key = key
@@ -768,7 +743,7 @@
  * Use the same steps as before to derive the symmetric AES key, this time
  * setting the Cipher up for decryption.
  *
- *   cipher = OpenSSL::Cipher.new 'AES-128-CBC'
+ *   cipher = OpenSSL::Cipher.new 'aes-256-cbc'
  *   cipher.decrypt
  *   cipher.iv = iv # the one generated with #random_iv
  *
@@ -776,7 +751,7 @@
  *   salt = ... # the one generated above
  *   iter = 20000
  *   key_len = cipher.key_len
- *   digest = OpenSSL::Digest::SHA256.new
+ *   digest = OpenSSL::Digest.new('SHA256')
  *
  *   key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
  *   cipher.key = key
@@ -786,45 +761,6 @@
  *   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-128-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-128-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
@@ -833,7 +769,7 @@
  * signature.
  *
  *   key = OpenSSL::PKey::RSA.new 2048
- *   name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example'
+ *   name = OpenSSL::X509::Name.parse '/CN=nobody/DC=example'
  *
  *   cert = OpenSSL::X509::Certificate.new
  *   cert.version = 2
@@ -872,7 +808,7 @@
  * certificate.
  *
  *   cert.issuer = name
- *   cert.sign key, OpenSSL::Digest::SHA1.new
+ *   cert.sign key, OpenSSL::Digest.new('SHA1')
  *
  *   open 'certificate.pem', 'w' do |io| io.write cert.to_pem end
  *
@@ -902,12 +838,12 @@
  * 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-128-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
@@ -915,7 +851,7 @@
  * A CA certificate is created the same way we created a certificate above, but
  * with different extensions.
  *
- *   ca_name = OpenSSL::X509::Name.parse 'CN=ca/DC=example'
+ *   ca_name = OpenSSL::X509::Name.parse '/CN=ca/DC=example'
  *
  *   ca_cert = OpenSSL::X509::Certificate.new
  *   ca_cert.serial = 0
@@ -948,7 +884,7 @@
  *
  * Root CA certificates are self-signed.
  *
- *   ca_cert.sign ca_key, OpenSSL::Digest::SHA1.new
+ *   ca_cert.sign ca_key, OpenSSL::Digest.new('SHA1')
  *
  * The CA certificate is saved to disk so it may be distributed to all the
  * users of the keys this CA will sign.
@@ -966,7 +902,7 @@
  *   csr.version = 0
  *   csr.subject = name
  *   csr.public_key = key.public_key
- *   csr.sign key, OpenSSL::Digest::SHA1.new
+ *   csr.sign key, OpenSSL::Digest.new('SHA1')
  *
  * A CSR is saved to disk and sent to the CA for signing.
  *
@@ -1010,7 +946,7 @@
  *   csr_cert.add_extension \
  *     extension_factory.create_extension('subjectKeyIdentifier', 'hash')
  *
- *   csr_cert.sign ca_key, OpenSSL::Digest::SHA1.new
+ *   csr_cert.sign ca_key, OpenSSL::Digest.new('SHA1')
  *
  *   open 'csr_cert.pem', 'w' do |io|
  *     io.write csr_cert.to_pem
@@ -1042,13 +978,13 @@
  *   loop do
  *     ssl_connection = ssl_server.accept
  *
- *     data = connection.gets
+ *     data = ssl_connection.gets
  *
  *     response = "I got #{data.dump}"
  *     puts response
  *
- *     connection.puts "I got #{data.dump}"
- *     connection.close
+ *     ssl_connection.puts "I got #{data.dump}"
+ *     ssl_connection.close
  *   end
  *
  * === SSL client
@@ -1099,6 +1035,10 @@
 void
 Init_openssl(void)
 {
+#ifdef HAVE_RB_EXT_RACTOR_SAFE
+    rb_ext_ractor_safe(true);
+#endif
+
 #undef rb_intern
     /*
      * Init timezone info
@@ -1123,13 +1063,9 @@
     /*
      * Init main module
      */
-    mOSSL = rb_define_module("OpenSSL");
     rb_global_variable(&mOSSL);
-
-    /*
-     * OpenSSL ruby extension version
-     */
-    rb_define_const(mOSSL, "VERSION", rb_str_new2(OSSL_VERSION));
+    mOSSL = rb_define_module("OpenSSL");
+    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
@@ -1147,15 +1083,35 @@
 
     /*
      * 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
@@ -1165,12 +1121,12 @@
     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
@@ -1194,56 +1150,22 @@
     /*
      * Init components
      */
+    Init_ossl_asn1();
     Init_ossl_bn();
     Init_ossl_cipher();
     Init_ossl_config();
     Init_ossl_digest();
+    Init_ossl_engine();
     Init_ossl_hmac();
+    Init_ossl_kdf();
     Init_ossl_ns_spki();
+    Init_ossl_ocsp();
     Init_ossl_pkcs12();
     Init_ossl_pkcs7();
     Init_ossl_pkey();
+    Init_ossl_provider();
     Init_ossl_rand();
     Init_ossl_ssl();
+    Init_ossl_ts();
     Init_ossl_x509();
-    Init_ossl_ocsp();
-    Init_ossl_engine();
-    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 -ruN ruby-2.5.9.orig/ext/openssl/ossl_cipher.c ruby-2.5.9/ext/openssl/ossl_cipher.c
--- ruby-2.5.9.orig/ext/openssl/ossl_cipher.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_cipher.c	2025-01-29 19:08:02.377421257 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -30,8 +30,8 @@
 /*
  * Classes
  */
-VALUE cCipher;
-VALUE eCipherError;
+static VALUE cCipher;
+static VALUE eCipherError;
 static ID id_auth_tag_len, id_key_set;
 
 static VALUE ossl_cipher_alloc(VALUE klass);
@@ -42,7 +42,7 @@
     {
 	0, ossl_cipher_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -104,7 +104,7 @@
  *  call-seq:
  *     Cipher.new(string) -> cipher
  *
- *  The string must be a valid cipher name like "AES-128-CBC" or "3DES".
+ *  The string must contain a valid cipher name like "aes-256-cbc".
  *
  *  A list of cipher names is available by calling OpenSSL::Cipher.ciphers.
  */
@@ -149,11 +149,11 @@
     return self;
 }
 
-static void*
-add_cipher_name_to_ary(const OBJ_NAME *name, VALUE ary)
+static void
+add_cipher_name_to_ary(const OBJ_NAME *name, void *arg)
 {
+    VALUE ary = (VALUE)arg;
     rb_ary_push(ary, rb_str_new2(name->name));
-    return NULL;
 }
 
 /*
@@ -169,7 +169,7 @@
 
     ary = rb_ary_new();
     OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
-                    (void(*)(const OBJ_NAME*,void*))add_cipher_name_to_ary,
+                    add_cipher_name_to_ary,
                     (void*)ary);
 
     return ary;
@@ -237,8 +237,7 @@
 	ossl_raise(eCipherError, NULL);
     }
 
-    if (p_key)
-	rb_ivar_set(self, id_key_set, Qtrue);
+    rb_ivar_set(self, id_key_set, p_key ? Qtrue : Qfalse);
 
     return self;
 }
@@ -385,25 +384,39 @@
 
     StringValue(data);
     in = (unsigned char *)RSTRING_PTR(data);
-    if ((in_len = RSTRING_LEN(data)) == 0)
-        ossl_raise(rb_eArgError, "data must not be empty");
+    in_len = RSTRING_LEN(data);
     GetCipher(self, ctx);
-    out_len = in_len+EVP_CIPHER_CTX_block_size(ctx);
-    if (out_len <= 0) {
+
+    /*
+     * As of OpenSSL 3.2, there is no reliable way to determine the required
+     * output buffer size for arbitrary cipher modes.
+     * https://github.com/openssl/openssl/issues/22628
+     *
+     * in_len+block_size is usually sufficient, but AES key wrap with padding
+     * ciphers require in_len+15 even though they have a block size of 8 bytes.
+     *
+     * Using EVP_MAX_BLOCK_LENGTH (32) as a safe upper bound for ciphers
+     * currently implemented in OpenSSL, but this can change in the future.
+     */
+    if (in_len > LONG_MAX - EVP_MAX_BLOCK_LENGTH) {
 	ossl_raise(rb_eRangeError,
 		   "data too big to make output buffer: %ld bytes", in_len);
     }
+    out_len = in_len + EVP_MAX_BLOCK_LENGTH;
 
     if (NIL_P(str)) {
         str = rb_str_new(0, out_len);
     } else {
         StringValue(str);
-        rb_str_resize(str, out_len);
+        if ((long)rb_str_capacity(str) >= out_len)
+            rb_str_modify(str);
+        else
+            rb_str_modify_expand(str, out_len - RSTRING_LEN(str));
     }
 
     if (!ossl_cipher_update_long(ctx, (unsigned char *)RSTRING_PTR(str), &out_len, in, in_len))
 	ossl_raise(eCipherError, NULL);
-    assert(out_len < RSTRING_LEN(str));
+    assert(out_len <= RSTRING_LEN(str));
     rb_str_set_len(str, out_len);
 
     return str;
@@ -444,8 +457,8 @@
  *  call-seq:
  *     cipher.name -> string
  *
- *  Returns the name of the cipher which may differ slightly from the original
- *  name provided.
+ *  Returns the short name of the cipher which may differ slightly from the
+ *  original name provided.
  */
 static VALUE
 ossl_cipher_name(VALUE self)
@@ -815,6 +828,31 @@
 }
 
 /*
+ *  call-seq:
+ *     cipher.ccm_data_len = integer -> integer
+ *
+ *  Sets the length of the plaintext / ciphertext message that will be
+ *  processed in CCM mode. Make sure to call this method after #key= and
+ *  #iv= have been set, and before #auth_data=.
+ *
+ *  Only call this method after calling Cipher#encrypt or Cipher#decrypt.
+ */
+static VALUE
+ossl_cipher_set_ccm_data_len(VALUE self, VALUE data_len)
+{
+    int in_len, out_len;
+    EVP_CIPHER_CTX *ctx;
+
+    in_len = NUM2INT(data_len);
+
+    GetCipher(self, ctx);
+    if (EVP_CipherUpdate(ctx, NULL, &out_len, NULL, in_len) != 1)
+        ossl_raise(eCipherError, NULL);
+
+    return data_len;
+}
+
+/*
  * INIT
  */
 void
@@ -850,23 +888,7 @@
      * individual components name, key length and mode. Either all uppercase
      * or all lowercase strings may be used, for example:
      *
-     *  cipher = OpenSSL::Cipher.new('AES-128-CBC')
-     *
-     * For each algorithm supported, there is a class defined under the
-     * Cipher class that goes by the name of the cipher, e.g. to obtain an
-     * instance of AES, you could also use
-     *
-     *   # these are equivalent
-     *   cipher = OpenSSL::Cipher::AES.new(128, :CBC)
-     *   cipher = OpenSSL::Cipher::AES.new(128, 'CBC')
-     *   cipher = OpenSSL::Cipher::AES.new('128-CBC')
-     *
-     * Finally, due to its wide-spread use, there are also extra classes
-     * defined for the different key sizes of AES
-     *
-     *   cipher = OpenSSL::Cipher::AES128.new(:CBC)
-     *   cipher = OpenSSL::Cipher::AES192.new(:CBC)
-     *   cipher = OpenSSL::Cipher::AES256.new(:CBC)
+     *  cipher = OpenSSL::Cipher.new('aes-128-cbc')
      *
      * === Choosing either encryption or decryption mode
      *
@@ -896,7 +918,7 @@
      * without processing the password further. A simple and secure way to
      * create a key for a particular Cipher is
      *
-     *  cipher = OpenSSL::AES256.new(:CFB)
+     *  cipher = OpenSSL::Cipher.new('aes-256-cfb')
      *  cipher.encrypt
      *  key = cipher.random_key # also sets the generated key on the Cipher
      *
@@ -964,14 +986,14 @@
      *
      *   data = "Very, very confidential data"
      *
-     *   cipher = OpenSSL::Cipher::AES.new(128, :CBC)
+     *   cipher = OpenSSL::Cipher.new('aes-128-cbc')
      *   cipher.encrypt
      *   key = cipher.random_key
      *   iv = cipher.random_iv
      *
      *   encrypted = cipher.update(data) + cipher.final
      *   ...
-     *   decipher = OpenSSL::Cipher::AES.new(128, :CBC)
+     *   decipher = OpenSSL::Cipher.new('aes-128-cbc')
      *   decipher.decrypt
      *   decipher.key = key
      *   decipher.iv = iv
@@ -1007,7 +1029,7 @@
      * not to reuse the _key_ and _nonce_ pair. Reusing an nonce ruins the
      * security guarantees of GCM mode.
      *
-     *   cipher = OpenSSL::Cipher::AES.new(128, :GCM).encrypt
+     *   cipher = OpenSSL::Cipher.new('aes-128-gcm').encrypt
      *   cipher.key = key
      *   cipher.iv = nonce
      *   cipher.auth_data = auth_data
@@ -1023,7 +1045,7 @@
      * ciphertext with a probability of 1/256.
      *
      *   raise "tag is truncated!" unless tag.bytesize == 16
-     *   decipher = OpenSSL::Cipher::AES.new(128, :GCM).decrypt
+     *   decipher = OpenSSL::Cipher.new('aes-128-gcm').decrypt
      *   decipher.key = key
      *   decipher.iv = nonce
      *   decipher.auth_tag = tag
@@ -1060,6 +1082,7 @@
     rb_define_method(cCipher, "iv_len", ossl_cipher_iv_length, 0);
     rb_define_method(cCipher, "block_size", ossl_cipher_block_size, 0);
     rb_define_method(cCipher, "padding=", ossl_cipher_set_padding, 1);
+    rb_define_method(cCipher, "ccm_data_len=", ossl_cipher_set_ccm_data_len, 1);
 
     id_auth_tag_len = rb_intern_const("auth_tag_len");
     id_key_set = rb_intern_const("key_set");
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_cipher.h ruby-2.5.9/ext/openssl/ossl_cipher.h
--- ruby-2.5.9.orig/ext/openssl/ossl_cipher.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_cipher.h	2025-01-29 19:08:02.378421281 +0100
@@ -5,14 +5,11 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_CIPHER_H_)
 #define _OSSL_CIPHER_H_
 
-extern VALUE cCipher;
-extern VALUE eCipherError;
-
 const EVP_CIPHER *ossl_evp_get_cipherbyname(VALUE);
 VALUE ossl_cipher_new(const EVP_CIPHER *);
 void Init_ossl_cipher(void);
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_config.c ruby-2.5.9/ext/openssl/ossl_config.c
--- ruby-2.5.9.orig/ext/openssl/ossl_config.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_config.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,85 +5,457 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
+static VALUE cConfig, eConfigError;
+
+static void
+nconf_free(void *conf)
+{
+    NCONF_free(conf);
+}
+
+static const rb_data_type_t ossl_config_type = {
+    "OpenSSL/CONF",
+    {
+        0, nconf_free,
+    },
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
+};
+
+CONF *
+GetConfig(VALUE obj)
+{
+    CONF *conf;
+
+    TypedData_Get_Struct(obj, CONF, &ossl_config_type, conf);
+    if (!conf)
+        rb_raise(rb_eRuntimeError, "CONF is not initialized");
+    return conf;
+}
+
+static VALUE
+config_s_alloc(VALUE klass)
+{
+    VALUE obj;
+    CONF *conf;
+
+    obj = TypedData_Wrap_Struct(klass, &ossl_config_type, 0);
+    conf = NCONF_new(NULL);
+    if (!conf)
+        ossl_raise(eConfigError, "NCONF_new");
+    RTYPEDDATA_DATA(obj) = conf;
+    return obj;
+}
+
+static void
+config_load_bio(CONF *conf, BIO *bio)
+{
+    long eline = -1;
+
+    if (!NCONF_load_bio(conf, bio, &eline)) {
+        BIO_free(bio);
+        if (eline <= 0)
+            ossl_raise(eConfigError, "wrong config format");
+        else
+            ossl_raise(eConfigError, "error in line %ld", eline);
+    }
+    BIO_free(bio);
+
+    /*
+     * Clear the error queue even if it is parsed successfully.
+     * Particularly, when the .include directive refers to a non-existent file,
+     * it is only reported in the error queue.
+     */
+    ossl_clear_error();
+}
 
 /*
- * Classes
- */
-VALUE cConfig;
-/* Document-class: OpenSSL::ConfigError
+ * call-seq:
+ *    Config.parse(string) -> OpenSSL::Config
  *
- * General error for openssl library configuration files. Including formatting,
- * parsing errors, etc.
+ * Parses a given _string_ as a blob that contains configuration for OpenSSL.
  */
-VALUE eConfigError;
+static VALUE
+config_s_parse(VALUE klass, VALUE str)
+{
+    VALUE obj = config_s_alloc(klass);
+    CONF *conf = GetConfig(obj);
+    BIO *bio;
+
+    bio = ossl_obj2bio(&str);
+    config_load_bio(conf, bio); /* Consumes BIO */
+    rb_obj_freeze(obj);
+    return obj;
+}
+
+static VALUE config_get_sections(VALUE self);
+static VALUE config_get_section(VALUE self, VALUE section);
 
 /*
- * Public
+ * call-seq:
+ *    Config.parse_config(io) -> hash
+ *
+ * Parses the configuration data read from _io_ and returns the whole content
+ * as a Hash.
  */
+static VALUE
+config_s_parse_config(VALUE klass, VALUE io)
+{
+    VALUE obj, sections, ret;
+    long i;
+
+    obj = config_s_parse(klass, io);
+    sections = config_get_sections(obj);
+    ret = rb_hash_new();
+    for (i = 0; i < RARRAY_LEN(sections); i++) {
+        VALUE section = rb_ary_entry(sections, i);
+        rb_hash_aset(ret, section, config_get_section(obj, section));
+    }
+    return ret;
+}
 
 /*
- * DupConfigPtr is a public C-level function for getting OpenSSL CONF struct
- * from an OpenSSL::Config(eConfig) instance.  We decided to implement
- * OpenSSL::Config in Ruby level but we need to pass native CONF struct for
- * some OpenSSL features such as X509V3_EXT_*.
+ * call-seq:
+ *    Config.new(filename) -> OpenSSL::Config
+ *
+ * Creates an instance of OpenSSL::Config from the content of the file
+ * specified by _filename_.
+ *
+ * This can be used in contexts like OpenSSL::X509::ExtensionFactory.config=
+ *
+ * This can raise IO exceptions based on the access, or availability of the
+ * file. A ConfigError exception may be raised depending on the validity of
+ * the data being configured.
  */
-CONF *
-DupConfigPtr(VALUE obj)
+static VALUE
+config_initialize(int argc, VALUE *argv, VALUE self)
 {
-    CONF *conf;
+    CONF *conf = GetConfig(self);
+    VALUE filename;
+
+    /* 0-arguments call has no use-case, but is kept for compatibility */
+    rb_scan_args(argc, argv, "01", &filename);
+    rb_check_frozen(self);
+    if (!NIL_P(filename)) {
+        BIO *bio = BIO_new_file(StringValueCStr(filename), "rb");
+        if (!bio)
+            ossl_raise(eConfigError, "BIO_new_file");
+        config_load_bio(conf, bio); /* Consumes BIO */
+    }
+    rb_obj_freeze(self);
+    return self;
+}
+
+static VALUE
+config_initialize_copy(VALUE self, VALUE other)
+{
+    CONF *conf = GetConfig(self);
     VALUE str;
     BIO *bio;
-    long eline = -1;
 
-    OSSL_Check_Kind(obj, cConfig);
-    str = rb_funcall(obj, rb_intern("to_s"), 0);
+    str = rb_funcall(other, rb_intern("to_s"), 0);
+    rb_check_frozen(self);
     bio = ossl_obj2bio(&str);
-    conf = NCONF_new(NULL);
-    if(!conf){
-	BIO_free(bio);
-	ossl_raise(eConfigError, NULL);
-    }
-    if(!NCONF_load_bio(conf, bio, &eline)){
-	BIO_free(bio);
-	NCONF_free(conf);
-	if (eline <= 0)
-	    ossl_raise(eConfigError, "wrong config format");
-	else
-	    ossl_raise(eConfigError, "error in line %d", eline);
+    config_load_bio(conf, bio); /* Consumes BIO */
+    rb_obj_freeze(self);
+    return self;
+}
+
+/*
+ * call-seq:
+ *    config.get_value(section, key) -> string
+ *
+ * Gets the value of _key_ from the given _section_.
+ *
+ * Given the following configurating file being loaded:
+ *
+ *   config = OpenSSL::Config.load('foo.cnf')
+ *     #=> #<OpenSSL::Config sections=["default"]>
+ *   puts config.to_s
+ *     #=> [ default ]
+ *     #   foo=bar
+ *
+ * You can get a specific value from the config if you know the _section_
+ * and _key_ like so:
+ *
+ *   config.get_value('default','foo')
+ *     #=> "bar"
+ */
+static VALUE
+config_get_value(VALUE self, VALUE section, VALUE key)
+{
+    CONF *conf = GetConfig(self);
+    const char *str, *sectionp;
+
+    StringValueCStr(section);
+    StringValueCStr(key);
+    /* For compatibility; NULL means "default". */
+    sectionp = RSTRING_LEN(section) ? RSTRING_PTR(section) : NULL;
+    str = NCONF_get_string(conf, sectionp, RSTRING_PTR(key));
+    if (!str) {
+        ossl_clear_error();
+        return Qnil;
     }
-    BIO_free(bio);
+    return rb_str_new_cstr(str);
+}
 
-    return conf;
+/*
+ * call-seq:
+ *    config[section] -> hash
+ *
+ * Gets all key-value pairs in a specific _section_ from the current
+ * configuration.
+ *
+ * Given the following configurating file being loaded:
+ *
+ *   config = OpenSSL::Config.load('foo.cnf')
+ *     #=> #<OpenSSL::Config sections=["default"]>
+ *   puts config.to_s
+ *     #=> [ default ]
+ *     #   foo=bar
+ *
+ * You can get a hash of the specific section like so:
+ *
+ *   config['default']
+ *     #=> {"foo"=>"bar"}
+ *
+ */
+static VALUE
+config_get_section(VALUE self, VALUE section)
+{
+    CONF *conf = GetConfig(self);
+    STACK_OF(CONF_VALUE) *sk;
+    int i, entries;
+    VALUE hash;
+
+    hash = rb_hash_new();
+    StringValueCStr(section);
+    if (!(sk = NCONF_get_section(conf, RSTRING_PTR(section)))) {
+        ossl_clear_error();
+        return hash;
+    }
+    entries = sk_CONF_VALUE_num(sk);
+    for (i = 0; i < entries; i++) {
+        CONF_VALUE *entry = sk_CONF_VALUE_value(sk, i);
+        rb_hash_aset(hash, rb_str_new_cstr(entry->name),
+                     rb_str_new_cstr(entry->value));
+    }
+    return hash;
 }
 
-/* Document-const: DEFAULT_CONFIG_FILE
+static void
+get_conf_section_doall_arg(CONF_VALUE *cv, VALUE *aryp)
+{
+    if (cv->name)
+        return;
+    rb_ary_push(*aryp, rb_str_new_cstr(cv->section));
+}
+
+/* IMPLEMENT_LHASH_DOALL_ARG_CONST() requires >= OpenSSL 1.1.0 */
+static IMPLEMENT_LHASH_DOALL_ARG_FN(get_conf_section, CONF_VALUE, VALUE)
+
+/*
+ * call-seq:
+ *    config.sections -> array of string
  *
- * The default system configuration file for openssl
+ * Get the names of all sections in the current configuration.
  */
+static VALUE
+config_get_sections(VALUE self)
+{
+    CONF *conf = GetConfig(self);
+    VALUE ary;
+
+    ary = rb_ary_new();
+    lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(get_conf_section),
+                 &ary);
+    return ary;
+}
+
+static void
+dump_conf_value_doall_arg(CONF_VALUE *cv, VALUE *strp)
+{
+    VALUE str = *strp;
+    STACK_OF(CONF_VALUE) *sk;
+    int i, num;
+
+    if (cv->name)
+        return;
+    sk = (STACK_OF(CONF_VALUE) *)cv->value;
+    num = sk_CONF_VALUE_num(sk);
+    rb_str_cat_cstr(str, "[ ");
+    rb_str_cat_cstr(str, cv->section);
+    rb_str_cat_cstr(str, " ]\n");
+    for (i = 0; i < num; i++){
+        CONF_VALUE *v = sk_CONF_VALUE_value(sk, i);
+        rb_str_cat_cstr(str, v->name ? v->name : "None");
+        rb_str_cat_cstr(str, "=");
+        rb_str_cat_cstr(str, v->value ? v->value : "None");
+        rb_str_cat_cstr(str, "\n");
+    }
+    rb_str_cat_cstr(str, "\n");
+}
+
+static IMPLEMENT_LHASH_DOALL_ARG_FN(dump_conf_value, CONF_VALUE, VALUE)
 
 /*
- * INIT
+ * call-seq:
+ *    config.to_s -> string
+ *
+ *
+ * Gets the parsable form of the current configuration.
+ *
+ * Given the following configuration file being loaded:
+ *
+ *   config = OpenSSL::Config.load('baz.cnf')
+ *     #=> #<OpenSSL::Config sections=["default"]>
+ *   puts config.to_s
+ *     #=> [ default ]
+ *     #   foo=bar
+ *     #   baz=buz
+ *
+ * You can get the serialized configuration using #to_s and then parse
+ * it later:
+ *
+ *   serialized_config = config.to_s
+ *   # much later...
+ *   new_config = OpenSSL::Config.parse(serialized_config)
+ *     #=> #<OpenSSL::Config sections=["default"]>
+ *   puts new_config
+ *     #=> [ default ]
+ *         foo=bar
+ *         baz=buz
  */
+static VALUE
+config_to_s(VALUE self)
+{
+    CONF *conf = GetConfig(self);
+    VALUE str;
+
+    str = rb_str_new(NULL, 0);
+    lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(dump_conf_value),
+                 &str);
+    return str;
+}
+
+static void
+each_conf_value_doall_arg(CONF_VALUE *cv, void *unused)
+{
+    STACK_OF(CONF_VALUE) *sk;
+    VALUE section;
+    int i, num;
+
+    if (cv->name)
+        return;
+    sk = (STACK_OF(CONF_VALUE) *)cv->value;
+    num = sk_CONF_VALUE_num(sk);
+    section = rb_str_new_cstr(cv->section);
+    for (i = 0; i < num; i++){
+        CONF_VALUE *v = sk_CONF_VALUE_value(sk, i);
+        VALUE name = v->name ? rb_str_new_cstr(v->name) : Qnil;
+        VALUE value = v->value ? rb_str_new_cstr(v->value) : Qnil;
+        rb_yield(rb_ary_new3(3, section, name, value));
+    }
+}
+
+static IMPLEMENT_LHASH_DOALL_ARG_FN(each_conf_value, CONF_VALUE, void)
+
+/*
+ * call-seq:
+ *    config.each { |section, key, value| }
+ *
+ * Retrieves the section and its pairs for the current configuration.
+ *
+ *    config.each do |section, key, value|
+ *      # ...
+ *    end
+ */
+static VALUE
+config_each(VALUE self)
+{
+    CONF *conf = GetConfig(self);
+
+    RETURN_ENUMERATOR(self, 0, 0);
+
+    lh_doall_arg((_LHASH *)conf->data, LHASH_DOALL_ARG_FN(each_conf_value),
+                 NULL);
+    return self;
+}
+
+/*
+ * call-seq:
+ *    config.inspect -> string
+ *
+ * String representation of this configuration object, including the class
+ * name and its sections.
+ */
+static VALUE
+config_inspect(VALUE self)
+{
+    VALUE str, ary = config_get_sections(self);
+    const char *cname = rb_class2name(rb_obj_class(self));
+
+    str = rb_str_new_cstr("#<");
+    rb_str_cat_cstr(str, cname);
+    rb_str_cat_cstr(str, " sections=");
+    rb_str_append(str, rb_inspect(ary));
+    rb_str_cat_cstr(str, ">");
+
+    return str;
+}
+
 void
 Init_ossl_config(void)
 {
-    char *default_config_file;
+    char *path;
+    VALUE path_str;
 
 #if 0
     mOSSL = rb_define_module("OpenSSL");
     eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
 #endif
 
-    eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError);
+    /* Document-class: OpenSSL::Config
+     *
+     * Configuration for the openssl library.
+     *
+     * Many system's installation of openssl library will depend on your system
+     * configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for
+     * the location of the file for your host.
+     *
+     * See also http://www.openssl.org/docs/apps/config.html
+     */
     cConfig = rb_define_class_under(mOSSL, "Config", rb_cObject);
 
-    default_config_file = CONF_get1_default_config_file();
-    rb_define_const(cConfig, "DEFAULT_CONFIG_FILE",
-		    rb_str_new2(default_config_file));
-    OPENSSL_free(default_config_file);
-    /* methods are defined by openssl/config.rb */
+    /* Document-class: OpenSSL::ConfigError
+     *
+     * General error for openssl library configuration files. Including formatting,
+     * parsing errors, etc.
+     */
+    eConfigError = rb_define_class_under(mOSSL, "ConfigError", eOSSLError);
+
+    rb_include_module(cConfig, rb_mEnumerable);
+    rb_define_singleton_method(cConfig, "parse", config_s_parse, 1);
+    rb_define_singleton_method(cConfig, "parse_config", config_s_parse_config, 1);
+    rb_define_alias(CLASS_OF(cConfig), "load", "new");
+    rb_define_alloc_func(cConfig, config_s_alloc);
+    rb_define_method(cConfig, "initialize", config_initialize, -1);
+    rb_define_method(cConfig, "initialize_copy", config_initialize_copy, 1);
+    rb_define_method(cConfig, "get_value", config_get_value, 2);
+    rb_define_method(cConfig, "[]", config_get_section, 1);
+    rb_define_method(cConfig, "sections", config_get_sections, 0);
+    rb_define_method(cConfig, "to_s", config_to_s, 0);
+    rb_define_method(cConfig, "each", config_each, 0);
+    rb_define_method(cConfig, "inspect", config_inspect, 0);
+
+    /* Document-const: DEFAULT_CONFIG_FILE
+     *
+     * The default system configuration file for OpenSSL.
+     */
+    path = CONF_get1_default_config_file();
+    path_str = rb_obj_freeze(ossl_buf2str(path, rb_long2int(strlen(path))));
+    rb_define_const(cConfig, "DEFAULT_CONFIG_FILE", path_str);
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_config.h ruby-2.5.9/ext/openssl/ossl_config.h
--- ruby-2.5.9.orig/ext/openssl/ossl_config.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_config.h	2025-01-29 19:08:02.378421281 +0100
@@ -5,15 +5,12 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
-#if !defined(_OSSL_CONFIG_H_)
-#define _OSSL_CONFIG_H_
+#ifndef OSSL_CONFIG_H
+#define OSSL_CONFIG_H
 
-extern VALUE cConfig;
-extern VALUE eConfigError;
-
-CONF* DupConfigPtr(VALUE obj);
+CONF *GetConfig(VALUE obj);
 void Init_ossl_config(void);
 
-#endif /* _OSSL_CONFIG_H_ */
+#endif /* OSSL_CONFIG_H */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_digest.c ruby-2.5.9/ext/openssl/ossl_digest.c
--- ruby-2.5.9.orig/ext/openssl/ossl_digest.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_digest.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -19,8 +19,8 @@
 /*
  * Classes
  */
-VALUE cDigest;
-VALUE eDigestError;
+static VALUE cDigest;
+static VALUE eDigestError;
 
 static VALUE ossl_digest_alloc(VALUE klass);
 
@@ -35,7 +35,7 @@
     {
 	0, ossl_digest_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -63,7 +63,7 @@
 
         GetDigest(obj, ctx);
 
-        md = EVP_MD_CTX_md(ctx);
+        md = EVP_MD_CTX_get0_md(ctx);
     }
 
     return md;
@@ -96,14 +96,15 @@
     return TypedData_Wrap_Struct(klass, &ossl_digest_type, 0);
 }
 
-VALUE ossl_digest_update(VALUE, VALUE);
+static VALUE ossl_digest_update(VALUE, VALUE);
 
 /*
  *  call-seq:
  *     Digest.new(string [, data]) -> Digest
  *
  * Creates a Digest instance based on _string_, which is either the ln
- * (long name) or sn (short name) of a supported digest algorithm.
+ * (long name) or sn (short name) of a supported digest algorithm. A list of
+ * supported algorithms can be obtained by calling OpenSSL::Digest.digests.
  *
  * If _data_ (a String) is given, it is used as the initial input to the
  * Digest instance, i.e.
@@ -162,6 +163,32 @@
     return self;
 }
 
+static void
+add_digest_name_to_ary(const OBJ_NAME *name, void *arg)
+{
+    VALUE ary = (VALUE)arg;
+    rb_ary_push(ary, rb_str_new2(name->name));
+}
+
+/*
+ *  call-seq:
+ *     OpenSSL::Digest.digests -> array[string...]
+ *
+ *  Returns the names of all available digests in an array.
+ */
+static VALUE
+ossl_s_digests(VALUE self)
+{
+    VALUE ary;
+
+    ary = rb_ary_new();
+    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH,
+                    add_digest_name_to_ary,
+                    (void*)ary);
+
+    return ary;
+}
+
 /*
  *  call-seq:
  *     digest.reset -> self
@@ -176,7 +203,7 @@
     EVP_MD_CTX *ctx;
 
     GetDigest(self, ctx);
-    if (EVP_DigestInit_ex(ctx, EVP_MD_CTX_md(ctx), NULL) != 1) {
+    if (EVP_DigestInit_ex(ctx, EVP_MD_CTX_get0_md(ctx), NULL) != 1) {
 	ossl_raise(eDigestError, "Digest initialization failed.");
     }
 
@@ -192,13 +219,13 @@
  * be passed individually to the Digest instance.
  *
  * === Example
- *   digest = OpenSSL::Digest::SHA256.new
+ *   digest = OpenSSL::Digest.new('SHA256')
  *   digest.update('First input')
  *   digest << 'Second input' # equivalent to digest.update('Second input')
  *   result = digest.digest
  *
  */
-VALUE
+static VALUE
 ossl_digest_update(VALUE self, VALUE data)
 {
     EVP_MD_CTX *ctx;
@@ -218,23 +245,13 @@
  *
  */
 static VALUE
-ossl_digest_finish(int argc, VALUE *argv, VALUE self)
+ossl_digest_finish(VALUE self)
 {
     EVP_MD_CTX *ctx;
     VALUE str;
-    int out_len;
 
     GetDigest(self, ctx);
-    rb_scan_args(argc, argv, "01", &str);
-    out_len = EVP_MD_CTX_size(ctx);
-
-    if (NIL_P(str)) {
-        str = rb_str_new(NULL, out_len);
-    } else {
-        StringValue(str);
-        rb_str_resize(str, out_len);
-    }
-
+    str = rb_str_new(NULL, EVP_MD_CTX_size(ctx));
     if (!EVP_DigestFinal_ex(ctx, (unsigned char *)RSTRING_PTR(str), NULL))
 	ossl_raise(eDigestError, "EVP_DigestFinal_ex");
 
@@ -245,10 +262,11 @@
  *  call-seq:
  *      digest.name -> string
  *
- * Returns the sn of this Digest algorithm.
+ * Returns the short name of this Digest algorithm which may differ slightly
+ * from the original name provided.
  *
  * === Example
- *   digest = OpenSSL::Digest::SHA512.new
+ *   digest = OpenSSL::Digest.new('SHA512')
  *   puts digest.name # => SHA512
  *
  */
@@ -259,7 +277,7 @@
 
     GetDigest(self, ctx);
 
-    return rb_str_new2(EVP_MD_name(EVP_MD_CTX_md(ctx)));
+    return rb_str_new_cstr(EVP_MD_name(EVP_MD_CTX_get0_md(ctx)));
 }
 
 /*
@@ -270,7 +288,7 @@
  * final message digest result.
  *
  * === Example
- *   digest = OpenSSL::Digest::SHA1.new
+ *   digest = OpenSSL::Digest.new('SHA1')
  *   puts digest.digest_length # => 20
  *
  */
@@ -294,7 +312,7 @@
  * consecutively.
  *
  * === Example
- *   digest = OpenSSL::Digest::SHA1.new
+ *   digest = OpenSSL::Digest.new('SHA1')
  *   puts digest.block_length # => 64
  */
 static VALUE
@@ -313,8 +331,6 @@
 void
 Init_ossl_digest(void)
 {
-    rb_require("digest");
-
 #if 0
     mOSSL = rb_define_module("OpenSSL");
     eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
@@ -348,54 +364,19 @@
      * the integrity of a signed document, it suffices to re-compute the hash
      * and verify that it is equal to that in the signature.
      *
-     * Among the supported message digest algorithms are:
-     * * SHA, SHA1, SHA224, SHA256, SHA384 and SHA512
-     * * MD2, MD4, MDC2 and MD5
-     * * RIPEMD160
-     * * DSS, DSS1 (Pseudo algorithms to be used for DSA signatures. DSS is
-     *   equal to SHA and DSS1 is equal to SHA1)
-     *
-     * For each of these algorithms, there is a sub-class of Digest that
-     * can be instantiated as simply as e.g.
-     *
-     *   digest = OpenSSL::Digest::SHA1.new
-     *
-     * === Mapping between Digest class and sn/ln
-     *
-     * The sn (short names) and ln (long names) are defined in
-     * <openssl/object.h> and <openssl/obj_mac.h>. They are textual
-     * representations of ASN.1 OBJECT IDENTIFIERs. Each supported digest
-     * algorithm has an OBJECT IDENTIFIER associated to it and those again
-     * have short/long names assigned to them.
-     * E.g. the OBJECT IDENTIFIER for SHA-1 is 1.3.14.3.2.26 and its
-     * sn is "SHA1" and its ln is "sha1".
-     * ==== MD2
-     * * sn: MD2
-     * * ln: md2
-     * ==== MD4
-     * * sn: MD4
-     * * ln: md4
-     * ==== MD5
-     * * sn: MD5
-     * * ln: md5
-     * ==== SHA
-     * * sn: SHA
-     * * ln: SHA
-     * ==== SHA-1
-     * * sn: SHA1
-     * * ln: sha1
-     * ==== SHA-224
-     * * sn: SHA224
-     * * ln: sha224
-     * ==== SHA-256
-     * * sn: SHA256
-     * * ln: sha256
-     * ==== SHA-384
-     * * sn: SHA384
-     * * ln: sha384
-     * ==== SHA-512
-     * * sn: SHA512
-     * * ln: sha512
+     * You can get a list of all digest algorithms supported on your system by
+     * running this command in your terminal:
+     *
+     *   openssl list -digest-algorithms
+     *
+     * Among the OpenSSL 1.1.1 supported message digest algorithms are:
+     * * SHA224, SHA256, SHA384, SHA512, SHA512-224 and SHA512-256
+     * * SHA3-224, SHA3-256, SHA3-384 and SHA3-512
+     * * BLAKE2s256 and BLAKE2b512
+     *
+     * Each of these algorithms can be instantiated using the name:
+     *
+     *   digest = OpenSSL::Digest.new('SHA256')
      *
      * "Breaking" a message digest algorithm means defying its one-way
      * function characteristics, i.e. producing a collision or finding a way
@@ -407,16 +388,16 @@
      *
      * === Hashing a file
      *
-     *   data = File.read('document')
-     *   sha256 = OpenSSL::Digest::SHA256.new
+     *   data = File.binread('document')
+     *   sha256 = OpenSSL::Digest.new('SHA256')
      *   digest = sha256.digest(data)
      *
      * === Hashing several pieces of data at once
      *
-     *   data1 = File.read('file1')
-     *   data2 = File.read('file2')
-     *   data3 = File.read('file3')
-     *   sha256 = OpenSSL::Digest::SHA256.new
+     *   data1 = File.binread('file1')
+     *   data2 = File.binread('file2')
+     *   data3 = File.binread('file3')
+     *   sha256 = OpenSSL::Digest.new('SHA256')
      *   sha256 << data1
      *   sha256 << data2
      *   sha256 << data3
@@ -424,15 +405,21 @@
      *
      * === Reuse a Digest instance
      *
-     *   data1 = File.read('file1')
-     *   sha256 = OpenSSL::Digest::SHA256.new
+     *   data1 = File.binread('file1')
+     *   sha256 = OpenSSL::Digest.new('SHA256')
      *   digest1 = sha256.digest(data1)
      *
-     *   data2 = File.read('file2')
+     *   data2 = File.binread('file2')
      *   sha256.reset
      *   digest2 = sha256.digest(data2)
      *
      */
+
+    /*
+     * Digest::Class is defined by the digest library. rb_require() cannot be
+     * used here because it bypasses RubyGems.
+     */
+    rb_funcall(Qnil, rb_intern_const("require"), 1, rb_str_new_cstr("digest"));
     cDigest = rb_define_class_under(mOSSL, "Digest", rb_path2class("Digest::Class"));
     /* Document-class: OpenSSL::Digest::DigestError
      *
@@ -443,12 +430,13 @@
 
     rb_define_alloc_func(cDigest, ossl_digest_alloc);
 
+    rb_define_module_function(cDigest, "digests", ossl_s_digests, 0);
     rb_define_method(cDigest, "initialize", ossl_digest_initialize, -1);
     rb_define_method(cDigest, "initialize_copy", ossl_digest_copy, 1);
     rb_define_method(cDigest, "reset", ossl_digest_reset, 0);
     rb_define_method(cDigest, "update", ossl_digest_update, 1);
     rb_define_alias(cDigest, "<<", "update");
-    rb_define_private_method(cDigest, "finish", ossl_digest_finish, -1);
+    rb_define_private_method(cDigest, "finish", ossl_digest_finish, 0);
     rb_define_method(cDigest, "digest_length", ossl_digest_size, 0);
     rb_define_method(cDigest, "block_length", ossl_digest_block_length, 0);
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_digest.h ruby-2.5.9/ext/openssl/ossl_digest.h
--- ruby-2.5.9.orig/ext/openssl/ossl_digest.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_digest.h	2025-01-29 19:08:02.378421281 +0100
@@ -5,14 +5,11 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_DIGEST_H_)
 #define _OSSL_DIGEST_H_
 
-extern VALUE cDigest;
-extern VALUE eDigestError;
-
 const EVP_MD *ossl_evp_get_digestbyname(VALUE);
 VALUE ossl_digest_new(const EVP_MD *);
 void Init_ossl_digest(void);
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_engine.c ruby-2.5.9/ext/openssl/ossl_engine.c
--- ruby-2.5.9.orig/ext/openssl/ossl_engine.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_engine.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,11 +5,12 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
-#if !defined(OPENSSL_NO_ENGINE)
+#ifdef OSSL_USE_ENGINE
+# include <openssl/engine.h>
 
 #define NewEngine(klass) \
     TypedData_Wrap_Struct((klass), &ossl_engine_type, 0)
@@ -36,12 +37,12 @@
  *
  * See also, https://www.openssl.org/docs/crypto/engine.html
  */
-VALUE cEngine;
+static VALUE cEngine;
 /* Document-class: OpenSSL::Engine::EngineError
  *
  * This is the generic exception for OpenSSL::Engine related errors
  */
-VALUE eEngineError;
+static VALUE eEngineError;
 
 /*
  * Private
@@ -77,7 +78,7 @@
     {
 	0, ossl_engine_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -93,9 +94,6 @@
 static VALUE
 ossl_engine_s_load(int argc, VALUE *argv, VALUE klass)
 {
-#if !defined(HAVE_ENGINE_LOAD_BUILTIN_ENGINES)
-    return Qnil;
-#else
     VALUE name;
 
     rb_scan_args(argc, argv, "01", &name);
@@ -104,60 +102,53 @@
         return Qtrue;
     }
     StringValueCStr(name);
-#ifndef OPENSSL_NO_STATIC_ENGINE
-#if HAVE_ENGINE_LOAD_DYNAMIC
+#ifdef HAVE_ENGINE_LOAD_DYNAMIC
     OSSL_ENGINE_LOAD_IF_MATCH(dynamic, DYNAMIC);
 #endif
-#if HAVE_ENGINE_LOAD_4758CCA
+#ifndef OPENSSL_NO_STATIC_ENGINE
+#ifdef HAVE_ENGINE_LOAD_4758CCA
     OSSL_ENGINE_LOAD_IF_MATCH(4758cca, 4758CCA);
 #endif
-#if HAVE_ENGINE_LOAD_AEP
+#ifdef HAVE_ENGINE_LOAD_AEP
     OSSL_ENGINE_LOAD_IF_MATCH(aep, AEP);
 #endif
-#if HAVE_ENGINE_LOAD_ATALLA
+#ifdef HAVE_ENGINE_LOAD_ATALLA
     OSSL_ENGINE_LOAD_IF_MATCH(atalla, ATALLA);
 #endif
-#if HAVE_ENGINE_LOAD_CHIL
+#ifdef HAVE_ENGINE_LOAD_CHIL
     OSSL_ENGINE_LOAD_IF_MATCH(chil, CHIL);
 #endif
-#if HAVE_ENGINE_LOAD_CSWIFT
+#ifdef HAVE_ENGINE_LOAD_CSWIFT
     OSSL_ENGINE_LOAD_IF_MATCH(cswift, CSWIFT);
 #endif
-#if HAVE_ENGINE_LOAD_NURON
+#ifdef HAVE_ENGINE_LOAD_NURON
     OSSL_ENGINE_LOAD_IF_MATCH(nuron, NURON);
 #endif
-#if HAVE_ENGINE_LOAD_SUREWARE
+#ifdef HAVE_ENGINE_LOAD_SUREWARE
     OSSL_ENGINE_LOAD_IF_MATCH(sureware, SUREWARE);
 #endif
-#if HAVE_ENGINE_LOAD_UBSEC
+#ifdef HAVE_ENGINE_LOAD_UBSEC
     OSSL_ENGINE_LOAD_IF_MATCH(ubsec, UBSEC);
 #endif
-#if HAVE_ENGINE_LOAD_PADLOCK
+#ifdef HAVE_ENGINE_LOAD_PADLOCK
     OSSL_ENGINE_LOAD_IF_MATCH(padlock, PADLOCK);
 #endif
-#if HAVE_ENGINE_LOAD_CAPI
+#ifdef HAVE_ENGINE_LOAD_CAPI
     OSSL_ENGINE_LOAD_IF_MATCH(capi, CAPI);
 #endif
-#if HAVE_ENGINE_LOAD_GMP
+#ifdef HAVE_ENGINE_LOAD_GMP
     OSSL_ENGINE_LOAD_IF_MATCH(gmp, GMP);
 #endif
-#if HAVE_ENGINE_LOAD_GOST
+#ifdef HAVE_ENGINE_LOAD_GOST
     OSSL_ENGINE_LOAD_IF_MATCH(gost, GOST);
 #endif
-#if HAVE_ENGINE_LOAD_CRYPTODEV
-    OSSL_ENGINE_LOAD_IF_MATCH(cryptodev, CRYPTODEV);
-#endif
-#if HAVE_ENGINE_LOAD_AESNI
-    OSSL_ENGINE_LOAD_IF_MATCH(aesni, AESNI);
 #endif
-#endif
-#ifdef HAVE_ENGINE_LOAD_OPENBSD_DEV_CRYPTO
-    OSSL_ENGINE_LOAD_IF_MATCH(openbsd_dev_crypto, OPENBSD_DEV_CRYPTO);
+#ifdef HAVE_ENGINE_LOAD_CRYPTODEV
+    OSSL_ENGINE_LOAD_IF_MATCH(cryptodev, CRYPTODEV);
 #endif
     OSSL_ENGINE_LOAD_IF_MATCH(openssl, OPENSSL);
     rb_warning("no such builtin loader for `%"PRIsVALUE"'", name);
     return Qnil;
-#endif /* HAVE_ENGINE_LOAD_BUILTIN_ENGINES */
 }
 
 /*
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_engine.h ruby-2.5.9/ext/openssl/ossl_engine.h
--- ruby-2.5.9.orig/ext/openssl/ossl_engine.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_engine.h	2025-01-29 19:08:02.378421281 +0100
@@ -6,14 +6,11 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(OSSL_ENGINE_H)
 #define OSSL_ENGINE_H
 
-extern VALUE cEngine;
-extern VALUE eEngineError;
-
 void Init_ossl_engine(void);
 
 #endif /* OSSL_ENGINE_H */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl.h ruby-2.5.9/ext/openssl/ossl.h
--- ruby-2.5.9.orig/ext/openssl/ossl.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl.h	2025-01-29 19:08:02.377182194 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_H_)
 #define _OSSL_H_
@@ -13,25 +13,30 @@
 #include RUBY_EXTCONF_H
 
 #include <assert.h>
-#include <errno.h>
 #include <ruby.h>
+#include <errno.h>
 #include <ruby/io.h>
 #include <ruby/thread.h>
+#ifdef HAVE_RUBY_RACTOR_H
+#include <ruby/ractor.h>
+#else
+#define RUBY_TYPED_FROZEN_SHAREABLE 0
+#endif
+
 #include <openssl/opensslv.h>
+
 #include <openssl/err.h>
 #include <openssl/asn1.h>
 #include <openssl/x509v3.h>
 #include <openssl/ssl.h>
 #include <openssl/pkcs12.h>
 #include <openssl/pkcs7.h>
-#include <openssl/hmac.h>
 #include <openssl/rand.h>
 #include <openssl/conf.h>
-#include <openssl/conf_api.h>
-#include <openssl/crypto.h>
-#if !defined(OPENSSL_NO_ENGINE)
-#  include <openssl/engine.h>
+#ifndef OPENSSL_NO_TS
+  #include <openssl/ts.h>
 #endif
+#include <openssl/crypto.h>
 #if !defined(OPENSSL_NO_OCSP)
 #  include <openssl/ocsp.h>
 #endif
@@ -40,6 +45,33 @@
 #include <openssl/dsa.h>
 #include <openssl/evp.h>
 #include <openssl/dh.h>
+#include "openssl_missing.h"
+
+#ifndef LIBRESSL_VERSION_NUMBER
+# define OSSL_IS_LIBRESSL 0
+# define OSSL_OPENSSL_PREREQ(maj, min, pat) \
+      (OPENSSL_VERSION_NUMBER >= ((maj << 28) | (min << 20) | (pat << 12)))
+# define OSSL_LIBRESSL_PREREQ(maj, min, pat) 0
+#else
+# define OSSL_IS_LIBRESSL 1
+# define OSSL_OPENSSL_PREREQ(maj, min, pat) 0
+# define OSSL_LIBRESSL_PREREQ(maj, min, pat) \
+      (LIBRESSL_VERSION_NUMBER >= ((maj << 28) | (min << 20) | (pat << 12)))
+#endif
+
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+# define OSSL_3_const const
+#else
+# define OSSL_3_const /* const */
+#endif
+
+#if !defined(OPENSSL_NO_ENGINE) && !OSSL_OPENSSL_PREREQ(3, 0, 0)
+# define OSSL_USE_ENGINE
+#endif
+
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+# define OSSL_USE_PROVIDER
+#endif
 
 /*
  * Common Module
@@ -86,9 +118,8 @@
 VALUE ossl_str_new(const char *, long, int *);
 #define ossl_str_adjust(str, p) \
 do{\
-    long len = RSTRING_LEN(str);\
     long newlen = (long)((p) - (unsigned char*)RSTRING_PTR(str));\
-    assert(newlen <= len);\
+    assert(newlen <= RSTRING_LEN(str));\
     rb_str_set_len((str), newlen);\
 }while(0)
 /*
@@ -120,7 +151,9 @@
 /*
  * ERRor messages
  */
-NORETURN(void ossl_raise(VALUE, const char *, ...));
+PRINTF_ARGS(NORETURN(void ossl_raise(VALUE, const char *, ...)), 2, 3);
+/* Make exception instance from str and OpenSSL error reason string. */
+VALUE ossl_make_error(VALUE exc, VALUE str);
 /* Clear OpenSSL error queue. If dOSSL is set, rb_warn() them. */
 void ossl_clear_error(void);
 
@@ -135,7 +168,6 @@
  */
 extern VALUE dOSSL;
 
-#if defined(HAVE_VA_ARGS_MACRO)
 #define OSSL_Debug(...) do { \
   if (dOSSL == Qtrue) { \
     fprintf(stderr, "OSSL_DEBUG: "); \
@@ -144,34 +176,28 @@
   } \
 } while (0)
 
-#else
-void ossl_debug(const char *, ...);
-#define OSSL_Debug ossl_debug
-#endif
-
 /*
  * Include all parts
  */
-#include "openssl_missing.h"
-#include "ruby_missing.h"
 #include "ossl_asn1.h"
 #include "ossl_bio.h"
 #include "ossl_bn.h"
 #include "ossl_cipher.h"
 #include "ossl_config.h"
 #include "ossl_digest.h"
+#include "ossl_engine.h"
 #include "ossl_hmac.h"
+#include "ossl_kdf.h"
 #include "ossl_ns_spki.h"
 #include "ossl_ocsp.h"
 #include "ossl_pkcs12.h"
 #include "ossl_pkcs7.h"
 #include "ossl_pkey.h"
+#include "ossl_provider.h"
 #include "ossl_rand.h"
 #include "ossl_ssl.h"
-#include "ossl_version.h"
+#include "ossl_ts.h"
 #include "ossl_x509.h"
-#include "ossl_engine.h"
-#include "ossl_kdf.h"
 
 void Init_openssl(void);
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_hmac.c ruby-2.5.9/ext/openssl/ossl_hmac.c
--- ruby-2.5.9.orig/ext/openssl/ossl_hmac.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_hmac.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,16 +5,14 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
-#if !defined(OPENSSL_NO_HMAC)
-
 #include "ossl.h"
 
 #define NewHMAC(klass) \
     TypedData_Wrap_Struct((klass), &ossl_hmac_type, 0)
 #define GetHMAC(obj, ctx) do { \
-    TypedData_Get_Struct((obj), HMAC_CTX, &ossl_hmac_type, (ctx)); \
+    TypedData_Get_Struct((obj), EVP_MD_CTX, &ossl_hmac_type, (ctx)); \
     if (!(ctx)) { \
 	ossl_raise(rb_eRuntimeError, "HMAC wasn't initialized"); \
     } \
@@ -23,8 +21,8 @@
 /*
  * Classes
  */
-VALUE cHMAC;
-VALUE eHMACError;
+static VALUE cHMAC;
+static VALUE eHMACError;
 
 /*
  * Public
@@ -36,7 +34,7 @@
 static void
 ossl_hmac_free(void *ctx)
 {
-    HMAC_CTX_free(ctx);
+    EVP_MD_CTX_free(ctx);
 }
 
 static const rb_data_type_t ossl_hmac_type = {
@@ -44,19 +42,19 @@
     {
 	0, ossl_hmac_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static VALUE
 ossl_hmac_alloc(VALUE klass)
 {
     VALUE obj;
-    HMAC_CTX *ctx;
+    EVP_MD_CTX *ctx;
 
     obj = NewHMAC(klass);
-    ctx = HMAC_CTX_new();
+    ctx = EVP_MD_CTX_new();
     if (!ctx)
-	ossl_raise(eHMACError, NULL);
+        ossl_raise(eHMACError, "EVP_MD_CTX");
     RTYPEDDATA_DATA(obj) = ctx;
 
     return obj;
@@ -76,37 +74,49 @@
  * === Example
  *
  *	key = 'key'
- * 	digest = OpenSSL::Digest.new('sha1')
- * 	instance = OpenSSL::HMAC.new(key, digest)
+ * 	instance = OpenSSL::HMAC.new(key, 'SHA1')
  * 	#=> f42bb0eeb018ebbd4597ae7213711ec60760843f
  * 	instance.class
  * 	#=> OpenSSL::HMAC
  *
  * === A note about comparisons
  *
- * Two instances won't be equal when they're compared, even if they have the
- * same value. Use #to_s or #hexdigest to return the authentication code that
- * the instance represents. For example:
- *
- *	other_instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
- *  	#=> f42bb0eeb018ebbd4597ae7213711ec60760843f
- *  	instance
- *  	#=> f42bb0eeb018ebbd4597ae7213711ec60760843f
- *  	instance == other_instance
- *  	#=> false
- *  	instance.to_s == other_instance.to_s
- *  	#=> true
+ * Two instances can be securely compared with #== in constant time:
+ *
+ *	other_instance = OpenSSL::HMAC.new('key', 'SHA1')
+ *  #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
+ *  instance == other_instance
+ *  #=> true
  *
  */
 static VALUE
 ossl_hmac_initialize(VALUE self, VALUE key, VALUE digest)
 {
-    HMAC_CTX *ctx;
+    EVP_MD_CTX *ctx;
+    EVP_PKEY *pkey;
 
-    StringValue(key);
     GetHMAC(self, ctx);
-    HMAC_Init_ex(ctx, RSTRING_PTR(key), RSTRING_LENINT(key),
-		 ossl_evp_get_digestbyname(digest), NULL);
+    StringValue(key);
+#ifdef HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY
+    pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL,
+                                        (unsigned char *)RSTRING_PTR(key),
+                                        RSTRING_LENINT(key));
+    if (!pkey)
+        ossl_raise(eHMACError, "EVP_PKEY_new_raw_private_key");
+#else
+    pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL,
+                                (unsigned char *)RSTRING_PTR(key),
+                                RSTRING_LENINT(key));
+    if (!pkey)
+        ossl_raise(eHMACError, "EVP_PKEY_new_mac_key");
+#endif
+    if (EVP_DigestSignInit(ctx, NULL, ossl_evp_get_digestbyname(digest),
+                           NULL, pkey) != 1) {
+        EVP_PKEY_free(pkey);
+        ossl_raise(eHMACError, "EVP_DigestSignInit");
+    }
+    /* Decrement reference counter; EVP_MD_CTX still keeps it */
+    EVP_PKEY_free(pkey);
 
     return self;
 }
@@ -114,16 +124,15 @@
 static VALUE
 ossl_hmac_copy(VALUE self, VALUE other)
 {
-    HMAC_CTX *ctx1, *ctx2;
+    EVP_MD_CTX *ctx1, *ctx2;
 
     rb_check_frozen(self);
     if (self == other) return self;
 
     GetHMAC(self, ctx1);
     GetHMAC(other, ctx2);
-
-    if (!HMAC_CTX_copy(ctx1, ctx2))
-	ossl_raise(eHMACError, "HMAC_CTX_copy");
+    if (EVP_MD_CTX_copy(ctx1, ctx2) != 1)
+        ossl_raise(eHMACError, "EVP_MD_CTX_copy");
     return self;
 }
 
@@ -148,33 +157,16 @@
 static VALUE
 ossl_hmac_update(VALUE self, VALUE data)
 {
-    HMAC_CTX *ctx;
+    EVP_MD_CTX *ctx;
 
     StringValue(data);
     GetHMAC(self, ctx);
-    HMAC_Update(ctx, (unsigned char *)RSTRING_PTR(data), RSTRING_LEN(data));
+    if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) != 1)
+        ossl_raise(eHMACError, "EVP_DigestSignUpdate");
 
     return self;
 }
 
-static void
-hmac_final(HMAC_CTX *ctx, unsigned char *buf, unsigned int *buf_len)
-{
-    HMAC_CTX *final;
-
-    final = HMAC_CTX_new();
-    if (!final)
-	ossl_raise(eHMACError, "HMAC_CTX_new");
-
-    if (!HMAC_CTX_copy(final, ctx)) {
-	HMAC_CTX_free(final);
-	ossl_raise(eHMACError, "HMAC_CTX_copy");
-    }
-
-    HMAC_Final(final, buf, buf_len);
-    HMAC_CTX_free(final);
-}
-
 /*
  *  call-seq:
  *     hmac.digest -> string
@@ -182,7 +174,7 @@
  * Returns the authentication code an instance represents as a binary string.
  *
  * === Example
- *  instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
+ *  instance = OpenSSL::HMAC.new('key', 'SHA1')
  *  #=> f42bb0eeb018ebbd4597ae7213711ec60760843f
  *  instance.digest
  *  #=> "\xF4+\xB0\xEE\xB0\x18\xEB\xBDE\x97\xAEr\x13q\x1E\xC6\a`\x84?"
@@ -190,15 +182,16 @@
 static VALUE
 ossl_hmac_digest(VALUE self)
 {
-    HMAC_CTX *ctx;
-    unsigned int buf_len;
+    EVP_MD_CTX *ctx;
+    size_t buf_len = EVP_MAX_MD_SIZE;
     VALUE ret;
 
     GetHMAC(self, ctx);
     ret = rb_str_new(NULL, EVP_MAX_MD_SIZE);
-    hmac_final(ctx, (unsigned char *)RSTRING_PTR(ret), &buf_len);
-    assert(buf_len <= EVP_MAX_MD_SIZE);
-    rb_str_set_len(ret, buf_len);
+    if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(ret),
+                            &buf_len) != 1)
+        ossl_raise(eHMACError, "EVP_DigestSignFinal");
+    rb_str_set_len(ret, (long)buf_len);
 
     return ret;
 }
@@ -213,13 +206,14 @@
 static VALUE
 ossl_hmac_hexdigest(VALUE self)
 {
-    HMAC_CTX *ctx;
+    EVP_MD_CTX *ctx;
     unsigned char buf[EVP_MAX_MD_SIZE];
-    unsigned int buf_len;
+    size_t buf_len = EVP_MAX_MD_SIZE;
     VALUE ret;
 
     GetHMAC(self, ctx);
-    hmac_final(ctx, buf, &buf_len);
+    if (EVP_DigestSignFinal(ctx, buf, &buf_len) != 1)
+        ossl_raise(eHMACError, "EVP_DigestSignFinal");
     ret = rb_str_new(NULL, buf_len * 2);
     ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len);
 
@@ -236,7 +230,7 @@
  * === Example
  *
  *	data = "The quick brown fox jumps over the lazy dog"
- * 	instance = OpenSSL::HMAC.new('key', OpenSSL::Digest.new('sha1'))
+ * 	instance = OpenSSL::HMAC.new('key', 'SHA1')
  * 	#=> f42bb0eeb018ebbd4597ae7213711ec60760843f
  *
  * 	instance.update(data)
@@ -248,85 +242,18 @@
 static VALUE
 ossl_hmac_reset(VALUE self)
 {
-    HMAC_CTX *ctx;
+    EVP_MD_CTX *ctx;
+    EVP_PKEY *pkey;
 
     GetHMAC(self, ctx);
-    HMAC_Init_ex(ctx, NULL, 0, NULL, NULL);
+    pkey = EVP_PKEY_CTX_get0_pkey(EVP_MD_CTX_get_pkey_ctx(ctx));
+    if (EVP_DigestSignInit(ctx, NULL, EVP_MD_CTX_get0_md(ctx), NULL, pkey) != 1)
+        ossl_raise(eHMACError, "EVP_DigestSignInit");
 
     return self;
 }
 
 /*
- *  call-seq:
- *     HMAC.digest(digest, key, data) -> aString
- *
- * Returns the authentication code as a binary string. The _digest_ parameter
- * specifies the digest algorithm to use. This may be a String representing
- * the algorithm name or an instance of OpenSSL::Digest.
- *
- * === Example
- *
- *	key = 'key'
- * 	data = 'The quick brown fox jumps over the lazy dog'
- *
- * 	hmac = OpenSSL::HMAC.digest('sha1', key, data)
- * 	#=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9"
- *
- */
-static VALUE
-ossl_hmac_s_digest(VALUE klass, VALUE digest, VALUE key, VALUE data)
-{
-    unsigned char *buf;
-    unsigned int buf_len;
-
-    StringValue(key);
-    StringValue(data);
-    buf = HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key),
-	       RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data),
-	       RSTRING_LEN(data), NULL, &buf_len);
-
-    return rb_str_new((const char *)buf, buf_len);
-}
-
-/*
- *  call-seq:
- *     HMAC.hexdigest(digest, key, data) -> aString
- *
- * Returns the authentication code as a hex-encoded string. The _digest_
- * parameter specifies the digest algorithm to use. This may be a String
- * representing the algorithm name or an instance of OpenSSL::Digest.
- *
- * === Example
- *
- *	key = 'key'
- * 	data = 'The quick brown fox jumps over the lazy dog'
- *
- * 	hmac = OpenSSL::HMAC.hexdigest('sha1', key, data)
- * 	#=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"
- *
- */
-static VALUE
-ossl_hmac_s_hexdigest(VALUE klass, VALUE digest, VALUE key, VALUE data)
-{
-    unsigned char buf[EVP_MAX_MD_SIZE];
-    unsigned int buf_len;
-    VALUE ret;
-
-    StringValue(key);
-    StringValue(data);
-
-    if (!HMAC(ossl_evp_get_digestbyname(digest), RSTRING_PTR(key),
-	      RSTRING_LENINT(key), (unsigned char *)RSTRING_PTR(data),
-	      RSTRING_LEN(data), buf, &buf_len))
-	ossl_raise(eHMACError, "HMAC");
-
-    ret = rb_str_new(NULL, buf_len * 2);
-    ossl_bin2hex(buf, RSTRING_PTR(ret), buf_len);
-
-    return ret;
-}
-
-/*
  * INIT
  */
 void
@@ -356,11 +283,10 @@
      *
      * === HMAC-SHA256 using incremental interface
      *
-     *   data1 = File.read("file1")
-     *   data2 = File.read("file2")
+     *   data1 = File.binread("file1")
+     *   data2 = File.binread("file2")
      *   key = "key"
-     *   digest = OpenSSL::Digest::SHA256.new
-     *   hmac = OpenSSL::HMAC.new(key, digest)
+     *   hmac = OpenSSL::HMAC.new(key, 'SHA256')
      *   hmac << data1
      *   hmac << data2
      *   mac = hmac.digest
@@ -370,8 +296,6 @@
     cHMAC = rb_define_class_under(mOSSL, "HMAC", rb_cObject);
 
     rb_define_alloc_func(cHMAC, ossl_hmac_alloc);
-    rb_define_singleton_method(cHMAC, "digest", ossl_hmac_s_digest, 3);
-    rb_define_singleton_method(cHMAC, "hexdigest", ossl_hmac_s_hexdigest, 3);
 
     rb_define_method(cHMAC, "initialize", ossl_hmac_initialize, 2);
     rb_define_method(cHMAC, "initialize_copy", ossl_hmac_copy, 1);
@@ -384,12 +308,3 @@
     rb_define_alias(cHMAC, "inspect", "hexdigest");
     rb_define_alias(cHMAC, "to_s", "hexdigest");
 }
-
-#else /* NO_HMAC */
-#  warning >>> OpenSSL is compiled without HMAC support <<<
-void
-Init_ossl_hmac(void)
-{
-    rb_warning("HMAC is not available: OpenSSL is compiled without HMAC.");
-}
-#endif /* NO_HMAC */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_hmac.h ruby-2.5.9/ext/openssl/ossl_hmac.h
--- ruby-2.5.9.orig/ext/openssl/ossl_hmac.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_hmac.h	2025-01-29 19:08:02.378421281 +0100
@@ -5,14 +5,11 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_HMAC_H_)
 #define _OSSL_HMAC_H_
 
-extern VALUE cHMAC;
-extern VALUE eHMACError;
-
 void Init_ossl_hmac(void);
 
 #endif /* _OSSL_HMAC_H_ */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_kdf.c ruby-2.5.9/ext/openssl/ossl_kdf.c
--- ruby-2.5.9.orig/ext/openssl/ossl_kdf.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_kdf.c	2025-01-29 19:08:02.378421281 +0100
@@ -3,7 +3,7 @@
  * Copyright (C) 2007, 2017 Ruby/OpenSSL Project Authors
  */
 #include "ossl.h"
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0)
 # include <openssl/kdf.h>
 #endif
 
@@ -18,10 +18,10 @@
  * of _length_ bytes.
  *
  * For more information about PBKDF2, see RFC 2898 Section 5.2
- * (https://tools.ietf.org/html/rfc2898#section-5.2).
+ * (https://www.rfc-editor.org/rfc/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.
@@ -81,10 +81,10 @@
  * bcrypt.
  *
  * The keyword arguments _N_, _r_ and _p_ can be used to tune scrypt. RFC 7914
- * (published on 2016-08, https://tools.ietf.org/html/rfc7914#section-2) states
+ * (published on 2016-08, https://www.rfc-editor.org/rfc/rfc7914#section-2) states
  * that using values r=8 and p=1 appears to yield good results.
  *
- * See RFC 7914 (https://tools.ietf.org/html/rfc7914) for more information.
+ * See RFC 7914 (https://www.rfc-editor.org/rfc/rfc7914) for more information.
  *
  * === Parameters
  * pass   :: Passphrase.
@@ -141,13 +141,13 @@
 }
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0)
 /*
  * call-seq:
  *    KDF.hkdf(ikm, salt:, info:, length:, hash:) -> String
  *
  * HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as specified in
- * {RFC 5869}[https://tools.ietf.org/html/rfc5869].
+ * {RFC 5869}[https://www.rfc-editor.org/rfc/rfc5869].
  *
  * New in OpenSSL 1.1.0.
  *
@@ -163,6 +163,14 @@
  *   HashLen is the length of the hash function output in octets.
  * _hash_::
  *   The hash function.
+ *
+ * === Example
+ *   # The values from https://www.rfc-editor.org/rfc/rfc5869#appendix-A.1
+ *   ikm = ["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*")
+ *   salt = ["000102030405060708090a0b0c"].pack("H*")
+ *   info = ["f0f1f2f3f4f5f6f7f8f9"].pack("H*")
+ *   p OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: 42, hash: "SHA256").unpack1("H*")
+ *   # => "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
  */
 static VALUE
 kdf_hkdf(int argc, VALUE *argv, VALUE self)
@@ -272,7 +280,7 @@
      *   # store this with the generated value
      *   salt = OpenSSL::Random.random_bytes(16)
      *   iter = 20_000
-     *   hash = OpenSSL::Digest::SHA256.new
+     *   hash = OpenSSL::Digest.new('SHA256')
      *   len = hash.digest_length
      *   # the final value to be stored
      *   value = OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
@@ -284,24 +292,8 @@
      * Typically, "==" short-circuits on evaluation, and is therefore
      * vulnerable to timing attacks. The proper way is to use a method that
      * always takes the same amount of time when comparing two values, thus
-     * not leaking any information to potential attackers. To compare two
-     * values, the following could be used:
-     *
-     *   def eql_time_cmp(a, b)
-     *     unless a.length == b.length
-     *       return false
-     *     end
-     *     cmp = b.bytes
-     *     result = 0
-     *     a.bytes.each_with_index {|c,i|
-     *       result |= c ^ cmp[i]
-     *     }
-     *     result == 0
-     *   end
-     *
-     * Please note that the premature return in case of differing lengths
-     * typically does not leak valuable information - when using PBKDF2, the
-     * length of the values to be compared is of fixed size.
+     * not leaking any information to potential attackers. To do this, use
+     * +OpenSSL.fixed_length_secure_compare+.
      */
     mKDF = rb_define_module_under(mOSSL, "KDF");
     /*
@@ -313,7 +305,7 @@
 #if defined(HAVE_EVP_PBE_SCRYPT)
     rb_define_module_function(mKDF, "scrypt", kdf_scrypt, -1);
 #endif
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 6, 0)
     rb_define_module_function(mKDF, "hkdf", kdf_hkdf, -1);
 #endif
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ns_spki.c ruby-2.5.9/ext/openssl/ossl_ns_spki.c
--- ruby-2.5.9.orig/ext/openssl/ossl_ns_spki.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_ns_spki.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -27,9 +27,9 @@
 /*
  * Classes
  */
-VALUE mNetscape;
-VALUE cSPKI;
-VALUE eSPKIError;
+static VALUE mNetscape;
+static VALUE cSPKI;
+static VALUE eSPKIError;
 
 /*
  * Public functions
@@ -50,7 +50,7 @@
     {
 	0, ossl_netscape_spki_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static VALUE
@@ -115,11 +115,11 @@
 
     GetSPKI(self, spki);
     if ((len = i2d_NETSCAPE_SPKI(spki, NULL)) <= 0)
-        ossl_raise(eX509CertError, NULL);
+        ossl_raise(eSPKIError, "i2d_NETSCAPE_SPKI");
     str = rb_str_new(0, len);
     p = (unsigned char *)RSTRING_PTR(str);
     if (i2d_NETSCAPE_SPKI(spki, &p) <= 0)
-        ossl_raise(eX509CertError, NULL);
+        ossl_raise(eSPKIError, "i2d_NETSCAPE_SPKI");
     ossl_str_adjust(str, p);
 
     return str;
@@ -350,7 +350,7 @@
  *   spki = OpenSSL::Netscape::SPKI.new
  *   spki.challenge = "RandomChallenge"
  *   spki.public_key = key.public_key
- *   spki.sign(key, OpenSSL::Digest::SHA256.new)
+ *   spki.sign(key, OpenSSL::Digest.new('SHA256'))
  *   #send a request containing this to a server generating a certificate
  * === Verifying an SPKI request
  *   request = #...
@@ -365,8 +365,8 @@
  *
  * OpenSSL::Netscape is a namespace for SPKI (Simple Public Key
  * Infrastructure) which implements Signed Public Key and Challenge.
- * See {RFC 2692}[http://tools.ietf.org/html/rfc2692] and {RFC
- * 2693}[http://tools.ietf.org/html/rfc2692] for details.
+ * See {RFC 2692}[https://www.rfc-editor.org/rfc/rfc2692] and {RFC
+ * 2693}[https://www.rfc-editor.org/rfc/rfc2692] for details.
  */
 
 /* Document-class: OpenSSL::Netscape::SPKIError
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ns_spki.h ruby-2.5.9/ext/openssl/ossl_ns_spki.h
--- ruby-2.5.9.orig/ext/openssl/ossl_ns_spki.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_ns_spki.h	2025-01-29 19:08:02.378421281 +0100
@@ -5,15 +5,11 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_NS_SPKI_H_)
 #define _OSSL_NS_SPKI_H_
 
-extern VALUE mNetscape;
-extern VALUE cSPKI;
-extern VALUE eSPKIError;
-
 void Init_ossl_ns_spki(void);
 
 #endif /* _OSSL_NS_SPKI_H_ */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ocsp.c ruby-2.5.9/ext/openssl/ossl_ocsp.c
--- ruby-2.5.9.orig/ext/openssl/ossl_ocsp.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_ocsp.c	2025-01-29 19:08:02.378421281 +0100
@@ -6,7 +6,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -67,13 +67,13 @@
     if(!(cid)) ossl_raise(rb_eRuntimeError, "Cert ID wasn't initialized!"); \
 } while (0)
 
-VALUE mOCSP;
-VALUE eOCSPError;
-VALUE cOCSPReq;
-VALUE cOCSPRes;
-VALUE cOCSPBasicRes;
-VALUE cOCSPSingleRes;
-VALUE cOCSPCertId;
+static VALUE mOCSP;
+static VALUE eOCSPError;
+static VALUE cOCSPReq;
+static VALUE cOCSPRes;
+static VALUE cOCSPBasicRes;
+static VALUE cOCSPSingleRes;
+static VALUE cOCSPCertId;
 
 static void
 ossl_ocsp_request_free(void *ptr)
@@ -86,7 +86,7 @@
     {
 	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 @@
     {
 	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 @@
     {
 	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 @@
     {
 	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 @@
     {
 	0, ossl_ocsp_certid_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -157,7 +157,7 @@
 }
 
 /*
- * OCSP::Resquest
+ * OCSP::Request
  */
 static VALUE
 ossl_ocspreq_alloc(VALUE klass)
@@ -382,7 +382,7 @@
     if (!NIL_P(flags))
 	flg = NUM2INT(flags);
     if (NIL_P(digest))
-	md = EVP_sha1();
+	md = NULL;
     else
 	md = ossl_evp_get_digestbyname(digest);
     if (NIL_P(certs))
@@ -803,7 +803,7 @@
  * revocation, and must be one of OpenSSL::OCSP::REVOKED_STATUS_* constants.
  * _revocation_time_ is the time when the certificate is revoked.
  *
- * _this_update_ and _next_update_ indicate the time at which ths status is
+ * _this_update_ and _next_update_ indicate the time at which the status is
  * verified to be correct and the time at or before which newer information
  * will be available, respectively. _next_update_ is optional.
  *
@@ -1033,7 +1033,7 @@
     if (!NIL_P(flags))
 	flg = NUM2INT(flags);
     if (NIL_P(digest))
-	md = EVP_sha1();
+	md = NULL;
     else
 	md = ossl_evp_get_digestbyname(digest);
     if (NIL_P(certs))
@@ -1069,55 +1069,7 @@
     x509st = GetX509StorePtr(store);
     flg = NIL_P(flags) ? 0 : NUM2INT(flags);
     x509s = ossl_x509_ary2sk(certs);
-#if (OPENSSL_VERSION_NUMBER < 0x1000202fL) || defined(LIBRESSL_VERSION_NUMBER)
-    /*
-     * OpenSSL had a bug that it doesn't use the certificates in x509s for
-     * verifying the chain. This can be a problem when the response is signed by
-     * a certificate issued by an intermediate CA.
-     *
-     *       root_ca
-     *         |
-     *   intermediate_ca
-     *         |-------------|
-     *     end_entity    ocsp_signer
-     *
-     * When the certificate hierarchy is like this, and the response contains
-     * only ocsp_signer certificate, the following code wrongly fails.
-     *
-     *   store = OpenSSL::X509::Store.new; store.add_cert(root_ca)
-     *   basic_response.verify([intermediate_ca], store)
-     *
-     * So add the certificates in x509s to the embedded certificates list first.
-     *
-     * This is fixed in OpenSSL 0.9.8zg, 1.0.0s, 1.0.1n, 1.0.2b. But it still
-     * exists in LibreSSL 2.1.10, 2.2.9, 2.3.6, 2.4.1.
-     */
-    if (!(flg & (OCSP_NOCHAIN | OCSP_NOVERIFY)) &&
-	sk_X509_num(x509s) && sk_X509_num(bs->certs)) {
-	int i;
-
-	bs = ASN1_item_dup(ASN1_ITEM_rptr(OCSP_BASICRESP), bs);
-	if (!bs) {
-	    sk_X509_pop_free(x509s, X509_free);
-	    ossl_raise(eOCSPError, "ASN1_item_dup");
-	}
-
-	for (i = 0; i < sk_X509_num(x509s); i++) {
-	    if (!OCSP_basic_add1_cert(bs, sk_X509_value(x509s, i))) {
-		sk_X509_pop_free(x509s, X509_free);
-		OCSP_BASICRESP_free(bs);
-		ossl_raise(eOCSPError, "OCSP_basic_add1_cert");
-	    }
-	}
-	result = OCSP_basic_verify(bs, x509s, x509st, flg);
-	OCSP_BASICRESP_free(bs);
-    }
-    else {
-	result = OCSP_basic_verify(bs, x509s, x509st, flg);
-    }
-#else
     result = OCSP_basic_verify(bs, x509s, x509st, flg);
-#endif
     sk_X509_pop_free(x509s, X509_free);
     if (result <= 0)
 	ossl_clear_error();
@@ -1489,13 +1441,15 @@
  * call-seq:
  *   OpenSSL::OCSP::CertificateId.new(subject, issuer, digest = nil) -> certificate_id
  *   OpenSSL::OCSP::CertificateId.new(der_string)                    -> certificate_id
+ *   OpenSSL::OCSP::CertificateId.new(obj)                           -> certificate_id
  *
  * Creates a new OpenSSL::OCSP::CertificateId for the given _subject_ and
  * _issuer_ X509 certificates.  The _digest_ is a digest algorithm that is used
  * to compute the hash values. This defaults to SHA-1.
  *
  * If only one argument is given, decodes it as DER representation of a
- * certificate ID.
+ * certificate ID or generates certificate ID from the object that responds to
+ * the to_der method.
  */
 static VALUE
 ossl_ocspcid_initialize(int argc, VALUE *argv, VALUE self)
@@ -1717,7 +1671,7 @@
      * subject certificate so the CA knows which certificate we are asking
      * about:
      *
-     *   digest = OpenSSL::Digest::SHA1.new
+     *   digest = OpenSSL::Digest.new('SHA1')
      *   certificate_id =
      *     OpenSSL::OCSP::CertificateId.new subject, issuer, digest
      *
@@ -1734,18 +1688,11 @@
      * To submit the request to the CA for verification we need to extract the
      * OCSP URI from the subject certificate:
      *
-     *   authority_info_access = subject.extensions.find do |extension|
-     *     extension.oid == 'authorityInfoAccess'
-     *   end
-     *
-     *   descriptions = authority_info_access.value.split "\n"
-     *   ocsp = descriptions.find do |description|
-     *     description.start_with? 'OCSP'
-     *   end
+     *   ocsp_uris = subject.ocsp_uris
      *
      *   require 'uri'
      *
-     *   ocsp_uri = URI ocsp[/URI:(.*)/, 1]
+     *   ocsp_uri = URI ocsp_uris[0]
      *
      * To submit the request we'll POST the request to the OCSP URI (per RFC
      * 2560).  Note that we only handle HTTP requests and don't handle any
@@ -1754,7 +1701,7 @@
      *   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
@@ -1792,7 +1739,7 @@
      *   single_response = basic_response.find_response(certificate_id)
      *
      *   unless single_response
-     *     raise 'basic_response does not have the status for the certificiate'
+     *     raise 'basic_response does not have the status for the certificate'
      *   end
      *
      * Then check the validity. A status issued in the future must be rejected.
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ocsp.h ruby-2.5.9/ext/openssl/ossl_ocsp.h
--- ruby-2.5.9.orig/ext/openssl/ossl_ocsp.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_ocsp.h	2025-01-29 19:08:02.378421281 +0100
@@ -6,18 +6,11 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_OCSP_H_)
 #define _OSSL_OCSP_H_
 
-#if !defined(OPENSSL_NO_OCSP)
-extern VALUE mOCSP;
-extern VALUE cOPCSReq;
-extern VALUE cOPCSRes;
-extern VALUE cOPCSBasicRes;
-#endif
-
 void Init_ossl_ocsp(void);
 
 #endif /* _OSSL_OCSP_H_ */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkcs12.c ruby-2.5.9/ext/openssl/ossl_pkcs12.c
--- ruby-2.5.9.orig/ext/openssl/ossl_pkcs12.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkcs12.c	2025-01-29 19:08:02.378421281 +0100
@@ -1,6 +1,6 @@
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -27,8 +27,8 @@
 /*
  * Classes
  */
-VALUE cPKCS12;
-VALUE ePKCS12Error;
+static VALUE cPKCS12;
+static VALUE ePKCS12Error;
 
 /*
  * Private
@@ -44,7 +44,7 @@
     {
 	0, ossl_pkcs12_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static VALUE
@@ -134,6 +134,10 @@
     if (!NIL_P(keytype))
         ktype = NUM2INT(keytype);
 
+    if (ktype != 0 && ktype != KEY_SIG && ktype != KEY_EX) {
+        ossl_raise(rb_eArgError, "Unknown key usage type %"PRIsVALUE, INT2NUM(ktype));
+    }
+
     obj = NewPKCS12(cPKCS12);
     x509s = NIL_P(ca) ? NULL : ossl_x509_ary2sk(ca);
     p12 = PKCS12_create(passphrase, friendlyname, key, x509, x509s,
@@ -149,6 +153,24 @@
     return obj;
 }
 
+static VALUE
+ossl_pkey_new_i(VALUE arg)
+{
+    return ossl_pkey_new((EVP_PKEY *)arg);
+}
+
+static VALUE
+ossl_x509_new_i(VALUE arg)
+{
+    return ossl_x509_new((X509 *)arg);
+}
+
+static VALUE
+ossl_x509_sk2ary_i(VALUE arg)
+{
+    return ossl_x509_sk2ary((STACK_OF(X509) *)arg);
+}
+
 /*
  * call-seq:
  *    PKCS12.new -> pkcs12
@@ -186,15 +208,15 @@
 	ossl_raise(ePKCS12Error, "PKCS12_parse");
     ERR_pop_to_mark();
     if (key) {
-	pkey = rb_protect((VALUE (*)(VALUE))ossl_pkey_new, (VALUE)key, &st);
+	pkey = rb_protect(ossl_pkey_new_i, (VALUE)key, &st);
 	if (st) goto err;
     }
     if (x509) {
-	cert = rb_protect((VALUE (*)(VALUE))ossl_x509_new, (VALUE)x509, &st);
+	cert = rb_protect(ossl_x509_new_i, (VALUE)x509, &st);
 	if (st) goto err;
     }
     if (x509s) {
-	ca = rb_protect((VALUE (*)(VALUE))ossl_x509_sk2ary, (VALUE)x509s, &st);
+	ca = rb_protect(ossl_x509_sk2ary_i, (VALUE)x509s, &st);
 	if (st) goto err;
     }
 
@@ -229,6 +251,48 @@
     return str;
 }
 
+/*
+ * call-seq:
+ *    pkcs12.set_mac(pass, salt = nil, iter = nil, md_type = nil)
+ *
+ * Sets MAC parameters and generates MAC over the PKCS #12 structure.
+ *
+ * This method uses HMAC and the PKCS #12 specific password-based KDF as
+ * specified in the original PKCS #12.
+ *
+ * See also the man page PKCS12_set_mac(3).
+ *
+ * Added in version 3.3.0.
+ */
+static VALUE
+pkcs12_set_mac(int argc, VALUE *argv, VALUE self)
+{
+    PKCS12 *p12;
+    VALUE pass, salt, iter, md_name;
+    int iter_i = 0;
+    const EVP_MD *md_type = NULL;
+
+    rb_scan_args(argc, argv, "13", &pass, &salt, &iter, &md_name);
+    rb_check_frozen(self);
+    GetPKCS12(self, p12);
+
+    StringValue(pass);
+    if (!NIL_P(salt))
+        StringValue(salt);
+    if (!NIL_P(iter))
+        iter_i = NUM2INT(iter);
+    if (!NIL_P(md_name))
+        md_type = ossl_evp_get_digestbyname(md_name);
+
+    if (!PKCS12_set_mac(p12, RSTRING_PTR(pass), RSTRING_LENINT(pass),
+                        !NIL_P(salt) ? (unsigned char *)RSTRING_PTR(salt) : NULL,
+                        !NIL_P(salt) ? RSTRING_LENINT(salt) : 0,
+                        iter_i, md_type))
+        ossl_raise(ePKCS12Error, "PKCS12_set_mac");
+
+    return Qnil;
+}
+
 void
 Init_ossl_pkcs12(void)
 {
@@ -254,4 +318,9 @@
     rb_attr(cPKCS12, rb_intern("ca_certs"), 1, 0, Qfalse);
     rb_define_method(cPKCS12, "initialize", ossl_pkcs12_initialize, -1);
     rb_define_method(cPKCS12, "to_der", ossl_pkcs12_to_der, 0);
+    rb_define_method(cPKCS12, "set_mac", pkcs12_set_mac, -1);
+
+    /* MSIE specific PKCS12 key usage extensions */
+    rb_define_const(cPKCS12, "KEY_EX", INT2NUM(KEY_EX));
+    rb_define_const(cPKCS12, "KEY_SIG", INT2NUM(KEY_SIG));
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkcs12.h ruby-2.5.9/ext/openssl/ossl_pkcs12.h
--- ruby-2.5.9.orig/ext/openssl/ossl_pkcs12.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkcs12.h	2025-01-29 19:08:02.378421281 +0100
@@ -1,13 +1,10 @@
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_PKCS12_H_)
 #define _OSSL_PKCS12_H_
 
-extern VALUE cPKCS12;
-extern VALUE ePKCS12Error;
-
 void Init_ossl_pkcs12(void);
 
 #endif /* _OSSL_PKCS12_H_ */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkcs7.c ruby-2.5.9/ext/openssl/ossl_pkcs7.c
--- ruby-2.5.9.orig/ext/openssl/ossl_pkcs7.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkcs7.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -13,14 +13,14 @@
     TypedData_Wrap_Struct((klass), &ossl_pkcs7_type, 0)
 #define SetPKCS7(obj, pkcs7) do { \
     if (!(pkcs7)) { \
-	ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
+        ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
     } \
     RTYPEDDATA_DATA(obj) = (pkcs7); \
 } while (0)
 #define GetPKCS7(obj, pkcs7) do { \
     TypedData_Get_Struct((obj), PKCS7, &ossl_pkcs7_type, (pkcs7)); \
     if (!(pkcs7)) { \
-	ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
+        ossl_raise(rb_eRuntimeError, "PKCS7 wasn't initialized."); \
     } \
 } while (0)
 
@@ -64,10 +64,10 @@
 /*
  * Classes
  */
-VALUE cPKCS7;
-VALUE cPKCS7Signer;
-VALUE cPKCS7Recipient;
-VALUE ePKCS7Error;
+static VALUE cPKCS7;
+static VALUE cPKCS7Signer;
+static VALUE cPKCS7Recipient;
+static VALUE ePKCS7Error;
 
 static void
 ossl_pkcs7_free(void *ptr)
@@ -80,9 +80,23 @@
     {
 	0, ossl_pkcs7_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
+VALUE
+ossl_pkcs7_new(PKCS7 *p7)
+{
+    PKCS7 *new;
+    VALUE obj = NewPKCS7(cPKCS7);
+
+    new = PKCS7_dup(p7);
+    if (!new)
+        ossl_raise(ePKCS7Error, "PKCS7_dup");
+    SetPKCS7(obj, new);
+
+    return obj;
+}
+
 static void
 ossl_pkcs7_signer_info_free(void *ptr)
 {
@@ -94,7 +108,7 @@
     {
 	0, ossl_pkcs7_signer_info_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static void
@@ -108,7 +122,7 @@
     {
 	0, ossl_pkcs7_recip_info_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -116,19 +130,24 @@
  * (MADE PRIVATE UNTIL SOMEBODY WILL NEED THEM)
  */
 static PKCS7_SIGNER_INFO *
-ossl_PKCS7_SIGNER_INFO_dup(const PKCS7_SIGNER_INFO *si)
+ossl_PKCS7_SIGNER_INFO_dup(PKCS7_SIGNER_INFO *si)
 {
-    return (PKCS7_SIGNER_INFO *)ASN1_dup((i2d_of_void *)i2d_PKCS7_SIGNER_INFO,
-					 (d2i_of_void *)d2i_PKCS7_SIGNER_INFO,
-					 (char *)si);
+    PKCS7_SIGNER_INFO *si_new = ASN1_dup((i2d_of_void *)i2d_PKCS7_SIGNER_INFO,
+                                         (d2i_of_void *)d2i_PKCS7_SIGNER_INFO,
+                                         si);
+    if (si_new && si->pkey) {
+        EVP_PKEY_up_ref(si->pkey);
+        si_new->pkey = si->pkey;
+    }
+    return si_new;
 }
 
 static PKCS7_RECIP_INFO *
-ossl_PKCS7_RECIP_INFO_dup(const PKCS7_RECIP_INFO *si)
+ossl_PKCS7_RECIP_INFO_dup(PKCS7_RECIP_INFO *si)
 {
-    return (PKCS7_RECIP_INFO *)ASN1_dup((i2d_of_void *)i2d_PKCS7_RECIP_INFO,
-					(d2i_of_void *)d2i_PKCS7_RECIP_INFO,
-					(char *)si);
+    return ASN1_dup((i2d_of_void *)i2d_PKCS7_RECIP_INFO,
+                    (d2i_of_void *)d2i_PKCS7_RECIP_INFO,
+                    si);
 }
 
 static VALUE
@@ -145,19 +164,6 @@
     return obj;
 }
 
-static PKCS7_SIGNER_INFO *
-DupPKCS7SignerPtr(VALUE obj)
-{
-    PKCS7_SIGNER_INFO *p7si, *pkcs7;
-
-    GetPKCS7si(obj, p7si);
-    if (!(pkcs7 = ossl_PKCS7_SIGNER_INFO_dup(p7si))) {
-	ossl_raise(ePKCS7Error, NULL);
-    }
-
-    return pkcs7;
-}
-
 static VALUE
 ossl_pkcs7ri_new(PKCS7_RECIP_INFO *p7ri)
 {
@@ -172,19 +178,6 @@
     return obj;
 }
 
-static PKCS7_RECIP_INFO *
-DupPKCS7RecipientPtr(VALUE obj)
-{
-    PKCS7_RECIP_INFO *p7ri, *pkcs7;
-
-    GetPKCS7ri(obj, p7ri);
-    if (!(pkcs7 = ossl_PKCS7_RECIP_INFO_dup(p7ri))) {
-	ossl_raise(ePKCS7Error, NULL);
-    }
-
-    return pkcs7;
-}
-
 /*
  * call-seq:
  *    PKCS7.read_smime(string) => pkcs7
@@ -201,7 +194,13 @@
     out = NULL;
     pkcs7 = SMIME_read_PKCS7(in, &out);
     BIO_free(in);
-    if(!pkcs7) ossl_raise(ePKCS7Error, NULL);
+    if (!pkcs7)
+        ossl_raise(ePKCS7Error, "Could not parse the PKCS7");
+    if (!pkcs7->d.ptr) {
+        PKCS7_free(pkcs7);
+        ossl_raise(ePKCS7Error, "No content in PKCS7");
+    }
+
     data = out ? ossl_membio2str(out) : Qnil;
     SetPKCS7(ret, pkcs7);
     ossl_pkcs7_set_data(ret, data);
@@ -291,7 +290,14 @@
 
 /*
  * call-seq:
- *    PKCS7.encrypt(certs, data, [, cipher [, flags]]) => pkcs7
+ *    PKCS7.encrypt(certs, data, cipher, flags = 0) => pkcs7
+ *
+ * Creates a PKCS #7 enveloped-data structure.
+ *
+ * Before version 3.3.0, +cipher+ was optional and defaulted to
+ * <tt>"RC2-40-CBC"</tt>.
+ *
+ * See also the man page PKCS7_encrypt(3).
  */
 static VALUE
 ossl_pkcs7_s_encrypt(int argc, VALUE *argv, VALUE klass)
@@ -305,21 +311,12 @@
     PKCS7 *p7;
 
     rb_scan_args(argc, argv, "22", &certs, &data, &cipher, &flags);
-    if(NIL_P(cipher)){
-#if !defined(OPENSSL_NO_RC2)
-	ciph = EVP_rc2_40_cbc();
-#elif !defined(OPENSSL_NO_DES)
-	ciph = EVP_des_ede3_cbc();
-#elif !defined(OPENSSL_NO_RC2)
-	ciph = EVP_rc2_40_cbc();
-#elif !defined(OPENSSL_NO_AES)
-	ciph = EVP_EVP_aes_128_cbc();
-#else
-	ossl_raise(ePKCS7Error, "Must specify cipher");
-#endif
-
+    if (NIL_P(cipher)) {
+        rb_raise(rb_eArgError,
+                 "cipher must be specified. Before version 3.3, " \
+                 "the default cipher was RC2-40-CBC.");
     }
-    else ciph = ossl_evp_get_cipherbyname(cipher);
+    ciph = ossl_evp_get_cipherbyname(cipher);
     flg = NIL_P(flags) ? 0 : NUM2INT(flags);
     ret = NewPKCS7(cPKCS7);
     in = ossl_obj2bio(&data);
@@ -366,7 +363,7 @@
 static VALUE
 ossl_pkcs7_initialize(int argc, VALUE *argv, VALUE self)
 {
-    PKCS7 *p7, *pkcs = DATA_PTR(self);
+    PKCS7 *p7, *p7_orig = RTYPEDDATA_DATA(self);
     BIO *in;
     VALUE arg;
 
@@ -374,19 +371,21 @@
 	return self;
     arg = ossl_to_der_if_possible(arg);
     in = ossl_obj2bio(&arg);
-    p7 = PEM_read_bio_PKCS7(in, &pkcs, NULL, NULL);
+    p7 = d2i_PKCS7_bio(in, NULL);
     if (!p7) {
-	OSSL_BIO_reset(in);
-        p7 = d2i_PKCS7_bio(in, &pkcs);
-	if (!p7) {
-	    BIO_free(in);
-	    PKCS7_free(pkcs);
-	    DATA_PTR(self) = NULL;
-	    ossl_raise(rb_eArgError, "Could not parse the PKCS7");
-	}
+        OSSL_BIO_reset(in);
+        p7 = PEM_read_bio_PKCS7(in, NULL, NULL, NULL);
     }
-    DATA_PTR(self) = pkcs;
     BIO_free(in);
+    if (!p7)
+        ossl_raise(rb_eArgError, "Could not parse the PKCS7");
+    if (!p7->d.ptr) {
+        PKCS7_free(p7);
+        ossl_raise(rb_eArgError, "No content in PKCS7");
+    }
+
+    RTYPEDDATA_DATA(self) = p7;
+    PKCS7_free(p7_orig);
     ossl_pkcs7_set_data(self, Qnil);
     ossl_pkcs7_set_err_string(self, Qnil);
 
@@ -536,17 +535,18 @@
 ossl_pkcs7_add_signer(VALUE self, VALUE signer)
 {
     PKCS7 *pkcs7;
-    PKCS7_SIGNER_INFO *p7si;
+    PKCS7_SIGNER_INFO *si, *si_new;
 
-    p7si = DupPKCS7SignerPtr(signer); /* NEED TO DUP */
     GetPKCS7(self, pkcs7);
-    if (!PKCS7_add_signer(pkcs7, p7si)) {
-	PKCS7_SIGNER_INFO_free(p7si);
-	ossl_raise(ePKCS7Error, "Could not add signer.");
-    }
-    if (PKCS7_type_is_signed(pkcs7)){
-	PKCS7_add_signed_attribute(p7si, NID_pkcs9_contentType,
-				   V_ASN1_OBJECT, OBJ_nid2obj(NID_pkcs7_data));
+    GetPKCS7si(signer, si);
+
+    si_new = ossl_PKCS7_SIGNER_INFO_dup(si);
+    if (!si_new)
+        ossl_raise(ePKCS7Error, "PKCS7_SIGNER_INFO_dup");
+
+    if (PKCS7_add_signer(pkcs7, si_new) != 1) {
+        PKCS7_SIGNER_INFO_free(si_new);
+        ossl_raise(ePKCS7Error, "PKCS7_add_signer");
     }
 
     return self;
@@ -582,13 +582,18 @@
 ossl_pkcs7_add_recipient(VALUE self, VALUE recip)
 {
     PKCS7 *pkcs7;
-    PKCS7_RECIP_INFO *ri;
+    PKCS7_RECIP_INFO *ri, *ri_new;
 
-    ri = DupPKCS7RecipientPtr(recip); /* NEED TO DUP */
     GetPKCS7(self, pkcs7);
-    if (!PKCS7_add_recipient_info(pkcs7, ri)) {
-	PKCS7_RECIP_INFO_free(ri);
-	ossl_raise(ePKCS7Error, "Could not add recipient.");
+    GetPKCS7ri(recip, ri);
+
+    ri_new = ossl_PKCS7_RECIP_INFO_dup(ri);
+    if (!ri_new)
+        ossl_raise(ePKCS7Error, "PKCS7_RECIP_INFO_dup");
+
+    if (PKCS7_add_recipient_info(pkcs7, ri_new) != 1) {
+        PKCS7_RECIP_INFO_free(ri_new);
+        ossl_raise(ePKCS7Error, "PKCS7_add_recipient_info");
     }
 
     return self;
@@ -803,9 +808,9 @@
     BIO *out;
     VALUE str;
 
-    rb_scan_args(argc, argv, "21", &pkey, &cert, &flags);
+    rb_scan_args(argc, argv, "12", &pkey, &cert, &flags);
     key = GetPrivPKeyPtr(pkey); /* NO NEED TO DUP */
-    x509 = GetX509CertPtr(cert); /* NO NEED TO DUP */
+    x509 = NIL_P(cert) ? NULL : GetX509CertPtr(cert); /* NO NEED TO DUP */
     flg = NIL_P(flags) ? 0 : NUM2INT(flags);
     GetPKCS7(self, p7);
     if(!(out = BIO_new(BIO_s_mem())))
@@ -874,6 +879,25 @@
 }
 
 static VALUE
+ossl_pkcs7_to_text(VALUE self)
+{
+    PKCS7 *pkcs7;
+    BIO *out;
+    VALUE str;
+
+    GetPKCS7(self, pkcs7);
+    if(!(out = BIO_new(BIO_s_mem())))
+        ossl_raise(ePKCS7Error, NULL);
+    if(!PKCS7_print_ctx(out, pkcs7, 0, NULL)) {
+        BIO_free(out);
+        ossl_raise(ePKCS7Error, NULL);
+    }
+    str = ossl_membio2str(out);
+
+    return str;
+}
+
+static VALUE
 ossl_pkcs7_to_pem(VALUE self)
 {
     PKCS7 *pkcs7;
@@ -1082,13 +1106,13 @@
     rb_define_method(cPKCS7, "to_pem", ossl_pkcs7_to_pem, 0);
     rb_define_alias(cPKCS7,  "to_s", "to_pem");
     rb_define_method(cPKCS7, "to_der", ossl_pkcs7_to_der, 0);
+    rb_define_method(cPKCS7, "to_text", ossl_pkcs7_to_text, 0);
 
     cPKCS7Signer = rb_define_class_under(cPKCS7, "SignerInfo", rb_cObject);
     rb_define_const(cPKCS7, "Signer", cPKCS7Signer);
     rb_define_alloc_func(cPKCS7Signer, ossl_pkcs7si_alloc);
     rb_define_method(cPKCS7Signer, "initialize", ossl_pkcs7si_initialize,3);
     rb_define_method(cPKCS7Signer, "issuer", ossl_pkcs7si_get_issuer, 0);
-    rb_define_alias(cPKCS7Signer, "name", "issuer");
     rb_define_method(cPKCS7Signer, "serial", ossl_pkcs7si_get_serial,0);
     rb_define_method(cPKCS7Signer,"signed_time",ossl_pkcs7si_get_signed_time,0);
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkcs7.h ruby-2.5.9/ext/openssl/ossl_pkcs7.h
--- ruby-2.5.9.orig/ext/openssl/ossl_pkcs7.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkcs7.h	2025-01-29 19:08:02.378421281 +0100
@@ -5,16 +5,12 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_PKCS7_H_)
 #define _OSSL_PKCS7_H_
 
-extern VALUE cPKCS7;
-extern VALUE cPKCS7Signer;
-extern VALUE cPKCS7Recipient;
-extern VALUE ePKCS7Error;
-
+VALUE ossl_pkcs7_new(PKCS7 *p7);
 void Init_ossl_pkcs7(void);
 
 #endif /* _OSSL_PKCS7_H_ */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkey.c ruby-2.5.9/ext/openssl/ossl_pkey.c
--- ruby-2.5.9.orig/ext/openssl/ossl_pkey.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkey.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,10 +5,14 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
+#ifdef OSSL_USE_ENGINE
+# include <openssl/engine.h>
+#endif
+
 /*
  * Classes
  */
@@ -17,64 +21,6 @@
 VALUE ePKeyError;
 static ID id_private_q;
 
-/*
- * callback for generating keys
- */
-static VALUE
-call_check_ints0(VALUE arg)
-{
-    rb_thread_check_ints();
-    return Qnil;
-}
-
-static void *
-call_check_ints(void *arg)
-{
-    int state;
-    rb_protect(call_check_ints0, Qnil, &state);
-    return (void *)(VALUE)state;
-}
-
-int
-ossl_generate_cb_2(int p, int n, BN_GENCB *cb)
-{
-    VALUE ary;
-    struct ossl_generate_cb_arg *arg;
-    int state;
-
-    arg = (struct ossl_generate_cb_arg *)BN_GENCB_get_arg(cb);
-    if (arg->yield) {
-	ary = rb_ary_new2(2);
-	rb_ary_store(ary, 0, INT2NUM(p));
-	rb_ary_store(ary, 1, INT2NUM(n));
-
-	/*
-	* can be break by raising exception or 'break'
-	*/
-	rb_protect(rb_yield, ary, &state);
-	if (state) {
-	    arg->state = state;
-	    return 0;
-	}
-    }
-    if (arg->interrupted) {
-	arg->interrupted = 0;
-	state = (int)(VALUE)rb_thread_call_with_gvl(call_check_ints, NULL);
-	if (state) {
-	    arg->state = state;
-	    return 0;
-	}
-    }
-    return 1;
-}
-
-void
-ossl_generate_cb_stop(void *ptr)
-{
-    struct ossl_generate_cb_arg *arg = (struct ossl_generate_cb_arg *)ptr;
-    arg->interrupted = 1;
-}
-
 static void
 ossl_evp_pkey_free(void *ptr)
 {
@@ -89,40 +35,33 @@
     {
 	0, ossl_evp_pkey_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static VALUE
-pkey_new0(EVP_PKEY *pkey)
+pkey_new0(VALUE arg)
 {
-    VALUE obj;
-    int type;
+    EVP_PKEY *pkey = (EVP_PKEY *)arg;
+    VALUE klass, obj;
 
-    if (!pkey || (type = EVP_PKEY_base_id(pkey)) == EVP_PKEY_NONE)
-	ossl_raise(rb_eRuntimeError, "pkey is empty");
-
-    switch (type) {
+    switch (EVP_PKEY_base_id(pkey)) {
 #if !defined(OPENSSL_NO_RSA)
-    case EVP_PKEY_RSA:
-	return ossl_rsa_new(pkey);
+      case EVP_PKEY_RSA: klass = cRSA; break;
 #endif
 #if !defined(OPENSSL_NO_DSA)
-    case EVP_PKEY_DSA:
-	return ossl_dsa_new(pkey);
+      case EVP_PKEY_DSA: klass = cDSA; break;
 #endif
 #if !defined(OPENSSL_NO_DH)
-    case EVP_PKEY_DH:
-	return ossl_dh_new(pkey);
+      case EVP_PKEY_DH:  klass = cDH; break;
 #endif
 #if !defined(OPENSSL_NO_EC)
-    case EVP_PKEY_EC:
-	return ossl_ec_new(pkey);
+      case EVP_PKEY_EC:  klass = cEC; break;
 #endif
-    default:
-	obj = NewPKey(cPKey);
-	SetPKey(obj, pkey);
-	return obj;
+      default:           klass = cPKey; break;
     }
+    obj = rb_obj_alloc(klass);
+    RTYPEDDATA_DATA(obj) = pkey;
+    return obj;
 }
 
 VALUE
@@ -131,7 +70,7 @@
     VALUE obj;
     int status;
 
-    obj = rb_protect((VALUE (*)(VALUE))pkey_new0, (VALUE)pkey, &status);
+    obj = rb_protect(pkey_new0, (VALUE)pkey, &status);
     if (status) {
 	EVP_PKEY_free(pkey);
 	rb_jump_tag(status);
@@ -140,6 +79,137 @@
     return obj;
 }
 
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+# include <openssl/decoder.h>
+
+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, 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;
+    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;
+}
+
+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 *));
+    /*
+     * 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-----
+     *     BggqhkjOPQMBBw==
+     *     -----END EC PARAMETERS-----
+     *     -----BEGIN EC PRIVATE KEY-----
+     *     MHcCAQEEIAG8ugBbA5MHkqnZ9ujQF93OyUfL9tk8sxqM5Wv5tKg5oAoGCCqGSM49
+     *     AwEHoUQDQgAEVcjhJfkwqh5C7kGuhAf8XaAjVuG5ADwb5ayg/cJijCgs+GcXeedj
+     *     86avKpGH84DXUlB23C/kPt+6fXYlitUmXQ==
+     *     -----END EC PRIVATE KEY-----
+     *
+     * While the first PEM block is a proper encoding of ECParameters, thus
+     * OSSL_DECODER_from_bio() would pick it up, ruby/openssl used to return
+     * the latter instead. Existing applications expect this behavior.
+     *
+     * 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.
+     */
+    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:
+    return pkey;
+}
+#else
+EVP_PKEY *
+ossl_pkey_read_generic(BIO *bio, VALUE pass)
+{
+    void *ppass = (void *)pass;
+    EVP_PKEY *pkey;
+
+    if ((pkey = d2i_PrivateKey_bio(bio, NULL)))
+	goto out;
+    OSSL_BIO_reset(bio);
+    if ((pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, ossl_pem_passwd_cb, ppass)))
+	goto out;
+    OSSL_BIO_reset(bio);
+    if ((pkey = d2i_PUBKEY_bio(bio, NULL)))
+	goto out;
+    OSSL_BIO_reset(bio);
+    /* PEM_read_bio_PrivateKey() also parses PKCS #8 formats */
+    if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, ppass)))
+	goto out;
+    OSSL_BIO_reset(bio);
+    if ((pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL)))
+	goto out;
+    OSSL_BIO_reset(bio);
+    if ((pkey = PEM_read_bio_Parameters(bio, NULL)))
+	goto out;
+
+  out:
+    return pkey;
+}
+#endif
+
 /*
  *  call-seq:
  *     OpenSSL::PKey.read(string [, pwd ]) -> PKey
@@ -149,7 +219,7 @@
  * instance of the appropriate PKey class.
  *
  * === Parameters
- * * _string+ is a DER- or PEM-encoded string containing an arbitrary private
+ * * _string_ is a DER- or PEM-encoded string containing an arbitrary private
  *   or public key.
  * * _io_ is an instance of IO containing a DER- or PEM-encoded
  *   arbitrary private or public key.
@@ -164,30 +234,283 @@
     VALUE data, pass;
 
     rb_scan_args(argc, argv, "11", &data, &pass);
-    pass = ossl_pem_passwd_value(pass);
-
     bio = ossl_obj2bio(&data);
-    if (!(pkey = d2i_PrivateKey_bio(bio, NULL))) {
-	OSSL_BIO_reset(bio);
-	if (!(pkey = PEM_read_bio_PrivateKey(bio, NULL, ossl_pem_passwd_cb, (void *)pass))) {
-	    OSSL_BIO_reset(bio);
-	    if (!(pkey = d2i_PUBKEY_bio(bio, NULL))) {
-		OSSL_BIO_reset(bio);
-		pkey = PEM_read_bio_PUBKEY(bio, NULL, ossl_pem_passwd_cb, (void *)pass);
-	    }
-	}
-    }
-
+    pkey = ossl_pkey_read_generic(bio, ossl_pem_passwd_value(pass));
     BIO_free(bio);
     if (!pkey)
 	ossl_raise(ePKeyError, "Could not parse PKey");
-
     return ossl_pkey_new(pkey);
 }
 
+static VALUE
+pkey_ctx_apply_options_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, ctx_v))
+{
+    VALUE key = rb_ary_entry(i, 0), value = rb_ary_entry(i, 1);
+    EVP_PKEY_CTX *ctx = (EVP_PKEY_CTX *)ctx_v;
+
+    if (SYMBOL_P(key))
+        key = rb_sym2str(key);
+    value = rb_String(value);
+
+    if (EVP_PKEY_CTX_ctrl_str(ctx, StringValueCStr(key), StringValueCStr(value)) <= 0)
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_ctrl_str(ctx, %+"PRIsVALUE", %+"PRIsVALUE")",
+                   key, value);
+    return Qnil;
+}
+
+static VALUE
+pkey_ctx_apply_options0(VALUE args_v)
+{
+    VALUE *args = (VALUE *)args_v;
+    Check_Type(args[1], T_HASH);
+
+    rb_block_call(args[1], rb_intern("each"), 0, NULL,
+                  pkey_ctx_apply_options_i, args[0]);
+    return Qnil;
+}
+
+static void
+pkey_ctx_apply_options(EVP_PKEY_CTX *ctx, VALUE options, int *state)
+{
+    VALUE args[2];
+    args[0] = (VALUE)ctx;
+    args[1] = options;
+
+    rb_protect(pkey_ctx_apply_options0, (VALUE)args, state);
+}
+
+struct pkey_blocking_generate_arg {
+    EVP_PKEY_CTX *ctx;
+    EVP_PKEY *pkey;
+    int state;
+    unsigned int yield: 1;
+    unsigned int genparam: 1;
+    unsigned int interrupted: 1;
+};
+
+static VALUE
+pkey_gen_cb_yield(VALUE ctx_v)
+{
+    EVP_PKEY_CTX *ctx = (void *)ctx_v;
+    int i, info_num;
+    VALUE *argv;
+
+    info_num = EVP_PKEY_CTX_get_keygen_info(ctx, -1);
+    argv = ALLOCA_N(VALUE, info_num);
+    for (i = 0; i < info_num; i++)
+        argv[i] = INT2NUM(EVP_PKEY_CTX_get_keygen_info(ctx, i));
+
+    return rb_yield_values2(info_num, argv);
+}
+
+static VALUE
+call_check_ints0(VALUE arg)
+{
+    rb_thread_check_ints();
+    return Qnil;
+}
+
+static void *
+call_check_ints(void *arg)
+{
+    int state;
+    rb_protect(call_check_ints0, Qnil, &state);
+    return (void *)(VALUE)state;
+}
+
+static int
+pkey_gen_cb(EVP_PKEY_CTX *ctx)
+{
+    struct pkey_blocking_generate_arg *arg = EVP_PKEY_CTX_get_app_data(ctx);
+    int state;
+
+    if (arg->yield) {
+        rb_protect(pkey_gen_cb_yield, (VALUE)ctx, &state);
+        if (state) {
+            arg->state = state;
+            return 0;
+        }
+    }
+    if (arg->interrupted) {
+        arg->interrupted = 0;
+        state = (int)(VALUE)rb_thread_call_with_gvl(call_check_ints, NULL);
+        if (state) {
+            arg->state = state;
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static void
+pkey_blocking_gen_stop(void *ptr)
+{
+    struct pkey_blocking_generate_arg *arg = ptr;
+    arg->interrupted = 1;
+}
+
+static void *
+pkey_blocking_gen(void *ptr)
+{
+    struct pkey_blocking_generate_arg *arg = ptr;
+
+    if (arg->genparam && EVP_PKEY_paramgen(arg->ctx, &arg->pkey) <= 0)
+        return NULL;
+    if (!arg->genparam && EVP_PKEY_keygen(arg->ctx, &arg->pkey) <= 0)
+        return NULL;
+    return arg->pkey;
+}
+
+static VALUE
+pkey_generate(int argc, VALUE *argv, VALUE self, int genparam)
+{
+    EVP_PKEY_CTX *ctx;
+    VALUE alg, options;
+    struct pkey_blocking_generate_arg gen_arg = { 0 };
+    int state;
+
+    rb_scan_args(argc, argv, "11", &alg, &options);
+    if (rb_obj_is_kind_of(alg, cPKey)) {
+        EVP_PKEY *base_pkey;
+
+        GetPKey(alg, base_pkey);
+        ctx = EVP_PKEY_CTX_new(base_pkey, NULL/* engine */);
+        if (!ctx)
+            ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
+    }
+    else {
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+        ctx = EVP_PKEY_CTX_new_from_name(NULL, StringValueCStr(alg), NULL);
+        if (!ctx)
+            ossl_raise(ePKeyError, "EVP_PKEY_CTX_new_from_name");
+#else
+        const EVP_PKEY_ASN1_METHOD *ameth;
+        ENGINE *tmpeng;
+        int pkey_id;
+
+        StringValue(alg);
+        ameth = EVP_PKEY_asn1_find_str(&tmpeng, RSTRING_PTR(alg),
+                                       RSTRING_LENINT(alg));
+        if (!ameth)
+            ossl_raise(ePKeyError, "algorithm %"PRIsVALUE" not found", alg);
+        EVP_PKEY_asn1_get0_info(&pkey_id, NULL, NULL, NULL, NULL, ameth);
+#if !defined(OPENSSL_NO_ENGINE)
+        if (tmpeng)
+            ENGINE_finish(tmpeng);
+#endif
+
+        ctx = EVP_PKEY_CTX_new_id(pkey_id, NULL/* engine */);
+        if (!ctx)
+            ossl_raise(ePKeyError, "EVP_PKEY_CTX_new_id");
+#endif
+    }
+
+    if (genparam && EVP_PKEY_paramgen_init(ctx) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_paramgen_init");
+    }
+    if (!genparam && EVP_PKEY_keygen_init(ctx) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_keygen_init");
+    }
+
+    if (!NIL_P(options)) {
+        pkey_ctx_apply_options(ctx, options, &state);
+        if (state) {
+            EVP_PKEY_CTX_free(ctx);
+            rb_jump_tag(state);
+        }
+    }
+
+    gen_arg.genparam = genparam;
+    gen_arg.ctx = ctx;
+    gen_arg.yield = rb_block_given_p();
+    EVP_PKEY_CTX_set_app_data(ctx, &gen_arg);
+    EVP_PKEY_CTX_set_cb(ctx, pkey_gen_cb);
+    if (gen_arg.yield)
+        pkey_blocking_gen(&gen_arg);
+    else
+        rb_thread_call_without_gvl(pkey_blocking_gen, &gen_arg,
+                                   pkey_blocking_gen_stop, &gen_arg);
+    EVP_PKEY_CTX_free(ctx);
+    if (!gen_arg.pkey) {
+        if (gen_arg.state) {
+            ossl_clear_error();
+            rb_jump_tag(gen_arg.state);
+        }
+        else {
+            ossl_raise(ePKeyError, genparam ? "EVP_PKEY_paramgen" : "EVP_PKEY_keygen");
+        }
+    }
+
+    return ossl_pkey_new(gen_arg.pkey);
+}
+
+/*
+ * call-seq:
+ *    OpenSSL::PKey.generate_parameters(algo_name [, options]) -> pkey
+ *
+ * Generates new parameters for the algorithm. _algo_name_ is a String that
+ * represents the algorithm. The optional argument _options_ is a Hash that
+ * specifies the options specific to the algorithm. The order of the options
+ * can be important.
+ *
+ * A block can be passed optionally. The meaning of the arguments passed to
+ * the block varies depending on the implementation of the algorithm. The block
+ * may be called once or multiple times, or may not even be called.
+ *
+ * For the supported options, see the documentation for the 'openssl genpkey'
+ * utility command.
+ *
+ * == Example
+ *   pkey = OpenSSL::PKey.generate_parameters("DSA", "dsa_paramgen_bits" => 2048)
+ *   p pkey.p.num_bits #=> 2048
+ */
+static VALUE
+ossl_pkey_s_generate_parameters(int argc, VALUE *argv, VALUE self)
+{
+    return pkey_generate(argc, argv, self, 1);
+}
+
+/*
+ * call-seq:
+ *    OpenSSL::PKey.generate_key(algo_name [, options]) -> pkey
+ *    OpenSSL::PKey.generate_key(pkey [, options]) -> pkey
+ *
+ * Generates a new key (pair).
+ *
+ * If a String is given as the first argument, it generates a new random key
+ * for the algorithm specified by the name just as ::generate_parameters does.
+ * If an OpenSSL::PKey::PKey is given instead, it generates a new random key
+ * for the same algorithm as the key, using the parameters the key contains.
+ *
+ * See ::generate_parameters for the details of _options_ and the given block.
+ *
+ * == Example
+ *   pkey_params = OpenSSL::PKey.generate_parameters("DSA", "dsa_paramgen_bits" => 2048)
+ *   pkey_params.priv_key #=> nil
+ *   pkey = OpenSSL::PKey.generate_key(pkey_params)
+ *   pkey.priv_key #=> #<OpenSSL::BN 6277...
+ */
+static VALUE
+ossl_pkey_s_generate_key(int argc, VALUE *argv, VALUE self)
+{
+    return pkey_generate(argc, argv, self, 0);
+}
+
+/*
+ * TODO: There is no convenient way to check the presence of public key
+ * components on OpenSSL 3.0. But since keys are immutable on 3.0, pkeys without
+ * these should only be created by OpenSSL::PKey.generate_parameters or by
+ * parsing DER-/PEM-encoded string. We would need another flag for that.
+ */
 void
 ossl_pkey_check_public_key(const EVP_PKEY *pkey)
 {
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+    if (EVP_PKEY_missing_parameters(pkey))
+        ossl_raise(ePKeyError, "parameters missing");
+#else
     void *ptr;
     const BIGNUM *n, *e, *pubkey;
 
@@ -223,6 +546,7 @@
 	return;
     }
     ossl_raise(ePKeyError, "public key missing");
+#endif
 }
 
 EVP_PKEY *
@@ -240,12 +564,19 @@
 {
     EVP_PKEY *pkey;
 
-    if (rb_funcallv(obj, id_private_q, 0, NULL) != Qtrue) {
-	ossl_raise(rb_eArgError, "Private key is needed.");
-    }
     GetPKey(obj, pkey);
+    if (OSSL_PKEY_IS_PRIVATE(obj))
+        return pkey;
+    /*
+     * The EVP API does not provide a way to check if the EVP_PKEY has private
+     * components. Assuming it does...
+     */
+    if (!rb_respond_to(obj, id_private_q))
+        return pkey;
+    if (RTEST(rb_funcallv(obj, id_private_q, 0, NULL)))
+        return pkey;
 
-    return pkey;
+    rb_raise(rb_eArgError, "private key is needed");
 }
 
 EVP_PKEY *
@@ -265,16 +596,7 @@
 static VALUE
 ossl_pkey_alloc(VALUE klass)
 {
-    EVP_PKEY *pkey;
-    VALUE obj;
-
-    obj = NewPKey(klass);
-    if (!(pkey = EVP_PKEY_new())) {
-	ossl_raise(ePKeyError, NULL);
-    }
-    SetPKey(obj, pkey);
-
-    return obj;
+    return TypedData_Wrap_Struct(klass, &ossl_evp_pkey_type, NULL);
 }
 
 /*
@@ -293,119 +615,1058 @@
     return self;
 }
 
+#ifdef HAVE_EVP_PKEY_DUP
+static VALUE
+ossl_pkey_initialize_copy(VALUE self, VALUE other)
+{
+    EVP_PKEY *pkey, *pkey_other;
+
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    TypedData_Get_Struct(other, EVP_PKEY, &ossl_evp_pkey_type, pkey_other);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
+    if (pkey_other) {
+        pkey = EVP_PKEY_dup(pkey_other);
+        if (!pkey)
+            ossl_raise(ePKeyError, "EVP_PKEY_dup");
+        RTYPEDDATA_DATA(self) = pkey;
+    }
+    return self;
+}
+#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:
- *      pkey.sign(digest, data) -> String
+ *      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
  *
- * To sign the String _data_, _digest_, an instance of OpenSSL::Digest, must
- * be provided. The return value is again a String containing the signature.
- * A PKeyError is raised should errors occur.
- * Any previous state of the Digest instance is irrelevant to the signature
- * outcome, the digest instance is reset to its initial state during the
- * operation.
+ * Returns the short name of the OID associated with _pkey_.
+ */
+static VALUE
+ossl_pkey_oid(VALUE self)
+{
+    EVP_PKEY *pkey;
+    int nid;
+
+    GetPKey(self, pkey);
+    nid = EVP_PKEY_id(pkey);
+    return rb_str_new_cstr(OBJ_nid2sn(nid));
+}
+
+/*
+ * call-seq:
+ *    pkey.inspect -> string
+ *
+ * Returns a string describing the PKey object.
+ */
+static VALUE
+ossl_pkey_inspect(VALUE self)
+{
+    EVP_PKEY *pkey;
+    int nid;
+
+    GetPKey(self, pkey);
+    nid = EVP_PKEY_id(pkey);
+    return rb_sprintf("#<%"PRIsVALUE":%p oid=%s>",
+                      rb_class_name(CLASS_OF(self)), (void *)self,
+                      OBJ_nid2sn(nid));
+}
+
+/*
+ * call-seq:
+ *    pkey.to_text -> string
+ *
+ * Dumps key parameters, public key, and private key components contained in
+ * the key into a human-readable text.
+ *
+ * This is intended for debugging purpose.
+ *
+ * See also the man page EVP_PKEY_print_private(3).
+ */
+static VALUE
+ossl_pkey_to_text(VALUE self)
+{
+    EVP_PKEY *pkey;
+    BIO *bio;
+
+    GetPKey(self, pkey);
+    if (!(bio = BIO_new(BIO_s_mem())))
+        ossl_raise(ePKeyError, "BIO_new");
+
+    if (EVP_PKEY_print_private(bio, pkey, 0, NULL) == 1)
+        goto out;
+    OSSL_BIO_reset(bio);
+    if (EVP_PKEY_print_public(bio, pkey, 0, NULL) == 1)
+        goto out;
+    OSSL_BIO_reset(bio);
+    if (EVP_PKEY_print_params(bio, pkey, 0, NULL) == 1)
+        goto out;
+
+    BIO_free(bio);
+    ossl_raise(ePKeyError, "EVP_PKEY_print_params");
+
+  out:
+    return ossl_membio2str(bio);
+}
+
+VALUE
+ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self, int to_der)
+{
+    EVP_PKEY *pkey;
+    VALUE cipher, pass;
+    const EVP_CIPHER *enc = NULL;
+    BIO *bio;
+
+    GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "02", &cipher, &pass);
+    if (!NIL_P(cipher)) {
+	enc = ossl_evp_get_cipherbyname(cipher);
+	pass = ossl_pem_passwd_value(pass);
+    }
+
+    bio = BIO_new(BIO_s_mem());
+    if (!bio)
+	ossl_raise(ePKeyError, "BIO_new");
+    if (to_der) {
+	if (!i2d_PrivateKey_bio(bio, pkey)) {
+	    BIO_free(bio);
+	    ossl_raise(ePKeyError, "i2d_PrivateKey_bio");
+	}
+    }
+    else {
+#if OSSL_OPENSSL_PREREQ(1, 1, 0) || OSSL_LIBRESSL_PREREQ(3, 5, 0)
+	if (!PEM_write_bio_PrivateKey_traditional(bio, pkey, enc, NULL, 0,
+						  ossl_pem_passwd_cb,
+						  (void *)pass)) {
+#else
+	char pem_str[80];
+	const char *aname;
+
+	EVP_PKEY_asn1_get0_info(NULL, NULL, NULL, NULL, &aname, pkey->ameth);
+	snprintf(pem_str, sizeof(pem_str), "%s PRIVATE KEY", aname);
+	if (!PEM_ASN1_write_bio((i2d_of_void *)i2d_PrivateKey, pem_str, bio,
+				pkey, enc, NULL, 0, ossl_pem_passwd_cb,
+				(void *)pass)) {
+#endif
+	    BIO_free(bio);
+	    ossl_raise(ePKeyError, "PEM_write_bio_PrivateKey_traditional");
+	}
+    }
+    return ossl_membio2str(bio);
+}
+
+static VALUE
+do_pkcs8_export(int argc, VALUE *argv, VALUE self, int to_der)
+{
+    EVP_PKEY *pkey;
+    VALUE cipher, pass;
+    const EVP_CIPHER *enc = NULL;
+    BIO *bio;
+
+    GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "02", &cipher, &pass);
+    if (argc > 0) {
+	/*
+	 * TODO: EncryptedPrivateKeyInfo actually has more options.
+	 * Should they be exposed?
+	 */
+	enc = ossl_evp_get_cipherbyname(cipher);
+	pass = ossl_pem_passwd_value(pass);
+    }
+
+    bio = BIO_new(BIO_s_mem());
+    if (!bio)
+	ossl_raise(ePKeyError, "BIO_new");
+    if (to_der) {
+	if (!i2d_PKCS8PrivateKey_bio(bio, pkey, enc, NULL, 0,
+				     ossl_pem_passwd_cb, (void *)pass)) {
+	    BIO_free(bio);
+	    ossl_raise(ePKeyError, "i2d_PKCS8PrivateKey_bio");
+	}
+    }
+    else {
+	if (!PEM_write_bio_PKCS8PrivateKey(bio, pkey, enc, NULL, 0,
+					   ossl_pem_passwd_cb, (void *)pass)) {
+	    BIO_free(bio);
+	    ossl_raise(ePKeyError, "PEM_write_bio_PKCS8PrivateKey");
+	}
+    }
+    return ossl_membio2str(bio);
+}
+
+/*
+ * call-seq:
+ *    pkey.private_to_der                   -> string
+ *    pkey.private_to_der(cipher, password) -> string
+ *
+ * Serializes the private key to DER-encoded PKCS #8 format. If called without
+ * arguments, unencrypted PKCS #8 PrivateKeyInfo format is used. If called with
+ * a cipher name and a password, PKCS #8 EncryptedPrivateKeyInfo format with
+ * PBES2 encryption scheme is used.
+ */
+static VALUE
+ossl_pkey_private_to_der(int argc, VALUE *argv, VALUE self)
+{
+    return do_pkcs8_export(argc, argv, self, 1);
+}
+
+/*
+ * call-seq:
+ *    pkey.private_to_pem                   -> string
+ *    pkey.private_to_pem(cipher, password) -> string
+ *
+ * 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)
+{
+    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)
+{
+    EVP_PKEY *pkey;
+    BIO *bio;
+
+    GetPKey(self, pkey);
+    bio = BIO_new(BIO_s_mem());
+    if (!bio)
+	ossl_raise(ePKeyError, "BIO_new");
+    if (to_der) {
+	if (!i2d_PUBKEY_bio(bio, pkey)) {
+	    BIO_free(bio);
+	    ossl_raise(ePKeyError, "i2d_PUBKEY_bio");
+	}
+    }
+    else {
+	if (!PEM_write_bio_PUBKEY(bio, pkey)) {
+	    BIO_free(bio);
+	    ossl_raise(ePKeyError, "PEM_write_bio_PUBKEY");
+	}
+    }
+    return ossl_membio2str(bio);
+}
+
+/*
+ * call-seq:
+ *    pkey.public_to_der -> string
+ *
+ * Serializes the public key to DER-encoded X.509 SubjectPublicKeyInfo format.
+ */
+static VALUE
+ossl_pkey_public_to_der(VALUE self)
+{
+    return ossl_pkey_export_spki(self, 1);
+}
+
+/*
+ * call-seq:
+ *    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)
+{
+    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
+ *
+ * Used primarily to check if an OpenSSL::X509::Certificate#public_key compares to its private key.
  *
  * == Example
- *   data = 'Sign me!'
- *   digest = OpenSSL::Digest::SHA256.new
- *   pkey = OpenSSL::PKey::RSA.new(2048)
- *   signature = pkey.sign(digest, data)
+ *   x509 = OpenSSL::X509::Certificate.new(pem_encoded_certificate)
+ *   rsa_key = OpenSSL::PKey::RSA.new(pem_encoded_private_key)
+ *
+ *   rsa_key.compare?(x509.public_key) => true | false
  */
 static VALUE
-ossl_pkey_sign(VALUE self, VALUE digest, VALUE data)
+ossl_pkey_compare(VALUE self, VALUE other)
+{
+    int ret;
+    EVP_PKEY *selfPKey;
+    EVP_PKEY *otherPKey;
+
+    GetPKey(self, selfPKey);
+    GetPKey(other, otherPKey);
+
+    /* Explicitly check the key type given EVP_PKEY_ASN1_METHOD(3)
+     * docs param_cmp could return any negative number.
+     */
+    if (EVP_PKEY_id(selfPKey) != EVP_PKEY_id(otherPKey))
+        ossl_raise(rb_eTypeError, "cannot match different PKey types");
+
+    ret = EVP_PKEY_eq(selfPKey, otherPKey);
+
+    if (ret == 0)
+        return Qfalse;
+    else if (ret == 1)
+        return Qtrue;
+    else
+        ossl_raise(ePKeyError, "EVP_PKEY_eq");
+}
+
+/*
+ * call-seq:
+ *    pkey.sign(digest, data [, options]) -> string
+ *
+ * Hashes and signs the +data+ using a message digest algorithm +digest+ and
+ * a private key +pkey+.
+ *
+ * See #verify for the verification operation.
+ *
+ * See also the man page EVP_DigestSign(3).
+ *
+ * +digest+::
+ *   A String that represents the message digest algorithm name, or +nil+
+ *   if the PKey type requires no digest algorithm.
+ *   For backwards compatibility, this can be an instance of OpenSSL::Digest.
+ *   Its state will not affect the signature.
+ * +data+::
+ *   A String. The data to be hashed and signed.
+ * +options+::
+ *   A Hash that contains algorithm specific control operations to \OpenSSL.
+ *   See OpenSSL's man page EVP_PKEY_CTX_ctrl_str(3) for details.
+ *   +options+ parameter was added in version 3.0.
+ *
+ * Example:
+ *   data = "Sign me!"
+ *   pkey = OpenSSL::PKey.generate_key("RSA", rsa_keygen_bits: 2048)
+ *   signopts = { rsa_padding_mode: "pss" }
+ *   signature = pkey.sign("SHA256", data, signopts)
+ *
+ *   # Creates a copy of the RSA key pkey, but without the private components
+ *   pub_key = pkey.public_key
+ *   puts pub_key.verify("SHA256", signature, data, signopts) # => true
+ */
+static VALUE
+ossl_pkey_sign(int argc, VALUE *argv, VALUE self)
 {
     EVP_PKEY *pkey;
-    const EVP_MD *md;
+    VALUE digest, data, options, sig;
+    const EVP_MD *md = NULL;
     EVP_MD_CTX *ctx;
-    unsigned int buf_len;
-    VALUE str;
-    int result;
+    EVP_PKEY_CTX *pctx;
+    size_t siglen;
+    int state;
 
     pkey = GetPrivPKeyPtr(self);
-    md = ossl_evp_get_digestbyname(digest);
+    rb_scan_args(argc, argv, "21", &digest, &data, &options);
+    if (!NIL_P(digest))
+        md = ossl_evp_get_digestbyname(digest);
     StringValue(data);
-    str = rb_str_new(0, EVP_PKEY_size(pkey));
 
     ctx = EVP_MD_CTX_new();
     if (!ctx)
-	ossl_raise(ePKeyError, "EVP_MD_CTX_new");
-    if (!EVP_SignInit_ex(ctx, md, NULL)) {
-	EVP_MD_CTX_free(ctx);
-	ossl_raise(ePKeyError, "EVP_SignInit_ex");
-    }
-    if (!EVP_SignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) {
-	EVP_MD_CTX_free(ctx);
-	ossl_raise(ePKeyError, "EVP_SignUpdate");
+        ossl_raise(ePKeyError, "EVP_MD_CTX_new");
+    if (EVP_DigestSignInit(ctx, &pctx, md, /* engine */NULL, pkey) < 1) {
+        EVP_MD_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_DigestSignInit");
+    }
+    if (!NIL_P(options)) {
+        pkey_ctx_apply_options(pctx, options, &state);
+        if (state) {
+            EVP_MD_CTX_free(ctx);
+            rb_jump_tag(state);
+        }
+    }
+#if OSSL_OPENSSL_PREREQ(1, 1, 1) || OSSL_LIBRESSL_PREREQ(3, 4, 0)
+    if (EVP_DigestSign(ctx, NULL, &siglen, (unsigned char *)RSTRING_PTR(data),
+                       RSTRING_LEN(data)) < 1) {
+        EVP_MD_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_DigestSign");
+    }
+    if (siglen > LONG_MAX) {
+        EVP_MD_CTX_free(ctx);
+        rb_raise(ePKeyError, "signature would be too large");
+    }
+    sig = ossl_str_new(NULL, (long)siglen, &state);
+    if (state) {
+        EVP_MD_CTX_free(ctx);
+        rb_jump_tag(state);
+    }
+    if (EVP_DigestSign(ctx, (unsigned char *)RSTRING_PTR(sig), &siglen,
+                       (unsigned char *)RSTRING_PTR(data),
+                       RSTRING_LEN(data)) < 1) {
+        EVP_MD_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_DigestSign");
+    }
+#else
+    if (EVP_DigestSignUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) {
+        EVP_MD_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_DigestSignUpdate");
+    }
+    if (EVP_DigestSignFinal(ctx, NULL, &siglen) < 1) {
+        EVP_MD_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_DigestSignFinal");
     }
-    result = EVP_SignFinal(ctx, (unsigned char *)RSTRING_PTR(str), &buf_len, pkey);
+    if (siglen > LONG_MAX) {
+        EVP_MD_CTX_free(ctx);
+        rb_raise(ePKeyError, "signature would be too large");
+    }
+    sig = ossl_str_new(NULL, (long)siglen, &state);
+    if (state) {
+        EVP_MD_CTX_free(ctx);
+        rb_jump_tag(state);
+    }
+    if (EVP_DigestSignFinal(ctx, (unsigned char *)RSTRING_PTR(sig),
+                            &siglen) < 1) {
+        EVP_MD_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_DigestSignFinal");
+    }
+#endif
     EVP_MD_CTX_free(ctx);
-    if (!result)
-	ossl_raise(ePKeyError, "EVP_SignFinal");
-    rb_str_set_len(str, buf_len);
-
-    return str;
+    rb_str_set_len(sig, siglen);
+    return sig;
 }
 
 /*
- *  call-seq:
- *      pkey.verify(digest, signature, data) -> String
+ * call-seq:
+ *    pkey.verify(digest, signature, data [, options]) -> true or false
  *
- * To verify the String _signature_, _digest_, an instance of
- * OpenSSL::Digest, must be provided to re-compute the message digest of the
- * original _data_, also a String. The return value is +true+ if the
- * signature is valid, +false+ otherwise. A PKeyError is raised should errors
- * occur.
- * Any previous state of the Digest instance is irrelevant to the validation
- * outcome, the digest instance is reset to its initial state during the
- * operation.
+ * Verifies the +signature+ for the +data+ using a message digest algorithm
+ * +digest+ and a public key +pkey+.
  *
- * == Example
- *   data = 'Sign me!'
- *   digest = OpenSSL::Digest::SHA256.new
- *   pkey = OpenSSL::PKey::RSA.new(2048)
- *   signature = pkey.sign(digest, data)
- *   pub_key = pkey.public_key
- *   puts pub_key.verify(digest, signature, data) # => true
+ * Returns +true+ if the signature is successfully verified, +false+ otherwise.
+ * The caller must check the return value.
+ *
+ * See #sign for the signing operation and an example.
+ *
+ * See also the man page EVP_DigestVerify(3).
+ *
+ * +digest+::
+ *   See #sign.
+ * +signature+::
+ *   A String containing the signature to be verified.
+ * +data+::
+ *   See #sign.
+ * +options+::
+ *   See #sign. +options+ parameter was added in version 3.0.
  */
 static VALUE
-ossl_pkey_verify(VALUE self, VALUE digest, VALUE sig, VALUE data)
+ossl_pkey_verify(int argc, VALUE *argv, VALUE self)
 {
     EVP_PKEY *pkey;
-    const EVP_MD *md;
+    VALUE digest, sig, data, options;
+    const EVP_MD *md = NULL;
     EVP_MD_CTX *ctx;
-    int siglen, result;
+    EVP_PKEY_CTX *pctx;
+    int state, ret;
 
     GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "31", &digest, &sig, &data, &options);
     ossl_pkey_check_public_key(pkey);
-    md = ossl_evp_get_digestbyname(digest);
+    if (!NIL_P(digest))
+        md = ossl_evp_get_digestbyname(digest);
     StringValue(sig);
-    siglen = RSTRING_LENINT(sig);
     StringValue(data);
 
     ctx = EVP_MD_CTX_new();
     if (!ctx)
-	ossl_raise(ePKeyError, "EVP_MD_CTX_new");
-    if (!EVP_VerifyInit_ex(ctx, md, NULL)) {
-	EVP_MD_CTX_free(ctx);
-	ossl_raise(ePKeyError, "EVP_VerifyInit_ex");
-    }
-    if (!EVP_VerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data))) {
-	EVP_MD_CTX_free(ctx);
-	ossl_raise(ePKeyError, "EVP_VerifyUpdate");
+        ossl_raise(ePKeyError, "EVP_MD_CTX_new");
+    if (EVP_DigestVerifyInit(ctx, &pctx, md, /* engine */NULL, pkey) < 1) {
+        EVP_MD_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_DigestVerifyInit");
+    }
+    if (!NIL_P(options)) {
+        pkey_ctx_apply_options(pctx, options, &state);
+        if (state) {
+            EVP_MD_CTX_free(ctx);
+            rb_jump_tag(state);
+        }
     }
-    result = EVP_VerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig), siglen, pkey);
+#if OSSL_OPENSSL_PREREQ(1, 1, 1) || OSSL_LIBRESSL_PREREQ(3, 4, 0)
+    ret = EVP_DigestVerify(ctx, (unsigned char *)RSTRING_PTR(sig),
+                           RSTRING_LEN(sig), (unsigned char *)RSTRING_PTR(data),
+                           RSTRING_LEN(data));
     EVP_MD_CTX_free(ctx);
-    switch (result) {
-    case 0:
-	ossl_clear_error();
-	return Qfalse;
-    case 1:
-	return Qtrue;
-    default:
-	ossl_raise(ePKeyError, "EVP_VerifyFinal");
+    if (ret < 0)
+        ossl_raise(ePKeyError, "EVP_DigestVerify");
+#else
+    if (EVP_DigestVerifyUpdate(ctx, RSTRING_PTR(data), RSTRING_LEN(data)) < 1) {
+        EVP_MD_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_DigestVerifyUpdate");
+    }
+    ret = EVP_DigestVerifyFinal(ctx, (unsigned char *)RSTRING_PTR(sig),
+                                RSTRING_LEN(sig));
+    EVP_MD_CTX_free(ctx);
+    if (ret < 0)
+        ossl_raise(ePKeyError, "EVP_DigestVerifyFinal");
+#endif
+    if (ret)
+        return Qtrue;
+    else {
+        ossl_clear_error();
+        return Qfalse;
     }
 }
 
 /*
+ * call-seq:
+ *    pkey.sign_raw(digest, data [, options]) -> string
+ *
+ * Signs +data+ using a private key +pkey+. Unlike #sign, +data+ will not be
+ * hashed by +digest+ automatically.
+ *
+ * See #verify_raw for the verification operation.
+ *
+ * Added in version 3.0. See also the man page EVP_PKEY_sign(3).
+ *
+ * +digest+::
+ *   A String that represents the message digest algorithm name, or +nil+
+ *   if the PKey type requires no digest algorithm.
+ *   Although this method will not hash +data+ with it, this parameter may still
+ *   be required depending on the signature algorithm.
+ * +data+::
+ *   A String. The data to be signed.
+ * +options+::
+ *   A Hash that contains algorithm specific control operations to \OpenSSL.
+ *   See OpenSSL's man page EVP_PKEY_CTX_ctrl_str(3) for details.
+ *
+ * Example:
+ *   data = "Sign me!"
+ *   hash = OpenSSL::Digest.digest("SHA256", data)
+ *   pkey = OpenSSL::PKey.generate_key("RSA", rsa_keygen_bits: 2048)
+ *   signopts = { rsa_padding_mode: "pss" }
+ *   signature = pkey.sign_raw("SHA256", hash, signopts)
+ *
+ *   # Creates a copy of the RSA key pkey, but without the private components
+ *   pub_key = pkey.public_key
+ *   puts pub_key.verify_raw("SHA256", signature, hash, signopts) # => true
+ */
+static VALUE
+ossl_pkey_sign_raw(int argc, VALUE *argv, VALUE self)
+{
+    EVP_PKEY *pkey;
+    VALUE digest, data, options, sig;
+    const EVP_MD *md = NULL;
+    EVP_PKEY_CTX *ctx;
+    size_t outlen;
+    int state;
+
+    GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "21", &digest, &data, &options);
+    if (!NIL_P(digest))
+        md = ossl_evp_get_digestbyname(digest);
+    StringValue(data);
+
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
+    if (!ctx)
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
+    if (EVP_PKEY_sign_init(ctx) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_sign_init");
+    }
+    if (md && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_set_signature_md");
+    }
+    if (!NIL_P(options)) {
+        pkey_ctx_apply_options(ctx, options, &state);
+        if (state) {
+            EVP_PKEY_CTX_free(ctx);
+            rb_jump_tag(state);
+        }
+    }
+    if (EVP_PKEY_sign(ctx, NULL, &outlen, (unsigned char *)RSTRING_PTR(data),
+                      RSTRING_LEN(data)) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_sign");
+    }
+    if (outlen > LONG_MAX) {
+        EVP_PKEY_CTX_free(ctx);
+        rb_raise(ePKeyError, "signature would be too large");
+    }
+    sig = ossl_str_new(NULL, (long)outlen, &state);
+    if (state) {
+        EVP_PKEY_CTX_free(ctx);
+        rb_jump_tag(state);
+    }
+    if (EVP_PKEY_sign(ctx, (unsigned char *)RSTRING_PTR(sig), &outlen,
+                      (unsigned char *)RSTRING_PTR(data),
+                      RSTRING_LEN(data)) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_sign");
+    }
+    EVP_PKEY_CTX_free(ctx);
+    rb_str_set_len(sig, outlen);
+    return sig;
+}
+
+/*
+ * call-seq:
+ *    pkey.verify_raw(digest, signature, data [, options]) -> true or false
+ *
+ * Verifies the +signature+ for the +data+ using a public key +pkey+. Unlike
+ * #verify, this method will not hash +data+ with +digest+ automatically.
+ *
+ * Returns +true+ if the signature is successfully verified, +false+ otherwise.
+ * The caller must check the return value.
+ *
+ * See #sign_raw for the signing operation and an example code.
+ *
+ * Added in version 3.0. See also the man page EVP_PKEY_verify(3).
+ *
+ * +signature+::
+ *   A String containing the signature to be verified.
+ */
+static VALUE
+ossl_pkey_verify_raw(int argc, VALUE *argv, VALUE self)
+{
+    EVP_PKEY *pkey;
+    VALUE digest, sig, data, options;
+    const EVP_MD *md = NULL;
+    EVP_PKEY_CTX *ctx;
+    int state, ret;
+
+    GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "31", &digest, &sig, &data, &options);
+    ossl_pkey_check_public_key(pkey);
+    if (!NIL_P(digest))
+        md = ossl_evp_get_digestbyname(digest);
+    StringValue(sig);
+    StringValue(data);
+
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
+    if (!ctx)
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
+    if (EVP_PKEY_verify_init(ctx) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_verify_init");
+    }
+    if (md && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_set_signature_md");
+    }
+    if (!NIL_P(options)) {
+        pkey_ctx_apply_options(ctx, options, &state);
+        if (state) {
+            EVP_PKEY_CTX_free(ctx);
+            rb_jump_tag(state);
+        }
+    }
+    ret = EVP_PKEY_verify(ctx, (unsigned char *)RSTRING_PTR(sig),
+                          RSTRING_LEN(sig),
+                          (unsigned char *)RSTRING_PTR(data),
+                          RSTRING_LEN(data));
+    EVP_PKEY_CTX_free(ctx);
+    if (ret < 0)
+        ossl_raise(ePKeyError, "EVP_PKEY_verify");
+
+    if (ret)
+        return Qtrue;
+    else {
+        ossl_clear_error();
+        return Qfalse;
+    }
+}
+
+/*
+ * call-seq:
+ *    pkey.verify_recover(digest, signature [, options]) -> string
+ *
+ * Recovers the signed data from +signature+ using a public key +pkey+. Not all
+ * signature algorithms support this operation.
+ *
+ * Added in version 3.0. See also the man page EVP_PKEY_verify_recover(3).
+ *
+ * +signature+::
+ *   A String containing the signature to be verified.
+ */
+static VALUE
+ossl_pkey_verify_recover(int argc, VALUE *argv, VALUE self)
+{
+    EVP_PKEY *pkey;
+    VALUE digest, sig, options, out;
+    const EVP_MD *md = NULL;
+    EVP_PKEY_CTX *ctx;
+    int state;
+    size_t outlen;
+
+    GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "21", &digest, &sig, &options);
+    ossl_pkey_check_public_key(pkey);
+    if (!NIL_P(digest))
+        md = ossl_evp_get_digestbyname(digest);
+    StringValue(sig);
+
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
+    if (!ctx)
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
+    if (EVP_PKEY_verify_recover_init(ctx) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_verify_recover_init");
+    }
+    if (md && EVP_PKEY_CTX_set_signature_md(ctx, md) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_set_signature_md");
+    }
+    if (!NIL_P(options)) {
+        pkey_ctx_apply_options(ctx, options, &state);
+        if (state) {
+            EVP_PKEY_CTX_free(ctx);
+            rb_jump_tag(state);
+        }
+    }
+    if (EVP_PKEY_verify_recover(ctx, NULL, &outlen,
+                                (unsigned char *)RSTRING_PTR(sig),
+                                RSTRING_LEN(sig)) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_verify_recover");
+    }
+    out = ossl_str_new(NULL, (long)outlen, &state);
+    if (state) {
+        EVP_PKEY_CTX_free(ctx);
+        rb_jump_tag(state);
+    }
+    if (EVP_PKEY_verify_recover(ctx, (unsigned char *)RSTRING_PTR(out), &outlen,
+                                (unsigned char *)RSTRING_PTR(sig),
+                                RSTRING_LEN(sig)) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_verify_recover");
+    }
+    EVP_PKEY_CTX_free(ctx);
+    rb_str_set_len(out, outlen);
+    return out;
+}
+
+/*
+ * call-seq:
+ *    pkey.derive(peer_pkey) -> string
+ *
+ * Derives a shared secret from _pkey_ and _peer_pkey_. _pkey_ must contain
+ * the private components, _peer_pkey_ must contain the public components.
+ */
+static VALUE
+ossl_pkey_derive(int argc, VALUE *argv, VALUE self)
+{
+    EVP_PKEY *pkey, *peer_pkey;
+    EVP_PKEY_CTX *ctx;
+    VALUE peer_pkey_obj, str;
+    size_t keylen;
+    int state;
+
+    GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "1", &peer_pkey_obj);
+    GetPKey(peer_pkey_obj, peer_pkey);
+
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
+    if (!ctx)
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
+    if (EVP_PKEY_derive_init(ctx) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_derive_init");
+    }
+    if (EVP_PKEY_derive_set_peer(ctx, peer_pkey) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_derive_set_peer");
+    }
+    if (EVP_PKEY_derive(ctx, NULL, &keylen) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_derive");
+    }
+    if (keylen > LONG_MAX)
+        rb_raise(ePKeyError, "derived key would be too large");
+    str = ossl_str_new(NULL, (long)keylen, &state);
+    if (state) {
+        EVP_PKEY_CTX_free(ctx);
+        rb_jump_tag(state);
+    }
+    if (EVP_PKEY_derive(ctx, (unsigned char *)RSTRING_PTR(str), &keylen) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_derive");
+    }
+    EVP_PKEY_CTX_free(ctx);
+    rb_str_set_len(str, keylen);
+    return str;
+}
+
+/*
+ * call-seq:
+ *    pkey.encrypt(data [, options]) -> string
+ *
+ * Performs a public key encryption operation using +pkey+.
+ *
+ * See #decrypt for the reverse operation.
+ *
+ * Added in version 3.0. See also the man page EVP_PKEY_encrypt(3).
+ *
+ * +data+::
+ *   A String to be encrypted.
+ * +options+::
+ *   A Hash that contains algorithm specific control operations to \OpenSSL.
+ *   See OpenSSL's man page EVP_PKEY_CTX_ctrl_str(3) for details.
+ *
+ * Example:
+ *   pkey = OpenSSL::PKey.generate_key("RSA", rsa_keygen_bits: 2048)
+ *   data = "secret data"
+ *   encrypted = pkey.encrypt(data, rsa_padding_mode: "oaep")
+ *   decrypted = pkey.decrypt(data, rsa_padding_mode: "oaep")
+ *   p decrypted #=> "secret data"
+ */
+static VALUE
+ossl_pkey_encrypt(int argc, VALUE *argv, VALUE self)
+{
+    EVP_PKEY *pkey;
+    EVP_PKEY_CTX *ctx;
+    VALUE data, options, str;
+    size_t outlen;
+    int state;
+
+    GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "11", &data, &options);
+    StringValue(data);
+
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
+    if (!ctx)
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
+    if (EVP_PKEY_encrypt_init(ctx) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_encrypt_init");
+    }
+    if (!NIL_P(options)) {
+        pkey_ctx_apply_options(ctx, options, &state);
+        if (state) {
+            EVP_PKEY_CTX_free(ctx);
+            rb_jump_tag(state);
+        }
+    }
+    if (EVP_PKEY_encrypt(ctx, NULL, &outlen,
+                         (unsigned char *)RSTRING_PTR(data),
+                         RSTRING_LEN(data)) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_encrypt");
+    }
+    if (outlen > LONG_MAX) {
+        EVP_PKEY_CTX_free(ctx);
+        rb_raise(ePKeyError, "encrypted data would be too large");
+    }
+    str = ossl_str_new(NULL, (long)outlen, &state);
+    if (state) {
+        EVP_PKEY_CTX_free(ctx);
+        rb_jump_tag(state);
+    }
+    if (EVP_PKEY_encrypt(ctx, (unsigned char *)RSTRING_PTR(str), &outlen,
+                         (unsigned char *)RSTRING_PTR(data),
+                         RSTRING_LEN(data)) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_encrypt");
+    }
+    EVP_PKEY_CTX_free(ctx);
+    rb_str_set_len(str, outlen);
+    return str;
+}
+
+/*
+ * call-seq:
+ *    pkey.decrypt(data [, options]) -> string
+ *
+ * Performs a public key decryption operation using +pkey+.
+ *
+ * See #encrypt for a description of the parameters and an example.
+ *
+ * Added in version 3.0. See also the man page EVP_PKEY_decrypt(3).
+ */
+static VALUE
+ossl_pkey_decrypt(int argc, VALUE *argv, VALUE self)
+{
+    EVP_PKEY *pkey;
+    EVP_PKEY_CTX *ctx;
+    VALUE data, options, str;
+    size_t outlen;
+    int state;
+
+    GetPKey(self, pkey);
+    rb_scan_args(argc, argv, "11", &data, &options);
+    StringValue(data);
+
+    ctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
+    if (!ctx)
+        ossl_raise(ePKeyError, "EVP_PKEY_CTX_new");
+    if (EVP_PKEY_decrypt_init(ctx) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_decrypt_init");
+    }
+    if (!NIL_P(options)) {
+        pkey_ctx_apply_options(ctx, options, &state);
+        if (state) {
+            EVP_PKEY_CTX_free(ctx);
+            rb_jump_tag(state);
+        }
+    }
+    if (EVP_PKEY_decrypt(ctx, NULL, &outlen,
+                         (unsigned char *)RSTRING_PTR(data),
+                         RSTRING_LEN(data)) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_decrypt");
+    }
+    if (outlen > LONG_MAX) {
+        EVP_PKEY_CTX_free(ctx);
+        rb_raise(ePKeyError, "decrypted data would be too large");
+    }
+    str = ossl_str_new(NULL, (long)outlen, &state);
+    if (state) {
+        EVP_PKEY_CTX_free(ctx);
+        rb_jump_tag(state);
+    }
+    if (EVP_PKEY_decrypt(ctx, (unsigned char *)RSTRING_PTR(str), &outlen,
+                         (unsigned char *)RSTRING_PTR(data),
+                         RSTRING_LEN(data)) <= 0) {
+        EVP_PKEY_CTX_free(ctx);
+        ossl_raise(ePKeyError, "EVP_PKEY_decrypt");
+    }
+    EVP_PKEY_CTX_free(ctx);
+    rb_str_set_len(str, outlen);
+    return str;
+}
+
+/*
  * INIT
  */
 void
@@ -488,12 +1749,41 @@
     cPKey = rb_define_class_under(mPKey, "PKey", rb_cObject);
 
     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);
+#ifdef HAVE_EVP_PKEY_DUP
+    rb_define_method(cPKey, "initialize_copy", ossl_pkey_initialize_copy, 1);
+#else
+    rb_undef_method(cPKey, "initialize_copy");
+#endif
+    rb_define_method(cPKey, "oid", ossl_pkey_oid, 0);
+    rb_define_method(cPKey, "inspect", ossl_pkey_inspect, 0);
+    rb_define_method(cPKey, "to_text", ossl_pkey_to_text, 0);
+    rb_define_method(cPKey, "private_to_der", ossl_pkey_private_to_der, -1);
+    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, 2);
-    rb_define_method(cPKey, "verify", ossl_pkey_verify, 3);
+    rb_define_method(cPKey, "sign", ossl_pkey_sign, -1);
+    rb_define_method(cPKey, "verify", ossl_pkey_verify, -1);
+    rb_define_method(cPKey, "sign_raw", ossl_pkey_sign_raw, -1);
+    rb_define_method(cPKey, "verify_raw", ossl_pkey_verify_raw, -1);
+    rb_define_method(cPKey, "verify_recover", ossl_pkey_verify_recover, -1);
+    rb_define_method(cPKey, "derive", ossl_pkey_derive, -1);
+    rb_define_method(cPKey, "encrypt", ossl_pkey_encrypt, -1);
+    rb_define_method(cPKey, "decrypt", ossl_pkey_decrypt, -1);
 
     id_private_q = rb_intern("private?");
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkey_dh.c ruby-2.5.9/ext/openssl/ossl_pkey_dh.c
--- ruby-2.5.9.orig/ext/openssl/ossl_pkey_dh.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkey_dh.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -27,219 +27,107 @@
  * Classes
  */
 VALUE cDH;
-VALUE eDHError;
-
-/*
- * Public
- */
-static VALUE
-dh_instance(VALUE klass, DH *dh)
-{
-    EVP_PKEY *pkey;
-    VALUE obj;
-
-    if (!dh) {
-	return Qfalse;
-    }
-    obj = NewPKey(klass);
-    if (!(pkey = EVP_PKEY_new())) {
-	return Qfalse;
-    }
-    if (!EVP_PKEY_assign_DH(pkey, dh)) {
-	EVP_PKEY_free(pkey);
-	return Qfalse;
-    }
-    SetPKey(obj, pkey);
-
-    return obj;
-}
-
-VALUE
-ossl_dh_new(EVP_PKEY *pkey)
-{
-    VALUE obj;
-
-    if (!pkey) {
-	obj = dh_instance(cDH, DH_new());
-    } else {
-	obj = NewPKey(cDH);
-	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH) {
-	    ossl_raise(rb_eTypeError, "Not a DH key!");
-	}
-	SetPKey(obj, pkey);
-    }
-    if (obj == Qfalse) {
-	ossl_raise(eDHError, NULL);
-    }
-
-    return obj;
-}
+static VALUE eDHError;
 
 /*
  * Private
  */
-struct dh_blocking_gen_arg {
-    DH *dh;
-    int size;
-    int gen;
-    BN_GENCB *cb;
-    int result;
-};
-
-static void *
-dh_blocking_gen(void *arg)
-{
-    struct dh_blocking_gen_arg *gen = (struct dh_blocking_gen_arg *)arg;
-    gen->result = DH_generate_parameters_ex(gen->dh, gen->size, gen->gen, gen->cb);
-    return 0;
-}
-
-static DH *
-dh_generate(int size, int gen)
-{
-    struct ossl_generate_cb_arg cb_arg = { 0 };
-    struct dh_blocking_gen_arg gen_arg;
-    DH *dh = DH_new();
-    BN_GENCB *cb = BN_GENCB_new();
-
-    if (!dh || !cb) {
-	DH_free(dh);
-	BN_GENCB_free(cb);
-	return NULL;
-    }
-
-    if (rb_block_given_p())
-	cb_arg.yield = 1;
-    BN_GENCB_set(cb, ossl_generate_cb_2, &cb_arg);
-    gen_arg.dh = dh;
-    gen_arg.size = size;
-    gen_arg.gen = gen;
-    gen_arg.cb = cb;
-    if (cb_arg.yield == 1) {
-	/* we cannot release GVL when callback proc is supplied */
-	dh_blocking_gen(&gen_arg);
-    } else {
-	/* there's a chance to unblock */
-	rb_thread_call_without_gvl(dh_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg);
-    }
-
-    BN_GENCB_free(cb);
-    if (!gen_arg.result) {
-	DH_free(dh);
-	if (cb_arg.state) {
-	    /* Clear OpenSSL error queue before re-raising. */
-	    ossl_clear_error();
-	    rb_jump_tag(cb_arg.state);
-	}
-	return NULL;
-    }
-
-    if (!DH_generate_key(dh)) {
-        DH_free(dh);
-        return NULL;
-    }
-
-    return dh;
-}
-
-/*
- *  call-seq:
- *     DH.generate(size [, generator]) -> dh
- *
- * Creates a new DH instance from scratch by generating the private and public
- * components alike.
- *
- * === Parameters
- * * _size_ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure.
- * * _generator_ is a small number > 1, typically 2 or 5.
- *
- */
-static VALUE
-ossl_dh_s_generate(int argc, VALUE *argv, VALUE klass)
-{
-    DH *dh ;
-    int g = 2;
-    VALUE size, gen, obj;
-
-    if (rb_scan_args(argc, argv, "11", &size, &gen) == 2) {
-	g = NUM2INT(gen);
-    }
-    dh = dh_generate(NUM2INT(size), g);
-    obj = dh_instance(klass, dh);
-    if (obj == Qfalse) {
-	DH_free(dh);
-	ossl_raise(eDHError, NULL);
-    }
-
-    return obj;
-}
-
 /*
  * call-seq:
  *   DH.new -> dh
  *   DH.new(string) -> dh
  *   DH.new(size [, generator]) -> dh
  *
- * Either generates a DH instance from scratch or by reading already existing
- * DH parameters from _string_. Note that when reading a DH instance from
- * data that was encoded from a DH instance by using DH#to_pem or DH#to_der
- * the result will *not* contain a public/private key pair yet. This needs to
- * be generated using DH#generate_key! first.
- *
- * === Parameters
- * * _size_ is an integer representing the desired key size. Keys smaller than 1024 bits should be considered insecure.
- * * _generator_ is a small number > 1, typically 2 or 5.
- * * _string_ contains the DER or PEM encoded key.
- *
- * === Examples
- *  DH.new # -> dh
- *  DH.new(1024) # -> dh
- *  DH.new(1024, 5) # -> dh
- *  #Reading DH parameters
- *  dh = DH.new(File.read('parameters.pem')) # -> dh, but no public/private key yet
- *  dh.generate_key! # -> dh with public and private key
+ * Creates a new instance of OpenSSL::PKey::DH.
+ *
+ * If called without arguments, an empty instance without any parameter or key
+ * components is created. Use #set_pqg to manually set the parameters afterwards
+ * (and optionally #set_key to set private and public key components).
+ *
+ * If a String is given, tries to parse it as a DER- or PEM- encoded parameters.
+ * See also OpenSSL::PKey.read which can parse keys of any kinds.
+ *
+ * The DH.new(size [, generator]) form is an alias of DH.generate.
+ *
+ * +string+::
+ *   A String that contains the DER or PEM encoded key.
+ * +size+::
+ *   See DH.generate.
+ * +generator+::
+ *   See DH.generate.
+ *
+ * Examples:
+ *   # Creating an instance from scratch
+ *   # Note that this is deprecated and will not work on OpenSSL 3.0 or later.
+ *   dh = OpenSSL::PKey::DH.new
+ *   dh.set_pqg(bn_p, nil, bn_g)
+ *
+ *   # Generating a parameters and a key pair
+ *   dh = OpenSSL::PKey::DH.new(2048) # An alias of OpenSSL::PKey::DH.generate(2048)
+ *
+ *   # Reading DH parameters
+ *   dh_params = OpenSSL::PKey::DH.new(File.read('parameters.pem')) # loads parameters only
+ *   dh = OpenSSL::PKey.generate_key(dh_params) # generates a key pair
  */
 static VALUE
 ossl_dh_initialize(int argc, VALUE *argv, VALUE self)
 {
     EVP_PKEY *pkey;
+    int type;
     DH *dh;
-    int g = 2;
-    BIO *in;
-    VALUE arg, gen;
+    BIO *in = NULL;
+    VALUE arg;
 
-    GetPKey(self, pkey);
-    if(rb_scan_args(argc, argv, "02", &arg, &gen) == 0) {
-      dh = DH_new();
-    }
-    else if (RB_INTEGER_TYPE_P(arg)) {
-	if (!NIL_P(gen)) {
-	    g = NUM2INT(gen);
-	}
-	if (!(dh = dh_generate(NUM2INT(arg), g))) {
-	    ossl_raise(eDHError, NULL);
-	}
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
+
+    /* The DH.new(size, generator) form is handled by lib/openssl/pkey.rb */
+    if (rb_scan_args(argc, argv, "01", &arg) == 0) {
+        dh = DH_new();
+        if (!dh)
+            ossl_raise(eDHError, "DH_new");
+        goto legacy;
     }
-    else {
-	arg = ossl_to_der_if_possible(arg);
-	in = ossl_obj2bio(&arg);
-	dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
-	if (!dh){
-	    OSSL_BIO_reset(in);
-	    dh = d2i_DHparams_bio(in, NULL);
-	}
-	BIO_free(in);
-	if (!dh) {
-	    ossl_raise(eDHError, NULL);
-	}
+
+    arg = ossl_to_der_if_possible(arg);
+    in = ossl_obj2bio(&arg);
+
+    /*
+     * On OpenSSL <= 1.1.1 and current versions of LibreSSL, the generic
+     * routine does not support DER-encoded parameters
+     */
+    dh = d2i_DHparams_bio(in, NULL);
+    if (dh)
+        goto legacy;
+    OSSL_BIO_reset(in);
+
+    pkey = ossl_pkey_read_generic(in, Qnil);
+    BIO_free(in);
+    if (!pkey)
+        ossl_raise(eDHError, "could not parse pkey");
+
+    type = EVP_PKEY_base_id(pkey);
+    if (type != EVP_PKEY_DH) {
+        EVP_PKEY_free(pkey);
+        rb_raise(eDHError, "incorrect pkey type: %s", OBJ_nid2sn(type));
     }
-    if (!EVP_PKEY_assign_DH(pkey, dh)) {
-	DH_free(dh);
-	ossl_raise(eDHError, NULL);
+    RTYPEDDATA_DATA(self) = pkey;
+    return self;
+
+  legacy:
+    BIO_free(in);
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_DH(pkey, dh) != 1) {
+        EVP_PKEY_free(pkey);
+        DH_free(dh);
+        ossl_raise(eDHError, "EVP_PKEY_assign_DH");
     }
+    RTYPEDDATA_DATA(self) = pkey;
     return self;
 }
 
+#ifndef HAVE_EVP_PKEY_DUP
 static VALUE
 ossl_dh_initialize_copy(VALUE self, VALUE other)
 {
@@ -247,22 +135,21 @@
     DH *dh, *dh_other;
     const BIGNUM *pub, *priv;
 
-    GetPKey(self, pkey);
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
-	ossl_raise(eDHError, "DH already initialized");
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
     GetDH(other, dh_other);
 
     dh = DHparams_dup(dh_other);
     if (!dh)
 	ossl_raise(eDHError, "DHparams_dup");
-    EVP_PKEY_assign_DH(pkey, dh);
 
     DH_get0_key(dh_other, &pub, &priv);
     if (pub) {
 	BIGNUM *pub2 = BN_dup(pub);
 	BIGNUM *priv2 = BN_dup(priv);
 
-	if (!pub2 || priv && !priv2) {
+        if (!pub2 || (priv && !priv2)) {
 	    BN_clear_free(pub2);
 	    BN_clear_free(priv2);
 	    ossl_raise(eDHError, "BN_dup");
@@ -270,8 +157,16 @@
 	DH_set0_key(dh, pub2, priv2);
     }
 
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_DH(pkey, dh) != 1) {
+        EVP_PKEY_free(pkey);
+        DH_free(dh);
+        ossl_raise(eDHError, "EVP_PKEY_assign_DH");
+    }
+    RTYPEDDATA_DATA(self) = pkey;
     return self;
 }
+#endif
 
 /*
  *  call-seq:
@@ -283,7 +178,7 @@
 static VALUE
 ossl_dh_is_public(VALUE self)
 {
-    DH *dh;
+    OSSL_3_const DH *dh;
     const BIGNUM *bn;
 
     GetDH(self, dh);
@@ -302,14 +197,14 @@
 static VALUE
 ossl_dh_is_private(VALUE self)
 {
-    DH *dh;
+    OSSL_3_const DH *dh;
     const BIGNUM *bn;
 
     GetDH(self, dh);
     DH_get0_key(dh, NULL, &bn);
 
 #if !defined(OPENSSL_NO_ENGINE)
-    return (bn || DH_get0_engine(dh)) ? Qtrue : Qfalse;
+    return (bn || DH_get0_engine((DH *)dh)) ? Qtrue : Qfalse;
 #else
     return bn ? Qtrue : Qfalse;
 #endif
@@ -321,14 +216,25 @@
  *     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)
 {
-    DH *dh;
+    OSSL_3_const DH *dh;
     BIO *out;
     VALUE str;
 
@@ -349,15 +255,19 @@
  *  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)
 {
-    DH *dh;
+    OSSL_3_const DH *dh;
     unsigned char *p;
     long len;
     VALUE str;
@@ -385,7 +295,7 @@
 static VALUE
 ossl_dh_get_params(VALUE self)
 {
-    DH *dh;
+    OSSL_3_const DH *dh;
     VALUE hash;
     const BIGNUM *p, *q, *g, *pub_key, *priv_key;
 
@@ -405,151 +315,43 @@
 
 /*
  *  call-seq:
- *     dh.to_text -> aString
- *
- * Prints all parameters of key to buffer
- * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
- * Don't use :-)) (I's up to you)
- */
-static VALUE
-ossl_dh_to_text(VALUE self)
-{
-    DH *dh;
-    BIO *out;
-    VALUE str;
-
-    GetDH(self, dh);
-    if (!(out = BIO_new(BIO_s_mem()))) {
-	ossl_raise(eDHError, NULL);
-    }
-    if (!DHparams_print(out, dh)) {
-	BIO_free(out);
-	ossl_raise(eDHError, NULL);
-    }
-    str = ossl_membio2str(out);
-
-    return str;
-}
-
-/*
- *  call-seq:
- *     dh.public_key -> aDH
- *
- * Returns a new DH instance that carries just the public information, i.e.
- * the prime _p_ and the generator _g_, but no public/private key yet. Such
- * a pair may be generated using DH#generate_key!. The "public key" needed
- * for a key exchange with DH#compute_key is considered as per-session
- * information and may be retrieved with DH#pub_key once a key pair has
- * been generated.
- * If the current instance already contains private information (and thus a
- * valid public/private key pair), this information will no longer be present
- * in the new instance generated by DH#public_key. This feature is helpful for
- * publishing the Diffie-Hellman parameters without leaking any of the private
- * per-session information.
- *
- * === Example
- *  dh = OpenSSL::PKey::DH.new(2048) # has public and private key set
- *  public_key = dh.public_key # contains only prime and generator
- *  parameters = public_key.to_der # it's safe to publish this
- */
-static VALUE
-ossl_dh_to_public_key(VALUE self)
-{
-    DH *orig_dh, *dh;
-    VALUE obj;
-
-    GetDH(self, orig_dh);
-    dh = DHparams_dup(orig_dh); /* err check perfomed by dh_instance */
-    obj = dh_instance(rb_obj_class(self), dh);
-    if (obj == Qfalse) {
-	DH_free(dh);
-	ossl_raise(eDHError, NULL);
-    }
-
-    return obj;
-}
-
-/*
- *  call-seq:
  *     dh.params_ok? -> true | false
  *
  * Validates the Diffie-Hellman parameters associated with this instance.
  * It checks whether a safe prime and a suitable generator are used. If this
  * is not the case, +false+ is returned.
+ *
+ * See also the man page EVP_PKEY_param_check(3).
  */
 static VALUE
 ossl_dh_check_params(VALUE self)
 {
-    DH *dh;
-    int codes;
-
-    GetDH(self, dh);
-    if (!DH_check(dh, &codes)) {
-	return Qfalse;
-    }
-
-    return codes == 0 ? Qtrue : Qfalse;
-}
+    int ret;
+#ifdef HAVE_EVP_PKEY_CHECK
+    EVP_PKEY *pkey;
+    EVP_PKEY_CTX *pctx;
 
-/*
- *  call-seq:
- *     dh.generate_key! -> self
- *
- * Generates a private and public key unless a private key already exists.
- * If this DH instance was generated from public DH parameters (e.g. by
- * encoding the result of DH#public_key), then this method needs to be
- * called first in order to generate the per-session keys before performing
- * the actual key exchange.
- *
- * === Example
- *   dh = OpenSSL::PKey::DH.new(2048)
- *   public_key = dh.public_key #contains no private/public key yet
- *   public_key.generate_key!
- *   puts public_key.private? # => true
- */
-static VALUE
-ossl_dh_generate_key(VALUE self)
-{
+    GetPKey(self, pkey);
+    pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
+    if (!pctx)
+        ossl_raise(eDHError, "EVP_PKEY_CTX_new");
+    ret = EVP_PKEY_param_check(pctx);
+    EVP_PKEY_CTX_free(pctx);
+#else
     DH *dh;
+    int codes;
 
     GetDH(self, dh);
-    if (!DH_generate_key(dh))
-	ossl_raise(eDHError, "Failed to generate key");
-    return self;
-}
-
-/*
- *  call-seq:
- *     dh.compute_key(pub_bn) -> aString
- *
- * Returns a String containing a shared secret computed from the other party's public value.
- * See DH_compute_key() for further information.
- *
- * === Parameters
- * * _pub_bn_ is a OpenSSL::BN, *not* the DH instance returned by
- *   DH#public_key as that contains the DH parameters only.
- */
-static VALUE
-ossl_dh_compute_key(VALUE self, VALUE pub)
-{
-    DH *dh;
-    const BIGNUM *pub_key, *dh_p;
-    VALUE str;
-    int len;
+    ret = DH_check(dh, &codes) == 1 && codes == 0;
+#endif
 
-    GetDH(self, dh);
-    DH_get0_pqg(dh, &dh_p, NULL, NULL);
-    if (!dh_p)
-	ossl_raise(eDHError, "incomplete DH");
-    pub_key = GetBNPtr(pub);
-    len = DH_size(dh);
-    str = rb_str_new(0, len);
-    if ((len = DH_compute_key((unsigned char *)RSTRING_PTR(str), pub_key, dh)) < 0) {
-	ossl_raise(eDHError, NULL);
+    if (ret == 1)
+        return Qtrue;
+    else {
+        /* DH_check_ex() will put error entry on failure */
+        ossl_clear_error();
+        return Qfalse;
     }
-    rb_str_set_len(str, len);
-
-    return str;
 }
 
 /*
@@ -606,30 +408,33 @@
      *   The per-session private key, an OpenSSL::BN.
      *
      * === Example of a key exchange
-     *  dh1 = OpenSSL::PKey::DH.new(2048)
-     *  der = dh1.public_key.to_der #you may send this publicly to the participating party
-     *  dh2 = OpenSSL::PKey::DH.new(der)
-     *  dh2.generate_key! #generate the per-session key pair
-     *  symm_key1 = dh1.compute_key(dh2.pub_key)
-     *  symm_key2 = dh2.compute_key(dh1.pub_key)
+     *   # you may send the parameters (der) and own public key (pub1) publicly
+     *   # to the participating party
+     *   dh1 = OpenSSL::PKey::DH.new(2048)
+     *   der = dh1.to_der
+     *   pub1 = dh1.pub_key
      *
-     *  puts symm_key1 == symm_key2 # => true
+     *   # the other party generates its per-session key pair
+     *   dhparams = OpenSSL::PKey::DH.new(der)
+     *   dh2 = OpenSSL::PKey.generate_key(dhparams)
+     *   pub2 = dh2.pub_key
+     *
+     *   symm_key1 = dh1.compute_key(pub2)
+     *   symm_key2 = dh2.compute_key(pub1)
+     *   puts symm_key1 == symm_key2 # => true
      */
     cDH = rb_define_class_under(mPKey, "DH", cPKey);
-    rb_define_singleton_method(cDH, "generate", ossl_dh_s_generate, -1);
     rb_define_method(cDH, "initialize", ossl_dh_initialize, -1);
+#ifndef HAVE_EVP_PKEY_DUP
     rb_define_method(cDH, "initialize_copy", ossl_dh_initialize_copy, 1);
+#endif
     rb_define_method(cDH, "public?", ossl_dh_is_public, 0);
     rb_define_method(cDH, "private?", ossl_dh_is_private, 0);
-    rb_define_method(cDH, "to_text", ossl_dh_to_text, 0);
     rb_define_method(cDH, "export", ossl_dh_export, 0);
     rb_define_alias(cDH, "to_pem", "export");
     rb_define_alias(cDH, "to_s", "export");
     rb_define_method(cDH, "to_der", ossl_dh_to_der, 0);
-    rb_define_method(cDH, "public_key", ossl_dh_to_public_key, 0);
     rb_define_method(cDH, "params_ok?", ossl_dh_check_params, 0);
-    rb_define_method(cDH, "generate_key!", ossl_dh_generate_key, 0);
-    rb_define_method(cDH, "compute_key", ossl_dh_compute_key, 1);
 
     DEF_OSSL_PKEY_BN(cDH, dh, p);
     DEF_OSSL_PKEY_BN(cDH, dh, q);
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkey_dsa.c ruby-2.5.9/ext/openssl/ossl_pkey_dsa.c
--- ruby-2.5.9.orig/ext/openssl/ossl_pkey_dsa.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkey_dsa.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -24,7 +24,7 @@
 } while (0)
 
 static inline int
-DSA_HAS_PRIVATE(DSA *dsa)
+DSA_HAS_PRIVATE(OSSL_3_const DSA *dsa)
 {
     const BIGNUM *bn;
     DSA_get0_key(dsa, NULL, &bn);
@@ -32,7 +32,7 @@
 }
 
 static inline int
-DSA_PRIVATE(VALUE obj, DSA *dsa)
+DSA_PRIVATE(VALUE obj, OSSL_3_const DSA *dsa)
 {
     return DSA_HAS_PRIVATE(dsa) || OSSL_PKEY_IS_PRIVATE(obj);
 }
@@ -41,248 +41,133 @@
  * Classes
  */
 VALUE cDSA;
-VALUE eDSAError;
-
-/*
- * Public
- */
-static VALUE
-dsa_instance(VALUE klass, DSA *dsa)
-{
-    EVP_PKEY *pkey;
-    VALUE obj;
-
-    if (!dsa) {
-	return Qfalse;
-    }
-    obj = NewPKey(klass);
-    if (!(pkey = EVP_PKEY_new())) {
-	return Qfalse;
-    }
-    if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
-	EVP_PKEY_free(pkey);
-	return Qfalse;
-    }
-    SetPKey(obj, pkey);
-
-    return obj;
-}
-
-VALUE
-ossl_dsa_new(EVP_PKEY *pkey)
-{
-    VALUE obj;
-
-    if (!pkey) {
-	obj = dsa_instance(cDSA, DSA_new());
-    } else {
-	obj = NewPKey(cDSA);
-	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DSA) {
-	    ossl_raise(rb_eTypeError, "Not a DSA key!");
-	}
-	SetPKey(obj, pkey);
-    }
-    if (obj == Qfalse) {
-	ossl_raise(eDSAError, NULL);
-    }
-
-    return obj;
-}
+static VALUE eDSAError;
 
 /*
  * Private
  */
-struct dsa_blocking_gen_arg {
-    DSA *dsa;
-    int size;
-    int *counter;
-    unsigned long *h;
-    BN_GENCB *cb;
-    int result;
-};
-
-static void *
-dsa_blocking_gen(void *arg)
-{
-    struct dsa_blocking_gen_arg *gen = (struct dsa_blocking_gen_arg *)arg;
-    gen->result = DSA_generate_parameters_ex(gen->dsa, gen->size, NULL, 0,
-					     gen->counter, gen->h, gen->cb);
-    return 0;
-}
-
-static DSA *
-dsa_generate(int size)
-{
-    struct ossl_generate_cb_arg cb_arg = { 0 };
-    struct dsa_blocking_gen_arg gen_arg;
-    DSA *dsa = DSA_new();
-    BN_GENCB *cb = BN_GENCB_new();
-    int counter;
-    unsigned long h;
-
-    if (!dsa || !cb) {
-	DSA_free(dsa);
-	BN_GENCB_free(cb);
-	return NULL;
-    }
-
-    if (rb_block_given_p())
-	cb_arg.yield = 1;
-    BN_GENCB_set(cb, ossl_generate_cb_2, &cb_arg);
-    gen_arg.dsa = dsa;
-    gen_arg.size = size;
-    gen_arg.counter = &counter;
-    gen_arg.h = &h;
-    gen_arg.cb = cb;
-    if (cb_arg.yield == 1) {
-	/* we cannot release GVL when callback proc is supplied */
-	dsa_blocking_gen(&gen_arg);
-    } else {
-	/* there's a chance to unblock */
-	rb_thread_call_without_gvl(dsa_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg);
-    }
-
-    BN_GENCB_free(cb);
-    if (!gen_arg.result) {
-	DSA_free(dsa);
-	if (cb_arg.state) {
-	    /* Clear OpenSSL error queue before re-raising. By the way, the
-	     * documentation of DSA_generate_parameters_ex() says the error code
-	     * can be obtained by ERR_get_error(), but the default
-	     * implementation, dsa_builtin_paramgen() doesn't put any error... */
-	    ossl_clear_error();
-	    rb_jump_tag(cb_arg.state);
-	}
-	return NULL;
-    }
-
-    if (!DSA_generate_key(dsa)) {
-	DSA_free(dsa);
-	return NULL;
-    }
-
-    return dsa;
-}
-
-/*
- *  call-seq:
- *    DSA.generate(size) -> dsa
- *
- * Creates a new DSA instance by generating a private/public key pair
- * from scratch.
- *
- * === Parameters
- * * _size_ is an integer representing the desired key size.
- *
- */
-static VALUE
-ossl_dsa_s_generate(VALUE klass, VALUE size)
-{
-    DSA *dsa = dsa_generate(NUM2INT(size)); /* err handled by dsa_instance */
-    VALUE obj = dsa_instance(klass, dsa);
-
-    if (obj == Qfalse) {
-	DSA_free(dsa);
-	ossl_raise(eDSAError, NULL);
-    }
-
-    return obj;
-}
-
 /*
  *  call-seq:
  *    DSA.new -> dsa
- *    DSA.new(size) -> dsa
  *    DSA.new(string [, pass]) -> dsa
+ *    DSA.new(size) -> dsa
  *
  * Creates a new DSA instance by reading an existing key from _string_.
  *
- * === Parameters
- * * _size_ is an integer representing the desired key size.
- * * _string_ contains a DER or PEM encoded key.
- * * _pass_ is a string that contains an optional password.
- *
- * === Examples
- *  DSA.new -> dsa
- *  DSA.new(1024) -> dsa
- *  DSA.new(File.read('dsa.pem')) -> dsa
- *  DSA.new(File.read('dsa.pem'), 'mypassword') -> dsa
+ * If called without arguments, creates a new instance with no key components
+ * set. They can be set individually by #set_pqg and #set_key.
+ *
+ * If called with a String, tries to parse as DER or PEM encoding of a \DSA key.
+ * See also OpenSSL::PKey.read which can parse keys of any kinds.
+ *
+ * If called with a number, generates random parameters and a key pair. This
+ * form works as an alias of DSA.generate.
+ *
+ * +string+::
+ *   A String that contains a DER or PEM encoded key.
+ * +pass+::
+ *   A String that contains an optional password.
+ * +size+::
+ *   See DSA.generate.
+ *
+ * Examples:
+ *   p OpenSSL::PKey::DSA.new(1024)
+ *   #=> #<OpenSSL::PKey::DSA:0x000055a8d6025bf0 oid=DSA>
  *
+ *   p OpenSSL::PKey::DSA.new(File.read('dsa.pem'))
+ *   #=> #<OpenSSL::PKey::DSA:0x000055555d6b8110 oid=DSA>
+ *
+ *   p OpenSSL::PKey::DSA.new(File.read('dsa.pem'), 'mypassword')
+ *   #=> #<OpenSSL::PKey::DSA:0x0000556f973c40b8 oid=DSA>
  */
 static VALUE
 ossl_dsa_initialize(int argc, VALUE *argv, VALUE self)
 {
     EVP_PKEY *pkey;
     DSA *dsa;
-    BIO *in;
+    BIO *in = NULL;
     VALUE arg, pass;
+    int type;
 
-    GetPKey(self, pkey);
-    if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) {
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
+
+    /* The DSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
+    rb_scan_args(argc, argv, "02", &arg, &pass);
+    if (argc == 0) {
         dsa = DSA_new();
+        if (!dsa)
+            ossl_raise(eDSAError, "DSA_new");
+        goto legacy;
+    }
+
+    pass = ossl_pem_passwd_value(pass);
+    arg = ossl_to_der_if_possible(arg);
+    in = ossl_obj2bio(&arg);
+
+    /* DER-encoded DSAPublicKey format isn't supported by the generic routine */
+    dsa = (DSA *)PEM_ASN1_read_bio((d2i_of_void *)d2i_DSAPublicKey,
+                                   PEM_STRING_DSA_PUBLIC,
+                                   in, NULL, NULL, NULL);
+    if (dsa)
+        goto legacy;
+    OSSL_BIO_reset(in);
+
+    pkey = ossl_pkey_read_generic(in, pass);
+    BIO_free(in);
+    if (!pkey)
+        ossl_raise(eDSAError, "Neither PUB key nor PRIV key");
+
+    type = EVP_PKEY_base_id(pkey);
+    if (type != EVP_PKEY_DSA) {
+        EVP_PKEY_free(pkey);
+        rb_raise(eDSAError, "incorrect pkey type: %s", OBJ_nid2sn(type));
     }
-    else if (RB_INTEGER_TYPE_P(arg)) {
-	if (!(dsa = dsa_generate(NUM2INT(arg)))) {
-	    ossl_raise(eDSAError, NULL);
-	}
-    }
-    else {
-	pass = ossl_pem_passwd_value(pass);
-	arg = ossl_to_der_if_possible(arg);
-	in = ossl_obj2bio(&arg);
-	dsa = PEM_read_bio_DSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass);
-	if (!dsa) {
-	    OSSL_BIO_reset(in);
-	    dsa = PEM_read_bio_DSA_PUBKEY(in, NULL, NULL, NULL);
-	}
-	if (!dsa) {
-	    OSSL_BIO_reset(in);
-	    dsa = d2i_DSAPrivateKey_bio(in, NULL);
-	}
-	if (!dsa) {
-	    OSSL_BIO_reset(in);
-	    dsa = d2i_DSA_PUBKEY_bio(in, NULL);
-	}
-	if (!dsa) {
-	    OSSL_BIO_reset(in);
-#define PEM_read_bio_DSAPublicKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \
-	(d2i_of_void *)d2i_DSAPublicKey, PEM_STRING_DSA_PUBLIC, (bp), (void **)(x), (cb), (u))
-	    dsa = PEM_read_bio_DSAPublicKey(in, NULL, NULL, NULL);
-#undef PEM_read_bio_DSAPublicKey
-	}
-	BIO_free(in);
-	if (!dsa) {
-	    ossl_clear_error();
-	    ossl_raise(eDSAError, "Neither PUB key nor PRIV key");
-	}
-    }
-    if (!EVP_PKEY_assign_DSA(pkey, dsa)) {
-	DSA_free(dsa);
-	ossl_raise(eDSAError, NULL);
-    }
+    RTYPEDDATA_DATA(self) = pkey;
+    return self;
 
+  legacy:
+    BIO_free(in);
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_DSA(pkey, dsa) != 1) {
+        EVP_PKEY_free(pkey);
+        DSA_free(dsa);
+        ossl_raise(eDSAError, "EVP_PKEY_assign_DSA");
+    }
+    RTYPEDDATA_DATA(self) = pkey;
     return self;
 }
 
+#ifndef HAVE_EVP_PKEY_DUP
 static VALUE
 ossl_dsa_initialize_copy(VALUE self, VALUE other)
 {
     EVP_PKEY *pkey;
     DSA *dsa, *dsa_new;
 
-    GetPKey(self, pkey);
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
-	ossl_raise(eDSAError, "DSA already initialized");
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
     GetDSA(other, dsa);
 
-    dsa_new = ASN1_dup((i2d_of_void *)i2d_DSAPrivateKey, (d2i_of_void *)d2i_DSAPrivateKey, (char *)dsa);
+    dsa_new = (DSA *)ASN1_dup((i2d_of_void *)i2d_DSAPrivateKey,
+                              (d2i_of_void *)d2i_DSAPrivateKey,
+                              (char *)dsa);
     if (!dsa_new)
 	ossl_raise(eDSAError, "ASN1_dup");
 
-    EVP_PKEY_assign_DSA(pkey, dsa_new);
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_DSA(pkey, dsa_new) != 1) {
+        EVP_PKEY_free(pkey);
+        DSA_free(dsa_new);
+        ossl_raise(eDSAError, "EVP_PKEY_assign_DSA");
+    }
+    RTYPEDDATA_DATA(self) = pkey;
 
     return self;
 }
+#endif
 
 /*
  *  call-seq:
@@ -294,7 +179,7 @@
 static VALUE
 ossl_dsa_is_public(VALUE self)
 {
-    DSA *dsa;
+    const DSA *dsa;
     const BIGNUM *bn;
 
     GetDSA(self, dsa);
@@ -313,7 +198,7 @@
 static VALUE
 ossl_dsa_is_private(VALUE self)
 {
-    DSA *dsa;
+    OSSL_3_const DSA *dsa;
 
     GetDSA(self, dsa);
 
@@ -326,81 +211,95 @@
  *    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.
+ *
+ * [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 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
  *
- * === Parameters
- * * _cipher_ is an OpenSSL::Cipher.
- * * _password_ is a string containing your password.
- *
- * === Examples
- *  DSA.to_pem -> aString
- *  DSA.to_pem(cipher, 'mypassword') -> aString
+ *     [...]
+ *     -----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)
 {
-    DSA *dsa;
-    BIO *out;
-    const EVP_CIPHER *ciph = NULL;
-    VALUE cipher, pass, str;
+    OSSL_3_const DSA *dsa;
 
     GetDSA(self, dsa);
-    rb_scan_args(argc, argv, "02", &cipher, &pass);
-    if (!NIL_P(cipher)) {
-	ciph = ossl_evp_get_cipherbyname(cipher);
-	pass = ossl_pem_passwd_value(pass);
-    }
-    if (!(out = BIO_new(BIO_s_mem()))) {
-	ossl_raise(eDSAError, NULL);
-    }
-    if (DSA_HAS_PRIVATE(dsa)) {
-	if (!PEM_write_bio_DSAPrivateKey(out, dsa, ciph, NULL, 0,
-					 ossl_pem_passwd_cb, (void *)pass)){
-	    BIO_free(out);
-	    ossl_raise(eDSAError, NULL);
-	}
-    } else {
-	if (!PEM_write_bio_DSA_PUBKEY(out, dsa)) {
-	    BIO_free(out);
-	    ossl_raise(eDSAError, NULL);
-	}
-    }
-    str = ossl_membio2str(out);
-
-    return str;
+    if (DSA_HAS_PRIVATE(dsa))
+        return ossl_pkey_export_traditional(argc, argv, self, 0);
+    else
+        return ossl_pkey_export_spki(self, 0);
 }
 
 /*
  *  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)
 {
-    DSA *dsa;
-    int (*i2d_func)(DSA *, unsigned char **);
-    unsigned char *p;
-    long len;
-    VALUE str;
+    OSSL_3_const DSA *dsa;
 
     GetDSA(self, dsa);
-    if(DSA_HAS_PRIVATE(dsa))
-	i2d_func = (int (*)(DSA *,unsigned char **))i2d_DSAPrivateKey;
+    if (DSA_HAS_PRIVATE(dsa))
+        return ossl_pkey_export_traditional(0, NULL, self, 1);
     else
-	i2d_func = i2d_DSA_PUBKEY;
-    if((len = i2d_func(dsa, NULL)) <= 0)
-	ossl_raise(eDSAError, NULL);
-    str = rb_str_new(0, len);
-    p = (unsigned char *)RSTRING_PTR(str);
-    if(i2d_func(dsa, &p) < 0)
-	ossl_raise(eDSAError, NULL);
-    ossl_str_adjust(str, p);
-
-    return str;
+        return ossl_pkey_export_spki(self, 1);
 }
 
 
@@ -415,7 +314,7 @@
 static VALUE
 ossl_dsa_get_params(VALUE self)
 {
-    DSA *dsa;
+    OSSL_3_const DSA *dsa;
     VALUE hash;
     const BIGNUM *p, *q, *g, *pub_key, *priv_key;
 
@@ -434,158 +333,6 @@
 }
 
 /*
- *  call-seq:
- *    dsa.to_text -> aString
- *
- * Prints all parameters of key to buffer
- * INSECURE: PRIVATE INFORMATIONS CAN LEAK OUT!!!
- * Don't use :-)) (I's up to you)
- */
-static VALUE
-ossl_dsa_to_text(VALUE self)
-{
-    DSA *dsa;
-    BIO *out;
-    VALUE str;
-
-    GetDSA(self, dsa);
-    if (!(out = BIO_new(BIO_s_mem()))) {
-	ossl_raise(eDSAError, NULL);
-    }
-    if (!DSA_print(out, dsa, 0)) { /* offset = 0 */
-	BIO_free(out);
-	ossl_raise(eDSAError, NULL);
-    }
-    str = ossl_membio2str(out);
-
-    return str;
-}
-
-/*
- *  call-seq:
- *    dsa.public_key -> aDSA
- *
- * Returns a new DSA instance that carries just the public key information.
- * If the current instance has also private key information, this will no
- * longer be present in the new instance. This feature is helpful for
- * publishing the public key information without leaking any of the private
- * information.
- *
- * === Example
- *  dsa = OpenSSL::PKey::DSA.new(2048) # has public and private information
- *  pub_key = dsa.public_key # has only the public part available
- *  pub_key_der = pub_key.to_der # it's safe to publish this
- *
- *
- */
-static VALUE
-ossl_dsa_to_public_key(VALUE self)
-{
-    EVP_PKEY *pkey;
-    DSA *dsa;
-    VALUE obj;
-
-    GetPKeyDSA(self, pkey);
-    /* err check performed by dsa_instance */
-#define DSAPublicKey_dup(dsa) (DSA *)ASN1_dup( \
-	(i2d_of_void *)i2d_DSAPublicKey, (d2i_of_void *)d2i_DSAPublicKey, (char *)(dsa))
-    dsa = DSAPublicKey_dup(EVP_PKEY_get0_DSA(pkey));
-#undef DSAPublicKey_dup
-    obj = dsa_instance(rb_obj_class(self), dsa);
-    if (obj == Qfalse) {
-	DSA_free(dsa);
-	ossl_raise(eDSAError, NULL);
-    }
-    return obj;
-}
-
-/*
- *  call-seq:
- *    dsa.syssign(string) -> aString
- *
- * Computes and returns the DSA signature of _string_, where _string_ is
- * expected to be an already-computed message digest of the original input
- * data. The signature is issued using the private key of this DSA instance.
- *
- * === Parameters
- * * _string_ is a message digest of the original input data to be signed.
- *
- * === Example
- *  dsa = OpenSSL::PKey::DSA.new(2048)
- *  doc = "Sign me"
- *  digest = OpenSSL::Digest::SHA1.digest(doc)
- *  sig = dsa.syssign(digest)
- *
- *
- */
-static VALUE
-ossl_dsa_sign(VALUE self, VALUE data)
-{
-    DSA *dsa;
-    const BIGNUM *dsa_q;
-    unsigned int buf_len;
-    VALUE str;
-
-    GetDSA(self, dsa);
-    DSA_get0_pqg(dsa, NULL, &dsa_q, NULL);
-    if (!dsa_q)
-	ossl_raise(eDSAError, "incomplete DSA");
-    if (!DSA_PRIVATE(self, dsa))
-	ossl_raise(eDSAError, "Private DSA key needed!");
-    StringValue(data);
-    str = rb_str_new(0, DSA_size(dsa));
-    if (!DSA_sign(0, (unsigned char *)RSTRING_PTR(data), RSTRING_LENINT(data),
-		  (unsigned char *)RSTRING_PTR(str),
-		  &buf_len, dsa)) { /* type is ignored (0) */
-	ossl_raise(eDSAError, NULL);
-    }
-    rb_str_set_len(str, buf_len);
-
-    return str;
-}
-
-/*
- *  call-seq:
- *    dsa.sysverify(digest, sig) -> true | false
- *
- * Verifies whether the signature is valid given the message digest input. It
- * does so by validating _sig_ using the public key of this DSA instance.
- *
- * === Parameters
- * * _digest_ is a message digest of the original input data to be signed
- * * _sig_ is a DSA signature value
- *
- * === Example
- *  dsa = OpenSSL::PKey::DSA.new(2048)
- *  doc = "Sign me"
- *  digest = OpenSSL::Digest::SHA1.digest(doc)
- *  sig = dsa.syssign(digest)
- *  puts dsa.sysverify(digest, sig) # => true
- *
- */
-static VALUE
-ossl_dsa_verify(VALUE self, VALUE digest, VALUE sig)
-{
-    DSA *dsa;
-    int ret;
-
-    GetDSA(self, dsa);
-    StringValue(digest);
-    StringValue(sig);
-    /* type is ignored (0) */
-    ret = DSA_verify(0, (unsigned char *)RSTRING_PTR(digest), RSTRING_LENINT(digest),
-		     (unsigned char *)RSTRING_PTR(sig), RSTRING_LENINT(sig), dsa);
-    if (ret < 0) {
-	ossl_raise(eDSAError, NULL);
-    }
-    else if (ret == 1) {
-	return Qtrue;
-    }
-
-    return Qfalse;
-}
-
-/*
  * Document-method: OpenSSL::PKey::DSA#set_pqg
  * call-seq:
  *   dsa.set_pqg(p, q, g) -> self
@@ -630,20 +377,17 @@
      */
     cDSA = rb_define_class_under(mPKey, "DSA", cPKey);
 
-    rb_define_singleton_method(cDSA, "generate", ossl_dsa_s_generate, 1);
     rb_define_method(cDSA, "initialize", ossl_dsa_initialize, -1);
+#ifndef HAVE_EVP_PKEY_DUP
     rb_define_method(cDSA, "initialize_copy", ossl_dsa_initialize_copy, 1);
+#endif
 
     rb_define_method(cDSA, "public?", ossl_dsa_is_public, 0);
     rb_define_method(cDSA, "private?", ossl_dsa_is_private, 0);
-    rb_define_method(cDSA, "to_text", ossl_dsa_to_text, 0);
     rb_define_method(cDSA, "export", ossl_dsa_export, -1);
     rb_define_alias(cDSA, "to_pem", "export");
     rb_define_alias(cDSA, "to_s", "export");
     rb_define_method(cDSA, "to_der", ossl_dsa_to_der, 0);
-    rb_define_method(cDSA, "public_key", ossl_dsa_to_public_key, 0);
-    rb_define_method(cDSA, "syssign", ossl_dsa_sign, 1);
-    rb_define_method(cDSA, "sysverify", ossl_dsa_verify, 2);
 
     DEF_OSSL_PKEY_BN(cDSA, dsa, p);
     DEF_OSSL_PKEY_BN(cDSA, dsa, q);
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkey_ec.c ruby-2.5.9/ext/openssl/ossl_pkey_ec.c
--- ruby-2.5.9.orig/ext/openssl/ossl_pkey_ec.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkey_ec.c	2025-01-29 19:08:02.378421281 +0100
@@ -41,18 +41,13 @@
 } while (0)
 
 VALUE cEC;
-VALUE eECError;
-VALUE cEC_GROUP;
-VALUE eEC_GROUP;
-VALUE cEC_POINT;
-VALUE eEC_POINT;
-
-static ID s_GFp;
-static ID s_GFp_simple;
-static ID s_GFp_mont;
-static ID s_GFp_nist;
-static ID s_GF2m;
-static ID s_GF2m_simple;
+static VALUE eECError;
+static VALUE cEC_GROUP;
+static VALUE eEC_GROUP;
+static VALUE cEC_POINT;
+static VALUE eEC_POINT;
+
+static ID s_GFp, s_GF2m;
 
 static ID ID_uncompressed;
 static ID ID_compressed;
@@ -63,47 +58,6 @@
 static VALUE ec_group_new(const EC_GROUP *group);
 static VALUE ec_point_new(const EC_POINT *point, const EC_GROUP *group);
 
-static VALUE ec_instance(VALUE klass, EC_KEY *ec)
-{
-    EVP_PKEY *pkey;
-    VALUE obj;
-
-    if (!ec) {
-	return Qfalse;
-    }
-    obj = NewPKey(klass);
-    if (!(pkey = EVP_PKEY_new())) {
-	return Qfalse;
-    }
-    if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) {
-	EVP_PKEY_free(pkey);
-	return Qfalse;
-    }
-    SetPKey(obj, pkey);
-
-    return obj;
-}
-
-VALUE ossl_ec_new(EVP_PKEY *pkey)
-{
-    VALUE obj;
-
-    if (!pkey) {
-	obj = ec_instance(cEC, EC_KEY_new());
-    } else {
-	obj = NewPKey(cEC);
-	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
-	    ossl_raise(rb_eTypeError, "Not a EC key!");
-	}
-	SetPKey(obj, pkey);
-    }
-    if (obj == Qfalse) {
-	ossl_raise(eECError, NULL);
-    }
-
-    return obj;
-}
-
 /*
  * Creates a new EC_KEY on the EC group obj. arg can be an EC::Group or a String
  * representing an OID.
@@ -150,16 +104,20 @@
 static VALUE
 ossl_ec_key_s_generate(VALUE klass, VALUE arg)
 {
+    EVP_PKEY *pkey;
     EC_KEY *ec;
     VALUE obj;
 
-    ec = ec_key_new_from_group(arg);
+    obj = rb_obj_alloc(klass);
 
-    obj = ec_instance(klass, ec);
-    if (obj == Qfalse) {
-	EC_KEY_free(ec);
-	ossl_raise(eECError, NULL);
+    ec = ec_key_new_from_group(arg);
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
+        EVP_PKEY_free(pkey);
+        EC_KEY_free(ec);
+        ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
     }
+    RTYPEDDATA_DATA(obj) = pkey;
 
     if (!EC_KEY_generate_key(ec))
 	ossl_raise(eECError, "EC_KEY_generate_key");
@@ -182,81 +140,82 @@
 {
     EVP_PKEY *pkey;
     EC_KEY *ec;
+    BIO *in;
     VALUE arg, pass;
+    int type;
 
-    GetPKey(self, pkey);
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
-        ossl_raise(eECError, "EC_KEY already initialized");
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
 
     rb_scan_args(argc, argv, "02", &arg, &pass);
-
     if (NIL_P(arg)) {
         if (!(ec = EC_KEY_new()))
-	    ossl_raise(eECError, NULL);
-    } else if (rb_obj_is_kind_of(arg, cEC)) {
-	EC_KEY *other_ec = NULL;
-
-	GetEC(arg, other_ec);
-	if (!(ec = EC_KEY_dup(other_ec)))
-	    ossl_raise(eECError, NULL);
-    } else if (rb_obj_is_kind_of(arg, cEC_GROUP)) {
-	ec = ec_key_new_from_group(arg);
-    } else {
-	BIO *in;
-
-	pass = ossl_pem_passwd_value(pass);
-	in = ossl_obj2bio(&arg);
+            ossl_raise(eECError, "EC_KEY_new");
+        goto legacy;
+    }
+    else if (rb_obj_is_kind_of(arg, cEC_GROUP)) {
+        ec = ec_key_new_from_group(arg);
+        goto legacy;
+    }
 
-	ec = PEM_read_bio_ECPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass);
-	if (!ec) {
-	    OSSL_BIO_reset(in);
-	    ec = PEM_read_bio_EC_PUBKEY(in, NULL, ossl_pem_passwd_cb, (void *)pass);
-	}
-	if (!ec) {
-	    OSSL_BIO_reset(in);
-	    ec = d2i_ECPrivateKey_bio(in, NULL);
-	}
-	if (!ec) {
-	    OSSL_BIO_reset(in);
-	    ec = d2i_EC_PUBKEY_bio(in, NULL);
-	}
-	BIO_free(in);
+    pass = ossl_pem_passwd_value(pass);
+    arg = ossl_to_der_if_possible(arg);
+    in = ossl_obj2bio(&arg);
 
-	if (!ec) {
-	    ossl_clear_error();
-	    ec = ec_key_new_from_group(arg);
-	}
+    pkey = ossl_pkey_read_generic(in, pass);
+    BIO_free(in);
+    if (!pkey) {
+        ossl_clear_error();
+        ec = ec_key_new_from_group(arg);
+        goto legacy;
     }
 
-    if (!EVP_PKEY_assign_EC_KEY(pkey, ec)) {
-	EC_KEY_free(ec);
-	ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
+    type = EVP_PKEY_base_id(pkey);
+    if (type != EVP_PKEY_EC) {
+        EVP_PKEY_free(pkey);
+        rb_raise(eECError, "incorrect pkey type: %s", OBJ_nid2sn(type));
     }
+    RTYPEDDATA_DATA(self) = pkey;
+    return self;
 
+  legacy:
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec) != 1) {
+        EVP_PKEY_free(pkey);
+        EC_KEY_free(ec);
+        ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
+    }
+    RTYPEDDATA_DATA(self) = pkey;
     return self;
 }
 
+#ifndef HAVE_EVP_PKEY_DUP
 static VALUE
 ossl_ec_key_initialize_copy(VALUE self, VALUE other)
 {
     EVP_PKEY *pkey;
     EC_KEY *ec, *ec_new;
 
-    GetPKey(self, pkey);
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
-	ossl_raise(eECError, "EC already initialized");
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
     GetEC(other, ec);
 
     ec_new = EC_KEY_dup(ec);
     if (!ec_new)
 	ossl_raise(eECError, "EC_KEY_dup");
-    if (!EVP_PKEY_assign_EC_KEY(pkey, ec_new)) {
-	EC_KEY_free(ec_new);
-	ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
+
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, ec_new) != 1) {
+        EC_KEY_free(ec_new);
+        ossl_raise(eECError, "EVP_PKEY_assign_EC_KEY");
     }
+    RTYPEDDATA_DATA(self) = pkey;
 
     return self;
 }
+#endif
 
 /*
  * call-seq:
@@ -268,7 +227,7 @@
 static VALUE
 ossl_ec_key_get_group(VALUE self)
 {
-    EC_KEY *ec;
+    OSSL_3_const EC_KEY *ec;
     const EC_GROUP *group;
 
     GetEC(self, ec);
@@ -289,6 +248,9 @@
 static VALUE
 ossl_ec_key_set_group(VALUE self, VALUE group_v)
 {
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+    rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
+#else
     EC_KEY *ec;
     EC_GROUP *group;
 
@@ -299,6 +261,7 @@
         ossl_raise(eECError, "EC_KEY_set_group");
 
     return group_v;
+#endif
 }
 
 /*
@@ -309,7 +272,7 @@
  */
 static VALUE ossl_ec_key_get_private_key(VALUE self)
 {
-    EC_KEY *ec;
+    OSSL_3_const EC_KEY *ec;
     const BIGNUM *bn;
 
     GetEC(self, ec);
@@ -327,6 +290,9 @@
  */
 static VALUE ossl_ec_key_set_private_key(VALUE self, VALUE private_key)
 {
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+    rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
+#else
     EC_KEY *ec;
     BIGNUM *bn = NULL;
 
@@ -340,11 +306,13 @@
     case 0:
         if (bn == NULL)
             break;
+	/* fallthrough */
     default:
         ossl_raise(eECError, "EC_KEY_set_private_key");
     }
 
     return private_key;
+#endif
 }
 
 /*
@@ -355,7 +323,7 @@
  */
 static VALUE ossl_ec_key_get_public_key(VALUE self)
 {
-    EC_KEY *ec;
+    OSSL_3_const EC_KEY *ec;
     const EC_POINT *point;
 
     GetEC(self, ec);
@@ -373,6 +341,9 @@
  */
 static VALUE ossl_ec_key_set_public_key(VALUE self, VALUE public_key)
 {
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+    rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
+#else
     EC_KEY *ec;
     EC_POINT *point = NULL;
 
@@ -386,11 +357,13 @@
     case 0:
         if (point == NULL)
             break;
+	/* fallthrough */
     default:
         ossl_raise(eECError, "EC_KEY_set_public_key");
     }
 
     return public_key;
+#endif
 }
 
 /*
@@ -402,7 +375,7 @@
  */
 static VALUE ossl_ec_key_is_public(VALUE self)
 {
-    EC_KEY *ec;
+    OSSL_3_const EC_KEY *ec;
 
     GetEC(self, ec);
 
@@ -418,126 +391,112 @@
  */
 static VALUE ossl_ec_key_is_private(VALUE self)
 {
-    EC_KEY *ec;
+    OSSL_3_const EC_KEY *ec;
 
     GetEC(self, ec);
 
     return EC_KEY_get0_private_key(ec) ? Qtrue : Qfalse;
 }
 
-static VALUE ossl_ec_key_to_string(VALUE self, VALUE ciph, VALUE pass, int format)
+/*
+ *  call-seq:
+ *     key.export([cipher, password]) => String
+ *     key.to_pem([cipher, password]) => String
+ *
+ * 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)
 {
-    EC_KEY *ec;
-    BIO *out;
-    int i = -1;
-    int private = 0;
-    VALUE str;
-    const EVP_CIPHER *cipher = NULL;
+    OSSL_3_const EC_KEY *ec;
 
     GetEC(self, ec);
-
     if (EC_KEY_get0_public_key(ec) == NULL)
         ossl_raise(eECError, "can't export - no public key set");
-
-    if (EC_KEY_check_key(ec) != 1)
-	ossl_raise(eECError, "can't export - EC_KEY_check_key failed");
-
     if (EC_KEY_get0_private_key(ec))
-        private = 1;
-
-    if (!NIL_P(ciph)) {
-	cipher = ossl_evp_get_cipherbyname(ciph);
-	pass = ossl_pem_passwd_value(pass);
-    }
-
-    if (!(out = BIO_new(BIO_s_mem())))
-        ossl_raise(eECError, "BIO_new(BIO_s_mem())");
-
-    switch(format) {
-    case EXPORT_PEM:
-    	if (private) {
-            i = PEM_write_bio_ECPrivateKey(out, ec, cipher, NULL, 0, ossl_pem_passwd_cb, (void *)pass);
-    	} else {
-            i = PEM_write_bio_EC_PUBKEY(out, ec);
-        }
-
-    	break;
-    case EXPORT_DER:
-        if (private) {
-            i = i2d_ECPrivateKey_bio(out, ec);
-        } else {
-            i = i2d_EC_PUBKEY_bio(out, ec);
-        }
-
-    	break;
-    default:
-        BIO_free(out);
-    	ossl_raise(rb_eRuntimeError, "unknown format (internal error)");
-    }
-
-    if (i != 1) {
-        BIO_free(out);
-        ossl_raise(eECError, "outlen=%d", i);
-    }
-
-    str = ossl_membio2str(out);
-
-    return str;
-}
-
-/*
- *  call-seq:
- *     key.export([cipher, pass_phrase]) => String
- *     key.to_pem([cipher, pass_phrase]) => 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.
- */
-static VALUE ossl_ec_key_export(int argc, VALUE *argv, VALUE self)
-{
-    VALUE cipher, passwd;
-    rb_scan_args(argc, argv, "02", &cipher, &passwd);
-    return ossl_ec_key_to_string(self, cipher, passwd, EXPORT_PEM);
+        return ossl_pkey_export_traditional(argc, argv, self, 0);
+    else
+        return ossl_pkey_export_spki(self, 0);
 }
 
 /*
  *  call-seq:
  *     key.to_der   => String
  *
- *  See the OpenSSL documentation for i2d_ECPrivateKey_bio()
- */
-static VALUE ossl_ec_key_to_der(VALUE self)
-{
-    return ossl_ec_key_to_string(self, Qnil, Qnil, EXPORT_DER);
-}
-
-/*
- *  call-seq:
- *     key.to_text   => String
+ * 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.
  *
- *  See the OpenSSL documentation for EC_KEY_print()
+ * Consider using #public_to_der or #private_to_der instead.
  */
-static VALUE ossl_ec_key_to_text(VALUE self)
+static VALUE
+ossl_ec_key_to_der(VALUE self)
 {
-    EC_KEY *ec;
-    BIO *out;
-    VALUE str;
+    OSSL_3_const EC_KEY *ec;
 
     GetEC(self, ec);
-    if (!(out = BIO_new(BIO_s_mem()))) {
-	ossl_raise(eECError, "BIO_new(BIO_s_mem())");
-    }
-    if (!EC_KEY_print(out, ec, 0)) {
-	BIO_free(out);
-	ossl_raise(eECError, "EC_KEY_print");
-    }
-    str = ossl_membio2str(out);
-
-    return str;
+    if (EC_KEY_get0_public_key(ec) == NULL)
+        ossl_raise(eECError, "can't export - no public key set");
+    if (EC_KEY_get0_private_key(ec))
+        return ossl_pkey_export_traditional(0, NULL, self, 1);
+    else
+        return ossl_pkey_export_spki(self, 1);
 }
-
 /*
  *  call-seq:
  *     key.generate_key!   => self
@@ -554,6 +513,9 @@
  */
 static VALUE ossl_ec_key_generate_key(VALUE self)
 {
+#if OSSL_OPENSSL_PREREQ(3, 0, 0)
+    rb_raise(ePKeyError, "pkeys are immutable on OpenSSL 3.0");
+#else
     EC_KEY *ec;
 
     GetEC(self, ec);
@@ -561,107 +523,53 @@
 	ossl_raise(eECError, "EC_KEY_generate_key");
 
     return self;
+#endif
 }
 
 /*
- *  call-seq:
- *     key.check_key   => true
+ * call-seq:
+ *    key.check_key   => true
  *
- *  Raises an exception if the key is invalid.
+ * Raises an exception if the key is invalid.
  *
- *  See the OpenSSL documentation for EC_KEY_check_key()
+ * See also the man page EVP_PKEY_public_check(3).
  */
 static VALUE ossl_ec_key_check_key(VALUE self)
 {
-    EC_KEY *ec;
-
-    GetEC(self, ec);
-    if (EC_KEY_check_key(ec) != 1)
-	ossl_raise(eECError, "EC_KEY_check_key");
-
-    return Qtrue;
-}
-
-/*
- *  call-seq:
- *     key.dh_compute_key(pubkey)   => String
- *
- *  See the OpenSSL documentation for ECDH_compute_key()
- */
-static VALUE ossl_ec_key_dh_compute_key(VALUE self, VALUE pubkey)
-{
-    EC_KEY *ec;
-    EC_POINT *point;
-    int buf_len;
-    VALUE str;
-
-    GetEC(self, ec);
-    GetECPoint(pubkey, point);
-
-/* BUG: need a way to figure out the maximum string size */
-    buf_len = 1024;
-    str = rb_str_new(0, buf_len);
-/* BUG: take KDF as a block */
-    buf_len = ECDH_compute_key(RSTRING_PTR(str), buf_len, point, ec, NULL);
-    if (buf_len < 0)
-         ossl_raise(eECError, "ECDH_compute_key");
-
-    rb_str_resize(str, buf_len);
-
-    return str;
-}
-
-/* sign_setup */
-
-/*
- *  call-seq:
- *     key.dsa_sign_asn1(data)   => String
- *
- *  See the OpenSSL documentation for ECDSA_sign()
- */
-static VALUE ossl_ec_key_dsa_sign_asn1(VALUE self, VALUE data)
-{
-    EC_KEY *ec;
-    unsigned int buf_len;
-    VALUE str;
+#ifdef HAVE_EVP_PKEY_CHECK
+    EVP_PKEY *pkey;
+    EVP_PKEY_CTX *pctx;
+    const EC_KEY *ec;
 
+    GetPKey(self, pkey);
     GetEC(self, ec);
-    StringValue(data);
-
-    if (EC_KEY_get0_private_key(ec) == NULL)
-	ossl_raise(eECError, "Private EC key needed!");
-
-    str = rb_str_new(0, ECDSA_size(ec));
-    if (ECDSA_sign(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(str), &buf_len, ec) != 1)
-	ossl_raise(eECError, "ECDSA_sign");
-    rb_str_set_len(str, buf_len);
-
-    return str;
-}
+    pctx = EVP_PKEY_CTX_new(pkey, /* engine */NULL);
+    if (!pctx)
+        ossl_raise(eECError, "EVP_PKEY_CTX_new");
+
+    if (EC_KEY_get0_private_key(ec) != NULL) {
+        if (EVP_PKEY_check(pctx) != 1) {
+            EVP_PKEY_CTX_free(pctx);
+            ossl_raise(eECError, "EVP_PKEY_check");
+        }
+    }
+    else {
+        if (EVP_PKEY_public_check(pctx) != 1) {
+            EVP_PKEY_CTX_free(pctx);
+            ossl_raise(eECError, "EVP_PKEY_public_check");
+        }
+    }
 
-/*
- *  call-seq:
- *     key.dsa_verify_asn1(data, sig)   => true or false
- *
- *  See the OpenSSL documentation for ECDSA_verify()
- */
-static VALUE ossl_ec_key_dsa_verify_asn1(VALUE self, VALUE data, VALUE sig)
-{
+    EVP_PKEY_CTX_free(pctx);
+#else
     EC_KEY *ec;
 
     GetEC(self, ec);
-    StringValue(data);
-    StringValue(sig);
-
-    switch (ECDSA_verify(0, (unsigned char *) RSTRING_PTR(data), RSTRING_LENINT(data), (unsigned char *) RSTRING_PTR(sig), (int)RSTRING_LEN(sig), ec)) {
-    case 1:	return Qtrue;
-    case 0:	return Qfalse;
-    default:	break;
-    }
-
-    ossl_raise(eECError, "ECDSA_verify");
+    if (EC_KEY_check_key(ec) != 1)
+	ossl_raise(eECError, "EC_KEY_check_key");
+#endif
 
-    UNREACHABLE;
+    return Qtrue;
 }
 
 /*
@@ -670,7 +578,7 @@
 static void
 ossl_ec_group_free(void *ptr)
 {
-    EC_GROUP_clear_free(ptr);
+    EC_GROUP_free(ptr);
 }
 
 static const rb_data_type_t ossl_ec_group_type = {
@@ -678,7 +586,7 @@
     {
 	0, ossl_ec_group_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static VALUE
@@ -706,20 +614,11 @@
  * call-seq:
  *   OpenSSL::PKey::EC::Group.new(ec_group)
  *   OpenSSL::PKey::EC::Group.new(pem_or_der_encoded)
- *   OpenSSL::PKey::EC::Group.new(ec_method)
  *   OpenSSL::PKey::EC::Group.new(:GFp, bignum_p, bignum_a, bignum_b)
  *   OpenSSL::PKey::EC::Group.new(:GF2m, bignum_p, bignum_a, bignum_b)
  *
  * Creates a new EC::Group object.
  *
- * _ec_method_ is a symbol that represents an EC_METHOD. Currently the following
- * are supported:
- *
- * * :GFp_simple
- * * :GFp_mont
- * * :GFp_nist
- * * :GF2m_simple
- *
  * If the first argument is :GFp or :GF2m, creates a new curve with given
  * parameters.
  */
@@ -734,29 +633,7 @@
 
     switch (rb_scan_args(argc, argv, "13", &arg1, &arg2, &arg3, &arg4)) {
     case 1:
-        if (SYMBOL_P(arg1)) {
-            const EC_METHOD *method = NULL;
-            ID id = SYM2ID(arg1);
-
-            if (id == s_GFp_simple) {
-                method = EC_GFp_simple_method();
-            } else if (id == s_GFp_mont) {
-                method = EC_GFp_mont_method();
-            } else if (id == s_GFp_nist) {
-                method = EC_GFp_nist_method();
-#if !defined(OPENSSL_NO_EC2M)
-            } else if (id == s_GF2m_simple) {
-                method = EC_GF2m_simple_method();
-#endif
-            }
-
-            if (method) {
-                if ((group = EC_GROUP_new(method)) == NULL)
-                    ossl_raise(eEC_GROUP, "EC_GROUP_new");
-            } else {
-                ossl_raise(rb_eArgError, "unknown symbol, must be :GFp_simple, :GFp_mont, :GFp_nist or :GF2m_simple");
-            }
-        } else if (rb_obj_is_kind_of(arg1, cEC_GROUP)) {
+        if (rb_obj_is_kind_of(arg1, cEC_GROUP)) {
             const EC_GROUP *arg1_group;
 
             GetECGroup(arg1, arg1_group);
@@ -820,8 +697,7 @@
         ossl_raise(rb_eArgError, "wrong number of arguments");
     }
 
-    if (group == NULL)
-        ossl_raise(eEC_GROUP, "");
+    ASSUME(group);
     RTYPEDDATA_DATA(self) = group;
 
     return self;
@@ -860,10 +736,11 @@
     GetECGroup(a, group1);
     GetECGroup(b, group2);
 
-    if (EC_GROUP_cmp(group1, group2, ossl_bn_ctx) == 1)
-       return Qfalse;
-
-    return Qtrue;
+    switch (EC_GROUP_cmp(group1, group2, ossl_bn_ctx)) {
+    case 0: return Qtrue;
+    case 1: return Qfalse;
+    default: ossl_raise(eEC_GROUP, "EC_GROUP_cmp");
+    }
 }
 
 /*
@@ -1294,7 +1171,7 @@
     {
 	0, ossl_ec_point_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static VALUE
@@ -1424,10 +1301,13 @@
     GetECPoint(b, point2);
     GetECGroup(group_v1, group);
 
-    if (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx) == 1)
-        return Qfalse;
+    switch (EC_POINT_cmp(group, point1, point2, ossl_bn_ctx)) {
+    case 0: return Qtrue;
+    case 1: return Qfalse;
+    default: ossl_raise(eEC_POINT, "EC_POINT_cmp");
+    }
 
-    return Qtrue;
+    UNREACHABLE;
 }
 
 /*
@@ -1445,7 +1325,7 @@
     switch (EC_POINT_is_at_infinity(group, point)) {
     case 1: return Qtrue;
     case 0: return Qfalse;
-    default: ossl_raise(cEC_POINT, "EC_POINT_is_at_infinity");
+    default: ossl_raise(eEC_POINT, "EC_POINT_is_at_infinity");
     }
 
     UNREACHABLE;
@@ -1466,7 +1346,7 @@
     switch (EC_POINT_is_on_curve(group, point, ossl_bn_ctx)) {
     case 1: return Qtrue;
     case 0: return Qfalse;
-    default: ossl_raise(cEC_POINT, "EC_POINT_is_on_curve");
+    default: ossl_raise(eEC_POINT, "EC_POINT_is_on_curve");
     }
 
     UNREACHABLE;
@@ -1475,6 +1355,8 @@
 /*
  * call-seq:
  *   point.make_affine! => self
+ *
+ * This method is deprecated and should not be used. This is a no-op.
  */
 static VALUE ossl_ec_point_make_affine(VALUE self)
 {
@@ -1484,8 +1366,11 @@
     GetECPoint(self, point);
     GetECPointGroup(self, group);
 
+    rb_warn("OpenSSL::PKey::EC::Point#make_affine! is deprecated");
+#if !OSSL_OPENSSL_PREREQ(3, 0, 0)
     if (EC_POINT_make_affine(group, point, ossl_bn_ctx) != 1)
-        ossl_raise(cEC_POINT, "EC_POINT_make_affine");
+        ossl_raise(eEC_POINT, "EC_POINT_make_affine");
+#endif
 
     return self;
 }
@@ -1503,7 +1388,7 @@
     GetECPointGroup(self, group);
 
     if (EC_POINT_invert(group, point, ossl_bn_ctx) != 1)
-        ossl_raise(cEC_POINT, "EC_POINT_invert");
+        ossl_raise(eEC_POINT, "EC_POINT_invert");
 
     return self;
 }
@@ -1521,7 +1406,7 @@
     GetECPointGroup(self, group);
 
     if (EC_POINT_set_to_infinity(group, point) != 1)
-        ossl_raise(cEC_POINT, "EC_POINT_set_to_infinity");
+        ossl_raise(eEC_POINT, "EC_POINT_set_to_infinity");
 
     return self;
 }
@@ -1564,6 +1449,34 @@
 
 /*
  * call-seq:
+ *   point.add(point) => point
+ *
+ * Performs elliptic curve point addition.
+ */
+static VALUE ossl_ec_point_add(VALUE self, VALUE other)
+{
+    EC_POINT *point_self, *point_other, *point_result;
+    const EC_GROUP *group;
+    VALUE group_v = rb_attr_get(self, id_i_group);
+    VALUE result;
+
+    GetECPoint(self, point_self);
+    GetECPoint(other, point_other);
+    GetECGroup(group_v, group);
+
+    result = rb_obj_alloc(cEC_POINT);
+    ossl_ec_point_initialize(1, &group_v, result);
+    GetECPoint(result, point_result);
+
+    if (EC_POINT_add(group, point_result, point_self, point_other, ossl_bn_ctx) != 1) {
+        ossl_raise(eEC_POINT, "EC_POINT_add");
+    }
+
+    return result;
+}
+
+/*
+ * call-seq:
  *   point.mul(bn1 [, bn2]) => point
  *   point.mul(bns, points [, bn2]) => point
  *
@@ -1603,6 +1516,10 @@
 	if (EC_POINT_mul(group, point_result, bn_g, point_self, bn, ossl_bn_ctx) != 1)
 	    ossl_raise(eEC_POINT, NULL);
     } else {
+#if (defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3) || defined(LIBRESSL_VERSION_NUMBER)
+        rb_raise(rb_eNotImpError, "calling #mul with arrays is not" \
+                 "supported by this OpenSSL version");
+#else
 	/*
 	 * bignums | arg1[0] | arg1[1] | arg1[2] | ...
 	 * points  | self    | arg2[0] | arg2[1] | ...
@@ -1617,6 +1534,9 @@
 	if (RARRAY_LEN(arg1) != RARRAY_LEN(arg2) + 1) /* arg2 must be 1 larger */
 	    ossl_raise(rb_eArgError, "bns must be 1 longer than points; see the documentation");
 
+        rb_warning("OpenSSL::PKey::EC::Point#mul(ary, ary) is deprecated; " \
+                   "use #mul(bn) form instead");
+
 	num = RARRAY_LEN(arg1);
 	bns_tmp = rb_ary_tmp_new(num);
 	bignums = ALLOCV_N(const BIGNUM *, tmp_b, num);
@@ -1642,6 +1562,7 @@
 
 	ALLOCV_END(tmp_b);
 	ALLOCV_END(tmp_p);
+#endif
     }
 
     return result;
@@ -1682,10 +1603,6 @@
 
     s_GFp = rb_intern("GFp");
     s_GF2m = rb_intern("GF2m");
-    s_GFp_simple = rb_intern("GFp_simple");
-    s_GFp_mont = rb_intern("GFp_mont");
-    s_GFp_nist = rb_intern("GFp_nist");
-    s_GF2m_simple = rb_intern("GF2m_simple");
 
     ID_uncompressed = rb_intern("uncompressed");
     ID_compressed = rb_intern("compressed");
@@ -1700,8 +1617,9 @@
 
     rb_define_singleton_method(cEC, "generate", ossl_ec_key_s_generate, 1);
     rb_define_method(cEC, "initialize", ossl_ec_key_initialize, -1);
+#ifndef HAVE_EVP_PKEY_DUP
     rb_define_method(cEC, "initialize_copy", ossl_ec_key_initialize_copy, 1);
-/* copy/dup/cmp */
+#endif
 
     rb_define_method(cEC, "group", ossl_ec_key_get_group, 0);
     rb_define_method(cEC, "group=", ossl_ec_key_set_group, 1);
@@ -1724,15 +1642,9 @@
     rb_define_alias(cEC, "generate_key", "generate_key!");
     rb_define_method(cEC, "check_key", ossl_ec_key_check_key, 0);
 
-    rb_define_method(cEC, "dh_compute_key", ossl_ec_key_dh_compute_key, 1);
-    rb_define_method(cEC, "dsa_sign_asn1", ossl_ec_key_dsa_sign_asn1, 1);
-    rb_define_method(cEC, "dsa_verify_asn1", ossl_ec_key_dsa_verify_asn1, 2);
-/* do_sign/do_verify */
-
     rb_define_method(cEC, "export", ossl_ec_key_export, -1);
     rb_define_alias(cEC, "to_pem", "export");
     rb_define_method(cEC, "to_der", ossl_ec_key_to_der, 0);
-    rb_define_method(cEC, "to_text", ossl_ec_key_to_text, 0);
 
 
     rb_define_alloc_func(cEC_GROUP, ossl_ec_group_alloc);
@@ -1786,6 +1698,7 @@
 /* all the other methods */
 
     rb_define_method(cEC_POINT, "to_octet_string", ossl_ec_point_to_octet_string, 1);
+    rb_define_method(cEC_POINT, "add", ossl_ec_point_add, 1);
     rb_define_method(cEC_POINT, "mul", ossl_ec_point_mul, -1);
 
     id_i_group = rb_intern("@group");
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkey.h ruby-2.5.9/ext/openssl/ossl_pkey.h
--- ruby-2.5.9.orig/ext/openssl/ossl_pkey.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkey.h	2025-01-29 19:08:02.378421281 +0100
@@ -5,29 +5,20 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
-#if !defined(_OSSL_PKEY_H_)
-#define _OSSL_PKEY_H_
+#if !defined(OSSL_PKEY_H)
+#define OSSL_PKEY_H
 
 extern VALUE mPKey;
 extern VALUE cPKey;
 extern VALUE ePKeyError;
 extern const rb_data_type_t ossl_evp_pkey_type;
 
-#define OSSL_PKEY_SET_PRIVATE(obj) rb_iv_set((obj), "private", Qtrue)
-#define OSSL_PKEY_SET_PUBLIC(obj)  rb_iv_set((obj), "private", Qfalse)
-#define OSSL_PKEY_IS_PRIVATE(obj)  (rb_iv_get((obj), "private") == Qtrue)
-
-#define NewPKey(klass) \
-    TypedData_Wrap_Struct((klass), &ossl_evp_pkey_type, 0)
-#define SetPKey(obj, pkey) do { \
-    if (!(pkey)) { \
-	rb_raise(rb_eRuntimeError, "PKEY wasn't initialized!"); \
-    } \
-    RTYPEDDATA_DATA(obj) = (pkey); \
-    OSSL_PKEY_SET_PUBLIC(obj); \
-} while (0)
+/* For ENGINE */
+#define OSSL_PKEY_SET_PRIVATE(obj) rb_ivar_set((obj), rb_intern("private"), Qtrue)
+#define OSSL_PKEY_IS_PRIVATE(obj)  (rb_attr_get((obj), rb_intern("private")) == Qtrue)
+
 #define GetPKey(obj, pkey) do {\
     TypedData_Get_Struct((obj), EVP_PKEY, &ossl_evp_pkey_type, (pkey)); \
     if (!(pkey)) { \
@@ -35,57 +26,51 @@
     } \
 } while (0)
 
-struct ossl_generate_cb_arg {
-    int yield;
-    int interrupted;
-    int state;
-};
-int ossl_generate_cb_2(int p, int n, BN_GENCB *cb);
-void ossl_generate_cb_stop(void *ptr);
-
+/* Takes ownership of the EVP_PKEY */
 VALUE ossl_pkey_new(EVP_PKEY *);
 void ossl_pkey_check_public_key(const EVP_PKEY *);
+EVP_PKEY *ossl_pkey_read_generic(BIO *, VALUE);
 EVP_PKEY *GetPKeyPtr(VALUE);
 EVP_PKEY *DupPKeyPtr(VALUE);
 EVP_PKEY *GetPrivPKeyPtr(VALUE);
+
+/*
+ * Serializes _self_ in X.509 SubjectPublicKeyInfo format and returns the
+ * resulting String. Sub-classes use this when overriding #to_der.
+ */
+VALUE ossl_pkey_export_spki(VALUE self, int to_der);
+/*
+ * Serializes the private key _self_ in the traditional private key format
+ * and returns the resulting String. Sub-classes use this when overriding
+ * #to_der.
+ */
+VALUE ossl_pkey_export_traditional(int argc, VALUE *argv, VALUE self,
+				   int to_der);
+
 void Init_ossl_pkey(void);
 
 /*
  * RSA
  */
 extern VALUE cRSA;
-extern VALUE eRSAError;
-
-VALUE ossl_rsa_new(EVP_PKEY *);
 void Init_ossl_rsa(void);
 
 /*
  * DSA
  */
 extern VALUE cDSA;
-extern VALUE eDSAError;
-
-VALUE ossl_dsa_new(EVP_PKEY *);
 void Init_ossl_dsa(void);
 
 /*
  * DH
  */
 extern VALUE cDH;
-extern VALUE eDHError;
-
-VALUE ossl_dh_new(EVP_PKEY *);
 void Init_ossl_dh(void);
 
 /*
  * EC
  */
 extern VALUE cEC;
-extern VALUE eECError;
-extern VALUE cEC_GROUP;
-extern VALUE eEC_GROUP;
-extern VALUE cEC_POINT;
-extern VALUE eEC_POINT;
 VALUE ossl_ec_new(EVP_PKEY *);
 void Init_ossl_ec(void);
 
@@ -96,7 +81,7 @@
  */									\
 static VALUE ossl_##_keytype##_get_##_name(VALUE self)			\
 {									\
-	_type *obj;							\
+	const _type *obj;						\
 	const BIGNUM *bn;						\
 									\
 	Get##_type(self, obj);						\
@@ -120,6 +105,7 @@
 	OSSL_PKEY_BN_DEF_GETTER0(_keytype, _type, a2,			\
 		_type##_get0_##_group(obj, NULL, &bn))
 
+#if !OSSL_OPENSSL_PREREQ(3, 0, 0)
 #define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3)	\
 /*									\
  *  call-seq:								\
@@ -133,13 +119,13 @@
 	BIGNUM *bn3 = NULL, *orig_bn3 = NIL_P(v3) ? NULL : GetBNPtr(v3);\
 									\
 	Get##_type(self, obj);						\
-	if (orig_bn1 && !(bn1 = BN_dup(orig_bn1)) ||			\
-	    orig_bn2 && !(bn2 = BN_dup(orig_bn2)) ||			\
-	    orig_bn3 && !(bn3 = BN_dup(orig_bn3))) {			\
+        if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) ||			\
+            (orig_bn2 && !(bn2 = BN_dup(orig_bn2))) ||			\
+            (orig_bn3 && !(bn3 = BN_dup(orig_bn3)))) {			\
 		BN_clear_free(bn1);					\
 		BN_clear_free(bn2);					\
 		BN_clear_free(bn3);					\
-		ossl_raise(eBNError, NULL);				\
+		ossl_raise(ePKeyError, "BN_dup");			\
 	}								\
 									\
 	if (!_type##_set0_##_group(obj, bn1, bn2, bn3)) {		\
@@ -163,11 +149,11 @@
 	BIGNUM *bn2 = NULL, *orig_bn2 = NIL_P(v2) ? NULL : GetBNPtr(v2);\
 									\
 	Get##_type(self, obj);						\
-	if (orig_bn1 && !(bn1 = BN_dup(orig_bn1)) ||			\
-	    orig_bn2 && !(bn2 = BN_dup(orig_bn2))) {			\
+        if ((orig_bn1 && !(bn1 = BN_dup(orig_bn1))) ||			\
+            (orig_bn2 && !(bn2 = BN_dup(orig_bn2)))) {			\
 		BN_clear_free(bn1);					\
 		BN_clear_free(bn2);					\
-		ossl_raise(eBNError, NULL);				\
+		ossl_raise(ePKeyError, "BN_dup");			\
 	}								\
 									\
 	if (!_type##_set0_##_group(obj, bn1, bn2)) {			\
@@ -177,36 +163,22 @@
 	}								\
 	return self;							\
 }
+#else
+#define OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3)	\
+static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2, VALUE v3) \
+{									\
+        rb_raise(ePKeyError,						\
+                 #_keytype"#set_"#_group"= is incompatible with OpenSSL 3.0"); \
+}
 
-#define OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, _name)	\
-/*									\
- *  call-seq:								\
- *     _keytype##.##_name = bn -> bn					\
- */									\
-static VALUE ossl_##_keytype##_set_##_name(VALUE self, VALUE bignum)	\
+#define OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2)	\
+static VALUE ossl_##_keytype##_set_##_group(VALUE self, VALUE v1, VALUE v2) \
 {									\
-	_type *obj;							\
-	BIGNUM *bn;							\
-									\
-	rb_warning("#"#_name"= is deprecated; use #set_"#_group);	\
-	Get##_type(self, obj);						\
-	if (NIL_P(bignum)) {						\
-		BN_clear_free(obj->_name);				\
-		obj->_name = NULL;					\
-		return Qnil;						\
-	}								\
-									\
-	bn = GetBNPtr(bignum);						\
-	if (obj->_name == NULL)						\
-		obj->_name = BN_new();					\
-	if (obj->_name == NULL)						\
-		ossl_raise(eBNError, NULL);				\
-	if (BN_copy(obj->_name, bn) == NULL)				\
-		ossl_raise(eBNError, NULL);				\
-	return bignum;							\
+        rb_raise(ePKeyError,						\
+                 #_keytype"#set_"#_group"= is incompatible with OpenSSL 3.0"); \
 }
+#endif
 
-#if defined(HAVE_OPAQUE_OPENSSL) /* OpenSSL 1.1.0 */
 #define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3)		\
 	OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3)	\
 	OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3)
@@ -218,24 +190,4 @@
 #define DEF_OSSL_PKEY_BN(class, keytype, name)				\
 	rb_define_method((class), #name, ossl_##keytype##_get_##name, 0)
 
-#else
-#define OSSL_PKEY_BN_DEF3(_keytype, _type, _group, a1, a2, a3)		\
-	OSSL_PKEY_BN_DEF_GETTER3(_keytype, _type, _group, a1, a2, a3)	\
-	OSSL_PKEY_BN_DEF_SETTER3(_keytype, _type, _group, a1, a2, a3)	\
-	OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a1)	\
-	OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a2)	\
-	OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a3)
-
-#define OSSL_PKEY_BN_DEF2(_keytype, _type, _group, a1, a2)		\
-	OSSL_PKEY_BN_DEF_GETTER2(_keytype, _type, _group, a1, a2)	\
-	OSSL_PKEY_BN_DEF_SETTER2(_keytype, _type, _group, a1, a2)	\
-	OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a1)	\
-	OSSL_PKEY_BN_DEF_SETTER_OLD(_keytype, _type, _group, a2)
-
-#define DEF_OSSL_PKEY_BN(class, keytype, name) do {			\
-	rb_define_method((class), #name, ossl_##keytype##_get_##name, 0);\
-	rb_define_method((class), #name "=", ossl_##keytype##_set_##name, 1);\
-} while (0)
-#endif /* HAVE_OPAQUE_OPENSSL */
-
-#endif /* _OSSL_PKEY_H_ */
+#endif /* OSSL_PKEY_H */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_pkey_rsa.c ruby-2.5.9/ext/openssl/ossl_pkey_rsa.c
--- ruby-2.5.9.orig/ext/openssl/ossl_pkey_rsa.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_pkey_rsa.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -24,16 +24,16 @@
 } while (0)
 
 static inline int
-RSA_HAS_PRIVATE(RSA *rsa)
+RSA_HAS_PRIVATE(OSSL_3_const RSA *rsa)
 {
-    const BIGNUM *p, *q;
+    const BIGNUM *e, *d;
 
-    RSA_get0_factors(rsa, &p, &q);
-    return p && q; /* d? why? */
+    RSA_get0_key(rsa, NULL, &e, &d);
+    return e && d;
 }
 
 static inline int
-RSA_PRIVATE(VALUE obj, RSA *rsa)
+RSA_PRIVATE(VALUE obj, OSSL_3_const RSA *rsa)
 {
     return RSA_HAS_PRIVATE(rsa) || OSSL_PKEY_IS_PRIVATE(obj);
 }
@@ -42,254 +42,127 @@
  * Classes
  */
 VALUE cRSA;
-VALUE eRSAError;
-
-/*
- * Public
- */
-static VALUE
-rsa_instance(VALUE klass, RSA *rsa)
-{
-    EVP_PKEY *pkey;
-    VALUE obj;
-
-    if (!rsa) {
-	return Qfalse;
-    }
-    obj = NewPKey(klass);
-    if (!(pkey = EVP_PKEY_new())) {
-	return Qfalse;
-    }
-    if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
-	EVP_PKEY_free(pkey);
-	return Qfalse;
-    }
-    SetPKey(obj, pkey);
-
-    return obj;
-}
-
-VALUE
-ossl_rsa_new(EVP_PKEY *pkey)
-{
-    VALUE obj;
-
-    if (!pkey) {
-	obj = rsa_instance(cRSA, RSA_new());
-    }
-    else {
-	obj = NewPKey(cRSA);
-	if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
-	    ossl_raise(rb_eTypeError, "Not a RSA key!");
-	}
-	SetPKey(obj, pkey);
-    }
-    if (obj == Qfalse) {
-	ossl_raise(eRSAError, NULL);
-    }
-
-    return obj;
-}
+static VALUE eRSAError;
 
 /*
  * Private
  */
-struct rsa_blocking_gen_arg {
-    RSA *rsa;
-    BIGNUM *e;
-    int size;
-    BN_GENCB *cb;
-    int result;
-};
-
-static void *
-rsa_blocking_gen(void *arg)
-{
-    struct rsa_blocking_gen_arg *gen = (struct rsa_blocking_gen_arg *)arg;
-    gen->result = RSA_generate_key_ex(gen->rsa, gen->size, gen->e, gen->cb);
-    return 0;
-}
-
-static RSA *
-rsa_generate(int size, unsigned long exp)
-{
-    int i;
-    struct ossl_generate_cb_arg cb_arg = { 0 };
-    struct rsa_blocking_gen_arg gen_arg;
-    RSA *rsa = RSA_new();
-    BIGNUM *e = BN_new();
-    BN_GENCB *cb = BN_GENCB_new();
-
-    if (!rsa || !e || !cb) {
-	RSA_free(rsa);
-	BN_free(e);
-	BN_GENCB_free(cb);
-	return NULL;
-    }
-    for (i = 0; i < (int)sizeof(exp) * 8; ++i) {
-	if (exp & (1UL << i)) {
-	    if (BN_set_bit(e, i) == 0) {
-		BN_free(e);
-		RSA_free(rsa);
-		BN_GENCB_free(cb);
-		return NULL;
-	    }
-	}
-    }
-
-    if (rb_block_given_p())
-	cb_arg.yield = 1;
-    BN_GENCB_set(cb, ossl_generate_cb_2, &cb_arg);
-    gen_arg.rsa = rsa;
-    gen_arg.e = e;
-    gen_arg.size = size;
-    gen_arg.cb = cb;
-    if (cb_arg.yield == 1) {
-	/* we cannot release GVL when callback proc is supplied */
-	rsa_blocking_gen(&gen_arg);
-    } else {
-	/* there's a chance to unblock */
-	rb_thread_call_without_gvl(rsa_blocking_gen, &gen_arg, ossl_generate_cb_stop, &cb_arg);
-    }
-
-    BN_GENCB_free(cb);
-    BN_free(e);
-    if (!gen_arg.result) {
-	RSA_free(rsa);
-	if (cb_arg.state) {
-	    /* must clear OpenSSL error stack */
-	    ossl_clear_error();
-	    rb_jump_tag(cb_arg.state);
-	}
-	return NULL;
-    }
-
-    return rsa;
-}
-
-/*
- * call-seq:
- *   RSA.generate(size)           => RSA instance
- *   RSA.generate(size, exponent) => RSA instance
- *
- * Generates an RSA keypair.  _size_ is an integer representing the desired key
- * size.  Keys smaller than 1024 should be considered insecure.  _exponent_ is
- * an odd number normally 3, 17, or 65537.
- */
-static VALUE
-ossl_rsa_s_generate(int argc, VALUE *argv, VALUE klass)
-{
-/* why does this method exist?  why can't initialize take an optional exponent? */
-    RSA *rsa;
-    VALUE size, exp;
-    VALUE obj;
-
-    rb_scan_args(argc, argv, "11", &size, &exp);
-
-    rsa = rsa_generate(NUM2INT(size), NIL_P(exp) ? RSA_F4 : NUM2ULONG(exp)); /* err handled by rsa_instance */
-    obj = rsa_instance(klass, rsa);
-
-    if (obj == Qfalse) {
-	RSA_free(rsa);
-	ossl_raise(eRSAError, NULL);
-    }
-
-    return obj;
-}
-
 /*
  * call-seq:
- *   RSA.new(key_size)                 => RSA instance
- *   RSA.new(encoded_key)              => RSA instance
- *   RSA.new(encoded_key, pass_phrase) => RSA instance
- *
- * Generates or loads an RSA keypair.  If an integer _key_size_ is given it
- * represents the desired key size.  Keys less than 1024 bits should be
- * considered insecure.
+ *   RSA.new -> rsa
+ *   RSA.new(encoded_key [, password ]) -> rsa
+ *   RSA.new(encoded_key) { password } -> rsa
+ *   RSA.new(size [, exponent]) -> rsa
+ *
+ * Generates or loads an \RSA keypair.
+ *
+ * If called without arguments, creates a new instance with no key components
+ * set. They can be set individually by #set_key, #set_factors, and
+ * #set_crt_params.
+ *
+ * If called with a String, tries to parse as DER or PEM encoding of an \RSA key.
+ * 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.
  *
- * A key can instead be loaded from an _encoded_key_ which must be PEM or DER
- * encoded.  A _pass_phrase_ can be used to decrypt the key.  If none is given
- * OpenSSL will prompt for the pass phrase.
- *
- * = Examples
+ * If called with a number, generates a new key pair. This form works as an
+ * alias of RSA.generate.
  *
+ * 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)
 {
     EVP_PKEY *pkey;
     RSA *rsa;
-    BIO *in;
+    BIO *in = NULL;
     VALUE arg, pass;
+    int type;
 
-    GetPKey(self, pkey);
-    if(rb_scan_args(argc, argv, "02", &arg, &pass) == 0) {
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
+
+    /* The RSA.new(size, generator) form is handled by lib/openssl/pkey.rb */
+    rb_scan_args(argc, argv, "02", &arg, &pass);
+    if (argc == 0) {
 	rsa = RSA_new();
+        if (!rsa)
+            ossl_raise(eRSAError, "RSA_new");
+        goto legacy;
+    }
+
+    pass = ossl_pem_passwd_value(pass);
+    arg = ossl_to_der_if_possible(arg);
+    in = ossl_obj2bio(&arg);
+
+    /* First try RSAPublicKey format */
+    rsa = d2i_RSAPublicKey_bio(in, NULL);
+    if (rsa)
+        goto legacy;
+    OSSL_BIO_reset(in);
+    rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);
+    if (rsa)
+        goto legacy;
+    OSSL_BIO_reset(in);
+
+    /* Use the generic routine */
+    pkey = ossl_pkey_read_generic(in, pass);
+    BIO_free(in);
+    if (!pkey)
+        ossl_raise(eRSAError, "Neither PUB key nor PRIV key");
+
+    type = EVP_PKEY_base_id(pkey);
+    if (type != EVP_PKEY_RSA) {
+        EVP_PKEY_free(pkey);
+        rb_raise(eRSAError, "incorrect pkey type: %s", OBJ_nid2sn(type));
     }
-    else if (RB_INTEGER_TYPE_P(arg)) {
-	rsa = rsa_generate(NUM2INT(arg), NIL_P(pass) ? RSA_F4 : NUM2ULONG(pass));
-	if (!rsa) ossl_raise(eRSAError, NULL);
-    }
-    else {
-	pass = ossl_pem_passwd_value(pass);
-	arg = ossl_to_der_if_possible(arg);
-	in = ossl_obj2bio(&arg);
-	rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ossl_pem_passwd_cb, (void *)pass);
-	if (!rsa) {
-	    OSSL_BIO_reset(in);
-	    rsa = PEM_read_bio_RSA_PUBKEY(in, NULL, NULL, NULL);
-	}
-	if (!rsa) {
-	    OSSL_BIO_reset(in);
-	    rsa = d2i_RSAPrivateKey_bio(in, NULL);
-	}
-	if (!rsa) {
-	    OSSL_BIO_reset(in);
-	    rsa = d2i_RSA_PUBKEY_bio(in, NULL);
-	}
-	if (!rsa) {
-	    OSSL_BIO_reset(in);
-	    rsa = PEM_read_bio_RSAPublicKey(in, NULL, NULL, NULL);
-	}
-	if (!rsa) {
-	    OSSL_BIO_reset(in);
-	    rsa = d2i_RSAPublicKey_bio(in, NULL);
-	}
-	BIO_free(in);
-	if (!rsa) {
-	    ossl_raise(eRSAError, "Neither PUB key nor PRIV key");
-	}
-    }
-    if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
-	RSA_free(rsa);
-	ossl_raise(eRSAError, NULL);
-    }
+    RTYPEDDATA_DATA(self) = pkey;
+    return self;
 
+  legacy:
+    BIO_free(in);
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
+        EVP_PKEY_free(pkey);
+        RSA_free(rsa);
+        ossl_raise(eRSAError, "EVP_PKEY_assign_RSA");
+    }
+    RTYPEDDATA_DATA(self) = pkey;
     return self;
 }
 
+#ifndef HAVE_EVP_PKEY_DUP
 static VALUE
 ossl_rsa_initialize_copy(VALUE self, VALUE other)
 {
     EVP_PKEY *pkey;
     RSA *rsa, *rsa_new;
 
-    GetPKey(self, pkey);
-    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_NONE)
-	ossl_raise(eRSAError, "RSA already initialized");
+    TypedData_Get_Struct(self, EVP_PKEY, &ossl_evp_pkey_type, pkey);
+    if (pkey)
+        rb_raise(rb_eTypeError, "pkey already initialized");
     GetRSA(other, rsa);
 
-    rsa_new = ASN1_dup((i2d_of_void *)i2d_RSAPrivateKey, (d2i_of_void *)d2i_RSAPrivateKey, (char *)rsa);
+    rsa_new = (RSA *)ASN1_dup((i2d_of_void *)i2d_RSAPrivateKey,
+                              (d2i_of_void *)d2i_RSAPrivateKey,
+                              (char *)rsa);
     if (!rsa_new)
 	ossl_raise(eRSAError, "ASN1_dup");
 
-    EVP_PKEY_assign_RSA(pkey, rsa_new);
+    pkey = EVP_PKEY_new();
+    if (!pkey || EVP_PKEY_assign_RSA(pkey, rsa_new) != 1) {
+        RSA_free(rsa_new);
+        ossl_raise(eRSAError, "EVP_PKEY_assign_RSA");
+    }
+    RTYPEDDATA_DATA(self) = pkey;
 
     return self;
 }
+#endif
 
 /*
  * call-seq:
@@ -301,7 +174,7 @@
 static VALUE
 ossl_rsa_is_public(VALUE self)
 {
-    RSA *rsa;
+    OSSL_3_const RSA *rsa;
 
     GetRSA(self, rsa);
     /*
@@ -320,220 +193,115 @@
 static VALUE
 ossl_rsa_is_private(VALUE self)
 {
-    RSA *rsa;
+    OSSL_3_const RSA *rsa;
 
     GetRSA(self, rsa);
 
     return RSA_PRIVATE(self, rsa) ? Qtrue : Qfalse;
 }
 
-/*
- * 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
- *
- * 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.
- */
-static VALUE
-ossl_rsa_export(int argc, VALUE *argv, VALUE self)
+static int
+can_export_rsaprivatekey(VALUE self)
 {
-    RSA *rsa;
-    BIO *out;
-    const EVP_CIPHER *ciph = NULL;
-    VALUE cipher, pass, str;
+    OSSL_3_const RSA *rsa;
+    const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
 
     GetRSA(self, rsa);
 
-    rb_scan_args(argc, argv, "02", &cipher, &pass);
-
-    if (!NIL_P(cipher)) {
-	ciph = ossl_evp_get_cipherbyname(cipher);
-	pass = ossl_pem_passwd_value(pass);
-    }
-    if (!(out = BIO_new(BIO_s_mem()))) {
-	ossl_raise(eRSAError, NULL);
-    }
-    if (RSA_HAS_PRIVATE(rsa)) {
-	if (!PEM_write_bio_RSAPrivateKey(out, rsa, ciph, NULL, 0,
-					 ossl_pem_passwd_cb, (void *)pass)) {
-	    BIO_free(out);
-	    ossl_raise(eRSAError, NULL);
-	}
-    } else {
-	if (!PEM_write_bio_RSA_PUBKEY(out, rsa)) {
-	    BIO_free(out);
-	    ossl_raise(eRSAError, NULL);
-	}
-    }
-    str = ossl_membio2str(out);
+    RSA_get0_key(rsa, &n, &e, &d);
+    RSA_get0_factors(rsa, &p, &q);
+    RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
 
-    return str;
+    return n && e && d && p && q && dmp1 && dmq1 && iqmp;
 }
 
 /*
  * call-seq:
- *   rsa.to_der => DER-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
+ *
+ * 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:
  *
- * Outputs this keypair in DER encoding.
+ *     -----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_to_der(VALUE self)
+ossl_rsa_export(int argc, VALUE *argv, VALUE self)
 {
-    RSA *rsa;
-    int (*i2d_func)(const RSA *, unsigned char **);
-    unsigned char *p;
-    long len;
-    VALUE str;
-
-    GetRSA(self, rsa);
-    if (RSA_HAS_PRIVATE(rsa))
-	i2d_func = i2d_RSAPrivateKey;
+    if (can_export_rsaprivatekey(self))
+        return ossl_pkey_export_traditional(argc, argv, self, 0);
     else
-	i2d_func = (int (*)(const RSA *, unsigned char **))i2d_RSA_PUBKEY;
-    if((len = i2d_func(rsa, NULL)) <= 0)
-	ossl_raise(eRSAError, NULL);
-    str = rb_str_new(0, len);
-    p = (unsigned char *)RSTRING_PTR(str);
-    if(i2d_func(rsa, &p) < 0)
-	ossl_raise(eRSAError, NULL);
-    ossl_str_adjust(str, p);
-
-    return str;
+        return ossl_pkey_export_spki(self, 0);
 }
 
 /*
  * call-seq:
- *   rsa.public_encrypt(string)          => String
- *   rsa.public_encrypt(string, padding) => String
+ *   rsa.to_der => DER-format String
  *
- * Encrypt _string_ with the public key.  _padding_ defaults to PKCS1_PADDING.
- * The encrypted string output can be decrypted using #private_decrypt.
- */
-static VALUE
-ossl_rsa_public_encrypt(int argc, VALUE *argv, VALUE self)
-{
-    RSA *rsa;
-    const BIGNUM *rsa_n;
-    int buf_len, pad;
-    VALUE str, buffer, padding;
-
-    GetRSA(self, rsa);
-    RSA_get0_key(rsa, &rsa_n, NULL, NULL);
-    if (!rsa_n)
-	ossl_raise(eRSAError, "incomplete RSA");
-    rb_scan_args(argc, argv, "11", &buffer, &padding);
-    pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
-    StringValue(buffer);
-    str = rb_str_new(0, RSA_size(rsa));
-    buf_len = RSA_public_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
-				 (unsigned char *)RSTRING_PTR(str), rsa, pad);
-    if (buf_len < 0) ossl_raise(eRSAError, NULL);
-    rb_str_set_len(str, buf_len);
-
-    return str;
-}
-
-/*
- * call-seq:
- *   rsa.public_decrypt(string)          => String
- *   rsa.public_decrypt(string, padding) => String
+ * Serializes a private or public key to a DER-encoding.
  *
- * Decrypt _string_, which has been encrypted with the private key, with the
- * public key.  _padding_ defaults to PKCS1_PADDING.
- */
-static VALUE
-ossl_rsa_public_decrypt(int argc, VALUE *argv, VALUE self)
-{
-    RSA *rsa;
-    const BIGNUM *rsa_n;
-    int buf_len, pad;
-    VALUE str, buffer, padding;
-
-    GetRSA(self, rsa);
-    RSA_get0_key(rsa, &rsa_n, NULL, NULL);
-    if (!rsa_n)
-	ossl_raise(eRSAError, "incomplete RSA");
-    rb_scan_args(argc, argv, "11", &buffer, &padding);
-    pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
-    StringValue(buffer);
-    str = rb_str_new(0, RSA_size(rsa));
-    buf_len = RSA_public_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
-				 (unsigned char *)RSTRING_PTR(str), rsa, pad);
-    if (buf_len < 0) ossl_raise(eRSAError, NULL);
-    rb_str_set_len(str, buf_len);
-
-    return str;
-}
-
-/*
- * call-seq:
- *   rsa.private_encrypt(string)          => String
- *   rsa.private_encrypt(string, padding) => String
+ * See #to_pem for details.
  *
- * Encrypt _string_ with the private key.  _padding_ defaults to PKCS1_PADDING.
- * The encrypted string output can be decrypted using #public_decrypt.
- */
-static VALUE
-ossl_rsa_private_encrypt(int argc, VALUE *argv, VALUE self)
-{
-    RSA *rsa;
-    const BIGNUM *rsa_n;
-    int buf_len, pad;
-    VALUE str, buffer, padding;
-
-    GetRSA(self, rsa);
-    RSA_get0_key(rsa, &rsa_n, NULL, NULL);
-    if (!rsa_n)
-	ossl_raise(eRSAError, "incomplete RSA");
-    if (!RSA_PRIVATE(self, rsa))
-	ossl_raise(eRSAError, "private key needed.");
-    rb_scan_args(argc, argv, "11", &buffer, &padding);
-    pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
-    StringValue(buffer);
-    str = rb_str_new(0, RSA_size(rsa));
-    buf_len = RSA_private_encrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
-				  (unsigned char *)RSTRING_PTR(str), rsa, pad);
-    if (buf_len < 0) ossl_raise(eRSAError, NULL);
-    rb_str_set_len(str, buf_len);
-
-    return str;
-}
-
-/*
- * call-seq:
- *   rsa.private_decrypt(string)          => String
- *   rsa.private_decrypt(string, padding) => String
+ * <b>This method is kept for compatibility.</b>
+ * This should only be used when the PKCS #1 RSAPrivateKey format is required.
  *
- * Decrypt _string_, which has been encrypted with the public key, with the
- * private key.  _padding_ defaults to PKCS1_PADDING.
+ * Consider using #public_to_der or #private_to_der instead.
  */
 static VALUE
-ossl_rsa_private_decrypt(int argc, VALUE *argv, VALUE self)
+ossl_rsa_to_der(VALUE self)
 {
-    RSA *rsa;
-    const BIGNUM *rsa_n;
-    int buf_len, pad;
-    VALUE str, buffer, padding;
-
-    GetRSA(self, rsa);
-    RSA_get0_key(rsa, &rsa_n, NULL, NULL);
-    if (!rsa_n)
-	ossl_raise(eRSAError, "incomplete RSA");
-    if (!RSA_PRIVATE(self, rsa))
-	ossl_raise(eRSAError, "private key needed.");
-    rb_scan_args(argc, argv, "11", &buffer, &padding);
-    pad = (argc == 1) ? RSA_PKCS1_PADDING : NUM2INT(padding);
-    StringValue(buffer);
-    str = rb_str_new(0, RSA_size(rsa));
-    buf_len = RSA_private_decrypt(RSTRING_LENINT(buffer), (unsigned char *)RSTRING_PTR(buffer),
-				  (unsigned char *)RSTRING_PTR(str), rsa, pad);
-    if (buf_len < 0) ossl_raise(eRSAError, NULL);
-    rb_str_set_len(str, buf_len);
-
-    return str;
+    if (can_export_rsaprivatekey(self))
+        return ossl_pkey_export_traditional(0, NULL, self, 1);
+    else
+        return ossl_pkey_export_spki(self, 1);
 }
 
 /*
@@ -565,7 +333,7 @@
  *   data = "Sign me!"
  *   pkey = OpenSSL::PKey::RSA.new(2048)
  *   signature = pkey.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA256")
- *   pub_key = pkey.public_key
+ *   pub_key = OpenSSL::PKey.read(pkey.public_to_der)
  *   puts pub_key.verify_pss("SHA256", signature, data,
  *                           salt_length: :auto, mgf1_hash: "SHA256") # => true
  */
@@ -740,7 +508,7 @@
 static VALUE
 ossl_rsa_get_params(VALUE self)
 {
-    RSA *rsa;
+    OSSL_3_const RSA *rsa;
     VALUE hash;
     const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
 
@@ -763,88 +531,6 @@
 }
 
 /*
- * call-seq:
- *   rsa.to_text => String
- *
- * THIS METHOD IS INSECURE, PRIVATE INFORMATION CAN LEAK OUT!!!
- *
- * Dumps all parameters of a keypair to a String
- *
- * Don't use :-)) (It's up to you)
- */
-static VALUE
-ossl_rsa_to_text(VALUE self)
-{
-    RSA *rsa;
-    BIO *out;
-    VALUE str;
-
-    GetRSA(self, rsa);
-    if (!(out = BIO_new(BIO_s_mem()))) {
-	ossl_raise(eRSAError, NULL);
-    }
-    if (!RSA_print(out, rsa, 0)) { /* offset = 0 */
-	BIO_free(out);
-	ossl_raise(eRSAError, NULL);
-    }
-    str = ossl_membio2str(out);
-
-    return str;
-}
-
-/*
- * call-seq:
- *    rsa.public_key -> RSA
- *
- * Makes new RSA instance containing the public key from the private key.
- */
-static VALUE
-ossl_rsa_to_public_key(VALUE self)
-{
-    EVP_PKEY *pkey;
-    RSA *rsa;
-    VALUE obj;
-
-    GetPKeyRSA(self, pkey);
-    /* err check performed by rsa_instance */
-    rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey));
-    obj = rsa_instance(rb_obj_class(self), rsa);
-    if (obj == Qfalse) {
-	RSA_free(rsa);
-	ossl_raise(eRSAError, NULL);
-    }
-    return obj;
-}
-
-/*
- * TODO: Test me
-
-static VALUE
-ossl_rsa_blinding_on(VALUE self)
-{
-    RSA *rsa;
-
-    GetRSA(self, rsa);
-
-    if (RSA_blinding_on(rsa, ossl_bn_ctx) != 1) {
-	ossl_raise(eRSAError, NULL);
-    }
-    return self;
-}
-
-static VALUE
-ossl_rsa_blinding_off(VALUE self)
-{
-    RSA *rsa;
-
-    GetRSA(self, rsa);
-    RSA_blinding_off(rsa);
-
-    return self;
-}
- */
-
-/*
  * Document-method: OpenSSL::PKey::RSA#set_key
  * call-seq:
  *   rsa.set_key(n, e, d) -> self
@@ -905,22 +591,17 @@
      */
     cRSA = rb_define_class_under(mPKey, "RSA", cPKey);
 
-    rb_define_singleton_method(cRSA, "generate", ossl_rsa_s_generate, -1);
     rb_define_method(cRSA, "initialize", ossl_rsa_initialize, -1);
+#ifndef HAVE_EVP_PKEY_DUP
     rb_define_method(cRSA, "initialize_copy", ossl_rsa_initialize_copy, 1);
+#endif
 
     rb_define_method(cRSA, "public?", ossl_rsa_is_public, 0);
     rb_define_method(cRSA, "private?", ossl_rsa_is_private, 0);
-    rb_define_method(cRSA, "to_text", ossl_rsa_to_text, 0);
     rb_define_method(cRSA, "export", ossl_rsa_export, -1);
     rb_define_alias(cRSA, "to_pem", "export");
     rb_define_alias(cRSA, "to_s", "export");
     rb_define_method(cRSA, "to_der", ossl_rsa_to_der, 0);
-    rb_define_method(cRSA, "public_key", ossl_rsa_to_public_key, 0);
-    rb_define_method(cRSA, "public_encrypt", ossl_rsa_public_encrypt, -1);
-    rb_define_method(cRSA, "public_decrypt", ossl_rsa_public_decrypt, -1);
-    rb_define_method(cRSA, "private_encrypt", ossl_rsa_private_encrypt, -1);
-    rb_define_method(cRSA, "private_decrypt", ossl_rsa_private_decrypt, -1);
     rb_define_method(cRSA, "sign_pss", ossl_rsa_sign_pss, -1);
     rb_define_method(cRSA, "verify_pss", ossl_rsa_verify_pss, -1);
 
@@ -938,11 +619,6 @@
 
     rb_define_method(cRSA, "params", ossl_rsa_get_params, 0);
 
-    DefRSAConst(PKCS1_PADDING);
-    DefRSAConst(SSLV23_PADDING);
-    DefRSAConst(NO_PADDING);
-    DefRSAConst(PKCS1_OAEP_PADDING);
-
 /*
  * TODO: Test it
     rb_define_method(cRSA, "blinding_on!", ossl_rsa_blinding_on, 0);
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_provider.c ruby-2.5.9/ext/openssl/ossl_provider.c
--- ruby-2.5.9.orig/ext/openssl/ossl_provider.c	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/ext/openssl/ossl_provider.c	2025-01-29 19:08:02.378421281 +0100
@@ -0,0 +1,211 @@
+/*
+ * This program is licensed under the same licence as Ruby.
+ * (See the file 'COPYING'.)
+ */
+#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 -ruN ruby-2.5.9.orig/ext/openssl/ossl_provider.h ruby-2.5.9/ext/openssl/ossl_provider.h
--- ruby-2.5.9.orig/ext/openssl/ossl_provider.h	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/ext/openssl/ossl_provider.h	2025-01-29 19:08:02.378421281 +0100
@@ -0,0 +1,5 @@
+#if !defined(OSSL_PROVIDER_H)
+#define OSSL_PROVIDER_H
+
+void Init_ossl_provider(void);
+#endif
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_rand.c ruby-2.5.9/ext/openssl/ossl_rand.c
--- ruby-2.5.9.orig/ext/openssl/ossl_rand.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_rand.c	2025-01-29 19:08:02.378421281 +0100
@@ -5,12 +5,12 @@
  * All rights reserved.
  *
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
-VALUE mRandom;
-VALUE eRandomError;
+static VALUE mRandom;
+static VALUE eRandomError;
 
 /*
  *  call-seq:
@@ -67,8 +67,6 @@
 static VALUE
 ossl_rand_load_file(VALUE self, VALUE filename)
 {
-    rb_check_safe_obj(filename);
-
     if(!RAND_load_file(StringValueCStr(filename), -1)) {
 	ossl_raise(eRandomError, NULL);
     }
@@ -86,8 +84,6 @@
 static VALUE
 ossl_rand_write_file(VALUE self, VALUE filename)
 {
-    rb_check_safe_obj(filename);
-
     if (RAND_write_file(StringValueCStr(filename)) == -1) {
 	ossl_raise(eRandomError, NULL);
     }
@@ -124,36 +120,6 @@
     return str;
 }
 
-#if defined(HAVE_RAND_PSEUDO_BYTES)
-/*
- *  call-seq:
- *	pseudo_bytes(length) -> string
- *
- * Generates a String with _length_ number of pseudo-random bytes.
- *
- * Pseudo-random byte sequences generated by ::pseudo_bytes will be unique if
- * they are of sufficient length, but are not necessarily unpredictable.
- *
- * === Example
- *
- *    OpenSSL::Random.pseudo_bytes(12)
- *    #=> "..."
- */
-static VALUE
-ossl_rand_pseudo_bytes(VALUE self, VALUE len)
-{
-    VALUE str;
-    int n = NUM2INT(len);
-
-    str = rb_str_new(0, n);
-    if (RAND_pseudo_bytes((unsigned char *)RSTRING_PTR(str), n) < 1) {
-	ossl_raise(eRandomError, NULL);
-    }
-
-    return str;
-}
-#endif
-
 #ifdef HAVE_RAND_EGD
 /*
  *  call-seq:
@@ -164,8 +130,6 @@
 static VALUE
 ossl_rand_egd(VALUE self, VALUE filename)
 {
-    rb_check_safe_obj(filename);
-
     if (RAND_egd(StringValueCStr(filename)) == -1) {
 	ossl_raise(eRandomError, NULL);
     }
@@ -186,8 +150,6 @@
 {
     int n = NUM2INT(len);
 
-    rb_check_safe_obj(filename);
-
     if (RAND_egd_bytes(StringValueCStr(filename), n) == -1) {
 	ossl_raise(eRandomError, NULL);
     }
@@ -227,8 +189,8 @@
     rb_define_module_function(mRandom, "load_random_file", ossl_rand_load_file, 1);
     rb_define_module_function(mRandom, "write_random_file", ossl_rand_write_file, 1);
     rb_define_module_function(mRandom, "random_bytes", ossl_rand_bytes, 1);
-#if defined(HAVE_RAND_PSEUDO_BYTES)
-    rb_define_module_function(mRandom, "pseudo_bytes", ossl_rand_pseudo_bytes, 1);
+#if OPENSSL_VERSION_NUMBER < 0x10101000 || defined(LIBRESSL_VERSION_NUMBER)
+    rb_define_alias(rb_singleton_class(mRandom), "pseudo_bytes", "random_bytes");
 #endif
 #ifdef HAVE_RAND_EGD
     rb_define_module_function(mRandom, "egd", ossl_rand_egd, 1);
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_rand.h ruby-2.5.9/ext/openssl/ossl_rand.h
--- ruby-2.5.9.orig/ext/openssl/ossl_rand.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_rand.h	2025-01-29 19:08:02.378421281 +0100
@@ -5,14 +5,11 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_RAND_H_)
 #define _OSSL_RAND_H_
 
-extern VALUE mRandom;
-extern VALUE eRandomError;
-
 void Init_ossl_rand(void);
 
 #endif /* _OSSL_RAND_H_ */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ssl.c ruby-2.5.9/ext/openssl/ossl_ssl.c
--- ruby-2.5.9.orig/ext/openssl/ossl_ssl.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_ssl.c	2025-01-29 19:08:02.379421304 +0100
@@ -7,12 +7,22 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
+#ifndef OPENSSL_NO_SOCK
 #define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0]))
 
+#if !defined(OPENSSL_NO_NEXTPROTONEG) && !OSSL_IS_LIBRESSL
+# define OSSL_USE_NEXTPROTONEG
+#endif
+
+#if !defined(TLS1_3_VERSION) && \
+    OSSL_LIBRESSL_PREREQ(3, 2, 0) && !OSSL_LIBRESSL_PREREQ(3, 4, 0)
+#  define TLS1_3_VERSION 0x0304
+#endif
+
 #ifdef _WIN32
 #  define TO_SOCKET(s) _get_osfhandle(s)
 #else
@@ -24,52 +34,49 @@
 } while (0)
 
 VALUE mSSL;
-static VALUE mSSLExtConfig;
 static VALUE eSSLError;
-VALUE cSSLContext;
+static VALUE cSSLContext;
 VALUE cSSLSocket;
 
 static VALUE eSSLErrorWaitReadable;
 static VALUE eSSLErrorWaitWritable;
 
-static ID id_call, ID_callback_state, id_tmp_dh_callback, id_tmp_ecdh_callback,
-	  id_npn_protocols_encoded;
+static ID id_call, ID_callback_state, id_tmp_dh_callback,
+	  id_npn_protocols_encoded, id_each;
 static VALUE sym_exception, sym_wait_readable, sym_wait_writable;
 
 static ID id_i_cert_store, id_i_ca_file, id_i_ca_path, id_i_verify_mode,
 	  id_i_verify_depth, id_i_verify_callback, id_i_client_ca,
 	  id_i_renegotiation_cb, id_i_cert, id_i_key, id_i_extra_chain_cert,
-	  id_i_client_cert_cb, id_i_tmp_ecdh_callback, id_i_timeout,
+	  id_i_client_cert_cb, id_i_timeout,
 	  id_i_session_id_context, id_i_session_get_cb, id_i_session_new_cb,
 	  id_i_session_remove_cb, id_i_npn_select_cb, id_i_npn_protocols,
 	  id_i_alpn_select_cb, id_i_alpn_protocols, id_i_servername_cb,
-	  id_i_verify_hostname;
+	  id_i_verify_hostname, id_i_keylog_cb;
 static ID id_i_io, id_i_context, id_i_hostname;
 
-static int ossl_ssl_ex_vcb_idx;
 static int ossl_ssl_ex_ptr_idx;
 static int ossl_sslctx_ex_ptr_idx;
-#if !defined(HAVE_X509_STORE_UP_REF)
-static int ossl_sslctx_ex_store_p;
-#endif
 
 static void
-ossl_sslctx_free(void *ptr)
+ossl_sslctx_mark(void *ptr)
 {
     SSL_CTX *ctx = ptr;
-#if !defined(HAVE_X509_STORE_UP_REF)
-    if (ctx && SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_store_p))
-	ctx->cert_store = NULL;
-#endif
-    SSL_CTX_free(ctx);
+    rb_gc_mark((VALUE)SSL_CTX_get_ex_data(ctx, ossl_sslctx_ex_ptr_idx));
+}
+
+static void
+ossl_sslctx_free(void *ptr)
+{
+    SSL_CTX_free(ptr);
 }
 
 static const rb_data_type_t ossl_sslctx_type = {
     "OpenSSL/SSL/CTX",
     {
-	0, ossl_sslctx_free,
+        ossl_sslctx_mark, ossl_sslctx_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static VALUE
@@ -83,7 +90,7 @@
     VALUE obj;
 
     obj = TypedData_Wrap_Struct(klass, &ossl_sslctx_type, 0);
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000 || defined(LIBRESSL_VERSION_NUMBER)
     ctx = SSL_CTX_new(TLS_method());
 #else
     ctx = SSL_CTX_new(SSLv23_method());
@@ -95,14 +102,15 @@
     RTYPEDDATA_DATA(obj) = ctx;
     SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_ptr_idx, (void *)obj);
 
-#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
+#if !defined(OPENSSL_NO_EC) && OPENSSL_VERSION_NUMBER < 0x10100000 && \
+    !defined(LIBRESSL_VERSION_NUMBER)
     /* We use SSL_CTX_set1_curves_list() to specify the curve used in ECDH. It
      * allows to specify multiple curve names and OpenSSL will select
      * automatically from them. In OpenSSL 1.0.2, the automatic selection has to
-     * be enabled explicitly. But OpenSSL 1.1.0 removed the knob and it is
-     * always enabled. To uniform the behavior, we enable the automatic
-     * selection also in 1.0.2. Users can still disable ECDH by removing ECDH
-     * cipher suites by SSLContext#ciphers=. */
+     * be enabled explicitly. OpenSSL 1.1.0 and LibreSSL 2.6.1 removed the knob
+     * and it is always enabled. To uniform the behavior, we enable the
+     * automatic selection also in 1.0.2. Users can still disable ECDH by
+     * removing ECDH cipher suites by SSLContext#ciphers=. */
     if (!SSL_CTX_set_ecdh_auto(ctx, 1))
 	ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
 #endif
@@ -184,8 +192,10 @@
 
 	for (i = 0; i < numberof(options_map); i++) {
 	    sum |= options_map[i].opts;
-	    if (min && min > options_map[i].ver || max && max < options_map[i].ver)
+            if ((min && min > options_map[i].ver) ||
+                (max && max < options_map[i].ver)) {
 		opts |= options_map[i].opts;
+            }
 	}
 	SSL_CTX_clear_options(ctx, sum);
 	SSL_CTX_set_options(ctx, opts);
@@ -229,8 +239,7 @@
     return 1;
 }
 
-#if !defined(OPENSSL_NO_DH) || \
-    !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
+#if !defined(OPENSSL_NO_DH)
 struct tmp_dh_callback_args {
     VALUE ssl_obj;
     ID id;
@@ -239,22 +248,23 @@
     int keylength;
 };
 
-static EVP_PKEY *
-ossl_call_tmp_dh_callback(struct tmp_dh_callback_args *args)
+static VALUE
+ossl_call_tmp_dh_callback(VALUE arg)
 {
+    struct tmp_dh_callback_args *args = (struct tmp_dh_callback_args *)arg;
     VALUE cb, dh;
     EVP_PKEY *pkey;
 
     cb = rb_funcall(args->ssl_obj, args->id, 0);
     if (NIL_P(cb))
-	return NULL;
+	return (VALUE)NULL;
     dh = rb_funcall(cb, id_call, 3, args->ssl_obj, INT2NUM(args->is_export),
 		    INT2NUM(args->keylength));
     pkey = GetPKeyPtr(dh);
     if (EVP_PKEY_base_id(pkey) != args->type)
-	return NULL;
+	return (VALUE)NULL;
 
-    return pkey;
+    return (VALUE)pkey;
 }
 #endif
 
@@ -274,7 +284,7 @@
     args.keylength = keylength;
     args.type = EVP_PKEY_DH;
 
-    pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback,
+    pkey = (EVP_PKEY *)rb_protect(ossl_call_tmp_dh_callback,
 				  (VALUE)&args, &state);
     if (state) {
 	rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state));
@@ -283,39 +293,10 @@
     if (!pkey)
 	return NULL;
 
-    return EVP_PKEY_get0_DH(pkey);
+    return (DH *)EVP_PKEY_get0_DH(pkey);
 }
 #endif /* OPENSSL_NO_DH */
 
-#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
-static EC_KEY *
-ossl_tmp_ecdh_callback(SSL *ssl, int is_export, int keylength)
-{
-    VALUE rb_ssl;
-    EVP_PKEY *pkey;
-    struct tmp_dh_callback_args args;
-    int state;
-
-    rb_ssl = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
-    args.ssl_obj = rb_ssl;
-    args.id = id_tmp_ecdh_callback;
-    args.is_export = is_export;
-    args.keylength = keylength;
-    args.type = EVP_PKEY_EC;
-
-    pkey = (EVP_PKEY *)rb_protect((VALUE (*)(VALUE))ossl_call_tmp_dh_callback,
-				  (VALUE)&args, &state);
-    if (state) {
-	rb_ivar_set(rb_ssl, ID_callback_state, INT2NUM(state));
-	return NULL;
-    }
-    if (!pkey)
-	return NULL;
-
-    return EVP_PKEY_get0_EC_KEY(pkey);
-}
-#endif
-
 static VALUE
 call_verify_certificate_identity(VALUE ctx_v)
 {
@@ -345,9 +326,9 @@
     int status;
 
     ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
-    cb = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_vcb_idx);
     ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
     sslctx_obj = rb_attr_get(ssl_obj, id_i_context);
+    cb = rb_attr_get(sslctx_obj, id_i_verify_callback);
     verify_hostname = rb_attr_get(sslctx_obj, id_i_verify_hostname);
 
     if (preverify_ok && RTEST(verify_hostname) && !SSL_is_server(ssl) &&
@@ -357,7 +338,14 @@
 	    rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(status));
 	    return 0;
 	}
-	preverify_ok = ret == Qtrue;
+        if (ret != Qtrue) {
+            preverify_ok = 0;
+#if defined(X509_V_ERR_HOSTNAME_MISMATCH)
+            X509_STORE_CTX_set_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH);
+#else
+            X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REJECTED);
+#endif
+        }
     }
 
     return ossl_verify_cb_call(cb, preverify_ok, ctx);
@@ -378,7 +366,7 @@
 }
 
 static SSL_SESSION *
-#if OPENSSL_VERSION_NUMBER >= 0x10100000 && !defined(LIBRESSL_VERSION_NUMBER)
+#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER >= 0x10100000
 ossl_sslctx_session_get_cb(SSL *ssl, const unsigned char *buf, int len, int *copy)
 #else
 ossl_sslctx_session_get_cb(SSL *ssl, unsigned char *buf, int len, int *copy)
@@ -455,6 +443,54 @@
     return 0;
 }
 
+#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
+ * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
+ * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+ */
+
+struct ossl_call_keylog_cb_args {
+    VALUE ssl_obj;
+    const char * line;
+};
+
+static VALUE
+ossl_call_keylog_cb(VALUE args_v)
+{
+    VALUE sslctx_obj, cb, line_v;
+    struct ossl_call_keylog_cb_args *args = (struct ossl_call_keylog_cb_args *) args_v;
+
+    sslctx_obj = rb_attr_get(args->ssl_obj, id_i_context);
+
+    cb = rb_attr_get(sslctx_obj, id_i_keylog_cb);
+    if (NIL_P(cb)) return Qnil;
+
+    line_v = rb_str_new_cstr(args->line);
+
+    return rb_funcall(cb, id_call, 2, args->ssl_obj, line_v);
+}
+
+static void
+ossl_sslctx_keylog_cb(const SSL *ssl, const char *line)
+{
+    VALUE ssl_obj;
+    struct ossl_call_keylog_cb_args args;
+    int state = 0;
+
+    OSSL_Debug("SSL keylog callback entered");
+
+    ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+    args.ssl_obj = ssl_obj;
+    args.line = line;
+
+    rb_protect(ossl_call_keylog_cb, (VALUE)&args, &state);
+    if (state) {
+        rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
+    }
+}
+#endif
+
 static VALUE
 ossl_call_session_remove_cb(VALUE ary)
 {
@@ -521,52 +557,42 @@
 static VALUE ossl_sslctx_setup(VALUE self);
 
 static VALUE
-ossl_call_servername_cb(VALUE ary)
+ossl_call_servername_cb(VALUE arg)
 {
-    VALUE ssl_obj, sslctx_obj, cb, ret_obj;
-
-    Check_Type(ary, T_ARRAY);
-    ssl_obj = rb_ary_entry(ary, 0);
+    SSL *ssl = (void *)arg;
+    const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+    if (!servername)
+        return Qnil;
 
-    sslctx_obj = rb_attr_get(ssl_obj, id_i_context);
-    cb = rb_attr_get(sslctx_obj, id_i_servername_cb);
-    if (NIL_P(cb)) return Qnil;
+    VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
+    VALUE sslctx_obj = rb_attr_get(ssl_obj, id_i_context);
+    VALUE cb = rb_attr_get(sslctx_obj, id_i_servername_cb);
+    VALUE ary = rb_assoc_new(ssl_obj, rb_str_new_cstr(servername));
 
-    ret_obj = rb_funcallv(cb, id_call, 1, &ary);
+    VALUE ret_obj = rb_funcallv(cb, id_call, 1, &ary);
     if (rb_obj_is_kind_of(ret_obj, cSSLContext)) {
-        SSL *ssl;
         SSL_CTX *ctx2;
-
         ossl_sslctx_setup(ret_obj);
-        GetSSL(ssl_obj, ssl);
         GetSSLCTX(ret_obj, ctx2);
-        SSL_set_SSL_CTX(ssl, ctx2);
+        if (!SSL_set_SSL_CTX(ssl, ctx2))
+            ossl_raise(eSSLError, "SSL_set_SSL_CTX");
         rb_ivar_set(ssl_obj, id_i_context, ret_obj);
     } else if (!NIL_P(ret_obj)) {
 	ossl_raise(rb_eArgError, "servername_cb must return an "
 		   "OpenSSL::SSL::SSLContext object or nil");
     }
 
-    return ret_obj;
+    return Qnil;
 }
 
 static int
 ssl_servername_cb(SSL *ssl, int *ad, void *arg)
 {
-    VALUE ary, ssl_obj;
-    int state = 0;
-    const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
-
-    if (!servername)
-        return SSL_TLSEXT_ERR_OK;
-
-    ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
-    ary = rb_ary_new2(2);
-    rb_ary_push(ary, ssl_obj);
-    rb_ary_push(ary, rb_str_new2(servername));
+    int state;
 
-    rb_protect(ossl_call_servername_cb, ary, &state);
+    rb_protect(ossl_call_servername_cb, (VALUE)ssl, &state);
     if (state) {
+        VALUE ssl_obj = (VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx);
         rb_ivar_set(ssl_obj, ID_callback_state, INT2NUM(state));
         return SSL_TLSEXT_ERR_ALERT_FATAL;
     }
@@ -587,10 +613,8 @@
     rb_funcallv(cb, id_call, 1, &ssl_obj);
 }
 
-#if !defined(OPENSSL_NO_NEXTPROTONEG) || \
-    defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
 static VALUE
-ssl_npn_encode_protocol_i(VALUE cur, VALUE encoded)
+ssl_npn_encode_protocol_i(RB_BLOCK_CALL_FUNC_ARGLIST(cur, encoded))
 {
     int len = RSTRING_LENINT(cur);
     char len_byte;
@@ -607,7 +631,7 @@
 ssl_encode_npn_protocols(VALUE protocols)
 {
     VALUE encoded = rb_str_new(NULL, 0);
-    rb_iterate(rb_each, protocols, ssl_npn_encode_protocol_i, encoded);
+    rb_block_call(protocols, id_each, 0, 0, ssl_npn_encode_protocol_i, encoded);
     return encoded;
 }
 
@@ -670,14 +694,13 @@
 
     return SSL_TLSEXT_ERR_OK;
 }
-#endif
 
-#ifndef OPENSSL_NO_NEXTPROTONEG
+#ifdef OSSL_USE_NEXTPROTONEG
 static int
 ssl_npn_advertise_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen,
 		     void *arg)
 {
-    VALUE protocols = (VALUE)arg;
+    VALUE protocols = rb_attr_get((VALUE)arg, id_npn_protocols_encoded);
 
     *out = (const unsigned char *) RSTRING_PTR(protocols);
     *outlen = RSTRING_LENINT(protocols);
@@ -699,7 +722,6 @@
 }
 #endif
 
-#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
 static int
 ssl_alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
 		   const unsigned char *in, unsigned int inlen, void *arg)
@@ -711,7 +733,6 @@
 
     return ssl_npn_select_cb_common(ssl, cb, out, outlen, in, inlen);
 }
-#endif
 
 /* This function may serve as the entry point to support further callbacks. */
 static void
@@ -725,7 +746,10 @@
 }
 
 /*
- * Gets various OpenSSL options.
+ * call-seq:
+ *    ctx.options -> integer
+ *
+ * Gets various \OpenSSL options.
  */
 static VALUE
 ossl_sslctx_get_options(VALUE self)
@@ -740,7 +764,17 @@
 }
 
 /*
- * Sets various OpenSSL options.
+ * call-seq:
+ *    ctx.options = integer
+ *
+ * Sets various \OpenSSL options. The options are a bit field and can be
+ * combined with the bitwise OR operator (<tt>|</tt>). Available options are
+ * defined as constants in OpenSSL::SSL that begin with +OP_+.
+ *
+ * For backwards compatibility, passing +nil+ has the same effect as passing
+ * OpenSSL::SSL::OP_ALL.
+ *
+ * See also man page SSL_CTX_set_options(3).
  */
 static VALUE
 ossl_sslctx_set_options(VALUE self, VALUE options)
@@ -788,41 +822,15 @@
     SSL_CTX_set_tmp_dh_callback(ctx, ossl_tmp_dh_callback);
 #endif
 
-#if !defined(OPENSSL_NO_EC)
-    /* We added SSLContext#tmp_ecdh_callback= in Ruby 2.3.0,
-     * but SSL_CTX_set_tmp_ecdh_callback() was removed in OpenSSL 1.1.0. */
-    if (RTEST(rb_attr_get(self, id_i_tmp_ecdh_callback))) {
-# if defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
-	rb_warn("#tmp_ecdh_callback= is deprecated; use #ecdh_curves= instead");
-	SSL_CTX_set_tmp_ecdh_callback(ctx, ossl_tmp_ecdh_callback);
-#  if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
-	/* tmp_ecdh_callback and ecdh_auto conflict; OpenSSL ignores
-	 * tmp_ecdh_callback. So disable ecdh_auto. */
-	if (!SSL_CTX_set_ecdh_auto(ctx, 0))
-	    ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
-#  endif
-# else
-	ossl_raise(eSSLError, "OpenSSL does not support tmp_ecdh_callback; "
-		   "use #ecdh_curves= instead");
-# endif
-    }
-#endif /* OPENSSL_NO_EC */
+#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
+    SSL_CTX_set_post_handshake_auth(ctx, 1);
+#endif
 
     val = rb_attr_get(self, id_i_cert_store);
     if (!NIL_P(val)) {
 	X509_STORE *store = GetX509StorePtr(val); /* NO NEED TO DUP */
 	SSL_CTX_set_cert_store(ctx, store);
-#if !defined(HAVE_X509_STORE_UP_REF)
-	/*
-         * WORKAROUND:
-	 *   X509_STORE can count references, but
-	 *   X509_STORE_free() doesn't care it.
-	 *   So we won't increment it but mark it by ex_data.
-	 */
-        SSL_CTX_set_ex_data(ctx, ossl_sslctx_ex_store_p, ctx);
-#else /* Fixed in OpenSSL 1.0.2; bff9ce4db38b (master), 5b4b9ce976fc (1.0.2) */
 	X509_STORE_up_ref(store);
-#endif
     }
 
     val = rb_attr_get(self, id_i_extra_chain_cert);
@@ -873,10 +881,17 @@
     ca_file = NIL_P(val) ? NULL : StringValueCStr(val);
     val = rb_attr_get(self, id_i_ca_path);
     ca_path = NIL_P(val) ? NULL : StringValueCStr(val);
-    if(ca_file || ca_path){
-	if (!SSL_CTX_load_verify_locations(ctx, ca_file, ca_path))
-	    rb_warning("can't set verify locations");
+#ifdef HAVE_SSL_CTX_LOAD_VERIFY_FILE
+    if (ca_file && !SSL_CTX_load_verify_file(ctx, ca_file))
+        ossl_raise(eSSLError, "SSL_CTX_load_verify_file");
+    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))
+            ossl_raise(eSSLError, "SSL_CTX_load_verify_locations");
     }
+#endif
 
     val = rb_attr_get(self, id_i_verify_mode);
     verify_mode = NIL_P(val) ? SSL_VERIFY_NONE : NUM2INT(val);
@@ -890,12 +905,12 @@
     val = rb_attr_get(self, id_i_verify_depth);
     if(!NIL_P(val)) SSL_CTX_set_verify_depth(ctx, NUM2INT(val));
 
-#ifndef OPENSSL_NO_NEXTPROTONEG
+#ifdef OSSL_USE_NEXTPROTONEG
     val = rb_attr_get(self, id_i_npn_protocols);
     if (!NIL_P(val)) {
 	VALUE encoded = ssl_encode_npn_protocols(val);
 	rb_ivar_set(self, id_npn_protocols_encoded, encoded);
-	SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)encoded);
+	SSL_CTX_set_next_protos_advertised_cb(ctx, ssl_npn_advertise_cb, (void *)self);
 	OSSL_Debug("SSL NPN advertise callback added");
     }
     if (RTEST(rb_attr_get(self, id_i_npn_select_cb))) {
@@ -904,7 +919,6 @@
     }
 #endif
 
-#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
     val = rb_attr_get(self, id_i_alpn_protocols);
     if (!NIL_P(val)) {
 	VALUE rprotos = ssl_encode_npn_protocols(val);
@@ -919,7 +933,6 @@
 	SSL_CTX_set_alpn_select_cb(ctx, ssl_alpn_select_cb, (void *) self);
 	OSSL_Debug("SSL ALPN select callback added");
     }
-#endif
 
     rb_obj_freeze(self);
 
@@ -951,6 +964,18 @@
 	OSSL_Debug("SSL TLSEXT servername callback added");
     }
 
+#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(LIBRESSL_VERSION_NUMBER)
+    /*
+     * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
+     * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
+     * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+     */
+    if (RTEST(rb_attr_get(self, id_i_keylog_cb))) {
+        SSL_CTX_set_keylog_callback(ctx, ossl_sslctx_keylog_cb);
+        OSSL_Debug("SSL keylog callback added");
+    }
+#endif
+
     return Qtrue;
 }
 
@@ -999,27 +1024,13 @@
     return ary;
 }
 
-/*
- * call-seq:
- *    ctx.ciphers = "cipher1:cipher2:..."
- *    ctx.ciphers = [name, ...]
- *    ctx.ciphers = [[name, version, bits, alg_bits], ...]
- *
- * Sets the list of available cipher suites for this context.  Note in a server
- * context some ciphers require the appropriate certificates.  For example, an
- * RSA cipher suite can only be chosen when an RSA certificate is available.
- */
 static VALUE
-ossl_sslctx_set_ciphers(VALUE self, VALUE v)
+build_cipher_string(VALUE v)
 {
-    SSL_CTX *ctx;
     VALUE str, elem;
     int i;
 
-    rb_check_frozen(self);
-    if (NIL_P(v))
-	return v;
-    else if (RB_TYPE_P(v, T_ARRAY)) {
+    if (RB_TYPE_P(v, T_ARRAY)) {
         str = rb_str_new(0, 0);
         for (i = 0; i < RARRAY_LEN(v); i++) {
             elem = rb_ary_entry(v, i);
@@ -1033,14 +1044,113 @@
         StringValue(str);
     }
 
+    return str;
+}
+
+/*
+ * call-seq:
+ *    ctx.ciphers = "cipher1:cipher2:..."
+ *    ctx.ciphers = [name, ...]
+ *    ctx.ciphers = [[name, version, bits, alg_bits], ...]
+ *
+ * Sets the list of available cipher suites for this context.  Note in a server
+ * context some ciphers require the appropriate certificates.  For example, an
+ * RSA cipher suite can only be chosen when an RSA certificate is available.
+ */
+static VALUE
+ossl_sslctx_set_ciphers(VALUE self, VALUE v)
+{
+    SSL_CTX *ctx;
+    VALUE str;
+
+    rb_check_frozen(self);
+    if (NIL_P(v))
+        return v;
+
+    str = build_cipher_string(v);
+
     GetSSLCTX(self, ctx);
-    if (!SSL_CTX_set_cipher_list(ctx, StringValueCStr(str))) {
+    if (!SSL_CTX_set_cipher_list(ctx, StringValueCStr(str)))
         ossl_raise(eSSLError, "SSL_CTX_set_cipher_list");
-    }
 
     return v;
 }
 
+#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
+/*
+ * call-seq:
+ *    ctx.ciphersuites = "cipher1:cipher2:..."
+ *    ctx.ciphersuites = [name, ...]
+ *    ctx.ciphersuites = [[name, version, bits, alg_bits], ...]
+ *
+ * Sets the list of available TLSv1.3 cipher suites for this context.
+ */
+static VALUE
+ossl_sslctx_set_ciphersuites(VALUE self, VALUE v)
+{
+    SSL_CTX *ctx;
+    VALUE str;
+
+    rb_check_frozen(self);
+    if (NIL_P(v))
+        return v;
+
+    str = build_cipher_string(v);
+
+    GetSSLCTX(self, ctx);
+    if (!SSL_CTX_set_ciphersuites(ctx, StringValueCStr(str)))
+        ossl_raise(eSSLError, "SSL_CTX_set_ciphersuites");
+
+    return v;
+}
+#endif
+
+#ifndef OPENSSL_NO_DH
+/*
+ * call-seq:
+ *    ctx.tmp_dh = pkey
+ *
+ * Sets DH parameters used for ephemeral DH key exchange. This is relevant for
+ * servers only.
+ *
+ * +pkey+ is an instance of OpenSSL::PKey::DH. Note that key components
+ * contained in the key object, if any, are ignored. The server will always
+ * generate a new key pair for each handshake.
+ *
+ * Added in version 3.0. See also the man page SSL_set0_tmp_dh_pkey(3).
+ *
+ * Example:
+ *   ctx = OpenSSL::SSL::SSLContext.new
+ *   ctx.tmp_dh = OpenSSL::DH.generate(2048)
+ *   svr = OpenSSL::SSL::SSLServer.new(tcp_svr, ctx)
+ *   Thread.new { svr.accept }
+ */
+static VALUE
+ossl_sslctx_set_tmp_dh(VALUE self, VALUE arg)
+{
+    SSL_CTX *ctx;
+    EVP_PKEY *pkey;
+
+    rb_check_frozen(self);
+    GetSSLCTX(self, ctx);
+    pkey = GetPKeyPtr(arg);
+
+    if (EVP_PKEY_base_id(pkey) != EVP_PKEY_DH)
+        rb_raise(eSSLError, "invalid pkey type %s (expected DH)",
+                 OBJ_nid2sn(EVP_PKEY_base_id(pkey)));
+#ifdef HAVE_SSL_SET0_TMP_DH_PKEY
+    if (!SSL_CTX_set0_tmp_dh_pkey(ctx, pkey))
+        ossl_raise(eSSLError, "SSL_CTX_set0_tmp_dh_pkey");
+    EVP_PKEY_up_ref(pkey);
+#else
+    if (!SSL_CTX_set_tmp_dh(ctx, EVP_PKEY_get0_DH(pkey)))
+        ossl_raise(eSSLError, "SSL_CTX_set_tmp_dh");
+#endif
+
+    return arg;
+}
+#endif
+
 #if !defined(OPENSSL_NO_EC)
 /*
  * call-seq:
@@ -1052,9 +1162,6 @@
  * Extension. For a server, the list is used by OpenSSL to determine the set of
  * shared curves. OpenSSL will pick the most appropriate one from it.
  *
- * Note that this works differently with old OpenSSL (<= 1.0.1). Only one curve
- * can be set, and this has no effect for TLS clients.
- *
  * === Example
  *   ctx1 = OpenSSL::SSL::SSLContext.new
  *   ctx1.ecdh_curves = "X25519:P-256:P-224"
@@ -1078,48 +1185,8 @@
     GetSSLCTX(self, ctx);
     StringValueCStr(arg);
 
-#if defined(HAVE_SSL_CTX_SET1_CURVES_LIST)
     if (!SSL_CTX_set1_curves_list(ctx, RSTRING_PTR(arg)))
 	ossl_raise(eSSLError, NULL);
-#else
-    /* OpenSSL does not have SSL_CTX_set1_curves_list()... Fallback to
-     * SSL_CTX_set_tmp_ecdh(). So only the first curve is used. */
-    {
-	VALUE curve, splitted;
-	EC_KEY *ec;
-	int nid;
-
-	splitted = rb_str_split(arg, ":");
-	if (!RARRAY_LEN(splitted))
-	    ossl_raise(eSSLError, "invalid input format");
-	curve = RARRAY_AREF(splitted, 0);
-	StringValueCStr(curve);
-
-	/* SSL_CTX_set1_curves_list() accepts NIST names */
-	nid = EC_curve_nist2nid(RSTRING_PTR(curve));
-	if (nid == NID_undef)
-	    nid = OBJ_txt2nid(RSTRING_PTR(curve));
-	if (nid == NID_undef)
-	    ossl_raise(eSSLError, "unknown curve name");
-
-	ec = EC_KEY_new_by_curve_name(nid);
-	if (!ec)
-	    ossl_raise(eSSLError, NULL);
-	EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE);
-	if (!SSL_CTX_set_tmp_ecdh(ctx, ec)) {
-	    EC_KEY_free(ec);
-	    ossl_raise(eSSLError, "SSL_CTX_set_tmp_ecdh");
-	}
-	EC_KEY_free(ec);
-# if defined(HAVE_SSL_CTX_SET_ECDH_AUTO)
-	/* tmp_ecdh and ecdh_auto conflict. tmp_ecdh is ignored when ecdh_auto
-	 * is enabled. So disable ecdh_auto. */
-	if (!SSL_CTX_set_ecdh_auto(ctx, 0))
-	    ossl_raise(eSSLError, "SSL_CTX_set_ecdh_auto");
-# endif
-    }
-#endif
-
     return arg;
 }
 #else
@@ -1210,7 +1277,7 @@
 
 /*
  * call-seq:
- *    ctx.add_certificate(certiticate, pkey [, extra_certs]) -> self
+ *    ctx.add_certificate(certificate, pkey [, extra_certs]) -> self
  *
  * Adds a certificate to the context. _pkey_ must be a corresponding private
  * key with _certificate_.
@@ -1242,10 +1309,6 @@
  *   ecdsa_pkey = ...
  *   another_ca_cert = ...
  *   ctx.add_certificate(ecdsa_cert, ecdsa_pkey, [another_ca_cert])
- *
- * === Note
- * OpenSSL before the version 1.0.2 could handle only one extra chain across
- * all key types. Calling this method discards the chain set previously.
  */
 static VALUE
 ossl_sslctx_add_certificate(int argc, VALUE *argv, VALUE self)
@@ -1270,7 +1333,7 @@
     EVP_PKEY_free(pub_pkey);
     if (!pub_pkey)
 	rb_raise(rb_eArgError, "certificate does not contain public key");
-    if (EVP_PKEY_cmp(pub_pkey, pkey) != 1)
+    if (EVP_PKEY_eq(pub_pkey, pkey) != 1)
 	rb_raise(rb_eArgError, "public key mismatch");
 
     if (argc >= 3)
@@ -1284,34 +1347,9 @@
 	sk_X509_pop_free(extra_chain, X509_free);
 	ossl_raise(eSSLError, "SSL_CTX_use_PrivateKey");
     }
-
-    if (extra_chain) {
-#if OPENSSL_VERSION_NUMBER >= 0x10002000 && !defined(LIBRESSL_VERSION_NUMBER)
-	if (!SSL_CTX_set0_chain(ctx, extra_chain)) {
-	    sk_X509_pop_free(extra_chain, X509_free);
-	    ossl_raise(eSSLError, "SSL_CTX_set0_chain");
-	}
-#else
-	STACK_OF(X509) *orig_extra_chain;
-	X509 *x509_tmp;
-
-	/* First, clear the existing chain */
-	SSL_CTX_get_extra_chain_certs(ctx, &orig_extra_chain);
-	if (orig_extra_chain && sk_X509_num(orig_extra_chain)) {
-	    rb_warning("SSL_CTX_set0_chain() is not available; " \
-		       "clearing previously set certificate chain");
-	    SSL_CTX_clear_extra_chain_certs(ctx);
-	}
-	while ((x509_tmp = sk_X509_shift(extra_chain))) {
-	    /* Transfers ownership */
-	    if (!SSL_CTX_add_extra_chain_cert(ctx, x509_tmp)) {
-		X509_free(x509_tmp);
-		sk_X509_pop_free(extra_chain, X509_free);
-		ossl_raise(eSSLError, "SSL_CTX_add_extra_chain_cert");
-	    }
-	}
-	sk_X509_free(extra_chain);
-#endif
+    if (extra_chain && !SSL_CTX_set0_chain(ctx, extra_chain)) {
+        sk_X509_pop_free(extra_chain, X509_free);
+        ossl_raise(eSSLError, "SSL_CTX_set0_chain");
     }
     return self;
 }
@@ -1505,12 +1543,18 @@
 /*
  * SSLSocket class
  */
-#ifndef OPENSSL_NO_SOCK
 static inline int
 ssl_started(SSL *ssl)
 {
-    /* the FD is set in ossl_ssl_setup(), called by #connect or #accept */
-    return SSL_get_fd(ssl) >= 0;
+    /* BIO is created through ossl_ssl_setup(), called by #connect or #accept */
+    return SSL_get_rbio(ssl) != NULL;
+}
+
+static void
+ossl_ssl_mark(void *ptr)
+{
+    SSL *ssl = ptr;
+    rb_gc_mark((VALUE)SSL_get_ex_data(ssl, ossl_ssl_ex_ptr_idx));
 }
 
 static void
@@ -1522,9 +1566,9 @@
 const rb_data_type_t ossl_ssl_type = {
     "OpenSSL/SSL",
     {
-	0, ossl_ssl_free,
+        ossl_ssl_mark, ossl_ssl_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 static VALUE
@@ -1533,6 +1577,29 @@
     return TypedData_Wrap_Struct(klass, &ossl_ssl_type, NULL);
 }
 
+static VALUE
+peer_ip_address(VALUE self)
+{
+    VALUE remote_address = rb_funcall(rb_attr_get(self, id_i_io), rb_intern("remote_address"), 0);
+
+    return rb_funcall(remote_address, rb_intern("inspect_sockaddr"), 0);
+}
+
+static VALUE
+fallback_peer_ip_address(VALUE self, VALUE args)
+{
+    return rb_str_new_cstr("(null)");
+}
+
+static VALUE
+peeraddr_ip_str(VALUE self)
+{
+    VALUE rb_mErrno = rb_const_get(rb_cObject, rb_intern("Errno"));
+    VALUE rb_eSystemCallError = rb_const_get(rb_mErrno, rb_intern("SystemCallError"));
+
+    return rb_rescue2(peer_ip_address, self, fallback_peer_ip_address, (VALUE)0, rb_eSystemCallError, NULL);
+}
+
 /*
  * call-seq:
  *    SSLSocket.new(io) => aSSLSocket
@@ -1552,7 +1619,7 @@
 static VALUE
 ossl_ssl_initialize(int argc, VALUE *argv, VALUE self)
 {
-    VALUE io, v_ctx, verify_cb;
+    VALUE io, v_ctx;
     SSL *ssl;
     SSL_CTX *ctx;
 
@@ -1569,6 +1636,7 @@
 
     if (rb_respond_to(io, rb_intern("nonblock=")))
 	rb_funcall(io, rb_intern("nonblock="), 1, Qtrue);
+    Check_Type(io, T_FILE);
     rb_ivar_set(self, id_i_io, io);
 
     ssl = SSL_new(ctx);
@@ -1578,14 +1646,23 @@
 
     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);
-    SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)verify_cb);
 
     rb_call_super(0, NULL);
 
     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)
 {
@@ -1601,8 +1678,8 @@
     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;
 }
@@ -1636,70 +1713,118 @@
     return 0;
 }
 
+// Provided by Ruby 3.2.0 and later in order to support the default IO#timeout.
+#ifndef RUBY_IO_TIMEOUT_DEFAULT
+#define RUBY_IO_TIMEOUT_DEFAULT Qnil
+#endif
+
+#ifdef HAVE_RB_IO_TIMEOUT
+#define IO_TIMEOUT_ERROR rb_eIOTimeoutError
+#else
+#define IO_TIMEOUT_ERROR rb_eIOError
+#endif
+
+
+static void
+io_wait_writable(VALUE io)
+{
+#ifdef HAVE_RB_IO_MAYBE_WAIT
+    if (!rb_io_maybe_wait_writable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) {
+        rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become writable!");
+    }
+#else
+    rb_io_t *fptr;
+    GetOpenFile(io, fptr);
+    rb_io_wait_writable(fptr->fd);
+#endif
+}
+
+static void
+io_wait_readable(VALUE io)
+{
+#ifdef HAVE_RB_IO_MAYBE_WAIT
+    if (!rb_io_maybe_wait_readable(errno, io, RUBY_IO_TIMEOUT_DEFAULT)) {
+        rb_raise(IO_TIMEOUT_ERROR, "Timed out while waiting to become readable!");
+    }
+#else
+    rb_io_t *fptr;
+    GetOpenFile(io, fptr);
+    rb_io_wait_readable(fptr->fd);
+#endif
+}
+
 static VALUE
-ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts)
+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);
-            rb_io_wait_writable(fptr->fd);
+            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);
-            rb_io_wait_readable(fptr->fd);
+            io_wait_readable(io);
             continue;
-	case SSL_ERROR_SYSCALL:
-	    if (errno) rb_sys_fail(funcname);
-	    ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
-#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 state=%s: %s (%s)",
-			   funcname, ret2, errno, SSL_state_string_long(ssl),
-			   err_msg, verify_msg);
-	    }
+          case SSL_ERROR_SYSCALL:
+#ifdef __APPLE__
+            /* See ossl_ssl_write_internal() */
+            if (errno == EPROTOTYPE)
+                continue;
 #endif
-	default:
-	    ossl_raise(eSSLError, "%s returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl));
-	}
+            if (errno) rb_sys_fail(funcname);
+            /* fallthrough */
+          default: {
+              VALUE error_append = Qnil;
+#if defined(SSL_R_CERTIFICATE_VERIFY_FAILED)
+              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
+              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;
@@ -1709,8 +1834,7 @@
  * call-seq:
  *    ssl.connect => self
  *
- * Initiates an SSL/TLS handshake with a server.  The handshake may be started
- * after unencrypted data has been sent over the socket.
+ * Initiates an SSL/TLS handshake with a server.
  */
 static VALUE
 ossl_ssl_connect(VALUE self)
@@ -1757,8 +1881,7 @@
  * call-seq:
  *    ssl.accept => self
  *
- * Waits for a SSL/TLS client to initiate a handshake.  The handshake may be
- * started after unencrypted data has been sent over the socket.
+ * Waits for a SSL/TLS client to initiate a handshake.
  */
 static VALUE
 ossl_ssl_accept(VALUE self)
@@ -1805,16 +1928,18 @@
 ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
 {
     SSL *ssl;
-    int ilen, nread = 0;
-    VALUE len, str;
-    rb_io_t *fptr;
-    VALUE io, opts = Qnil;
+    int ilen;
+    VALUE len, str, cb_state;
+    VALUE opts = Qnil;
 
     if (nonblock) {
 	rb_scan_args(argc, argv, "11:", &len, &str, &opts);
     } else {
 	rb_scan_args(argc, argv, "11", &len, &str);
     }
+    GetSSL(self, ssl);
+    if (!ssl_started(ssl))
+        rb_raise(eSSLError, "SSL session is not started yet");
 
     ilen = NUM2INT(len);
     if (NIL_P(str))
@@ -1826,67 +1951,73 @@
 	else
 	    rb_str_modify_expand(str, ilen - RSTRING_LEN(str));
     }
-    OBJ_TAINT(str);
-    rb_str_set_len(str, 0);
-    if (ilen == 0)
-	return str;
 
-    GetSSL(self, ssl);
-    io = rb_attr_get(self, id_i_io);
-    GetOpenFile(io, fptr);
-    if (ssl_started(ssl)) {
-	for (;;){
-	    nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
-	    switch(ssl_get_error(ssl, nread)){
-	    case SSL_ERROR_NONE:
-		goto end;
-	    case SSL_ERROR_ZERO_RETURN:
-		if (no_exception_p(opts)) { return Qnil; }
-		rb_eof_error();
-	    case SSL_ERROR_WANT_WRITE:
-		if (no_exception_p(opts)) { return sym_wait_writable; }
+    if (ilen == 0) {
+        rb_str_set_len(str, 0);
+        return str;
+    }
+
+    VALUE io = rb_attr_get(self, id_i_io);
+
+    rb_str_locktmp(str);
+    for (;;) {
+        int nread = SSL_read(ssl, RSTRING_PTR(str), ilen);
+
+        cb_state = rb_attr_get(self, ID_callback_state);
+        if (!NIL_P(cb_state)) {
+            rb_ivar_set(self, ID_callback_state, Qnil);
+            ossl_clear_error();
+            rb_jump_tag(NUM2INT(cb_state));
+        }
+
+        switch (ssl_get_error(ssl, nread)) {
+          case SSL_ERROR_NONE:
+            rb_str_unlocktmp(str);
+            rb_str_set_len(str, nread);
+            return str;
+          case SSL_ERROR_ZERO_RETURN:
+            rb_str_unlocktmp(str);
+            if (no_exception_p(opts)) { return Qnil; }
+            rb_eof_error();
+          case SSL_ERROR_WANT_WRITE:
+            if (nonblock) {
+                rb_str_unlocktmp(str);
+                if (no_exception_p(opts)) { return sym_wait_writable; }
                 write_would_block(nonblock);
-                rb_io_wait_writable(fptr->fd);
-                continue;
-	    case SSL_ERROR_WANT_READ:
-		if (no_exception_p(opts)) { return sym_wait_readable; }
+            }
+            io_wait_writable(io);
+            continue;
+          case SSL_ERROR_WANT_READ:
+            if (nonblock) {
+                rb_str_unlocktmp(str);
+                if (no_exception_p(opts)) { return sym_wait_readable; }
                 read_would_block(nonblock);
-                rb_io_wait_readable(fptr->fd);
-		continue;
-	    case SSL_ERROR_SYSCALL:
-		if (!ERR_peek_error()) {
-		    if (errno)
-			rb_sys_fail(0);
-		    else {
-			/*
-			 * The underlying BIO returned 0. This is actually a
-			 * protocol error. But unfortunately, not all
-			 * implementations cleanly shutdown the TLS connection
-			 * but just shutdown/close the TCP connection. So report
-			 * EOF for now...
-			 */
-			if (no_exception_p(opts)) { return Qnil; }
-			rb_eof_error();
-		    }
-		}
-	    default:
-		ossl_raise(eSSLError, "SSL_read");
-	    }
+            }
+            io_wait_readable(io);
+            continue;
+          case SSL_ERROR_SYSCALL:
+            if (!ERR_peek_error()) {
+                rb_str_unlocktmp(str);
+                if (errno)
+                    rb_sys_fail(0);
+                else {
+                    /*
+                     * The underlying BIO returned 0. This is actually a
+                     * protocol error. But unfortunately, not all
+                     * implementations cleanly shutdown the TLS connection
+                     * but just shutdown/close the TCP connection. So report
+                     * EOF for now...
+                     */
+                    if (no_exception_p(opts)) { return Qnil; }
+                    rb_eof_error();
+                }
+            }
+            /* fall through */
+          default:
+            rb_str_unlocktmp(str);
+            ossl_raise(eSSLError, "SSL_read");
         }
     }
-    else {
-	ID meth = nonblock ? rb_intern("read_nonblock") : rb_intern("sysread");
-
-	rb_warning("SSL session is not started yet.");
-	if (nonblock)
-	    return rb_funcall(io, meth, 3, len, str, opts);
-	else
-	    return rb_funcall(io, meth, 2, len, str);
-    }
-
-  end:
-    rb_str_set_len(str, nread);
-    return str;
 }
 
 /*
@@ -1926,57 +2057,63 @@
 ossl_ssl_write_internal(VALUE self, VALUE str, VALUE opts)
 {
     SSL *ssl;
-    int nwrite = 0;
     rb_io_t *fptr;
-    int nonblock = opts != Qfalse;
-    VALUE io;
+    int num, nonblock = opts != Qfalse;
+    VALUE tmp, cb_state;
 
-    StringValue(str);
     GetSSL(self, ssl);
-    io = rb_attr_get(self, id_i_io);
+    if (!ssl_started(ssl))
+        rb_raise(eSSLError, "SSL session is not started yet");
+
+    tmp = rb_str_new_frozen(StringValue(str));
+    VALUE io = rb_attr_get(self, id_i_io);
     GetOpenFile(io, fptr);
-    if (ssl_started(ssl)) {
-	for (;;){
-	    int num = RSTRING_LENINT(str);
-
-	    /* SSL_write(3ssl) manpage states num == 0 is undefined */
-	    if (num == 0)
-		goto end;
-
-	    nwrite = SSL_write(ssl, RSTRING_PTR(str), num);
-	    switch(ssl_get_error(ssl, nwrite)){
-	    case SSL_ERROR_NONE:
-		goto end;
-	    case SSL_ERROR_WANT_WRITE:
-		if (no_exception_p(opts)) { return sym_wait_writable; }
-                write_would_block(nonblock);
-                rb_io_wait_writable(fptr->fd);
-                continue;
-	    case SSL_ERROR_WANT_READ:
-		if (no_exception_p(opts)) { return sym_wait_readable; }
-                read_would_block(nonblock);
-                rb_io_wait_readable(fptr->fd);
-                continue;
-	    case SSL_ERROR_SYSCALL:
-		if (errno) rb_sys_fail(0);
-	    default:
-		ossl_raise(eSSLError, "SSL_write");
-	    }
+
+    /* SSL_write(3ssl) manpage states num == 0 is undefined */
+    num = RSTRING_LENINT(tmp);
+    if (num == 0)
+        return INT2FIX(0);
+
+    for (;;) {
+        int nwritten = SSL_write(ssl, RSTRING_PTR(tmp), num);
+
+        cb_state = rb_attr_get(self, ID_callback_state);
+        if (!NIL_P(cb_state)) {
+            rb_ivar_set(self, ID_callback_state, Qnil);
+            ossl_clear_error();
+            rb_jump_tag(NUM2INT(cb_state));
         }
-    }
-    else {
-	ID meth = nonblock ?
-	    rb_intern("write_nonblock") : rb_intern("syswrite");
 
-	rb_warning("SSL session is not started yet.");
-	if (nonblock)
-	    return rb_funcall(io, meth, 2, str, opts);
-	else
-	    return rb_funcall(io, meth, 1, str);
+        switch (ssl_get_error(ssl, nwritten)) {
+          case SSL_ERROR_NONE:
+            return INT2NUM(nwritten);
+          case SSL_ERROR_WANT_WRITE:
+            if (no_exception_p(opts)) { return sym_wait_writable; }
+            write_would_block(nonblock);
+            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(io);
+            continue;
+          case SSL_ERROR_SYSCALL:
+#ifdef __APPLE__
+            /*
+             * It appears that send syscall can return EPROTOTYPE if the
+             * socket is being torn down. Retry to get a proper errno to
+             * make the error handling in line with the socket library.
+             * [Bug #14713] https://bugs.ruby-lang.org/issues/14713
+             */
+            if (errno == EPROTOTYPE)
+                continue;
+#endif
+            if (errno) rb_sys_fail(0);
+            /* fallthrough */
+          default:
+            ossl_raise(eSSLError, "SSL_write");
+        }
     }
-
-  end:
-    return INT2NUM(nwrite);
 }
 
 /*
@@ -2275,7 +2412,57 @@
 
     GetSSL(self, ssl);
 
-    return INT2NUM(SSL_get_verify_result(ssl));
+    return LONG2NUM(SSL_get_verify_result(ssl));
+}
+
+/*
+ * call-seq:
+ *    ssl.finished_message => "finished message"
+ *
+ * Returns the last *Finished* message sent
+ *
+ */
+static VALUE
+ossl_ssl_get_finished(VALUE self)
+{
+    SSL *ssl;
+    char sizer[1], *buf;
+    size_t len;
+
+    GetSSL(self, ssl);
+
+    len = SSL_get_finished(ssl, sizer, 0);
+    if (len == 0)
+        return Qnil;
+
+    buf = ALLOCA_N(char, len);
+    SSL_get_finished(ssl, buf, len);
+    return rb_str_new(buf, len);
+}
+
+/*
+ * call-seq:
+ *    ssl.peer_finished_message => "peer finished message"
+ *
+ * Returns the last *Finished* message received
+ *
+ */
+static VALUE
+ossl_ssl_get_peer_finished(VALUE self)
+{
+    SSL *ssl;
+    char sizer[1], *buf;
+    size_t len;
+
+    GetSSL(self, ssl);
+
+    len = SSL_get_peer_finished(ssl, sizer, 0);
+    if (len == 0)
+        return Qnil;
+
+    buf = ALLOCA_N(char, len);
+    SSL_get_peer_finished(ssl, buf, len);
+    return rb_str_new(buf, len);
 }
 
 /*
@@ -2301,7 +2488,7 @@
     return ossl_x509name_sk2ary(ca);
 }
 
-# ifndef OPENSSL_NO_NEXTPROTONEG
+# ifdef OSSL_USE_NEXTPROTONEG
 /*
  * call-seq:
  *    ssl.npn_protocol => String | nil
@@ -2326,7 +2513,6 @@
 }
 # endif
 
-# ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
 /*
  * call-seq:
  *    ssl.alpn_protocol => String | nil
@@ -2349,9 +2535,50 @@
     else
 	return rb_str_new((const char *) out, outlen);
 }
-# endif
 
-# ifdef HAVE_SSL_GET_SERVER_TMP_KEY
+/*
+ * call-seq:
+ *    session.export_keying_material(label, length) -> String
+ *
+ * Enables use of shared session key material in accordance with RFC 5705.
+ */
+static VALUE
+ossl_ssl_export_keying_material(int argc, VALUE *argv, VALUE self)
+{
+    SSL *ssl;
+    VALUE str;
+    VALUE label;
+    VALUE length;
+    VALUE context;
+    unsigned char *p;
+    size_t len;
+    int use_ctx = 0;
+    unsigned char *ctx = NULL;
+    size_t ctx_len = 0;
+    int ret;
+
+    rb_scan_args(argc, argv, "21", &label, &length, &context);
+    StringValue(label);
+
+    GetSSL(self, ssl);
+
+    len = (size_t)NUM2LONG(length);
+    str = rb_str_new(0, len);
+    p = (unsigned char *)RSTRING_PTR(str);
+    if (!NIL_P(context)) {
+	use_ctx = 1;
+	StringValue(context);
+	ctx = (unsigned char *)RSTRING_PTR(context);
+	ctx_len = RSTRING_LEN(context);
+    }
+    ret = SSL_export_keying_material(ssl, p, len, (char *)RSTRING_PTR(label),
+				     RSTRING_LENINT(label), ctx, ctx_len, use_ctx);
+    if (ret == 0 || ret == -1) {
+	ossl_raise(eSSLError, "SSL_export_keying_material");
+    }
+    return str;
+}
+
 /*
  * call-seq:
  *    ssl.tmp_key => PKey or nil
@@ -2369,11 +2596,8 @@
 	return Qnil;
     return ossl_pkey_new(key);
 }
-# endif /* defined(HAVE_SSL_GET_SERVER_TMP_KEY) */
 #endif /* !defined(OPENSSL_NO_SOCK) */
 
-#undef rb_intern
-#define rb_intern(s) rb_intern_const(s)
 void
 Init_ossl_ssl(void)
 {
@@ -2384,23 +2608,16 @@
     rb_mWaitWritable = rb_define_module_under(rb_cIO, "WaitWritable");
 #endif
 
-    id_call = rb_intern("call");
-    ID_callback_state = rb_intern("callback_state");
+#ifndef OPENSSL_NO_SOCK
+    id_call = rb_intern_const("call");
+    ID_callback_state = rb_intern_const("callback_state");
 
-    ossl_ssl_ex_vcb_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_vcb_idx", 0, 0, 0);
-    if (ossl_ssl_ex_vcb_idx < 0)
-	ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index");
     ossl_ssl_ex_ptr_idx = SSL_get_ex_new_index(0, (void *)"ossl_ssl_ex_ptr_idx", 0, 0, 0);
     if (ossl_ssl_ex_ptr_idx < 0)
 	ossl_raise(rb_eRuntimeError, "SSL_get_ex_new_index");
     ossl_sslctx_ex_ptr_idx = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_ptr_idx", 0, 0, 0);
     if (ossl_sslctx_ex_ptr_idx < 0)
 	ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index");
-#if !defined(HAVE_X509_STORE_UP_REF)
-    ossl_sslctx_ex_store_p = SSL_CTX_get_ex_new_index(0, (void *)"ossl_sslctx_ex_store_p", 0, 0, 0);
-    if (ossl_sslctx_ex_store_p < 0)
-	ossl_raise(rb_eRuntimeError, "SSL_CTX_get_ex_new_index");
-#endif
 
     /* Document-module: OpenSSL::SSL
      *
@@ -2411,16 +2628,6 @@
      */
     mSSL = rb_define_module_under(mOSSL, "SSL");
 
-    /* Document-module: OpenSSL::ExtConfig
-     *
-     * This module contains configuration information about the SSL extension,
-     * for example if socket support is enabled, or the host name TLS extension
-     * is enabled.  Constants in this module will always be defined, but contain
-     * +true+ or +false+ values depending on the configuration of your OpenSSL
-     * installation.
-     */
-    mSSLExtConfig = rb_define_module_under(mOSSL, "ExtConfig");
-
     /* Document-class: OpenSSL::SSL::SSLError
      *
      * Generic error class raised by SSLSocket and SSLContext.
@@ -2452,7 +2659,7 @@
      * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
      * It is recommended to use #add_certificate instead.
      */
-    rb_attr(cSSLContext, rb_intern("cert"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("cert"), 1, 1, Qfalse);
 
     /*
      * Context private key
@@ -2460,29 +2667,29 @@
      * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
      * It is recommended to use #add_certificate instead.
      */
-    rb_attr(cSSLContext, rb_intern("key"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("key"), 1, 1, Qfalse);
 
     /*
      * A certificate or Array of certificates that will be sent to the client.
      */
-    rb_attr(cSSLContext, rb_intern("client_ca"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("client_ca"), 1, 1, Qfalse);
 
     /*
      * The path to a file containing a PEM-format CA certificate
      */
-    rb_attr(cSSLContext, rb_intern("ca_file"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("ca_file"), 1, 1, Qfalse);
 
     /*
      * The path to a directory containing CA certificates in PEM format.
      *
      * Files are looked up by subject's X509 name's hash value.
      */
-    rb_attr(cSSLContext, rb_intern("ca_path"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("ca_path"), 1, 1, Qfalse);
 
     /*
      * Maximum session lifetime in seconds.
      */
-    rb_attr(cSSLContext, rb_intern("timeout"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("timeout"), 1, 1, Qfalse);
 
     /*
      * Session verification mode.
@@ -2495,12 +2702,12 @@
      *
      * See SSL_CTX_set_verify(3) for details.
      */
-    rb_attr(cSSLContext, rb_intern("verify_mode"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("verify_mode"), 1, 1, Qfalse);
 
     /*
      * Number of CA certificates to walk when verifying a certificate chain.
      */
-    rb_attr(cSSLContext, rb_intern("verify_depth"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("verify_depth"), 1, 1, Qfalse);
 
     /*
      * A callback for additional certificate verification.  The callback is
@@ -2514,7 +2721,7 @@
      * If the callback returns +false+, the chain verification is immediately
      * stopped and a bad_certificate alert is then sent.
      */
-    rb_attr(cSSLContext, rb_intern("verify_callback"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("verify_callback"), 1, 1, Qfalse);
 
     /*
      * Whether to check the server certificate is valid for the hostname.
@@ -2522,12 +2729,12 @@
      * In order to make this work, verify_mode must be set to VERIFY_PEER and
      * the server hostname must be given by OpenSSL::SSL::SSLSocket#hostname=.
      */
-    rb_attr(cSSLContext, rb_intern("verify_hostname"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("verify_hostname"), 1, 1, Qfalse);
 
     /*
      * An OpenSSL::X509::Store used for certificate verification.
      */
-    rb_attr(cSSLContext, rb_intern("cert_store"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("cert_store"), 1, 1, Qfalse);
 
     /*
      * An Array of extra X509 certificates to be added to the certificate
@@ -2536,7 +2743,7 @@
      * The _cert_, _key_, and _extra_chain_cert_ attributes are deprecated.
      * It is recommended to use #add_certificate instead.
      */
-    rb_attr(cSSLContext, rb_intern("extra_chain_cert"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("extra_chain_cert"), 1, 1, Qfalse);
 
     /*
      * A callback invoked when a client certificate is requested by a server
@@ -2546,28 +2753,14 @@
      * containing an OpenSSL::X509::Certificate and an OpenSSL::PKey.  If any
      * other value is returned the handshake is suspended.
      */
-    rb_attr(cSSLContext, rb_intern("client_cert_cb"), 1, 1, Qfalse);
-
-#if !defined(OPENSSL_NO_EC) && defined(HAVE_SSL_CTX_SET_TMP_ECDH_CALLBACK)
-    /*
-     * A callback invoked when ECDH parameters are required.
-     *
-     * The callback is invoked with the Session for the key exchange, an
-     * flag indicating the use of an export cipher and the keylength
-     * required.
-     *
-     * The callback is deprecated. This does not work with recent versions of
-     * OpenSSL. Use OpenSSL::SSL::SSLContext#ecdh_curves= instead.
-     */
-    rb_attr(cSSLContext, rb_intern("tmp_ecdh_callback"), 1, 1, Qfalse);
-#endif
+    rb_attr(cSSLContext, rb_intern_const("client_cert_cb"), 1, 1, Qfalse);
 
     /*
      * Sets the context in which a session can be reused.  This allows
      * sessions for multiple applications to be distinguished, for example, by
      * name.
      */
-    rb_attr(cSSLContext, rb_intern("session_id_context"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("session_id_context"), 1, 1, Qfalse);
 
     /*
      * A callback invoked on a server when a session is proposed by the client
@@ -2576,7 +2769,7 @@
      * The callback is invoked with the SSLSocket and session id.  The
      * callback may return a Session from an external cache.
      */
-    rb_attr(cSSLContext, rb_intern("session_get_cb"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("session_get_cb"), 1, 1, Qfalse);
 
     /*
      * A callback invoked when a new session was negotiated.
@@ -2584,7 +2777,7 @@
      * The callback is invoked with an SSLSocket.  If +false+ is returned the
      * session will be removed from the internal cache.
      */
-    rb_attr(cSSLContext, rb_intern("session_new_cb"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("session_new_cb"), 1, 1, Qfalse);
 
     /*
      * A callback invoked when a session is removed from the internal cache.
@@ -2595,18 +2788,16 @@
      * multi-threaded application. The callback is called inside a global lock
      * and it can randomly cause deadlock on Ruby thread switching.
      */
-    rb_attr(cSSLContext, rb_intern("session_remove_cb"), 1, 1, Qfalse);
-
-    rb_define_const(mSSLExtConfig, "HAVE_TLSEXT_HOST_NAME", Qtrue);
+    rb_attr(cSSLContext, rb_intern_const("session_remove_cb"), 1, 1, Qfalse);
 
     /*
-     * A callback invoked whenever a new handshake is initiated. May be used
-     * to disable renegotiation entirely.
+     * A callback invoked whenever a new handshake is initiated on an
+     * established connection. May be used to disable renegotiation entirely.
      *
      * The callback is invoked with the active SSLSocket. The callback's
-     * return value is irrelevant, normal return indicates "approval" of the
+     * return value is ignored. A normal return indicates "approval" of the
      * renegotiation and will continue the process. To forbid renegotiation
-     * and to cancel the process, an Error may be raised within the callback.
+     * and to cancel the process, raise an exception within the callback.
      *
      * === Disable client renegotiation
      *
@@ -2614,14 +2805,12 @@
      * renegotiation entirely. You may use a callback as follows to implement
      * this feature:
      *
-     *   num_handshakes = 0
      *   ctx.renegotiation_cb = lambda do |ssl|
-     *     num_handshakes += 1
-     *     raise RuntimeError.new("Client renegotiation disabled") if num_handshakes > 1
+     *     raise RuntimeError, "Client renegotiation disabled"
      *   end
      */
-    rb_attr(cSSLContext, rb_intern("renegotiation_cb"), 1, 1, Qfalse);
-#ifndef OPENSSL_NO_NEXTPROTONEG
+    rb_attr(cSSLContext, rb_intern_const("renegotiation_cb"), 1, 1, Qfalse);
+#ifdef OSSL_USE_NEXTPROTONEG
     /*
      * An Enumerable of Strings. Each String represents a protocol to be
      * advertised as the list of supported protocols for Next Protocol
@@ -2633,7 +2822,7 @@
      *
      *   ctx.npn_protocols = ["http/1.1", "spdy/2"]
      */
-    rb_attr(cSSLContext, rb_intern("npn_protocols"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("npn_protocols"), 1, 1, Qfalse);
     /*
      * A callback invoked on the client side when the client needs to select
      * a protocol from the list sent by the server. Supported in OpenSSL 1.0.1
@@ -2650,10 +2839,9 @@
      *     protocols.first
      *   end
      */
-    rb_attr(cSSLContext, rb_intern("npn_select_cb"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("npn_select_cb"), 1, 1, Qfalse);
 #endif
 
-#ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
     /*
      * An Enumerable of Strings. Each String represents a protocol to be
      * advertised as the list of supported protocols for Application-Layer
@@ -2665,7 +2853,7 @@
      *
      *   ctx.alpn_protocols = ["http/1.1", "spdy/2", "h2"]
      */
-    rb_attr(cSSLContext, rb_intern("alpn_protocols"), 1, 1, Qfalse);
+    rb_attr(cSSLContext, rb_intern_const("alpn_protocols"), 1, 1, Qfalse);
     /*
      * A callback invoked on the server side when the server needs to select
      * a protocol from the list sent by the client. Supported in OpenSSL 1.0.2
@@ -2682,8 +2870,30 @@
      *     protocols.first
      *   end
      */
-    rb_attr(cSSLContext, rb_intern("alpn_select_cb"), 1, 1, Qfalse);
-#endif
+    rb_attr(cSSLContext, rb_intern_const("alpn_select_cb"), 1, 1, Qfalse);
+
+    /*
+     * A callback invoked when TLS key material is generated or received, in
+     * order to allow applications to store this keying material for debugging
+     * purposes.
+     *
+     * The callback is invoked with an SSLSocket and a string containing the
+     * key material in the format used by NSS for its SSLKEYLOGFILE debugging
+     * output.
+     *
+     * It is only compatible with OpenSSL >= 1.1.1. Even if LibreSSL implements
+     * SSL_CTX_set_keylog_callback() from v3.4.2, it does nothing (see
+     * https://github.com/libressl-portable/openbsd/commit/648d39f0f035835d0653342d139883b9661e9cb6).
+     *
+     * === Example
+     *
+     *   context.keylog_cb = proc do |_sock, line|
+     *     File.open('ssl_keylog_file', "a") do |f|
+     *       f.write("#{line}\n")
+     *     end
+     *   end
+     */
+    rb_attr(cSSLContext, rb_intern_const("keylog_cb"), 1, 1, Qfalse);
 
     rb_define_alias(cSSLContext, "ssl_timeout", "timeout");
     rb_define_alias(cSSLContext, "ssl_timeout=", "timeout=");
@@ -2691,6 +2901,12 @@
 			     ossl_sslctx_set_minmax_proto_version, 2);
     rb_define_method(cSSLContext, "ciphers",     ossl_sslctx_get_ciphers, 0);
     rb_define_method(cSSLContext, "ciphers=",    ossl_sslctx_set_ciphers, 1);
+#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
+    rb_define_method(cSSLContext, "ciphersuites=", ossl_sslctx_set_ciphersuites, 1);
+#endif
+#ifndef OPENSSL_NO_DH
+    rb_define_method(cSSLContext, "tmp_dh=", ossl_sslctx_set_tmp_dh, 1);
+#endif
     rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1);
     rb_define_method(cSSLContext, "security_level", ossl_sslctx_get_security_level, 0);
     rb_define_method(cSSLContext, "security_level=", ossl_sslctx_set_security_level, 1);
@@ -2764,11 +2980,6 @@
      * Document-class: OpenSSL::SSL::SSLSocket
      */
     cSSLSocket = rb_define_class_under(mSSL, "SSLSocket", rb_cObject);
-#ifdef OPENSSL_NO_SOCK
-    rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qtrue);
-    rb_define_method(cSSLSocket, "initialize", rb_f_notimplement, -1);
-#else
-    rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qfalse);
     rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc);
     rb_define_method(cSSLSocket, "initialize", ossl_ssl_initialize, -1);
     rb_undef_method(cSSLSocket, "initialize_copy");
@@ -2795,16 +3006,14 @@
     rb_define_method(cSSLSocket, "client_ca", ossl_ssl_get_client_ca_list, 0);
     /* #hostname is defined in lib/openssl/ssl.rb */
     rb_define_method(cSSLSocket, "hostname=", ossl_ssl_set_hostname, 1);
-# ifdef HAVE_SSL_GET_SERVER_TMP_KEY
+    rb_define_method(cSSLSocket, "finished_message", ossl_ssl_get_finished, 0);
+    rb_define_method(cSSLSocket, "peer_finished_message", ossl_ssl_get_peer_finished, 0);
     rb_define_method(cSSLSocket, "tmp_key", ossl_ssl_tmp_key, 0);
-# endif
-# ifdef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
     rb_define_method(cSSLSocket, "alpn_protocol", ossl_ssl_alpn_protocol, 0);
-# endif
-# ifndef OPENSSL_NO_NEXTPROTONEG
+    rb_define_method(cSSLSocket, "export_keying_material", ossl_ssl_export_keying_material, -1);
+# ifdef OSSL_USE_NEXTPROTONEG
     rb_define_method(cSSLSocket, "npn_protocol", ossl_ssl_npn_protocol, 0);
 # endif
-#endif
 
     rb_define_const(mSSL, "VERIFY_NONE", INT2NUM(SSL_VERIFY_NONE));
     rb_define_const(mSSL, "VERIFY_PEER", INT2NUM(SSL_VERIFY_PEER));
@@ -2812,12 +3021,23 @@
     rb_define_const(mSSL, "VERIFY_CLIENT_ONCE", INT2NUM(SSL_VERIFY_CLIENT_ONCE));
 
     rb_define_const(mSSL, "OP_ALL", ULONG2NUM(SSL_OP_ALL));
+#ifdef SSL_OP_CLEANSE_PLAINTEXT /* OpenSSL 3.0 */
+    rb_define_const(mSSL, "OP_CLEANSE_PLAINTEXT", ULONG2NUM(SSL_OP_CLEANSE_PLAINTEXT));
+#endif
     rb_define_const(mSSL, "OP_LEGACY_SERVER_CONNECT", ULONG2NUM(SSL_OP_LEGACY_SERVER_CONNECT));
-#ifdef SSL_OP_TLSEXT_PADDING /* OpenSSL 1.0.1h and OpenSSL 1.0.2 */
-    rb_define_const(mSSL, "OP_TLSEXT_PADDING", ULONG2NUM(SSL_OP_TLSEXT_PADDING));
+#ifdef SSL_OP_ENABLE_KTLS /* OpenSSL 3.0 */
+    rb_define_const(mSSL, "OP_ENABLE_KTLS", ULONG2NUM(SSL_OP_ENABLE_KTLS));
 #endif
-#ifdef SSL_OP_SAFARI_ECDHE_ECDSA_BUG /* OpenSSL 1.0.1f and OpenSSL 1.0.2 */
+    rb_define_const(mSSL, "OP_TLSEXT_PADDING", ULONG2NUM(SSL_OP_TLSEXT_PADDING));
     rb_define_const(mSSL, "OP_SAFARI_ECDHE_ECDSA_BUG", ULONG2NUM(SSL_OP_SAFARI_ECDHE_ECDSA_BUG));
+#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF /* OpenSSL 3.0 */
+    rb_define_const(mSSL, "OP_IGNORE_UNEXPECTED_EOF", ULONG2NUM(SSL_OP_IGNORE_UNEXPECTED_EOF));
+#endif
+#ifdef SSL_OP_ALLOW_CLIENT_RENEGOTIATION /* OpenSSL 3.0 */
+    rb_define_const(mSSL, "OP_ALLOW_CLIENT_RENEGOTIATION", ULONG2NUM(SSL_OP_ALLOW_CLIENT_RENEGOTIATION));
+#endif
+#ifdef SSL_OP_DISABLE_TLSEXT_CA_NAMES /* OpenSSL 3.0 */
+    rb_define_const(mSSL, "OP_DISABLE_TLSEXT_CA_NAMES", ULONG2NUM(SSL_OP_DISABLE_TLSEXT_CA_NAMES));
 #endif
 #ifdef SSL_OP_ALLOW_NO_DHE_KEX /* OpenSSL 1.1.1 */
     rb_define_const(mSSL, "OP_ALLOW_NO_DHE_KEX", ULONG2NUM(SSL_OP_ALLOW_NO_DHE_KEX));
@@ -2830,13 +3050,15 @@
 #ifdef SSL_OP_NO_ENCRYPT_THEN_MAC /* OpenSSL 1.1.1 */
     rb_define_const(mSSL, "OP_NO_ENCRYPT_THEN_MAC", ULONG2NUM(SSL_OP_NO_ENCRYPT_THEN_MAC));
 #endif
-    rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE));
-    rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG));
-#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */
-    rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION));
+#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT /* OpenSSL 1.1.1 */
+    rb_define_const(mSSL, "OP_ENABLE_MIDDLEBOX_COMPAT", ULONG2NUM(SSL_OP_ENABLE_MIDDLEBOX_COMPAT));
+#endif
+#ifdef SSL_OP_PRIORITIZE_CHACHA /* OpenSSL 1.1.1 */
+    rb_define_const(mSSL, "OP_PRIORITIZE_CHACHA", ULONG2NUM(SSL_OP_PRIORITIZE_CHACHA));
+#endif
+#ifdef SSL_OP_NO_ANTI_REPLAY /* OpenSSL 1.1.1 */
+    rb_define_const(mSSL, "OP_NO_ANTI_REPLAY", ULONG2NUM(SSL_OP_NO_ANTI_REPLAY));
 #endif
-    rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG));
-
     rb_define_const(mSSL, "OP_NO_SSLv3", ULONG2NUM(SSL_OP_NO_SSLv3));
     rb_define_const(mSSL, "OP_NO_TLSv1", ULONG2NUM(SSL_OP_NO_TLSv1));
     rb_define_const(mSSL, "OP_NO_TLSv1_1", ULONG2NUM(SSL_OP_NO_TLSv1_1));
@@ -2844,6 +3066,12 @@
 #ifdef SSL_OP_NO_TLSv1_3 /* OpenSSL 1.1.1 */
     rb_define_const(mSSL, "OP_NO_TLSv1_3", ULONG2NUM(SSL_OP_NO_TLSv1_3));
 #endif
+    rb_define_const(mSSL, "OP_CIPHER_SERVER_PREFERENCE", ULONG2NUM(SSL_OP_CIPHER_SERVER_PREFERENCE));
+    rb_define_const(mSSL, "OP_TLS_ROLLBACK_BUG", ULONG2NUM(SSL_OP_TLS_ROLLBACK_BUG));
+#ifdef SSL_OP_NO_RENEGOTIATION /* OpenSSL 1.1.1 */
+    rb_define_const(mSSL, "OP_NO_RENEGOTIATION", ULONG2NUM(SSL_OP_NO_RENEGOTIATION));
+#endif
+    rb_define_const(mSSL, "OP_CRYPTOPRO_TLSEXT_BUG", ULONG2NUM(SSL_OP_CRYPTOPRO_TLSEXT_BUG));
 
     /* SSL_OP_* flags for DTLS */
 #if 0
@@ -2908,16 +3136,16 @@
 #endif
 
 
-    sym_exception = ID2SYM(rb_intern("exception"));
-    sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
-    sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
-
-    id_tmp_dh_callback = rb_intern("tmp_dh_callback");
-    id_tmp_ecdh_callback = rb_intern("tmp_ecdh_callback");
-    id_npn_protocols_encoded = rb_intern("npn_protocols_encoded");
+    sym_exception = ID2SYM(rb_intern_const("exception"));
+    sym_wait_readable = ID2SYM(rb_intern_const("wait_readable"));
+    sym_wait_writable = ID2SYM(rb_intern_const("wait_writable"));
+
+    id_tmp_dh_callback = rb_intern_const("tmp_dh_callback");
+    id_npn_protocols_encoded = rb_intern_const("npn_protocols_encoded");
+    id_each = rb_intern_const("each");
 
 #define DefIVarID(name) do \
-    id_i_##name = rb_intern("@"#name); while (0)
+    id_i_##name = rb_intern_const("@"#name); while (0)
 
     DefIVarID(cert_store);
     DefIVarID(ca_file);
@@ -2931,7 +3159,6 @@
     DefIVarID(key);
     DefIVarID(extra_chain_cert);
     DefIVarID(client_cert_cb);
-    DefIVarID(tmp_ecdh_callback);
     DefIVarID(timeout);
     DefIVarID(session_id_context);
     DefIVarID(session_get_cb);
@@ -2943,8 +3170,10 @@
     DefIVarID(alpn_select_cb);
     DefIVarID(servername_cb);
     DefIVarID(verify_hostname);
+    DefIVarID(keylog_cb);
 
     DefIVarID(io);
     DefIVarID(context);
     DefIVarID(hostname);
+#endif /* !defined(OPENSSL_NO_SOCK) */
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ssl.h ruby-2.5.9/ext/openssl/ossl_ssl.h
--- ruby-2.5.9.orig/ext/openssl/ossl_ssl.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_ssl.h	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_SSL_H_)
 #define _OSSL_SSL_H_
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ssl_session.c ruby-2.5.9/ext/openssl/ossl_ssl_session.c
--- ruby-2.5.9.orig/ext/openssl/ossl_ssl_session.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_ssl_session.c	2025-01-29 19:08:02.379421304 +0100
@@ -4,6 +4,7 @@
 
 #include "ossl.h"
 
+#ifndef OPENSSL_NO_SOCK
 VALUE cSSLSession;
 static VALUE eSSLSession;
 
@@ -18,7 +19,7 @@
     {
 	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)
@@ -34,43 +35,38 @@
  * Creates a new Session object from an instance of SSLSocket or DER/PEM encoded
  * String.
  */
-static VALUE ossl_ssl_session_initialize(VALUE self, VALUE arg1)
+static VALUE
+ossl_ssl_session_initialize(VALUE self, VALUE arg1)
 {
-	SSL_SESSION *ctx = NULL;
-
-	if (RDATA(self)->data)
-		ossl_raise(eSSLSession, "SSL Session already initialized");
-
-	if (rb_obj_is_instance_of(arg1, cSSLSocket)) {
-		SSL *ssl;
-
-		GetSSL(arg1, ssl);
-
-		if ((ctx = SSL_get1_session(ssl)) == NULL)
-			ossl_raise(eSSLSession, "no session available");
-	} else {
-		BIO *in = ossl_obj2bio(&arg1);
-
-		ctx = PEM_read_bio_SSL_SESSION(in, NULL, NULL, NULL);
+    SSL_SESSION *ctx;
 
-		if (!ctx) {
-		        OSSL_BIO_reset(in);
-			ctx = d2i_SSL_SESSION_bio(in, NULL);
-		}
+    if (RTYPEDDATA_DATA(self))
+        ossl_raise(eSSLSession, "SSL Session already initialized");
 
-		BIO_free(in);
+    if (rb_obj_is_instance_of(arg1, cSSLSocket)) {
+        SSL *ssl;
 
-		if (!ctx)
-			ossl_raise(rb_eArgError, "unknown type");
-	}
+        GetSSL(arg1, ssl);
 
-	/* should not happen */
-	if (ctx == NULL)
-		ossl_raise(eSSLSession, "ctx not set - internal error");
+        if ((ctx = SSL_get1_session(ssl)) == NULL)
+            ossl_raise(eSSLSession, "no session available");
+    }
+    else {
+        BIO *in = ossl_obj2bio(&arg1);
+
+        ctx = d2i_SSL_SESSION_bio(in, NULL);
+        if (!ctx) {
+            OSSL_BIO_reset(in);
+            ctx = PEM_read_bio_SSL_SESSION(in, NULL, NULL, NULL);
+        }
+        BIO_free(in);
+        if (!ctx)
+            ossl_raise(rb_eArgError, "unknown type");
+    }
 
-	RDATA(self)->data = ctx;
+    RTYPEDDATA_DATA(self) = ctx;
 
-	return self;
+    return self;
 }
 
 static VALUE
@@ -304,6 +300,7 @@
 	return ossl_membio2str(out);
 }
 
+#endif /* !defined(OPENSSL_NO_SOCK) */
 
 void Init_ossl_ssl_session(void)
 {
@@ -312,6 +309,7 @@
     mSSL = rb_define_module_under(mOSSL, "SSL");
     eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
 #endif
+#ifndef OPENSSL_NO_SOCK
 	cSSLSession = rb_define_class_under(mSSL, "Session", rb_cObject);
 	eSSLSession = rb_define_class_under(cSSLSession, "SessionError", eOSSLError);
 
@@ -329,4 +327,5 @@
 	rb_define_method(cSSLSession, "to_der", ossl_ssl_session_to_der, 0);
 	rb_define_method(cSSLSession, "to_pem", ossl_ssl_session_to_pem, 0);
 	rb_define_method(cSSLSession, "to_text", ossl_ssl_session_to_text, 0);
+#endif /* !defined(OPENSSL_NO_SOCK) */
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ts.c ruby-2.5.9/ext/openssl/ossl_ts.c
--- ruby-2.5.9.orig/ext/openssl/ossl_ts.c	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/ext/openssl/ossl_ts.c	2025-01-29 19:08:02.379421304 +0100
@@ -0,0 +1,1597 @@
+/*
+ *
+ * Copyright (C) 2010 Martin Bosslet <Martin.Bosslet@googlemail.com>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'COPYING'.)
+ */
+#include "ossl.h"
+
+#ifndef OPENSSL_NO_TS
+
+#define NewTSRequest(klass) \
+    TypedData_Wrap_Struct((klass), &ossl_ts_req_type, 0)
+#define SetTSRequest(obj, req) do { \
+    if (!(req)) { \
+        ossl_raise(rb_eRuntimeError, "TS_REQ wasn't initialized."); \
+    } \
+    RTYPEDDATA_DATA(obj) = (req); \
+} while (0)
+#define GetTSRequest(obj, req) do { \
+    TypedData_Get_Struct((obj), TS_REQ, &ossl_ts_req_type, (req)); \
+    if (!(req)) { \
+        ossl_raise(rb_eRuntimeError, "TS_REQ wasn't initialized."); \
+    } \
+} while (0)
+
+#define NewTSResponse(klass) \
+    TypedData_Wrap_Struct((klass), &ossl_ts_resp_type, 0)
+#define SetTSResponse(obj, resp) do { \
+    if (!(resp)) { \
+        ossl_raise(rb_eRuntimeError, "TS_RESP wasn't initialized."); \
+    } \
+    RTYPEDDATA_DATA(obj) = (resp); \
+} while (0)
+#define GetTSResponse(obj, resp) do { \
+    TypedData_Get_Struct((obj), TS_RESP, &ossl_ts_resp_type, (resp)); \
+    if (!(resp)) { \
+        ossl_raise(rb_eRuntimeError, "TS_RESP wasn't initialized."); \
+    } \
+} while (0)
+
+#define NewTSTokenInfo(klass) \
+    TypedData_Wrap_Struct((klass), &ossl_ts_token_info_type, 0)
+#define SetTSTokenInfo(obj, info) do { \
+    if (!(info)) { \
+        ossl_raise(rb_eRuntimeError, "TS_TST_INFO wasn't initialized."); \
+    } \
+    RTYPEDDATA_DATA(obj) = (info); \
+} while (0)
+#define GetTSTokenInfo(obj, info) do { \
+    TypedData_Get_Struct((obj), TS_TST_INFO, &ossl_ts_token_info_type, (info)); \
+    if (!(info)) { \
+        ossl_raise(rb_eRuntimeError, "TS_TST_INFO wasn't initialized."); \
+    } \
+} while (0)
+
+#define ossl_tsfac_get_default_policy_id(o)      rb_attr_get((o),rb_intern("@default_policy_id"))
+#define ossl_tsfac_get_serial_number(o)          rb_attr_get((o),rb_intern("@serial_number"))
+#define ossl_tsfac_get_gen_time(o)               rb_attr_get((o),rb_intern("@gen_time"))
+#define ossl_tsfac_get_additional_certs(o)       rb_attr_get((o),rb_intern("@additional_certs"))
+#define ossl_tsfac_get_allowed_digests(o)        rb_attr_get((o),rb_intern("@allowed_digests"))
+
+static VALUE mTimestamp;
+static VALUE eTimestampError;
+static VALUE cTimestampRequest;
+static VALUE cTimestampResponse;
+static VALUE cTimestampTokenInfo;
+static VALUE cTimestampFactory;
+static VALUE sBAD_ALG, sBAD_REQUEST, sBAD_DATA_FORMAT, sTIME_NOT_AVAILABLE;
+static VALUE sUNACCEPTED_POLICY, sUNACCEPTED_EXTENSION, sADD_INFO_NOT_AVAILABLE;
+static VALUE sSYSTEM_FAILURE;
+
+static void
+ossl_ts_req_free(void *ptr)
+{
+    TS_REQ_free(ptr);
+}
+
+static const rb_data_type_t ossl_ts_req_type = {
+    "OpenSSL/Timestamp/Request",
+    {
+        0, ossl_ts_req_free,
+    },
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
+};
+
+static void
+ossl_ts_resp_free(void *ptr)
+{
+    TS_RESP_free(ptr);
+}
+
+static  const rb_data_type_t ossl_ts_resp_type = {
+    "OpenSSL/Timestamp/Response",
+    {
+        0, ossl_ts_resp_free,
+    },
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
+};
+
+static void
+ossl_ts_token_info_free(void *ptr)
+{
+        TS_TST_INFO_free(ptr);
+}
+
+static const rb_data_type_t ossl_ts_token_info_type = {
+    "OpenSSL/Timestamp/TokenInfo",
+    {
+        0, ossl_ts_token_info_free,
+    },
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
+};
+
+static VALUE
+asn1_to_der(void *template, int (*i2d)(void *template, unsigned char **pp))
+{
+    VALUE str;
+    int len;
+    unsigned char *p;
+
+    if((len = i2d(template, NULL)) <= 0)
+        ossl_raise(eTimestampError, "Error when encoding to DER");
+    str = rb_str_new(0, len);
+    p = (unsigned char *)RSTRING_PTR(str);
+    if(i2d(template, &p) <= 0)
+        ossl_raise(eTimestampError, "Error when encoding to DER");
+    rb_str_set_len(str, p - (unsigned char*)RSTRING_PTR(str));
+
+    return str;
+}
+
+static ASN1_OBJECT*
+obj_to_asn1obj(VALUE obj)
+{
+    ASN1_OBJECT *a1obj;
+
+    StringValue(obj);
+    a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 0);
+    if(!a1obj) a1obj = OBJ_txt2obj(RSTRING_PTR(obj), 1);
+    if(!a1obj) ossl_raise(eASN1Error, "invalid OBJECT ID");
+
+    return a1obj;
+}
+
+static VALUE
+obj_to_asn1obj_i(VALUE obj)
+{
+    return (VALUE)obj_to_asn1obj(obj);
+}
+
+static VALUE
+get_asn1obj(ASN1_OBJECT *obj)
+{
+    BIO *out;
+    VALUE ret;
+    int nid;
+    if ((nid = OBJ_obj2nid(obj)) != NID_undef)
+        ret = rb_str_new2(OBJ_nid2sn(nid));
+    else{
+        if (!(out = BIO_new(BIO_s_mem())))
+            ossl_raise(eTimestampError, "BIO_new(BIO_s_mem())");
+        if (i2a_ASN1_OBJECT(out, obj) <= 0) {
+            BIO_free(out);
+            ossl_raise(eTimestampError, "i2a_ASN1_OBJECT");
+        }
+        ret = ossl_membio2str(out);
+    }
+
+    return ret;
+}
+
+static VALUE
+ossl_ts_req_alloc(VALUE klass)
+{
+    TS_REQ *req;
+    VALUE obj;
+
+    obj = NewTSRequest(klass);
+    if (!(req = TS_REQ_new()))
+        ossl_raise(eTimestampError, NULL);
+    SetTSRequest(obj, req);
+
+    /* Defaults */
+    TS_REQ_set_version(req, 1);
+    TS_REQ_set_cert_req(req, 1);
+
+    return obj;
+}
+
+/*
+ * When creating a Request with the +File+ or +string+ parameter, the
+ * corresponding +File+ or +string+ must be DER-encoded.
+ *
+ * call-seq:
+ *       OpenSSL::Timestamp::Request.new(file)    -> request
+ *       OpenSSL::Timestamp::Request.new(string)  -> request
+ *       OpenSSL::Timestamp::Request.new          -> empty request
+ */
+static VALUE
+ossl_ts_req_initialize(int argc, VALUE *argv, VALUE self)
+{
+    TS_REQ *ts_req = DATA_PTR(self);
+    BIO *in;
+    VALUE arg;
+
+    if(rb_scan_args(argc, argv, "01", &arg) == 0) {
+        return self;
+    }
+
+    arg = ossl_to_der_if_possible(arg);
+    in = ossl_obj2bio(&arg);
+    ts_req = d2i_TS_REQ_bio(in, &ts_req);
+    BIO_free(in);
+    if (!ts_req) {
+        DATA_PTR(self) = NULL;
+        ossl_raise(eTimestampError, "Error when decoding the timestamp request");
+    }
+    DATA_PTR(self) = ts_req;
+
+    return self;
+}
+
+/*
+ * Returns the 'short name' of the object identifier that represents the
+ * algorithm that was used to create the message imprint digest.
+ *
+ *  call-seq:
+ *       request.algorithm    -> string
+ */
+static VALUE
+ossl_ts_req_get_algorithm(VALUE self)
+{
+    TS_REQ *req;
+    TS_MSG_IMPRINT *mi;
+    X509_ALGOR *algor;
+
+    GetTSRequest(self, req);
+    mi = TS_REQ_get_msg_imprint(req);
+    algor = TS_MSG_IMPRINT_get_algo(mi);
+    return get_asn1obj(algor->algorithm);
+}
+
+/*
+ * Allows to set the object identifier  or the 'short name' of the
+ * algorithm that was used to create the message imprint digest.
+ *
+ * ===Example:
+ *      request.algorithm = "SHA1"
+ *
+ *  call-seq:
+ *       request.algorithm = "string"    -> string
+ */
+static VALUE
+ossl_ts_req_set_algorithm(VALUE self, VALUE algo)
+{
+    TS_REQ *req;
+    TS_MSG_IMPRINT *mi;
+    ASN1_OBJECT *obj;
+    X509_ALGOR *algor;
+
+    GetTSRequest(self, req);
+    obj = obj_to_asn1obj(algo);
+    mi = TS_REQ_get_msg_imprint(req);
+    algor = TS_MSG_IMPRINT_get_algo(mi);
+    if (!X509_ALGOR_set0(algor, obj, V_ASN1_NULL, NULL)) {
+        ASN1_OBJECT_free(obj);
+        ossl_raise(eTimestampError, "X509_ALGOR_set0");
+    }
+
+    return algo;
+}
+
+/*
+ * Returns the message imprint (digest) of the data to be timestamped.
+ *
+ * call-seq:
+ *       request.message_imprint    -> string or nil
+ */
+static VALUE
+ossl_ts_req_get_msg_imprint(VALUE self)
+{
+    TS_REQ *req;
+    TS_MSG_IMPRINT *mi;
+    ASN1_OCTET_STRING *hashed_msg;
+    VALUE ret;
+
+    GetTSRequest(self, req);
+    mi = TS_REQ_get_msg_imprint(req);
+    hashed_msg = TS_MSG_IMPRINT_get_msg(mi);
+
+    ret = rb_str_new((const char *)hashed_msg->data, hashed_msg->length);
+
+    return ret;
+}
+
+/*
+ * Set the message imprint digest.
+ *
+ *  call-seq:
+ *       request.message_imprint = "string"    -> string
+ */
+static VALUE
+ossl_ts_req_set_msg_imprint(VALUE self, VALUE hash)
+{
+    TS_REQ *req;
+    TS_MSG_IMPRINT *mi;
+    StringValue(hash);
+
+    GetTSRequest(self, req);
+    mi = TS_REQ_get_msg_imprint(req);
+    if (!TS_MSG_IMPRINT_set_msg(mi, (unsigned char *)RSTRING_PTR(hash), RSTRING_LENINT(hash)))
+        ossl_raise(eTimestampError, "TS_MSG_IMPRINT_set_msg");
+
+    return hash;
+}
+
+/*
+ * Returns the version of this request. +1+ is the default value.
+ *
+ * call-seq:
+ *       request.version -> Integer
+ */
+static VALUE
+ossl_ts_req_get_version(VALUE self)
+{
+    TS_REQ *req;
+
+    GetTSRequest(self, req);
+    return LONG2NUM(TS_REQ_get_version(req));
+}
+
+/*
+ * Sets the version number for this Request. This should be +1+ for compliant
+ * servers.
+ *
+ * call-seq:
+ *       request.version = number    -> Integer
+ */
+static VALUE
+ossl_ts_req_set_version(VALUE self, VALUE version)
+{
+    TS_REQ *req;
+    long ver;
+
+    if ((ver = NUM2LONG(version)) < 0)
+        ossl_raise(eTimestampError, "version must be >= 0!");
+    GetTSRequest(self, req);
+    if (!TS_REQ_set_version(req, ver))
+        ossl_raise(eTimestampError, "TS_REQ_set_version");
+
+    return version;
+}
+
+/*
+ * Returns the 'short name' of the object identifier that represents the
+ * timestamp policy under which the server shall create the timestamp.
+ *
+ * call-seq:
+ *       request.policy_id    -> string or nil
+ */
+static VALUE
+ossl_ts_req_get_policy_id(VALUE self)
+{
+    TS_REQ *req;
+
+    GetTSRequest(self, req);
+    if (!TS_REQ_get_policy_id(req))
+        return Qnil;
+    return get_asn1obj(TS_REQ_get_policy_id(req));
+}
+
+/*
+ * Allows to set the object identifier that represents the
+ * timestamp policy under which the server shall create the timestamp. This
+ * may be left +nil+, implying that the timestamp server will issue the
+ * timestamp using some default policy.
+ *
+ * ===Example:
+ *      request.policy_id = "1.2.3.4.5"
+ *
+ * call-seq:
+ *       request.policy_id = "string"   -> string
+ */
+static VALUE
+ossl_ts_req_set_policy_id(VALUE self, VALUE oid)
+{
+    TS_REQ *req;
+    ASN1_OBJECT *obj;
+    int ok;
+
+    GetTSRequest(self, req);
+    obj = obj_to_asn1obj(oid);
+    ok = TS_REQ_set_policy_id(req, obj);
+    ASN1_OBJECT_free(obj);
+    if (!ok)
+        ossl_raise(eTimestampError, "TS_REQ_set_policy_id");
+
+    return oid;
+}
+
+/*
+ * Returns the nonce (number used once) that the server shall include in its
+ * response.
+ *
+ * call-seq:
+ *       request.nonce    -> BN or nil
+ */
+static VALUE
+ossl_ts_req_get_nonce(VALUE self)
+{
+    TS_REQ *req;
+    const ASN1_INTEGER * nonce;
+
+    GetTSRequest(self, req);
+    if (!(nonce = TS_REQ_get_nonce(req)))
+        return Qnil;
+    return asn1integer_to_num(nonce);
+}
+
+/*
+ * Sets the nonce (number used once) that the server shall include in its
+ * response. If the nonce is set, the server must return the same nonce value in
+ * a valid Response.
+ *
+ * call-seq:
+ *       request.nonce = number    -> BN
+ */
+static VALUE
+ossl_ts_req_set_nonce(VALUE self, VALUE num)
+{
+    TS_REQ *req;
+    ASN1_INTEGER *nonce;
+    int ok;
+
+    GetTSRequest(self, req);
+    nonce = num_to_asn1integer(num, NULL);
+    ok = TS_REQ_set_nonce(req, nonce);
+    ASN1_INTEGER_free(nonce);
+    if (!ok)
+        ossl_raise(eTimestampError, NULL);
+    return num;
+}
+
+/*
+ * Indicates whether the response shall contain the timestamp authority's
+ * certificate or not.
+ *
+ * call-seq:
+ *       request.cert_requested?  -> true or false
+ */
+static VALUE
+ossl_ts_req_get_cert_requested(VALUE self)
+{
+    TS_REQ *req;
+
+    GetTSRequest(self, req);
+    return TS_REQ_get_cert_req(req) ? Qtrue: Qfalse;
+}
+
+/*
+ * Specify whether the response shall contain the timestamp authority's
+ * certificate or not. The default value is +true+.
+ *
+ * call-seq:
+ *       request.cert_requested = boolean -> true or false
+ */
+static VALUE
+ossl_ts_req_set_cert_requested(VALUE self, VALUE requested)
+{
+    TS_REQ *req;
+
+    GetTSRequest(self, req);
+    TS_REQ_set_cert_req(req, RTEST(requested));
+
+    return requested;
+}
+
+/*
+ * DER-encodes this Request.
+ *
+ * call-seq:
+ *       request.to_der    -> DER-encoded string
+ */
+static VALUE
+ossl_ts_req_to_der(VALUE self)
+{
+    TS_REQ *req;
+    TS_MSG_IMPRINT *mi;
+    X509_ALGOR *algo;
+    ASN1_OCTET_STRING *hashed_msg;
+
+    GetTSRequest(self, req);
+    mi = TS_REQ_get_msg_imprint(req);
+
+    algo = TS_MSG_IMPRINT_get_algo(mi);
+    if (OBJ_obj2nid(algo->algorithm) == NID_undef)
+        ossl_raise(eTimestampError, "Message imprint missing algorithm");
+
+    hashed_msg = TS_MSG_IMPRINT_get_msg(mi);
+    if (!hashed_msg->length)
+        ossl_raise(eTimestampError, "Message imprint missing hashed message");
+
+    return asn1_to_der((void *)req, (int (*)(void *, unsigned char **))i2d_TS_REQ);
+}
+
+static VALUE
+ossl_ts_req_to_text(VALUE self)
+{
+    TS_REQ *req;
+    BIO *out;
+
+    GetTSRequest(self, req);
+
+    out = BIO_new(BIO_s_mem());
+    if (!out) ossl_raise(eTimestampError, NULL);
+
+    if (!TS_REQ_print_bio(out, req)) {
+        BIO_free(out);
+        ossl_raise(eTimestampError, NULL);
+    }
+
+    return ossl_membio2str(out);
+}
+
+static VALUE
+ossl_ts_resp_alloc(VALUE klass)
+{
+    TS_RESP *resp;
+    VALUE obj;
+
+    obj = NewTSResponse(klass);
+    if (!(resp = TS_RESP_new()))
+        ossl_raise(eTimestampError, NULL);
+    SetTSResponse(obj, resp);
+
+    return obj;
+}
+
+/*
+ * Creates a Response from a +File+ or +string+ parameter, the
+ * corresponding +File+ or +string+ must be DER-encoded. Please note
+ * that Response is an immutable read-only class. If you'd like to create
+ * timestamps please refer to Factory instead.
+ *
+ * call-seq:
+ *       OpenSSL::Timestamp::Response.new(file)    -> response
+ *       OpenSSL::Timestamp::Response.new(string)  -> response
+ */
+static VALUE
+ossl_ts_resp_initialize(VALUE self, VALUE der)
+{
+    TS_RESP *ts_resp = DATA_PTR(self);
+    BIO *in;
+
+    der = ossl_to_der_if_possible(der);
+    in  = ossl_obj2bio(&der);
+    ts_resp = d2i_TS_RESP_bio(in, &ts_resp);
+    BIO_free(in);
+    if (!ts_resp) {
+        DATA_PTR(self) = NULL;
+        ossl_raise(eTimestampError, "Error when decoding the timestamp response");
+    }
+    DATA_PTR(self) = ts_resp;
+
+    return self;
+}
+
+/*
+ * Returns one of GRANTED, GRANTED_WITH_MODS, REJECTION, WAITING,
+ * REVOCATION_WARNING or REVOCATION_NOTIFICATION. A timestamp token has
+ * been created only in case +status+ is equal to GRANTED or GRANTED_WITH_MODS.
+ *
+ * call-seq:
+ *       response.status -> BN (never nil)
+ */
+static VALUE
+ossl_ts_resp_get_status(VALUE self)
+{
+    TS_RESP *resp;
+    TS_STATUS_INFO *si;
+    const ASN1_INTEGER *st;
+
+    GetTSResponse(self, resp);
+    si = TS_RESP_get_status_info(resp);
+    st = TS_STATUS_INFO_get0_status(si);
+
+    return asn1integer_to_num(st);
+}
+
+/*
+ * In cases no timestamp token has been created, this field contains further
+ * info about the reason why response creation failed. The method returns either
+ * nil (the request was successful and a timestamp token was created) or one of
+ * the following:
+ * * :BAD_ALG - Indicates that the timestamp server rejects the message
+ *   imprint algorithm used in the Request
+ * * :BAD_REQUEST - Indicates that the timestamp server was not able to process
+ *   the Request properly
+ * * :BAD_DATA_FORMAT - Indicates that the timestamp server was not able to
+ *   parse certain data in the Request
+ * * :TIME_NOT_AVAILABLE - Indicates that the server could not access its time
+ *   source
+ * * :UNACCEPTED_POLICY - Indicates that the requested policy identifier is not
+ *   recognized or supported by the timestamp server
+ * * :UNACCEPTED_EXTENSIION - Indicates that an extension in the Request is
+ *   not supported by the timestamp server
+ * * :ADD_INFO_NOT_AVAILABLE -Indicates that additional information requested
+ *   is either not understood or currently not available
+ * * :SYSTEM_FAILURE - Timestamp creation failed due to an internal error that
+ *   occurred on the timestamp server
+ *
+ * call-seq:
+ *       response.failure_info -> nil or symbol
+ */
+static VALUE
+ossl_ts_resp_get_failure_info(VALUE self)
+{
+    TS_RESP *resp;
+    TS_STATUS_INFO *si;
+
+    /* The ASN1_BIT_STRING_get_bit changed from 1.0.0. to 1.1.0, making this
+     * const. */
+    #if defined(HAVE_TS_STATUS_INFO_GET0_FAILURE_INFO)
+    const ASN1_BIT_STRING *fi;
+    #else
+    ASN1_BIT_STRING *fi;
+    #endif
+
+    GetTSResponse(self, resp);
+    si = TS_RESP_get_status_info(resp);
+    fi = TS_STATUS_INFO_get0_failure_info(si);
+    if (!fi)
+        return Qnil;
+    if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_BAD_ALG))
+        return sBAD_ALG;
+    if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_BAD_REQUEST))
+        return sBAD_REQUEST;
+    if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_BAD_DATA_FORMAT))
+        return sBAD_DATA_FORMAT;
+    if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_TIME_NOT_AVAILABLE))
+        return sTIME_NOT_AVAILABLE;
+    if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_UNACCEPTED_POLICY))
+        return sUNACCEPTED_POLICY;
+    if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_UNACCEPTED_EXTENSION))
+        return sUNACCEPTED_EXTENSION;
+    if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_ADD_INFO_NOT_AVAILABLE))
+        return sADD_INFO_NOT_AVAILABLE;
+    if (ASN1_BIT_STRING_get_bit(fi, TS_INFO_SYSTEM_FAILURE))
+        return sSYSTEM_FAILURE;
+
+    ossl_raise(eTimestampError, "Unrecognized failure info.");
+}
+
+/*
+ * In cases of failure this field may contain an array of strings further
+ * describing the origin of the failure.
+ *
+ * call-seq:
+ *       response.status_text -> Array of strings or nil
+ */
+static VALUE
+ossl_ts_resp_get_status_text(VALUE self)
+{
+    TS_RESP *resp;
+    TS_STATUS_INFO *si;
+    const STACK_OF(ASN1_UTF8STRING) *text;
+    ASN1_UTF8STRING *current;
+    int i;
+    VALUE ret = rb_ary_new();
+
+    GetTSResponse(self, resp);
+    si = TS_RESP_get_status_info(resp);
+    if ((text = TS_STATUS_INFO_get0_text(si))) {
+        for (i = 0; i < sk_ASN1_UTF8STRING_num(text); i++) {
+            current = sk_ASN1_UTF8STRING_value(text, i);
+            rb_ary_push(ret, asn1str_to_str(current));
+        }
+    }
+
+    return ret;
+}
+
+/*
+ * If a timestamp token is present, this returns it in the form of a
+ * OpenSSL::PKCS7.
+ *
+ * call-seq:
+ *       response.token -> nil or OpenSSL::PKCS7
+ */
+static VALUE
+ossl_ts_resp_get_token(VALUE self)
+{
+    TS_RESP *resp;
+    PKCS7 *p7;
+
+    GetTSResponse(self, resp);
+    if (!(p7 = TS_RESP_get_token(resp)))
+        return Qnil;
+    return ossl_pkcs7_new(p7);
+}
+
+/*
+ * Get the response's token info if present.
+ *
+ * call-seq:
+ *       response.token_info -> nil or OpenSSL::Timestamp::TokenInfo
+ */
+static VALUE
+ossl_ts_resp_get_token_info(VALUE self)
+{
+    TS_RESP *resp;
+    TS_TST_INFO *info, *copy;
+    VALUE obj;
+
+    GetTSResponse(self, resp);
+    if (!(info = TS_RESP_get_tst_info(resp)))
+        return Qnil;
+
+    obj = NewTSTokenInfo(cTimestampTokenInfo);
+
+    if (!(copy = TS_TST_INFO_dup(info)))
+        ossl_raise(eTimestampError, NULL);
+
+    SetTSTokenInfo(obj, copy);
+
+    return obj;
+}
+
+/*
+ * If the Request specified to request the TSA certificate
+ * (Request#cert_requested = true), then this field contains the
+ * certificate of the timestamp authority.
+ *
+ * call-seq:
+ *       response.tsa_certificate -> OpenSSL::X509::Certificate or nil
+ */
+static VALUE
+ossl_ts_resp_get_tsa_certificate(VALUE self)
+{
+    TS_RESP *resp;
+    PKCS7 *p7;
+    PKCS7_SIGNER_INFO *ts_info;
+    X509 *cert;
+
+    GetTSResponse(self, resp);
+    if (!(p7 = TS_RESP_get_token(resp)))
+        return Qnil;
+    ts_info = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0);
+    cert = PKCS7_cert_from_signer_info(p7, ts_info);
+    if (!cert)
+        return Qnil;
+    return ossl_x509_new(cert);
+}
+
+/*
+ * Returns the Response in DER-encoded form.
+ *
+ * call-seq:
+ *       response.to_der -> string
+ */
+static VALUE
+ossl_ts_resp_to_der(VALUE self)
+{
+    TS_RESP *resp;
+
+    GetTSResponse(self, resp);
+    return asn1_to_der((void *)resp, (int (*)(void *, unsigned char **))i2d_TS_RESP);
+}
+
+static VALUE
+ossl_ts_resp_to_text(VALUE self)
+{
+    TS_RESP *resp;
+    BIO *out;
+
+    GetTSResponse(self, resp);
+
+    out = BIO_new(BIO_s_mem());
+    if (!out) ossl_raise(eTimestampError, NULL);
+
+    if (!TS_RESP_print_bio(out, resp)) {
+        BIO_free(out);
+        ossl_raise(eTimestampError, NULL);
+    }
+
+    return ossl_membio2str(out);
+}
+
+/*
+ * Verifies a timestamp token by checking the signature, validating the
+ * certificate chain implied by tsa_certificate and by checking conformance to
+ * a given Request. Mandatory parameters are the Request associated to this
+ * Response, and an OpenSSL::X509::Store of trusted roots.
+ *
+ * Intermediate certificates can optionally be supplied for creating the
+ * certificate chain. These intermediate certificates must all be
+ * instances of OpenSSL::X509::Certificate.
+ *
+ * If validation fails, several kinds of exceptions can be raised:
+ * * TypeError if types don't fit
+ * * TimestampError if something is wrong with the timestamp token itself, if
+ *   it is not conformant to the Request, or if validation of the timestamp
+ *   certificate chain fails.
+ *
+ * call-seq:
+ *       response.verify(Request, root_store) -> Response
+ *       response.verify(Request, root_store, [intermediate_cert]) -> Response
+ */
+static VALUE
+ossl_ts_resp_verify(int argc, VALUE *argv, VALUE self)
+{
+    VALUE ts_req, store, intermediates;
+    TS_RESP *resp;
+    TS_REQ *req;
+    X509_STORE *x509st;
+    TS_VERIFY_CTX *ctx;
+    STACK_OF(X509) *x509inter = NULL;
+    PKCS7* p7;
+    X509 *cert;
+    int status, i, ok;
+
+    rb_scan_args(argc, argv, "21", &ts_req, &store, &intermediates);
+
+    GetTSResponse(self, resp);
+    GetTSRequest(ts_req, req);
+    x509st = GetX509StorePtr(store);
+
+    if (!(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL))) {
+        ossl_raise(eTimestampError, "Error when creating the verification context.");
+    }
+
+    if (!NIL_P(intermediates)) {
+        x509inter = ossl_protect_x509_ary2sk(intermediates, &status);
+        if (status) {
+            TS_VERIFY_CTX_free(ctx);
+            rb_jump_tag(status);
+        }
+    } else if (!(x509inter = sk_X509_new_null())) {
+        TS_VERIFY_CTX_free(ctx);
+        ossl_raise(eTimestampError, "sk_X509_new_null");
+    }
+
+    if (!(p7 = TS_RESP_get_token(resp))) {
+        TS_VERIFY_CTX_free(ctx);
+        sk_X509_pop_free(x509inter, X509_free);
+        ossl_raise(eTimestampError, "TS_RESP_get_token");
+    }
+    for (i=0; i < sk_X509_num(p7->d.sign->cert); i++) {
+        cert = sk_X509_value(p7->d.sign->cert, i);
+        if (!sk_X509_push(x509inter, cert)) {
+            sk_X509_pop_free(x509inter, X509_free);
+            TS_VERIFY_CTX_free(ctx);
+            ossl_raise(eTimestampError, "sk_X509_push");
+        }
+        X509_up_ref(cert);
+    }
+
+    TS_VERIFY_CTX_set_certs(ctx, x509inter);
+    TS_VERIFY_CTX_add_flags(ctx, TS_VFY_SIGNATURE);
+    TS_VERIFY_CTX_set_store(ctx, x509st);
+
+    ok = TS_RESP_verify_response(ctx, resp);
+    /*
+     * TS_VERIFY_CTX_set_store() call above does not increment the reference
+     * counter, so it must be unset before TS_VERIFY_CTX_free() is called.
+     */
+    TS_VERIFY_CTX_set_store(ctx, NULL);
+    TS_VERIFY_CTX_free(ctx);
+
+    if (!ok)
+        ossl_raise(eTimestampError, "TS_RESP_verify_response");
+
+    return self;
+}
+
+static VALUE
+ossl_ts_token_info_alloc(VALUE klass)
+{
+    TS_TST_INFO *info;
+    VALUE obj;
+
+    obj = NewTSTokenInfo(klass);
+    if (!(info = TS_TST_INFO_new()))
+        ossl_raise(eTimestampError, NULL);
+    SetTSTokenInfo(obj, info);
+
+    return obj;
+}
+
+/*
+ * Creates a TokenInfo from a +File+ or +string+ parameter, the
+ * corresponding +File+ or +string+ must be DER-encoded. Please note
+ * that TokenInfo is an immutable read-only class. If you'd like to create
+ * timestamps please refer to Factory instead.
+ *
+ * call-seq:
+ *       OpenSSL::Timestamp::TokenInfo.new(file)    -> token-info
+ *       OpenSSL::Timestamp::TokenInfo.new(string)  -> token-info
+ */
+static VALUE
+ossl_ts_token_info_initialize(VALUE self, VALUE der)
+{
+    TS_TST_INFO *info = DATA_PTR(self);
+    BIO *in;
+
+    der = ossl_to_der_if_possible(der);
+    in  = ossl_obj2bio(&der);
+    info = d2i_TS_TST_INFO_bio(in, &info);
+    BIO_free(in);
+    if (!info) {
+        DATA_PTR(self) = NULL;
+        ossl_raise(eTimestampError, "Error when decoding the timestamp token info");
+    }
+    DATA_PTR(self) = info;
+
+    return self;
+}
+
+/*
+ * Returns the version number of the token info. With compliant servers,
+ * this value should be +1+ if present. If status is GRANTED or
+ * GRANTED_WITH_MODS.
+ *
+ * call-seq:
+ *       token_info.version -> Integer or nil
+ */
+static VALUE
+ossl_ts_token_info_get_version(VALUE self)
+{
+    TS_TST_INFO *info;
+
+    GetTSTokenInfo(self, info);
+    return LONG2NUM(TS_TST_INFO_get_version(info));
+}
+
+/*
+ * Returns the timestamp policy object identifier of the policy this timestamp
+ * was created under. If status is GRANTED or GRANTED_WITH_MODS, this is never
+ * +nil+.
+ *
+ * ===Example:
+ *      id = token_info.policy_id
+ *      puts id                 -> "1.2.3.4.5"
+ *
+ * call-seq:
+ *       token_info.policy_id -> string or nil
+ */
+static VALUE
+ossl_ts_token_info_get_policy_id(VALUE self)
+{
+    TS_TST_INFO *info;
+
+    GetTSTokenInfo(self, info);
+    return get_asn1obj(TS_TST_INFO_get_policy_id(info));
+}
+
+/*
+ * Returns the 'short name' of the object identifier representing the algorithm
+ * that was used to derive the message imprint digest. For valid timestamps,
+ * this is the same value that was already given in the Request. If status is
+ * GRANTED or GRANTED_WITH_MODS, this is never +nil+.
+ *
+ * ===Example:
+ *      algo = token_info.algorithm
+ *      puts algo                -> "SHA1"
+ *
+ * call-seq:
+ *       token_info.algorithm -> string or nil
+ */
+static VALUE
+ossl_ts_token_info_get_algorithm(VALUE self)
+{
+    TS_TST_INFO *info;
+    TS_MSG_IMPRINT *mi;
+    X509_ALGOR *algo;
+
+    GetTSTokenInfo(self, info);
+    mi = TS_TST_INFO_get_msg_imprint(info);
+    algo = TS_MSG_IMPRINT_get_algo(mi);
+    return get_asn1obj(algo->algorithm);
+}
+
+/*
+ * Returns the message imprint digest. For valid timestamps,
+ * this is the same value that was already given in the Request.
+ * If status is GRANTED or GRANTED_WITH_MODS, this is never +nil+.
+ *
+ * ===Example:
+ *      mi = token_info.msg_imprint
+ *      puts mi                -> "DEADBEEF"
+ *
+ * call-seq:
+ *       token_info.msg_imprint -> string.
+ */
+static VALUE
+ossl_ts_token_info_get_msg_imprint(VALUE self)
+{
+    TS_TST_INFO *info;
+    TS_MSG_IMPRINT *mi;
+    ASN1_OCTET_STRING *hashed_msg;
+    VALUE ret;
+
+    GetTSTokenInfo(self, info);
+    mi = TS_TST_INFO_get_msg_imprint(info);
+    hashed_msg = TS_MSG_IMPRINT_get_msg(mi);
+    ret = rb_str_new((const char *)hashed_msg->data, hashed_msg->length);
+
+    return ret;
+}
+
+/*
+ * Returns serial number of the timestamp token. This value shall never be the
+ * same for two timestamp tokens issued by a dedicated timestamp authority.
+ * If status is GRANTED or GRANTED_WITH_MODS, this is never +nil+.
+ *
+ * call-seq:
+ *       token_info.serial_number -> BN or nil
+ */
+static VALUE
+ossl_ts_token_info_get_serial_number(VALUE self)
+{
+    TS_TST_INFO *info;
+
+    GetTSTokenInfo(self, info);
+    return asn1integer_to_num(TS_TST_INFO_get_serial(info));
+}
+
+/*
+ * Returns time when this timestamp token was created. If status is GRANTED or
+ * GRANTED_WITH_MODS, this is never +nil+.
+ *
+ * call-seq:
+ *       token_info.gen_time -> Time
+ */
+static VALUE
+ossl_ts_token_info_get_gen_time(VALUE self)
+{
+    TS_TST_INFO *info;
+
+    GetTSTokenInfo(self, info);
+    return asn1time_to_time(TS_TST_INFO_get_time(info));
+}
+
+/*
+ * If the ordering field is missing, or if the ordering field is present
+ * and set to false, then the genTime field only indicates the time at
+ * which the time-stamp token has been created by the TSA.  In such a
+ * case, the ordering of time-stamp tokens issued by the same TSA or
+ * different TSAs is only possible when the difference between the
+ * genTime of the first time-stamp token and the genTime of the second
+ * time-stamp token is greater than the sum of the accuracies of the
+ * genTime for each time-stamp token.
+ *
+ * If the ordering field is present and set to true, every time-stamp
+ * token from the same TSA can always be ordered based on the genTime
+ * field, regardless of the genTime accuracy.
+ *
+ * call-seq:
+ *       token_info.ordering -> true, falses or nil
+ */
+static VALUE
+ossl_ts_token_info_get_ordering(VALUE self)
+{
+    TS_TST_INFO *info;
+
+    GetTSTokenInfo(self, info);
+    return TS_TST_INFO_get_ordering(info) ? Qtrue : Qfalse;
+}
+
+/*
+ * If the timestamp token is valid then this field contains the same nonce that
+ * was passed to the timestamp server in the initial Request.
+ *
+ * call-seq:
+ *       token_info.nonce -> BN or nil
+ */
+static VALUE
+ossl_ts_token_info_get_nonce(VALUE self)
+{
+    TS_TST_INFO *info;
+    const ASN1_INTEGER *nonce;
+
+    GetTSTokenInfo(self, info);
+    if (!(nonce = TS_TST_INFO_get_nonce(info)))
+        return Qnil;
+
+    return asn1integer_to_num(nonce);
+}
+
+/*
+ * Returns the TokenInfo in DER-encoded form.
+ *
+ * call-seq:
+ *       token_info.to_der -> string
+ */
+static VALUE
+ossl_ts_token_info_to_der(VALUE self)
+{
+    TS_TST_INFO *info;
+
+    GetTSTokenInfo(self, info);
+    return asn1_to_der((void *)info, (int (*)(void *, unsigned char **))i2d_TS_TST_INFO);
+}
+
+static VALUE
+ossl_ts_token_info_to_text(VALUE self)
+{
+    TS_TST_INFO *info;
+    BIO *out;
+
+    GetTSTokenInfo(self, info);
+
+    out = BIO_new(BIO_s_mem());
+    if (!out) ossl_raise(eTimestampError, NULL);
+
+    if (!TS_TST_INFO_print_bio(out, info)) {
+        BIO_free(out);
+        ossl_raise(eTimestampError, NULL);
+    }
+
+    return ossl_membio2str(out);
+}
+
+static ASN1_INTEGER *
+ossl_tsfac_serial_cb(struct TS_resp_ctx *ctx, void *data)
+{
+    ASN1_INTEGER **snptr = (ASN1_INTEGER **)data;
+    ASN1_INTEGER *sn = *snptr;
+    *snptr = NULL;
+    return sn;
+}
+
+static int
+#if !defined(LIBRESSL_VERSION_NUMBER)
+ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, long *sec, long *usec)
+#else
+ossl_tsfac_time_cb(struct TS_resp_ctx *ctx, void *data, time_t *sec, long *usec)
+#endif
+{
+    *sec = *((long *)data);
+    *usec = 0;
+    return 1;
+}
+
+static VALUE
+ossl_evp_get_digestbyname_i(VALUE arg)
+{
+    return (VALUE)ossl_evp_get_digestbyname(arg);
+}
+
+static VALUE
+ossl_obj2bio_i(VALUE arg)
+{
+    return (VALUE)ossl_obj2bio((VALUE *)arg);
+}
+
+/*
+ * Creates a Response with the help of an OpenSSL::PKey, an
+ * OpenSSL::X509::Certificate and a Request.
+ *
+ * Mandatory parameters for timestamp creation that need to be set in the
+ * Request:
+ *
+ * * Request#algorithm
+ * * Request#message_imprint
+ *
+ * Mandatory parameters that need to be set in the Factory:
+ * * Factory#serial_number
+ * * Factory#gen_time
+ * * Factory#allowed_digests
+ *
+ * In addition one of either Request#policy_id or Factory#default_policy_id
+ * must be set.
+ *
+ * Raises a TimestampError if creation fails, though successfully created error
+ * responses may be returned.
+ *
+ * call-seq:
+ *       factory.create_timestamp(key, certificate, request) -> Response
+ */
+static VALUE
+ossl_tsfac_create_ts(VALUE self, VALUE key, VALUE certificate, VALUE request)
+{
+    VALUE serial_number, def_policy_id, gen_time, additional_certs, allowed_digests;
+    VALUE str;
+    STACK_OF(X509) *inter_certs;
+    VALUE tsresp, ret = Qnil;
+    EVP_PKEY *sign_key;
+    X509 *tsa_cert;
+    TS_REQ *req;
+    TS_RESP *response = NULL;
+    TS_RESP_CTX *ctx = NULL;
+    BIO *req_bio;
+    ASN1_INTEGER *asn1_serial = NULL;
+    ASN1_OBJECT *def_policy_id_obj = NULL;
+    long lgen_time;
+    const char * err_msg = NULL;
+    int status = 0;
+
+    tsresp = NewTSResponse(cTimestampResponse);
+    tsa_cert = GetX509CertPtr(certificate);
+    sign_key = GetPrivPKeyPtr(key);
+    GetTSRequest(request, req);
+
+    gen_time = ossl_tsfac_get_gen_time(self);
+    if (!rb_obj_is_instance_of(gen_time, rb_cTime)) {
+        err_msg = "@gen_time must be a Time.";
+        goto end;
+    }
+    lgen_time = NUM2LONG(rb_funcall(gen_time, rb_intern("to_i"), 0));
+
+    serial_number = ossl_tsfac_get_serial_number(self);
+    if (NIL_P(serial_number)) {
+        err_msg = "@serial_number must be set.";
+        goto end;
+    }
+    asn1_serial = num_to_asn1integer(serial_number, NULL);
+
+    def_policy_id = ossl_tsfac_get_default_policy_id(self);
+    if (NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req)) {
+        err_msg = "No policy id in the request and no default policy set";
+        goto end;
+    }
+    if (!NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req)) {
+        def_policy_id_obj = (ASN1_OBJECT*)rb_protect(obj_to_asn1obj_i, (VALUE)def_policy_id, &status);
+        if (status)
+            goto end;
+    }
+
+    if (!(ctx = TS_RESP_CTX_new())) {
+        err_msg = "Memory allocation failed.";
+        goto end;
+    }
+
+    TS_RESP_CTX_set_serial_cb(ctx, ossl_tsfac_serial_cb, &asn1_serial);
+    if (!TS_RESP_CTX_set_signer_cert(ctx, tsa_cert)) {
+        err_msg = "Certificate does not contain the timestamping extension";
+        goto end;
+    }
+
+    additional_certs = ossl_tsfac_get_additional_certs(self);
+    if (rb_obj_is_kind_of(additional_certs, rb_cArray)) {
+        inter_certs = ossl_protect_x509_ary2sk(additional_certs, &status);
+        if (status)
+                goto end;
+
+        /* this dups the sk_X509 and ups each cert's ref count */
+        TS_RESP_CTX_set_certs(ctx, inter_certs);
+        sk_X509_pop_free(inter_certs, X509_free);
+    }
+
+    TS_RESP_CTX_set_signer_key(ctx, sign_key);
+    if (!NIL_P(def_policy_id) && !TS_REQ_get_policy_id(req))
+        TS_RESP_CTX_set_def_policy(ctx, def_policy_id_obj);
+    if (TS_REQ_get_policy_id(req))
+        TS_RESP_CTX_set_def_policy(ctx, TS_REQ_get_policy_id(req));
+    TS_RESP_CTX_set_time_cb(ctx, ossl_tsfac_time_cb, &lgen_time);
+
+    allowed_digests = ossl_tsfac_get_allowed_digests(self);
+    if (rb_obj_is_kind_of(allowed_digests, rb_cArray)) {
+        int i;
+        VALUE rbmd;
+        const EVP_MD *md;
+
+        for (i = 0; i < RARRAY_LEN(allowed_digests); i++) {
+            rbmd = rb_ary_entry(allowed_digests, i);
+            md = (const EVP_MD *)rb_protect(ossl_evp_get_digestbyname_i, rbmd, &status);
+            if (status)
+                goto end;
+            TS_RESP_CTX_add_md(ctx, md);
+        }
+    }
+
+    str = rb_protect(ossl_to_der, request, &status);
+    if (status)
+        goto end;
+
+    req_bio = (BIO*)rb_protect(ossl_obj2bio_i, (VALUE)&str, &status);
+    if (status)
+        goto end;
+
+    response = TS_RESP_create_response(ctx, req_bio);
+    BIO_free(req_bio);
+
+    if (!response) {
+        err_msg = "Error during response generation";
+        goto end;
+    }
+
+    /* bad responses aren't exceptional, but openssl still sets error
+     * information. */
+    ossl_clear_error();
+
+    SetTSResponse(tsresp, response);
+    ret = tsresp;
+
+end:
+    ASN1_INTEGER_free(asn1_serial);
+    ASN1_OBJECT_free(def_policy_id_obj);
+    TS_RESP_CTX_free(ctx);
+    if (err_msg)
+        rb_exc_raise(ossl_make_error(eTimestampError, rb_str_new_cstr(err_msg)));
+    if (status)
+        rb_jump_tag(status);
+    return ret;
+}
+
+/*
+ * INIT
+ */
+void
+Init_ossl_ts(void)
+{
+    #if 0
+    mOSSL = rb_define_module("OpenSSL"); /* let rdoc know about mOSSL */
+    #endif
+
+    /*
+     * Possible return value for +Response#failure_info+. Indicates that the
+     * timestamp server rejects the message imprint algorithm used in the
+     * +Request+
+     */
+    sBAD_ALG = ID2SYM(rb_intern_const("BAD_ALG"));
+
+    /*
+     * Possible return value for +Response#failure_info+. Indicates that the
+     * timestamp server was not able to process the +Request+ properly.
+     */
+    sBAD_REQUEST = ID2SYM(rb_intern_const("BAD_REQUEST"));
+    /*
+     * Possible return value for +Response#failure_info+. Indicates that the
+     * timestamp server was not able to parse certain data in the +Request+.
+     */
+    sBAD_DATA_FORMAT = ID2SYM(rb_intern_const("BAD_DATA_FORMAT"));
+
+    sTIME_NOT_AVAILABLE = ID2SYM(rb_intern_const("TIME_NOT_AVAILABLE"));
+    sUNACCEPTED_POLICY = ID2SYM(rb_intern_const("UNACCEPTED_POLICY"));
+    sUNACCEPTED_EXTENSION = ID2SYM(rb_intern_const("UNACCEPTED_EXTENSION"));
+    sADD_INFO_NOT_AVAILABLE = ID2SYM(rb_intern_const("ADD_INFO_NOT_AVAILABLE"));
+    sSYSTEM_FAILURE = ID2SYM(rb_intern_const("SYSTEM_FAILURE"));
+
+    /* Document-class: OpenSSL::Timestamp
+     * Provides classes and methods to request, create and validate
+     * {RFC3161-compliant}[http://www.ietf.org/rfc/rfc3161.txt] timestamps.
+     * Request may be used to either create requests from scratch or to parse
+     * existing requests that again can be used to request timestamps from a
+     * timestamp server, e.g. via the net/http. The resulting timestamp
+     * response may be parsed using Response.
+     *
+     * Please note that Response is read-only and immutable. To create a
+     * Response, an instance of Factory as well as a valid Request are needed.
+     *
+     * ===Create a Response:
+     *      #Assumes ts.p12 is a PKCS#12-compatible file with a private key
+     *      #and a certificate that has an extended key usage of 'timeStamping'
+     *      p12 = OpenSSL::PKCS12.new(File.binread('ts.p12'), 'pwd')
+     *      md = OpenSSL::Digest.new('SHA1')
+     *      hash = md.digest(data) #some binary data to be timestamped
+     *      req = OpenSSL::Timestamp::Request.new
+     *      req.algorithm = 'SHA1'
+     *      req.message_imprint = hash
+     *      req.policy_id = "1.2.3.4.5"
+     *      req.nonce = 42
+     *      fac = OpenSSL::Timestamp::Factory.new
+     *      fac.gen_time = Time.now
+     *      fac.serial_number = 1
+     *      timestamp = fac.create_timestamp(p12.key, p12.certificate, req)
+     *
+     * ===Verify a timestamp response:
+     *      #Assume we have a timestamp token in a file called ts.der
+     *      ts = OpenSSL::Timestamp::Response.new(File.binread('ts.der'))
+     *      #Assume we have the Request for this token in a file called req.der
+     *      req = OpenSSL::Timestamp::Request.new(File.binread('req.der'))
+     *      # Assume the associated root CA certificate is contained in a
+     *      # DER-encoded file named root.cer
+     *      root = OpenSSL::X509::Certificate.new(File.binread('root.cer'))
+     *      # get the necessary intermediate certificates, available in
+     *      # DER-encoded form in inter1.cer and inter2.cer
+     *      inter1 = OpenSSL::X509::Certificate.new(File.binread('inter1.cer'))
+     *      inter2 = OpenSSL::X509::Certificate.new(File.binread('inter2.cer'))
+     *      ts.verify(req, root, inter1, inter2) -> ts or raises an exception if validation fails
+     *
+     */
+    mTimestamp = rb_define_module_under(mOSSL, "Timestamp");
+
+    /* Document-class: OpenSSL::Timestamp::TimestampError
+     * Generic exception class of the Timestamp module.
+     */
+    eTimestampError = rb_define_class_under(mTimestamp, "TimestampError", eOSSLError);
+
+    /* Document-class: OpenSSL::Timestamp::Response
+     * Immutable and read-only representation of a timestamp response returned
+     * from a timestamp server after receiving an associated Request. Allows
+     * access to specific information about the response but also allows to
+     * verify the Response.
+     */
+    cTimestampResponse = rb_define_class_under(mTimestamp, "Response", rb_cObject);
+    rb_define_alloc_func(cTimestampResponse, ossl_ts_resp_alloc);
+    rb_define_method(cTimestampResponse, "initialize", ossl_ts_resp_initialize, 1);
+    rb_define_method(cTimestampResponse, "status", ossl_ts_resp_get_status, 0);
+    rb_define_method(cTimestampResponse, "failure_info", ossl_ts_resp_get_failure_info, 0);
+    rb_define_method(cTimestampResponse, "status_text", ossl_ts_resp_get_status_text, 0);
+    rb_define_method(cTimestampResponse, "token", ossl_ts_resp_get_token, 0);
+    rb_define_method(cTimestampResponse, "token_info", ossl_ts_resp_get_token_info, 0);
+    rb_define_method(cTimestampResponse, "tsa_certificate", ossl_ts_resp_get_tsa_certificate, 0);
+    rb_define_method(cTimestampResponse, "to_der", ossl_ts_resp_to_der, 0);
+    rb_define_method(cTimestampResponse, "to_text", ossl_ts_resp_to_text, 0);
+    rb_define_method(cTimestampResponse, "verify", ossl_ts_resp_verify, -1);
+
+    /* Document-class: OpenSSL::Timestamp::TokenInfo
+     * Immutable and read-only representation of a timestamp token info from a
+     * Response.
+     */
+    cTimestampTokenInfo = rb_define_class_under(mTimestamp, "TokenInfo", rb_cObject);
+    rb_define_alloc_func(cTimestampTokenInfo, ossl_ts_token_info_alloc);
+    rb_define_method(cTimestampTokenInfo, "initialize", ossl_ts_token_info_initialize, 1);
+    rb_define_method(cTimestampTokenInfo, "version", ossl_ts_token_info_get_version, 0);
+    rb_define_method(cTimestampTokenInfo, "policy_id", ossl_ts_token_info_get_policy_id, 0);
+    rb_define_method(cTimestampTokenInfo, "algorithm", ossl_ts_token_info_get_algorithm, 0);
+    rb_define_method(cTimestampTokenInfo, "message_imprint", ossl_ts_token_info_get_msg_imprint, 0);
+    rb_define_method(cTimestampTokenInfo, "serial_number", ossl_ts_token_info_get_serial_number, 0);
+    rb_define_method(cTimestampTokenInfo, "gen_time", ossl_ts_token_info_get_gen_time, 0);
+    rb_define_method(cTimestampTokenInfo, "ordering", ossl_ts_token_info_get_ordering, 0);
+    rb_define_method(cTimestampTokenInfo, "nonce", ossl_ts_token_info_get_nonce, 0);
+    rb_define_method(cTimestampTokenInfo, "to_der", ossl_ts_token_info_to_der, 0);
+    rb_define_method(cTimestampTokenInfo, "to_text", ossl_ts_token_info_to_text, 0);
+
+    /* Document-class: OpenSSL::Timestamp::Request
+     * Allows to create timestamp requests or parse existing ones. A Request is
+     * also needed for creating timestamps from scratch with Factory. When
+     * created from scratch, some default values are set:
+     * * version is set to +1+
+     * * cert_requested is set to +true+
+     * * algorithm, message_imprint, policy_id, and nonce are set to +false+
+     */
+    cTimestampRequest = rb_define_class_under(mTimestamp, "Request", rb_cObject);
+    rb_define_alloc_func(cTimestampRequest, ossl_ts_req_alloc);
+    rb_define_method(cTimestampRequest, "initialize", ossl_ts_req_initialize, -1);
+    rb_define_method(cTimestampRequest, "version=", ossl_ts_req_set_version, 1);
+    rb_define_method(cTimestampRequest, "version", ossl_ts_req_get_version, 0);
+    rb_define_method(cTimestampRequest, "algorithm=", ossl_ts_req_set_algorithm, 1);
+    rb_define_method(cTimestampRequest, "algorithm", ossl_ts_req_get_algorithm, 0);
+    rb_define_method(cTimestampRequest, "message_imprint=", ossl_ts_req_set_msg_imprint, 1);
+    rb_define_method(cTimestampRequest, "message_imprint", ossl_ts_req_get_msg_imprint, 0);
+    rb_define_method(cTimestampRequest, "policy_id=", ossl_ts_req_set_policy_id, 1);
+    rb_define_method(cTimestampRequest, "policy_id", ossl_ts_req_get_policy_id, 0);
+    rb_define_method(cTimestampRequest, "nonce=", ossl_ts_req_set_nonce, 1);
+    rb_define_method(cTimestampRequest, "nonce", ossl_ts_req_get_nonce, 0);
+    rb_define_method(cTimestampRequest, "cert_requested=", ossl_ts_req_set_cert_requested, 1);
+    rb_define_method(cTimestampRequest, "cert_requested?", ossl_ts_req_get_cert_requested, 0);
+    rb_define_method(cTimestampRequest, "to_der", ossl_ts_req_to_der, 0);
+    rb_define_method(cTimestampRequest, "to_text", ossl_ts_req_to_text, 0);
+
+    /*
+     * Indicates a successful response. Equal to +0+.
+     */
+    rb_define_const(cTimestampResponse, "GRANTED", INT2NUM(TS_STATUS_GRANTED));
+    /*
+     * Indicates a successful response that probably contains modifications
+     * from the initial request. Equal to +1+.
+     */
+    rb_define_const(cTimestampResponse, "GRANTED_WITH_MODS", INT2NUM(TS_STATUS_GRANTED_WITH_MODS));
+    /*
+     * Indicates a failure. No timestamp token was created. Equal to +2+.
+     */
+    rb_define_const(cTimestampResponse, "REJECTION", INT2NUM(TS_STATUS_REJECTION));
+    /*
+     * Indicates a failure. No timestamp token was created. Equal to +3+.
+     */
+    rb_define_const(cTimestampResponse, "WAITING", INT2NUM(TS_STATUS_WAITING));
+    /*
+     * Indicates a failure. No timestamp token was created. Revocation of a
+     * certificate is imminent. Equal to +4+.
+     */
+    rb_define_const(cTimestampResponse, "REVOCATION_WARNING", INT2NUM(TS_STATUS_REVOCATION_WARNING));
+    /*
+     * Indicates a failure. No timestamp token was created. A certificate
+     * has been revoked. Equal to +5+.
+     */
+    rb_define_const(cTimestampResponse, "REVOCATION_NOTIFICATION", INT2NUM(TS_STATUS_REVOCATION_NOTIFICATION));
+
+    /* Document-class: OpenSSL::Timestamp::Factory
+     *
+     * Used to generate a Response from scratch.
+     *
+     * Please bear in mind that the implementation will always apply and prefer
+     * the policy object identifier given in the request over the default policy
+     * id specified in the Factory. As a consequence, +default_policy_id+ will
+     * only be applied if no Request#policy_id was given. But this also means
+     * that one needs to check the policy identifier in the request manually
+     * before creating the Response, e.g. to check whether it complies to a
+     * specific set of acceptable policies.
+     *
+     * There exists also the possibility to add certificates (instances of
+     * OpenSSL::X509::Certificate) besides the timestamping certificate
+     * that will be included in the resulting timestamp token if
+     * Request#cert_requested? is +true+. Ideally, one would also include any
+     * intermediate certificates (the root certificate can be left out - in
+     * order to trust it any verifying party will have to be in its possession
+     * anyway). This simplifies validation of the timestamp since these
+     * intermediate certificates are "already there" and need not be passed as
+     * external parameters to Response#verify anymore, thus minimizing external
+     * resources needed for verification.
+     *
+     * ===Example: Inclusion of (untrusted) intermediate certificates
+     *
+     * Assume we received a timestamp request that has set Request#policy_id to
+     * +nil+ and Request#cert_requested? to true. The raw request bytes are
+     * stored in a variable called +req_raw+. We'd still like to integrate
+     * the necessary intermediate certificates (in +inter1.cer+ and
+     * +inter2.cer+) to simplify validation of the resulting Response. +ts.p12+
+     * is a PKCS#12-compatible file including the private key and the
+     * timestamping certificate.
+     *
+     *      req = OpenSSL::Timestamp::Request.new(raw_bytes)
+     *      p12 = OpenSSL::PKCS12.new(File.binread('ts.p12'), 'pwd')
+     *      inter1 = OpenSSL::X509::Certificate.new(File.binread('inter1.cer'))
+     *      inter2 = OpenSSL::X509::Certificate.new(File.binread('inter2.cer'))
+     *      fac = OpenSSL::Timestamp::Factory.new
+     *      fac.gen_time = Time.now
+     *      fac.serial_number = 1
+     *      fac.allowed_digests = ["sha256", "sha384", "sha512"]
+     *      #needed because the Request contained no policy identifier
+     *      fac.default_policy_id = '1.2.3.4.5'
+     *      fac.additional_certificates = [ inter1, inter2 ]
+     *      timestamp = fac.create_timestamp(p12.key, p12.certificate, req)
+     *
+     * ==Attributes
+     *
+     * ===default_policy_id
+     *
+     * Request#policy_id will always be preferred over this if present in the
+     * Request, only if Request#policy_id is nil default_policy will be used.
+     * If none of both is present, a TimestampError will be raised when trying
+     * to create a Response.
+     *
+     * call-seq:
+     *       factory.default_policy_id = "string" -> string
+     *       factory.default_policy_id            -> string or nil
+     *
+     * ===serial_number
+     *
+     * Sets or retrieves the serial number to be used for timestamp creation.
+     * Must be present for timestamp creation.
+     *
+     * call-seq:
+     *       factory.serial_number = number -> number
+     *       factory.serial_number          -> number or nil
+     *
+     * ===gen_time
+     *
+     * Sets or retrieves the Time value to be used in the Response. Must be
+     * present for timestamp creation.
+     *
+     * call-seq:
+     *       factory.gen_time = Time -> Time
+     *       factory.gen_time        -> Time or nil
+     *
+     * ===additional_certs
+     *
+     * Sets or retrieves additional certificates apart from the timestamp
+     * certificate (e.g. intermediate certificates) to be added to the Response.
+     * Must be an Array of OpenSSL::X509::Certificate.
+     *
+     * call-seq:
+     *       factory.additional_certs = [cert1, cert2] -> [ cert1, cert2 ]
+     *       factory.additional_certs                  -> array or nil
+     *
+     * ===allowed_digests
+     *
+     * Sets or retrieves the digest algorithms that the factory is allowed
+     * create timestamps for. Known vulnerable or weak algorithms should not be
+     * allowed where possible.
+     * Must be an Array of String or OpenSSL::Digest subclass instances.
+     *
+     * call-seq:
+     *       factory.allowed_digests = ["sha1", OpenSSL::Digest.new('SHA256').new] -> [ "sha1", OpenSSL::Digest) ]
+     *       factory.allowed_digests                                               -> array or nil
+     *
+     */
+    cTimestampFactory = rb_define_class_under(mTimestamp, "Factory", rb_cObject);
+    rb_attr(cTimestampFactory, rb_intern_const("allowed_digests"), 1, 1, 0);
+    rb_attr(cTimestampFactory, rb_intern_const("default_policy_id"), 1, 1, 0);
+    rb_attr(cTimestampFactory, rb_intern_const("serial_number"), 1, 1, 0);
+    rb_attr(cTimestampFactory, rb_intern_const("gen_time"), 1, 1, 0);
+    rb_attr(cTimestampFactory, rb_intern_const("additional_certs"), 1, 1, 0);
+    rb_define_method(cTimestampFactory, "create_timestamp", ossl_tsfac_create_ts, 3);
+}
+#else /* OPENSSL_NO_TS */
+void
+Init_ossl_ts(void)
+{
+}
+#endif
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_ts.h ruby-2.5.9/ext/openssl/ossl_ts.h
--- ruby-2.5.9.orig/ext/openssl/ossl_ts.h	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/ext/openssl/ossl_ts.h	2025-01-29 19:08:02.379421304 +0100
@@ -0,0 +1,16 @@
+/*
+ *
+ * Copyright (C) 2010 Martin Bosslet <Martin.Bosslet@googlemail.com>
+ * All rights reserved.
+ */
+/*
+ * This program is licenced under the same licence as Ruby.
+ * (See the file 'COPYING'.)
+ */
+
+#if !defined(_OSSL_TS_H_)
+#define _OSSL_TS_H_
+
+void Init_ossl_ts(void);
+
+#endif
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_version.h ruby-2.5.9/ext/openssl/ossl_version.h
--- ruby-2.5.9.orig/ext/openssl/ossl_version.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_version.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,15 +0,0 @@
-/*
- * 'OpenSSL for Ruby' project
- * Copyright (C) 2001-2002  Michal Rokos <m.rokos@sh.cvut.cz>
- * All rights reserved.
- */
-/*
- * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
- */
-#if !defined(_OSSL_VERSION_H_)
-#define _OSSL_VERSION_H_
-
-#define OSSL_VERSION "2.1.2"
-
-#endif /* _OSSL_VERSION_H_ */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509attr.c ruby-2.5.9/ext/openssl/ossl_x509attr.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509attr.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509attr.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -28,7 +28,7 @@
  * Classes
  */
 VALUE cX509Attr;
-VALUE eX509AttrError;
+static VALUE eX509AttrError;
 
 static void
 ossl_x509attr_free(void *ptr)
@@ -41,7 +41,7 @@
     {
 	0, ossl_x509attr_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -201,37 +201,36 @@
 ossl_x509attr_set_value(VALUE self, VALUE value)
 {
     X509_ATTRIBUTE *attr;
-    VALUE asn1_value;
-    int i, asn1_tag;
+    GetX509Attr(self, attr);
 
     OSSL_Check_Kind(value, cASN1Data);
-    asn1_tag = NUM2INT(rb_attr_get(value, rb_intern("@tag")));
-    asn1_value = rb_attr_get(value, rb_intern("@value"));
-    if (asn1_tag != V_ASN1_SET)
-	ossl_raise(eASN1Error, "argument must be ASN1::Set");
-    if (!RB_TYPE_P(asn1_value, T_ARRAY))
-	ossl_raise(eASN1Error, "ASN1::Set has non-array value");
+    VALUE der = ossl_to_der(value);
+    const unsigned char *p = (const unsigned char *)RSTRING_PTR(der);
+    STACK_OF(ASN1_TYPE) *sk = d2i_ASN1_SET_ANY(NULL, &p, RSTRING_LEN(der));
+    if (!sk)
+        ossl_raise(eX509AttrError, "attribute value must be ASN1::Set");
 
-    GetX509Attr(self, attr);
     if (X509_ATTRIBUTE_count(attr)) { /* populated, reset first */
-	ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr);
-	X509_ATTRIBUTE *new_attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, 0, NULL, -1);
-	if (!new_attr)
-	    ossl_raise(eX509AttrError, NULL);
-	SetX509Attr(self, new_attr);
-	X509_ATTRIBUTE_free(attr);
-	attr = new_attr;
-    }
-
-    for (i = 0; i < RARRAY_LEN(asn1_value); i++) {
-	ASN1_TYPE *a1type = ossl_asn1_get_asn1type(RARRAY_AREF(asn1_value, i));
-	if (!X509_ATTRIBUTE_set1_data(attr, ASN1_TYPE_get(a1type),
-				      a1type->value.ptr, -1)) {
-	    ASN1_TYPE_free(a1type);
-	    ossl_raise(eX509AttrError, NULL);
-	}
-	ASN1_TYPE_free(a1type);
+        ASN1_OBJECT *obj = X509_ATTRIBUTE_get0_object(attr);
+        X509_ATTRIBUTE *new_attr = X509_ATTRIBUTE_create_by_OBJ(NULL, obj, 0, NULL, -1);
+        if (!new_attr) {
+            sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free);
+            ossl_raise(eX509AttrError, "X509_ATTRIBUTE_create_by_OBJ");
+        }
+        SetX509Attr(self, new_attr);
+        X509_ATTRIBUTE_free(attr);
+        attr = new_attr;
+    }
+
+    for (int i = 0; i < sk_ASN1_TYPE_num(sk); i++) {
+        ASN1_TYPE *a1type = sk_ASN1_TYPE_value(sk, i);
+        if (!X509_ATTRIBUTE_set1_data(attr, ASN1_TYPE_get(a1type),
+                                      a1type->value.ptr, -1)) {
+            sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free);
+            ossl_raise(eX509AttrError, "X509_ATTRIBUTE_set1_data");
+        }
     }
+    sk_ASN1_TYPE_pop_free(sk, ASN1_TYPE_free);
 
     return value;
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509.c ruby-2.5.9/ext/openssl/ossl_x509.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -44,7 +44,13 @@
     Init_ossl_x509revoked();
     Init_ossl_x509store();
 
+    /* Constants are up-to-date with 1.1.1. */
+
+    /* Certificate verification error code */
     DefX509Const(V_OK);
+#if defined(X509_V_ERR_UNSPECIFIED) /* 1.0.1r, 1.0.2f, 1.1.0 */
+    DefX509Const(V_ERR_UNSPECIFIED);
+#endif
     DefX509Const(V_ERR_UNABLE_TO_GET_ISSUER_CERT);
     DefX509Const(V_ERR_UNABLE_TO_GET_CRL);
     DefX509Const(V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE);
@@ -76,8 +82,71 @@
     DefX509Const(V_ERR_AKID_SKID_MISMATCH);
     DefX509Const(V_ERR_AKID_ISSUER_SERIAL_MISMATCH);
     DefX509Const(V_ERR_KEYUSAGE_NO_CERTSIGN);
+    DefX509Const(V_ERR_UNABLE_TO_GET_CRL_ISSUER);
+    DefX509Const(V_ERR_UNHANDLED_CRITICAL_EXTENSION);
+    DefX509Const(V_ERR_KEYUSAGE_NO_CRL_SIGN);
+    DefX509Const(V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION);
+    DefX509Const(V_ERR_INVALID_NON_CA);
+    DefX509Const(V_ERR_PROXY_PATH_LENGTH_EXCEEDED);
+    DefX509Const(V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE);
+    DefX509Const(V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED);
+    DefX509Const(V_ERR_INVALID_EXTENSION);
+    DefX509Const(V_ERR_INVALID_POLICY_EXTENSION);
+    DefX509Const(V_ERR_NO_EXPLICIT_POLICY);
+    DefX509Const(V_ERR_DIFFERENT_CRL_SCOPE);
+    DefX509Const(V_ERR_UNSUPPORTED_EXTENSION_FEATURE);
+    DefX509Const(V_ERR_UNNESTED_RESOURCE);
+    DefX509Const(V_ERR_PERMITTED_VIOLATION);
+    DefX509Const(V_ERR_EXCLUDED_VIOLATION);
+    DefX509Const(V_ERR_SUBTREE_MINMAX);
     DefX509Const(V_ERR_APPLICATION_VERIFICATION);
+    DefX509Const(V_ERR_UNSUPPORTED_CONSTRAINT_TYPE);
+    DefX509Const(V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX);
+    DefX509Const(V_ERR_UNSUPPORTED_NAME_SYNTAX);
+    DefX509Const(V_ERR_CRL_PATH_VALIDATION_ERROR);
+#if defined(X509_V_ERR_PATH_LOOP)
+    DefX509Const(V_ERR_PATH_LOOP);
+#endif
+#if defined(X509_V_ERR_SUITE_B_INVALID_VERSION)
+    DefX509Const(V_ERR_SUITE_B_INVALID_VERSION);
+    DefX509Const(V_ERR_SUITE_B_INVALID_ALGORITHM);
+    DefX509Const(V_ERR_SUITE_B_INVALID_CURVE);
+    DefX509Const(V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM);
+    DefX509Const(V_ERR_SUITE_B_LOS_NOT_ALLOWED);
+    DefX509Const(V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256);
+#endif
+    DefX509Const(V_ERR_HOSTNAME_MISMATCH);
+    DefX509Const(V_ERR_EMAIL_MISMATCH);
+    DefX509Const(V_ERR_IP_ADDRESS_MISMATCH);
+#if defined(X509_V_ERR_DANE_NO_MATCH)
+    DefX509Const(V_ERR_DANE_NO_MATCH);
+#endif
+#if defined(X509_V_ERR_EE_KEY_TOO_SMALL)
+    DefX509Const(V_ERR_EE_KEY_TOO_SMALL);
+    DefX509Const(V_ERR_CA_KEY_TOO_SMALL);
+    DefX509Const(V_ERR_CA_MD_TOO_WEAK);
+#endif
+#if defined(X509_V_ERR_INVALID_CALL)
+    DefX509Const(V_ERR_INVALID_CALL);
+#endif
+#if defined(X509_V_ERR_STORE_LOOKUP)
+    DefX509Const(V_ERR_STORE_LOOKUP);
+#endif
+#if defined(X509_V_ERR_NO_VALID_SCTS)
+    DefX509Const(V_ERR_NO_VALID_SCTS);
+#endif
+#if defined(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION)
+    DefX509Const(V_ERR_PROXY_SUBJECT_NAME_VIOLATION);
+#endif
+#if defined(X509_V_ERR_OCSP_VERIFY_NEEDED)
+    DefX509Const(V_ERR_OCSP_VERIFY_NEEDED);
+    DefX509Const(V_ERR_OCSP_VERIFY_FAILED);
+    DefX509Const(V_ERR_OCSP_CERT_UNKNOWN);
+#endif
 
+    /* Certificate verify flags */
+    /* Set by Store#flags= and StoreContext#flags=. */
+    DefX509Const(V_FLAG_USE_CHECK_TIME);
     /* Set by Store#flags= and StoreContext#flags=. Enables CRL checking for the
      * certificate chain leaf. */
     DefX509Const(V_FLAG_CRL_CHECK);
@@ -116,12 +185,28 @@
     /* Set by Store#flags= and StoreContext#flags=. Enables checking of the
      * signature of the root self-signed CA. */
     DefX509Const(V_FLAG_CHECK_SS_SIGNATURE);
-#if defined(X509_V_FLAG_TRUSTED_FIRST)
     /* Set by Store#flags= and StoreContext#flags=. When constructing a
      * certificate chain, search the Store first for the issuer certificate.
      * Enabled by default in OpenSSL >= 1.1.0. */
     DefX509Const(V_FLAG_TRUSTED_FIRST);
+#if defined(X509_V_FLAG_SUITEB_128_LOS_ONLY)
+    /* Set by Store#flags= and StoreContext#flags=.
+     * Enables Suite B 128 bit only mode. */
+    DefX509Const(V_FLAG_SUITEB_128_LOS_ONLY);
 #endif
+#if defined(X509_V_FLAG_SUITEB_192_LOS)
+    /* Set by Store#flags= and StoreContext#flags=.
+     * Enables Suite B 192 bit only mode. */
+    DefX509Const(V_FLAG_SUITEB_192_LOS);
+#endif
+#if defined(X509_V_FLAG_SUITEB_128_LOS)
+    /* Set by Store#flags= and StoreContext#flags=.
+     * Enables Suite B 128 bit mode allowing 192 bit algorithms. */
+    DefX509Const(V_FLAG_SUITEB_128_LOS);
+#endif
+    /* Set by Store#flags= and StoreContext#flags=.
+     * Allows partial chains if at least one certificate is in trusted store. */
+    DefX509Const(V_FLAG_PARTIAL_CHAIN);
 #if defined(X509_V_FLAG_NO_ALT_CHAINS)
     /* Set by Store#flags= and StoreContext#flags=. Suppresses searching for
      * a alternative chain. No effect in OpenSSL >= 1.1.0. */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509cert.c ruby-2.5.9/ext/openssl/ossl_x509cert.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509cert.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509cert.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -28,7 +28,7 @@
  * Classes
  */
 VALUE cX509Cert;
-VALUE eX509CertError;
+static VALUE eX509CertError;
 
 static void
 ossl_x509_free(void *ptr)
@@ -41,7 +41,7 @@
     {
 	0, ossl_x509_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -115,24 +115,27 @@
 ossl_x509_initialize(int argc, VALUE *argv, VALUE self)
 {
     BIO *in;
-    X509 *x509, *x = DATA_PTR(self);
+    X509 *x509, *x509_orig = RTYPEDDATA_DATA(self);
     VALUE arg;
 
+    rb_check_frozen(self);
     if (rb_scan_args(argc, argv, "01", &arg) == 0) {
 	/* create just empty X509Cert */
 	return self;
     }
     arg = ossl_to_der_if_possible(arg);
     in = ossl_obj2bio(&arg);
-    x509 = PEM_read_bio_X509(in, &x, NULL, NULL);
-    DATA_PTR(self) = x;
+    x509 = d2i_X509_bio(in, NULL);
     if (!x509) {
-	OSSL_BIO_reset(in);
-	x509 = d2i_X509_bio(in, &x);
-	DATA_PTR(self) = x;
+        OSSL_BIO_reset(in);
+        x509 = PEM_read_bio_X509(in, NULL, NULL, NULL);
     }
     BIO_free(in);
-    if (!x509) ossl_raise(eX509CertError, NULL);
+    if (!x509)
+        ossl_raise(eX509CertError, "PEM_read_bio_X509");
+
+    RTYPEDDATA_DATA(self) = x509;
+    X509_free(x509_orig);
 
     return self;
 }
@@ -536,7 +539,11 @@
     const EVP_MD *md;
 
     pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
-    md = ossl_evp_get_digestbyname(digest);
+    if (NIL_P(digest)) {
+        md = NULL; /* needed for some key types, e.g. Ed25519 */
+    } else {
+        md = ossl_evp_get_digestbyname(digest);
+    }
     GetX509(self, x509);
     if (!X509_sign(x509, pkey, md)) {
 	ossl_raise(eX509CertError, NULL);
@@ -639,12 +646,12 @@
 	OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
     }
     GetX509(self, x509);
-    while ((ext = X509_delete_ext(x509, 0)))
-	X509_EXTENSION_free(ext);
+    for (i = X509_get_ext_count(x509); i > 0; i--)
+        X509_EXTENSION_free(X509_delete_ext(x509, 0));
     for (i=0; i<RARRAY_LEN(ary); i++) {
 	ext = GetX509ExtPtr(RARRAY_AREF(ary, i));
 	if (!X509_add_ext(x509, ext, -1)) { /* DUPs ext */
-	    ossl_raise(eX509CertError, NULL);
+	    ossl_raise(eX509CertError, "X509_add_ext");
 	}
     }
 
@@ -704,6 +711,189 @@
     return !X509_cmp(a, b) ? Qtrue : Qfalse;
 }
 
+#ifdef HAVE_I2D_RE_X509_TBS
+/*
+ * call-seq:
+ *    cert.tbs_bytes => string
+ *
+ * Returns the DER-encoded bytes of the certificate's to be signed certificate.
+ * This is mainly useful for validating embedded certificate transparency signatures.
+ */
+static VALUE
+ossl_x509_tbs_bytes(VALUE self)
+{
+    X509 *x509;
+    int len;
+    unsigned char *p0;
+    VALUE str;
+
+    GetX509(self, x509);
+    len = i2d_re_X509_tbs(x509, NULL);
+    if (len <= 0) {
+        ossl_raise(eX509CertError, "i2d_re_X509_tbs");
+    }
+    str = rb_str_new(NULL, len);
+    p0 = (unsigned char *)RSTRING_PTR(str);
+    if (i2d_re_X509_tbs(x509, &p0) <= 0) {
+        ossl_raise(eX509CertError, "i2d_re_X509_tbs");
+    }
+    ossl_str_adjust(str, p0);
+
+    return str;
+}
+#endif
+
+struct load_chained_certificates_arguments {
+    VALUE certificates;
+    X509 *certificate;
+};
+
+static VALUE
+load_chained_certificates_append_push(VALUE _arguments) {
+    struct load_chained_certificates_arguments *arguments = (struct load_chained_certificates_arguments*)_arguments;
+
+    if (arguments->certificates == Qnil) {
+        arguments->certificates = rb_ary_new();
+    }
+
+    rb_ary_push(arguments->certificates, ossl_x509_new(arguments->certificate));
+
+    return Qnil;
+}
+
+static VALUE
+load_chained_certificate_append_ensure(VALUE _arguments) {
+    struct load_chained_certificates_arguments *arguments = (struct load_chained_certificates_arguments*)_arguments;
+
+    X509_free(arguments->certificate);
+
+    return Qnil;
+}
+
+inline static VALUE
+load_chained_certificates_append(VALUE certificates, X509 *certificate) {
+    struct load_chained_certificates_arguments arguments;
+    arguments.certificates = certificates;
+    arguments.certificate = certificate;
+
+    rb_ensure(load_chained_certificates_append_push, (VALUE)&arguments, load_chained_certificate_append_ensure, (VALUE)&arguments);
+
+    return arguments.certificates;
+}
+
+static VALUE
+load_chained_certificates_PEM(BIO *in) {
+    VALUE certificates = Qnil;
+    X509 *certificate = PEM_read_bio_X509(in, NULL, NULL, NULL);
+
+    /* If we cannot read even one certificate: */
+    if (certificate == NULL) {
+        /* If we cannot read one certificate because we could not read the PEM encoding: */
+        if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
+            ossl_clear_error();
+        }
+
+        if (ERR_peek_last_error())
+            ossl_raise(eX509CertError, NULL);
+        else
+            return Qnil;
+    }
+
+    certificates = load_chained_certificates_append(Qnil, certificate);
+
+    while ((certificate = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
+      load_chained_certificates_append(certificates, certificate);
+    }
+
+    /* We tried to read one more certificate but could not read start line: */
+    if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
+        /* This is not an error, it means we are finished: */
+        ossl_clear_error();
+
+        return certificates;
+    }
+
+    /* Alternatively, if we reached the end of the file and there was no error: */
+    if (BIO_eof(in) && !ERR_peek_last_error()) {
+        return certificates;
+    } else {
+        /* Otherwise, we tried to read a certificate but failed somewhere: */
+        ossl_raise(eX509CertError, NULL);
+    }
+}
+
+static VALUE
+load_chained_certificates_DER(BIO *in) {
+    X509 *certificate = d2i_X509_bio(in, NULL);
+
+    /* If we cannot read one certificate: */
+    if (certificate == NULL) {
+        /* Ignore error. We could not load. */
+        ossl_clear_error();
+
+        return Qnil;
+    }
+
+    return load_chained_certificates_append(Qnil, certificate);
+}
+
+static VALUE
+load_chained_certificates(VALUE _io) {
+    BIO *in = (BIO*)_io;
+    VALUE certificates = Qnil;
+
+    /*
+      DER is a binary format and it may contain octets within it that look like
+      PEM encoded certificates. So we need to check DER first.
+    */
+    certificates = load_chained_certificates_DER(in);
+
+    if (certificates != Qnil)
+        return certificates;
+
+    OSSL_BIO_reset(in);
+
+    certificates = load_chained_certificates_PEM(in);
+
+    if (certificates != Qnil)
+        return certificates;
+
+    /* Otherwise we couldn't read the output correctly so fail: */
+    ossl_raise(eX509CertError, "Could not detect format of certificate data!");
+}
+
+static VALUE
+load_chained_certificates_ensure(VALUE _io) {
+    BIO *in = (BIO*)_io;
+
+    BIO_free(in);
+
+    return Qnil;
+}
+
+/*
+ * call-seq:
+ *    OpenSSL::X509::Certificate.load(string) -> [certs...]
+ *    OpenSSL::X509::Certificate.load(file) -> [certs...]
+ *
+ * Read the chained certificates from the given input. Supports both PEM
+ * and DER encoded certificates.
+ *
+ * PEM is a text format and supports more than one certificate.
+ *
+ * DER is a binary format and only supports one certificate.
+ *
+ * If the file is empty, or contains only unrelated data, an
+ * +OpenSSL::X509::CertificateError+ exception will be raised.
+ */
+static VALUE
+ossl_x509_load(VALUE klass, VALUE buffer)
+{
+    BIO *in = ossl_obj2bio(&buffer);
+
+    return rb_ensure(load_chained_certificates, (VALUE)in, load_chained_certificates_ensure, (VALUE)in);
+}
+
 /*
  * INIT
  */
@@ -730,7 +920,7 @@
      * Certificate is capable of handling DER-encoded certificates and
      * certificates encoded in OpenSSL's PEM format.
      *
-     *   raw = File.read "cert.cer" # DER- or PEM-encoded
+     *   raw = File.binread "cert.cer" # DER- or PEM-encoded
      *   certificate = OpenSSL::X509::Certificate.new raw
      *
      * === Saving a certificate to a file
@@ -788,7 +978,7 @@
      *   root_ca.add_extension(ef.create_extension("keyUsage","keyCertSign, cRLSign", true))
      *   root_ca.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
      *   root_ca.add_extension(ef.create_extension("authorityKeyIdentifier","keyid:always",false))
-     *   root_ca.sign(root_key, OpenSSL::Digest::SHA256.new)
+     *   root_ca.sign(root_key, OpenSSL::Digest.new('SHA256'))
      *
      * The next step is to create the end-entity certificate using the root CA
      * certificate.
@@ -807,11 +997,13 @@
      *   ef.issuer_certificate = root_ca
      *   cert.add_extension(ef.create_extension("keyUsage","digitalSignature", true))
      *   cert.add_extension(ef.create_extension("subjectKeyIdentifier","hash",false))
-     *   cert.sign(root_key, OpenSSL::Digest::SHA256.new)
+     *   cert.sign(root_key, OpenSSL::Digest.new('SHA256'))
      *
      */
     cX509Cert = rb_define_class_under(mX509, "Certificate", rb_cObject);
 
+    rb_define_singleton_method(cX509Cert, "load", ossl_x509_load, 1);
+
     rb_define_alloc_func(cX509Cert, ossl_x509_alloc);
     rb_define_method(cX509Cert, "initialize", ossl_x509_initialize, -1);
     rb_define_method(cX509Cert, "initialize_copy", ossl_x509_copy, 1);
@@ -843,4 +1035,7 @@
     rb_define_method(cX509Cert, "add_extension", ossl_x509_add_extension, 1);
     rb_define_method(cX509Cert, "inspect", ossl_x509_inspect, 0);
     rb_define_method(cX509Cert, "==", ossl_x509_eq, 1);
+#ifdef HAVE_I2D_RE_X509_TBS
+    rb_define_method(cX509Cert, "tbs_bytes", ossl_x509_tbs_bytes, 0);
+#endif
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509crl.c ruby-2.5.9/ext/openssl/ossl_x509crl.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509crl.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509crl.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -27,8 +27,8 @@
 /*
  * Classes
  */
-VALUE cX509CRL;
-VALUE eX509CRLError;
+static VALUE cX509CRL;
+static VALUE eX509CRLError;
 
 static void
 ossl_x509crl_free(void *ptr)
@@ -41,7 +41,7 @@
     {
 	0, ossl_x509crl_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -93,23 +93,26 @@
 ossl_x509crl_initialize(int argc, VALUE *argv, VALUE self)
 {
     BIO *in;
-    X509_CRL *crl, *x = DATA_PTR(self);
+    X509_CRL *crl, *crl_orig = RTYPEDDATA_DATA(self);
     VALUE arg;
 
+    rb_check_frozen(self);
     if (rb_scan_args(argc, argv, "01", &arg) == 0) {
 	return self;
     }
     arg = ossl_to_der_if_possible(arg);
     in = ossl_obj2bio(&arg);
-    crl = PEM_read_bio_X509_CRL(in, &x, NULL, NULL);
-    DATA_PTR(self) = x;
+    crl = d2i_X509_CRL_bio(in, NULL);
     if (!crl) {
-	OSSL_BIO_reset(in);
-	crl = d2i_X509_CRL_bio(in, &x);
-	DATA_PTR(self) = x;
+        OSSL_BIO_reset(in);
+        crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
     }
     BIO_free(in);
-    if (!crl) ossl_raise(eX509CRLError, NULL);
+    if (!crl)
+        ossl_raise(eX509CRLError, "PEM_read_bio_X509_CRL");
+
+    RTYPEDDATA_DATA(self) = crl;
+    X509_CRL_free(crl_orig);
 
     return self;
 }
@@ -347,7 +350,11 @@
 
     GetX509CRL(self, crl);
     pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
-    md = ossl_evp_get_digestbyname(digest);
+    if (NIL_P(digest)) {
+	md = NULL; /* needed for some key types, e.g. Ed25519 */
+    } else {
+	md = ossl_evp_get_digestbyname(digest);
+    }
     if (!X509_CRL_sign(crl, pkey, md)) {
 	ossl_raise(eX509CRLError, NULL);
     }
@@ -471,12 +478,12 @@
 	OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
     }
     GetX509CRL(self, crl);
-    while ((ext = X509_CRL_delete_ext(crl, 0)))
-	X509_EXTENSION_free(ext);
+    for (i = X509_CRL_get_ext_count(crl); i > 0; i--)
+        X509_EXTENSION_free(X509_CRL_delete_ext(crl, 0));
     for (i=0; i<RARRAY_LEN(ary); i++) {
 	ext = GetX509ExtPtr(RARRAY_AREF(ary, i)); /* NO NEED TO DUP */
 	if (!X509_CRL_add_ext(crl, ext, -1)) {
-	    ossl_raise(eX509CRLError, NULL);
+	    ossl_raise(eX509CRLError, "X509_CRL_add_ext");
 	}
     }
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509ext.c ruby-2.5.9/ext/openssl/ossl_x509ext.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509ext.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509ext.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -41,8 +41,8 @@
  * Classes
  */
 VALUE cX509Ext;
-VALUE cX509ExtFactory;
-VALUE eX509ExtError;
+static VALUE cX509ExtFactory;
+static VALUE eX509ExtError;
 
 static void
 ossl_x509ext_free(void *ptr)
@@ -55,7 +55,7 @@
     {
 	0, ossl_x509ext_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -108,7 +108,7 @@
     {
 	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 @@
     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);
@@ -226,11 +227,15 @@
     GetX509ExtFactory(self, ctx);
     obj = NewX509Ext(cX509Ext);
     rconf = rb_iv_get(self, "@config");
-    conf = NIL_P(rconf) ? NULL : DupConfigPtr(rconf);
+    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);
-    NCONF_free(conf);
     if (!ext){
 	ossl_raise(eX509ExtError, "%"PRIsVALUE" = %"PRIsVALUE, oid, valstr);
     }
@@ -403,6 +408,19 @@
 }
 
 static VALUE
+ossl_x509ext_get_value_der(VALUE obj)
+{
+    X509_EXTENSION *ext;
+    ASN1_OCTET_STRING *value;
+
+    GetX509Ext(obj, ext);
+    if ((value = X509_EXTENSION_get_data(ext)) == NULL)
+	ossl_raise(eX509ExtError, NULL);
+
+    return rb_str_new((const char *)value->data, value->length);
+}
+
+static VALUE
 ossl_x509ext_get_critical(VALUE obj)
 {
     X509_EXTENSION *ext;
@@ -472,6 +490,7 @@
     rb_define_method(cX509Ext, "critical=", ossl_x509ext_set_critical, 1);
     rb_define_method(cX509Ext, "oid", ossl_x509ext_get_oid, 0);
     rb_define_method(cX509Ext, "value", ossl_x509ext_get_value, 0);
+    rb_define_method(cX509Ext, "value_der", ossl_x509ext_get_value_der, 0);
     rb_define_method(cX509Ext, "critical?", ossl_x509ext_get_critical, 0);
     rb_define_method(cX509Ext, "to_der", ossl_x509ext_to_der, 0);
 }
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509.h ruby-2.5.9/ext/openssl/ossl_x509.h
--- ruby-2.5.9.orig/ext/openssl/ossl_x509.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509.h	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #if !defined(_OSSL_X509_H_)
 #define _OSSL_X509_H_
@@ -28,7 +28,6 @@
  * X509Attr
  */
 extern VALUE cX509Attr;
-extern VALUE eX509AttrError;
 
 VALUE ossl_x509attr_new(X509_ATTRIBUTE *);
 X509_ATTRIBUTE *GetX509AttrPtr(VALUE);
@@ -38,7 +37,6 @@
  * X509Cert
  */
 extern VALUE cX509Cert;
-extern VALUE eX509CertError;
 
 VALUE ossl_x509_new(X509 *);
 X509 *GetX509CertPtr(VALUE);
@@ -48,9 +46,6 @@
 /*
  * X509CRL
  */
-extern VALUE cX509CRL;
-extern VALUE eX509CRLError;
-
 VALUE ossl_x509crl_new(X509_CRL *);
 X509_CRL *GetX509CRLPtr(VALUE);
 void Init_ossl_x509crl(void);
@@ -59,8 +54,6 @@
  * X509Extension
  */
 extern VALUE cX509Ext;
-extern VALUE cX509ExtFactory;
-extern VALUE eX509ExtError;
 
 VALUE ossl_x509ext_new(X509_EXTENSION *);
 X509_EXTENSION *GetX509ExtPtr(VALUE);
@@ -69,9 +62,6 @@
 /*
  * X509Name
  */
-extern VALUE cX509Name;
-extern VALUE eX509NameError;
-
 VALUE ossl_x509name_new(X509_NAME *);
 X509_NAME *GetX509NamePtr(VALUE);
 void Init_ossl_x509name(void);
@@ -79,9 +69,6 @@
 /*
  * X509Request
  */
-extern VALUE cX509Req;
-extern VALUE eX509ReqError;
-
 X509_REQ *GetX509ReqPtr(VALUE);
 void Init_ossl_x509req(void);
 
@@ -89,7 +76,6 @@
  * X509Revoked
  */
 extern VALUE cX509Rev;
-extern VALUE eX509RevError;
 
 VALUE ossl_x509revoked_new(X509_REVOKED *);
 X509_REVOKED *DupX509RevokedPtr(VALUE);
@@ -98,12 +84,7 @@
 /*
  * X509Store and X509StoreContext
  */
-extern VALUE cX509Store;
-extern VALUE cX509StoreContext;
-extern VALUE eX509StoreError;
-
 X509_STORE *GetX509StorePtr(VALUE);
-
 void Init_ossl_x509store(void);
 
 /*
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509name.c ruby-2.5.9/ext/openssl/ossl_x509name.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509name.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509name.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -32,8 +32,8 @@
 /*
  * Classes
  */
-VALUE cX509Name;
-VALUE eX509NameError;
+static VALUE cX509Name;
+static VALUE eX509NameError;
 
 static void
 ossl_x509name_free(void *ptr)
@@ -46,7 +46,7 @@
     {
 	0, ossl_x509name_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -270,7 +270,7 @@
     if (!out)
 	ossl_raise(eX509NameError, NULL);
     ret = X509_NAME_print_ex(out, name, 0, iflag);
-    if (ret < 0 || iflag == XN_FLAG_COMPAT && ret == 0) {
+    if (ret < 0 || (iflag == XN_FLAG_COMPAT && ret == 0)) {
 	BIO_free(out);
 	ossl_raise(eX509NameError, "X509_NAME_print_ex");
     }
@@ -291,7 +291,14 @@
  * * OpenSSL::X509::Name::MULTILINE
  *
  * If _format_ is omitted, the largely broken and traditional OpenSSL format
- * is used.
+ * (<tt>X509_NAME_oneline()</tt> format) is chosen.
+ *
+ * <b>Use of this method is discouraged.</b> None of the formats other than
+ * OpenSSL::X509::Name::RFC2253 is standardized and may show an inconsistent
+ * behavior through \OpenSSL versions.
+ *
+ * It is recommended to use #to_utf8 instead, which is equivalent to calling
+ * <tt>name.to_s(OpenSSL::X509::Name::RFC2253).force_encoding("UTF-8")</tt>.
  */
 static VALUE
 ossl_x509name_to_s(int argc, VALUE *argv, VALUE self)
@@ -387,17 +394,21 @@
 
 /*
  * call-seq:
- *    name.cmp(other) -> -1 | 0 | 1
- *    name <=> other  -> -1 | 0 | 1
+ *    name.cmp(other) -> -1 | 0 | 1 | nil
+ *    name <=> other  -> -1 | 0 | 1 | nil
  *
  * Compares this Name with _other_ and returns +0+ if they are the same and +-1+
  * or ++1+ if they are greater or less than each other respectively.
+ * Returns +nil+ if they are not comparable (i.e. different types).
  */
 static VALUE
 ossl_x509name_cmp(VALUE self, VALUE other)
 {
     int result;
 
+    if (!rb_obj_is_kind_of(other, cX509Name))
+	return Qnil;
+
     result = ossl_x509name_cmp0(self, other);
     if (result < 0) return INT2FIX(-1);
     if (result > 0) return INT2FIX(1);
@@ -494,7 +505,7 @@
  * You can create a Name by parsing a distinguished name String or by
  * supplying the distinguished name as an Array.
  *
- *   name = OpenSSL::X509::Name.parse 'CN=nobody/DC=example'
+ *   name = OpenSSL::X509::Name.parse_rfc2253 'DC=example,CN=nobody'
  *
  *   name = OpenSSL::X509::Name.new [['CN', 'nobody'], ['DC', 'example']]
  */
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509req.c ruby-2.5.9/ext/openssl/ossl_x509req.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509req.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509req.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -27,8 +27,8 @@
 /*
  * Classes
  */
-VALUE cX509Req;
-VALUE eX509ReqError;
+static VALUE cX509Req;
+static VALUE eX509ReqError;
 
 static void
 ossl_x509req_free(void *ptr)
@@ -41,7 +41,7 @@
     {
 	0, ossl_x509req_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -79,23 +79,26 @@
 ossl_x509req_initialize(int argc, VALUE *argv, VALUE self)
 {
     BIO *in;
-    X509_REQ *req, *x = DATA_PTR(self);
+    X509_REQ *req, *req_orig = RTYPEDDATA_DATA(self);
     VALUE arg;
 
+    rb_check_frozen(self);
     if (rb_scan_args(argc, argv, "01", &arg) == 0) {
 	return self;
     }
     arg = ossl_to_der_if_possible(arg);
     in = ossl_obj2bio(&arg);
-    req = PEM_read_bio_X509_REQ(in, &x, NULL, NULL);
-    DATA_PTR(self) = x;
+    req = d2i_X509_REQ_bio(in, NULL);
     if (!req) {
-	OSSL_BIO_reset(in);
-	req = d2i_X509_REQ_bio(in, &x);
-	DATA_PTR(self) = x;
+        OSSL_BIO_reset(in);
+        req = PEM_read_bio_X509_REQ(in, NULL, NULL, NULL);
     }
     BIO_free(in);
-    if (!req) ossl_raise(eX509ReqError, NULL);
+    if (!req)
+        ossl_raise(eX509ReqError, "PEM_read_bio_X509_REQ");
+
+    RTYPEDDATA_DATA(self) = req;
+    X509_REQ_free(req_orig);
 
     return self;
 }
@@ -309,7 +312,11 @@
 
     GetX509Req(self, req);
     pkey = GetPrivPKeyPtr(key); /* NO NEED TO DUP */
-    md = ossl_evp_get_digestbyname(digest);
+    if (NIL_P(digest)) {
+        md = NULL; /* needed for some key types, e.g. Ed25519 */
+    } else {
+        md = ossl_evp_get_digestbyname(digest);
+    }
     if (!X509_REQ_sign(req, pkey, md)) {
 	ossl_raise(eX509ReqError, NULL);
     }
@@ -377,13 +384,13 @@
 	OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Attr);
     }
     GetX509Req(self, req);
-    while ((attr = X509_REQ_delete_attr(req, 0)))
-	X509_ATTRIBUTE_free(attr);
+    for (i = X509_REQ_get_attr_count(req); i > 0; i--)
+        X509_ATTRIBUTE_free(X509_REQ_delete_attr(req, 0));
     for (i=0;i<RARRAY_LEN(ary); i++) {
 	item = RARRAY_AREF(ary, i);
 	attr = GetX509AttrPtr(item);
 	if (!X509_REQ_add1_attr(req, attr)) {
-	    ossl_raise(eX509ReqError, NULL);
+	    ossl_raise(eX509ReqError, "X509_REQ_add1_attr");
 	}
     }
     return ary;
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509revoked.c ruby-2.5.9/ext/openssl/ossl_x509revoked.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509revoked.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509revoked.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -28,7 +28,7 @@
  * Classes
  */
 VALUE cX509Rev;
-VALUE eX509RevError;
+static VALUE eX509RevError;
 
 static void
 ossl_x509rev_free(void *ptr)
@@ -41,7 +41,7 @@
     {
 	0, ossl_x509rev_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -223,13 +223,13 @@
 	OSSL_Check_Kind(RARRAY_AREF(ary, i), cX509Ext);
     }
     GetX509Rev(self, rev);
-    while ((ext = X509_REVOKED_delete_ext(rev, 0)))
-	X509_EXTENSION_free(ext);
+    for (i = X509_REVOKED_get_ext_count(rev); i > 0; i--)
+        X509_EXTENSION_free(X509_REVOKED_delete_ext(rev, 0));
     for (i=0; i<RARRAY_LEN(ary); i++) {
 	item = RARRAY_AREF(ary, i);
 	ext = GetX509ExtPtr(item);
 	if(!X509_REVOKED_add_ext(rev, ext, -1)) {
-	    ossl_raise(eX509RevError, NULL);
+	    ossl_raise(eX509RevError, "X509_REVOKED_add_ext");
 	}
     }
 
diff -ruN ruby-2.5.9.orig/ext/openssl/ossl_x509store.c ruby-2.5.9/ext/openssl/ossl_x509store.c
--- ruby-2.5.9.orig/ext/openssl/ossl_x509store.c	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ossl_x509store.c	2025-01-29 19:08:02.379421304 +0100
@@ -5,7 +5,7 @@
  */
 /*
  * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
+ * (See the file 'COPYING'.)
  */
 #include "ossl.h"
 
@@ -52,8 +52,15 @@
 };
 
 static VALUE
-call_verify_cb_proc(struct ossl_verify_cb_args *args)
+ossl_x509stctx_new_i(VALUE arg)
 {
+    return ossl_x509stctx_new((X509_STORE_CTX *)arg);
+}
+
+static VALUE
+call_verify_cb_proc(VALUE arg)
+{
+    struct ossl_verify_cb_args *args = (struct ossl_verify_cb_args *)arg;
     return rb_funcall(args->proc, rb_intern("call"), 2,
 		      args->preverify_ok, args->store_ctx);
 }
@@ -69,7 +76,7 @@
 	return ok;
 
     ret = Qfalse;
-    rctx = rb_protect((VALUE(*)(VALUE))ossl_x509stctx_new, (VALUE)ctx, &state);
+    rctx = rb_protect(ossl_x509stctx_new_i, (VALUE)ctx, &state);
     if (state) {
 	rb_set_errinfo(Qnil);
 	rb_warn("StoreContext initialization failure");
@@ -78,7 +85,7 @@
 	args.proc = proc;
 	args.preverify_ok = ok ? Qtrue : Qfalse;
 	args.store_ctx = rctx;
-	ret = rb_protect((VALUE(*)(VALUE))call_verify_cb_proc, (VALUE)&args, &state);
+	ret = rb_protect(call_verify_cb_proc, (VALUE)&args, &state);
 	if (state) {
 	    rb_set_errinfo(Qnil);
 	    rb_warn("exception in verify_callback is ignored");
@@ -101,9 +108,19 @@
 /*
  * Classes
  */
-VALUE cX509Store;
-VALUE cX509StoreContext;
-VALUE eX509StoreError;
+static VALUE cX509Store;
+static VALUE cX509StoreContext;
+static VALUE eX509StoreError;
+
+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));
+}
 
 static void
 ossl_x509store_free(void *ptr)
@@ -114,9 +131,9 @@
 static const rb_data_type_t ossl_x509store_type = {
     "OpenSSL/X509/STORE",
     {
-	0, ossl_x509store_free,
+        ossl_x509store_mark, ossl_x509store_free,
     },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
 };
 
 /*
@@ -157,9 +174,8 @@
     VALUE obj;
 
     obj = NewX509Store(klass);
-    if((store = X509_STORE_new()) == NULL){
-        ossl_raise(eX509StoreError, NULL);
-    }
+    if ((store = X509_STORE_new()) == NULL)
+        ossl_raise(eX509StoreError, "X509_STORE_new");
     SetX509Store(obj, store);
 
     return obj;
@@ -174,8 +190,9 @@
     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;
 }
@@ -192,8 +209,9 @@
 {
     X509_STORE *store;
 
-/* BUG: This method takes any number of arguments but appears to ignore them. */
     GetX509Store(self, store);
+    if (argc != 0)
+        rb_warn("OpenSSL::X509::Store.new does not take any arguments");
 #if !defined(HAVE_OPAQUE_OPENSSL)
     /* [Bug #405] [Bug #1678] [Bug #3000]; already fixed? */
     store->ex_data.sk = NULL;
@@ -205,7 +223,6 @@
     rb_iv_set(self, "@error", Qnil);
     rb_iv_set(self, "@error_string", Qnil);
     rb_iv_set(self, "@chain", Qnil);
-    rb_iv_set(self, "@time", Qnil);
 
     return self;
 }
@@ -214,8 +231,16 @@
  * call-seq:
  *   store.flags = flags
  *
- * Sets _flags_ to the Store. _flags_ consists of zero or more of the constants
- * defined in with name V_FLAG_* or'ed together.
+ * Sets the default flags used by certificate chain verification performed with
+ * the Store.
+ *
+ * _flags_ consists of zero or more of the constants defined in OpenSSL::X509
+ * with name V_FLAG_* or'ed together.
+ *
+ * OpenSSL::X509::StoreContext#flags= can be used to change the flags for a
+ * single verification operation.
+ *
+ * See also the man page X509_VERIFY_PARAM_set_flags(3).
  */
 static VALUE
 ossl_x509store_set_flags(VALUE self, VALUE flags)
@@ -233,9 +258,9 @@
  * call-seq:
  *   store.purpose = purpose
  *
- * Sets the store's purpose to _purpose_. If specified, the verifications on
- * the store will check every untrusted certificate's extensions are consistent
- * with the purpose. The purpose is specified by constants:
+ * Sets the store's default verification purpose. If specified,
+ * the verifications on the store will check every certificate's extensions are
+ * consistent with the purpose. The purpose is specified by constants:
  *
  * * X509::PURPOSE_SSL_CLIENT
  * * X509::PURPOSE_SSL_SERVER
@@ -246,6 +271,11 @@
  * * X509::PURPOSE_ANY
  * * X509::PURPOSE_OCSP_HELPER
  * * X509::PURPOSE_TIMESTAMP_SIGN
+ *
+ * OpenSSL::X509::StoreContext#purpose= can be used to change the value for a
+ * single verification operation.
+ *
+ * See also the man page X509_VERIFY_PARAM_set_purpose(3).
  */
 static VALUE
 ossl_x509store_set_purpose(VALUE self, VALUE purpose)
@@ -262,6 +292,14 @@
 /*
  * call-seq:
  *   store.trust = trust
+ *
+ * Sets the default trust settings used by the certificate verification with
+ * the store.
+ *
+ * OpenSSL::X509::StoreContext#trust= can be used to change the value for a
+ * single verification operation.
+ *
+ * See also the man page X509_VERIFY_PARAM_set_trust(3).
  */
 static VALUE
 ossl_x509store_set_trust(VALUE self, VALUE trust)
@@ -279,12 +317,27 @@
  * call-seq:
  *   store.time = time
  *
- * Sets the time to be used in verifications.
+ * Sets the time to be used in the certificate verifications with the store.
+ * By default, if not specified, the current system time is used.
+ *
+ * OpenSSL::X509::StoreContext#time= can be used to change the value for a
+ * single verification operation.
+ *
+ * See also the man page X509_VERIFY_PARAM_set_time(3).
  */
 static VALUE
 ossl_x509store_set_time(VALUE self, VALUE time)
 {
-    rb_iv_set(self, "@time", time);
+    X509_STORE *store;
+    X509_VERIFY_PARAM *param;
+
+    GetX509Store(self, store);
+#ifdef HAVE_X509_STORE_GET0_PARAM
+    param = X509_STORE_get0_param(store);
+#else
+    param = store->param;
+#endif
+    X509_VERIFY_PARAM_set_time(param, NUM2LONG(rb_Integer(time)));
     return time;
 }
 
@@ -295,24 +348,23 @@
  * Adds the certificates in _file_ to the certificate store. _file_ is the path
  * to the file, and the file contains one or more certificates in PEM format
  * concatenated together.
+ *
+ * See also the man page X509_LOOKUP_file(3).
  */
 static VALUE
 ossl_x509store_add_file(VALUE self, VALUE file)
 {
     X509_STORE *store;
     X509_LOOKUP *lookup;
-    char *path = NULL;
+    const char *path;
 
-    if(file != Qnil){
-	rb_check_safe_obj(file);
-	path = StringValueCStr(file);
-    }
     GetX509Store(self, store);
+    path = StringValueCStr(file);
     lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
-    if(lookup == NULL) ossl_raise(eX509StoreError, NULL);
-    if(X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1){
-        ossl_raise(eX509StoreError, NULL);
-    }
+    if (!lookup)
+        ossl_raise(eX509StoreError, "X509_STORE_add_lookup");
+    if (X509_LOOKUP_load_file(lookup, path, X509_FILETYPE_PEM) != 1)
+        ossl_raise(eX509StoreError, "X509_LOOKUP_load_file");
 #if OPENSSL_VERSION_NUMBER < 0x10101000 || defined(LIBRESSL_VERSION_NUMBER)
     /*
      * X509_load_cert_crl_file() which is called from X509_LOOKUP_load_file()
@@ -331,24 +383,23 @@
  *   store.add_path(path) -> self
  *
  * Adds _path_ as the hash dir to be looked up by the store.
+ *
+ * See also the man page X509_LOOKUP_hash_dir(3).
  */
 static VALUE
 ossl_x509store_add_path(VALUE self, VALUE dir)
 {
     X509_STORE *store;
     X509_LOOKUP *lookup;
-    char *path = NULL;
+    const char *path;
 
-    if(dir != Qnil){
-	rb_check_safe_obj(dir);
-	path = StringValueCStr(dir);
-    }
     GetX509Store(self, store);
+    path = StringValueCStr(dir);
     lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir());
-    if(lookup == NULL) ossl_raise(eX509StoreError, NULL);
-    if(X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1){
-        ossl_raise(eX509StoreError, NULL);
-    }
+    if (!lookup)
+        ossl_raise(eX509StoreError, "X509_STORE_add_lookup");
+    if (X509_LOOKUP_add_dir(lookup, path, X509_FILETYPE_PEM) != 1)
+        ossl_raise(eX509StoreError, "X509_LOOKUP_add_dir");
 
     return self;
 }
@@ -363,6 +414,8 @@
  *
  * * OpenSSL::X509::DEFAULT_CERT_FILE
  * * OpenSSL::X509::DEFAULT_CERT_DIR
+ *
+ * See also the man page X509_STORE_set_default_paths(3).
  */
 static VALUE
 ossl_x509store_set_default_paths(VALUE self)
@@ -370,18 +423,19 @@
     X509_STORE *store;
 
     GetX509Store(self, store);
-    if (X509_STORE_set_default_paths(store) != 1){
-        ossl_raise(eX509StoreError, NULL);
-    }
+    if (X509_STORE_set_default_paths(store) != 1)
+        ossl_raise(eX509StoreError, "X509_STORE_set_default_paths");
 
     return Qnil;
 }
 
 /*
  * call-seq:
- *   store.add_cert(cert)
+ *   store.add_cert(cert) -> self
  *
  * Adds the OpenSSL::X509::Certificate _cert_ to the certificate store.
+ *
+ * See also the man page X509_STORE_add_cert(3).
  */
 static VALUE
 ossl_x509store_add_cert(VALUE self, VALUE arg)
@@ -391,9 +445,8 @@
 
     cert = GetX509CertPtr(arg); /* NO NEED TO DUP */
     GetX509Store(self, store);
-    if (X509_STORE_add_cert(store, cert) != 1){
-        ossl_raise(eX509StoreError, NULL);
-    }
+    if (X509_STORE_add_cert(store, cert) != 1)
+        ossl_raise(eX509StoreError, "X509_STORE_add_cert");
 
     return self;
 }
@@ -403,6 +456,8 @@
  *   store.add_crl(crl) -> self
  *
  * Adds the OpenSSL::X509::CRL _crl_ to the store.
+ *
+ * See also the man page X509_STORE_add_crl(3).
  */
 static VALUE
 ossl_x509store_add_crl(VALUE self, VALUE arg)
@@ -412,9 +467,8 @@
 
     crl = GetX509CRLPtr(arg); /* NO NEED TO DUP */
     GetX509Store(self, store);
-    if (X509_STORE_add_crl(store, crl) != 1){
-        ossl_raise(eX509StoreError, NULL);
-    }
+    if (X509_STORE_add_crl(store, crl) != 1)
+        ossl_raise(eX509StoreError, "X509_STORE_add_crl");
 
     return self;
 }
@@ -459,23 +513,19 @@
 }
 
 /*
- * Public Functions
- */
-static void ossl_x509stctx_free(void*);
-
-
-static const rb_data_type_t ossl_x509stctx_type = {
-    "OpenSSL/X509/STORE_CTX",
-    {
-	0, ossl_x509stctx_free,
-    },
-    0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
-};
-
-/*
  * Private functions
  */
 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));
+}
+
+static void
 ossl_x509stctx_free(void *ptr)
 {
     X509_STORE_CTX *ctx = ptr;
@@ -486,6 +536,14 @@
     X509_STORE_CTX_free(ctx);
 }
 
+static const rb_data_type_t ossl_x509stctx_type = {
+    "OpenSSL/X509/STORE_CTX",
+    {
+        ossl_x509stctx_mark, ossl_x509stctx_free,
+    },
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
+};
+
 static VALUE
 ossl_x509stctx_alloc(VALUE klass)
 {
@@ -493,9 +551,8 @@
     VALUE obj;
 
     obj = NewX509StCtx(klass);
-    if((ctx = X509_STORE_CTX_new()) == NULL){
-        ossl_raise(eX509StoreError, NULL);
-    }
+    if ((ctx = X509_STORE_CTX_new()) == NULL)
+        ossl_raise(eX509StoreError, "X509_STORE_CTX_new");
     SetX509StCtx(obj, ctx);
 
     return obj;
@@ -515,32 +572,40 @@
 static VALUE ossl_x509stctx_set_flags(VALUE, VALUE);
 static VALUE ossl_x509stctx_set_purpose(VALUE, VALUE);
 static VALUE ossl_x509stctx_set_trust(VALUE, VALUE);
-static VALUE ossl_x509stctx_set_time(VALUE, VALUE);
 
 /*
  * call-seq:
- *   StoreContext.new(store, cert = nil, chain = nil)
+ *   StoreContext.new(store, cert = nil, untrusted = nil)
+ *
+ * Sets up a StoreContext for a verification of the X.509 certificate _cert_.
  */
 static VALUE
 ossl_x509stctx_initialize(int argc, VALUE *argv, VALUE self)
 {
-    VALUE store, cert, chain, t;
+    VALUE store, cert, chain;
     X509_STORE_CTX *ctx;
     X509_STORE *x509st;
     X509 *x509 = NULL;
     STACK_OF(X509) *x509s = NULL;
+    int state;
 
     rb_scan_args(argc, argv, "12", &store, &cert, &chain);
     GetX509StCtx(self, ctx);
     GetX509Store(store, x509st);
-    if(!NIL_P(cert)) x509 = DupX509CertPtr(cert); /* NEED TO DUP */
-    if(!NIL_P(chain)) x509s = ossl_x509_ary2sk(chain);
-    if(X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){
+    if (!NIL_P(cert))
+        x509 = DupX509CertPtr(cert); /* NEED TO DUP */
+    if (!NIL_P(chain)) {
+        x509s = ossl_protect_x509_ary2sk(chain, &state);
+        if (state) {
+            X509_free(x509);
+            rb_jump_tag(state);
+        }
+    }
+    if (X509_STORE_CTX_init(ctx, x509st, x509, x509s) != 1){
+        X509_free(x509);
         sk_X509_pop_free(x509s, X509_free);
-        ossl_raise(eX509StoreError, NULL);
+        ossl_raise(eX509StoreError, "X509_STORE_CTX_init");
     }
-    if (!NIL_P(t = rb_iv_get(store, "@time")))
-	ossl_x509stctx_set_time(self, t);
     rb_iv_set(self, "@verify_callback", rb_iv_get(store, "@verify_callback"));
     rb_iv_set(self, "@cert", cert);
 
@@ -550,6 +615,10 @@
 /*
  * call-seq:
  *   stctx.verify -> true | false
+ *
+ * Performs the certificate verification using the parameters set to _stctx_.
+ *
+ * See also the man page X509_verify_cert(3).
  */
 static VALUE
 ossl_x509stctx_verify(VALUE self)
@@ -557,53 +626,50 @@
     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:
-	return Qtrue;
+        return Qtrue;
       case 0:
-	ossl_clear_error();
-	return Qfalse;
+        ossl_clear_error();
+        return Qfalse;
       default:
-	ossl_raise(eX509CertError, NULL);
+        ossl_raise(eX509StoreError, "X509_verify_cert");
     }
 }
 
 /*
  * call-seq:
- *   stctx.chain -> Array of X509::Certificate
+ *   stctx.chain -> nil | Array of X509::Certificate
+ *
+ * Returns the verified chain.
+ *
+ * See also the man page X509_STORE_CTX_set0_verified_chain(3).
  */
 static VALUE
 ossl_x509stctx_get_chain(VALUE self)
 {
     X509_STORE_CTX *ctx;
-    STACK_OF(X509) *chain;
-    X509 *x509;
-    int i, num;
-    VALUE ary;
+    const STACK_OF(X509) *chain;
 
     GetX509StCtx(self, ctx);
-    if((chain = X509_STORE_CTX_get0_chain(ctx)) == NULL){
-        return Qnil;
-    }
-    if((num = sk_X509_num(chain)) < 0){
-	OSSL_Debug("certs in chain < 0???");
-	return rb_ary_new();
-    }
-    ary = rb_ary_new2(num);
-    for(i = 0; i < num; i++) {
-	x509 = sk_X509_value(chain, i);
-	rb_ary_push(ary, ossl_x509_new(x509));
-    }
-
-    return ary;
+    chain = X509_STORE_CTX_get0_chain(ctx);
+    if (!chain)
+        return Qnil; /* Could be an empty array instead? */
+    return ossl_x509_sk2ary(chain);
 }
 
 /*
  * call-seq:
  *   stctx.error -> Integer
+ *
+ * Returns the error code of _stctx_. This is typically called after #verify
+ * is done, or from the verification callback set to
+ * OpenSSL::X509::Store#verify_callback=.
+ *
+ * See also the man page X509_STORE_CTX_get_error(3).
  */
 static VALUE
 ossl_x509stctx_get_err(VALUE self)
@@ -618,6 +684,11 @@
 /*
  * call-seq:
  *   stctx.error = error_code
+ *
+ * Sets the error code of _stctx_. This is used by the verification callback
+ * set to OpenSSL::X509::Store#verify_callback=.
+ *
+ * See also the man page X509_STORE_CTX_set_error(3).
  */
 static VALUE
 ossl_x509stctx_set_error(VALUE self, VALUE err)
@@ -634,7 +705,10 @@
  * call-seq:
  *   stctx.error_string -> String
  *
- * Returns the error string corresponding to the error code retrieved by #error.
+ * Returns the human readable error string corresponding to the error code
+ * retrieved by #error.
+ *
+ * See also the man page X509_verify_cert_error_string(3).
  */
 static VALUE
 ossl_x509stctx_get_err_string(VALUE self)
@@ -651,6 +725,10 @@
 /*
  * call-seq:
  *   stctx.error_depth -> Integer
+ *
+ * Returns the depth of the chain. This is used in combination with #error.
+ *
+ * See also the man page X509_STORE_CTX_get_error_depth(3).
  */
 static VALUE
 ossl_x509stctx_get_err_depth(VALUE self)
@@ -665,6 +743,10 @@
 /*
  * call-seq:
  *   stctx.current_cert -> X509::Certificate
+ *
+ * Returns the certificate which caused the error.
+ *
+ * See also the man page X509_STORE_CTX_get_current_cert(3).
  */
 static VALUE
 ossl_x509stctx_get_curr_cert(VALUE self)
@@ -679,6 +761,10 @@
 /*
  * call-seq:
  *   stctx.current_crl -> X509::CRL
+ *
+ * Returns the CRL which caused the error.
+ *
+ * See also the man page X509_STORE_CTX_get_current_crl(3).
  */
 static VALUE
 ossl_x509stctx_get_curr_crl(VALUE self)
@@ -698,7 +784,10 @@
  * call-seq:
  *   stctx.flags = flags
  *
- * Sets the verification flags to the context. See Store#flags=.
+ * Sets the verification flags to the context. This overrides the default value
+ * set by Store#flags=.
+ *
+ * See also the man page X509_VERIFY_PARAM_set_flags(3).
  */
 static VALUE
 ossl_x509stctx_set_flags(VALUE self, VALUE flags)
@@ -716,7 +805,10 @@
  * call-seq:
  *   stctx.purpose = purpose
  *
- * Sets the purpose of the context. See Store#purpose=.
+ * Sets the purpose of the context. This overrides the default value set by
+ * Store#purpose=.
+ *
+ * See also the man page X509_VERIFY_PARAM_set_purpose(3).
  */
 static VALUE
 ossl_x509stctx_set_purpose(VALUE self, VALUE purpose)
@@ -733,6 +825,11 @@
 /*
  * call-seq:
  *   stctx.trust = trust
+ *
+ * Sets the trust settings of the context. This overrides the default value set
+ * by Store#trust=.
+ *
+ * See also the man page X509_VERIFY_PARAM_set_trust(3).
  */
 static VALUE
 ossl_x509stctx_set_trust(VALUE self, VALUE trust)
@@ -751,6 +848,8 @@
  *   stctx.time = time
  *
  * Sets the time used in the verification. If not set, the current time is used.
+ *
+ * See also the man page X509_VERIFY_PARAM_set_time(3).
  */
 static VALUE
 ossl_x509stctx_set_time(VALUE self, VALUE time)
@@ -826,23 +925,37 @@
     cX509Store = rb_define_class_under(mX509, "Store", rb_cObject);
     /*
      * The callback for additional certificate verification. It is invoked for
-     * each untrusted certificate in the chain.
+     * each certificate in the chain and can be used to implement custom
+     * certificate verification conditions.
      *
      * The callback is invoked with two values, a boolean that indicates if the
      * pre-verification by OpenSSL has succeeded or not, and the StoreContext in
-     * use. The callback must return either true or false.
+     * use.
+     *
+     * The callback can use StoreContext#error= to change the error code as
+     * needed. The callback must return either true or false.
+     *
+     * NOTE: any exception raised within the callback will be ignored.
+     *
+     * See also the man page X509_STORE_CTX_set_verify_cb(3).
      */
     rb_attr(cX509Store, rb_intern("verify_callback"), 1, 0, Qfalse);
     /*
      * The error code set by the last call of #verify.
+     *
+     * See also StoreContext#error.
      */
     rb_attr(cX509Store, rb_intern("error"), 1, 0, Qfalse);
     /*
      * The description for the error code set by the last call of #verify.
+     *
+     * See also StoreContext#error_string.
      */
     rb_attr(cX509Store, rb_intern("error_string"), 1, 0, Qfalse);
     /*
      * The certificate chain constructed by the last call of #verify.
+     *
+     * See also StoreContext#chain.
      */
     rb_attr(cX509Store, rb_intern("chain"), 1, 0, Qfalse);
     rb_define_alloc_func(cX509Store, ossl_x509store_alloc);
diff -ruN ruby-2.5.9.orig/ext/openssl/ruby_missing.h ruby-2.5.9/ext/openssl/ruby_missing.h
--- ruby-2.5.9.orig/ext/openssl/ruby_missing.h	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/ext/openssl/ruby_missing.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,24 +0,0 @@
-/*
- * 'OpenSSL for Ruby' project
- * Copyright (C) 2001-2003  Michal Rokos <m.rokos@sh.cvut.cz>
- * All rights reserved.
- */
-/*
- * This program is licensed under the same licence as Ruby.
- * (See the file 'LICENCE'.)
- */
-#if !defined(_OSSL_RUBY_MISSING_H_)
-#define _OSSL_RUBY_MISSING_H_
-
-/* Ruby 2.4 */
-#ifndef RB_INTEGER_TYPE_P
-# define RB_INTEGER_TYPE_P(obj) (RB_FIXNUM_P(obj) || RB_TYPE_P(obj, T_BIGNUM))
-#endif
-
-/* Ruby 2.5 */
-#ifndef ST2FIX
-# define RB_ST2FIX(h) LONG2FIX((long)(h))
-# define ST2FIX(h) RB_ST2FIX(h)
-#endif
-
-#endif /* _OSSL_RUBY_MISSING_H_ */
diff -ruN ruby-2.5.9.orig/lib/openssl/asn1.rb ruby-2.5.9/lib/openssl/asn1.rb
--- ruby-2.5.9.orig/lib/openssl/asn1.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/asn1.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,188 @@
+# frozen_string_literal: true
+#--
+#
+# = Ruby-space definitions that completes C-space funcs for ASN.1
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'COPYING'.)
+#++
+
+module OpenSSL
+  module ASN1
+    class ASN1Data
+      #
+      # Carries the value of a ASN.1 type.
+      # Please confer Constructive and Primitive for the mappings between
+      # ASN.1 data types and Ruby classes.
+      #
+      attr_accessor :value
+
+      # An Integer representing the tag number of this ASN1Data. Never +nil+.
+      attr_accessor :tag
+
+      # A Symbol representing the tag class of this ASN1Data. Never +nil+.
+      # See ASN1Data for possible values.
+      attr_accessor :tag_class
+
+      #
+      # Never +nil+. A boolean value indicating whether the encoding uses
+      # indefinite length (in the case of parsing) or whether an indefinite
+      # length form shall be used (in the encoding case).
+      # In DER, every value uses definite length form. But in scenarios where
+      # large amounts of data need to be transferred it might be desirable to
+      # have some kind of streaming support available.
+      # For example, huge OCTET STRINGs are preferably sent in smaller-sized
+      # chunks, each at a time.
+      # This is possible in BER by setting the length bytes of an encoding
+      # to zero and by this indicating that the following value will be
+      # sent in chunks. Indefinite length encodings are always constructed.
+      # The end of such a stream of chunks is indicated by sending a EOC
+      # (End of Content) tag. SETs and SEQUENCEs may use an indefinite length
+      # encoding, but also primitive types such as e.g. OCTET STRINGS or
+      # BIT STRINGS may leverage this functionality (cf. ITU-T X.690).
+      #
+      attr_accessor :indefinite_length
+
+      alias infinite_length indefinite_length
+      alias infinite_length= indefinite_length=
+
+      #
+      # :call-seq:
+      #    OpenSSL::ASN1::ASN1Data.new(value, tag, tag_class) => ASN1Data
+      #
+      # _value_: Please have a look at Constructive and Primitive to see how Ruby
+      # types are mapped to ASN.1 types and vice versa.
+      #
+      # _tag_: An Integer indicating the tag number.
+      #
+      # _tag_class_: A Symbol indicating the tag class. Please cf. ASN1 for
+      # possible values.
+      #
+      # == Example
+      #   asn1_int = OpenSSL::ASN1Data.new(42, 2, :UNIVERSAL) # => Same as OpenSSL::ASN1::Integer.new(42)
+      #   tagged_int = OpenSSL::ASN1Data.new(42, 0, :CONTEXT_SPECIFIC) # implicitly 0-tagged INTEGER
+      #
+      def initialize(value, tag, tag_class)
+        raise ASN1Error, "invalid tag class" unless tag_class.is_a?(Symbol)
+
+        @tag = tag
+        @value = value
+        @tag_class = tag_class
+        @indefinite_length = false
+      end
+    end
+
+    module TaggedASN1Data
+      #
+      # May be used as a hint for encoding a value either implicitly or
+      # explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+.
+      # _tagging_ is not set when a ASN.1 structure is parsed using
+      # OpenSSL::ASN1.decode.
+      #
+      attr_accessor :tagging
+
+      # :call-seq:
+      #    OpenSSL::ASN1::Primitive.new(value [, tag, tagging, tag_class ]) => Primitive
+      #
+      # _value_: is mandatory.
+      #
+      # _tag_: optional, may be specified for tagged values. If no _tag_ is
+      # specified, the UNIVERSAL tag corresponding to the Primitive sub-class
+      # is used by default.
+      #
+      # _tagging_: may be used as an encoding hint to encode a value either
+      # explicitly or implicitly, see ASN1 for possible values.
+      #
+      # _tag_class_: if _tag_ and _tagging_ are +nil+ then this is set to
+      # +:UNIVERSAL+ by default. If either _tag_ or _tagging_ are set then
+      # +:CONTEXT_SPECIFIC+ is used as the default. For possible values please
+      # cf. ASN1.
+      #
+      # == Example
+      #   int = OpenSSL::ASN1::Integer.new(42)
+      #   zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :IMPLICIT)
+      #   private_explicit_zero_tagged_int = OpenSSL::ASN1::Integer.new(42, 0, :EXPLICIT, :PRIVATE)
+      #
+      def initialize(value, tag = nil, tagging = nil, tag_class = nil)
+        tag ||= ASN1.take_default_tag(self.class)
+
+        raise ASN1Error, "must specify tag number" unless tag
+
+        if tagging
+          raise ASN1Error, "invalid tagging method" unless tagging.is_a?(Symbol)
+        end
+
+        tag_class ||= tagging ? :CONTEXT_SPECIFIC : :UNIVERSAL
+
+        raise ASN1Error, "invalid tag class" unless tag_class.is_a?(Symbol)
+
+        @tagging = tagging
+        super(value ,tag, tag_class)
+      end
+    end
+
+    class Primitive < ASN1Data
+      include TaggedASN1Data
+
+      undef_method :indefinite_length=
+      undef_method :infinite_length=
+    end
+
+    class Constructive < ASN1Data
+      include TaggedASN1Data
+      include Enumerable
+
+      # :call-seq:
+      #    asn1_ary.each { |asn1| block } => asn1_ary
+      #
+      # Calls the given block once for each element in self, passing that element
+      # as parameter _asn1_. If no block is given, an enumerator is returned
+      # instead.
+      #
+      # == Example
+      #   asn1_ary.each do |asn1|
+      #     puts asn1
+      #   end
+      #
+      def each(&blk)
+        @value.each(&blk)
+
+        self
+      end
+    end
+
+    class Boolean < Primitive ; end
+    class Integer < Primitive ; end
+    class Enumerated < Primitive ; end
+
+    class BitString < Primitive
+      attr_accessor :unused_bits
+
+      def initialize(*)
+        super
+
+        @unused_bits = 0
+      end
+    end
+
+    class EndOfContent < ASN1Data
+      def initialize
+        super("", 0, :UNIVERSAL)
+      end
+    end
+
+    # :nodoc:
+    def self.take_default_tag(klass)
+      tag = CLASS_TAG_MAP[klass]
+
+      return tag if tag
+
+      sklass = klass.superclass
+
+      return unless sklass
+
+      take_default_tag(sklass)
+    end
+  end
+end
diff -ruN ruby-2.5.9.orig/lib/openssl/bn.rb ruby-2.5.9/lib/openssl/bn.rb
--- ruby-2.5.9.orig/lib/openssl/bn.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/bn.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+#--
+#
+# = Ruby-space definitions that completes C-space funcs for BN
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'COPYING'.)
+#++
+
+module OpenSSL
+  class BN
+    include Comparable
+
+    def pretty_print(q)
+      q.object_group(self) {
+        q.text ' '
+        q.text to_i.to_s
+      }
+    end
+  end # BN
+end # OpenSSL
+
+##
+#--
+# Add double dispatch to Integer
+#++
+class Integer
+  # Casts an Integer as an OpenSSL::BN
+  #
+  # See `man bn` for more info.
+  def to_bn
+    OpenSSL::BN::new(self)
+  end
+end # Integer
diff -ruN ruby-2.5.9.orig/lib/openssl/buffering.rb ruby-2.5.9/lib/openssl/buffering.rb
--- ruby-2.5.9.orig/lib/openssl/buffering.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/buffering.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,499 @@
+# coding: binary
+# frozen_string_literal: true
+#--
+#= Info
+#  'OpenSSL for Ruby 2' project
+#  Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
+#  All rights reserved.
+#
+#= Licence
+#  This program is licensed under the same licence as Ruby.
+#  (See the file 'COPYING'.)
+#++
+
+##
+# OpenSSL IO buffering mix-in module.
+#
+# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO.
+#
+# You typically won't use this module directly, you can see it implemented in
+# OpenSSL::SSL::SSLSocket.
+
+module OpenSSL::Buffering
+  include Enumerable
+
+  # A buffer which will retain binary encoding.
+  class Buffer < String
+    BINARY = Encoding::BINARY
+
+    def initialize
+      super
+
+      force_encoding(BINARY)
+    end
+
+    def << string
+      if string.encoding == BINARY
+        super(string)
+      else
+        super(string.b)
+      end
+
+      return self
+    end
+
+    alias concat <<
+  end
+
+  ##
+  # The "sync mode" of the SSLSocket.
+  #
+  # See IO#sync for full details.
+
+  attr_accessor :sync
+
+  ##
+  # Default size to read from or write to the SSLSocket for buffer operations.
+
+  BLOCK_SIZE = 1024*16
+
+  ##
+  # Creates an instance of OpenSSL's buffering IO module.
+
+  def initialize(*)
+    super
+    @eof = false
+    @rbuffer = Buffer.new
+    @sync = @io.sync
+  end
+
+  #
+  # for reading.
+  #
+  private
+
+  ##
+  # Fills the buffer from the underlying SSLSocket
+
+  def fill_rbuff
+    begin
+      @rbuffer << self.sysread(BLOCK_SIZE)
+    rescue Errno::EAGAIN
+      retry
+    rescue EOFError
+      @eof = true
+    end
+  end
+
+  ##
+  # Consumes _size_ bytes from the buffer
+
+  def consume_rbuff(size=nil)
+    if @rbuffer.empty?
+      nil
+    else
+      size = @rbuffer.size unless size
+      @rbuffer.slice!(0, size)
+    end
+  end
+
+  public
+
+  # call-seq:
+  #   ssl.getbyte => 81
+  #
+  # Get the next 8bit byte from `ssl`.  Returns `nil` on EOF
+  def getbyte
+    read(1)&.ord
+  end
+
+  # Get the next 8bit byte. Raises EOFError on EOF
+  def readbyte
+    raise EOFError if eof?
+    getbyte
+  end
+
+  ##
+  # Reads _size_ bytes from the stream.  If _buf_ is provided it must
+  # reference a string which will receive the data.
+  #
+  # See IO#read for full details.
+
+  def read(size=nil, buf=nil)
+    if size == 0
+      if buf
+        buf.clear
+        return buf
+      else
+        return ""
+      end
+    end
+    until @eof
+      break if size && size <= @rbuffer.size
+      fill_rbuff
+    end
+    ret = consume_rbuff(size) || ""
+    if buf
+      buf.replace(ret)
+      ret = buf
+    end
+    (size && ret.empty?) ? nil : ret
+  end
+
+  ##
+  # Reads at most _maxlen_ bytes from the stream.  If _buf_ is provided it
+  # must reference a string which will receive the data.
+  #
+  # See IO#readpartial for full details.
+
+  def readpartial(maxlen, buf=nil)
+    if maxlen == 0
+      if buf
+        buf.clear
+        return buf
+      else
+        return ""
+      end
+    end
+    if @rbuffer.empty?
+      begin
+        return sysread(maxlen, buf)
+      rescue Errno::EAGAIN
+        retry
+      end
+    end
+    ret = consume_rbuff(maxlen)
+    if buf
+      buf.replace(ret)
+      ret = buf
+    end
+    ret
+  end
+
+  ##
+  # Reads at most _maxlen_ bytes in the non-blocking manner.
+  #
+  # When no data can be read without blocking it raises
+  # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
+  #
+  # IO::WaitReadable means SSL needs to read internally so read_nonblock
+  # should be called again when the underlying IO is readable.
+  #
+  # IO::WaitWritable means SSL needs to write internally so read_nonblock
+  # should be called again after the underlying IO is writable.
+  #
+  # OpenSSL::Buffering#read_nonblock needs two rescue clause as follows:
+  #
+  #   # emulates blocking read (readpartial).
+  #   begin
+  #     result = ssl.read_nonblock(maxlen)
+  #   rescue IO::WaitReadable
+  #     IO.select([io])
+  #     retry
+  #   rescue IO::WaitWritable
+  #     IO.select(nil, [io])
+  #     retry
+  #   end
+  #
+  # Note that one reason that read_nonblock writes to the underlying IO is
+  # when the peer requests a new TLS/SSL handshake.  See openssl the FAQ for
+  # more details.  http://www.openssl.org/support/faq.html
+  #
+  # By specifying a keyword argument _exception_ to +false+, you can indicate
+  # that read_nonblock should not raise an IO::Wait*able exception, but
+  # return the symbol +:wait_writable+ or +:wait_readable+ instead. At EOF,
+  # it will return +nil+ instead of raising EOFError.
+
+  def read_nonblock(maxlen, buf=nil, exception: true)
+    if maxlen == 0
+      if buf
+        buf.clear
+        return buf
+      else
+        return ""
+      end
+    end
+    if @rbuffer.empty?
+      return sysread_nonblock(maxlen, buf, exception: exception)
+    end
+    ret = consume_rbuff(maxlen)
+    if buf
+      buf.replace(ret)
+      ret = buf
+    end
+    ret
+  end
+
+  ##
+  # Reads the next "line" from the stream.  Lines are separated by _eol_.  If
+  # _limit_ is provided the result will not be longer than the given number of
+  # bytes.
+  #
+  # _eol_ may be a String or Regexp.
+  #
+  # Unlike IO#gets the line read will not be assigned to +$_+.
+  #
+  # Unlike IO#gets the separator must be provided if a limit is provided.
+
+  def gets(eol=$/, limit=nil, chomp: false)
+    idx = @rbuffer.index(eol)
+    until @eof
+      break if idx
+      fill_rbuff
+      idx = @rbuffer.index(eol)
+    end
+    if eol.is_a?(Regexp)
+      size = idx ? idx+$&.size : nil
+    else
+      size = idx ? idx+eol.size : nil
+    end
+    if size && limit && limit >= 0
+      size = [size, limit].min
+    end
+    line = consume_rbuff(size)
+    if chomp && line
+      line.chomp!(eol)
+    end
+    line
+  end
+
+  ##
+  # Executes the block for every line in the stream where lines are separated
+  # by _eol_.
+  #
+  # See also #gets
+
+  def each(eol=$/)
+    while line = self.gets(eol)
+      yield line
+    end
+  end
+  alias each_line each
+
+  ##
+  # Reads lines from the stream which are separated by _eol_.
+  #
+  # See also #gets
+
+  def readlines(eol=$/)
+    ary = []
+    while line = self.gets(eol)
+      ary << line
+    end
+    ary
+  end
+
+  ##
+  # Reads a line from the stream which is separated by _eol_.
+  #
+  # Raises EOFError if at end of file.
+
+  def readline(eol=$/)
+    raise EOFError if eof?
+    gets(eol)
+  end
+
+  ##
+  # Reads one character from the stream.  Returns nil if called at end of
+  # file.
+
+  def getc
+    read(1)
+  end
+
+  ##
+  # Calls the given block once for each byte in the stream.
+
+  def each_byte # :yields: byte
+    while c = getc
+      yield(c.ord)
+    end
+  end
+
+  ##
+  # Reads a one-character string from the stream.  Raises an EOFError at end
+  # of file.
+
+  def readchar
+    raise EOFError if eof?
+    getc
+  end
+
+  ##
+  # Pushes character _c_ back onto the stream such that a subsequent buffered
+  # character read will return it.
+  #
+  # Unlike IO#getc multiple bytes may be pushed back onto the stream.
+  #
+  # Has no effect on unbuffered reads (such as #sysread).
+
+  def ungetc(c)
+    @rbuffer[0,0] = c.chr
+  end
+
+  ##
+  # Returns true if the stream is at file which means there is no more data to
+  # be read.
+
+  def eof?
+    fill_rbuff if !@eof && @rbuffer.empty?
+    @eof && @rbuffer.empty?
+  end
+  alias eof eof?
+
+  #
+  # for writing.
+  #
+  private
+
+  ##
+  # Writes _s_ to the buffer.  When the buffer is full or #sync is true the
+  # buffer is flushed to the underlying socket.
+
+  def do_write(s)
+    @wbuffer = Buffer.new unless defined? @wbuffer
+    @wbuffer << s
+    @wbuffer.force_encoding(Encoding::BINARY)
+    @sync ||= false
+    buffer_size = @wbuffer.size
+    if @sync or buffer_size > BLOCK_SIZE
+      nwrote = 0
+      begin
+        while nwrote < buffer_size do
+          begin
+            nwrote += syswrite(@wbuffer[nwrote, buffer_size - nwrote])
+          rescue Errno::EAGAIN
+            retry
+          end
+        end
+      ensure
+        @wbuffer[0, nwrote] = ""
+      end
+    end
+  end
+
+  public
+
+  ##
+  # Writes _s_ to the stream.  If the argument is not a String it will be
+  # converted using +.to_s+ method.  Returns the number of bytes written.
+
+  def write(*s)
+    s.inject(0) do |written, str|
+      do_write(str)
+      written + str.bytesize
+    end
+  end
+
+  ##
+  # Writes _s_ in the non-blocking manner.
+  #
+  # If there is buffered data, it is flushed first.  This may block.
+  #
+  # write_nonblock returns number of bytes written to the SSL connection.
+  #
+  # When no data can be written without blocking it raises
+  # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable.
+  #
+  # IO::WaitReadable means SSL needs to read internally so write_nonblock
+  # should be called again after the underlying IO is readable.
+  #
+  # IO::WaitWritable means SSL needs to write internally so write_nonblock
+  # should be called again after underlying IO is writable.
+  #
+  # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
+  #
+  #   # emulates blocking write.
+  #   begin
+  #     result = ssl.write_nonblock(str)
+  #   rescue IO::WaitReadable
+  #     IO.select([io])
+  #     retry
+  #   rescue IO::WaitWritable
+  #     IO.select(nil, [io])
+  #     retry
+  #   end
+  #
+  # Note that one reason that write_nonblock reads from the underlying IO
+  # is when the peer requests a new TLS/SSL handshake.  See the openssl FAQ
+  # for more details.  http://www.openssl.org/support/faq.html
+  #
+  # By specifying a keyword argument _exception_ to +false+, you can indicate
+  # that write_nonblock should not raise an IO::Wait*able exception, but
+  # return the symbol +:wait_writable+ or +:wait_readable+ instead.
+
+  def write_nonblock(s, exception: true)
+    flush
+    syswrite_nonblock(s, exception: exception)
+  end
+
+  ##
+  # Writes _s_ to the stream.  _s_ will be converted to a String using
+  # +.to_s+ method.
+
+  def <<(s)
+    do_write(s)
+    self
+  end
+
+  ##
+  # Writes _args_ to the stream along with a record separator.
+  #
+  # See IO#puts for full details.
+
+  def puts(*args)
+    s = Buffer.new
+    if args.empty?
+      s << "\n"
+    end
+    args.each{|arg|
+      s << arg.to_s
+      s.sub!(/(?<!\n)\z/, "\n")
+    }
+    do_write(s)
+    nil
+  end
+
+  ##
+  # Writes _args_ to the stream.
+  #
+  # See IO#print for full details.
+
+  def print(*args)
+    s = Buffer.new
+    args.each{ |arg| s << arg.to_s }
+    do_write(s)
+    nil
+  end
+
+  ##
+  # Formats and writes to the stream converting parameters under control of
+  # the format string.
+  #
+  # See Kernel#sprintf for format string details.
+
+  def printf(s, *args)
+    do_write(s % args)
+    nil
+  end
+
+  ##
+  # Flushes buffered data to the SSLSocket.
+
+  def flush
+    osync = @sync
+    @sync = true
+    do_write ""
+    return self
+  ensure
+    @sync = osync
+  end
+
+  ##
+  # Closes the SSLSocket and flushes any unwritten data.
+
+  def close
+    flush rescue nil
+    sysclose
+  end
+end
diff -ruN ruby-2.5.9.orig/lib/openssl/cipher.rb ruby-2.5.9/lib/openssl/cipher.rb
--- ruby-2.5.9.orig/lib/openssl/cipher.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/cipher.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+#--
+# = Ruby-space predefined Cipher subclasses
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'COPYING'.)
+#++
+
+module OpenSSL
+  class Cipher
+    %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name|
+      klass = Class.new(Cipher){
+        define_method(:initialize){|*args|
+          cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" }
+          super(cipher_name.downcase)
+        }
+      }
+      const_set(name, klass)
+    }
+
+    %w(128 192 256).each{|keylen|
+      klass = Class.new(Cipher){
+        define_method(:initialize){|mode = "CBC"|
+          super("aes-#{keylen}-#{mode}".downcase)
+        }
+      }
+      const_set("AES#{keylen}", klass)
+    }
+
+    # call-seq:
+    #   cipher.random_key -> key
+    #
+    # Generate a random key with OpenSSL::Random.random_bytes and sets it to
+    # the cipher, and returns it.
+    #
+    # You must call #encrypt or #decrypt before calling this method.
+    def random_key
+      str = OpenSSL::Random.random_bytes(self.key_len)
+      self.key = str
+    end
+
+    # call-seq:
+    #   cipher.random_iv -> iv
+    #
+    # Generate a random IV with OpenSSL::Random.random_bytes and sets it to the
+    # cipher, and returns it.
+    #
+    # You must call #encrypt or #decrypt before calling this method.
+    def random_iv
+      str = OpenSSL::Random.random_bytes(self.iv_len)
+      self.iv = str
+    end
+
+    # Deprecated.
+    #
+    # This class is only provided for backwards compatibility.
+    # Use OpenSSL::Cipher.
+    class Cipher < Cipher; end
+    deprecate_constant :Cipher
+  end # Cipher
+end # OpenSSL
diff -ruN ruby-2.5.9.orig/lib/openssl/digest.rb ruby-2.5.9/lib/openssl/digest.rb
--- ruby-2.5.9.orig/lib/openssl/digest.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/digest.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+#--
+# = Ruby-space predefined Digest subclasses
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'COPYING'.)
+#++
+
+module OpenSSL
+  class Digest
+
+    # Return the hash value computed with _name_ Digest. _name_ is either the
+    # long name or short name of a supported digest algorithm.
+    #
+    # === Example
+    #
+    #   OpenSSL::Digest.digest("SHA256", "abc")
+
+    def self.digest(name, data)
+      super(data, name)
+    end
+
+    %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512).each do |name|
+      klass = Class.new(self) {
+        define_method(:initialize, ->(data = nil) {super(name, data)})
+      }
+
+      singleton = (class << klass; self; end)
+
+      singleton.class_eval{
+        define_method(:digest) {|data| new.digest(data)}
+        define_method(:hexdigest) {|data| new.hexdigest(data)}
+      }
+
+      const_set(name.tr('-', '_'), klass)
+    end
+
+    # Deprecated.
+    #
+    # This class is only provided for backwards compatibility.
+    # Use OpenSSL::Digest instead.
+    class Digest < Digest; end # :nodoc:
+    deprecate_constant :Digest
+
+  end # Digest
+
+  # Returns a Digest subclass by _name_
+  #
+  #   require 'openssl'
+  #
+  #   OpenSSL::Digest("MD5")
+  #   # => OpenSSL::Digest::MD5
+  #
+  #   Digest("Foo")
+  #   # => NameError: wrong constant name Foo
+
+  def Digest(name)
+    OpenSSL::Digest.const_get(name)
+  end
+
+  module_function :Digest
+
+end # OpenSSL
diff -ruN ruby-2.5.9.orig/lib/openssl/hmac.rb ruby-2.5.9/lib/openssl/hmac.rb
--- ruby-2.5.9.orig/lib/openssl/hmac.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/hmac.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module OpenSSL
+  class HMAC
+    # Securely compare with another HMAC instance in constant time.
+    def ==(other)
+      return false unless HMAC === other
+      return false unless self.digest.bytesize == other.digest.bytesize
+
+      OpenSSL.fixed_length_secure_compare(self.digest, other.digest)
+    end
+
+    # :call-seq:
+    #    hmac.base64digest -> string
+    #
+    # Returns the authentication code an a Base64-encoded string.
+    def base64digest
+      [digest].pack("m0")
+    end
+
+    class << self
+      # :call-seq:
+      #    HMAC.digest(digest, key, data) -> aString
+      #
+      # Returns the authentication code as a binary string. The _digest_ parameter
+      # specifies the digest algorithm to use. This may be a String representing
+      # the algorithm name or an instance of OpenSSL::Digest.
+      #
+      # === Example
+      #  key = 'key'
+      #  data = 'The quick brown fox jumps over the lazy dog'
+      #
+      #  hmac = OpenSSL::HMAC.digest('SHA1', key, data)
+      #  #=> "\xDE|\x9B\x85\xB8\xB7\x8A\xA6\xBC\x8Az6\xF7\n\x90p\x1C\x9D\xB4\xD9"
+      def digest(digest, key, data)
+        hmac = new(key, digest)
+        hmac << data
+        hmac.digest
+      end
+
+      # :call-seq:
+      #    HMAC.hexdigest(digest, key, data) -> aString
+      #
+      # Returns the authentication code as a hex-encoded string. The _digest_
+      # parameter specifies the digest algorithm to use. This may be a String
+      # representing the algorithm name or an instance of OpenSSL::Digest.
+      #
+      # === Example
+      #  key = 'key'
+      #  data = 'The quick brown fox jumps over the lazy dog'
+      #
+      #  hmac = OpenSSL::HMAC.hexdigest('SHA1', key, data)
+      #  #=> "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"
+      def hexdigest(digest, key, data)
+        hmac = new(key, digest)
+        hmac << data
+        hmac.hexdigest
+      end
+
+      # :call-seq:
+      #    HMAC.base64digest(digest, key, data) -> aString
+      #
+      # Returns the authentication code as a Base64-encoded string. The _digest_
+      # parameter specifies the digest algorithm to use. This may be a String
+      # representing the algorithm name or an instance of OpenSSL::Digest.
+      #
+      # === Example
+      #  key = 'key'
+      #  data = 'The quick brown fox jumps over the lazy dog'
+      #
+      #  hmac = OpenSSL::HMAC.base64digest('SHA1', key, data)
+      #  #=> "3nybhbi3iqa8ino29wqQcBydtNk="
+      def base64digest(digest, key, data)
+        [digest(digest, key, data)].pack("m0")
+      end
+    end
+  end
+end
diff -ruN ruby-2.5.9.orig/lib/openssl/marshal.rb ruby-2.5.9/lib/openssl/marshal.rb
--- ruby-2.5.9.orig/lib/openssl/marshal.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/marshal.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+#--
+# = Ruby-space definitions to add DER (de)serialization to classes
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'COPYING'.)
+#++
+module OpenSSL
+  module Marshal
+    def self.included(base)
+      base.extend(ClassMethods)
+    end
+
+    module ClassMethods
+      def _load(string)
+        new(string)
+      end
+    end
+
+    def _dump(_level)
+      to_der
+    end
+  end
+end
diff -ruN ruby-2.5.9.orig/lib/openssl/pkcs5.rb ruby-2.5.9/lib/openssl/pkcs5.rb
--- ruby-2.5.9.orig/lib/openssl/pkcs5.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/pkcs5.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+#--
+# Ruby/OpenSSL Project
+# Copyright (C) 2017 Ruby/OpenSSL Project Authors
+#++
+
+module OpenSSL
+  module PKCS5
+    module_function
+
+    # OpenSSL::PKCS5.pbkdf2_hmac has been renamed to OpenSSL::KDF.pbkdf2_hmac.
+    # This method is provided for backwards compatibility.
+    def pbkdf2_hmac(pass, salt, iter, keylen, digest)
+      OpenSSL::KDF.pbkdf2_hmac(pass, salt: salt, iterations: iter,
+                               length: keylen, hash: digest)
+    end
+
+    def pbkdf2_hmac_sha1(pass, salt, iter, keylen)
+      pbkdf2_hmac(pass, salt, iter, keylen, "sha1")
+    end
+  end
+end
diff -ruN ruby-2.5.9.orig/lib/openssl/pkey.rb ruby-2.5.9/lib/openssl/pkey.rb
--- ruby-2.5.9.orig/lib/openssl/pkey.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/pkey.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,471 @@
+# frozen_string_literal: true
+#--
+# Ruby/OpenSSL Project
+# Copyright (C) 2017 Ruby/OpenSSL Project Authors
+#++
+
+require_relative 'marshal'
+
+module OpenSSL::PKey
+  class DH
+    include OpenSSL::Marshal
+
+    # :call-seq:
+    #    dh.public_key -> dhnew
+    #
+    # Returns a new DH instance that carries just the \DH parameters.
+    #
+    # Contrary to the method name, the returned DH object contains only
+    # parameters and not the public key.
+    #
+    # This method is provided for backwards compatibility. In most cases, there
+    # is no need to call this method.
+    #
+    # For the purpose of re-generating the key pair while keeping the
+    # parameters, check OpenSSL::PKey.generate_key.
+    #
+    # Example:
+    #   # OpenSSL::PKey::DH.generate by default generates a random key pair
+    #   dh1 = OpenSSL::PKey::DH.generate(2048)
+    #   p dh1.priv_key #=> #<OpenSSL::BN 1288347...>
+    #   dhcopy = dh1.public_key
+    #   p dhcopy.priv_key #=> nil
+    def public_key
+      DH.new(to_der)
+    end
+
+    # :call-seq:
+    #    dh.compute_key(pub_bn) -> string
+    #
+    # Returns a String containing a shared secret computed from the other
+    # party's public value.
+    #
+    # This method is provided for backwards compatibility, and calls #derive
+    # internally.
+    #
+    # === Parameters
+    # * _pub_bn_ is a OpenSSL::BN, *not* the DH instance returned by
+    #   DH#public_key as that contains the DH parameters only.
+    def compute_key(pub_bn)
+      # FIXME: This is constructing an X.509 SubjectPublicKeyInfo and is very
+      # inefficient
+      obj = OpenSSL::ASN1.Sequence([
+        OpenSSL::ASN1.Sequence([
+          OpenSSL::ASN1.ObjectId("dhKeyAgreement"),
+          OpenSSL::ASN1.Sequence([
+            OpenSSL::ASN1.Integer(p),
+            OpenSSL::ASN1.Integer(g),
+          ]),
+        ]),
+        OpenSSL::ASN1.BitString(OpenSSL::ASN1.Integer(pub_bn).to_der),
+      ])
+      derive(OpenSSL::PKey.read(obj.to_der))
+    end
+
+    # :call-seq:
+    #    dh.generate_key! -> self
+    #
+    # Generates a private and public key unless a private key already exists.
+    # If this DH instance was generated from public \DH parameters (e.g. by
+    # encoding the result of DH#public_key), then this method needs to be
+    # called first in order to generate the per-session keys before performing
+    # the actual key exchange.
+    #
+    # <b>Deprecated in version 3.0</b>. This method is incompatible with
+    # OpenSSL 3.0.0 or later.
+    #
+    # See also OpenSSL::PKey.generate_key.
+    #
+    # Example:
+    #   # DEPRECATED USAGE: This will not work on OpenSSL 3.0 or later
+    #   dh0 = OpenSSL::PKey::DH.new(2048)
+    #   dh = dh0.public_key # #public_key only copies the DH parameters (contrary to the name)
+    #   dh.generate_key!
+    #   puts dh.private? # => true
+    #   puts dh0.pub_key == dh.pub_key #=> false
+    #
+    #   # With OpenSSL::PKey.generate_key
+    #   dh0 = OpenSSL::PKey::DH.new(2048)
+    #   dh = OpenSSL::PKey.generate_key(dh0)
+    #   puts dh0.pub_key == dh.pub_key #=> false
+    def generate_key!
+      if OpenSSL::OPENSSL_VERSION_NUMBER >= 0x30000000
+        raise DHError, "OpenSSL::PKey::DH is immutable on OpenSSL 3.0; " \
+        "use OpenSSL::PKey.generate_key instead"
+      end
+
+      unless priv_key
+        tmp = OpenSSL::PKey.generate_key(self)
+        set_key(tmp.pub_key, tmp.priv_key)
+      end
+      self
+    end
+
+    class << self
+      # :call-seq:
+      #    DH.generate(size, generator = 2) -> dh
+      #
+      # Creates a new DH instance from scratch by generating random parameters
+      # and a key pair.
+      #
+      # See also OpenSSL::PKey.generate_parameters and
+      # OpenSSL::PKey.generate_key.
+      #
+      # +size+::
+      #   The desired key size in bits.
+      # +generator+::
+      #   The generator.
+      def generate(size, generator = 2, &blk)
+        dhparams = OpenSSL::PKey.generate_parameters("DH", {
+          "dh_paramgen_prime_len" => size,
+          "dh_paramgen_generator" => generator,
+        }, &blk)
+        OpenSSL::PKey.generate_key(dhparams)
+      end
+
+      # Handle DH.new(size, generator) form here; new(str) and new() forms
+      # are handled by #initialize
+      def new(*args, &blk) # :nodoc:
+        if args[0].is_a?(Integer)
+          generate(*args, &blk)
+        else
+          super
+        end
+      end
+    end
+  end
+
+  class DSA
+    include OpenSSL::Marshal
+
+    # :call-seq:
+    #    dsa.public_key -> dsanew
+    #
+    # Returns a new DSA instance that carries just the \DSA parameters and the
+    # public key.
+    #
+    # This method is provided for backwards compatibility. In most cases, there
+    # is no need to call this method.
+    #
+    # For the purpose of serializing the public key, to PEM or DER encoding of
+    # X.509 SubjectPublicKeyInfo format, check PKey#public_to_pem and
+    # PKey#public_to_der.
+    def public_key
+      OpenSSL::PKey.read(public_to_der)
+    end
+
+    class << self
+      # :call-seq:
+      #    DSA.generate(size) -> dsa
+      #
+      # Creates a new DSA instance by generating a private/public key pair
+      # from scratch.
+      #
+      # See also OpenSSL::PKey.generate_parameters and
+      # OpenSSL::PKey.generate_key.
+      #
+      # +size+::
+      #   The desired key size in bits.
+      def generate(size, &blk)
+        # FIPS 186-4 specifies four (L,N) pairs: (1024,160), (2048,224),
+        # (2048,256), and (3072,256).
+        #
+        # q size is derived here with compatibility with
+        # DSA_generator_parameters_ex() which previous versions of ruby/openssl
+        # used to call.
+        qsize = size >= 2048 ? 256 : 160
+        dsaparams = OpenSSL::PKey.generate_parameters("DSA", {
+          "dsa_paramgen_bits" => size,
+          "dsa_paramgen_q_bits" => qsize,
+        }, &blk)
+        OpenSSL::PKey.generate_key(dsaparams)
+      end
+
+      # Handle DSA.new(size) form here; new(str) and new() forms
+      # are handled by #initialize
+      def new(*args, &blk) # :nodoc:
+        if args[0].is_a?(Integer)
+          generate(*args, &blk)
+        else
+          super
+        end
+      end
+    end
+
+    # :call-seq:
+    #    dsa.syssign(string) -> string
+    #
+    # Computes and returns the \DSA signature of +string+, where +string+ is
+    # expected to be an already-computed message digest of the original input
+    # data. The signature is issued using the private key of this DSA instance.
+    #
+    # <b>Deprecated in version 3.0</b>.
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
+    #
+    # +string+::
+    #   A message digest of the original input data to be signed.
+    #
+    # Example:
+    #   dsa = OpenSSL::PKey::DSA.new(2048)
+    #   doc = "Sign me"
+    #   digest = OpenSSL::Digest.digest('SHA1', doc)
+    #
+    #   # With legacy #syssign and #sysverify:
+    #   sig = dsa.syssign(digest)
+    #   p dsa.sysverify(digest, sig) #=> true
+    #
+    #   # With #sign_raw and #verify_raw:
+    #   sig = dsa.sign_raw(nil, digest)
+    #   p dsa.verify_raw(nil, sig, digest) #=> true
+    def syssign(string)
+      q or raise OpenSSL::PKey::DSAError, "incomplete DSA"
+      private? or raise OpenSSL::PKey::DSAError, "Private DSA key needed!"
+      begin
+        sign_raw(nil, string)
+      rescue OpenSSL::PKey::PKeyError
+        raise OpenSSL::PKey::DSAError, $!.message
+      end
+    end
+
+    # :call-seq:
+    #    dsa.sysverify(digest, sig) -> true | false
+    #
+    # Verifies whether the signature is valid given the message digest input.
+    # It does so by validating +sig+ using the public key of this DSA instance.
+    #
+    # <b>Deprecated in version 3.0</b>.
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
+    #
+    # +digest+::
+    #   A message digest of the original input data to be signed.
+    # +sig+::
+    #   A \DSA signature value.
+    def sysverify(digest, sig)
+      verify_raw(nil, sig, digest)
+    rescue OpenSSL::PKey::PKeyError
+      raise OpenSSL::PKey::DSAError, $!.message
+    end
+  end
+
+  if defined?(EC)
+  class EC
+    include OpenSSL::Marshal
+
+    # :call-seq:
+    #    key.dsa_sign_asn1(data) -> String
+    #
+    # <b>Deprecated in version 3.0</b>.
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
+    def dsa_sign_asn1(data)
+      sign_raw(nil, data)
+    rescue OpenSSL::PKey::PKeyError
+      raise OpenSSL::PKey::ECError, $!.message
+    end
+
+    # :call-seq:
+    #    key.dsa_verify_asn1(data, sig) -> true | false
+    #
+    # <b>Deprecated in version 3.0</b>.
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw instead.
+    def dsa_verify_asn1(data, sig)
+      verify_raw(nil, sig, data)
+    rescue OpenSSL::PKey::PKeyError
+      raise OpenSSL::PKey::ECError, $!.message
+    end
+
+    # :call-seq:
+    #    ec.dh_compute_key(pubkey) -> string
+    #
+    # Derives a shared secret by ECDH. _pubkey_ must be an instance of
+    # OpenSSL::PKey::EC::Point and must belong to the same group.
+    #
+    # This method is provided for backwards compatibility, and calls #derive
+    # internally.
+    def dh_compute_key(pubkey)
+      obj = OpenSSL::ASN1.Sequence([
+        OpenSSL::ASN1.Sequence([
+          OpenSSL::ASN1.ObjectId("id-ecPublicKey"),
+          group.to_der,
+        ]),
+        OpenSSL::ASN1.BitString(pubkey.to_octet_string(:uncompressed)),
+      ])
+      derive(OpenSSL::PKey.read(obj.to_der))
+    end
+  end
+
+  class EC::Point
+    # :call-seq:
+    #    point.to_bn([conversion_form]) -> OpenSSL::BN
+    #
+    # Returns the octet string representation of the EC point as an instance of
+    # OpenSSL::BN.
+    #
+    # If _conversion_form_ is not given, the _point_conversion_form_ attribute
+    # set to the group is used.
+    #
+    # See #to_octet_string for more information.
+    def to_bn(conversion_form = group.point_conversion_form)
+      OpenSSL::BN.new(to_octet_string(conversion_form), 2)
+    end
+  end
+  end
+
+  class RSA
+    include OpenSSL::Marshal
+
+    # :call-seq:
+    #    rsa.public_key -> rsanew
+    #
+    # Returns a new RSA instance that carries just the public key components.
+    #
+    # This method is provided for backwards compatibility. In most cases, there
+    # is no need to call this method.
+    #
+    # For the purpose of serializing the public key, to PEM or DER encoding of
+    # X.509 SubjectPublicKeyInfo format, check PKey#public_to_pem and
+    # PKey#public_to_der.
+    def public_key
+      OpenSSL::PKey.read(public_to_der)
+    end
+
+    class << self
+      # :call-seq:
+      #    RSA.generate(size, exponent = 65537) -> RSA
+      #
+      # Generates an \RSA keypair.
+      #
+      # See also OpenSSL::PKey.generate_key.
+      #
+      # +size+::
+      #   The desired key size in bits.
+      # +exponent+::
+      #   An odd Integer, normally 3, 17, or 65537.
+      def generate(size, exp = 0x10001, &blk)
+        OpenSSL::PKey.generate_key("RSA", {
+          "rsa_keygen_bits" => size,
+          "rsa_keygen_pubexp" => exp,
+        }, &blk)
+      end
+
+      # Handle RSA.new(size, exponent) form here; new(str) and new() forms
+      # are handled by #initialize
+      def new(*args, &blk) # :nodoc:
+        if args[0].is_a?(Integer)
+          generate(*args, &blk)
+        else
+          super
+        end
+      end
+    end
+
+    # :call-seq:
+    #    rsa.private_encrypt(string)          -> String
+    #    rsa.private_encrypt(string, padding) -> String
+    #
+    # Encrypt +string+ with the private key.  +padding+ defaults to
+    # PKCS1_PADDING, which is known to be insecure but is kept for backwards
+    # compatibility. The encrypted string output can be decrypted using
+    # #public_decrypt.
+    #
+    # <b>Deprecated in version 3.0</b>.
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and
+    # PKey::PKey#verify_recover instead.
+    def private_encrypt(string, padding = PKCS1_PADDING)
+      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
+      private? or raise OpenSSL::PKey::RSAError, "private key needed."
+      begin
+        sign_raw(nil, string, {
+          "rsa_padding_mode" => translate_padding_mode(padding),
+        })
+      rescue OpenSSL::PKey::PKeyError
+        raise OpenSSL::PKey::RSAError, $!.message
+      end
+    end
+
+    # :call-seq:
+    #    rsa.public_decrypt(string)          -> String
+    #    rsa.public_decrypt(string, padding) -> String
+    #
+    # Decrypt +string+, which has been encrypted with the private key, with the
+    # public key.  +padding+ defaults to PKCS1_PADDING which is known to be
+    # insecure but is kept for backwards compatibility.
+    #
+    # <b>Deprecated in version 3.0</b>.
+    # Consider using PKey::PKey#sign_raw and PKey::PKey#verify_raw, and
+    # PKey::PKey#verify_recover instead.
+    def public_decrypt(string, padding = PKCS1_PADDING)
+      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
+      begin
+        verify_recover(nil, string, {
+          "rsa_padding_mode" => translate_padding_mode(padding),
+        })
+      rescue OpenSSL::PKey::PKeyError
+        raise OpenSSL::PKey::RSAError, $!.message
+      end
+    end
+
+    # :call-seq:
+    #    rsa.public_encrypt(string)          -> String
+    #    rsa.public_encrypt(string, padding) -> String
+    #
+    # Encrypt +string+ with the public key.  +padding+ defaults to
+    # PKCS1_PADDING, which is known to be insecure but is kept for backwards
+    # compatibility. The encrypted string output can be decrypted using
+    # #private_decrypt.
+    #
+    # <b>Deprecated in version 3.0</b>.
+    # Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead.
+    def public_encrypt(data, padding = PKCS1_PADDING)
+      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
+      begin
+        encrypt(data, {
+          "rsa_padding_mode" => translate_padding_mode(padding),
+        })
+      rescue OpenSSL::PKey::PKeyError
+        raise OpenSSL::PKey::RSAError, $!.message
+      end
+    end
+
+    # :call-seq:
+    #    rsa.private_decrypt(string)          -> String
+    #    rsa.private_decrypt(string, padding) -> String
+    #
+    # Decrypt +string+, which has been encrypted with the public key, with the
+    # private key. +padding+ defaults to PKCS1_PADDING, which is known to be
+    # insecure but is kept for backwards compatibility.
+    #
+    # <b>Deprecated in version 3.0</b>.
+    # Consider using PKey::PKey#encrypt and PKey::PKey#decrypt instead.
+    def private_decrypt(data, padding = PKCS1_PADDING)
+      n or raise OpenSSL::PKey::RSAError, "incomplete RSA"
+      private? or raise OpenSSL::PKey::RSAError, "private key needed."
+      begin
+        decrypt(data, {
+          "rsa_padding_mode" => translate_padding_mode(padding),
+        })
+      rescue OpenSSL::PKey::PKeyError
+        raise OpenSSL::PKey::RSAError, $!.message
+      end
+    end
+
+    PKCS1_PADDING = 1
+    SSLV23_PADDING = 2
+    NO_PADDING = 3
+    PKCS1_OAEP_PADDING = 4
+
+    private def translate_padding_mode(num)
+      case num
+      when PKCS1_PADDING
+        "pkcs1"
+      when SSLV23_PADDING
+        "sslv23"
+      when NO_PADDING
+        "none"
+      when PKCS1_OAEP_PADDING
+        "oaep"
+      else
+        raise OpenSSL::PKey::PKeyError, "unsupported padding mode"
+      end
+    end
+  end
+end
diff -ruN ruby-2.5.9.orig/lib/openssl/ssl.rb ruby-2.5.9/lib/openssl/ssl.rb
--- ruby-2.5.9.orig/lib/openssl/ssl.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/ssl.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,610 @@
+# frozen_string_literal: true
+=begin
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
+  All rights reserved.
+
+= Licence
+  This program is licensed under the same licence as Ruby.
+  (See the file 'COPYING'.)
+=end
+
+require "openssl/buffering"
+
+if defined?(OpenSSL::SSL)
+
+require "io/nonblock"
+require "ipaddr"
+require "socket"
+
+module OpenSSL
+  module SSL
+    class SSLContext
+      DEFAULT_PARAMS = { # :nodoc:
+        :verify_mode => OpenSSL::SSL::VERIFY_PEER,
+        :verify_hostname => true,
+        :options => -> {
+          opts = OpenSSL::SSL::OP_ALL
+          opts &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
+          opts |= OpenSSL::SSL::OP_NO_COMPRESSION
+          opts
+        }.call
+      }
+
+      if defined?(OpenSSL::PKey::DH)
+        DH_ffdhe2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+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 :DH_ffdhe2048
+
+        DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
+          warn "using default DH parameters." if $VERBOSE
+          DH_ffdhe2048
+        }
+      end
+
+      if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") &&
+           OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000)
+        DEFAULT_PARAMS.merge!(
+          min_version: OpenSSL::SSL::TLS1_VERSION,
+          ciphers: %w{
+            ECDHE-ECDSA-AES128-GCM-SHA256
+            ECDHE-RSA-AES128-GCM-SHA256
+            ECDHE-ECDSA-AES256-GCM-SHA384
+            ECDHE-RSA-AES256-GCM-SHA384
+            DHE-RSA-AES128-GCM-SHA256
+            DHE-DSS-AES128-GCM-SHA256
+            DHE-RSA-AES256-GCM-SHA384
+            DHE-DSS-AES256-GCM-SHA384
+            ECDHE-ECDSA-AES128-SHA256
+            ECDHE-RSA-AES128-SHA256
+            ECDHE-ECDSA-AES128-SHA
+            ECDHE-RSA-AES128-SHA
+            ECDHE-ECDSA-AES256-SHA384
+            ECDHE-RSA-AES256-SHA384
+            ECDHE-ECDSA-AES256-SHA
+            ECDHE-RSA-AES256-SHA
+            DHE-RSA-AES128-SHA256
+            DHE-RSA-AES256-SHA256
+            DHE-RSA-AES128-SHA
+            DHE-RSA-AES256-SHA
+            DHE-DSS-AES128-SHA256
+            DHE-DSS-AES256-SHA256
+            DHE-DSS-AES128-SHA
+            DHE-DSS-AES256-SHA
+            AES128-GCM-SHA256
+            AES256-GCM-SHA384
+            AES128-SHA256
+            AES256-SHA256
+            AES128-SHA
+            AES256-SHA
+          }.join(":"),
+        )
+      end
+
+      DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
+      DEFAULT_CERT_STORE.set_default_paths
+      DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+
+      # A callback invoked when DH parameters are required for ephemeral DH key
+      # exchange.
+      #
+      # The callback is invoked with the SSLSocket, a
+      # flag indicating the use of an export cipher and the keylength
+      # required.
+      #
+      # The callback must return an OpenSSL::PKey::DH instance of the correct
+      # key length.
+      #
+      # <b>Deprecated in version 3.0.</b> Use #tmp_dh= instead.
+      attr_accessor :tmp_dh_callback
+
+      # A callback invoked at connect time to distinguish between multiple
+      # server names.
+      #
+      # The callback is invoked with an SSLSocket and a server name.  The
+      # callback must return an SSLContext for the server name or nil.
+      attr_accessor :servername_cb
+
+      # call-seq:
+      #    SSLContext.new           -> ctx
+      #    SSLContext.new(:TLSv1)   -> ctx
+      #    SSLContext.new("SSLv23") -> ctx
+      #
+      # Creates a new SSL context.
+      #
+      # If an argument is given, #ssl_version= is called with the value. Note
+      # that this form is deprecated. New applications should use #min_version=
+      # and #max_version= as necessary.
+      def initialize(version = nil)
+        self.ssl_version = version if version
+        self.verify_mode = OpenSSL::SSL::VERIFY_NONE
+        self.verify_hostname = false
+      end
+
+      ##
+      # call-seq:
+      #   ctx.set_params(params = {}) -> params
+      #
+      # Sets saner defaults optimized for the use with HTTP-like protocols.
+      #
+      # If a Hash _params_ is given, the parameters are overridden with it.
+      # The keys in _params_ must be assignment methods on SSLContext.
+      #
+      # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
+      # cert_store are not set then the system default certificate store is
+      # used.
+      def set_params(params={})
+        params = DEFAULT_PARAMS.merge(params)
+        self.options |= params.delete(:options) # set before min_version/max_version
+        params.each{|name, value| self.__send__("#{name}=", value) }
+        if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
+          unless self.ca_file or self.ca_path or self.cert_store
+            self.cert_store = DEFAULT_CERT_STORE
+          end
+        end
+        return params
+      end
+
+      # call-seq:
+      #    ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
+      #    ctx.min_version = :TLS1_2
+      #    ctx.min_version = nil
+      #
+      # Sets the lower bound on the supported SSL/TLS protocol version. The
+      # version may be specified by an integer constant named
+      # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
+      #
+      # Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v*
+      # options by #options= once you have called #min_version= or
+      # #max_version=.
+      #
+      # === Example
+      #   ctx = OpenSSL::SSL::SSLContext.new
+      #   ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
+      #   ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+      #
+      #   sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
+      #   sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
+      def min_version=(version)
+        set_minmax_proto_version(version, @max_proto_version ||= nil)
+        @min_proto_version = version
+      end
+
+      # call-seq:
+      #    ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+      #    ctx.max_version = :TLS1_2
+      #    ctx.max_version = nil
+      #
+      # Sets the upper bound of the supported SSL/TLS protocol version. See
+      # #min_version= for the possible values.
+      def max_version=(version)
+        set_minmax_proto_version(@min_proto_version ||= nil, version)
+        @max_proto_version = version
+      end
+
+      # call-seq:
+      #    ctx.ssl_version = :TLSv1
+      #    ctx.ssl_version = "SSLv23"
+      #
+      # Sets the SSL/TLS protocol version for the context. This forces
+      # connections to use only the specified protocol version. This is
+      # deprecated and only provided for backwards compatibility. Use
+      # #min_version= and #max_version= instead.
+      #
+      # === History
+      # As the name hints, this used to call the SSL_CTX_set_ssl_version()
+      # function which sets the SSL method used for connections created from
+      # the context. As of Ruby/OpenSSL 2.1, this accessor method is
+      # implemented to call #min_version= and #max_version= instead.
+      def ssl_version=(meth)
+        meth = meth.to_s if meth.is_a?(Symbol)
+        if /(?<type>_client|_server)\z/ =~ meth
+          meth = $`
+          if $VERBOSE
+            warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored"
+          end
+        end
+        version = METHODS_MAP[meth.intern] or
+          raise ArgumentError, "unknown SSL method `%s'" % meth
+        set_minmax_proto_version(version, version)
+        @min_proto_version = @max_proto_version = version
+      end
+
+      METHODS_MAP = {
+        SSLv23: 0,
+        SSLv2: OpenSSL::SSL::SSL2_VERSION,
+        SSLv3: OpenSSL::SSL::SSL3_VERSION,
+        TLSv1: OpenSSL::SSL::TLS1_VERSION,
+        TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
+        TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
+      }.freeze
+      private_constant :METHODS_MAP
+
+      # The list of available SSL/TLS methods. This constant is only provided
+      # for backwards compatibility.
+      METHODS = METHODS_MAP.flat_map { |name,|
+        [name, :"#{name}_client", :"#{name}_server"]
+      }.freeze
+      deprecate_constant :METHODS
+    end
+
+    module SocketForwarder
+      # The file descriptor for the socket.
+      def fileno
+        to_io.fileno
+      end
+
+      def addr
+        to_io.addr
+      end
+
+      def peeraddr
+        to_io.peeraddr
+      end
+
+      def local_address
+        to_io.local_address
+      end
+
+      def remote_address
+        to_io.remote_address
+      end
+
+      def setsockopt(level, optname, optval)
+        to_io.setsockopt(level, optname, optval)
+      end
+
+      def getsockopt(level, optname)
+        to_io.getsockopt(level, optname)
+      end
+
+      def fcntl(*args)
+        to_io.fcntl(*args)
+      end
+
+      def closed?
+        to_io.closed?
+      end
+
+      def do_not_reverse_lookup=(flag)
+        to_io.do_not_reverse_lookup = flag
+      end
+
+      def close_on_exec=(value)
+        to_io.close_on_exec = value
+      end
+
+      def close_on_exec?
+        to_io.close_on_exec?
+      end
+
+      def wait(*args)
+        to_io.wait(*args)
+      end
+
+      def wait_readable(*args)
+        to_io.wait_readable(*args)
+      end
+
+      def wait_writable(*args)
+        to_io.wait_writable(*args)
+      end
+
+      if IO.method_defined?(:timeout)
+        def timeout
+          to_io.timeout
+        end
+
+        def timeout=(value)
+          to_io.timeout=(value)
+        end
+      end
+    end
+
+    def verify_certificate_identity(cert, hostname)
+      should_verify_common_name = true
+      cert.extensions.each{|ext|
+        next if ext.oid != "subjectAltName"
+        ostr = OpenSSL::ASN1.decode(ext.to_der).value.last
+        sequence = OpenSSL::ASN1.decode(ostr.value)
+        sequence.value.each{|san|
+          case san.tag
+          when 2 # dNSName in GeneralName (RFC5280)
+            should_verify_common_name = false
+            return true if verify_hostname(hostname, san.value)
+          when 7 # iPAddress in GeneralName (RFC5280)
+            should_verify_common_name = false
+            if san.value.size == 4 || san.value.size == 16
+              begin
+                return true if san.value == IPAddr.new(hostname).hton
+              rescue IPAddr::InvalidAddressError
+              end
+            end
+          end
+        }
+      }
+      if should_verify_common_name
+        cert.subject.to_a.each{|oid, value|
+          if oid == "CN"
+            return true if verify_hostname(hostname, value)
+          end
+        }
+      end
+      return false
+    end
+    module_function :verify_certificate_identity
+
+    def verify_hostname(hostname, san) # :nodoc:
+      # RFC 5280, IA5String is limited to the set of ASCII characters
+      return false unless san.ascii_only?
+      return false unless hostname.ascii_only?
+
+      # See RFC 6125, section 6.4.1
+      # Matching is case-insensitive.
+      san_parts = san.downcase.split(".")
+
+      # TODO: this behavior should probably be more strict
+      return san == hostname if san_parts.size < 2
+
+      # Matching is case-insensitive.
+      host_parts = hostname.downcase.split(".")
+
+      # RFC 6125, section 6.4.3, subitem 2.
+      # If the wildcard character is the only character of the left-most
+      # label in the presented identifier, the client SHOULD NOT compare
+      # against anything but the left-most label of the reference
+      # identifier (e.g., *.example.com would match foo.example.com but
+      # not bar.foo.example.com or example.com).
+      return false unless san_parts.size == host_parts.size
+
+      # RFC 6125, section 6.4.3, subitem 1.
+      # The client SHOULD NOT attempt to match a presented identifier in
+      # which the wildcard character comprises a label other than the
+      # left-most label (e.g., do not match bar.*.example.net).
+      return false unless verify_wildcard(host_parts.shift, san_parts.shift)
+
+      san_parts.join(".") == host_parts.join(".")
+    end
+    module_function :verify_hostname
+
+    def verify_wildcard(domain_component, san_component) # :nodoc:
+      parts = san_component.split("*", -1)
+
+      return false if parts.size > 2
+      return san_component == domain_component if parts.size == 1
+
+      # RFC 6125, section 6.4.3, subitem 3.
+      # The client SHOULD NOT attempt to match a presented identifier
+      # where the wildcard character is embedded within an A-label or
+      # U-label of an internationalized domain name.
+      return false if domain_component.start_with?("xn--") && san_component != "*"
+
+      parts[0].length + parts[1].length < domain_component.length &&
+      domain_component.start_with?(parts[0]) &&
+      domain_component.end_with?(parts[1])
+    end
+    module_function :verify_wildcard
+
+    class SSLSocket
+      include Buffering
+      include SocketForwarder
+
+      attr_reader :hostname
+
+      # The underlying IO object.
+      attr_reader :io
+      alias :to_io :io
+
+      # The SSLContext object used in this connection.
+      attr_reader :context
+
+      # Whether to close the underlying socket as well, when the SSL/TLS
+      # connection is shut down. This defaults to +false+.
+      attr_accessor :sync_close
+
+      # call-seq:
+      #    ssl.sysclose => nil
+      #
+      # Sends "close notify" to the peer and tries to shut down the SSL
+      # connection gracefully.
+      #
+      # If sync_close is set to +true+, the underlying IO is also closed.
+      def sysclose
+        return if closed?
+        stop
+        io.close if sync_close
+      end
+
+      # call-seq:
+      #   ssl.post_connection_check(hostname) -> true
+      #
+      # Perform hostname verification following RFC 6125.
+      #
+      # This method MUST be called after calling #connect to ensure that the
+      # hostname of a remote peer has been verified.
+      def post_connection_check(hostname)
+        if peer_cert.nil?
+          msg = "Peer verification enabled, but no certificate received."
+          if using_anon_cipher?
+            msg += " Anonymous cipher suite #{cipher[0]} was negotiated. " \
+                   "Anonymous suites must be disabled to use peer verification."
+          end
+          raise SSLError, msg
+        end
+
+        unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
+          raise SSLError, "hostname \"#{hostname}\" does not match the server certificate"
+        end
+        return true
+      end
+
+      # call-seq:
+      #   ssl.session -> aSession
+      #
+      # Returns the SSLSession object currently used, or nil if the session is
+      # not established.
+      def session
+        SSL::Session.new(self)
+      rescue SSL::Session::SessionError
+        nil
+      end
+
+      # Close the stream for reading.
+      # This method is ignored by OpenSSL as there is no reasonable way to
+      # implement it, but exists for compatibility with IO.
+      def close_read
+        # Unsupported and ignored.
+        # Just don't read any more.
+      end
+
+      # Closes the stream for writing. The behavior of this method depends on
+      # the version of OpenSSL and the TLS protocol in use.
+      #
+      # - Sends a 'close_notify' alert to the peer.
+      # - Does not wait for the peer's 'close_notify' alert in response.
+      #
+      # In TLS 1.2 and earlier:
+      # - On receipt of a 'close_notify' alert, responds with a 'close_notify'
+      #   alert of its own and close down the connection immediately,
+      #   discarding any pending writes.
+      #
+      # Therefore, on TLS 1.2, this method will cause the connection to be
+      # completely shut down. On TLS 1.3, the connection will remain open for
+      # reading only.
+      def close_write
+        stop
+      end
+
+      private
+
+      def using_anon_cipher?
+        ctx = OpenSSL::SSL::SSLContext.new
+        ctx.ciphers = "aNULL"
+        ctx.ciphers.include?(cipher)
+      end
+
+      def client_cert_cb
+        @context.client_cert_cb
+      end
+
+      def tmp_dh_callback
+        @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
+      end
+
+      def session_new_cb
+        @context.session_new_cb
+      end
+
+      def session_get_cb
+        @context.session_get_cb
+      end
+
+      class << self
+
+        # call-seq:
+        #   open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
+        #
+        # Creates a new instance of SSLSocket.
+        # _remote\_host_ and _remote\_port_ are used to open TCPSocket.
+        # If _local\_host_ and _local\_port_ are specified,
+        # then those parameters are used on the local end to establish the connection.
+        # If _context_ is provided,
+        # the SSL Sockets initial params will be taken from the context.
+        #
+        # === Examples
+        #
+        #   sock = OpenSSL::SSL::SSLSocket.open('localhost', 443)
+        #   sock.connect # Initiates a connection to localhost:443
+        #
+        # with SSLContext:
+        #
+        #   ctx = OpenSSL::SSL::SSLContext.new
+        #   sock = OpenSSL::SSL::SSLSocket.open('localhost', 443, context: ctx)
+        #   sock.connect # Initiates a connection to localhost:443 with SSLContext
+        def open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
+          sock = ::TCPSocket.open(remote_host, remote_port, local_host, local_port)
+          if context.nil?
+            return OpenSSL::SSL::SSLSocket.new(sock)
+          else
+            return OpenSSL::SSL::SSLSocket.new(sock, context)
+          end
+        end
+      end
+    end
+
+    ##
+    # SSLServer represents a TCP/IP server socket with Secure Sockets Layer.
+    class SSLServer
+      include SocketForwarder
+      # When true then #accept works exactly the same as TCPServer#accept
+      attr_accessor :start_immediately
+
+      # Creates a new instance of SSLServer.
+      # * _srv_ is an instance of TCPServer.
+      # * _ctx_ is an instance of OpenSSL::SSL::SSLContext.
+      def initialize(svr, ctx)
+        @svr = svr
+        @ctx = ctx
+        unless ctx.session_id_context
+          # see #6137 - session id may not exceed 32 bytes
+          prng = ::Random.new($0.hash)
+          session_id = prng.bytes(16).unpack1('H*')
+          @ctx.session_id_context = session_id
+        end
+        @start_immediately = true
+      end
+
+      # Returns the TCPServer passed to the SSLServer when initialized.
+      def to_io
+        @svr
+      end
+
+      # See TCPServer#listen for details.
+      def listen(backlog=Socket::SOMAXCONN)
+        @svr.listen(backlog)
+      end
+
+      # See BasicSocket#shutdown for details.
+      def shutdown(how=Socket::SHUT_RDWR)
+        @svr.shutdown(how)
+      end
+
+      # Works similar to TCPServer#accept.
+      def accept
+        # Socket#accept returns [socket, addrinfo].
+        # TCPServer#accept returns a socket.
+        # The following comma strips addrinfo.
+        sock, = @svr.accept
+        begin
+          ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
+          ssl.sync_close = true
+          ssl.accept if @start_immediately
+          ssl
+        rescue Exception => ex
+          if ssl
+            ssl.close
+          else
+            sock.close
+          end
+          raise ex
+        end
+      end
+
+      # See IO#close for details.
+      def close
+        @svr.close
+      end
+    end
+  end
+end
+
+end
diff -ruN ruby-2.5.9.orig/lib/openssl/version.rb ruby-2.5.9/lib/openssl/version.rb
--- ruby-2.5.9.orig/lib/openssl/version.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/version.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module OpenSSL
+  VERSION = "3.3.0"
+end
diff -ruN ruby-2.5.9.orig/lib/openssl/x509.rb ruby-2.5.9/lib/openssl/x509.rb
--- ruby-2.5.9.orig/lib/openssl/x509.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl/x509.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,391 @@
+# frozen_string_literal: true
+#--
+# = Ruby-space definitions that completes C-space funcs for X509 and subclasses
+#
+# = Info
+# 'OpenSSL for Ruby 2' project
+# Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
+# All rights reserved.
+#
+# = Licence
+# This program is licensed under the same licence as Ruby.
+# (See the file 'COPYING'.)
+#++
+
+require_relative 'marshal'
+
+module OpenSSL
+  module X509
+    class ExtensionFactory
+      def create_extension(*arg)
+        if arg.size > 1
+          create_ext(*arg)
+        else
+          send("create_ext_from_"+arg[0].class.name.downcase, arg[0])
+        end
+      end
+
+      def create_ext_from_array(ary)
+        raise ExtensionError, "unexpected array form" if ary.size > 3
+        create_ext(ary[0], ary[1], ary[2])
+      end
+
+      def create_ext_from_string(str) # "oid = critical, value"
+        oid, value = str.split(/=/, 2)
+        oid.strip!
+        value.strip!
+        create_ext(oid, value)
+      end
+
+      def create_ext_from_hash(hash)
+        create_ext(hash["oid"], hash["value"], hash["critical"])
+      end
+    end
+
+    class Extension
+      include OpenSSL::Marshal
+
+      def ==(other)
+        return false unless Extension === other
+        to_der == other.to_der
+      end
+
+      def to_s # "oid = critical, value"
+        str = self.oid
+        str << " = "
+        str << "critical, " if self.critical?
+        str << self.value.gsub(/\n/, ", ")
+      end
+
+      def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false}
+        {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?}
+      end
+
+      def to_a
+        [ self.oid, self.value, self.critical? ]
+      end
+
+      module Helpers
+        def find_extension(oid)
+          extensions.find { |e| e.oid == oid }
+        end
+      end
+
+      module SubjectKeyIdentifier
+        include Helpers
+
+        # Get the subject's key identifier from the subjectKeyIdentifier
+        # exteension, as described in RFC5280 Section 4.2.1.2.
+        #
+        # Returns the binary String key identifier or nil or raises
+        # ASN1::ASN1Error.
+        def subject_key_identifier
+          ext = find_extension("subjectKeyIdentifier")
+          return nil if ext.nil?
+
+          ski_asn1 = ASN1.decode(ext.value_der)
+          if ext.critical? || ski_asn1.tag_class != :UNIVERSAL || ski_asn1.tag != ASN1::OCTET_STRING
+            raise ASN1::ASN1Error, "invalid extension"
+          end
+
+          ski_asn1.value
+        end
+      end
+
+      module AuthorityKeyIdentifier
+        include Helpers
+
+        # Get the issuing certificate's key identifier from the
+        # authorityKeyIdentifier extension, as described in RFC5280
+        # Section 4.2.1.1
+        #
+        # Returns the binary String keyIdentifier or nil or raises
+        # ASN1::ASN1Error.
+        def authority_key_identifier
+          ext = find_extension("authorityKeyIdentifier")
+          return nil if ext.nil?
+
+          aki_asn1 = ASN1.decode(ext.value_der)
+          if ext.critical? || aki_asn1.tag_class != :UNIVERSAL || aki_asn1.tag != ASN1::SEQUENCE
+            raise ASN1::ASN1Error, "invalid extension"
+          end
+
+          key_id = aki_asn1.value.find do |v|
+            v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+          end
+
+          key_id.nil? ? nil : key_id.value
+        end
+      end
+
+      module CRLDistributionPoints
+        include Helpers
+
+        # Get the distributionPoint fullName URI from the certificate's CRL
+        # distribution points extension, as described in RFC 5280 Section
+        # 4.2.1.13.
+        #
+        # Returns an array of strings or nil or raises ASN1::ASN1Error.
+        def crl_uris
+          ext = find_extension("crlDistributionPoints")
+          return nil if ext.nil?
+
+          cdp_asn1 = ASN1.decode(ext.value_der)
+          if cdp_asn1.tag_class != :UNIVERSAL || cdp_asn1.tag != ASN1::SEQUENCE
+            raise ASN1::ASN1Error, "invalid extension"
+          end
+
+          crl_uris = cdp_asn1.flat_map do |crl_distribution_point|
+            distribution_point = crl_distribution_point.value.find do |v|
+              v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+            end
+            full_name = distribution_point&.value&.find do |v|
+              v.tag_class == :CONTEXT_SPECIFIC && v.tag == 0
+            end
+            full_name&.value&.select do |v|
+              v.tag_class == :CONTEXT_SPECIFIC && v.tag == 6 # uniformResourceIdentifier
+            end
+          end
+
+          crl_uris.empty? ? nil : crl_uris.map(&:value)
+        end
+      end
+
+      module AuthorityInfoAccess
+        include Helpers
+
+        # Get the information and services for the issuer from the certificate's
+        # authority information access extension exteension, as described in RFC5280
+        # Section 4.2.2.1.
+        #
+        # Returns an array of strings or nil or raises ASN1::ASN1Error.
+        def ca_issuer_uris
+          aia_asn1 = parse_aia_asn1
+          return nil if aia_asn1.nil?
+
+          ca_issuer = aia_asn1.value.select do |authority_info_access|
+            authority_info_access.value.first.value == "caIssuers"
+          end
+
+          ca_issuer&.map(&:value)&.map(&:last)&.map(&:value)
+        end
+
+        # Get the URIs for OCSP from the certificate's authority information access
+        # extension exteension, as described in RFC5280 Section 4.2.2.1.
+        #
+        # Returns an array of strings or nil or raises ASN1::ASN1Error.
+        def ocsp_uris
+          aia_asn1 = parse_aia_asn1
+          return nil if aia_asn1.nil?
+
+          ocsp = aia_asn1.value.select do |authority_info_access|
+            authority_info_access.value.first.value == "OCSP"
+          end
+
+          ocsp&.map(&:value)&.map(&:last)&.map(&:value)
+        end
+
+        private
+
+          def parse_aia_asn1
+            ext = find_extension("authorityInfoAccess")
+            return nil if ext.nil?
+
+            aia_asn1 = ASN1.decode(ext.value_der)
+            if ext.critical? || aia_asn1.tag_class != :UNIVERSAL || aia_asn1.tag != ASN1::SEQUENCE
+              raise ASN1::ASN1Error, "invalid extension"
+            end
+
+            aia_asn1
+          end
+      end
+    end
+
+    class Name
+      include OpenSSL::Marshal
+
+      module RFC2253DN
+        Special = ',=+<>#;'
+        HexChar = /[0-9a-fA-F]/
+        HexPair = /#{HexChar}#{HexChar}/
+        HexString = /#{HexPair}+/
+        Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/
+        StringChar = /[^\\"#{Special}]/
+        QuoteChar = /[^\\"]/
+        AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/
+        AttributeValue = /
+          (?!["#])((?:#{StringChar}|#{Pair})*)|
+          \#(#{HexString})|
+          "((?:#{QuoteChar}|#{Pair})*)"
+        /x
+        TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/
+
+        module_function
+
+        def expand_pair(str)
+          return nil unless str
+          return str.gsub(Pair){
+            pair = $&
+            case pair.size
+            when 2 then pair[1,1]
+            when 3 then Integer("0x#{pair[1,2]}").chr
+            else raise OpenSSL::X509::NameError, "invalid pair: #{str}"
+            end
+          }
+        end
+
+        def expand_hexstring(str)
+          return nil unless str
+          der = str.gsub(HexPair){$&.to_i(16).chr }
+          a1 = OpenSSL::ASN1.decode(der)
+          return a1.value, a1.tag
+        end
+
+        def expand_value(str1, str2, str3)
+          value = expand_pair(str1)
+          value, tag = expand_hexstring(str2) unless value
+          value = expand_pair(str3) unless value
+          return value, tag
+        end
+
+        def scan(dn)
+          str = dn
+          ary = []
+          while true
+            if md = TypeAndValue.match(str)
+              remain = md.post_match
+              type = md[1]
+              value, tag = expand_value(md[2], md[3], md[4]) rescue nil
+              if value
+                type_and_value = [type, value]
+                type_and_value.push(tag) if tag
+                ary.unshift(type_and_value)
+                if remain.length > 2 && remain[0] == ?,
+                  str = remain[1..-1]
+                  next
+                elsif remain.length > 2 && remain[0] == ?+
+                  raise OpenSSL::X509::NameError,
+                    "multi-valued RDN is not supported: #{dn}"
+                elsif remain.empty?
+                  break
+                end
+              end
+            end
+            msg_dn = dn[0, dn.length - str.length] + " =>" + str
+            raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}"
+          end
+          return ary
+        end
+      end
+
+      class << self
+        # Parses the UTF-8 string representation of a distinguished name,
+        # according to RFC 2253.
+        #
+        # See also #to_utf8 for the opposite operation.
+        def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE)
+          ary = OpenSSL::X509::Name::RFC2253DN.scan(str)
+          self.new(ary, template)
+        end
+
+        # Parses the string representation of a distinguished name. Two
+        # different forms are supported:
+        #
+        # - \OpenSSL format (<tt>X509_NAME_oneline()</tt>) used by
+        #   <tt>#to_s</tt>. For example: <tt>/DC=com/DC=example/CN=nobody</tt>
+        # - \OpenSSL format (<tt>X509_NAME_print()</tt>)
+        #   used by <tt>#to_s(OpenSSL::X509::Name::COMPAT)</tt>. For example:
+        #   <tt>DC=com, DC=example, CN=nobody</tt>
+        #
+        # Neither of them is standardized and has quirks and inconsistencies
+        # in handling of escaped characters or multi-valued RDNs.
+        #
+        # Use of this method is discouraged in new applications. See
+        # Name.parse_rfc2253 and #to_utf8 for the alternative.
+        def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE)
+          if str.start_with?("/")
+            # /A=B/C=D format
+            ary = str[1..-1].split("/").map { |i| i.split("=", 2) }
+          else
+            # Comma-separated
+            ary = str.split(",").map { |i| i.strip.split("=", 2) }
+          end
+          self.new(ary, template)
+        end
+
+        alias parse parse_openssl
+      end
+
+      def pretty_print(q)
+        q.object_group(self) {
+          q.text ' '
+          q.text to_s(OpenSSL::X509::Name::RFC2253)
+        }
+      end
+    end
+
+    class Attribute
+      include OpenSSL::Marshal
+
+      def ==(other)
+        return false unless Attribute === other
+        to_der == other.to_der
+      end
+    end
+
+    class StoreContext
+      def cleanup
+        warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE
+      end
+    end
+
+    class Certificate
+      include OpenSSL::Marshal
+      include Extension::SubjectKeyIdentifier
+      include Extension::AuthorityKeyIdentifier
+      include Extension::CRLDistributionPoints
+      include Extension::AuthorityInfoAccess
+
+      def pretty_print(q)
+        q.object_group(self) {
+          q.breakable
+          q.text 'subject='; q.pp self.subject; q.text ','; q.breakable
+          q.text 'issuer='; q.pp self.issuer; q.text ','; q.breakable
+          q.text 'serial='; q.pp self.serial; q.text ','; q.breakable
+          q.text 'not_before='; q.pp self.not_before; q.text ','; q.breakable
+          q.text 'not_after='; q.pp self.not_after
+        }
+      end
+
+      def self.load_file(path)
+        load(File.binread(path))
+      end
+    end
+
+    class CRL
+      include OpenSSL::Marshal
+      include Extension::AuthorityKeyIdentifier
+
+      def ==(other)
+        return false unless CRL === other
+        to_der == other.to_der
+      end
+    end
+
+    class Revoked
+      def ==(other)
+        return false unless Revoked === other
+        to_der == other.to_der
+      end
+    end
+
+    class Request
+      include OpenSSL::Marshal
+
+      def ==(other)
+        return false unless Request === other
+        to_der == other.to_der
+      end
+    end
+  end
+end
diff -ruN ruby-2.5.9.orig/lib/openssl.rb ruby-2.5.9/lib/openssl.rb
--- ruby-2.5.9.orig/lib/openssl.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/lib/openssl.rb	2025-01-29 19:07:28.584390905 +0100
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+=begin
+= Info
+  'OpenSSL for Ruby 2' project
+  Copyright (C) 2002  Michal Rokos <m.rokos@sh.cvut.cz>
+  All rights reserved.
+
+= Licence
+  This program is licensed under the same licence as Ruby.
+  (See the file 'COPYING'.)
+=end
+
+require 'openssl.so'
+
+require_relative 'openssl/bn'
+require_relative 'openssl/asn1'
+require_relative 'openssl/pkey'
+require_relative 'openssl/cipher'
+require_relative 'openssl/digest'
+require_relative 'openssl/hmac'
+require_relative 'openssl/x509'
+require_relative 'openssl/ssl'
+require_relative 'openssl/pkcs5'
+require_relative 'openssl/version'
+
+module OpenSSL
+  # call-seq:
+  #   OpenSSL.secure_compare(string, string) -> boolean
+  #
+  # 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.
+  def self.secure_compare(a, b)
+    hashed_a = OpenSSL::Digest.digest('SHA256', a)
+    hashed_b = OpenSSL::Digest.digest('SHA256', b)
+    OpenSSL.fixed_length_secure_compare(hashed_a, hashed_b) && a == b
+  end
+end
diff -ruN ruby-2.5.9.orig/sample/openssl/cert2text.rb ruby-2.5.9/sample/openssl/cert2text.rb
--- ruby-2.5.9.orig/sample/openssl/cert2text.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/sample/openssl/cert2text.rb	2025-01-29 19:09:01.676804836 +0100
@@ -1,10 +1,13 @@
 #!/usr/bin/env ruby
 
 require 'openssl'
-include OpenSSL::X509
 
 def cert2text(cert_str)
-  [Certificate, CRL, Request].each do |klass|
+  [
+    OpenSSL::X509::Certificate,
+    OpenSSL::X509::CRL,
+    OpenSSL::X509::Request,
+  ].each do |klass|
     begin
       puts klass.new(cert_str).to_text
       return
diff -ruN ruby-2.5.9.orig/sample/openssl/certstore.rb ruby-2.5.9/sample/openssl/certstore.rb
--- ruby-2.5.9.orig/sample/openssl/certstore.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/sample/openssl/certstore.rb	2025-01-29 19:09:01.676804836 +0100
@@ -3,9 +3,6 @@
 
 
 class CertStore
-  include OpenSSL
-  include X509
-
   attr_reader :self_signed_ca
   attr_reader :other_ca
   attr_reader :ee
@@ -17,11 +14,11 @@
     @c_store = CHashDir.new(@certs_dir)
     @c_store.hash_dir(true)
     @crl_store = CrlStore.new(@c_store)
-    @x509store = Store.new
+    @x509store = OpenSSL::X509::Store.new
     @self_signed_ca = @other_ca = @ee = @crl = nil
 
     # Uncomment this line to let OpenSSL to check CRL for each certs.
-    # @x509store.flags = V_FLAG_CRL_CHECK | V_FLAG_CRL_CHECK_ALL
+    # @x509store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK | OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
 
     add_path
     scan_certs
diff -ruN ruby-2.5.9.orig/sample/openssl/c_rehash.rb ruby-2.5.9/sample/openssl/c_rehash.rb
--- ruby-2.5.9.orig/sample/openssl/c_rehash.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/sample/openssl/c_rehash.rb	2025-01-29 19:09:01.676804836 +0100
@@ -1,7 +1,6 @@
 #!/usr/bin/env ruby
 
 require 'openssl'
-require 'digest/md5'
 
 class CHashDir
   include Enumerable
@@ -161,7 +160,7 @@
   end
 
   def fingerprint(der)
-    Digest::MD5.hexdigest(der).upcase
+    OpenSSL::Digest.hexdigest('MD5', der).upcase
   end
 end
 
diff -ruN ruby-2.5.9.orig/sample/openssl/echo_cli.rb ruby-2.5.9/sample/openssl/echo_cli.rb
--- ruby-2.5.9.orig/sample/openssl/echo_cli.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/sample/openssl/echo_cli.rb	2025-01-29 19:09:01.676804836 +0100
@@ -15,7 +15,7 @@
 ctx = OpenSSL::SSL::SSLContext.new()
 if cert_file && key_file
   ctx.cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
-  ctx.key  = OpenSSL::PKey::RSA.new(File::read(key_file))
+  ctx.key  = OpenSSL::PKey.read(File::read(key_file))
 end
 if ca_path
   ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
diff -ruN ruby-2.5.9.orig/sample/openssl/echo_svr.rb ruby-2.5.9/sample/openssl/echo_svr.rb
--- ruby-2.5.9.orig/sample/openssl/echo_svr.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/sample/openssl/echo_svr.rb	2025-01-29 19:09:01.676804836 +0100
@@ -13,9 +13,9 @@
 
 if cert_file && key_file
   cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
-  key  = OpenSSL::PKey::RSA.new(File::read(key_file))
+  key  = OpenSSL::PKey.read(File::read(key_file))
 else
-  key = OpenSSL::PKey::RSA.new(512){ print "." }
+  key = OpenSSL::PKey::RSA.new(2048){ print "." }
   puts
   cert = OpenSSL::X509::Certificate.new
   cert.version = 2
@@ -25,7 +25,7 @@
   cert.issuer = name
   cert.not_before = Time.now
   cert.not_after = Time.now + 3600
-  cert.public_key = key.public_key
+  cert.public_key = key
   ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
   cert.extensions = [
     ef.create_extension("basicConstraints","CA:FALSE"),
@@ -37,7 +37,7 @@
   ef.issuer_certificate = cert
   cert.add_extension ef.create_extension("authorityKeyIdentifier",
                                          "keyid:always,issuer:always")
-  cert.sign(key, OpenSSL::Digest::SHA1.new)
+  cert.sign(key, "SHA1")
 end
 
 ctx = OpenSSL::SSL::SSLContext.new()
diff -ruN ruby-2.5.9.orig/sample/openssl/gen_csr.rb ruby-2.5.9/sample/openssl/gen_csr.rb
--- ruby-2.5.9.orig/sample/openssl/gen_csr.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/sample/openssl/gen_csr.rb	2025-01-29 19:09:01.676804836 +0100
@@ -3,8 +3,6 @@
 require 'optparse'
 require 'openssl'
 
-include OpenSSL
-
 def usage
   myname = File::basename($0)
   $stderr.puts <<EOS
@@ -21,13 +19,13 @@
 
 $stdout.sync = true
 name_str = ARGV.shift or usage()
-name = X509::Name.parse(name_str)
+name = OpenSSL::X509::Name.parse(name_str)
 
 keypair = nil
 if keypair_file
-  keypair = PKey::RSA.new(File.open(keypair_file).read)
+  keypair = OpenSSL::PKey.read(File.read(keypair_file))
 else
-  keypair = PKey::RSA.new(1024) { putc "." }
+  keypair = OpenSSL::PKey::RSA.new(2048) { putc "." }
   puts
   puts "Writing #{keyout}..."
   File.open(keyout, "w", 0400) do |f|
@@ -37,11 +35,11 @@
 
 puts "Generating CSR for #{name_str}"
 
-req = X509::Request.new
+req = OpenSSL::X509::Request.new
 req.version = 0
 req.subject = name
-req.public_key = keypair.public_key
-req.sign(keypair, Digest::MD5.new)
+req.public_key = keypair
+req.sign(keypair, "MD5")
 
 puts "Writing #{csrout}..."
 File.open(csrout, "w") do |f|
diff -ruN ruby-2.5.9.orig/sample/openssl/smime_read.rb ruby-2.5.9/sample/openssl/smime_read.rb
--- ruby-2.5.9.orig/sample/openssl/smime_read.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/sample/openssl/smime_read.rb	2025-01-29 19:09:01.676804836 +0100
@@ -1,6 +1,5 @@
 require 'optparse'
 require 'openssl'
-include OpenSSL
 
 options = ARGV.getopts("c:k:C:")
 
@@ -10,14 +9,14 @@
 
 data = $stdin.read
 
-cert = X509::Certificate.new(File::read(cert_file))
-key = PKey::RSA.new(File::read(key_file))
-p7enc = PKCS7::read_smime(data)
+cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
+key = OpenSSL::PKey::read(File::read(key_file))
+p7enc = OpenSSL::PKCS7::read_smime(data)
 data = p7enc.decrypt(key, cert)
 
-store = X509::Store.new
+store = OpenSSL::X509::Store.new
 store.add_path(ca_path)
-p7sig = PKCS7::read_smime(data)
+p7sig = OpenSSL::PKCS7::read_smime(data)
 if p7sig.verify([], store)
   puts p7sig.data
 end
diff -ruN ruby-2.5.9.orig/sample/openssl/smime_write.rb ruby-2.5.9/sample/openssl/smime_write.rb
--- ruby-2.5.9.orig/sample/openssl/smime_write.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/sample/openssl/smime_write.rb	2025-01-29 19:09:01.676804836 +0100
@@ -1,6 +1,5 @@
 require 'openssl'
 require 'optparse'
-include OpenSSL
 
 options = ARGV.getopts("c:k:r:")
 
@@ -8,16 +7,16 @@
 key_file  = options["k"]
 rcpt_file = options["r"]
 
-cert = X509::Certificate.new(File::read(cert_file))
-key = PKey::RSA.new(File::read(key_file))
+cert = OpenSSL::X509::Certificate.new(File::read(cert_file))
+key = OpenSSL::PKey::read(File::read(key_file))
 
 data  = "Content-Type: text/plain\r\n"
 data << "\r\n"
 data << "This is a clear-signed message.\r\n"
 
-p7sig  = PKCS7::sign(cert, key, data, [], PKCS7::DETACHED)
-smime0 = PKCS7::write_smime(p7sig)
+p7sig  = OpenSSL::PKCS7::sign(cert, key, data, [], OpenSSL::PKCS7::DETACHED)
+smime0 = OpenSSL::PKCS7::write_smime(p7sig)
 
-rcpt  = X509::Certificate.new(File::read(rcpt_file))
-p7enc = PKCS7::encrypt([rcpt], smime0)
-print PKCS7::write_smime(p7enc)
+rcpt  = OpenSSL::X509::Certificate.new(File::read(rcpt_file))
+p7enc = OpenSSL::PKCS7::encrypt([rcpt], smime0)
+print OpenSSL::PKCS7::write_smime(p7enc)
Binary files ruby-2.5.9.orig/test/openssl/fixtures/pkey/certificate.der and ruby-2.5.9/test/openssl/fixtures/pkey/certificate.der differ
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/dh1024.pem ruby-2.5.9/test/openssl/fixtures/pkey/dh1024.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/dh1024.pem	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/fixtures/pkey/dh1024.pem	1970-01-01 01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
------BEGIN DH PARAMETERS-----
-MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
-pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
-AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
------END DH PARAMETERS-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/dh-1.pem ruby-2.5.9/test/openssl/fixtures/pkey/dh-1.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/dh-1.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/dh-1.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,13 @@
+-----BEGIN DH PARAMETERS-----
+MIICCAKCAgEAvRzXYxY6L2DjeYmm1eowtMDu1it3j+VwFr6s6PRWzc1apMtztr9G
+xZ2mYndUAJLgNLO3n2fUDCYVMB6ZkcekW8Siocof3xWiMA6wqZ6uw0dsE3q7ZX+6
+TLjgSjaXeGvjutvuEwVrFeaUi83bMgfXN8ToxIQVprIF35sYFt6fpbFATKfW7qqi
+P1pQkjmCskU4tztaWvlLh0qg85wuQGnpJaQT3gS30378i0IGbA0EBvJcSpTHYbLa
+nsdI9bfN/ZVgeolVMNMU9/n8R8vRhNPcHuciFwaqS656q+HavCIyxw/LfjSwwFvR
+TngCn0wytRErkzFIXnRKckh8/BpI4S+0+l1NkOwG4WJ55KJ/9OOdZW5o/QCp2bDi
+E0JN1EP/gkSom/prq8JR/yEqtsy99uc5nUxPmzv0IgdcFHZEfiQU7iRggEbx7qfQ
+Ve55XksmmJInmpCy1bSabAEgIKp8Ckt5KLYZ0RgTXUhcEpsxEo6cuAwoSJT5o4Rp
+yG3xow2ozPcqZkvb+d2CHj1sc54w9BVFAjVANEKmRil/9WKz14bu3wxEhOPqC54n
+QojjLcoXSoT66ZUOQnYxTSiLtzoKGPy8cAVPbkBrXz2u2sj5gcvr1JjoGjdHm9/3
+qnqC8fsTz8UndKNIQC337o4K0833bQMzRGl1/qjbAPit2B7E3b6xTZMCAQI=
+-----END DH PARAMETERS-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/dh2048_ffdhe2048.pem ruby-2.5.9/test/openssl/fixtures/pkey/dh2048_ffdhe2048.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/dh2048_ffdhe2048.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/dh2048_ffdhe2048.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
+-----END DH PARAMETERS-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/dsa2048.pem ruby-2.5.9/test/openssl/fixtures/pkey/dsa2048.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/dsa2048.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/dsa2048.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,15 @@
+-----BEGIN PRIVATE KEY-----
+MIICXgIBADCCAjYGByqGSM44BAEwggIpAoIBAQDXZhJ/dQoWkQELzjzlx8FtIp96
+voCYe5NY0H8j0jz7GyHpXt41+MteqkZK3/Ah+cNR9uG8iEYArAZ71LcWotfee2Gz
+xdxozr9bRt0POYhO2YIsfMpBrEskPsDH2g/2nFV8l4OJgxU2qZUrF4PN5ha+Mu6u
+sVtN8hjvAvnbf4Pxn0b8NN9f4PJncroUa8acv5WsV85E1RW7NYCefggU4LytYIHg
+euRF9eY9gVCX5MkUgW2xODHIYJhwk/+5lJxG7qUsSahD/nPHO/yoWgdVHq2DkdTq
+KYXkAxx2PJcTBOHTglhE6mgCbEKp8vcfElnBWyCT6QykclZiPXXD2JV829J/Ah0A
+vYa+/G/gUZiomyejVje6UsGoCc+vInxmovOL8QKCAQEAhnKEigYPw6u8JY7v5iGo
+Ylz8qiMFYmaJCwevf3KCjWeEXuNO4OrKdfzkQl1tPuGLioYFfP1A2yGosjdUdLEB
+0JqnzlKxUp+G6RfBj+WYzbgc5hr7t0M+reAJh09/hDzqfxjcgiHstq7mpRXBP8Y7
+iu27s7TRYJNSAYRvWcXNSBEUym3mHBBbZn7VszYooSrn60/iZ8I+VY1UF/fgqhbj
+JfaaZNQCDO9K3Vb3rsXoYd8+bOZIen9uHB+pNjMqhpl4waysqrlpGFeeqdxivH6S
+vkrHLs6/eWVMnS08RdcryoCrI3Bm8mMBKQglDwKLnWLfzG565qEhslzyCd/l9k9a
+cwQfAh0Ao8/g72fSFmo04FizM7DZJSIPqDLjfZu9hLvUFA==
+-----END PRIVATE KEY-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/fullchain.pem ruby-2.5.9/test/openssl/fixtures/pkey/fullchain.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/fullchain.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/fullchain.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,56 @@
+-----BEGIN CERTIFICATE-----
+MIIFKTCCBBGgAwIBAgISBFspP+tJfRaC6xprreB4Rp9KMA0GCSqGSIb3DQEBCwUA
+MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
+EwJSMzAeFw0yMTA0MTcwMjQzMTlaFw0yMTA3MTYwMjQzMTlaMBwxGjAYBgNVBAMT
+EXd3dy5jb2Rlb3Rha3UuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAx6h5vNPfkkrtYWxn1PWDDLRAwrGmZbkYPttjHBRSwTcd7rsIX4PcSzw9fWxm
+K4vIkAYoKAElIvsSE3xRUjyzMrACfdhK5J8rG25fq94iVyoYaNBQV0WMJkO6X47s
+hGeIKkK91ohR5b2tMw3/z9zELP0TVo2TPG7rYsBZm34myldqDA8yVEBEOa+Qdpda
+9xewPhkkdpAU55qgWTrD21m7vGq9WpsBz4wNKnwVsaugtkRH82VPIfaL4ZI9kox6
+QoPWe/tHUBdlDkuT7ud77eLAWnC/5Clg28/9GU/Z8Nj8SrrKuXL6WUXmxxaAhWUR
+Qx4VblZeuIpwd0nHyP0hz4CWKQIDAQABo4ICTTCCAkkwDgYDVR0PAQH/BAQDAgWg
+MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0G
+A1UdDgQWBBTKiSGZuLFSIG2JPbFSZa9TxMu5WTAfBgNVHSMEGDAWgBQULrMXt1hW
+y65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6
+Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iu
+b3JnLzAcBgNVHREEFTATghF3d3cuY29kZW90YWt1LmNvbTBMBgNVHSAERTBDMAgG
+BmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUFBwIBFhpodHRwOi8vY3Bz
+LmxldHNlbmNyeXB0Lm9yZzCCAQUGCisGAQQB1nkCBAIEgfYEgfMA8QB3AJQgvB6O
+1Y1siHMfgosiLA3R2k1ebE+UPWHbTi9YTaLCAAABeN3s/lgAAAQDAEgwRgIhAKFY
+Q+vBe3zyeBazxp8kVN7oLvcQ6Y9PPz199tVhYnEbAiEAhU/xdbQaY/6b93h+7NTF
+sPG7X4lq/3UoNgoXcAVGZgoAdgD2XJQv0XcwIhRUGAgwlFaO400TGTO/3wwvIAvM
+TvFk4wAAAXjd7P5OAAAEAwBHMEUCIQDWd79+jWaGuf3acm5/yV95jL2KvzeGFfdU
+HZlKIeWFmAIgDSZ6ug7AyhYNKjzFV4ZSICln+L4yI92EpOa+8gDG6/0wDQYJKoZI
+hvcNAQELBQADggEBAHIhMYm06lLFmJL+cfIg5fFEmFNdHmmZn88Hypv4/MtmqTKv
+5asF/z3TvhW4hX2+TY+NdcqGT7cZFo/ZF/tS6oBXPgmBYM1dEfp2FAdnGNOySC5Y
+7RC4Uk9TUpP2g101YBmj6dQKQluAwIQk+gO4MSlHE0J0U/lMpjvrLWcuHbV4/xWJ
+IdM+iPq8GeYt5epYmNc7XeRIgv7V3RxDQdBv2OVM5mtPVerdiO0ISrdbe5mvz2+Z
+rhSg+EJNHlmMwcq5HqtMwS8M8Ax+vLmWCOkPWXhyV8wQaQcFjZJfpIGUvCnMTqsh
+kSIYXq2CbSDUUFRFssNN6EdVms0KnmW3BUu0xAk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/
+MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
+DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow
+MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT
+AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs
+jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp
+Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB
+U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7
+gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel
+/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R
+oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E
+BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p
+ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE
+p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE
+AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu
+Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0
+LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf
+r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
+AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH
+ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8
+S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL
+qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p
+O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw
+UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==
+-----END CERTIFICATE-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/garbage.txt ruby-2.5.9/test/openssl/fixtures/pkey/garbage.txt
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/garbage.txt	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/garbage.txt	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1 @@
+Hello World
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/p256_too_large.pem ruby-2.5.9/test/openssl/fixtures/pkey/p256_too_large.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/p256_too_large.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/p256_too_large.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIP+TT0V8Fndsnacji9tyf6hmhHywcOWTee9XkiBeJoVloAoGCCqGSM49
+AwEHoUQDQgAEBkhhJIU/2/YdPSlY2I1k25xjK4trr5OXSgXvBC21PtY0HQ7lor7A
+jzT0giJITqmcd81fwGw5+96zLcdxTF1hVQ==
+-----END EC PRIVATE KEY-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/p384_invalid.pem ruby-2.5.9/test/openssl/fixtures/pkey/p384_invalid.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/p384_invalid.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/p384_invalid.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDDA1Tm0m7YhkfeVpFuarAJYVlHp2tQj+1fOBiLa10t9E8TiQO/hVfxB
+vGaVEQwOheWgBwYFK4EEACKhZANiAASyGqmryZGqdpsq5gEDIfNvgC3AwSJxiBCL
+XKHBTFRp+tCezLDOK/6V8KK/vVGBJlGFW6/I7ahyXprxS7xs7hPA9iz5YiuqXlu+
+lbrIpZOz7b73hyQQCkvbBO/Avg+hPAk=
+-----END EC PRIVATE KEY-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/rsa-1.pem ruby-2.5.9/test/openssl/fixtures/pkey/rsa-1.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/rsa-1.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/rsa-1.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJJwIBAAKCAgEArIEJUYZrXhMfUXXdl2gLcXrRB4ciWNEeXt5UVLG0nPhygZwJ
+xis8tOrjXOJEpUXUsfgF35pQiJLD4T9/Vp3zLFtMOOQjOR3AxjIelbH9KPyGFEr9
+TcPtsJ24zhcG7RbwOGXR4iIcDaTx+bCLSAd7BjG3XHQtyeepGGRZkGyGUvXjPorH
+XP+dQjQnMd09wv0GMZSqQ06PedUUKQ4PJRfMCP+mwjFP+rB3NZuThF0CsNmpoixg
+GdoQ591Yrf5rf2Bs848JrYdqJlKlBL6rTFf2glHiC+mE5YRny7RZtv/qIkyUNotV
+ce1cE0GFrRmCpw9bqulDDcgKjFkhihTg4Voq0UYdJ6Alg7Ur4JerKTfyCaRGF27V
+fh/g2A2/6Vu8xKYYwTAwLn+Tvkx9OTVZ1t15wM7Ma8hHowNoO0g/lWkeltgHLMji
+rmeuIYQ20BQmdx2RRgWKl57D0wO/N0HIR+Bm4vcBoNPgMlk9g5WHA6idHR8TLxOr
+dMMmTiWfefB0/FzGXBv7DuuzHN3+urdCvG1QIMFQ06kHXhr4rC28KbWIxg+PJGM8
+oGNEGtGWAOvi4Ov+BVsIdbD5Sfyb4nY3L9qqPl6TxRxMWTKsYCYx11jC8civCzOu
+yL1z+wgIICJ6iGzrfYf6C2BiNV3BC1YCtp2XsG+AooIxCwjL2CP/54MuRnUCAwEA
+AQKCAgAP4+8M0HoRd2d6JIZeDRqIwIyCygLy9Yh7qrVP+/KsRwKdR9dqps73x29c
+Pgeexdj67+Lynw9uFT7v/95mBzTAUESsNO+9sizw1OsWVQgB/4kGU4YT5Ml/bHf6
+nApqSqOkPlTgJM46v4f+vTGHWBEQGAJRBO62250q/wt1D1osSDQ/rZ8BxRYiZBV8
+NWocDRzF8nDgtFrpGSS7R21DuHZ2Gb6twscgS6MfkA49sieuTM6gfr/3gavu/+fM
+V1Rlrmc65GE61++CSjijQEEdTjkJ9isBd+hjEBhTnnBpOBfEQxOgFqOvU/MYXv/G
+W0Q6yWJjUwt3OIcoOImrY5L3j0vERneA1Alweqsbws3fXXMjA+jhLxlJqjPvSAKc
+POi7xu7QCJjSSLAzHSDPdmGmfzlrbdWS1h0mrC5YZYOyToLajfnmAlXNNrytnePg
+JV9/1136ZFrJyEi1JVN3kyrC+1iVd1E+lWK0U1UQ6/25tJvKFc1I+xToaUbK10UN
+ycXib7p2Zsc/+ZMlPRgCxWmpIHmKhnwbO7vtRunnnc6wzhvlQQNHWlIvkyQukV50
+6k/bzWw0M6A98B4oCICIcxcpS3njDlHyL7NlkCD+/OfZp6X3RZF/m4grmA2doebz
+glsaNMyGHFrpHkHq19Y63Y4jtBdW/XuBv06Cnr4r3BXdjEzzwQKCAQEA5bj737Nk
+ZLA0UgzVVvY67MTserTOECIt4i37nULjRQwsSFiz0AWFOBwUCBJ5N2qDEelbf0Fa
+t4VzrphryEgzLz/95ZXi+oxw1liqCHi8iHeU2wSclDtx2jKv2q7bFvFSaH4CKC4N
+zBJNfP92kdXuAjXkbK/jWwr64fLNh/2KFWUAmrYmtGfnOjjyL+yZhPxBatztE58q
+/T61pkvP9NiLfrr7Xq8fnzrwqGERhXKueyoK6ig9ZJPZ2VTykMUUvNYJJ7OYQZru
+EYA3zkuEZifqmjgF57Bgg7dkkIh285TzH3CNf3MCMTmjlWVyHjlyeSPYgISB9Mys
+VKKQth+SvYcChQKCAQEAwDyCcolA7+bQBfECs6GXi7RYy2YSlx562S5vhjSlY9Ko
+WiwVJWviF7uSBdZRnGUKoPv4K4LV34o2lJpSSTi5Xgp7FH986VdGePe3p4hcXSIZ
+NtsKImLVLnEjrmkZExfQl7p0MkcU/LheCf/eEZVp0Z84O54WCs6GRm9wHYIUyrag
+9FREqqxTRVNhQQ2EDVGq1slREdwB+aygE76axK/qosk0RaoLzGZiMn4Sb8bpJxXO
+mee+ftq5bayVltfR0DhC8eHkcPPFeQMll1g+ML7HbINwHTr01ONm3cFUO4zOLBOO
+ws/+vtNfiv6S/lO1RQSRoiApbENBLdSc3V8Cy70PMQKCAQBOcZN4uP5gL5c+KWm0
+T1KhxUDnSdRPyAwY/xC7i7qlullovvlv4GK0XUot03kXBkUJmcEHvF5o6qYtCZlM
+g/MOgHCHtF4Upl5lo1M0n13pz8PB4lpBd+cR1lscdrcTp4Y3bkf4RnmppNpXA7kO
+ZZnnoVWGE620ShSPkWTDuj0rvxisu+SNmClqRUXWPZnSwnzoK9a86443efF3fs3d
+UxCXTuxFUdGfgvXo2XStOBMCtcGSYflM3fv27b4C13mUXhY0O2yTgn8m9LyZsknc
+xGalENpbWmwqrjYl8KOF2+gFZV68FZ67Bm6otkJ4ta80VJw6joT9/eIe6IA34KIw
+G+ktAoIBAFRuPxzvC4ZSaasyX21l25mQbC9pdWDKEkqxCmp3VOyy6R4xnlgBOhwS
+VeAacV2vQyvRfv4dSLIVkkNSRDHEqCWVlNk75TDXFCytIAyE54xAHbLqIVlY7yim
+qHVB07F/FC6PxdkPPziAAU2DA5XVedSHibslg6jbbD4jU6qiJ1+hNrAZEs+jQC+C
+n4Ri20y+Qbp0URb2+icemnARlwgr+3HjzQGL3gK4NQjYNmDBjEWOXl9aWWB90FNL
+KahGwfAhxcVW4W56opCzwR7nsujV4eDXGba83itidRuQfd5pyWOyc1E86TYGwD/b
+79OkEElv6Ea8uXTDVS075GmWATRapQECggEAd9ZAbyT+KouTfi2e6yLOosxSZfns
+eF06QAJi5n9GOtdfK5fqdmHJqJI7wbubCnd0oxPeL71lRjrOAMXufaQRdZtfXSMn
+B1TljteNrh1en5xF451rCPR/Y6tNKBvIKnhy1waO27/vA+ovXrm17iR9rRuGZ29i
+IurlKA6z/96UdrSdpqITTCyTjSOBYg34f49ueGjlpL4+8HJq2wor4Cb1Sbv8ErqA
+bsQ/Jz+KIGUiuFCfNa6d6McPRXIrGgzpprXgfimkV3nj49QyrnuCF/Pc4psGgIaN
+l3EiGXzRt/55K7DQVadtbcjo9zREac8QnDD6dS/gOfJ82L7frQfMpNWgQA==
+-----END RSA PRIVATE KEY-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/rsa-2.pem ruby-2.5.9/test/openssl/fixtures/pkey/rsa-2.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/rsa-2.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/rsa-2.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA1HUbx825tG7+/ulC5DpDogzXqM2/KmeCwGXZY4XjiWa+Zj7b
+ECkZwQh7zxFUsPixGqQKJSyFwCogdaPzYTRNtqKKaw/IWS0um1PTn4C4/9atbIsf
+HVKu/fWg4VrZL+ixFIZxa8Z6pvTB2omMcx+uEzbXPsO01i1pHf7MaWBxUDGFyC9P
+lASJBfFZAf2Ar1H99OTS4SP+gxM9Kk5tcc22r8uFiqqbhJmQNSDApdHvT1zSZxAc
+T1BFEZqfmR0B0UegPyJc/9hW0dYpB9JjR29UaZRSta3LUMpqltoOF5bzaKVgMuBm
+Qy79xJ71LjGp8bKhgRaWXyPsDzAC0MQlOW6En0v8LK8fntivJEvw9PNOMcZ8oMTn
+no0NeVt32HiQJW8LIVo7dOLVFtguSBMWUVe8mdKbuIIULD6JlSYke9Ob6andUhzO
+U79m/aRWs2yjD6o5QAktjFBARdPgcpTdWfppc8xpJUkQgRmVhINoIMT9W6Wl898E
+P4aPx6mRV/k05ellN3zRgd9tx5dyNuj3RBaNmR47cAVvGYRQgtH9bQYs6jtf0oer
+A5yIYEKspNRlZZJKKrQdLflQFOEwjQJyZnTk7Mp0y21wOuEGgZBexew55/hUJDC2
+mQ8CqjV4ki/Mm3z6Cw3jXIMNBJkH7oveBGSX0S9bF8A/73oOCU3W/LkORxECAwEA
+AQKCAgBLK7RMmYmfQbaPUtEMF2FesNSNMV72DfHBSUgFYpYDQ4sSeiLgMOqf1fSY
+azVf+F4RYwED7iDUwRMDDKNMPUlR2WjIQKlOhCH9a0dxJAZQ3xA1W3QC2AJ6cLIf
+ihlWTip5bKgszekPsYH1ZL2A7jCVM84ssuoE7cRHjKOelTUCfsMq9TJe2MvyglZP
+0fX6EjSctWm3pxiiH+iAU4d9wJ9my8fQLFUiMYNIiPIguYrGtbzsIlMh7PDDLcZS
+UmUWOxWDwRDOpSjyzadu0Q23dLiVMpmhFoDdcQENptFdn1c4K2tCFQuZscKwEt4F
+HiVXEzD5j5hcyUT4irA0VXImQ+hAH3oSDmn7wyHvyOg0bDZpUZXEHXb83Vvo54/d
+Fb4AOUva1dwhjci8CTEMxCENMy/CLilRv46AeHbOX8KMPM7BnRSJPptvTTh/qB9C
+HI5hxfkO+EOYnu0kUlxhJfrqG86H4IS+zA8HWiSEGxQteMjUQfgJoBzJ94YChpzo
+ePpKSpjxxl1PNNWKxWM3yUvlKmI2lNl6YNC8JpF2wVg4VvYkG7iVjleeRg21ay89
+NCVMF98n3MI5jdzfDKACnuYxg7sw+gjMy8PSoFvQ5pvHuBBOpa8tho6vk7bLJixT
+QY5uXMNQaO6OwpkBssKpnuXhIJzDhO48nSjJ5nUEuadPH1nGwQKCAQEA7twrUIMi
+Vqze/X6VyfEBnX+n3ZyQHLGqUv/ww1ZOOHmSW5ceC4GxHa8EPDjoh9NEjYffwGq9
+bfQh9Gntjk5gFipT/SfPrIhbPt59HthUqVvOGgSErCmn0vhsa0+ROpVi4K2WHS7O
+7SEwnoCWd6p1omon2olVY0ODlMH4neCx/ZuKV8SRMREubABlL8/MLp37AkgKarTY
+tewd0lpaZMvsjOhr1zVCGUUBxy87Fc7OKAcoQY8//0r8VMH7Jlga7F2PKVPzqRKf
+tjeW5jMAuRxTqtEdIeclJZwvUMxvb23BbBE+mtvKpXv69TB3DK8T1YIkhW2CidZW
+lad4MESC+QFNbQKCAQEA47PtULM/0ZFdE+PDDHOa2kJ2arm94sVIqF2168ZLXR69
+NkvCWfjkUPDeejINCx7XQgk0d/+5BCvrJpcM7lE4XfnYVNtPpct1el6eTfaOcPU8
+wAMsnq5n9Mxt02U+XRPtEqGk+lt0KLPDDSG88Z7jPmfftigLyPH6i/ZJyRUETlGk
+rGnWSx/LFUxQU5aBa2jUCjKOKa+OOk2jGg50A5Cmk26v9sA/ksOHisMjfdIpZc9P
+r4R0IteDDD5awlkWTF++5u1GpgU2yav4uan0wzY8OWYFzVyceA6+wffEcoplLm82
+CPd/qJOB5HHkjoM+CJgfumFxlNtdowKvKNUxpoQNtQKCAQEAh3ugofFPp+Q0M4r6
+gWnPZbuDxsLIR05K8vszYEjy4zup1YO4ygQNJ24fM91/n5Mo/jJEqwqgWd6w58ax
+tRclj00BCMXtGMrbHqTqSXWhR9LH66AGdPTHuXWpYZDnKliTlic/z1u+iWhbAHyl
+XEj2omIeKunc4gnod5cyYrKRouz3omLfi/pX33C19FGkWgjH2HpuViowBbhhDfCr
+9yJoEWC/0njl/hlTMdzLYcpEyxWMMuuC/FZXG+hPgWdWFh3XVzTEL3Fd3+hWEkp5
+rYWwu2ITaSiHvHaDrAvZZVXW8WoynXnvzr+tECgmTq57zI4eEwSTl4VY5VfxZ0dl
+FsIzXQKCAQBC07GYd6MJPGJWzgeWhe8yk0Lxu6WRAll6oFYd5kqD/9uELePSSAup
+/actsbbGRrziMpVlinWgVctjvf0bjFbArezhqqPLgtTtnwtS0kOnvzGfIM9dms4D
+uGObISGWa5yuVSZ4G5MRxwA9wGMVfo4u6Iltin868FmZ7iRlkXd8DNYJi95KmgAe
+NhF1FrzQ6ykf/QpgDZfuYI63vPorea6JonieMHn39s622OJ3sNBZguheGL+E4j8h
+vsMgOskijQ8X8xdC7lDQC1qqEsk06ZvvNJQLW1zIl3tArhjHjPp5EEaJhym+Ldx3
+UT3E3Zu9JfhZ2PNevqrShp0lnLw/pI3pAoIBAAUMz5Lj6V9ftsl1pTa8WDFeBJW0
+Wa5AT1BZg/ip2uq2NLPnA5JWcD+v682fRSvIj1pU0DRi6VsXlzhs+1q3+sgqiXGz
+u2ArFylh8TvC1gXUctXKZz/M3Rqr6aSNoejUGLmvHre+ja/k6Zwmu6ePtB7dL50d
+6+xMTYquS4gLbrbSLcEu3iBAAnvRLreXK4KguPxaBdICB7v7epdpAKe3Z7hp/sst
+eJj1+6KRdlcmt8fh5MPkBBXa6I/9XGmX5UEo7q4wAxeM9nuFWY3watz/EO9LiO6P
+LmqUSWL65m4cX0VZPvhYEsHppKi1eoWGlHqS4Af5+aIXi2alu2iljQFeA+Q=
+-----END RSA PRIVATE KEY-----
diff -ruN ruby-2.5.9.orig/test/openssl/fixtures/pkey/rsa-3.pem ruby-2.5.9/test/openssl/fixtures/pkey/rsa-3.pem
--- ruby-2.5.9.orig/test/openssl/fixtures/pkey/rsa-3.pem	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/fixtures/pkey/rsa-3.pem	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAzn+YCcOh7BIRzrb7TEuhQLD545+/Fx/zCYO3l+y/8ogUxMTg
+LG5HrcXlX3JP796ie90/GHIf8/lwczVhP1jk/keYjkwoTYDt477R7KRcJPyGqHRr
+qLp7AnZxtz3JLNboTgO3bAYzlvtsSKU/R3oehBbGHzEWCP2UEYj/Kky0zpcjkhZU
+jiErr9ARPq8+dOGqBf+CE2NLKYC1bu8hZe9AddvvN2SvfMN6uhJtEGZO1k8tScwf
+AyvPJ1Po/6z08pzMAgfBUCE95waAVeYJWIOlnNB4eEievzlXdPB9vEt8OOwtWfQX
+V8xyMsoKeAW05s413E0eTYx1aulFXdWwG2mWEBRtNzKF1iBudlg1a3x1zThWi1pY
+jW5vROvoWZMCbl9bYQ/LxOCVqDoUl86+NPEGeuESMzm5NvOQA2e0Ty5wphnt9M19
+Wcc8neBhb6iCGqYzxWNvUYXZWUv1+/MrPHKyJuv7MSivwtctfp8SacUGxkd6T+u6
+V6ntHf3qtN/5pAmni6nzUTgjC65MS0LEhi/RTzwafkIfifeJH7/LqFtjrursuwua
++p9lkACck/J5TpzaAfLroFQuepP8qgeq1cpD5Iii56IJ+FPSnkvesHuRUmZIkhtR
+VVsVqMaNPv/Uzc02bOaRXWP4auUY91mDKx/FDmORa9YCDQxMkKke05SWQ90CAwEA
+AQKCAgA0+B/c6VTgxGXS+7cMhB3yBTOkgva2jNh/6Uyv6Of345ZIPyQt4X/7gFbt
+G9qLcjWFxmQH9kZiA+snclrmr/vVijIE1l5EOz1KfUlGBYcpaal1DqALIQKqyA01
+buDq4pmmYWesiw6yvP2yyMipohav1VOu7p1zYvCXaufhRtneYICcWaQI7VNSfvHd
+fYBs5PIDJd6M8Jx4Ie7obOjJSAzl7qu3LtmhDFev4Ugeu8+fQ6IfWv/dhWBW+zw6
+UXhnv3bJUonw7wX8+/rxjdd54BMcXZF5cU9fR+s6MPJf2ZEc3OBpQaa3O9dTVeZH
+kVctGVpRj2qlg9EewoWro0PQVE5Mjah+mdFhPAHWoGl1xht6xJmg0uHYxMCzbUSz
+7NSS3knR0qieFvsp5ESY72i7DnQsbhbn6mTuYdVtm9bphxifAWCP3jFdft/bjtSF
+4yuPI7Qga+3m0B8QhtbWhEzPVon6NyiY7qfa6qllp0opEbw2hE22uGFFNJo2mpPa
+pe9VwARtD0IyfeklE7KrBEwV8NjTaAipZTZODw0w/dt4K3dOiePDl3pPWjmERpVg
+Lkw7XSCMtu5X87I1BbfOYbQhOXksPY+W9Asf6ETBeIZ8bD6Iypuk2ssool1lukqv
+yq1Y8gbR9B2x91ftYwXgzqBSvd8PFNsaXWLD3nrai2G1vb81lQKCAQEA6W02eZcN
+7wJfkqNokcuqhc5OKXH14gVIRV+KocG6f3vg88wrCg5J2GqNhBFuwVrafJjRenm6
+C8zWdneeyrl6cztgbaySw7kXnqFdTBiuOT8bhiG5NTPjDQ109EucaTbZU9KUXk6k
+ChPlr4G6IPrONpvi/9BvDDZLZkwR6uIg1kFWBy9kZaxFUEIug02hrbkTpPtnEUrO
+r3nG0QL/D0vf+bm4YHIVRMH2O2ZTTWexMw9XlfCe1+WjbJ+PS35QRCRDcRdWHXDb
+HnIFIAajtH5LtaJLgWUYq3B25WkQYtbHmFkm94sp/G4trb8JIJGzVO8cj9t6KeAT
+LG+tk8OqplqsYwKCAQEA4ne81KXx8VNwsKVFqwmiDIoi1q3beNa2hoXdzAMrnYdj
+iLxbfCVgrKPav9hdfXPBncHaNlGsd2G5W1a1UsOr128lTdfBsgm1RVPhVMKvo3fl
+yUnWajtAR1q3tVEUhuFlbJ/RHEtxJaGrzudYCPWQiYhydpDgSckbxD8PuElEgFBX
+O91vnWZEjMsxrABWiZNBxmtBUEv+fjUU/9USYzO4sN79UeD1+ZuBxPFwscsRcjLr
+bPgZWOwiywH6UmQ+DJTzeu0wJ6jgPoy/pgEujsbPDz1wNos6NhA/RQv31QeX33/B
+7/F5XKNmbJ2AFb/B+xTaTQPg0pjT5Exm+HrNU5OivwKCAQEAsLLVi9FG4OiBBHXi
+UItFuChljoYPxVqOTMV4Id6OmLZjoOmqouASElsGaTTxDDkEL1FXMUk4Bnq21dLT
+R06EXPpTknISX0qbkJ9CCrqcGAWnhi+9DYMLmvPW1p7t9c9pUESVv5X0IxTQx7yB
+8zkoJLp4aYGUrj/jb7qhzZYDmWy3/JRpgXWYupp+rzJy8xiowDj22mYwczDRyaJl
+BWVAVL+7zHZPl07kYC6jXHLj9mzktkIBXBkfTriyNkmV5R82VkN+Eqc9l5xkOMwN
+3DHGieYjFf47YHuv5RVVLBy91puWHckgrU+SEHYOKLNidybSDivsHArdOMQJN1Pk
+uCznVQKCAQAYY7DQbfa6eLQAMixomSb8lrvdxueGAgmyPyR93jGKS5Rqm2521ket
+EBB07MZUxmyposDvbKhYSwv9TD9G5I/TKcMouP3BQM5m4vu3dygXQMhcfzk6Q5tO
+k/SI8Gx3gjq8EhIhK/bJiLnKFJwkit3AEhPRtRSSnbgB0JDO1gUslHpwlg55MxRa
+3V9CGN84/cTtq4tjLGwCB5F1Y+sRB/byBXHeqY2UDi1Rmnb6jtYYKGe2WpnQO84b
+cuEUknskO75lFLpE6ykLU3koVaQ/+CVAjOtS1He2btWBiCJurNysU0P9pVHeqjJT
+rDqpHPe1JK/F74783zyir5+/Tuph/9pdAoIBAANPdFRQkJVH8K6iuhxQk6vFqiYB
+MUxpIVeLonD0p9TgMdezVNESht/AIutc0+5wabM45XuDWFRTuonvcE8lckv2Ux3a
+AvSsamjuesxw2YmkEtzZouVqDU0+oxppQJiwBG3MiaHX9F5IfnK6YmQ6xPwZ6MXi
+9feq1jR4KOc1ZrHtRMNgjnBWEFWroGe3FHgV7O133hpMSshRFmwcbE0nAaDr82U9
+sl8dclDjEKBxaqjAeNajOr+BU0w0AAwWXL7dt/ctG2QClcj9wqbEfsXnOR10h4AI
+rqkcvQrOLbTwcrOD/6R1rQfQXtEHKf1maThxosootAQZXdf6jxU3oonx3tU=
+-----END RSA PRIVATE KEY-----
diff -ruN ruby-2.5.9.orig/test/openssl/test_asn1.rb ruby-2.5.9/test/openssl/test_asn1.rb
--- ruby-2.5.9.orig/test/openssl/test_asn1.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_asn1.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -14,7 +14,7 @@
       ["keyUsage","keyCertSign, cRLSign",true],
       ["subjectKeyIdentifier","hash",false],
     ]
-    dgst = OpenSSL::Digest::SHA1.new
+    dgst = OpenSSL::Digest.new('SHA256')
     cert = OpenSSL::TestUtils.issue_cert(
       subj, key, s, exts, nil, nil, digest: dgst, not_before: now, not_after: now+3600)
 
@@ -42,7 +42,7 @@
     assert_equal(OpenSSL::ASN1::Sequence, sig.class)
     assert_equal(2, sig.value.size)
     assert_equal(OpenSSL::ASN1::ObjectId, sig.value[0].class)
-    assert_equal("1.2.840.113549.1.1.5", sig.value[0].oid)
+    assert_equal("1.2.840.113549.1.1.11", sig.value[0].oid)
     assert_equal(OpenSSL::ASN1::Null, sig.value[1].class)
 
     dn = tbs_cert.value[3] # issuer
@@ -167,10 +167,10 @@
     assert_equal(OpenSSL::ASN1::OctetString, ext.value[2].class)
     extv = OpenSSL::ASN1.decode(ext.value[2].value)
     assert_equal(OpenSSL::ASN1::BitString, extv.class)
-    str = "\000"; str[0] = 0b00000110.chr
+    str = +"\000"; str[0] = 0b00000110.chr
     assert_equal(str, extv.value)
 
-    ext = extensions.value[0].value[2]  # subjetKeyIdentifier
+    ext = extensions.value[0].value[2]  # subjectKeyIdentifier
     assert_equal(OpenSSL::ASN1::Sequence, ext.class)
     assert_equal(2, ext.value.size)
     assert_equal(OpenSSL::ASN1::ObjectId, ext.value[0].class)
@@ -178,7 +178,7 @@
     assert_equal(OpenSSL::ASN1::OctetString, ext.value[1].class)
     extv = OpenSSL::ASN1.decode(ext.value[1].value)
     assert_equal(OpenSSL::ASN1::OctetString, extv.class)
-    sha1 = OpenSSL::Digest::SHA1.new
+    sha1 = OpenSSL::Digest.new('SHA1')
     sha1.update(pkey.value[1].value)
     assert_equal(sha1.digest, extv.value)
 
@@ -189,7 +189,7 @@
     assert_equal(OpenSSL::ASN1::Null, pkey.value[0].value[1].class)
 
     assert_equal(OpenSSL::ASN1::BitString, sig_val.class)
-    cululated_sig = key.sign(OpenSSL::Digest::SHA1.new, tbs_cert.to_der)
+    cululated_sig = key.sign(OpenSSL::Digest.new('SHA256'), tbs_cert.to_der)
     assert_equal(cululated_sig, sig_val.value)
   end
 
@@ -265,10 +265,9 @@
     assert_raise(OpenSSL::ASN1::ASN1Error) {
       OpenSSL::ASN1.decode(B(%w{ 03 00 }))
     }
-    # OpenSSL < OpenSSL_1_0_1k and LibreSSL ignore the error
-    # assert_raise(OpenSSL::ASN1::ASN1Error) {
-    #   OpenSSL::ASN1.decode(B(%w{ 03 03 08 FF 00 }))
-    # }
+    assert_raise(OpenSSL::ASN1::ASN1Error) {
+      OpenSSL::ASN1.decode(B(%w{ 03 03 08 FF 00 }))
+    }
     # OpenSSL does not seem to prohibit this, though X.690 8.6.2.3 (15/08) does
     # assert_raise(OpenSSL::ASN1::ASN1Error) {
     #   OpenSSL::ASN1.decode(B(%w{ 03 01 04 }))
@@ -324,14 +323,42 @@
     assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1::ObjectId.new("3.0".b).to_der }
     assert_raise(OpenSSL::ASN1::ASN1Error) { OpenSSL::ASN1::ObjectId.new("0.40".b).to_der }
 
-    begin
-      oid = (0...100).to_a.join(".").b
-      obj = OpenSSL::ASN1::ObjectId.new(oid)
-      assert_equal oid, obj.oid
-    rescue OpenSSL::ASN1::ASN1Error
-      pend "OBJ_obj2txt() not working (LibreSSL?)" if $!.message =~ /OBJ_obj2txt/
-      raise
+    oid = (0...100).to_a.join(".").b
+    obj = OpenSSL::ASN1::ObjectId.new(oid)
+    assert_equal oid, obj.oid
+  end
+
+  def test_object_identifier_equality
+    aki = [
+      OpenSSL::ASN1::ObjectId.new("authorityKeyIdentifier"),
+      OpenSSL::ASN1::ObjectId.new("X509v3 Authority Key Identifier"),
+      OpenSSL::ASN1::ObjectId.new("2.5.29.35")
+    ]
+
+    ski = [
+      OpenSSL::ASN1::ObjectId.new("subjectKeyIdentifier"),
+      OpenSSL::ASN1::ObjectId.new("X509v3 Subject Key Identifier"),
+      OpenSSL::ASN1::ObjectId.new("2.5.29.14")
+    ]
+
+    aki.each do |a|
+      aki.each do |b|
+        assert_equal true, a == b
+      end
+
+      ski.each do |b|
+        assert_equal false, a == b
+      end
     end
+
+    obj1 = OpenSSL::ASN1::ObjectId.new("1.2.34.56789.10")
+    obj2 = OpenSSL::ASN1::ObjectId.new("1.2.34.56789.10")
+    obj3 = OpenSSL::ASN1::ObjectId.new("1.2.34.56789.11")
+    omit "OID 1.2.34.56789.10 is registered" if obj1.sn
+    assert_equal true, obj1 == obj2
+    assert_equal false, obj1 == obj3
+
+    assert_equal false, OpenSSL::ASN1::ObjectId.new("authorityKeyIdentifier") == nil
   end
 
   def test_sequence
@@ -379,9 +406,6 @@
   def test_utctime
     encode_decode_test B(%w{ 17 0D }) + "160908234339Z".b,
       OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 39))
-    # Seconds is omitted
-    decode_test B(%w{ 17 0B }) + "1609082343Z".b,
-      OpenSSL::ASN1::UTCTime.new(Time.utc(2016, 9, 8, 23, 43, 0))
     begin
       # possible range of UTCTime is 1969-2068 currently
       encode_decode_test B(%w{ 17 0D }) + "690908234339Z".b,
@@ -407,8 +431,6 @@
       OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 29))
     encode_decode_test B(%w{ 18 0F }) + "99990908234339Z".b,
       OpenSSL::ASN1::GeneralizedTime.new(Time.utc(9999, 9, 8, 23, 43, 39))
-    decode_test B(%w{ 18 0D }) + "201612081934Z".b,
-      OpenSSL::ASN1::GeneralizedTime.new(Time.utc(2016, 12, 8, 19, 34, 0))
     # not implemented
     # decode_test B(%w{ 18 13 }) + "20161208193439+0930".b,
     #   OpenSSL::ASN1::GeneralizedTime.new(Time.new(2016, 12, 8, 19, 34, 39, "+09:30"))
@@ -635,10 +657,10 @@
     assert_equal data, seq.entries
   end
 
-  def test_gc_stress
-    skip "very time consuming test"
-    assert_ruby_status(['--disable-gems', '-eGC.stress=true', '-erequire "openssl.so"'])
-  end
+  # Very time consuming test.
+  # def test_gc_stress
+  #   assert_ruby_status(['--disable-gems', '-eGC.stress=true', '-erequire "openssl.so"'])
+  # end
 
   private
 
diff -ruN ruby-2.5.9.orig/test/openssl/test_bn.rb ruby-2.5.9/test/openssl/test_bn.rb
--- ruby-2.5.9.orig/test/openssl/test_bn.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_bn.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,7 +1,6 @@
 # coding: us-ascii
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
-require "prime"
 
 if defined?(OpenSSL)
 
@@ -15,6 +14,10 @@
   end
 
   def test_new
+    assert_raise(ArgumentError) { OpenSSL::BN.new }
+    assert_raise(ArgumentError) { OpenSSL::BN.new(nil) }
+    assert_raise(ArgumentError) { OpenSSL::BN.new(nil, 2) }
+
     assert_equal(@e1, OpenSSL::BN.new("999"))
     assert_equal(@e1, OpenSSL::BN.new("999", 10))
     assert_equal(@e1, OpenSSL::BN.new("\x03\xE7", 2))
@@ -127,6 +130,27 @@
     assert_equal(-999, +@e2)
     assert_equal(-999, -@e1)
     assert_equal(+999, -@e2)
+
+    # These methods create new BN instances due to BN mutability
+    # Ensure that the instance isn't the same
+    e1_plus = +@e1
+    e1_minus = -@e1
+    assert_equal(false, @e1.equal?(e1_plus))
+    assert_equal(true, @e1 == e1_plus)
+    assert_equal(false, @e1.equal?(e1_minus))
+  end
+
+  def test_abs
+    assert_equal(@e1, @e2.abs)
+    assert_equal(@e3, @e4.abs)
+    assert_not_equal(@e2, @e2.abs)
+    assert_not_equal(@e4, @e4.abs)
+    assert_equal(false, @e2.abs.negative?)
+    assert_equal(false, @e4.abs.negative?)
+    assert_equal(true, (-@e1.abs).negative?)
+    assert_equal(true, (-@e2.abs).negative?)
+    assert_equal(true, (-@e3.abs).negative?)
+    assert_equal(true, (-@e4.abs).negative?)
   end
 
   def test_mod
@@ -150,6 +174,14 @@
     assert_equal(0, 59.to_bn.mod_sqr(59))
   end
 
+  def test_mod_sqrt
+    assert_equal(4, 4.to_bn.mod_sqrt(5).mod_sqr(5))
+    # One of 189484 or 326277 is returned as a square root of 2 (mod 515761).
+    assert_equal(2, 2.to_bn.mod_sqrt(515761).mod_sqr(515761))
+    assert_equal(0, 5.to_bn.mod_sqrt(5))
+    assert_raise(OpenSSL::BNError) { 3.to_bn.mod_sqrt(5) }
+  end
+
   def test_mod_inverse
     assert_equal(2, 3.to_bn.mod_inverse(5))
     assert_raise(OpenSSL::BNError) { 3.to_bn.mod_inverse(6) }
@@ -224,25 +256,35 @@
       r5 = OpenSSL::BN.rand_range(256)
       assert_include(0..255, r5)
     }
-  end
 
-  def test_prime
-    p1 = OpenSSL::BN.generate_prime(32)
-    assert_include(0...2**32, p1)
-    assert_equal(true, Prime.prime?(p1.to_i))
-    p2 = OpenSSL::BN.generate_prime(32, true)
-    assert_equal(true, Prime.prime?((p2.to_i - 1) / 2))
-    p3 = OpenSSL::BN.generate_prime(32, false, 4)
-    assert_equal(1, p3 % 4)
-    p4 = OpenSSL::BN.generate_prime(32, false, 4, 3)
-    assert_equal(3, p4 % 4)
-
-    assert_equal(true, p1.prime?)
-    assert_equal(true, p2.prime?)
-    assert_equal(true, p3.prime?)
-    assert_equal(true, p4.prime?)
-    assert_equal(true, @e3.prime?)
-    assert_equal(true, @e3.prime_fasttest?)
+    # Aliases
+    assert_include(128..255, OpenSSL::BN.pseudo_rand(8))
+    assert_include(0..255, OpenSSL::BN.pseudo_rand_range(256))
+  end
+
+  begin
+    require "prime"
+
+    def test_prime
+      p1 = OpenSSL::BN.generate_prime(32)
+      assert_include(0...2**32, p1)
+      assert_equal(true, Prime.prime?(p1.to_i))
+      p2 = OpenSSL::BN.generate_prime(32, true)
+      assert_equal(true, Prime.prime?((p2.to_i - 1) / 2))
+      p3 = OpenSSL::BN.generate_prime(32, false, 4)
+      assert_equal(1, p3 % 4)
+      p4 = OpenSSL::BN.generate_prime(32, false, 4, 3)
+      assert_equal(3, p4 % 4)
+
+      assert_equal(true, p1.prime?)
+      assert_equal(true, p2.prime?)
+      assert_equal(true, p3.prime?)
+      assert_equal(true, p4.prime?)
+      assert_equal(true, @e3.prime?)
+      assert_equal(true, @e3.prime_fasttest?)
+    end
+  rescue LoadError
+    # prime is the bundled gems at Ruby 3.1
   end
 
   def test_num_bits_bytes
@@ -272,6 +314,61 @@
     assert_equal(0, @e1.ucmp(-999))
     assert_instance_of(String, @e1.hash.to_s)
   end
+
+  def test_argument_error
+    bug15760 = '[ruby-core:92231] [Bug #15760]'
+    assert_raise(ArgumentError, bug15760) { OpenSSL::BN.new(nil, 2) }
+  end
+
+  def test_get_flags_and_set_flags
+    e = OpenSSL::BN.new(999)
+
+    assert_equal(0, e.get_flags(OpenSSL::BN::CONSTTIME))
+
+    e.set_flags(OpenSSL::BN::CONSTTIME)
+    assert_equal(OpenSSL::BN::CONSTTIME, e.get_flags(OpenSSL::BN::CONSTTIME))
+
+    b = OpenSSL::BN.new(2)
+    m = OpenSSL::BN.new(99)
+    assert_equal("17", b.mod_exp(e, m).to_s)
+
+    # mod_exp fails when m is even and any argument has CONSTTIME flag
+    m = OpenSSL::BN.new(98)
+    assert_raise(OpenSSL::BNError) do
+      b.mod_exp(e, m)
+    end
+
+    # It looks like flags cannot be removed once enabled
+    e.set_flags(0)
+    assert_equal(4, e.get_flags(OpenSSL::BN::CONSTTIME))
+  end
+
+  if respond_to?(:ractor)
+    ractor
+    def test_ractor
+      assert_equal(@e1, Ractor.new { OpenSSL::BN.new("999") }.take)
+      assert_equal(@e3, Ractor.new { OpenSSL::BN.new("\a\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 2) }.take)
+      assert_equal("999", Ractor.new(@e1) { |e1| e1.to_s }.take)
+      assert_equal("07FFFFFFFFFFFFFFFFFFFFFFFFFF", Ractor.new(@e3) { |e3| e3.to_s(16) }.take)
+      assert_equal(2**107-1, Ractor.new(@e3) { _1.to_i }.take)
+      assert_equal([1000, -999], Ractor.new(@e2) { _1.coerce(1000) }.take)
+      assert_equal(false, Ractor.new  { 1.to_bn.zero? }.take)
+      assert_equal(true, Ractor.new { 1.to_bn.one? }.take)
+      assert_equal(true, Ractor.new(@e2) { _1.negative? }.take)
+      assert_equal("-03E7", Ractor.new(@e2) { _1.to_s(16) }.take)
+      assert_equal(2**107-1, Ractor.new(@e3) { _1.to_i }.take)
+      assert_equal([1000, -999], Ractor.new(@e2) { _1.coerce(1000) }.take)
+      assert_equal(true, Ractor.new { 0.to_bn.zero? }.take)
+      assert_equal(true, Ractor.new { 1.to_bn.one? }.take )
+      assert_equal(false,Ractor.new { 2.to_bn.odd? }.take)
+      assert_equal(true, Ractor.new(@e2) { _1.negative? }.take)
+      assert_include(128..255, Ractor.new { OpenSSL::BN.rand(8)}.take)
+      assert_include(0...2**32, Ractor.new { OpenSSL::BN.generate_prime(32) }.take)
+      assert_equal(0, Ractor.new { OpenSSL::BN.new(999).get_flags(OpenSSL::BN::CONSTTIME) }.take)
+      # test if shareable when frozen
+      assert Ractor.shareable?(@e1.freeze)
+    end
+  end
 end
 
 end
diff -ruN ruby-2.5.9.orig/test/openssl/test_buffering.rb ruby-2.5.9/test/openssl/test_buffering.rb
--- ruby-2.5.9.orig/test/openssl/test_buffering.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_buffering.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -10,7 +10,7 @@
     attr_accessor :sync
 
     def initialize
-      @io = ""
+      @io = Buffer.new
       def @io.sync
         true
       end
@@ -41,6 +41,13 @@
     @io = IO.new
   end
 
+  def test_encoding
+    @io.write '😊'
+    @io.flush
+
+    assert_equal @io.string.encoding, Encoding::BINARY
+  end
+
   def test_flush
     @io.write 'a'
 
diff -ruN ruby-2.5.9.orig/test/openssl/test_cipher.rb ruby-2.5.9/test/openssl/test_cipher.rb
--- ruby-2.5.9.orig/test/openssl/test_cipher.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_cipher.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -36,8 +36,8 @@
     cipher.pkcs5_keyivgen(pass, salt, num, "MD5")
     s1 = cipher.update(pt) << cipher.final
 
-    d1 = num.times.inject(pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) }
-    d2 = num.times.inject(d1 + pass + salt) {|out, _| OpenSSL::Digest::MD5.digest(out) }
+    d1 = num.times.inject(pass + salt) {|out, _| OpenSSL::Digest.digest('MD5', out) }
+    d2 = num.times.inject(d1 + pass + salt) {|out, _| OpenSSL::Digest.digest('MD5', out) }
     key = (d1 + d2)[0, 24]
     iv = (d1 + d2)[24, 8]
     cipher = new_encryptor("DES-EDE3-CBC", key: key, iv: iv)
@@ -108,12 +108,6 @@
     assert_not_equal s1, s2
   end
 
-  def test_empty_data
-    cipher = OpenSSL::Cipher.new("DES-EDE3-CBC").encrypt
-    cipher.random_key
-    assert_raise(ArgumentError) { cipher.update("") }
-  end
-
   def test_initialize
     cipher = OpenSSL::Cipher.new("DES-EDE3-CBC")
     assert_raise(RuntimeError) { cipher.__send__(:initialize, "DES-EDE3-CBC") }
@@ -134,26 +128,47 @@
     assert_equal pt, cipher.update(ct) << cipher.final
   end
 
+  def test_update_with_buffer
+    cipher = OpenSSL::Cipher.new("aes-128-ecb").encrypt
+    cipher.random_key
+    expected = cipher.update("data") << cipher.final
+    assert_equal 16, expected.bytesize
+
+    # Buffer is supplied
+    cipher.reset
+    buf = String.new
+    assert_same buf, cipher.update("data", buf)
+    assert_equal expected, buf + cipher.final
+
+    # Buffer is frozen
+    cipher.reset
+    assert_raise(FrozenError) { cipher.update("data", String.new.freeze) }
+
+    # Buffer is a shared string [ruby-core:120141] [Bug #20937]
+    cipher.reset
+    buf = "x" * 1024
+    shared = buf[-("data".bytesize + 32)..-1]
+    assert_same shared, cipher.update("data", shared)
+    assert_equal expected, shared + cipher.final
+  end
+
   def test_ciphers
-    OpenSSL::Cipher.ciphers.each{|name|
-      next if /netbsd/ =~ RUBY_PLATFORM && /idea|rc5/i =~ name
-      begin
-        assert_kind_of(OpenSSL::Cipher, OpenSSL::Cipher.new(name))
-      rescue OpenSSL::Cipher::CipherError => e
-        raise unless /wrap/ =~ name and /wrap mode not allowed/ =~ e.message
-      end
-    }
+    ciphers = OpenSSL::Cipher.ciphers
+    assert_kind_of Array, ciphers
+    assert_include ciphers, "aes-128-cbc"
+    assert_include ciphers, "aes128" # alias of aes-128-cbc
+    assert_include ciphers, "aes-128-gcm"
   end
 
   def test_AES
     pt = File.read(__FILE__)
-    %w(ECB CBC CFB OFB).each{|mode|
-      c1 = OpenSSL::Cipher::AES256.new(mode)
+    %w(ecb cbc cfb ofb).each{|mode|
+      c1 = OpenSSL::Cipher.new("aes-256-#{mode}")
       c1.encrypt
       c1.pkcs5_keyivgen("passwd")
       ct = c1.update(pt) + c1.final
 
-      c2 = OpenSSL::Cipher::AES256.new(mode)
+      c2 = OpenSSL::Cipher.new("aes-256-#{mode}")
       c2.decrypt
       c2.pkcs5_keyivgen("passwd")
       assert_equal(pt, c2.update(ct) + c2.final)
@@ -163,7 +178,7 @@
   def test_update_raise_if_key_not_set
     assert_raise(OpenSSL::Cipher::CipherError) do
       # it caused OpenSSL SEGV by uninitialized key [Bug #2768]
-      OpenSSL::Cipher::AES128.new("ECB").update "." * 17
+      OpenSSL::Cipher.new("aes-128-ecb").update "." * 17
     end
   end
 
@@ -174,6 +189,48 @@
     assert_not_predicate(cipher, :authenticated?)
   end
 
+  def test_aes_ccm
+    # RFC 3610 Section 8, Test Case 1
+    key = ["c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"].pack("H*")
+    iv =  ["00000003020100a0a1a2a3a4a5"].pack("H*")
+    aad = ["0001020304050607"].pack("H*")
+    pt =  ["08090a0b0c0d0e0f101112131415161718191a1b1c1d1e"].pack("H*")
+    ct =  ["588c979a61c663d2f066d0c2c0f989806d5f6b61dac384"].pack("H*")
+    tag = ["17e8d12cfdf926e0"].pack("H*")
+
+    kwargs = {auth_tag_len: 8, iv_len: 13, key: key, iv: iv}
+    cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad)
+    assert_equal ct, cipher.update(pt) << cipher.final
+    assert_equal tag, cipher.auth_tag
+    cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad)
+    assert_equal pt, cipher.update(ct) << cipher.final
+
+    # truncated tag is accepted
+    cipher = new_encryptor("aes-128-ccm", **kwargs, ccm_data_len: pt.length, auth_data: aad)
+    assert_equal ct, cipher.update(pt) << cipher.final
+    assert_equal tag[0, 8], cipher.auth_tag(8)
+    cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag[0, 8], auth_data: aad)
+    assert_equal pt, cipher.update(ct) << cipher.final
+
+    # wrong tag is rejected
+    tag2 = tag.dup
+    tag2.setbyte(-1, (tag2.getbyte(-1) + 1) & 0xff)
+    cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag2, auth_data: aad)
+    assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) }
+
+    # wrong aad is rejected
+    aad2 = aad[0..-2] << aad[-1].succ
+    cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct.length, auth_tag: tag, auth_data: aad2)
+    assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct) }
+
+    # wrong ciphertext is rejected
+    ct2 = ct[0..-2] << ct[-1].succ
+    cipher = new_decryptor("aes-128-ccm", **kwargs, ccm_data_len: ct2.length, auth_tag: tag, auth_data: aad)
+    assert_raise(OpenSSL::Cipher::CipherError) { cipher.update(ct2) }
+  end if has_cipher?("aes-128-ccm") &&
+         OpenSSL::Cipher.new("aes-128-ccm").authenticated? &&
+         openssl?(1, 1, 1, 0x03, 0xf) # version >= 1.1.1c
+
   def test_aes_gcm
     # GCM spec Appendix B Test Case 4
     key = ["feffe9928665731c6d6a8f9467308308"].pack("H*")
@@ -298,6 +355,22 @@
     assert_equal tag1, tag2
   end
 
+  def test_aes_keywrap_pad
+    # RFC 5649 Section 6; The second example
+    kek = ["5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8"].pack("H*")
+    key = ["466f7250617369"].pack("H*")
+    wrap = ["afbeb0f07dfbf5419200f2ccb50bb24f"].pack("H*")
+
+    begin
+      cipher = OpenSSL::Cipher.new("id-aes192-wrap-pad").encrypt
+    rescue OpenSSL::Cipher::CipherError, RuntimeError
+      omit "id-aes192-wrap-pad is not supported: #$!"
+    end
+    cipher.key = kek
+    ct = cipher.update(key) << cipher.final
+    assert_equal wrap, ct
+  end
+
   def test_non_aead_cipher_set_auth_data
     assert_raise(OpenSSL::Cipher::CipherError) {
       cipher = OpenSSL::Cipher.new("aes-128-cfb").encrypt
@@ -305,6 +378,21 @@
     }
   end
 
+  def test_crypt_after_key
+    key = ["2b7e151628aed2a6abf7158809cf4f3c"].pack("H*")
+    %w'ecb cbc cfb ctr gcm'.each do |c|
+      cipher = OpenSSL::Cipher.new("aes-128-#{c}")
+      cipher.key = key
+      cipher.encrypt
+      assert_raise(OpenSSL::Cipher::CipherError) { cipher.update("") }
+
+      cipher = OpenSSL::Cipher.new("aes-128-#{c}")
+      cipher.key = key
+      cipher.decrypt
+      assert_raise(OpenSSL::Cipher::CipherError) { cipher.update("") }
+    end
+  end
+
   private
 
   def new_encryptor(algo, **kwargs)
diff -ruN ruby-2.5.9.orig/test/openssl/test_config.rb ruby-2.5.9/test/openssl/test_config.rb
--- ruby-2.5.9.orig/test/openssl/test_config.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_config.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -39,6 +39,7 @@
     assert_equal("[ default ]\n\n", c.to_s)
     c = OpenSSL::Config.parse(@it.to_s)
     assert_equal(['CA_default', 'ca', 'default'], c.sections.sort)
+    assert_predicate(c, :frozen?)
   end
 
   def test_s_parse_format
@@ -61,14 +62,14 @@
 [default1  default2]\t\t  # space is allowed in section name
           fo =b  ar       # space allowed in value
 [emptysection]
- [doller ]
+ [dollar ]
 foo=bar
 bar = $(foo)
 baz = 123$(default::bar)456${foo}798
 qux = ${baz}
 quxx = $qux.$qux
 __EOC__
-    assert_equal(['default', 'default1  default2', 'doller', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort)
+    assert_equal(['default', 'default1  default2', 'dollar', 'emptysection', 'foo', 'foo\\bar'], c.sections.sort)
     assert_equal(['', 'a', 'bar', 'baz', 'd', 'dq', 'dq2', 'esc', 'foo\\bar', 'sq'], c['default'].keys.sort)
     assert_equal('c', c['default'][''])
     assert_equal('', c['default']['a'])
@@ -84,29 +85,26 @@
     assert_equal('baz', c['foo\\bar']['foo\\bar'])
     assert_equal('b  ar', c['default1  default2']['fo'])
 
-    # dolloer
-    assert_equal('bar', c['doller']['foo'])
-    assert_equal('bar', c['doller']['bar'])
-    assert_equal('123baz456bar798', c['doller']['baz'])
-    assert_equal('123baz456bar798', c['doller']['qux'])
-    assert_equal('123baz456bar798.123baz456bar798', c['doller']['quxx'])
+    # dollar
+    assert_equal('bar', c['dollar']['foo'])
+    assert_equal('bar', c['dollar']['bar'])
+    assert_equal('123baz456bar798', c['dollar']['baz'])
+    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 +115,54 @@
 [third                    # section not terminated
 __EOC__
     end
-    assert_equal("error in line 7: missing close square bracket", excn.message)
+  end
+
+  def test_s_parse_include
+    if !openssl?(1, 1, 1, 2)
+      # OpenSSL < 1.1.1 parses .include directive as a normal assignment
+      pend ".include directive is not supported"
+    end
+
+    in_tmpdir("ossl-config-include-test") do |dir|
+      Dir.mkdir("child")
+      File.write("child/a.conf", <<~__EOC__)
+        [default]
+        file-a = a.conf
+        [sec-a]
+        a = 123
+      __EOC__
+      File.write("child/b.cnf", <<~__EOC__)
+        [default]
+        file-b = b.cnf
+        [sec-b]
+        b = 123
+      __EOC__
+      File.write("include-child.conf", <<~__EOC__)
+        key_outside_section = value_a
+        .include child
+      __EOC__
+
+      include_file = <<~__EOC__
+        [default]
+        file-main = unnamed
+        [sec-main]
+        main = 123
+        .include = include-child.conf
+      __EOC__
+
+      # Include a file by relative path
+      c1 = OpenSSL::Config.parse(include_file)
+      assert_equal(["default", "sec-a", "sec-b", "sec-main"], c1.sections.sort)
+      assert_equal(["file-a", "file-b", "file-main"], c1["default"].keys.sort)
+      assert_equal({"a" => "123"}, c1["sec-a"])
+      assert_equal({"b" => "123"}, c1["sec-b"])
+      assert_equal({"main" => "123", "key_outside_section" => "value_a"}, c1["sec-main"])
+
+      # Relative paths are from the working directory
+      # Inclusion fails, but the error is ignored silently
+      c2 = Dir.chdir("child") { OpenSSL::Config.parse(include_file) }
+      assert_equal(["default", "sec-main"], c2.sections.sort)
+    end
   end
 
   def test_s_load
@@ -134,10 +179,17 @@
     }
   end
 
+  def test_s_parse_config
+    ret = OpenSSL::Config.parse_config(@it.to_s)
+    assert_equal(@it.sections.sort, ret.keys.sort)
+    assert_equal(@it["default"], ret["default"])
+  end
+
   def test_initialize
     c = OpenSSL::Config.new
     assert_equal("", c.to_s)
     assert_equal([], c.sections)
+    assert_predicate(c, :frozen?)
   end
 
   def test_initialize_with_empty_file
@@ -166,33 +218,14 @@
   end
 
   def test_get_value_ENV
+    # LibreSSL removed support for NCONF_get_string(conf, "ENV", str)
+    return if libressl?
+
     key = ENV.keys.first
     assert_not_nil(key) # make sure we have at least one ENV var.
     assert_equal(ENV[key], @it.get_value('ENV', key))
   end
 
-  def test_value
-    # suppress deprecation warnings
-    EnvUtil.suppress_warning do
-      assert_equal('CA_default', @it.value('ca', 'default_ca'))
-      assert_equal(nil, @it.value('ca', 'no such key'))
-      assert_equal(nil, @it.value('no such section', 'no such key'))
-      assert_equal('.', @it.value('', 'HOME'))
-      assert_equal('.', @it.value(nil, 'HOME'))
-      assert_equal('.', @it.value('HOME'))
-      # fallback to 'default' ugly...
-      assert_equal('.', @it.value('unknown', 'HOME'))
-    end
-  end
-
-  def test_value_ENV
-    EnvUtil.suppress_warning do
-      key = ENV.keys.first
-      assert_not_nil(key) # make sure we have at least one ENV var.
-      assert_equal(ENV[key], @it.value('ENV', key))
-    end
-  end
-
   def test_aref
     assert_equal({'HOME' => '.'}, @it['default'])
     assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it['CA_default'])
@@ -200,56 +233,19 @@
     assert_equal({}, @it[''])
   end
 
-  def test_section
-    EnvUtil.suppress_warning do
-      assert_equal({'HOME' => '.'}, @it.section('default'))
-      assert_equal({'dir' => './demoCA', 'certs' => './certs'}, @it.section('CA_default'))
-      assert_equal({}, @it.section('no_such_section'))
-      assert_equal({}, @it.section(''))
-    end
-  end
-
   def test_sections
     assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort)
-    @it['new_section'] = {'foo' => 'bar'}
-    assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort)
-    @it['new_section'] = {}
-    assert_equal(['CA_default', 'ca', 'default', 'new_section'], @it.sections.sort)
-  end
-
-  def test_add_value
-    c = OpenSSL::Config.new
-    assert_equal("", c.to_s)
-    # add key
-    c.add_value('default', 'foo', 'bar')
-    assert_equal("[ default ]\nfoo=bar\n\n", c.to_s)
-    # add another key
-    c.add_value('default', 'baz', 'qux')
-    assert_equal('bar', c['default']['foo'])
-    assert_equal('qux', c['default']['baz'])
-    # update the value
-    c.add_value('default', 'baz', 'quxxx')
-    assert_equal('bar', c['default']['foo'])
-    assert_equal('quxxx', c['default']['baz'])
-    # add section and key
-    c.add_value('section', 'foo', 'bar')
-    assert_equal('bar', c['default']['foo'])
-    assert_equal('quxxx', c['default']['baz'])
-    assert_equal('bar', c['section']['foo'])
-  end
-
-  def test_aset
-    @it['foo'] = {'bar' => 'baz'}
-    assert_equal({'bar' => 'baz'}, @it['foo'])
-    @it['foo'] = {'bar' => 'qux', 'baz' => 'quxx'}
-    assert_equal({'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
-
-    # OpenSSL::Config is add only for now.
-    @it['foo'] = {'foo' => 'foo'}
-    assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
-    # you cannot override or remove any section and key.
-    @it['foo'] = {}
-    assert_equal({'foo' => 'foo', 'bar' => 'qux', 'baz' => 'quxx'}, @it['foo'])
+    Tempfile.create("openssl.cnf") { |f|
+      f.write File.read(@tmpfile.path)
+      f.puts "[ new_section ]"
+      f.puts "foo = bar"
+      f.puts "[ empty_section ]"
+      f.close
+
+      c = OpenSSL::Config.new(f.path)
+      assert_equal(['CA_default', 'ca', 'default', 'empty_section', 'new_section'],
+                   c.sections.sort)
+    }
   end
 
   def test_each
@@ -271,33 +267,34 @@
     assert_match(/#<OpenSSL::Config sections=\[.*\]>/, @it.inspect)
   end
 
-  def test_freeze
-    c = OpenSSL::Config.new
-    c['foo'] = [['key', 'value']]
-    c.freeze
+  def test_dup
+    assert_equal(['CA_default', 'ca', 'default'], @it.sections.sort)
+    c1 = @it.dup
+    assert_predicate(c1, :frozen?)
+    assert_equal(@it.sections.sort, c1.sections.sort)
+    c2 = @it.clone
+    assert_predicate(c2, :frozen?)
+    assert_equal(@it.sections.sort, c2.sections.sort)
+  end
 
-    bug = '[ruby-core:18377]'
-    # RuntimeError for 1.9, TypeError for 1.8
-    e = assert_raise(TypeError, bug) do
-      c['foo'] = [['key', 'wrong']]
+  if respond_to?(:ractor)
+    ractor
+    def test_ractor
+      assert(Ractor.shareable?(@it))
+      assert(Ractor.shareable?(OpenSSL::Config.parse("[empty]\n")))
+      assert(Ractor.shareable?(OpenSSL::Config::DEFAULT_CONFIG_FILE))
     end
-    assert_match(/can't modify/, e.message, bug)
   end
 
-  def test_dup
-    assert(!@it.sections.empty?)
-    c = @it.dup
-    assert_equal(@it.sections.sort, c.sections.sort)
-    @it['newsection'] = {'a' => 'b'}
-    assert_not_equal(@it.sections.sort, c.sections.sort)
-  end
+  private
 
-  def test_clone
-    assert(!@it.sections.empty?)
-    c = @it.clone
-    assert_equal(@it.sections.sort, c.sections.sort)
-    @it['newsection'] = {'a' => 'b'}
-    assert_not_equal(@it.sections.sort, c.sections.sort)
+  def in_tmpdir(*args)
+    Dir.mktmpdir(*args) do |dir|
+      dir = File.realpath(dir)
+      Dir.chdir(dir) do
+        yield dir
+      end
+    end
   end
 end
 
diff -ruN ruby-2.5.9.orig/test/openssl/test_digest.rb ruby-2.5.9/test/openssl/test_digest.rb
--- ruby-2.5.9.orig/test/openssl/test_digest.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_digest.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -21,8 +21,8 @@
     @d1 << data
     assert_equal(bin, @d1.digest)
     assert_equal(hex, @d1.hexdigest)
-    assert_equal(bin, OpenSSL::Digest::MD5.digest(data))
-    assert_equal(hex, OpenSSL::Digest::MD5.hexdigest(data))
+    assert_equal(bin, OpenSSL::Digest.digest('MD5', data))
+    assert_equal(hex, OpenSSL::Digest.hexdigest('MD5', data))
   end
 
   def test_eql
@@ -54,13 +54,9 @@
   end
 
   def test_digest_constants
-    algs = %w(MD4 MD5 RIPEMD160 SHA1 SHA224 SHA256 SHA384 SHA512)
-    if !libressl? && !openssl?(1, 1, 0)
-      algs += %w(DSS1 SHA)
-    end
-    algs.each do |alg|
-      assert_not_nil(OpenSSL::Digest.new(alg))
-      klass = OpenSSL::Digest.const_get(alg)
+    %w{MD5 SHA1 SHA224 SHA256 SHA384 SHA512}.each do |name|
+      assert_not_nil(OpenSSL::Digest.new(name))
+      klass = OpenSSL::Digest.const_get(name.tr('-', '_'))
       assert_not_nil(klass.new)
     end
   end
@@ -71,7 +67,7 @@
   end
 
   def encode16(str)
-    str.unpack("H*").first
+    str.unpack1("H*")
   end
 
   def test_sha2
@@ -80,15 +76,39 @@
     sha384_a = "54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"
     sha512_a = "1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"
 
-    assert_equal(sha224_a, OpenSSL::Digest::SHA224.hexdigest("a"))
-    assert_equal(sha256_a, OpenSSL::Digest::SHA256.hexdigest("a"))
-    assert_equal(sha384_a, OpenSSL::Digest::SHA384.hexdigest("a"))
-    assert_equal(sha512_a, OpenSSL::Digest::SHA512.hexdigest("a"))
-
-    assert_equal(sha224_a, encode16(OpenSSL::Digest::SHA224.digest("a")))
-    assert_equal(sha256_a, encode16(OpenSSL::Digest::SHA256.digest("a")))
-    assert_equal(sha384_a, encode16(OpenSSL::Digest::SHA384.digest("a")))
-    assert_equal(sha512_a, encode16(OpenSSL::Digest::SHA512.digest("a")))
+    assert_equal(sha224_a, OpenSSL::Digest.hexdigest('SHA224', "a"))
+    assert_equal(sha256_a, OpenSSL::Digest.hexdigest('SHA256', "a"))
+    assert_equal(sha384_a, OpenSSL::Digest.hexdigest('SHA384', "a"))
+    assert_equal(sha512_a, OpenSSL::Digest.hexdigest('SHA512', "a"))
+
+    assert_equal(sha224_a, encode16(OpenSSL::Digest.digest('SHA224', "a")))
+    assert_equal(sha256_a, encode16(OpenSSL::Digest.digest('SHA256', "a")))
+    assert_equal(sha384_a, encode16(OpenSSL::Digest.digest('SHA384', "a")))
+    assert_equal(sha512_a, encode16(OpenSSL::Digest.digest('SHA512', "a")))
+  end
+
+  def test_sha512_truncate
+    pend "SHA512_224 is not implemented" unless digest_available?('sha512-224')
+    sha512_224_a = "d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327"
+    sha512_256_a = "455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8"
+
+    assert_equal(sha512_224_a, OpenSSL::Digest.hexdigest('SHA512-224', "a"))
+    assert_equal(sha512_256_a, OpenSSL::Digest.hexdigest('SHA512-256', "a"))
+
+    assert_equal(sha512_224_a, encode16(OpenSSL::Digest.digest('SHA512-224', "a")))
+    assert_equal(sha512_256_a, encode16(OpenSSL::Digest.digest('SHA512-256', "a")))
+  end
+
+  def test_sha3
+    pend "SHA3 is not implemented" unless digest_available?('sha3-224')
+    s224 = '6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7'
+    s256 = 'a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a'
+    s384 = '0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004'
+    s512 = 'a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26'
+    assert_equal(OpenSSL::Digest.hexdigest('SHA3-224', ""), s224)
+    assert_equal(OpenSSL::Digest.hexdigest('SHA3-256', ""), s256)
+    assert_equal(OpenSSL::Digest.hexdigest('SHA3-384', ""), s384)
+    assert_equal(OpenSSL::Digest.hexdigest('SHA3-512', ""), s512)
   end
 
   def test_digest_by_oid_and_name_sha2
@@ -106,6 +126,15 @@
     end
   end
 
+  def test_digests
+    digests = OpenSSL::Digest.digests
+    assert_kind_of Array, digests
+    assert_include digests, "md5"
+    assert_include digests, "sha1"
+    assert_include digests, "sha256"
+    assert_include digests, "sha512"
+  end
+
   private
 
   def check_digest(oid)
@@ -116,6 +145,11 @@
     d = OpenSSL::Digest.new(oid.oid)
     assert_not_nil(d)
   end
+
+  def digest_available?(name)
+    @digests ||= OpenSSL::Digest.digests
+    @digests.include?(name)
+  end
 end
 
 end
diff -ruN ruby-2.5.9.orig/test/openssl/test_engine.rb ruby-2.5.9/test/openssl/test_engine.rb
--- ruby-2.5.9.orig/test/openssl/test_engine.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_engine.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL) && defined?(OpenSSL::Engine)
@@ -26,7 +26,7 @@
     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 @@
 
   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,11 +43,11 @@
 
   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"
-      assert_equal(OpenSSL::Digest::SHA1.digest(data), digest.digest(data))
+      assert_equal(OpenSSL::Digest.digest('SHA1', data), digest.digest(data))
     end;
   end
 
@@ -59,12 +59,21 @@
     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 @@
 
   # 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 -ruN ruby-2.5.9.orig/test/openssl/test_fips.rb ruby-2.5.9/test/openssl/test_fips.rb
--- ruby-2.5.9.orig/test/openssl/test_fips.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_fips.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,25 +1,49 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 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 -ruN ruby-2.5.9.orig/test/openssl/test_hmac.rb ruby-2.5.9/test/openssl/test_hmac.rb
--- ruby-2.5.9.orig/test/openssl/test_hmac.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_hmac.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -10,12 +10,14 @@
     hmac.update("Hi There")
     assert_equal ["9294727a3638bb1c13f48ef8158bfc9d"].pack("H*"), hmac.digest
     assert_equal "9294727a3638bb1c13f48ef8158bfc9d", hmac.hexdigest
+    assert_equal "kpRyejY4uxwT9I74FYv8nQ==", hmac.base64digest
 
     # RFC 4231 4.2. Test Case 1
     hmac = OpenSSL::HMAC.new(["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*"), "SHA224")
     hmac.update("Hi There")
     assert_equal ["896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22"].pack("H*"), hmac.digest
     assert_equal "896fb1128abbdf196832107cd49df33f47b4b1169912ba4f53684b22", hmac.hexdigest
+    assert_equal "iW+xEoq73xloMhB81J3zP0e0sRaZErpPU2hLIg==", hmac.base64digest
   end
 
   def test_dup
@@ -39,6 +41,35 @@
     second = h1.update("test").hexdigest
     assert_equal first, second
   end
+
+  def test_eq
+    h1 = OpenSSL::HMAC.new("KEY", "MD5")
+    h2 = OpenSSL::HMAC.new("KEY", OpenSSL::Digest.new("MD5"))
+    h3 = OpenSSL::HMAC.new("FOO", "MD5")
+
+    assert_equal h1, h2
+    refute_equal h1, h2.digest
+    refute_equal h1, h3
+  end
+
+  def test_singleton_methods
+    # RFC 2202 2. Test Cases for HMAC-MD5
+    key = ["0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"].pack("H*")
+    digest = OpenSSL::HMAC.digest("MD5", key, "Hi There")
+    assert_equal ["9294727a3638bb1c13f48ef8158bfc9d"].pack("H*"), digest
+    hexdigest = OpenSSL::HMAC.hexdigest("MD5", key, "Hi There")
+    assert_equal "9294727a3638bb1c13f48ef8158bfc9d", hexdigest
+    b64digest = OpenSSL::HMAC.base64digest("MD5", key, "Hi There")
+    assert_equal "kpRyejY4uxwT9I74FYv8nQ==", b64digest
+  end
+
+  def test_zero_length_key
+    # Empty string as the key
+    hexdigest = OpenSSL::HMAC.hexdigest("SHA256", "\0"*32, "test")
+    assert_equal "43b0cef99265f9e34c10ea9d3501926d27b39f57c6d674561d8ba236e7a819fb", hexdigest
+    hexdigest = OpenSSL::HMAC.hexdigest("SHA256", "", "test")
+    assert_equal "43b0cef99265f9e34c10ea9d3501926d27b39f57c6d674561d8ba236e7a819fb", hexdigest
+  end
 end
 
 end
diff -ruN ruby-2.5.9.orig/test/openssl/test_kdf.rb ruby-2.5.9/test/openssl/test_kdf.rb
--- ruby-2.5.9.orig/test/openssl/test_kdf.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_kdf.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
diff -ruN ruby-2.5.9.orig/test/openssl/test_ns_spki.rb ruby-2.5.9/test/openssl/test_ns_spki.rb
--- ruby-2.5.9.orig/test/openssl/test_ns_spki.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_ns_spki.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -9,7 +9,7 @@
     # This request data is adopt from the specification of
     # "Netscape Extensions for User Key Generation".
     # -- http://wp.netscape.com/eng/security/comm4-keygen.html
-    @b64  = "MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue+PtwBRE6XfV"
+    @b64 = +"MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue+PtwBRE6XfV"
     @b64 << "WtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID"
     @b64 << "AQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n/S"
     @b64 << "r/7iJNroWlSzSMtTiQTEB+ADWHGj9u1xrUrOilq/o2cuQxIfZcNZkYAkWP4DubqW"
@@ -22,7 +22,7 @@
     spki = OpenSSL::Netscape::SPKI.new
     spki.challenge = "RandomString"
     spki.public_key = key1.public_key
-    spki.sign(key1, OpenSSL::Digest::SHA1.new)
+    spki.sign(key1, OpenSSL::Digest.new('SHA256'))
     assert(spki.verify(spki.public_key))
     assert(spki.verify(key1.public_key))
     assert(!spki.verify(key2.public_key))
@@ -38,13 +38,13 @@
   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 -ruN ruby-2.5.9.orig/test/openssl/test_ocsp.rb ruby-2.5.9/test/openssl/test_ocsp.rb
--- ruby-2.5.9.orig/test/openssl/test_ocsp.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_ocsp.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
@@ -50,26 +50,26 @@
     cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert)
     assert_kind_of OpenSSL::OCSP::CertificateId, cid
     assert_equal @cert.serial, cid.serial
-    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new)
+    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA256'))
     assert_kind_of OpenSSL::OCSP::CertificateId, cid
     assert_equal @cert.serial, cid.serial
   end
 
   def test_certificate_id_issuer_name_hash
     cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert)
-    assert_equal OpenSSL::Digest::SHA1.hexdigest(@cert.issuer.to_der), cid.issuer_name_hash
+    assert_equal OpenSSL::Digest.hexdigest('SHA1', @cert.issuer.to_der), cid.issuer_name_hash
     assert_equal "d91f736ac4dc3242f0fb9b77a3149bd83c5c43d0", cid.issuer_name_hash
   end
 
   def test_certificate_id_issuer_key_hash
     cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert)
-    assert_equal OpenSSL::Digest::SHA1.hexdigest(OpenSSL::ASN1.decode(@ca_cert.to_der).value[0].value[6].value[1].value), cid.issuer_key_hash
+    assert_equal OpenSSL::Digest.hexdigest('SHA1', OpenSSL::ASN1.decode(@ca_cert.to_der).value[0].value[6].value[1].value), cid.issuer_key_hash
     assert_equal "d1fef9fbf8ae1bc160cbfa03e2596dd873089213", cid.issuer_key_hash
   end
 
   def test_certificate_id_hash_algorithm
-    cid_sha1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
-    cid_sha256 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA256.new)
+    cid_sha1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
+    cid_sha256 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA256'))
     assert_equal "sha1", cid_sha1.hash_algorithm
     assert_equal "sha256", cid_sha256.hash_algorithm
   end
@@ -84,6 +84,7 @@
     assert_equal [cid.issuer_key_hash].pack("H*"), asn1.value[2].value
     assert_equal @cert.serial, asn1.value[3].value
     assert_equal der, OpenSSL::OCSP::CertificateId.new(der).to_der
+    assert_equal der, OpenSSL::OCSP::CertificateId.new(asn1).to_der
   end
 
   def test_certificate_id_dup
@@ -93,12 +94,12 @@
 
   def test_request_der
     request = OpenSSL::OCSP::Request.new
-    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
     request.add_certid(cid)
     request.sign(@cert, @cert_key, [@ca_cert], 0)
     asn1 = OpenSSL::ASN1.decode(request.to_der)
     assert_equal cid.to_der, asn1.value[0].value.find { |a| a.tag_class == :UNIVERSAL }.value[0].value[0].to_der
-    assert_equal OpenSSL::ASN1.ObjectId("sha1WithRSAEncryption").to_der, asn1.value[1].value[0].value[0].value[0].to_der
+    assert_equal OpenSSL::ASN1.ObjectId("sha256WithRSAEncryption").to_der, asn1.value[1].value[0].value[0].value[0].to_der
     assert_equal @cert.to_der, asn1.value[1].value[0].value[2].value[0].value[0].to_der
     assert_equal @ca_cert.to_der, asn1.value[1].value[0].value[2].value[0].value[1].to_der
     assert_equal asn1.to_der, OpenSSL::OCSP::Request.new(asn1.to_der).to_der
@@ -122,14 +123,7 @@
 
     assert_equal true, req.verify([@cert], store, OpenSSL::OCSP::NOINTERN)
     ret = req.verify([@cert], store)
-    if ret || openssl?(1, 0, 2)
-      assert_equal true, ret
-    else
-      # RT2560; OCSP_request_verify() does not find signer cert from 'certs' when
-      # OCSP_NOINTERN is not specified.
-      # fixed by OpenSSL 1.0.1j, 1.0.2
-      pend "RT2560: ocsp_req_find_signer"
-    end
+    assert_equal true, ret
 
     # not signed
     req = OpenSSL::OCSP::Request.new.add_certid(cid)
@@ -163,14 +157,14 @@
 
   def test_request_dup
     request = OpenSSL::OCSP::Request.new
-    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
     request.add_certid(cid)
     assert_equal request.to_der, request.dup.to_der
   end
 
   def test_basic_response_der
     bres = OpenSSL::OCSP::BasicResponse.new
-    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
     bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, [])
     bres.add_nonce("NONCE")
     bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0)
@@ -213,7 +207,7 @@
 
   def test_basic_response_dup
     bres = OpenSSL::OCSP::BasicResponse.new
-    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
     bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, [])
     bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0)
     assert_equal bres.to_der, bres.dup.to_der
@@ -222,9 +216,9 @@
   def test_basic_response_response_operations
     bres = OpenSSL::OCSP::BasicResponse.new
     now = Time.at(Time.now.to_i)
-    cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
-    cid2 = OpenSSL::OCSP::CertificateId.new(@ocsp_cert, @ca_cert, OpenSSL::Digest::SHA1.new)
-    cid3 = OpenSSL::OCSP::CertificateId.new(@ca_cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+    cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
+    cid2 = OpenSSL::OCSP::CertificateId.new(@ocsp_cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
+    cid3 = OpenSSL::OCSP::CertificateId.new(@ca_cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
     bres.add_status(cid1, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, now - 400, -300, nil, nil)
     bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, -300, 500, [])
 
@@ -234,7 +228,7 @@
     assert_equal OpenSSL::OCSP::V_CERTSTATUS_REVOKED, single.cert_status
     assert_equal OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, single.revocation_reason
     assert_equal now - 400, single.revocation_time
-    assert_in_delta (now - 301), single.this_update, 1
+    assert_in_delta (now - 300), single.this_update, 1
     assert_equal nil, single.next_update
     assert_equal [], single.extensions
 
@@ -256,8 +250,8 @@
 
   def test_single_response_check_validity
     bres = OpenSSL::OCSP::BasicResponse.new
-    cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
-    cid2 = OpenSSL::OCSP::CertificateId.new(@ocsp_cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+    cid1 = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
+    cid2 = OpenSSL::OCSP::CertificateId.new(@ocsp_cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
     bres.add_status(cid1, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, -50, [])
     bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_REVOKED, OpenSSL::OCSP::REVOKED_STATUS_UNSPECIFIED, -400, -300, nil, [])
     bres.add_status(cid2, OpenSSL::OCSP::V_CERTSTATUS_GOOD, nil, nil, Time.now + 100, nil, nil)
@@ -276,7 +270,7 @@
 
   def test_response
     bres = OpenSSL::OCSP::BasicResponse.new
-    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
     bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, [])
     bres.sign(@ocsp_cert, @ocsp_key, [])
     res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres)
@@ -287,7 +281,7 @@
 
   def test_response_der
     bres = OpenSSL::OCSP::BasicResponse.new
-    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest::SHA1.new)
+    cid = OpenSSL::OCSP::CertificateId.new(@cert, @ca_cert, OpenSSL::Digest.new('SHA1'))
     bres.add_status(cid, OpenSSL::OCSP::V_CERTSTATUS_GOOD, 0, nil, -300, 500, [])
     bres.sign(@ocsp_cert, @ocsp_key, [@ca_cert], 0)
     res = OpenSSL::OCSP::Response.create(OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL, bres)
diff -ruN ruby-2.5.9.orig/test/openssl/test_ossl.rb ruby-2.5.9/test/openssl/test_ossl.rb
--- ruby-2.5.9.orig/test/openssl/test_ossl.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/test_ossl.rb	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+require_relative "utils"
+
+if defined?(OpenSSL)
+
+class OpenSSL::OSSL < OpenSSL::SSLTestCase
+  def test_fixed_length_secure_compare
+    assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "a") }
+    assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "aa") }
+
+    assert OpenSSL.fixed_length_secure_compare("aaa", "aaa")
+    assert OpenSSL.fixed_length_secure_compare(
+      OpenSSL::Digest.digest('SHA256', "aaa"), OpenSSL::Digest::SHA256.digest("aaa")
+    )
+
+    assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "aaaa") }
+    refute OpenSSL.fixed_length_secure_compare("aaa", "baa")
+    refute OpenSSL.fixed_length_secure_compare("aaa", "aba")
+    refute OpenSSL.fixed_length_secure_compare("aaa", "aab")
+    assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "aaab") }
+    assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "b") }
+    assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "bb") }
+    refute OpenSSL.fixed_length_secure_compare("aaa", "bbb")
+    assert_raise(ArgumentError) { OpenSSL.fixed_length_secure_compare("aaa", "bbbb") }
+  end
+
+  def test_secure_compare
+    refute OpenSSL.secure_compare("aaa", "a")
+    refute OpenSSL.secure_compare("aaa", "aa")
+
+    assert OpenSSL.secure_compare("aaa", "aaa")
+
+    refute OpenSSL.secure_compare("aaa", "aaaa")
+    refute OpenSSL.secure_compare("aaa", "baa")
+    refute OpenSSL.secure_compare("aaa", "aba")
+    refute OpenSSL.secure_compare("aaa", "aab")
+    refute OpenSSL.secure_compare("aaa", "aaab")
+    refute OpenSSL.secure_compare("aaa", "b")
+    refute OpenSSL.secure_compare("aaa", "bb")
+    refute OpenSSL.secure_compare("aaa", "bbb")
+    refute OpenSSL.secure_compare("aaa", "bbbb")
+  end
+
+  def test_memcmp_timing
+    begin
+      require "benchmark"
+    rescue LoadError
+      pend "Benchmark is not available in this environment. Please install it with `gem install benchmark`."
+    end
+
+    # Ensure using fixed_length_secure_compare takes almost exactly the same amount of time to compare two different strings.
+    # Regular string comparison will short-circuit on the first non-matching character, failing this test.
+    # NOTE: this test may be susceptible to noise if the system running the tests is otherwise under load.
+    a = "x" * 512_000
+    b = "#{a}y"
+    c = "y#{a}"
+    a = "#{a}x"
+
+    a_b_time = a_c_time = 0
+    100.times do
+      a_b_time += Benchmark.measure { 100.times { OpenSSL.fixed_length_secure_compare(a, b) } }.real
+      a_c_time += Benchmark.measure { 100.times { OpenSSL.fixed_length_secure_compare(a, c) } }.real
+    end
+    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 -ruN ruby-2.5.9.orig/test/openssl/test_pair.rb ruby-2.5.9/test/openssl/test_pair.rb
--- ruby-2.5.9.orig/test/openssl/test_pair.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_pair.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,8 +1,8 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 require_relative 'ut_eof'
 
-if defined?(OpenSSL)
+if defined?(OpenSSL::SSL)
 
 module OpenSSL::SSLPairM
   def setup
@@ -10,7 +10,7 @@
     ee_exts = [
       ["keyUsage", "keyEncipherment,digitalSignature", true],
     ]
-    @svr_key = OpenSSL::TestUtils::Fixtures.pkey("rsa1024")
+    @svr_key = OpenSSL::TestUtils::Fixtures.pkey("rsa-1")
     @svr_cert = issue_cert(svr_dn, @svr_key, 1, ee_exts, nil, nil)
   end
 
@@ -23,7 +23,6 @@
       sctx = OpenSSL::SSL::SSLContext.new
       sctx.cert = @svr_cert
       sctx.key = @svr_key
-      sctx.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey_dh("dh1024") }
       sctx.options |= OpenSSL::SSL::OP_NO_COMPRESSION
       ssls = OpenSSL::SSL::SSLServer.new(tcps, sctx)
       ns = ssls.accept
@@ -102,6 +101,27 @@
     }
   end
 
+  def test_getbyte
+    ssl_pair {|s1, s2|
+      s1 << "a"
+      assert_equal(97, s2.getbyte)
+    }
+  end
+
+  def test_readbyte
+    ssl_pair {|s1, s2|
+      s1 << "b"
+      assert_equal(98, s2.readbyte)
+    }
+  end
+
+  def test_readbyte_eof
+    ssl_pair {|s1, s2|
+      s2.close
+      assert_raise(EOFError) { s1.readbyte }
+    }
+  end
+
   def test_gets
     ssl_pair {|s1, s2|
       s1 << "abc\n\n$def123ghi"
@@ -116,6 +136,17 @@
     }
   end
 
+  def test_gets_chomp
+    ssl_pair {|s1, s2|
+      s1 << "line1\r\nline2\r\nline3\r\n"
+      s1.close
+
+      assert_equal("line1", s2.gets("\r\n", chomp: true))
+      assert_equal("line2\r\n", s2.gets("\r\n", chomp: false))
+      assert_equal("line3", s2.gets(chomp: true))
+    }
+  end
+
   def test_gets_eof_limit
     ssl_pair {|s1, s2|
       s1.write("hello")
@@ -128,11 +159,11 @@
     ssl_pair {|s1, s2|
       s2.write "a\nbcd"
       assert_equal("a\n", s1.gets)
-      result = ""
+      result = String.new
       result << s1.readpartial(10) until result.length == 3
       assert_equal("bcd", result)
       s2.write "efg"
-      result = ""
+      result = String.new
       result << s1.readpartial(10) until result.length == 3
       assert_equal("efg", result)
       s2.close
@@ -156,20 +187,6 @@
     }
   end
 
-  def test_puts_meta
-    ssl_pair {|s1, s2|
-      begin
-        old = $/
-        $/ = '*'
-        s1.puts 'a'
-      ensure
-        $/ = old
-      end
-      s1.close
-      assert_equal("a\n", s2.read)
-    }
-  end
-
   def test_puts_empty
     ssl_pair {|s1, s2|
       s1.puts
@@ -242,24 +259,29 @@
   def test_read_with_outbuf
     ssl_pair { |s1, s2|
       s1.write("abc\n")
-      buf = ""
+      buf = String.new
       ret = s2.read(2, buf)
       assert_same ret, buf
       assert_equal "ab", ret
 
-      buf = "garbage"
+      buf = +"garbage"
       ret = s2.read(2, buf)
       assert_same ret, buf
       assert_equal "c\n", ret
 
-      buf = "garbage"
+      buf = +"garbage"
       assert_equal :wait_readable, s2.read_nonblock(100, buf, exception: false)
-      assert_equal "", buf
+      assert_equal "garbage", buf
 
       s1.close
-      buf = "garbage"
-      assert_equal nil, s2.read(100, buf)
+      buf = +"garbage"
+      assert_nil s2.read(100, buf)
       assert_equal "", buf
+
+      buf = +"garbage"
+      ret = s2.read(0, buf)
+      assert_same buf, ret
+      assert_equal "", ret
     }
   end
 
@@ -397,7 +419,6 @@
     ctx2 = OpenSSL::SSL::SSLContext.new
     ctx2.cert = @svr_cert
     ctx2.key = @svr_key
-    ctx2.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey_dh("dh1024") }
 
     sock1, sock2 = tcp_pair
 
@@ -442,54 +463,46 @@
   end
 
   def test_connect_accept_nonblock
-    ctx = OpenSSL::SSL::SSLContext.new()
+    ctx = OpenSSL::SSL::SSLContext.new
     ctx.cert = @svr_cert
     ctx.key = @svr_key
-    ctx.tmp_dh_callback = proc { OpenSSL::TestUtils::Fixtures.pkey_dh("dh1024") }
 
     sock1, sock2 = tcp_pair
 
     th = Thread.new {
       s2 = OpenSSL::SSL::SSLSocket.new(sock2, ctx)
-      s2.sync_close = true
-      begin
+      5.times {
+        begin
+          break s2.accept_nonblock
+        rescue IO::WaitReadable
+          IO.select([s2], nil, nil, 1)
+        rescue IO::WaitWritable
+          IO.select(nil, [s2], nil, 1)
+        end
         sleep 0.2
-        s2.accept_nonblock
+      }
+    }
+
+    s1 = OpenSSL::SSL::SSLSocket.new(sock1)
+    5.times {
+      begin
+        break s1.connect_nonblock
       rescue IO::WaitReadable
-        IO.select([s2])
-        retry
+        IO.select([s1], nil, nil, 1)
       rescue IO::WaitWritable
-        IO.select(nil, [s2])
-        retry
+        IO.select(nil, [s1], nil, 1)
       end
-      s2
-    }
-
-    sleep 0.1
-    ctx = OpenSSL::SSL::SSLContext.new()
-    s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx)
-    begin
       sleep 0.2
-      s1.connect_nonblock
-    rescue IO::WaitReadable
-      IO.select([s1])
-      retry
-    rescue IO::WaitWritable
-      IO.select(nil, [s1])
-      retry
-    end
-    s1.sync_close = true
+    }
 
     s2 = th.value
 
     s1.print "a\ndef"
     assert_equal("a\n", s2.gets)
   ensure
-    th.join if th
-    s1.close if s1 && !s1.closed?
-    s2.close if s2 && !s2.closed?
-    sock1.close if sock1 && !sock1.closed?
-    sock2.close if sock2 && !sock2.closed?
+    sock1&.close
+    sock2&.close
+    th&.join
   end
 end
 
diff -ruN ruby-2.5.9.orig/test/openssl/test_pkcs12.rb ruby-2.5.9/test/openssl/test_pkcs12.rb
--- ruby-2.5.9.orig/test/openssl/test_pkcs12.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_pkcs12.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,10 +1,13 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
 
 module OpenSSL
   class TestPKCS12 < OpenSSL::TestCase
+    DEFAULT_PBE_PKEYS = "PBE-SHA1-3DES"
+    DEFAULT_PBE_CERTS = "PBE-SHA1-3DES"
+
     def setup
       super
       ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
@@ -14,47 +17,41 @@
         ["subjectKeyIdentifier","hash",false],
         ["authorityKeyIdentifier","keyid:always",false],
       ]
-      @cacert = issue_cert(ca, Fixtures.pkey("rsa2048"), 1, ca_exts, nil, nil)
+      ca_key = Fixtures.pkey("rsa-1")
+      @cacert = issue_cert(ca, ca_key, 1, ca_exts, nil, nil)
 
       inter_ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Intermediate CA")
-      inter_ca_key = OpenSSL::PKey.read <<-_EOS_
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDp7hIG0SFMG/VWv1dBUWziAPrNmkMXJgTCAoB7jffzRtyyN04K
-oq/89HAszTMStZoMigQURfokzKsjpUp8OYCAEsBtt9d5zPndWMz/gHN73GrXk3LT
-ZsxEn7Xv5Da+Y9F/Hx2QZUHarV5cdZixq2NbzWGwrToogOQMh2pxN3Z/0wIDAQAB
-AoGBAJysUyx3olpsGzv3OMRJeahASbmsSKTXVLZvoIefxOINosBFpCIhZccAG6UV
-5c/xCvS89xBw8aD15uUfziw3AuT8QPEtHCgfSjeT7aWzBfYswEgOW4XPuWr7EeI9
-iNHGD6z+hCN/IQr7FiEBgTp6A+i/hffcSdR83fHWKyb4M7TRAkEA+y4BNd668HmC
-G5MPRx25n6LixuBxrNp1umfjEI6UZgEFVpYOg4agNuimN6NqM253kcTR94QNTUs5
-Kj3EhG1YWwJBAO5rUjiOyCNVX2WUQrOMYK/c1lU7fvrkdygXkvIGkhsPoNRzLPeA
-HGJszKtrKD8bNihWpWNIyqKRHfKVD7yXT+kCQGCAhVCIGTRoypcDghwljHqLnysf
-ci0h5ZdPcIqc7ODfxYhFsJ/Rql5ONgYsT5Ig/+lOQAkjf+TRYM4c2xKx2/8CQBvG
-jv6dy70qDgIUgqzONtlmHeYyFzn9cdBO5sShdVYHvRHjFSMEXsosqK9zvW2UqvuK
-FJx7d3f29gkzynCLJDkCQGQZlEZJC4vWmWJGRKJ24P6MyQn3VsPfErSKOg4lvyM3
-Li8JsX5yIiuVYaBg/6ha3tOg4TCa5K/3r3tVliRZ2Es=
------END RSA PRIVATE KEY-----
-      _EOS_
-      @inter_cacert = issue_cert(inter_ca, inter_ca_key, 2, ca_exts, @cacert, Fixtures.pkey("rsa2048"))
+      inter_ca_key = Fixtures.pkey("rsa-2")
+      @inter_cacert = issue_cert(inter_ca, inter_ca_key, 2, ca_exts, @cacert, ca_key)
 
       exts = [
         ["keyUsage","digitalSignature",true],
         ["subjectKeyIdentifier","hash",false],
       ]
       ee = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Ruby PKCS12 Test Certificate")
-      @mykey = Fixtures.pkey("rsa1024")
+      @mykey = Fixtures.pkey("rsa-3")
       @mycert = issue_cert(ee, @mykey, 3, exts, @inter_cacert, inter_ca_key)
     end
 
-    def test_create
+    def test_create_single_key_single_cert
       pkcs12 = OpenSSL::PKCS12.create(
         "omg",
         "hello",
         @mykey,
-        @mycert
+        @mycert,
+        nil,
+        DEFAULT_PBE_PKEYS,
+        DEFAULT_PBE_CERTS,
       )
-      assert_equal @mycert.to_der, pkcs12.certificate.to_der
+      assert_equal @mycert, pkcs12.certificate
       assert_equal @mykey.to_der, pkcs12.key.to_der
       assert_nil pkcs12.ca_certs
+
+      der = pkcs12.to_der
+      decoded = OpenSSL::PKCS12.new(der, "omg")
+      assert_equal @mykey.to_der, decoded.key.to_der
+      assert_equal @mycert, decoded.certificate
+      assert_equal [], Array(decoded.ca_certs)
     end
 
     def test_create_no_pass
@@ -62,14 +59,17 @@
         nil,
         "hello",
         @mykey,
-        @mycert
+        @mycert,
+        nil,
+        DEFAULT_PBE_PKEYS,
+        DEFAULT_PBE_CERTS,
       )
-      assert_equal @mycert.to_der, pkcs12.certificate.to_der
+      assert_equal @mycert, pkcs12.certificate
       assert_equal @mykey.to_der, pkcs12.key.to_der
       assert_nil pkcs12.ca_certs
 
       decoded = OpenSSL::PKCS12.new(pkcs12.to_der)
-      assert_cert @mycert, decoded.certificate
+      assert_equal @mycert, decoded.certificate
     end
 
     def test_create_with_chain
@@ -80,7 +80,9 @@
         "hello",
         @mykey,
         @mycert,
-        chain
+        chain,
+        DEFAULT_PBE_PKEYS,
+        DEFAULT_PBE_CERTS,
       )
       assert_equal chain, pkcs12.ca_certs
     end
@@ -95,14 +97,16 @@
         "hello",
         @mykey,
         @mycert,
-        chain
+        chain,
+        DEFAULT_PBE_PKEYS,
+        DEFAULT_PBE_CERTS,
       )
 
       decoded = OpenSSL::PKCS12.new(pkcs12.to_der, passwd)
       assert_equal chain.size, decoded.ca_certs.size
-      assert_include_cert @cacert, decoded.ca_certs
-      assert_include_cert @inter_cacert, decoded.ca_certs
-      assert_cert @mycert, decoded.certificate
+      assert_include decoded.ca_certs, @cacert
+      assert_include decoded.ca_certs, @inter_cacert
+      assert_equal @mycert, decoded.certificate
       assert_equal @mykey.to_der, decoded.key.to_der
     end
 
@@ -126,8 +130,8 @@
         @mykey,
         @mycert,
         [],
-        nil,
-        nil,
+        DEFAULT_PBE_PKEYS,
+        DEFAULT_PBE_CERTS,
         2048
       )
 
@@ -138,8 +142,8 @@
           @mykey,
           @mycert,
           [],
-          nil,
-          nil,
+          DEFAULT_PBE_PKEYS,
+          DEFAULT_PBE_CERTS,
           "omg"
         )
       end
@@ -152,8 +156,8 @@
         @mykey,
         @mycert,
         [],
-        nil,
-        nil,
+        DEFAULT_PBE_PKEYS,
+        DEFAULT_PBE_CERTS,
         nil,
         2048
       )
@@ -165,147 +169,215 @@
           @mykey,
           @mycert,
           [],
-          nil,
-          nil,
+          DEFAULT_PBE_PKEYS,
+          DEFAULT_PBE_CERTS,
           nil,
           "omg"
         )
       end
     end
 
-    def test_new_with_one_key_and_one_cert
-      # generated with:
-      #   openssl version #=> OpenSSL 1.0.2h  3 May 2016
-      #   openssl pkcs12 -in <@mycert> -inkey <RSA1024> -export -out <out>
-      str = <<~EOF.unpack("m").first
-MIIGQQIBAzCCBgcGCSqGSIb3DQEHAaCCBfgEggX0MIIF8DCCAu8GCSqGSIb3DQEH
-BqCCAuAwggLcAgEAMIIC1QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIeZPM
-Rh6KiXgCAggAgIICqL6O+LCZmBzdIg6mozPF3FpY0hVbWHvTNMiDHieW3CrAanhN
-YCH2/wHqH8WpFpEWwF0qEEXAWjHsIlYB4Cfqo6b7XpuZe5eVESsjNTOTMF1JCUJj
-A6iNefXmCFLync1JK5LUodRDhTlKLU1WPK20X9X4vuEwHn8wt5RUb8P0E+Xh6rpS
-XC4LkZKT45zF3cJa/n5+dW65ohVGNVnF9D1bCNEKHMOllK1V9omutQ9slW88hpga
-LGiFsJoFOb/ESGb78KO+bd6zbX1MdKdBV+WD6t1uF/cgU65y+2A4nXs1urda+MJ7
-7iVqiB7Vnc9cANTbAkTSGNyoUDVM/NZde782/8IvddLAzUZ2EftoRDke6PvuBOVL
-ljBhNWmdamrtBqzuzVZCRdWq44KZkF2Xoc9asepwIkdVmntzQF7f1Z+Ta5yg6HFp
-xnr7CuM+MlHEShXkMgYtHnwAq10fDMSXIvjhi/AA5XUAusDO3D+hbtcRDcJ4uUes
-dm5dhQE2qJ02Ysn4aH3o1F3RYNOzrxejHJwl0D2TCE8Ww2X342xib57+z9u03ufj
-jswhiMKxy67f1LhUMq3XrT3uV6kCVXk/KUOUPcXPlPVNA5JmZeFhMp6GrtB5xJJ9
-wwBZD8UL5A2U2Mxi2OZsdUBv8eo3jnjZ284aFpt+mCjIHrLW5O0jwY8OCwSlYUoY
-IY00wlabX0s82kBcIQNZbC1RSV2267ro/7A0MClc8YQ/zWN0FKY6apgtUkHJI1cL
-1dc77mhnjETjwW94iLMDFy4zQfVu7IfCBqOBzygRNnqqUG66UhTs1xFnWM0mWXl/
-Zh9+AMpbRLIPaKCktIjl5juzzm+KEgkhD+707XRCFIGUYGP5bSHzGaz8PK9hj0u1
-E2SpZHUvYOcawmxtA7pmpSxl5uQjMIIC+QYJKoZIhvcNAQcBoIIC6gSCAuYwggLi
-MIIC3gYLKoZIhvcNAQwKAQKgggKmMIICojAcBgoqhkiG9w0BDAEDMA4ECKB338m8
-qSzHAgIIAASCAoACFhJeqA3xx+s1qIH6udNQYY5hAL6oz7SXoGwFhDiceSyJjmAD
-Dby9XWM0bPl1Gj5nqdsuI/lAM++fJeoETk+rxw8q6Ofk2zUaRRE39qgpwBwSk44o
-0SAFJ6bzHpc5CFh6sZmDaUX5Lm9GtjnGFmmsPTSJT5an5JuJ9WczGBEd0nSBQhJq
-xHbTGZiN8i3SXcIH531Sub+CBIFWy5lyCKgDYh/kgJFGQAaWUOjLI+7dCEESonXn
-F3Jh2uPbnDF9MGJyAFoNgWFhgSpi1cf6AUi87GY4Oyur88ddJ1o0D0Kz2uw8/bpG
-s3O4PYnIW5naZ8mozzbnYByEFk7PoTwM7VhoFBfYNtBoAI8+hBnPY/Y71YUojEXf
-SeX6QbtkIANfzS1XuFNKElShC3DPQIHpKzaatEsfxHfP+8VOav6zcn4mioao7NHA
-x7Dp6R1enFGoQOq4UNjBT8YjnkG5vW8zQHW2dAHLTJBq6x2Fzm/4Pjo/8vM1FiGl
-BQdW5vfDeJ/l6NgQm3xR9ka2E2HaDqIcj1zWbN8jy/bHPFJYuF/HH8MBV/ngMIXE
-vFEW/ToYv8eif0+EpUtzBsCKD4a7qYYYh87RmEVoQU96q6m+UbhpD2WztYfAPkfo
-OSL9j2QHhVczhL7OAgqNeM95pOsjA9YMe7exTeqK31LYnTX8oH8WJD1xGbRSJYgu
-SY6PQbumcJkc/TFPn0GeVUpiDdf83SeG50lo/i7UKQi2l1hi5Y51fQhnBnyMr68D
-llSZEvSWqfDxBJkBpeg6PIYvkTpEwKRJpVQoM3uYvdqVSSnW6rydqIb+snfOrlhd
-f+xCtq9xr+kHeTSqLIDRRAnMfgFRhY3IBlj6MSUwIwYJKoZIhvcNAQkVMRYEFBdb
-8XGWehZ6oPj56Pf/uId46M9AMDEwITAJBgUrDgMCGgUABBRvSCB04/f8f13pp2PF
-vyl2WuMdEwQIMWFFphPkIUICAggA
-      EOF
-      p12 = OpenSSL::PKCS12.new(str, "abc123")
+    def test_create_with_keytype
+      OpenSSL::PKCS12.create(
+        "omg",
+        "hello",
+        @mykey,
+        @mycert,
+        [],
+        DEFAULT_PBE_PKEYS,
+        DEFAULT_PBE_CERTS,
+        nil,
+        nil,
+        OpenSSL::PKCS12::KEY_SIG
+      )
 
-      assert_equal @mykey.to_der, p12.key.to_der
-      assert_equal @mycert.subject.to_der, p12.certificate.subject.to_der
-      assert_equal [], Array(p12.ca_certs)
+      assert_raise(ArgumentError) do
+        OpenSSL::PKCS12.create(
+          "omg",
+          "hello",
+          @mykey,
+          @mycert,
+          [],
+          DEFAULT_PBE_PKEYS,
+          DEFAULT_PBE_CERTS,
+          nil,
+          nil,
+          2048
+        )
+      end
     end
 
     def test_new_with_no_keys
       # generated with:
-      #   openssl pkcs12 -in <@mycert> -nokeys -export -out <out>
-      str = <<~EOF.unpack("m").first
-MIIDHAIBAzCCAuIGCSqGSIb3DQEHAaCCAtMEggLPMIICyzCCAscGCSqGSIb3DQEH
-BqCCArgwggK0AgEAMIICrQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIX4+W
-irqwH40CAggAgIICgOaCyo+5+6IOVoGCCL80c50bkkzAwqdXxvkKExJSdcJz2uMU
-0gRrKnZEjL5wrUsN8RwZu8DvgQTEhNEkKsUgM7AWainmN/EnwohIdHZAHpm6WD67
-I9kLGp0/DHrqZrV9P2dLfhXLUSQE8PI0tqZPZ8UEABhizkViw4eISTkrOUN7pGbN
-Qtx/oqgitXDuX2polbxYYDwt9vfHZhykHoKgew26SeJyZfeMs/WZ6olEI4cQUAFr
-mvYGuC1AxEGTo9ERmU8Pm16j9Hr9PFk50WYe+rnk9oX3wJogQ7XUWS5kYf7XRycd
-NDkNiwV/ts94bbuaGZp1YA6I48FXpIc8b5fX7t9tY0umGaWy0bARe1L7o0Y89EPe
-lMg25rOM7j3uPtFG8whbSfdETSy57UxzzTcJ6UwexeaK6wb2jqEmj5AOoPLWeaX0
-LyOAszR3v7OPAcjIDYZGdrbb3MZ2f2vo2pdQfu9698BrWhXuM7Odh73RLhJVreNI
-aezNOAtPyBlvGiBQBGTzRIYHSLL5Y5aVj2vWLAa7hjm5qTL5C5mFdDIo6TkEMr6I
-OsexNQofEGs19kr8nARXDlcbEimk2VsPj4efQC2CEXZNzURsKca82pa62MJ8WosB
-DTFd8X06zZZ4nED50vLopZvyW4fyW60lELwOyThAdG8UchoAaz2baqP0K4de44yM
-Y5/yPFDu4+GoimipJfbiYviRwbzkBxYW8+958ILh0RtagLbvIGxbpaym9PqGjOzx
-ShNXjLK2aAFZsEizQ8kd09quJHU/ogq2cUXdqqhmOqPnUWrJVi/VCoRB3Pv1/lE4
-mrUgr2YZ11rYvBw6g5XvNvFcSc53OKyV7SLn0dwwMTAhMAkGBSsOAwIaBQAEFEWP
-1WRQykaoD4uJCpTx/wv0SLLBBAiDKI26LJK7xgICCAA=
+      #   openssl pkcs12 -certpbe PBE-SHA1-3DES -in <@mycert> -nokeys -export
+      str = <<~EOF.unpack1("m")
+MIIGJAIBAzCCBeoGCSqGSIb3DQEHAaCCBdsEggXXMIIF0zCCBc8GCSqGSIb3
+DQEHBqCCBcAwggW8AgEAMIIFtQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMw
+DgQIjv5c3OHvnBgCAggAgIIFiMJa8Z/w7errRvCQPXh9dGQz3eJaFq3S2gXD
+rh6oiwsgIRJZvYAWgU6ll9NV7N5SgvS2DDNVuc3tsP8TPWjp+bIxzS9qmGUV
+kYWuURWLMKhpF12ZRDab8jcIwBgKoSGiDJk8xHjx6L613/XcRM6ln3VeQK+C
+hlW5kXniNAUAgTft25Fn61Xa8xnhmsz/fk1ycGnyGjKCnr7Mgy7KV0C1vs23
+18n8+b1ktDWLZPYgpmXuMFVh0o+HJTV3O86mkIhJonMcnOMgKZ+i8KeXaocN
+JQlAPBG4+HOip7FbQT/h6reXv8/J+hgjLfqAb5aV3m03rUX9mXx66nR1tQU0
+Jq+XPfDh5+V4akIczLlMyyo/xZjI1/qupcMjr+giOGnGd8BA3cuXW+ueLQiA
+PpTp+DQLVHRfz9XTZbyqOReNEtEXvO9gOlKSEY5lp65ItXVEs2Oqyf9PfU9y
+DUltN6fCMilwPyyrsIBKXCu2ZLM5h65KVCXAYEX9lNqj9zrQ7vTqvCNN8RhS
+ScYouTX2Eqa4Z+gTZWLHa8RCQFoyP6hd+97/Tg2Gv2UTH0myQxIVcnpdi1wy
+cqb+er7tyKbcO96uSlUjpj/JvjlodtjJcX+oinEqGb/caj4UepbBwiG3vv70
+63bS3jTsOLNjDRsR9if3LxIhLa6DW8zOJiGC+EvMD1o4dzHcGVpQ/pZWCHZC
++YiNJpQOBApiZluE+UZ0m3XrtHFQYk7xblTrh+FJF91wBsok0rZXLAKd8m4p
+OJsc7quCq3cuHRRTzJQ4nSe01uqbwGDAYwLvi6VWy3svU5qa05eDRmgzEFTG
+e84Gp/1LQCtpQFr4txkjFchO2whWS80KoQKqmLPyGm1D9Lv53Q4ZsKMgNihs
+rEepuaOZMKHl4yMAYFoOXZCAYzfbhN6b2phcFAHjMUHUw9e3F0QuDk9D0tsr
+riYTrkocqlOKfK4QTomx27O0ON2J6f1rtEojGgfl9RNykN7iKGzjS3914QjW
+W6gGiZejxHsDPEAa4gUp0WiSUSXtD5WJgoyAzLydR2dKWsQ4WlaUXi01CuGy
++xvncSn2nO3bbot8VD5H6XU1CjREVtnIfbeRYO/uofyLUP3olK5RqN6ne6Xo
+eXnJ/bjYphA8NGuuuvuW1SCITmINkZDLC9cGlER9+K65RR/DR3TigkexXMeN
+aJ70ivZYAl0OuhZt3TGIlAzS64TIoyORe3z7Ta1Pp9PZQarYJpF9BBIZIFor
+757PHHuQKRuugiRkp8B7v4eq1BQ+VeAxCKpyZ7XrgEtbY/AWDiaKcGPKPjc3
+AqQraVeQm7kMBT163wFmZArCphzkDOI3bz2oEO8YArMgLq2Vto9jAZlqKyWr
+pi2bSJxuoP1aoD58CHcWMrf8/j1LVdQhKgHQXSik2ID0H2Wc/XnglhzlVFuJ
+JsNIW/EGJlZh/5WDez9U0bXqnBlu3uasPEOezdoKlcCmQlmTO5+uLHYLEtNA
+EH9MtnGZebi9XS5meTuS6z5LILt8O9IHZxmT3JRPHYj287FEzotlLdcJ4Ee5
+enW41UHjLrfv4OaITO1hVuoLRGdzjESx/fHMWmxroZ1nVClxECOdT42zvIYJ
+J3xBZ0gppzQ5fjoYiKjJpxTflRxUuxshk3ih6VUoKtqj/W18tBQ3g5SOlkgT
+yCW8r74yZlfYmNrPyDMUQYpLUPWj2n71GF0KyPfTU5yOatRgvheh262w5BG3
+omFY7mb3tCv8/U2jdMIoukRKacpZiagofz3SxojOJq52cHnCri+gTHBMX0cO
+j58ygfntHWRzst0pV7Ze2X3fdCAJ4DokH6bNJNthcgmolFJ/y3V1tJjgsdtQ
+7Pjn/vE6xUV0HXE2x4yoVYNirbAMIvkN/X+atxrN0dA4AchN+zGp8TAxMCEw
+CQYFKw4DAhoFAAQUQ+6XXkyhf6uYgtbibILN2IjKnOAECLiqoY45MPCrAgII
+AA==
       EOF
       p12 = OpenSSL::PKCS12.new(str, "abc123")
 
       assert_equal nil, p12.key
       assert_equal nil, p12.certificate
       assert_equal 1, p12.ca_certs.size
-      assert_equal @mycert.subject.to_der, p12.ca_certs[0].subject.to_der
+      assert_equal @mycert.subject, p12.ca_certs[0].subject
     end
 
     def test_new_with_no_certs
       # generated with:
-      #   openssl pkcs12 -inkey <RSA1024> -nocerts -export -out <out>
-      str = <<~EOF.unpack("m").first
-MIIDJwIBAzCCAu0GCSqGSIb3DQEHAaCCAt4EggLaMIIC1jCCAtIGCSqGSIb3DQEH
-AaCCAsMEggK/MIICuzCCArcGCyqGSIb3DQEMCgECoIICpjCCAqIwHAYKKoZIhvcN
-AQwBAzAOBAg6AaYnJs84SwICCAAEggKAQzZH+fWSpcQYD1J7PsGSune85A++fLCQ
-V7tacp2iv95GJkxwYmfTP176pJdgs00mceB9UJ/u9EX5nD0djdjjQjwo6sgKjY0q
-cpVhZw8CMxw7kBD2dhtui0zT8z5hy03LePxsjEKsGiSbeVeeGbSfw/I6AAYbv+Uh
-O/YPBGumeHj/D2WKnfsHJLQ9GAV3H6dv5VKYNxjciK7f/JEyZCuUQGIN64QFHDhJ
-7fzLqd/ul3FZzJZO6a+dwvcgux09SKVXDRSeFmRCEX4b486iWhJJVspCo9P2KNne
-ORrpybr3ZSwxyoICmjyo8gj0OSnEfdx9790Ej1takPqSA1wIdSdBLekbZqB0RBQg
-DEuPOsXNo3QFi8ji1vu0WBRJZZSNC2hr5NL6lNR+DKxG8yzDll2j4W4BBIp22mAE
-7QRX7kVxu17QJXQhOUac4Dd1qXmzebP8t6xkAxD9L7BWEN5OdiXWwSWGjVjMBneX
-nYObi/3UT/aVc5WHMHK2BhCI1bwH51E6yZh06d5m0TQpYGUTWDJdWGBSrp3A+8jN
-N2PMQkWBFrXP3smHoTEN4oZC4FWiPsIEyAkQsfKRhcV9lGKl2Xgq54ROTFLnwKoj
-Z3zJScnq9qmNzvVZSMmDLkjLyDq0pxRxGKBvgouKkWY7VFFIwwBIJM39iDJ5NbBY
-i1AQFTRsRSsZrNVPasCXrIq7bhMoJZb/YZOGBLNyJVqKUoYXhtwsajzSq54VlWft
-JxsPayEd4Vi6O9EU1ahnj6qFEZiKFzsicgK2J1Rb8cYagrp0XWjHW0SBn5GVUWCg
-GUokSFG/0JTdeYTo/sQuG4qNgJkOolRjpeI48Fciq5VUWLvVdKioXzAxMCEwCQYF
-Kw4DAhoFAAQUYAuwVtGD1TdgbFK4Yal2XBgwUR4ECEawsN3rNaa6AgIIAA==
+      #   openssl pkcs12 -inkey fixtures/openssl/pkey/rsa-1.pem -nocerts -export
+      str = <<~EOF.unpack1("m")
+MIIJ7wIBAzCCCbUGCSqGSIb3DQEHAaCCCaYEggmiMIIJnjCCCZoGCSqGSIb3
+DQEHAaCCCYsEggmHMIIJgzCCCX8GCyqGSIb3DQEMCgECoIIJbjCCCWowHAYK
+KoZIhvcNAQwBAzAOBAjX5nN8jyRKwQICCAAEgglIBIRLHfiY1mNHpl3FdX6+
+72L+ZOVXnlZ1MY9HSeg0RMkCJcm0mJ2UD7INUOGXvwpK9fr6WJUZM1IqTihQ
+1dM0crRC2m23aP7KtAlXh2DYD3otseDtwoN/NE19RsiJzeIiy5TSW1d47weU
++D4Ig/9FYVFPTDgMzdCxXujhvO/MTbZIjqtcS+IOyF+91KkXrHkfkGjZC7KS
+WRmYw9BBuIPQEewdTI35sAJcxT8rK7JIiL/9mewbSE+Z28Wq1WXwmjL3oZm9
+lw6+f515b197GYEGomr6LQqJJamSYpwQbTGHonku6Tf3ylB4NLFqOnRCKE4K
+zRSSYIqJBlKHmQ4pDm5awoupHYxMZLZKZvXNYyYN3kV8r1iiNVlY7KBR4CsX
+rqUkXehRmcPnuqEMW8aOpuYe/HWf8PYI93oiDZjcEZMwW2IZFFrgBbqUeNCM
+CQTkjAYxi5FyoaoTnHrj/aRtdLOg1xIJe4KKcmOXAVMmVM9QEPNfUwiXJrE7
+n42gl4NyzcZpxqwWBT++9TnQGZ/lEpwR6dzkZwICNQLdQ+elsdT7mumywP+1
+WaFqg9kpurimaiBu515vJNp9Iqv1Nmke6R8Lk6WVRKPg4Akw0fkuy6HS+LyN
+ofdCfVUkPGN6zkjAxGZP9ZBwvXUbLRC5W3N5qZuAy5WcsS75z+oVeX9ePV63
+cue23sClu8JSJcw3HFgPaAE4sfkQ4MoihPY5kezgT7F7Lw/j86S0ebrDNp4N
+Y685ec81NRHJ80CAM55f3kGCOEhoifD4VZrvr1TdHZY9Gm3b1RYaJCit2huF
+nlOfzeimdcv/tkjb6UsbpXx3JKkF2NFFip0yEBERRCdWRYMUpBRcl3ad6XHy
+w0pVTgIjTxGlbbtOCi3siqMOK0GNt6UgjoEFc1xqjsgLwU0Ta2quRu7RFPGM
+GoEwoC6VH23p9Hr4uTFOL0uHfkKWKunNN+7YPi6LT6IKmTQwrp+fTO61N6Xh
+KlqTpwESKsIJB2iMnc8wBkjXJtmG/e2n5oTqfhICIrxYmEb7zKDyK3eqeTj3
+FhQh2t7cUIiqcT52AckUqniPmlE6hf82yBjhaQUPfi/ExTBtTDSmFfRPUzq+
+Rlla4OHllPRzUXJExyansgCxZbPqlw46AtygSWRGcWoYAKUKwwoYjerqIV5g
+JoZICV9BOU9TXco1dHXZQTs/nnTwoRmYiL/Ly5XpvUAnQOhYeCPjBeFnPSBR
+R/hRNqrDH2MOV57v5KQIH2+mvy26tRG+tVGHmLMaOJeQkjLdxx+az8RfXIrH
+7hpAsoBb+g9jUDY1mUVavPk1T45GMpQH8u3kkzRvChfOst6533GyIZhE7FhN
+KanC6ACabVFDUs6P9pK9RPQMp1qJfpA0XJFx5TCbVbPkvnkZd8K5Tl/tzNM1
+n32eRao4MKr9KDwoDL93S1yJgYTlYjy1XW/ewdedtX+B4koAoz/wSXDYO+GQ
+Zu6ZSpKSEHTRPhchsJ4oICvpriVaJkn0/Z7H3YjNMB9U5RR9+GiIg1wY1Oa1
+S3WfuwrrI6eqfbQwj6PDNu3IKy6srEgvJwaofQALNBPSYWbauM2brc8qsD+t
+n8jC/aD1aMcy00+9t3H/RVCjEOb3yKfUpAldIkEA2NTTnZpoDQDXeNYU2F/W
+yhmFjJy8A0O4QOk2xnZK9kcxSRs0v8vI8HivvgWENoVPscsDC4742SSIe6SL
+f/T08reIX11f0K70rMtLhtFMQdHdYOTNl6JzhkHPLr/f9MEZsBEQx52depnF
+ARb3gXGbCt7BAi0OeCEBSbLr2yWuW4r55N0wRZSOBtgqgjsiHP7CDQSkbL6p
+FPlQS1do9gBSHiNYvsmN1LN5bG+mhcVb0UjZub4mL0EqGadjDfDdRJmWqlX0
+r5dyMcOWQVy4O2cPqYFlcP9lk8buc5otcyVI2isrAFdlvBK29oK6jc52Aq5Q
+0b2ESDlgX8WRgiOPPxK8dySKEeuIwngCtJyNTecP9Ug06TDsu0znZGCXJ+3P
+8JOpykgA8EQdOZOYHbo76ZfB2SkklI5KeRA5IBjGs9G3TZ4PHLy2DIwsbWzS
+H1g01o1x264nx1cJ+eEgUN/KIiGFIib42RS8Af4D5e+Vj54Rt3axq+ag3kI+
+53p8uotyu+SpvvXUP7Kv4xpQ/L6k41VM0rfrd9+DrlDVvSfxP2uh6I1TKF7A
+CT5n8zguMbng4PGjxvyPBM5k62t6hN5fuw6Af0aZFexh+IjB/5wFQ6onSz23
+fBzMW4St7RgSs8fDg3lrM+5rwXiey1jxY1ddaxOoUsWRMvvdd7rZxRZQoN5v
+AcI5iMkK/vvpQgC/sfzhtXtrJ2XOPZ+GVgi7VcuDLKSkdFMcPbGzO8SdxUnS
+SLV5XTKqKND+Lrfx7DAoKi5wbDFHu5496/MHK5qP4tBe6sJ5bZc+KDJIH46e
+wTV1oWtB5tV4q46hOb5WRcn/Wjz3HSKaGZgx5QbK1MfKTzD5CTUn+ArMockX
+2wJhPnFK85U4rgv8iBuh9bRjyw+YaKf7Z3loXRiE1eRG6RzuPF0ZecFiDumk
+AC/VUXynJhzePBLqzrQj0exanACdullN+pSfHiRWBxR2VFUkjoFP5X45GK3z
+OstSH6FOkMVU4afqEmjsIwozDFIyin5EyWTtdhJe3szdJSGY23Tut+9hUatx
+9FDFLESOd8z3tyQSNiLk/Hib+e/lbjxqbXBG/p/oyvP3N999PLUPtpKqtYkV
+H0+18sNh9CVfojiJl44fzxe8yCnuefBjut2PxEN0EFRBPv9P2wWlmOxkPKUq
+NrCJP0rDj5aONLrNZPrR8bZNdIShkZ/rKkoTuA0WMZ+xUlDRxAupdMkWAlrz
+8IcwNcdDjPnkGObpN5Ctm3vK7UGSBmPeNqkXOYf3QTJ9gStJEd0F6+DzTN5C
+KGt1IyuGwZqL2Yk51FDIIkr9ykEnBMaA39LS7GFHEDNGlW+fKC7AzA0zfoOr
+fXZlHMBuqHtXqk3zrsHRqGGoocigg4ctrhD1UREYKj+eIj1TBiRdf7c6+COf
+NIOmej8pX3FmZ4ui+dDA8r2ctgsWHrb4A6iiH+v1DRA61GtoaA/tNRggewXW
+VXCZCGWyyTuyHGOqq5ozrv5MlzZLWD/KV/uDsAWmy20RAed1C4AzcXlpX25O
+M4SNl47g5VRNJRtMqokc8j6TjZrzMDEwITAJBgUrDgMCGgUABBRrkIRuS5qg
+BC8fv38mue8LZVcbHQQIUNrWKEnskCoCAggA
       EOF
       p12 = OpenSSL::PKCS12.new(str, "abc123")
 
-      assert_equal @mykey.to_der, p12.key.to_der
+      assert_equal Fixtures.pkey("rsa-1").to_der, p12.key.to_der
       assert_equal nil, p12.certificate
       assert_equal [], Array(p12.ca_certs)
     end
 
     def test_dup
-      p12 = OpenSSL::PKCS12.create("pass", "name", @mykey, @mycert)
+      p12 = OpenSSL::PKCS12.create(
+        "pass",
+        "name",
+        @mykey,
+        @mycert,
+        nil,
+        DEFAULT_PBE_PKEYS,
+        DEFAULT_PBE_CERTS,
+      )
       assert_equal p12.to_der, p12.dup.to_der
     end
 
-    private
-    def assert_cert expected, actual
-      [
-        :subject,
-        :issuer,
-        :serial,
-        :not_before,
-        :not_after,
-      ].each do |attribute|
-        assert_equal expected.send(attribute), actual.send(attribute)
-      end
-      assert_equal expected.to_der, actual.to_der
+    def test_set_mac_pkcs12kdf
+      p12 = OpenSSL::PKCS12.create(
+        "pass",
+        "name",
+        @mykey,
+        @mycert,
+        nil,
+        nil,
+        nil,
+        nil,
+        1234, # mac_iter
+        nil,
+      )
+      macdata = macdata(p12)
+      # Depends on the OpenSSL version: SHA256 in OpenSSL >= 3.0
+      assert_include ["SHA1", "SHA256"], macdata[:mac_algo]
+      assert_equal 1234, macdata[:iter]
+
+      p12.set_mac("pass", "macsalt", 2345, "SHA384")
+      macdata = macdata(p12)
+      assert_equal "SHA384", macdata[:mac_algo]
+      assert_equal "macsalt", macdata[:salt]
+      assert_equal 2345, macdata[:iter]
+      assert_equal @mykey.to_der, OpenSSL::PKCS12.new(p12.to_der, "pass").key.to_der
     end
 
-    def assert_include_cert cert, ary
-      der = cert.to_der
-      ary.each do |candidate|
-        if candidate.to_der == der
-          return true
-        end
-      end
-      false
+    private
+
+    def macdata(p12)
+      # See RFC 7292
+      asn1 = OpenSSL::ASN1.decode(p12.to_der)
+      macdata = asn1.value[2]
+      mac = macdata.value[0]
+      mac_algo = mac.value[0].value[0].value
+      _mac_params = mac.value[0].value[1]
+      {
+        mac_algo: mac_algo,
+        salt: macdata.value[1].value,
+        iter: macdata.value[2]&.value,
+      }
     end
   end
 end
diff -ruN ruby-2.5.9.orig/test/openssl/test_pkcs7.rb ruby-2.5.9/test/openssl/test_pkcs7.rb
--- ruby-2.5.9.orig/test/openssl/test_pkcs7.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_pkcs7.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -89,6 +89,24 @@
     assert_equal(@ee2_cert.issuer.to_s, signers[1].issuer.to_s)
   end
 
+  def test_signed_add_signer
+    data = "aaaaa\nbbbbb\nccccc\n"
+    psi = OpenSSL::PKCS7::SignerInfo.new(@ee1_cert, @rsa1024, "sha256")
+    p7 = OpenSSL::PKCS7.new
+    p7.type = :signed
+    p7.add_signer(psi)
+    p7.add_certificate(@ee1_cert)
+    p7.add_certificate(@ca_cert)
+    p7.add_data(data)
+
+    store = OpenSSL::X509::Store.new
+    store.add_cert(@ca_cert)
+
+    assert_equal(true, p7.verify([], store))
+    assert_equal(true, OpenSSL::PKCS7.new(p7.to_der).verify([], store))
+    assert_equal(1, p7.signers.size)
+  end
+
   def test_detached_sign
     store = OpenSSL::X509::Store.new
     store.add_cert(@ca_cert)
@@ -133,6 +151,28 @@
     assert_equal(@ca_cert.subject.to_s, recip[1].issuer.to_s)
     assert_equal(3, recip[1].serial)
     assert_equal(data, p7.decrypt(@rsa1024, @ee2_cert))
+
+    assert_equal(data, p7.decrypt(@rsa1024))
+
+    # Default cipher has been removed in v3.3
+    assert_raise_with_message(ArgumentError, /RC2-40-CBC/) {
+      OpenSSL::PKCS7.encrypt(certs, data)
+    }
+  end
+
+  def test_empty_signed_data_ruby_bug_19974
+    data = "-----BEGIN PKCS7-----\nMAsGCSqGSIb3DQEHAg==\n-----END PKCS7-----\n"
+    assert_raise(ArgumentError) { OpenSSL::PKCS7.new(data) }
+
+    data = <<END
+MIME-Version: 1.0
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"
+Content-Transfer-Encoding: base64
+
+#{data}
+END
+    assert_raise(OpenSSL::PKCS7::PKCS7Error) { OpenSSL::PKCS7.read_smime(data) }
   end
 
   def test_graceful_parsing_failure #[ruby-core:43250]
@@ -170,6 +210,34 @@
     assert_equal(:encrypted, p7.type)
   end
 
+  def test_smime
+    store = OpenSSL::X509::Store.new
+    store.add_cert(@ca_cert)
+    ca_certs = [@ca_cert]
+
+    data = "aaaaa\r\nbbbbb\r\nccccc\r\n"
+    tmp = OpenSSL::PKCS7.sign(@ee1_cert, @rsa1024, data, ca_certs)
+    p7 = OpenSSL::PKCS7.new(tmp.to_der)
+    smime = OpenSSL::PKCS7.write_smime(p7)
+    assert_equal(true, smime.start_with?(<<END))
+MIME-Version: 1.0
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"
+Content-Transfer-Encoding: base64
+
+END
+    assert_equal(p7.to_der, OpenSSL::PKCS7.read_smime(smime).to_der)
+
+    smime = OpenSSL::PKCS7.write_smime(p7, nil, 0)
+    assert_equal(p7.to_der, OpenSSL::PKCS7.read_smime(smime).to_der)
+  end
+
+  def test_to_text
+    p7 = OpenSSL::PKCS7.new
+    p7.type = "signed"
+    assert_match(/signed/, p7.to_text)
+  end
+
   def test_degenerate_pkcs7
     ca_cert_pem = <<END
 -----BEGIN CERTIFICATE-----
diff -ruN ruby-2.5.9.orig/test/openssl/test_pkey_dh.rb ruby-2.5.9/test/openssl/test_pkey_dh.rb
--- ruby-2.5.9.orig/test/openssl/test_pkey_dh.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_pkey_dh.rb	2025-01-29 19:08:29.935863721 +0100
@@ -1,48 +1,92 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL) && defined?(OpenSSL::PKey::DH)
 
 class OpenSSL::TestPKeyDH < OpenSSL::PKeyTestCase
-  NEW_KEYLEN = 256
+  NEW_KEYLEN = 2048
 
-  def test_new
+  def test_new_empty
+    dh = OpenSSL::PKey::DH.new
+    assert_equal nil, dh.p
+    assert_equal nil, dh.priv_key
+  end
+
+  def test_new_generate
+    # This test is slow
     dh = OpenSSL::PKey::DH.new(NEW_KEYLEN)
     assert_key(dh)
-  end
+  end if ENV["OSSL_TEST_ALL"]
+
+  def test_new_break_on_non_fips
+    omit_on_fips
 
-  def test_new_break
     assert_nil(OpenSSL::PKey::DH.new(NEW_KEYLEN) { break })
     assert_raise(RuntimeError) do
       OpenSSL::PKey::DH.new(NEW_KEYLEN) { raise }
     end
   end
 
+  def test_new_break_on_fips
+    omit_on_non_fips
+
+    # The block argument is not executed in FIPS case.
+    # See https://github.com/ruby/openssl/issues/692 for details.
+    assert(OpenSSL::PKey::DH.new(NEW_KEYLEN) { break })
+    assert(OpenSSL::PKey::DH.new(NEW_KEYLEN) { raise })
+  end
+
+  def test_derive_key
+    params = Fixtures.pkey("dh2048_ffdhe2048")
+    dh1 = OpenSSL::PKey.generate_key(params)
+    dh2 = OpenSSL::PKey.generate_key(params)
+    dh1_pub = OpenSSL::PKey.read(dh1.public_to_der)
+    dh2_pub = OpenSSL::PKey.read(dh2.public_to_der)
+
+    z = dh1.g.mod_exp(dh1.priv_key, dh1.p).mod_exp(dh2.priv_key, dh1.p).to_s(2)
+    assert_equal z, dh1.derive(dh2_pub)
+    assert_equal z, dh2.derive(dh1_pub)
+
+    assert_raise(OpenSSL::PKey::PKeyError) { params.derive(dh1_pub) }
+    assert_raise(OpenSSL::PKey::PKeyError) { dh1_pub.derive(params) }
+
+    assert_equal z, dh1.compute_key(dh2.pub_key)
+    assert_equal z, dh2.compute_key(dh1.pub_key)
+  end
+
   def test_DHparams
-    dh1024 = Fixtures.pkey_dh("dh1024")
+    dh = Fixtures.pkey("dh2048_ffdhe2048")
+    dh_params = dh.public_key
+
     asn1 = OpenSSL::ASN1::Sequence([
-      OpenSSL::ASN1::Integer(dh1024.p),
-      OpenSSL::ASN1::Integer(dh1024.g)
+      OpenSSL::ASN1::Integer(dh.p),
+      OpenSSL::ASN1::Integer(dh.g)
     ])
     key = OpenSSL::PKey::DH.new(asn1.to_der)
-    assert_same_dh dup_public(dh1024), key
+    assert_same_dh dh_params, key
 
     pem = <<~EOF
     -----BEGIN DH PARAMETERS-----
-    MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0
-    pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG
-    AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC
+    MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+    +8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
+    87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
+    YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
+    7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
+    ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
     -----END DH PARAMETERS-----
     EOF
+
     key = OpenSSL::PKey::DH.new(pem)
-    assert_same_dh dup_public(dh1024), key
+    assert_same_dh dh_params, key
+    key = OpenSSL::PKey.read(pem)
+    assert_same_dh dh_params, key
 
-    assert_equal asn1.to_der, dh1024.to_der
-    assert_equal pem, dh1024.export
+    assert_equal asn1.to_der, dh.to_der
+    assert_equal pem, dh.export
   end
 
   def test_public_key
-    dh = Fixtures.pkey_dh("dh1024")
+    dh = Fixtures.pkey("dh2048_ffdhe2048")
     public_key = dh.public_key
     assert_no_key(public_key) #implies public_key.public? is false!
     assert_equal(dh.to_der, public_key.to_der)
@@ -50,37 +94,80 @@
   end
 
   def test_generate_key
-    dh = Fixtures.pkey_dh("dh1024").public_key # creates a copy
+    # Deprecated in v3.0.0; incompatible with OpenSSL 3.0
+    # Creates a copy with params only
+    dh = Fixtures.pkey("dh2048_ffdhe2048").public_key
     assert_no_key(dh)
     dh.generate_key!
     assert_key(dh)
-  end
 
-  def test_key_exchange
-    dh = Fixtures.pkey_dh("dh1024")
     dh2 = dh.public_key
-    dh.generate_key!
     dh2.generate_key!
     assert_equal(dh.compute_key(dh2.pub_key), dh2.compute_key(dh.pub_key))
+  end if !openssl?(3, 0, 0)
+
+  def test_params_ok?
+    # Skip the tests in old OpenSSL version 1.1.1c or early versions before
+    # applying the following commits in OpenSSL 1.1.1d to make `DH_check`
+    # function pass the RFC 7919 FFDHE group texts.
+    # https://github.com/openssl/openssl/pull/9435
+    unless openssl?(1, 1, 1, 4)
+      pend 'DH check for RFC 7919 FFDHE group texts is not implemented'
+    end
+
+    dh0 = Fixtures.pkey("dh2048_ffdhe2048")
+
+    dh1 = OpenSSL::PKey::DH.new(OpenSSL::ASN1::Sequence([
+      OpenSSL::ASN1::Integer(dh0.p),
+      OpenSSL::ASN1::Integer(dh0.g)
+    ]))
+    assert_equal(true, dh1.params_ok?)
+
+    dh2 = OpenSSL::PKey::DH.new(OpenSSL::ASN1::Sequence([
+      OpenSSL::ASN1::Integer(dh0.p + 1),
+      OpenSSL::ASN1::Integer(dh0.g)
+    ]))
+    assert_equal(false, dh2.params_ok?)
   end
 
   def test_dup
-    dh = OpenSSL::PKey::DH.new(NEW_KEYLEN)
-    dh2 = dh.dup
-    assert_equal dh.to_der, dh2.to_der # params
-    assert_equal_params dh, dh2 # keys
-    dh2.set_pqg(dh2.p + 1, nil, dh2.g)
-    assert_not_equal dh2.p, dh.p
-    assert_equal dh2.g, dh.g
-  end
+    # Parameters only
+    dh1 = Fixtures.pkey("dh2048_ffdhe2048")
+    dh2 = dh1.dup
+    assert_equal dh1.to_der, dh2.to_der
+    assert_not_equal nil, dh1.p
+    assert_not_equal nil, dh1.g
+    assert_equal [dh1.p, dh1.g], [dh2.p, dh2.g]
+    assert_equal nil, dh1.pub_key
+    assert_equal nil, dh1.priv_key
+    assert_equal [dh1.pub_key, dh1.priv_key], [dh2.pub_key, dh2.priv_key]
+
+    # PKey is immutable in OpenSSL >= 3.0
+    if !openssl?(3, 0, 0)
+      dh2.set_pqg(dh2.p + 1, nil, dh2.g)
+      assert_not_equal dh2.p, dh1.p
+    end
 
-  private
+    # With a key pair
+    dh3 = OpenSSL::PKey.generate_key(Fixtures.pkey("dh2048_ffdhe2048"))
+    dh4 = dh3.dup
+    assert_equal dh3.to_der, dh4.to_der
+    assert_equal dh1.to_der, dh4.to_der # encodes parameters only
+    assert_equal [dh1.p, dh1.g], [dh4.p, dh4.g]
+    assert_not_equal nil, dh3.pub_key
+    assert_not_equal nil, dh3.priv_key
+    assert_equal [dh3.pub_key, dh3.priv_key], [dh4.pub_key, dh4.priv_key]
+  end
+
+  def test_marshal
+    dh = Fixtures.pkey("dh2048_ffdhe2048")
+    deserialized = Marshal.load(Marshal.dump(dh))
 
-  def assert_equal_params(dh1, dh2)
-    assert_equal(dh1.g, dh2.g)
-    assert_equal(dh1.p, dh2.p)
+    assert_equal dh.to_der, deserialized.to_der
   end
 
+  private
+
   def assert_no_key(dh)
     assert_equal(false, dh.public?)
     assert_equal(false, dh.private?)
diff -ruN ruby-2.5.9.orig/test/openssl/test_pkey_dsa.rb ruby-2.5.9/test/openssl/test_pkey_dsa.rb
--- ruby-2.5.9.orig/test/openssl/test_pkey_dsa.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_pkey_dsa.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,64 +1,104 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL) && defined?(OpenSSL::PKey::DSA)
 
 class OpenSSL::TestPKeyDSA < OpenSSL::PKeyTestCase
+  def setup
+    # May not be available in FIPS mode as DSA has been deprecated in FIPS 186-5
+    omit_on_fips
+  end
+
   def test_private
-    key = OpenSSL::PKey::DSA.new(256)
-    assert(key.private?)
+    key = Fixtures.pkey("dsa1024")
+    assert_equal true, key.private?
     key2 = OpenSSL::PKey::DSA.new(key.to_der)
-    assert(key2.private?)
+    assert_equal true, key2.private?
     key3 = key.public_key
-    assert(!key3.private?)
+    assert_equal false, key3.private?
     key4 = OpenSSL::PKey::DSA.new(key3.to_der)
-    assert(!key4.private?)
+    assert_equal false, key4.private?
   end
 
   def test_new
-    key = OpenSSL::PKey::DSA.new 256
+    key = OpenSSL::PKey::DSA.new(2048)
     pem  = key.public_key.to_pem
     OpenSSL::PKey::DSA.new pem
-    if $0 == __FILE__
-      assert_nothing_raised {
-        key = OpenSSL::PKey::DSA.new 2048
-      }
-    end
   end
 
   def test_new_break
-    assert_nil(OpenSSL::PKey::DSA.new(512) { break })
+    assert_nil(OpenSSL::PKey::DSA.new(2048) { break })
     assert_raise(RuntimeError) do
-      OpenSSL::PKey::DSA.new(512) { raise }
+      OpenSSL::PKey::DSA.new(2048) { raise }
+    end
+  end
+
+  def test_generate
+    # DSA.generate used to call DSA_generate_parameters_ex(), which adjusts the
+    # size of q according to the size of p
+    key1024 = OpenSSL::PKey::DSA.generate(1024)
+    assert_predicate key1024, :private?
+    assert_equal 1024, key1024.p.num_bits
+    assert_equal 160, key1024.q.num_bits
+
+    key2048 = OpenSSL::PKey::DSA.generate(2048)
+    assert_equal 2048, key2048.p.num_bits
+    assert_equal 256, key2048.q.num_bits
+
+    if ENV["OSSL_TEST_ALL"] == "1" # slow
+      key3072 = OpenSSL::PKey::DSA.generate(3072)
+      assert_equal 3072, key3072.p.num_bits
+      assert_equal 256, key3072.q.num_bits
     end
   end
 
   def test_sign_verify
-    dsa512 = Fixtures.pkey("dsa512")
+    # The DSA valid size is 2048 or 3072 on FIPS.
+    # https://github.com/openssl/openssl/blob/7649b5548e5c0352b91d9d3ed695e42a2ac1e99c/providers/common/securitycheck.c#L185-L188
+    dsa = Fixtures.pkey("dsa2048")
     data = "Sign me!"
     if defined?(OpenSSL::Digest::DSS1)
-      signature = dsa512.sign(OpenSSL::Digest::DSS1.new, data)
-      assert_equal true, dsa512.verify(OpenSSL::Digest::DSS1.new, signature, data)
+      signature = dsa.sign(OpenSSL::Digest.new('DSS1'), data)
+      assert_equal true, dsa.verify(OpenSSL::Digest.new('DSS1'), signature, data)
     end
 
-    signature = dsa512.sign("SHA1", data)
-    assert_equal true, dsa512.verify("SHA1", signature, data)
+    signature = dsa.sign("SHA256", data)
+    assert_equal true, dsa.verify("SHA256", signature, data)
 
-    signature0 = (<<~'end;').unpack("m")[0]
-      MCwCFH5h40plgU5Fh0Z4wvEEpz0eE9SnAhRPbkRB8ggsN/vsSEYMXvJwjGg/
-      6g==
+    signature0 = (<<~'end;').unpack1("m")
+      MD4CHQC0zmRkVOAHJTm28fS5PVUv+4LtBeNaKqr/yfmVAh0AsTcLqofWHoW8X5oWu8AOvngOcFVZ
+      cLTvhY3XNw==
     end;
-    assert_equal true, dsa512.verify("SHA256", signature0, data)
+    assert_equal true, dsa.verify("SHA256", signature0, data)
     signature1 = signature0.succ
-    assert_equal false, dsa512.verify("SHA256", signature1, data)
+    assert_equal false, dsa.verify("SHA256", signature1, data)
   end
 
-  def test_sys_sign_verify
-    key = Fixtures.pkey("dsa256")
+  def test_sign_verify_raw
+    key = Fixtures.pkey("dsa2048")
     data = 'Sign me!'
-    digest = OpenSSL::Digest::SHA1.digest(data)
+    digest = OpenSSL::Digest.digest('SHA1', data)
+
+    invalid_sig = key.sign_raw(nil, digest.succ)
+    malformed_sig = "*" * invalid_sig.bytesize
+
+    # Sign by #syssign
     sig = key.syssign(digest)
-    assert(key.sysverify(digest, sig))
+    assert_equal true, key.sysverify(digest, sig)
+    assert_equal false, key.sysverify(digest, invalid_sig)
+    assert_raise(OpenSSL::PKey::DSAError) { key.sysverify(digest, malformed_sig) }
+    assert_equal true, key.verify_raw(nil, sig, digest)
+    assert_equal false, key.verify_raw(nil, invalid_sig, digest)
+    assert_raise(OpenSSL::PKey::PKeyError) { key.verify_raw(nil, malformed_sig, digest) }
+
+    # Sign by #sign_raw
+    sig = key.sign_raw(nil, digest)
+    assert_equal true, key.sysverify(digest, sig)
+    assert_equal false, key.sysverify(digest, invalid_sig)
+    assert_raise(OpenSSL::PKey::DSAError) { key.sysverify(digest, malformed_sig) }
+    assert_equal true, key.verify_raw(nil, sig, digest)
+    assert_equal false, key.verify_raw(nil, invalid_sig, digest)
+    assert_raise(OpenSSL::PKey::PKeyError) { key.verify_raw(nil, malformed_sig, digest) }
   end
 
   def test_DSAPrivateKey
@@ -124,6 +164,8 @@
 
   def test_PUBKEY
     dsa512 = Fixtures.pkey("dsa512")
+    dsa512pub = OpenSSL::PKey::DSA.new(dsa512.public_to_der)
+
     asn1 = OpenSSL::ASN1::Sequence([
       OpenSSL::ASN1::Sequence([
         OpenSSL::ASN1::ObjectId("DSA"),
@@ -139,7 +181,7 @@
     ])
     key = OpenSSL::PKey::DSA.new(asn1.to_der)
     assert_not_predicate key, :private?
-    assert_same_dsa dup_public(dsa512), key
+    assert_same_dsa dsa512pub, key
 
     pem = <<~EOF
     -----BEGIN PUBLIC KEY-----
@@ -152,10 +194,15 @@
     -----END PUBLIC KEY-----
     EOF
     key = OpenSSL::PKey::DSA.new(pem)
-    assert_same_dsa dup_public(dsa512), key
+    assert_same_dsa dsa512pub, key
 
-    assert_equal asn1.to_der, dup_public(dsa512).to_der
-    assert_equal pem, dup_public(dsa512).export
+    assert_equal asn1.to_der, key.to_der
+    assert_equal pem, key.export
+
+    assert_equal asn1.to_der, dsa512.public_to_der
+    assert_equal asn1.to_der, key.public_to_der
+    assert_equal pem, dsa512.public_to_pem
+    assert_equal pem, key.public_to_pem
   end
 
   def test_read_DSAPublicKey_pem
@@ -184,11 +231,22 @@
   end
 
   def test_dup
-    key = OpenSSL::PKey::DSA.new(256)
+    key = Fixtures.pkey("dsa1024")
     key2 = key.dup
     assert_equal key.params, key2.params
-    key2.set_pqg(key2.p + 1, key2.q, key2.g)
-    assert_not_equal key.params, key2.params
+
+    # PKey is immutable in OpenSSL >= 3.0
+    if !openssl?(3, 0, 0)
+      key2.set_pqg(key2.p + 1, key2.q, key2.g)
+      assert_not_equal key.params, key2.params
+    end
+  end
+
+  def test_marshal
+    key = Fixtures.pkey("dsa1024")
+    deserialized = Marshal.load(Marshal.dump(key))
+
+    assert_equal key.to_der, deserialized.to_der
   end
 
   private
diff -ruN ruby-2.5.9.orig/test/openssl/test_pkey_ec.rb ruby-2.5.9/test/openssl/test_pkey_ec.rb
--- ruby-2.5.9.orig/test/openssl/test_pkey_ec.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_pkey_ec.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,34 +1,22 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
-if defined?(OpenSSL) && defined?(OpenSSL::PKey::EC)
+if defined?(OpenSSL)
 
 class OpenSSL::TestEC < OpenSSL::PKeyTestCase
   def test_ec_key
-    builtin_curves = OpenSSL::PKey::EC.builtin_curves
-    assert_not_empty builtin_curves
+    key1 = OpenSSL::PKey::EC.generate("prime256v1")
 
-    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.new(curve_name)
-      key.generate_key!
-
-      assert_predicate key, :private?
-      assert_predicate key, :public?
-      assert_nothing_raised { key.check_key }
+    # PKey is immutable in OpenSSL >= 3.0; constructing an empty EC object is
+    # deprecated
+    if !openssl?(3, 0, 0)
+      key2 = OpenSSL::PKey::EC.new
+      key2.group = key1.group
+      key2.private_key = key1.private_key
+      key2.public_key = key1.public_key
+      assert_equal key1.to_der, key2.to_der
     end
 
-    key1 = OpenSSL::PKey::EC.new("prime256v1").generate_key!
-
-    key2 = OpenSSL::PKey::EC.new
-    key2.group = key1.group
-    key2.private_key = key1.private_key
-    key2.public_key = key1.public_key
-    assert_equal key1.to_der, key2.to_der
-
     key3 = OpenSSL::PKey::EC.new(key1)
     assert_equal key1.to_der, key3.to_der
 
@@ -37,10 +25,25 @@
 
     key5 = key1.dup
     assert_equal key1.to_der, key5.to_der
-    key_tmp = OpenSSL::PKey::EC.new("prime256v1").generate_key!
-    key5.private_key = key_tmp.private_key
-    key5.public_key = key_tmp.public_key
-    assert_not_equal key1.to_der, key5.to_der
+
+    # PKey is immutable in OpenSSL >= 3.0; EC object should not be modified
+    if !openssl?(3, 0, 0)
+      key_tmp = OpenSSL::PKey::EC.generate("prime256v1")
+      key5.private_key = key_tmp.private_key
+      key5.public_key = key_tmp.public_key
+      assert_not_equal key1.to_der, key5.to_der
+    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
@@ -52,32 +55,59 @@
     assert_equal(true, ec.private?)
   end
 
+  def test_generate_key
+    ec = OpenSSL::PKey::EC.new("prime256v1")
+    assert_equal false, ec.private?
+    assert_raise(OpenSSL::PKey::ECError) { ec.to_der }
+    ec.generate_key!
+    assert_equal true, ec.private?
+    assert_nothing_raised { ec.to_der }
+  end if !openssl?(3, 0, 0)
+
+  def test_marshal
+    key = Fixtures.pkey("p256")
+    deserialized = Marshal.load(Marshal.dump(key))
+
+    assert_equal key.to_der, deserialized.to_der
+  end
+
   def test_check_key
-    key = OpenSSL::PKey::EC.new("prime256v1").generate_key!
-    assert_equal(true, key.check_key)
-    assert_equal(true, key.private?)
-    assert_equal(true, key.public?)
-    key2 = OpenSSL::PKey::EC.new(key.group)
-    assert_equal(false, key2.private?)
-    assert_equal(false, key2.public?)
-    key2.public_key = key.public_key
-    assert_equal(false, key2.private?)
-    assert_equal(true, key2.public?)
-    key2.private_key = key.private_key
+    key0 = Fixtures.pkey("p256")
+    assert_equal(true, key0.check_key)
+    assert_equal(true, key0.private?)
+    assert_equal(true, key0.public?)
+
+    key1 = OpenSSL::PKey.read(key0.public_to_der)
+    assert_equal(true, key1.check_key)
+    assert_equal(false, key1.private?)
+    assert_equal(true, key1.public?)
+
+    key2 = OpenSSL::PKey.read(key0.private_to_der)
     assert_equal(true, key2.private?)
     assert_equal(true, key2.public?)
     assert_equal(true, key2.check_key)
-    key2.private_key += 1
-    assert_raise(OpenSSL::PKey::ECError) { key2.check_key }
+
+    # Behavior of EVP_PKEY_public_check changes between OpenSSL 1.1.1 and 3.0
+    key4 = Fixtures.pkey("p256_too_large")
+    assert_raise(OpenSSL::PKey::ECError) { key4.check_key }
+
+    key5 = Fixtures.pkey("p384_invalid")
+    assert_raise(OpenSSL::PKey::ECError) { key5.check_key }
+
+    # EC#private_key= is deprecated in 3.0 and won't work on OpenSSL 3.0
+    if !openssl?(3, 0, 0)
+      key2.private_key += 1
+      assert_raise(OpenSSL::PKey::ECError) { key2.check_key }
+    end
   end
 
   def test_sign_verify
     p256 = Fixtures.pkey("p256")
     data = "Sign me!"
-    signature = p256.sign("SHA1", data)
-    assert_equal true, p256.verify("SHA1", signature, data)
+    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;
@@ -86,17 +116,50 @@
     assert_equal false, p256.verify("SHA256", signature1, data)
   end
 
-  def test_dsa_sign_verify
+  def test_derive_key
+    # NIST CAVP, KAS_ECC_CDH_PrimitiveTest.txt, P-256 COUNT = 0
+    qCAVSx = "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
+    qCAVSy = "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac"
+    dIUT = "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534"
+    zIUT = "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b"
+    a = OpenSSL::PKey::EC.new("prime256v1")
+    a.private_key = OpenSSL::BN.new(dIUT, 16)
+    b = OpenSSL::PKey::EC.new("prime256v1")
+    uncompressed = OpenSSL::BN.new("04" + qCAVSx + qCAVSy, 16)
+    b.public_key = OpenSSL::PKey::EC::Point.new(b.group, uncompressed)
+    assert_equal [zIUT].pack("H*"), a.derive(b)
+
+    assert_equal a.derive(b), a.dh_compute_key(b.public_key)
+  end if !openssl?(3, 0, 0) # TODO: Test it without using #private_key=
+
+  def test_sign_verify_raw
+    key = Fixtures.pkey("p256")
     data1 = "foo"
     data2 = "bar"
-    key = OpenSSL::PKey::EC.new("prime256v1").generate_key!
+
+    malformed_sig = "*" * 30
+
+    # Sign by #dsa_sign_asn1
     sig = key.dsa_sign_asn1(data1)
     assert_equal true, key.dsa_verify_asn1(data1, sig)
     assert_equal false, key.dsa_verify_asn1(data2, sig)
+    assert_raise(OpenSSL::PKey::ECError) { key.dsa_verify_asn1(data1, malformed_sig) }
+    assert_equal true, key.verify_raw(nil, sig, data1)
+    assert_equal false, key.verify_raw(nil, sig, data2)
+    assert_raise(OpenSSL::PKey::PKeyError) { key.verify_raw(nil, malformed_sig, data1) }
+
+    # Sign by #sign_raw
+    sig = key.sign_raw(nil, data1)
+    assert_equal true, key.dsa_verify_asn1(data1, sig)
+    assert_equal false, key.dsa_verify_asn1(data2, sig)
+    assert_raise(OpenSSL::PKey::ECError) { key.dsa_verify_asn1(data1, malformed_sig) }
+    assert_equal true, key.verify_raw(nil, sig, data1)
+    assert_equal false, key.verify_raw(nil, sig, data2)
+    assert_raise(OpenSSL::PKey::PKeyError) { key.verify_raw(nil, malformed_sig, data1) }
   end
 
   def test_dsa_sign_asn1_FIPS186_3
-    key = OpenSSL::PKey::EC.new("prime256v1").generate_key!
+    key = OpenSSL::PKey::EC.generate("prime256v1")
     size = key.group.order.num_bits / 8 + 1
     dgst = (1..size).to_a.pack('C*')
     sig = key.dsa_sign_asn1(dgst)
@@ -105,8 +168,8 @@
   end
 
   def test_dh_compute_key
-    key_a = OpenSSL::PKey::EC.new("prime256v1").generate_key!
-    key_b = OpenSSL::PKey::EC.new(key_a.group).generate_key!
+    key_a = OpenSSL::PKey::EC.generate("prime256v1")
+    key_b = OpenSSL::PKey::EC.generate(key_a.group)
 
     pub_a = key_a.public_key
     pub_b = key_b.public_key
@@ -142,7 +205,32 @@
     assert_equal pem, p256.export
   end
 
+  def test_ECPrivateKey_with_parameters
+    p256 = Fixtures.pkey("p256")
+
+    # The format used by "openssl ecparam -name prime256v1 -genkey -outform PEM"
+    #
+    # "EC PARAMETERS" block should be ignored if it is followed by an
+    # "EC PRIVATE KEY" block
+    in_pem = <<~EOF
+    -----BEGIN EC PARAMETERS-----
+    BggqhkjOPQMBBw==
+    -----END EC PARAMETERS-----
+    -----BEGIN EC PRIVATE KEY-----
+    MHcCAQEEIID49FDqcf1O1eO8saTgG70UbXQw9Fqwseliit2aWhH1oAoGCCqGSM49
+    AwEHoUQDQgAEFglk2c+oVUIKQ64eZG9bhLNPWB7lSZ/ArK41eGy5wAzU/0G51Xtt
+    CeBUl+MahZtn9fO1JKdF4qJmS39dXnpENg==
+    -----END EC PRIVATE KEY-----
+    EOF
+
+    key = OpenSSL::PKey::EC.new(in_pem)
+    assert_same_ec p256, key
+    assert_equal p256.to_der, key.to_der
+  end
+
   def test_ECPrivateKey_encrypted
+    omit_on_fips
+
     p256 = Fixtures.pkey("p256")
     # key = abcdef
     pem = <<~EOF
@@ -170,6 +258,8 @@
 
   def test_PUBKEY
     p256 = Fixtures.pkey("p256")
+    p256pub = OpenSSL::PKey::EC.new(p256.public_to_der)
+
     asn1 = OpenSSL::ASN1::Sequence([
       OpenSSL::ASN1::Sequence([
         OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
@@ -181,7 +271,7 @@
     ])
     key = OpenSSL::PKey::EC.new(asn1.to_der)
     assert_not_predicate key, :private?
-    assert_same_ec dup_public(p256), key
+    assert_same_ec p256pub, key
 
     pem = <<~EOF
     -----BEGIN PUBLIC KEY-----
@@ -190,10 +280,15 @@
     -----END PUBLIC KEY-----
     EOF
     key = OpenSSL::PKey::EC.new(pem)
-    assert_same_ec dup_public(p256), key
+    assert_same_ec p256pub, key
 
-    assert_equal asn1.to_der, dup_public(p256).to_der
-    assert_equal pem, dup_public(p256).export
+    assert_equal asn1.to_der, key.to_der
+    assert_equal pem, key.export
+
+    assert_equal asn1.to_der, p256.public_to_der
+    assert_equal asn1.to_der, key.public_to_der
+    assert_equal pem, p256.public_to_pem
+    assert_equal pem, key.public_to_pem
   end
 
   def test_ec_group
@@ -229,7 +324,7 @@
 
   def test_ec_point
     group = OpenSSL::PKey::EC::Group.new("prime256v1")
-    key = OpenSSL::PKey::EC.new(group).generate_key!
+    key = OpenSSL::PKey::EC.generate(group)
     point = key.public_key
 
     point2 = OpenSSL::PKey::EC::Point.new(group, point.to_bn)
@@ -289,6 +384,27 @@
     assert_equal true, point.on_curve?
   end
 
+  def test_ec_point_add
+    begin
+      group = OpenSSL::PKey::EC::Group.new(:GFp, 17, 2, 2)
+      group.point_conversion_form = :uncompressed
+      gen = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 05 01 }))
+      group.set_generator(gen, 19, 1)
+
+      point_a = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 06 03 }))
+      point_b = OpenSSL::PKey::EC::Point.new(group, B(%w{ 04 10 0D }))
+    rescue OpenSSL::PKey::EC::Group::Error
+      pend "Patched OpenSSL rejected curve" if /unsupported field/ =~ $!.message
+      raise
+    end
+
+    result = point_a.add(point_b)
+    assert_equal B(%w{ 04 0D 07 }), result.to_octet_string(:uncompressed)
+
+    assert_raise(TypeError) { point_a.add(nil) }
+    assert_raise(ArgumentError) { point_a.add }
+  end
+
   def test_ec_point_mul
     begin
       # y^2 = x^3 + 2x + 2 over F_17
@@ -305,15 +421,28 @@
       # 3 * (6, 3) + 3 * (5, 1) = (7, 6)
       result_a2 = point_a.mul(3, 3)
       assert_equal B(%w{ 04 07 06 }), result_a2.to_octet_string(:uncompressed)
-      # 3 * point_a = 3 * (6, 3) = (16, 13)
-      result_b1 = point_a.mul([3], [])
-      assert_equal B(%w{ 04 10 0D }), result_b1.to_octet_string(:uncompressed)
-      # 3 * point_a + 2 * point_a = 3 * (6, 3) + 2 * (6, 3) = (7, 11)
-      result_b1 = point_a.mul([3, 2], [point_a])
-      assert_equal B(%w{ 04 07 0B }), result_b1.to_octet_string(:uncompressed)
-      # 3 * point_a + 5 * point_a.group.generator = 3 * (6, 3) + 5 * (5, 1) = (13, 10)
-      result_b1 = point_a.mul([3], [], 5)
-      assert_equal B(%w{ 04 0D 0A }), result_b1.to_octet_string(:uncompressed)
+      EnvUtil.suppress_warning do # Point#mul(ary, ary [, bn]) is deprecated
+        begin
+          result_b1 = point_a.mul([3], [])
+        rescue NotImplementedError
+          # LibreSSL and OpenSSL 3.0 do no longer support this form of calling
+          next
+        end
+
+        # 3 * point_a = 3 * (6, 3) = (16, 13)
+        result_b1 = point_a.mul([3], [])
+        assert_equal B(%w{ 04 10 0D }), result_b1.to_octet_string(:uncompressed)
+        # 3 * point_a + 2 * point_a = 3 * (6, 3) + 2 * (6, 3) = (7, 11)
+        result_b1 = point_a.mul([3, 2], [point_a])
+        assert_equal B(%w{ 04 07 0B }), result_b1.to_octet_string(:uncompressed)
+        # 3 * point_a + 5 * point_a.group.generator = 3 * (6, 3) + 5 * (5, 1) = (13, 10)
+        result_b1 = point_a.mul([3], [], 5)
+        assert_equal B(%w{ 04 0D 0A }), result_b1.to_octet_string(:uncompressed)
+
+        assert_raise(ArgumentError) { point_a.mul([1], [point_a]) }
+        assert_raise(TypeError) { point_a.mul([1], nil) }
+        assert_raise(TypeError) { point_a.mul([nil], []) }
+      end
     rescue OpenSSL::PKey::EC::Group::Error
       # CentOS patches OpenSSL to reject curves defined over Fp where p < 256 bits
       raise if $!.message !~ /unsupported field/
@@ -326,9 +455,6 @@
     # invalid argument
     point = p256_key.public_key
     assert_raise(TypeError) { point.mul(nil) }
-    assert_raise(ArgumentError) { point.mul([1], [point]) }
-    assert_raise(TypeError) { point.mul([1], nil) }
-    assert_raise(TypeError) { point.mul([nil], []) }
   end
 
 # test Group: asn1_flag, point_conversion
diff -ruN ruby-2.5.9.orig/test/openssl/test_pkey.rb ruby-2.5.9/test/openssl/test_pkey.rb
--- ruby-2.5.9.orig/test/openssl/test_pkey.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/test_pkey.rb	2025-01-29 19:08:29.935863721 +0100
@@ -0,0 +1,222 @@
+# frozen_string_literal: true
+require_relative "utils"
+
+class OpenSSL::TestPKey < OpenSSL::PKeyTestCase
+  def test_generic_oid_inspect
+    # RSA private key
+    rsa = Fixtures.pkey("rsa-1")
+    assert_instance_of OpenSSL::PKey::RSA, rsa
+    assert_equal "rsaEncryption", rsa.oid
+    assert_match %r{oid=rsaEncryption}, rsa.inspect
+
+    # X25519 private key
+    x25519_pem = <<~EOF
+    -----BEGIN PRIVATE KEY-----
+    MC4CAQAwBQYDK2VuBCIEIHcHbQpzGKV9PBbBclGyZkXfTC+H68CZKrF3+6UduSwq
+    -----END PRIVATE KEY-----
+    EOF
+    begin
+      x25519 = OpenSSL::PKey.read(x25519_pem)
+    rescue OpenSSL::PKey::PKeyError
+      # OpenSSL < 1.1.0
+      pend "X25519 is not implemented"
+    end
+    assert_instance_of OpenSSL::PKey::PKey, x25519
+    assert_equal "X25519", x25519.oid
+    assert_match %r{oid=X25519}, x25519.inspect
+  end
+
+  def test_s_generate_parameters
+    pkey = OpenSSL::PKey.generate_parameters("EC", {
+      "ec_paramgen_curve" => "secp384r1",
+    })
+    assert_instance_of OpenSSL::PKey::EC, pkey
+    assert_equal "secp384r1", pkey.group.curve_name
+    assert_equal nil, pkey.private_key
+
+    # Invalid options are checked
+    assert_raise(OpenSSL::PKey::PKeyError) {
+      OpenSSL::PKey.generate_parameters("EC", "invalid" => "option")
+    }
+  end
+
+  def test_s_generate_parameters_with_block
+    # DSA kengen is not FIPS-approved.
+    # https://github.com/openssl/openssl/commit/49a35f0#diff-605396c063194975af8ce31399d42690ab18186b422fb5012101cc9132660fe1R611-R614
+    omit_on_fips
+
+    # Parameter generation callback is called
+    if openssl?(3, 0, 0, 0) && !openssl?(3, 0, 0, 6)
+      # Errors in BN_GENCB were not properly handled. This special pend is to
+      # suppress failures on Ubuntu 22.04, which uses OpenSSL 3.0.2.
+      pend "unstable test on OpenSSL 3.0.[0-5]"
+    end
+    cb_called = []
+    assert_raise(RuntimeError) {
+      OpenSSL::PKey.generate_parameters("DSA") { |*args|
+        cb_called << args
+        raise "exit!" if cb_called.size == 3
+      }
+    }
+    assert_not_empty cb_called
+  end
+
+  def test_s_generate_key
+    assert_raise(OpenSSL::PKey::PKeyError) {
+      # DSA key pair cannot be generated without parameters
+      OpenSSL::PKey.generate_key("DSA")
+    }
+    pkey_params = OpenSSL::PKey.generate_parameters("EC", {
+      "ec_paramgen_curve" => "secp384r1",
+    })
+    pkey = OpenSSL::PKey.generate_key(pkey_params)
+    assert_instance_of OpenSSL::PKey::EC, pkey
+    assert_equal "secp384r1", pkey.group.curve_name
+    assert_not_equal nil, pkey.private_key
+  end
+
+  def test_hmac_sign_verify
+    pkey = OpenSSL::PKey.generate_key("HMAC", { "key" => "abcd" })
+
+    hmac = OpenSSL::HMAC.new("abcd", "SHA256").update("data").digest
+    assert_equal hmac, pkey.sign("SHA256", "data")
+
+    # EVP_PKEY_HMAC does not support verify
+    assert_raise(OpenSSL::PKey::PKeyError) {
+      pkey.verify("SHA256", "data", hmac)
+    }
+  end
+
+  def test_ed25519
+    # Ed25519 is not FIPS-approved.
+    omit_on_fips
+    # See EVP_PKEY_sign in Changelog for 3.7.0: https://github.com/libressl/portable/blob/master/ChangeLog
+    omit "Ed25519 not supported" unless openssl?(1, 1, 1) || libressl?(3, 7, 0)
+
+    # Test vector from RFC 8032 Section 7.1 TEST 2
+    priv_pem = <<~EOF
+    -----BEGIN PRIVATE KEY-----
+    MC4CAQAwBQYDK2VwBCIEIEzNCJso/5banbbDRuwRTg9bijGfNaumJNqM9u1PuKb7
+    -----END PRIVATE KEY-----
+    EOF
+    pub_pem = <<~EOF
+    -----BEGIN PUBLIC KEY-----
+    MCowBQYDK2VwAyEAPUAXw+hDiVqStwqnTRt+vJyYLM8uxJaMwM1V8Sr0Zgw=
+    -----END PUBLIC KEY-----
+    EOF
+    priv = OpenSSL::PKey.read(priv_pem)
+    pub = OpenSSL::PKey.read(pub_pem)
+    assert_instance_of OpenSSL::PKey::PKey, priv
+    assert_instance_of OpenSSL::PKey::PKey, pub
+    assert_equal priv_pem, priv.private_to_pem
+    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
+    085ac1e43e15996e458f3613d0f11d8c
+    387b2eaeb4302aeeb00d291612bb0c00
+    EOF
+    data = ["72"].pack("H*")
+    assert_equal sig, priv.sign(nil, data)
+    assert_equal true, priv.verify(nil, sig, data)
+    assert_equal true, pub.verify(nil, sig, data)
+    assert_equal false, pub.verify(nil, sig, data.succ)
+
+    # PureEdDSA wants nil as the message digest
+    assert_raise(OpenSSL::PKey::PKeyError) { priv.sign("SHA512", data) }
+    assert_raise(OpenSSL::PKey::PKeyError) { pub.verify("SHA512", sig, data) }
+
+    # Ed25519 pkey type does not support key derivation
+    assert_raise(OpenSSL::PKey::PKeyError) { priv.derive(pub) }
+  end
+
+  def test_x25519
+    # Test vector from RFC 7748 Section 6.1
+    alice_pem = <<~EOF
+    -----BEGIN PRIVATE KEY-----
+    MC4CAQAwBQYDK2VuBCIEIHcHbQpzGKV9PBbBclGyZkXfTC+H68CZKrF3+6UduSwq
+    -----END PRIVATE KEY-----
+    EOF
+    bob_pem = <<~EOF
+    -----BEGIN PUBLIC KEY-----
+    MCowBQYDK2VuAyEA3p7bfXt9wbTTW2HC7OQ1Nz+DQ8hbeGdNrfx+FG+IK08=
+    -----END PUBLIC KEY-----
+    EOF
+    shared_secret = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"
+    begin
+      alice = OpenSSL::PKey.read(alice_pem)
+      bob = OpenSSL::PKey.read(bob_pem)
+    rescue OpenSSL::PKey::PKeyError
+      # OpenSSL < 1.1.0
+      pend "X25519 is not implemented"
+    end
+    assert_instance_of OpenSSL::PKey::PKey, alice
+    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?
+    key1 = Fixtures.pkey("rsa1024")
+    key2 = Fixtures.pkey("rsa1024")
+    key3 = Fixtures.pkey("rsa2048")
+    key4 = Fixtures.pkey("dh-1")
+
+    assert_equal(true, key1.compare?(key2))
+    assert_equal(true, key1.public_key.compare?(key2))
+    assert_equal(true, key2.compare?(key1))
+    assert_equal(true, key2.public_key.compare?(key1))
+
+    assert_equal(false, key1.compare?(key3))
+
+    assert_raise(TypeError) do
+      key1.compare?(key4)
+    end
+  end
+
+  def test_to_text
+    rsa = Fixtures.pkey("rsa1024")
+    assert_include rsa.to_text, "publicExponent"
+  end
+end
diff -ruN ruby-2.5.9.orig/test/openssl/test_pkey_rsa.rb ruby-2.5.9/test/openssl/test_pkey_rsa.rb
--- ruby-2.5.9.orig/test/openssl/test_pkey_rsa.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_pkey_rsa.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,118 +1,181 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
 
 class OpenSSL::TestPKeyRSA < OpenSSL::PKeyTestCase
-  def test_padding
-    key = OpenSSL::PKey::RSA.new(512, 3)
-
-    # Need right size for raw mode
-    plain0 = "x" * (512/8)
-    cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING)
-    plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING)
-    assert_equal(plain0, plain1)
-
-    # Need smaller size for pkcs1 mode
-    plain0 = "x" * (512/8 - 11)
-    cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING)
-    plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING)
-    assert_equal(plain0, plain1)
-
-    cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default
-    plain1 = key.public_decrypt(cipherdef)
-    assert_equal(plain0, plain1)
-    assert_equal(cipher1, cipherdef)
-
-    # Failure cases
-    assert_raise(ArgumentError){ key.private_encrypt() }
-    assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) }
-    assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) }
-  end
+  def test_no_private_exp
+    key = OpenSSL::PKey::RSA.new
+    rsa = Fixtures.pkey("rsa2048")
+    key.set_key(rsa.n, rsa.e, nil)
+    key.set_factors(rsa.p, rsa.q)
+    assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt("foo") }
+    assert_raise(OpenSSL::PKey::RSAError){ key.private_decrypt("foo") }
+  end if !openssl?(3, 0, 0) # Impossible state in OpenSSL 3.0
 
   def test_private
-    key = OpenSSL::PKey::RSA.new(512, 3)
-    assert(key.private?)
+    key = Fixtures.pkey("rsa2048")
+
+    # Generated by DER
     key2 = OpenSSL::PKey::RSA.new(key.to_der)
     assert(key2.private?)
+
+    # public key
     key3 = key.public_key
     assert(!key3.private?)
+
+    # Generated by public key DER
     key4 = OpenSSL::PKey::RSA.new(key3.to_der)
     assert(!key4.private?)
+    rsa1024 = Fixtures.pkey("rsa1024")
+
+    if !openssl?(3, 0, 0)
+      key = OpenSSL::PKey::RSA.new
+      # Generated by RSA#set_key
+      key5 = OpenSSL::PKey::RSA.new
+      key5.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
+      assert(key5.private?)
+
+      # Generated by RSA#set_key, without d
+      key6 = OpenSSL::PKey::RSA.new
+      key6.set_key(rsa1024.n, rsa1024.e, nil)
+      assert(!key6.private?)
+    end
   end
 
   def test_new
-    key = OpenSSL::PKey::RSA.new 512
-    pem  = key.public_key.to_pem
-    OpenSSL::PKey::RSA.new pem
-    assert_equal([], OpenSSL.errors)
+    key = OpenSSL::PKey::RSA.new(2048)
+    assert_equal 2048, key.n.num_bits
+    assert_equal 65537, key.e
+    assert_not_nil key.d
+    assert(key.private?)
   end
 
-  def test_new_exponent_default
-    assert_equal(65537, OpenSSL::PKey::RSA.new(512).e)
+  def test_new_public_exponent
+    # At least 2024-bits RSA key are required in FIPS.
+    omit_on_fips
+
+    # Specify public exponent
+    key = OpenSSL::PKey::RSA.new(512, 3)
+    assert_equal 512, key.n.num_bits
+    assert_equal 3, key.e
   end
 
-  def test_new_with_exponent
-    1.upto(30) do |idx|
-      e = (2 ** idx) + 1
-      key = OpenSSL::PKey::RSA.new(512, e)
-      assert_equal(e, key.e)
-    end
+  def test_s_generate
+    key1 = OpenSSL::PKey::RSA.generate(2048)
+    assert_equal 2048, key1.n.num_bits
+    assert_equal 65537, key1.e
   end
 
-  def test_generate
-    key = OpenSSL::PKey::RSA.generate(512, 17)
+  def test_s_generate_public_exponent
+    # At least 2024-bits RSA key are required in FIPS.
+    omit_on_fips
+
+    # Specify public exponent
+    key = OpenSSL::PKey::RSA.generate(512, 3)
     assert_equal 512, key.n.num_bits
-    assert_equal 17, key.e
-    assert_not_nil key.d
+    assert_equal 3, key.e
   end
 
   def test_new_break
-    assert_nil(OpenSSL::PKey::RSA.new(1024) { break })
+    assert_nil(OpenSSL::PKey::RSA.new(2048) { break })
     assert_raise(RuntimeError) do
-      OpenSSL::PKey::RSA.new(1024) { raise }
+      OpenSSL::PKey::RSA.new(2048) { raise }
     end
   end
 
   def test_sign_verify
-    rsa1024 = Fixtures.pkey("rsa1024")
+    rsa = Fixtures.pkey("rsa2048")
     data = "Sign me!"
-    signature = rsa1024.sign("SHA1", data)
-    assert_equal true, rsa1024.verify("SHA1", signature, data)
+    signature = rsa.sign("SHA256", data)
+    assert_equal true, rsa.verify("SHA256", signature, data)
 
-    signature0 = (<<~'end;').unpack("m")[0]
-      oLCgbprPvfhM4pjFQiDTFeWI9Sk+Og7Nh9TmIZ/xSxf2CGXQrptlwo7NQ28+
-      WA6YQo8jPH4hSuyWIM4Gz4qRYiYRkl5TDMUYob94zm8Si1HxEiS9354tzvqS
-      zS8MLW2BtNPuTubMxTItHGTnOzo9sUg0LAHVFt8kHG2NfKAw/gQ=
+    signature0 = (<<~'end;').unpack1("m")
+      ooy49i8aeFtkDYUU0RPDsEugGiNw4lZxpbQPnIwtdftEkka945IqKZ/MY3YSw7wKsvBZeaTy8GqL
+      lSWLThsRFDV+UUS9zUBbQ9ygNIT8OjdV+tNL63ZpKGprczSnw4F05MQIpajNRud/8jiI9rf+Wysi
+      WwXecjMl2FlXlLJHY4PFQZU5TiametB4VCQRMcjLo1uf26u/yRpiGaYyqn5vxs0SqNtUDM1UL6x4
+      NHCAdqLjuFRQPjYp1vGLD3eSl4061pS8x1NVap3YGbYfGUyzZO4VfwFwf1jPdhp/OX/uZw4dGB2H
+      gSK+q1JiDFwEE6yym5tdKovL1g1NhFYHF6gkZg==
     end;
-    assert_equal true, rsa1024.verify("SHA256", signature0, data)
+    assert_equal true, rsa.verify("SHA256", signature0, data)
     signature1 = signature0.succ
-    assert_equal false, rsa1024.verify("SHA256", signature1, data)
+    assert_equal false, rsa.verify("SHA256", signature1, data)
   end
 
-  def test_digest_state_irrelevant_sign
-    key = Fixtures.pkey("rsa1024")
-    digest1 = OpenSSL::Digest::SHA1.new
-    digest2 = OpenSSL::Digest::SHA1.new
-    data = 'Sign me!'
-    digest1 << 'Change state of digest1'
-    sig1 = key.sign(digest1, data)
-    sig2 = key.sign(digest2, data)
-    assert_equal(sig1, sig2)
+  def test_sign_verify_options
+    key = Fixtures.pkey("rsa2048")
+    data = "Sign me!"
+    pssopts = {
+      "rsa_padding_mode" => "pss",
+      "rsa_pss_saltlen" => 20,
+      "rsa_mgf1_md" => "SHA1"
+    }
+    sig_pss = key.sign("SHA256", data, pssopts)
+    assert_equal 256, sig_pss.bytesize
+    assert_equal true, key.verify("SHA256", sig_pss, data, pssopts)
+    assert_equal true, key.verify_pss("SHA256", sig_pss, data,
+                                      salt_length: 20, mgf1_hash: "SHA1")
+    # Defaults to PKCS #1 v1.5 padding => verification failure
+    assert_equal false, key.verify("SHA256", sig_pss, data)
+
+    # option type check
+    assert_raise_with_message(TypeError, /expected Hash/) {
+      key.sign("SHA256", data, ["x"])
+    }
   end
 
-  def test_digest_state_irrelevant_verify
-    key = Fixtures.pkey("rsa1024")
-    digest1 = OpenSSL::Digest::SHA1.new
-    digest2 = OpenSSL::Digest::SHA1.new
-    data = 'Sign me!'
-    sig = key.sign(digest1, data)
-    digest1.reset
-    digest1 << 'Change state of digest1'
-    assert(key.verify(digest1, sig, data))
-    assert(key.verify(digest2, sig, data))
+  def test_sign_verify_raw
+    key = Fixtures.pkey("rsa-1")
+    data = "Sign me!"
+    hash = OpenSSL::Digest.digest("SHA256", data)
+    signature = key.sign_raw("SHA256", hash)
+    assert_equal true, key.verify_raw("SHA256", signature, hash)
+    assert_equal true, key.verify("SHA256", signature, data)
+
+    # Too long data
+    assert_raise(OpenSSL::PKey::PKeyError) {
+      key.sign_raw("SHA1", "x" * (key.n.num_bytes + 1))
+    }
+
+    # With options
+    pssopts = {
+      "rsa_padding_mode" => "pss",
+      "rsa_pss_saltlen" => 20,
+      "rsa_mgf1_md" => "SHA256"
+    }
+    sig_pss = key.sign_raw("SHA256", hash, pssopts)
+    assert_equal true, key.verify("SHA256", sig_pss, data, pssopts)
+    assert_equal true, key.verify_raw("SHA256", sig_pss, hash, pssopts)
   end
 
+  def test_sign_verify_raw_legacy
+    key = Fixtures.pkey("rsa-1")
+    bits = key.n.num_bits
+
+    # Need right size for raw mode
+    plain0 = "x" * (bits/8)
+    cipher = key.private_encrypt(plain0, OpenSSL::PKey::RSA::NO_PADDING)
+    plain1 = key.public_decrypt(cipher, OpenSSL::PKey::RSA::NO_PADDING)
+    assert_equal(plain0, plain1)
+
+    # Need smaller size for pkcs1 mode
+    plain0 = "x" * (bits/8 - 11)
+    cipher1 = key.private_encrypt(plain0, OpenSSL::PKey::RSA::PKCS1_PADDING)
+    plain1 = key.public_decrypt(cipher1, OpenSSL::PKey::RSA::PKCS1_PADDING)
+    assert_equal(plain0, plain1)
+
+    cipherdef = key.private_encrypt(plain0) # PKCS1_PADDING is default
+    plain1 = key.public_decrypt(cipherdef)
+    assert_equal(plain0, plain1)
+    assert_equal(cipher1, cipherdef)
+
+    # Failure cases
+    assert_raise(ArgumentError){ key.private_encrypt() }
+    assert_raise(ArgumentError){ key.private_encrypt("hi", 1, nil) }
+    assert_raise(OpenSSL::PKey::RSAError){ key.private_encrypt(plain0, 666) }
+  end
+
+
   def test_verify_empty_rsa
     rsa = OpenSSL::PKey::RSA.new
     assert_raise(OpenSSL::PKey::PKeyError, "[Bug #12783]") {
@@ -121,12 +184,12 @@
   end
 
   def test_sign_verify_pss
-    key = Fixtures.pkey("rsa1024")
+    key = Fixtures.pkey("rsa2048")
     data = "Sign me!"
     invalid_data = "Sign me?"
 
     signature = key.sign_pss("SHA256", data, salt_length: 20, mgf1_hash: "SHA1")
-    assert_equal 128, signature.bytesize
+    assert_equal 256, signature.bytesize
     assert_equal true,
       key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1")
     assert_equal true,
@@ -142,59 +205,182 @@
     assert_equal false,
       key.verify_pss("SHA256", signature, data, salt_length: 20, mgf1_hash: "SHA1")
 
-    signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA1")
-    assert_equal true,
-      key.verify_pss("SHA256", signature, data, salt_length: 94, mgf1_hash: "SHA1")
-    assert_equal true,
-      key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1")
+    # The sign_pss with `salt_length: :max` raises the "invalid salt length"
+    # error in FIPS. We need to skip the tests in FIPS.
+    # According to FIPS 186-5 section 5.4, the salt length shall be between zero
+    # and the output block length of the digest function (inclusive).
+    #
+    # FIPS 186-5 section 5.4 PKCS #1
+    # https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
+    unless OpenSSL.fips_mode
+      signature = key.sign_pss("SHA256", data, salt_length: :max, mgf1_hash: "SHA1")
+      # Should verify on the following salt_length (sLen).
+      # sLen <= emLen (octat) - 2 - hLen (octet) = 2048 / 8 - 2 - 256 / 8 = 222
+      # https://datatracker.ietf.org/doc/html/rfc8017#section-9.1.1
+      assert_equal true,
+        key.verify_pss("SHA256", signature, data, salt_length: 222, mgf1_hash: "SHA1")
+      assert_equal true,
+        key.verify_pss("SHA256", signature, data, salt_length: :auto, mgf1_hash: "SHA1")
+    end
 
     assert_raise(OpenSSL::PKey::RSAError) {
-      key.sign_pss("SHA256", data, salt_length: 95, mgf1_hash: "SHA1")
+      key.sign_pss("SHA256", data, salt_length: 223, mgf1_hash: "SHA1")
     }
   end
 
-  def test_RSAPrivateKey
+  def test_encrypt_decrypt
+    rsapriv = Fixtures.pkey("rsa-1")
+    rsapub = OpenSSL::PKey.read(rsapriv.public_to_der)
+
+    # Defaults to PKCS #1 v1.5
+    raw = "data"
+    # According to the NIST SP 800-131A Rev. 2 section 6, PKCS#1 v1.5 padding is
+    # not permitted for key agreement and key transport using RSA in FIPS.
+    # https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
+    unless OpenSSL.fips_mode
+      enc = rsapub.encrypt(raw)
+      assert_equal raw, rsapriv.decrypt(enc)
+    end
+
+    # Invalid options
+    assert_raise(OpenSSL::PKey::PKeyError) {
+      rsapub.encrypt(raw, { "nonexistent" => "option" })
+    }
+  end
+
+  def test_encrypt_decrypt_legacy
+    rsapriv = Fixtures.pkey("rsa-1")
+    rsapub = OpenSSL::PKey.read(rsapriv.public_to_der)
+
+    # Defaults to PKCS #1 v1.5
+    unless OpenSSL.fips_mode
+      raw = "data"
+      enc_legacy = rsapub.public_encrypt(raw)
+      assert_equal raw, rsapriv.decrypt(enc_legacy)
+      enc_new = rsapub.encrypt(raw)
+      assert_equal raw, rsapriv.private_decrypt(enc_new)
+    end
+
+    # OAEP with default parameters
+    raw = "data"
+    enc_legacy = rsapub.public_encrypt(raw, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
+    assert_equal raw, rsapriv.decrypt(enc_legacy, { "rsa_padding_mode" => "oaep" })
+    enc_new = rsapub.encrypt(raw, { "rsa_padding_mode" => "oaep" })
+    assert_equal raw, rsapriv.private_decrypt(enc_legacy, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
+  end
+
+  def test_export
+    rsa1024 = Fixtures.pkey("rsa1024")
+
+    pub = OpenSSL::PKey.read(rsa1024.public_to_der)
+    assert_not_equal rsa1024.export, pub.export
+    assert_equal rsa1024.public_to_pem, pub.export
+
+    # PKey is immutable in OpenSSL >= 3.0
+    if !openssl?(3, 0, 0)
+      key = OpenSSL::PKey::RSA.new
+
+      # key has only n, e and d
+      key.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
+      assert_equal rsa1024.public_key.export, key.export
+
+      # key has only n, e, d, p and q
+      key.set_factors(rsa1024.p, rsa1024.q)
+      assert_equal rsa1024.public_key.export, key.export
+
+      # key has n, e, d, p, q, dmp1, dmq1 and iqmp
+      key.set_crt_params(rsa1024.dmp1, rsa1024.dmq1, rsa1024.iqmp)
+      assert_equal rsa1024.export, key.export
+    end
+  end
+
+  def test_to_der
     rsa1024 = Fixtures.pkey("rsa1024")
+
+    pub = OpenSSL::PKey.read(rsa1024.public_to_der)
+    assert_not_equal rsa1024.to_der, pub.to_der
+    assert_equal rsa1024.public_to_der, pub.to_der
+
+    # PKey is immutable in OpenSSL >= 3.0
+    if !openssl?(3, 0, 0)
+      key = OpenSSL::PKey::RSA.new
+
+      # key has only n, e and d
+      key.set_key(rsa1024.n, rsa1024.e, rsa1024.d)
+      assert_equal rsa1024.public_key.to_der, key.to_der
+
+      # key has only n, e, d, p and q
+      key.set_factors(rsa1024.p, rsa1024.q)
+      assert_equal rsa1024.public_key.to_der, key.to_der
+
+      # key has n, e, d, p, q, dmp1, dmq1 and iqmp
+      key.set_crt_params(rsa1024.dmp1, rsa1024.dmq1, rsa1024.iqmp)
+      assert_equal rsa1024.to_der, key.to_der
+    end
+  end
+
+  def test_RSAPrivateKey
+    rsa = Fixtures.pkey("rsa2048")
     asn1 = OpenSSL::ASN1::Sequence([
       OpenSSL::ASN1::Integer(0),
-      OpenSSL::ASN1::Integer(rsa1024.n),
-      OpenSSL::ASN1::Integer(rsa1024.e),
-      OpenSSL::ASN1::Integer(rsa1024.d),
-      OpenSSL::ASN1::Integer(rsa1024.p),
-      OpenSSL::ASN1::Integer(rsa1024.q),
-      OpenSSL::ASN1::Integer(rsa1024.dmp1),
-      OpenSSL::ASN1::Integer(rsa1024.dmq1),
-      OpenSSL::ASN1::Integer(rsa1024.iqmp)
+      OpenSSL::ASN1::Integer(rsa.n),
+      OpenSSL::ASN1::Integer(rsa.e),
+      OpenSSL::ASN1::Integer(rsa.d),
+      OpenSSL::ASN1::Integer(rsa.p),
+      OpenSSL::ASN1::Integer(rsa.q),
+      OpenSSL::ASN1::Integer(rsa.dmp1),
+      OpenSSL::ASN1::Integer(rsa.dmq1),
+      OpenSSL::ASN1::Integer(rsa.iqmp)
     ])
     key = OpenSSL::PKey::RSA.new(asn1.to_der)
     assert_predicate key, :private?
-    assert_same_rsa rsa1024, key
+    assert_same_rsa rsa, key
 
     pem = <<~EOF
     -----BEGIN RSA PRIVATE KEY-----
-    MIICXgIBAAKBgQDLwsSw1ECnPtT+PkOgHhcGA71nwC2/nL85VBGnRqDxOqjVh7Cx
-    aKPERYHsk4BPCkE3brtThPWc9kjHEQQ7uf9Y1rbCz0layNqHyywQEVLFmp1cpIt/
-    Q3geLv8ZD9pihowKJDyMDiN6ArYUmZczvW4976MU3+l54E6lF/JfFEU5hwIDAQAB
-    AoGBAKSl/MQarye1yOysqX6P8fDFQt68VvtXkNmlSiKOGuzyho0M+UVSFcs6k1L0
-    maDE25AMZUiGzuWHyaU55d7RXDgeskDMakD1v6ZejYtxJkSXbETOTLDwUWTn618T
-    gnb17tU1jktUtU67xK/08i/XodlgnQhs6VoHTuCh3Hu77O6RAkEA7+gxqBuZR572
-    74/akiW/SuXm0SXPEviyO1MuSRwtI87B02D0qgV8D1UHRm4AhMnJ8MCs1809kMQE
-    JiQUCrp9mQJBANlt2ngBO14us6NnhuAseFDTBzCHXwUUu1YKHpMMmxpnGqaldGgX
-    sOZB3lgJsT9VlGf3YGYdkLTNVbogQKlKpB8CQQDiSwkb4vyQfDe8/NpU5Not0fII
-    8jsDUCb+opWUTMmfbxWRR3FBNu8wnym/m19N4fFj8LqYzHX4KY0oVPu6qvJxAkEA
-    wa5snNekFcqONLIE4G5cosrIrb74sqL8GbGb+KuTAprzj5z1K8Bm0UW9lTjVDjDi
-    qRYgZfZSL+x1P/54+xTFSwJAY1FxA/N3QPCXCjPh5YqFxAMQs2VVYTfg+t0MEcJD
-    dPMQD5JX6g5HKnHFg2mZtoXQrWmJSn7p8GJK8yNTopEErA==
+    MIIEpAIBAAKCAQEAuV9ht9J7k4NBs38jOXvvTKY9gW8nLICSno5EETR1cuF7i4pN
+    s9I1QJGAFAX0BEO4KbzXmuOvfCpD3CU+Slp1enenfzq/t/e/1IRW0wkJUJUFQign
+    4CtrkJL+P07yx18UjyPlBXb81ApEmAB5mrJVSrWmqbjs07JbuS4QQGGXLc+Su96D
+    kYKmSNVjBiLxVVSpyZfAY3hD37d60uG+X8xdW5v68JkRFIhdGlb6JL8fllf/A/bl
+    NwdJOhVr9mESHhwGjwfSeTDPfd8ZLE027E5lyAVX9KZYcU00mOX+fdxOSnGqS/8J
+    DRh0EPHDL15RcJjV2J6vZjPb0rOYGDoMcH+94wIDAQABAoIBAAzsamqfYQAqwXTb
+    I0CJtGg6msUgU7HVkOM+9d3hM2L791oGHV6xBAdpXW2H8LgvZHJ8eOeSghR8+dgq
+    PIqAffo4x1Oma+FOg3A0fb0evyiACyrOk+EcBdbBeLo/LcvahBtqnDfiUMQTpy6V
+    seSoFCwuN91TSCeGIsDpRjbG1vxZgtx+uI+oH5+ytqJOmfCksRDCkMglGkzyfcl0
+    Xc5CUhIJ0my53xijEUQl19rtWdMnNnnkdbG8PT3LZlOta5Do86BElzUYka0C6dUc
+    VsBDQ0Nup0P6rEQgy7tephHoRlUGTYamsajGJaAo1F3IQVIrRSuagi7+YpSpCqsW
+    wORqorkCgYEA7RdX6MDVrbw7LePnhyuaqTiMK+055/R1TqhB1JvvxJ1CXk2rDL6G
+    0TLHQ7oGofd5LYiemg4ZVtWdJe43BPZlVgT6lvL/iGo8JnrncB9Da6L7nrq/+Rvj
+    XGjf1qODCK+LmreZWEsaLPURIoR/Ewwxb9J2zd0CaMjeTwafJo1CZvcCgYEAyCgb
+    aqoWvUecX8VvARfuA593Lsi50t4MEArnOXXcd1RnXoZWhbx5rgO8/ATKfXr0BK/n
+    h2GF9PfKzHFm/4V6e82OL7gu/kLy2u9bXN74vOvWFL5NOrOKPM7Kg+9I131kNYOw
+    Ivnr/VtHE5s0dY7JChYWE1F3vArrOw3T00a4CXUCgYEA0SqY+dS2LvIzW4cHCe9k
+    IQqsT0yYm5TFsUEr4sA3xcPfe4cV8sZb9k/QEGYb1+SWWZ+AHPV3UW5fl8kTbSNb
+    v4ng8i8rVVQ0ANbJO9e5CUrepein2MPL0AkOATR8M7t7dGGpvYV0cFk8ZrFx0oId
+    U0PgYDotF/iueBWlbsOM430CgYEAqYI95dFyPI5/AiSkY5queeb8+mQH62sdcCCr
+    vd/w/CZA/K5sbAo4SoTj8dLk4evU6HtIa0DOP63y071eaxvRpTNqLUOgmLh+D6gS
+    Cc7TfLuFrD+WDBatBd5jZ+SoHccVrLR/4L8jeodo5FPW05A+9gnKXEXsTxY4LOUC
+    9bS4e1kCgYAqVXZh63JsMwoaxCYmQ66eJojKa47VNrOeIZDZvd2BPVf30glBOT41
+    gBoDG3WMPZoQj9pb7uMcrnvs4APj2FIhMU8U15LcPAj59cD6S6rWnAxO8NFK7HQG
+    4Jxg3JNNf8ErQoCHb1B3oVdXJkmbJkARoDpBKmTCgKtP8ADYLmVPQw==
     -----END RSA PRIVATE KEY-----
     EOF
     key = OpenSSL::PKey::RSA.new(pem)
-    assert_same_rsa rsa1024, key
+    assert_same_rsa rsa, key
+
+    assert_equal asn1.to_der, rsa.to_der
+    assert_equal pem, rsa.export
 
-    assert_equal asn1.to_der, rsa1024.to_der
-    assert_equal pem, rsa1024.export
+    # Unknown PEM prepended
+    cert = issue_cert(OpenSSL::X509::Name.new([["CN", "nobody"]]), rsa, 1, [], nil, nil)
+    str = cert.to_text + cert.to_pem + rsa.to_pem
+    key = OpenSSL::PKey::RSA.new(str)
+    assert_same_rsa rsa, key
   end
 
   def test_RSAPrivateKey_encrypted
+    omit_on_fips
+
     rsa1024 = Fixtures.pkey("rsa1024")
     # key = abcdef
     pem = <<~EOF
@@ -232,13 +418,15 @@
 
   def test_RSAPublicKey
     rsa1024 = Fixtures.pkey("rsa1024")
+    rsa1024pub = OpenSSL::PKey::RSA.new(rsa1024.public_to_der)
+
     asn1 = OpenSSL::ASN1::Sequence([
       OpenSSL::ASN1::Integer(rsa1024.n),
       OpenSSL::ASN1::Integer(rsa1024.e)
     ])
     key = OpenSSL::PKey::RSA.new(asn1.to_der)
     assert_not_predicate key, :private?
-    assert_same_rsa dup_public(rsa1024), key
+    assert_same_rsa rsa1024pub, key
 
     pem = <<~EOF
     -----BEGIN RSA PUBLIC KEY-----
@@ -248,11 +436,13 @@
     -----END RSA PUBLIC KEY-----
     EOF
     key = OpenSSL::PKey::RSA.new(pem)
-    assert_same_rsa dup_public(rsa1024), key
+    assert_same_rsa rsa1024pub, key
   end
 
   def test_PUBKEY
     rsa1024 = Fixtures.pkey("rsa1024")
+    rsa1024pub = OpenSSL::PKey::RSA.new(rsa1024.public_to_der)
+
     asn1 = OpenSSL::ASN1::Sequence([
       OpenSSL::ASN1::Sequence([
         OpenSSL::ASN1::ObjectId("rsaEncryption"),
@@ -267,7 +457,7 @@
     ])
     key = OpenSSL::PKey::RSA.new(asn1.to_der)
     assert_not_predicate key, :private?
-    assert_same_rsa dup_public(rsa1024), key
+    assert_same_rsa rsa1024pub, key
 
     pem = <<~EOF
     -----BEGIN PUBLIC KEY-----
@@ -278,13 +468,20 @@
     -----END PUBLIC KEY-----
     EOF
     key = OpenSSL::PKey::RSA.new(pem)
-    assert_same_rsa dup_public(rsa1024), key
+    assert_same_rsa rsa1024pub, key
+
+    assert_equal asn1.to_der, key.to_der
+    assert_equal pem, key.export
 
-    assert_equal asn1.to_der, dup_public(rsa1024).to_der
-    assert_equal pem, dup_public(rsa1024).export
+    assert_equal asn1.to_der, rsa1024.public_to_der
+    assert_equal asn1.to_der, key.public_to_der
+    assert_equal pem, rsa1024.public_to_pem
+    assert_equal pem, key.public_to_pem
   end
 
   def test_pem_passwd
+    omit_on_fips
+
     key = Fixtures.pkey("rsa1024")
     pem3c = key.to_pem("aes-128-cbc", "key")
     assert_match (/ENCRYPTED/), pem3c
@@ -295,12 +492,109 @@
     }
   end
 
+  def test_private_encoding
+    rsa1024 = Fixtures.pkey("rsa1024")
+    asn1 = OpenSSL::ASN1::Sequence([
+      OpenSSL::ASN1::Integer(0),
+      OpenSSL::ASN1::Sequence([
+        OpenSSL::ASN1::ObjectId("rsaEncryption"),
+        OpenSSL::ASN1::Null(nil)
+      ]),
+      OpenSSL::ASN1::OctetString(rsa1024.to_der)
+    ])
+    assert_equal asn1.to_der, rsa1024.private_to_der
+    assert_same_rsa rsa1024, OpenSSL::PKey.read(asn1.to_der)
+
+    pem = <<~EOF
+    -----BEGIN PRIVATE KEY-----
+    MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMvCxLDUQKc+1P4+
+    Q6AeFwYDvWfALb+cvzlUEadGoPE6qNWHsLFoo8RFgeyTgE8KQTduu1OE9Zz2SMcR
+    BDu5/1jWtsLPSVrI2ofLLBARUsWanVyki39DeB4u/xkP2mKGjAokPIwOI3oCthSZ
+    lzO9bj3voxTf6XngTqUX8l8URTmHAgMBAAECgYEApKX8xBqvJ7XI7Kypfo/x8MVC
+    3rxW+1eQ2aVKIo4a7PKGjQz5RVIVyzqTUvSZoMTbkAxlSIbO5YfJpTnl3tFcOB6y
+    QMxqQPW/pl6Ni3EmRJdsRM5MsPBRZOfrXxOCdvXu1TWOS1S1TrvEr/TyL9eh2WCd
+    CGzpWgdO4KHce7vs7pECQQDv6DGoG5lHnvbvj9qSJb9K5ebRJc8S+LI7Uy5JHC0j
+    zsHTYPSqBXwPVQdGbgCEycnwwKzXzT2QxAQmJBQKun2ZAkEA2W3aeAE7Xi6zo2eG
+    4Cx4UNMHMIdfBRS7VgoekwybGmcapqV0aBew5kHeWAmxP1WUZ/dgZh2QtM1VuiBA
+    qUqkHwJBAOJLCRvi/JB8N7z82lTk2i3R8gjyOwNQJv6ilZRMyZ9vFZFHcUE27zCf
+    Kb+bX03h8WPwupjMdfgpjShU+7qq8nECQQDBrmyc16QVyo40sgTgblyiysitvviy
+    ovwZsZv4q5MCmvOPnPUrwGbRRb2VONUOMOKpFiBl9lIv7HU//nj7FMVLAkBjUXED
+    83dA8JcKM+HlioXEAxCzZVVhN+D63QwRwkN08xAPklfqDkcqccWDaZm2hdCtaYlK
+    funwYkrzI1OikQSs
+    -----END PRIVATE KEY-----
+    EOF
+    assert_equal pem, rsa1024.private_to_pem
+    assert_same_rsa rsa1024, OpenSSL::PKey.read(pem)
+  end
+
+  def test_private_encoding_encrypted
+    rsa = Fixtures.pkey("rsa2048")
+    encoded = rsa.private_to_der("aes-128-cbc", "abcdef")
+    asn1 = OpenSSL::ASN1.decode(encoded) # PKCS #8 EncryptedPrivateKeyInfo
+    assert_kind_of OpenSSL::ASN1::Sequence, asn1
+    assert_equal 2, asn1.value.size
+    assert_not_equal rsa.private_to_der, encoded
+    assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdef")
+    assert_same_rsa rsa, OpenSSL::PKey.read(encoded) { "abcdef" }
+    assert_raise(OpenSSL::PKey::PKeyError) { OpenSSL::PKey.read(encoded, "abcxyz") }
+
+    encoded = rsa.private_to_pem("aes-128-cbc", "abcdef")
+    assert_match (/BEGIN ENCRYPTED PRIVATE KEY/), encoded.lines[0]
+    assert_same_rsa rsa, OpenSSL::PKey.read(encoded, "abcdef")
+
+    # certtool --load-privkey=test/openssl/fixtures/pkey/rsa2048.pem --to-p8 --password=abcdef
+    pem = <<~EOF
+    -----BEGIN ENCRYPTED PRIVATE KEY-----
+    MIIFOTBjBgkqhkiG9w0BBQ0wVjA1BgkqhkiG9w0BBQwwKAQSsTIsinrhNMr4owUz
+    cwYGgB0lAgMJJ8ACARAwCgYIKoZIhvcNAgkwHQYJYIZIAWUDBAECBBDtDYqmQOLV
+    Nh0T0DslWgovBIIE0ESbJey2Pjf9brTp9/41CPnI9Ev78CGSv8Ihyuynu6G7oj7N
+    G7jUB1pVMQ7ivebF5DmM0qHAix6fDqJetB3WCnRQpMLyIdq5VrnKwFNhwGYduWA5
+    IyaAc4DHj02e6YLyBTIKpu79OSFxLrnLCRaTbvZIUQaGhyd6pB7iAhqz5YBC0rpa
+    iMK5TRlNGPYG9n2eGFOhvUsbJ4T8VDzjpVWw0VNRaukXtg4xiR6o1f0qSXqAb5d9
+    REq5DfaQfoOKTV9j7KJHDRrBQG81vkU4K+xILrCBfbcYb82aCoinwSep9LC30HaH
+    LZ0hYQOuD/k/UbgjToS2wyMnkz75MN5ZNhDMZl/mACQdsMMtIxG37Mpo1Ca33uZi
+    71TCOEKIblZS11L1YhIni9Af8pOuHJBWwezP2zN2nPwV6OhgL7Jlax7ICQOPC6L/
+    yRGgC5eT4lDDAuTy0IdUhr0r5XrFzZR0/5Vgsq9cGfk9QkXOoETRhQVkEfUDdCs6
+    6CK+SwUR9qh5824ShODFG0SQpsqBPIVtkGrypBSUJtICmGMOAsclB7RDN7/opJwp
+    qv/iRJ5dhWrhRgQ/DfYifvO5On7RgC2hm48gF3Pt6XCA857ryyYxLYeMY42tAUqp
+    Hmc9HL7bMYF/jl3cJ32+gLvI3PBVvrvyeAhRo6z7MFVe9I04OywV6BHUx1Us6ybF
+    qkYnSpcJZdu7HyvzXm7XWLFmt7K5BlAgnFsa/8+cI1BGPgQRc1j0SWepXsSwFZX6
+    JkNQ0dewq4uRJXbGyQgfh5I5ETpqDhSt2JfBwAoze6cx3DPC711PUamxyWMiejs+
+    mYdia4p62NxaUvyXWmCGIEOzajRwywEhf9OLAmfqTN41TIrEL4BUxqtzDyw8Nl8T
+    KB7nJEC366jFASfumNQkXXyH5yBIF+XwwSKUOObRZVn2rUzFToo51hHu9efxHoXa
+    jZlpfglWijkmOuwoIGlGHOq8gUn76oq9WbV+YO+fWm/mf4S3ECzmYzxb6a1uCTy/
+    Itkm2qOe3yTM1t+oCqZ0/MeTZ84ALQaWv5reQfjronPZ1jeNtxrYz28tJ4KwBn0U
+    bJReXbOLsHAymipncxlmaevbx4GPTduu/lbpxefoN95w+SpEdyTmVWrfaCTgAbad
+    EzcRl60my3xOMQ7CaUbRgGiwohqHDvuXzeqoZ96u6CwfAoEfy4jETmKLRH6uTtj7
+    4jdTyoqyizjpvaM8LPspBS+oqFwLxBjpseQuScrZO1BjPxrImLy2/VRqwJ+CF4FB
+    iijEgDgDc1EMIGe5YmOAV+i22n9RqX+2IvkYp7CWXrB9/lmirLFukd7hT8DLPUGq
+    AvSZwTPbDPoZKG3DAebC3DbiC7A3x0KZp24doNRLamZ/MyKHo2Rzl0UhkzDU0ly2
+    eAnyNYsOAQck+C6L+ieD95Gksm9YJWurwttm5JragbIJwMCrsBQd4bXDkKdRhxS2
+    JpS0dT/aoDmgTzoG07x4cZk0rjBkfX1ta0j0b1lz7/PZXl9AbRvFdq5sJpmv4Ryz
+    S+OERqo4IEfJJq2WJ92WR+HLGV3Gvsdb7znZTEF1tp4pWOLAt83Pry282UJxO7Pe
+    ySf/868TEmXams06GYvH+7cMiIT2m9Dc+EFgNaPmm0uMmJ+ZjqHKSOLzrL7C
+    -----END ENCRYPTED PRIVATE KEY-----
+    EOF
+    assert_same_rsa rsa, OpenSSL::PKey.read(pem, "abcdef")
+  end
+
   def test_dup
     key = Fixtures.pkey("rsa1024")
     key2 = key.dup
     assert_equal key.params, key2.params
-    key2.set_key(key2.n, 3, key2.d)
-    assert_not_equal key.params, key2.params
+
+    # PKey is immutable in OpenSSL >= 3.0
+    if !openssl?(3, 0, 0)
+      key2.set_key(key2.n, 3, key2.d)
+      assert_not_equal key.params, key2.params
+    end
+  end
+
+  def test_marshal
+    key = Fixtures.pkey("rsa2048")
+    deserialized = Marshal.load(Marshal.dump(key))
+
+    assert_equal key.to_der, deserialized.to_der
   end
 
   private
diff -ruN ruby-2.5.9.orig/test/openssl/test_provider.rb ruby-2.5.9/test/openssl/test_provider.rb
--- ruby-2.5.9.orig/test/openssl/test_provider.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/test_provider.rb	2025-01-29 19:08:29.936064255 +0100
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+require_relative 'utils'
+if defined?(OpenSSL) && defined?(OpenSSL::Provider)
+
+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
+    # We expect the following providers are loaded in the cases:
+    # * Non-FIPS: default
+    # * FIPS: fips, base
+    # Use the null provider to test the added provider.
+    # See provider(7) - OPENSSL PROVIDERS to see the list of providers, and
+    # OSSL_PROVIDER-null(7) to check the details of the null provider.
+    with_openssl <<-'end;'
+      num = OpenSSL::Provider.provider_names.size
+
+      added_provider = OpenSSL::Provider.load("null")
+      assert_equal(num + 1, OpenSSL::Provider.provider_names.size)
+      assert_includes(OpenSSL::Provider.provider_names, "null")
+
+      assert_equal(true, added_provider.unload)
+      assert_equal(num, OpenSSL::Provider.provider_names.size)
+      assert_not_includes(OpenSSL::Provider.provider_names, "null")
+    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
+    # The legacy provider is not supported on FIPS.
+    omit_on_fips
+
+    with_openssl(<<-'end;')
+      begin
+        OpenSSL::Provider.load("legacy")
+      rescue OpenSSL::Provider::ProviderError
+        omit "Only for OpenSSL with legacy provider"
+      end
+
+      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 -ruN ruby-2.5.9.orig/test/openssl/test_random.rb ruby-2.5.9/test/openssl/test_random.rb
--- ruby-2.5.9.orig/test/openssl/test_random.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_random.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
diff -ruN ruby-2.5.9.orig/test/openssl/test_ssl.rb ruby-2.5.9/test/openssl/test_ssl.rb
--- ruby-2.5.9.orig/test/openssl/test_ssl.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_ssl.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,14 +1,30 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
-if defined?(OpenSSL)
+if defined?(OpenSSL::SSL)
 
 class OpenSSL::TestSSL < OpenSSL::SSLTestCase
+  def test_bad_socket
+    bad_socket = Struct.new(:sync).new
+    assert_raise TypeError do
+      socket = OpenSSL::SSL::SSLSocket.new bad_socket
+      # if the socket is not a T_FILE, `connect` will segv because it tries
+      # to get the underlying file descriptor but the API it calls assumes
+      # the object type is T_FILE
+      socket.connect
+    end
+  end
+
+  def test_ctx_setup
+    ctx = OpenSSL::SSL::SSLContext.new
+    assert_equal true, ctx.setup
+    assert_predicate ctx, :frozen?
+    assert_equal nil, ctx.setup
+  end
+
   def test_ctx_options
     ctx = OpenSSL::SSL::SSLContext.new
 
-    assert (OpenSSL::SSL::OP_ALL & ctx.options) == OpenSSL::SSL::OP_ALL,
-           "OP_ALL is set by default"
     ctx.options = 4
     assert_equal 4, ctx.options & 4
     if ctx.options != 4
@@ -22,6 +38,31 @@
     assert_equal nil, ctx.setup
   end
 
+  def test_ctx_options_config
+    omit "LibreSSL does not support OPENSSL_CONF" if libressl?
+    omit "OpenSSL < 1.1.1 does not support system_default" if openssl? && !openssl?(1, 1, 1)
+
+    Tempfile.create("openssl.cnf") { |f|
+      f.puts(<<~EOF)
+        openssl_conf = default_conf
+        [default_conf]
+        ssl_conf = ssl_sect
+        [ssl_sect]
+        system_default = ssl_default_sect
+        [ssl_default_sect]
+        Options = -SessionTicket
+      EOF
+      f.close
+
+      assert_separately([{ "OPENSSL_CONF" => f.path }, "-ropenssl"], <<~"end;")
+        ctx = OpenSSL::SSL::SSLContext.new
+        assert_equal OpenSSL::SSL::OP_NO_TICKET, ctx.options & OpenSSL::SSL::OP_NO_TICKET
+        ctx.set_params
+        assert_equal OpenSSL::SSL::OP_NO_TICKET, ctx.options & OpenSSL::SSL::OP_NO_TICKET
+      end;
+    }
+  end
+
   def test_ssl_with_server_cert
     ctx_proc = -> ctx {
       ctx.cert = @svr_cert
@@ -56,6 +97,80 @@
     }
   end
 
+  def test_socket_open
+    start_server { |port|
+      begin
+        ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port)
+        ssl.sync_close = true
+        ssl.connect
+
+        ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+      ensure
+        ssl&.close
+      end
+    }
+  end
+
+  def test_socket_open_with_context
+    start_server { |port|
+      begin
+        ctx = OpenSSL::SSL::SSLContext.new
+        ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port, context: ctx)
+        ssl.sync_close = true
+        ssl.connect
+
+        assert_equal ssl.context, ctx
+        ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+      ensure
+        ssl&.close
+      end
+    }
+  end
+
+  def test_socket_open_with_local_address_port_context
+    start_server { |port|
+      begin
+        # Guess a free port number
+        random_port = rand(49152..65535)
+        ctx = OpenSSL::SSL::SSLContext.new
+        ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port, "127.0.0.1", random_port, context: ctx)
+        ssl.sync_close = true
+        ssl.connect
+
+        assert_equal ctx, ssl.context
+        assert_equal random_port, ssl.io.local_address.ip_port
+        ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+      rescue Errno::EADDRINUSE, Errno::EACCES
+      ensure
+        ssl&.close
+      end
+    }
+  end
+
+  def test_socket_close_write
+    server_proc = proc do |ctx, ssl|
+      message = ssl.read
+      ssl.write(message)
+      ssl.close_write
+    ensure
+      ssl.close
+    end
+
+    start_server(server_proc: server_proc) do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ssl = OpenSSL::SSL::SSLSocket.open("127.0.0.1", port, context: ctx)
+      ssl.sync_close = true
+      ssl.connect
+
+      message = "abc"*1024
+      ssl.write message
+      ssl.close_write
+      assert_equal message, ssl.read
+    ensure
+      ssl&.close
+    end
+  end
+
   def test_add_certificate
     ctx_proc = -> ctx {
       # Unset values set by start_server
@@ -74,24 +189,13 @@
   end
 
   def test_add_certificate_multiple_certs
-    pend "EC is not supported" unless defined?(OpenSSL::PKey::EC)
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-
-    # SSL_CTX_set0_chain() is needed for setting multiple certificate chains
-    add0_chain_supported = openssl?(1, 0, 2)
-
-    if add0_chain_supported
-      ca2_key = Fixtures.pkey("rsa1024")
-      ca2_exts = [
-        ["basicConstraints", "CA:TRUE", true],
-        ["keyUsage", "cRLSign, keyCertSign", true],
-      ]
-      ca2_dn = OpenSSL::X509::Name.parse_rfc2253("CN=CA2")
-      ca2_cert = issue_cert(ca2_dn, ca2_key, 123, ca2_exts, nil, nil)
-    else
-      # Use the same CA as @svr_cert
-      ca2_key = @ca_key; ca2_cert = @ca_cert
-    end
+    ca2_key = Fixtures.pkey("rsa-3")
+    ca2_exts = [
+      ["basicConstraints", "CA:TRUE", true],
+      ["keyUsage", "cRLSign, keyCertSign", true],
+    ]
+    ca2_dn = OpenSSL::X509::Name.parse_rfc2253("CN=CA2")
+    ca2_cert = issue_cert(ca2_dn, ca2_key, 123, ca2_exts, nil, nil)
 
     ecdsa_key = Fixtures.pkey("p256")
     exts = [
@@ -100,23 +204,11 @@
     ecdsa_dn = OpenSSL::X509::Name.parse_rfc2253("CN=localhost2")
     ecdsa_cert = issue_cert(ecdsa_dn, ecdsa_key, 456, exts, ca2_cert, ca2_key)
 
-    if !add0_chain_supported
-      # Testing the warning emitted when 'extra' chain is replaced
-      tctx = OpenSSL::SSL::SSLContext.new
-      tctx.add_certificate(@svr_cert, @svr_key, [@ca_cert])
-      assert_warning(/set0_chain/) {
-        tctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert])
-      }
-    end
-
     ctx_proc = -> ctx {
       # Unset values set by start_server
       ctx.cert = ctx.key = ctx.extra_chain_cert = nil
-      ctx.ecdh_curves = "P-256" unless openssl?(1, 0, 2)
       ctx.add_certificate(@svr_cert, @svr_key, [@ca_cert]) # RSA
-      EnvUtil.suppress_warning do # !add0_chain_supported
-        ctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert])
-      end
+      ctx.add_certificate(ecdsa_cert, ecdsa_key, [ca2_cert])
     }
     start_server(ctx_proc: ctx_proc) do |port|
       ctx = OpenSSL::SSL::SSLContext.new
@@ -142,12 +234,12 @@
   def test_sysread_and_syswrite
     start_server { |port|
       server_connect(port) { |ssl|
-        str = "x" * 100 + "\n"
+        str = +("x" * 100 + "\n")
         ssl.syswrite(str)
         newstr = ssl.sysread(str.bytesize)
         assert_equal(str, newstr)
 
-        buf = ""
+        buf = String.new
         ssl.syswrite(str)
         assert_same buf, ssl.sysread(str.size, buf)
         assert_equal(str, buf)
@@ -155,8 +247,52 @@
     }
   end
 
-  def test_sync_close
+  def test_read_with_timeout
+    omit "does not support timeout" unless IO.method_defined?(:timeout)
+
+    start_server do |port|
+      server_connect(port) do |ssl|
+        str = +("x" * 100 + "\n")
+        ssl.syswrite(str)
+        assert_equal(str, ssl.sysread(str.bytesize))
+
+        ssl.timeout = 1
+        assert_raise(IO::TimeoutError) {ssl.read(1)}
+
+        ssl.syswrite(str)
+        assert_equal(str, ssl.sysread(str.bytesize))
+      end
+    end
+  end
+
+  def test_getbyte
     start_server { |port|
+      server_connect(port) { |ssl|
+        str = +("x" * 100 + "\n")
+        ssl.syswrite(str)
+        newstr = str.bytesize.times.map { |i|
+          ssl.getbyte
+        }.pack("C*")
+        assert_equal(str, newstr)
+      }
+    }
+  end
+
+  def test_readbyte
+    start_server { |port|
+      server_connect(port) { |ssl|
+        str = +("x" * 100 + "\n")
+        ssl.syswrite(str)
+        newstr = str.bytesize.times.map { |i|
+          ssl.readbyte
+        }.pack("C*")
+        assert_equal(str, newstr)
+      }
+    }
+  end
+
+  def test_sync_close
+    start_server do |port|
       begin
         sock = TCPSocket.new("127.0.0.1", port)
         ssl = OpenSSL::SSL::SSLSocket.new(sock)
@@ -179,7 +315,7 @@
       ensure
         sock&.close
       end
-    }
+    end
   end
 
   def test_copy_stream
@@ -196,7 +332,56 @@
     end
   end
 
-  def test_client_auth_failure
+  def test_verify_mode_default
+    ctx = OpenSSL::SSL::SSLContext.new
+    assert_equal OpenSSL::SSL::VERIFY_NONE, ctx.verify_mode
+  end
+
+  def test_verify_mode_server_cert
+    start_server(ignore_listener_error: true) { |port|
+      populated_store = OpenSSL::X509::Store.new
+      populated_store.add_cert(@ca_cert)
+      empty_store = OpenSSL::X509::Store.new
+
+      # Valid certificate, SSL_VERIFY_PEER
+      assert_nothing_raised {
+        ctx = OpenSSL::SSL::SSLContext.new
+        ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+        ctx.cert_store = populated_store
+        server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
+      }
+
+      # Invalid certificate, SSL_VERIFY_NONE
+      assert_nothing_raised {
+        ctx = OpenSSL::SSL::SSLContext.new
+        ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
+        ctx.cert_store = empty_store
+        server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
+      }
+
+      # Invalid certificate, SSL_VERIFY_PEER
+      assert_handshake_error {
+        ctx = OpenSSL::SSL::SSLContext.new
+        ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+        ctx.cert_store = empty_store
+        server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
+      }
+    }
+  end
+
+  def test_verify_mode_client_cert_required
+    # Optional, client certificate not supplied
+    vflag = OpenSSL::SSL::VERIFY_PEER
+    accept_proc = -> ssl {
+      assert_equal nil, ssl.peer_cert
+    }
+    start_server(verify_mode: vflag, accept_proc: accept_proc) { |port|
+      assert_nothing_raised {
+        server_connect(port) { |ssl| ssl.puts("abc"); ssl.gets }
+      }
+    }
+
+    # Required, client certificate not supplied
     vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
     start_server(verify_mode: vflag, ignore_listener_error: true) { |port|
       assert_handshake_error {
@@ -207,7 +392,10 @@
 
   def test_client_auth_success
     vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
-    start_server(verify_mode: vflag) { |port|
+    start_server(verify_mode: vflag,
+      ctx_proc: proc { |ctx|
+        ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
+    }) { |port|
       ctx = OpenSSL::SSL::SSLContext.new
       ctx.key = @cli_key
       ctx.cert = @cli_cert
@@ -232,20 +420,16 @@
     }
   end
 
-  def test_client_auth_public_key
+  def test_client_cert_cb_ignore_error
     vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
     start_server(verify_mode: vflag, ignore_listener_error: true) do |port|
-      assert_raise(ArgumentError) {
-        ctx = OpenSSL::SSL::SSLContext.new
-        ctx.key = @cli_key.public_key
-        ctx.cert = @cli_cert
-        server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
-      }
-
       ctx = OpenSSL::SSL::SSLContext.new
-      ctx.client_cert_cb = Proc.new{ |ssl|
-        [@cli_cert, @cli_key.public_key]
+      ctx.client_cert_cb = -> ssl {
+        raise "exception in client_cert_cb must be suppressed"
       }
+      # 1. Exception in client_cert_cb is suppressed
+      # 2. No client certificate will be sent to the server
+      # 3. SSL_VERIFY_FAIL_IF_NO_PEER_CERT causes the handshake to fail
       assert_handshake_error {
         server_connect(port, ctx) { |ssl| ssl.puts("abc"); ssl.gets }
       }
@@ -253,6 +437,8 @@
   end
 
   def test_client_ca
+    pend "LibreSSL 3.2 has broken client CA support" if libressl?(3, 2, 0)
+
     ctx_proc = Proc.new do |ctx|
       ctx.client_ca = [@ca_cert]
     end
@@ -272,59 +458,20 @@
     }
   end
 
-  def test_read_nonblock_without_session
-    EnvUtil.suppress_warning do
-      start_server(start_immediately: false) { |port|
-        sock = TCPSocket.new("127.0.0.1", port)
-        ssl = OpenSSL::SSL::SSLSocket.new(sock)
-        ssl.sync_close = true
-
-        assert_equal :wait_readable, ssl.read_nonblock(100, exception: false)
-        ssl.write("abc\n")
-        IO.select [ssl]
-        assert_equal('a', ssl.read_nonblock(1))
-        assert_equal("bc\n", ssl.read_nonblock(100))
-        assert_equal :wait_readable, ssl.read_nonblock(100, exception: false)
-        ssl.close
-      }
-    end
-  end
-
-  def test_starttls
-    server_proc = -> (ctx, ssl) {
-      while line = ssl.gets
-        if line =~ /^STARTTLS$/
-          ssl.write("x")
-          ssl.flush
-          ssl.accept
-          break
-        end
-        ssl.write(line)
-      end
-      readwrite_loop(ctx, ssl)
-    }
-
-    EnvUtil.suppress_warning do # read/write on not started session
-      start_server(start_immediately: false,
-                   server_proc: server_proc) { |port|
-        begin
-          sock = TCPSocket.new("127.0.0.1", port)
-          ssl = OpenSSL::SSL::SSLSocket.new(sock)
-
-          ssl.puts "plaintext"
-          assert_equal "plaintext\n", ssl.gets
+  def test_unstarted_session
+    start_server do |port|
+      sock = TCPSocket.new("127.0.0.1", port)
+      ssl = OpenSSL::SSL::SSLSocket.new(sock)
 
-          ssl.puts("STARTTLS")
-          ssl.read(1)
-          ssl.connect
+      assert_raise(OpenSSL::SSL::SSLError) { ssl.syswrite("data") }
+      assert_raise(OpenSSL::SSL::SSLError) { ssl.sysread(1) }
 
-          ssl.puts "over-tls"
-          assert_equal "over-tls\n", ssl.gets
-        ensure
-          ssl&.close
-          sock&.close
-        end
-      }
+      ssl.connect
+      ssl.puts "abc"
+      assert_equal "abc\n", ssl.gets
+    ensure
+      ssl&.close
+      sock&.close
     end
   end
 
@@ -419,6 +566,68 @@
     }
   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
+    client_finished = nil
+    client_peer_finished = nil
+
+    start_server(accept_proc: proc { |server|
+      server_finished = server.finished_message
+      server_peer_finished = server.peer_finished_message
+    }, ctx_proc: proc { |ctx|
+      ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
+    }) { |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
+      ctx.max_version = :TLS1_2 if libressl?(3, 2, 0) && !libressl?(3, 3, 0)
+      server_connect(port, ctx) { |ssl|
+        ssl.puts "abc"; ssl.gets
+
+        client_finished = ssl.finished_message
+        client_peer_finished = ssl.peer_finished_message
+      }
+    }
+    assert_not_nil(server_finished)
+    assert_not_nil(client_finished)
+    assert_equal(server_finished, client_peer_finished)
+    assert_equal(server_peer_finished, client_finished)
+  end
+
   def test_sslctx_set_params
     ctx = OpenSSL::SSL::SSLContext.new
     ctx.set_params
@@ -433,11 +642,10 @@
   end
 
   def test_post_connect_check_with_anon_ciphers
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-
     ctx_proc = -> ctx {
       ctx.ssl_version = :TLSv1_2
       ctx.ciphers = "aNULL"
+      ctx.tmp_dh = Fixtures.pkey("dh-1")
       ctx.security_level = 0
     }
 
@@ -476,8 +684,7 @@
 
     exts = [
       ["keyUsage","keyEncipherment,digitalSignature",true],
-      ["subjectAltName","DNS:localhost.localdomain",false],
-      ["subjectAltName","IP:127.0.0.1",false],
+      ["subjectAltName","DNS:localhost.localdomain,IP:127.0.0.1",false],
     ]
     @svr_cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key)
     start_server { |port|
@@ -526,8 +733,12 @@
       assert_equal(true,  OpenSSL::SSL.verify_certificate_identity(cert, "www.example.com\0.evil.com"))
       assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.255'))
       assert_equal(true,  OpenSSL::SSL.verify_certificate_identity(cert, '192.168.7.1'))
-      assert_equal(false, OpenSSL::SSL.verify_certificate_identity(cert, '13::17'))
+      assert_equal(true,  OpenSSL::SSL.verify_certificate_identity(cert, '13::17'))
+      assert_equal(false,  OpenSSL::SSL.verify_certificate_identity(cert, '13::18'))
       assert_equal(true,  OpenSSL::SSL.verify_certificate_identity(cert, '13:0:0:0:0:0:0:17'))
+      assert_equal(false,  OpenSSL::SSL.verify_certificate_identity(cert, '44:0:0:0:0:0:0:17'))
+      assert_equal(true,  OpenSSL::SSL.verify_certificate_identity(cert, '0013:0000:0000:0000:0000:0000:0000:0017'))
+      assert_equal(false,  OpenSSL::SSL.verify_certificate_identity(cert, '1313:0000:0000:0000:0000:0000:0000:0017'))
     end
   end
 
@@ -547,7 +758,7 @@
     assert_equal(true,  OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b"))
   end
 
-  # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27
+  # Comments in this test is excerpted from https://www.rfc-editor.org/rfc/rfc6125#page-27
   def test_post_connection_check_wildcard_san
     # case-insensitive ASCII comparison
     # RFC 6125, section 6.4.1
@@ -584,10 +795,16 @@
     #     buzz.example.net, respectively).  ...
     assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
       create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com'))
+
+    # LibreSSL 3.5.0+ doesn't support other wildcard certificates
+    # (it isn't required to, as RFC states MAY, not MUST)
+    return if libressl?(3, 5, 0)
+
     assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
       create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com'))
     assert_equal(true, OpenSSL::SSL.verify_certificate_identity(
       create_cert_with_san('DNS:b*z.example.com'), 'buzz.example.com'))
+
     # Section 6.4.3 of RFC6125 states that client should NOT match identifier
     # where wildcard is other than left-most label.
     #
@@ -706,9 +923,56 @@
     end
   end
 
+  def test_keylog_cb
+    pend "Keylog callback is not supported" if !openssl?(1, 1, 1) || libressl?
+
+    prefix = 'CLIENT_RANDOM'
+    context = OpenSSL::SSL::SSLContext.new
+    context.min_version = context.max_version = OpenSSL::SSL::TLS1_2_VERSION
+
+    cb_called = false
+    context.keylog_cb = proc do |_sock, line|
+      cb_called = true
+      assert_equal(prefix, line.split.first)
+    end
+
+    start_server do |port|
+      server_connect(port, context) do |ssl|
+        ssl.puts "abc"
+        assert_equal("abc\n", ssl.gets)
+        assert_equal(true, cb_called)
+      end
+    end
+
+    if tls13_supported?
+      prefixes = [
+        'SERVER_HANDSHAKE_TRAFFIC_SECRET',
+        'EXPORTER_SECRET',
+        'SERVER_TRAFFIC_SECRET_0',
+        'CLIENT_HANDSHAKE_TRAFFIC_SECRET',
+        'CLIENT_TRAFFIC_SECRET_0',
+      ]
+      context = OpenSSL::SSL::SSLContext.new
+      context.min_version = context.max_version = OpenSSL::SSL::TLS1_3_VERSION
+      cb_called = false
+      context.keylog_cb = proc do |_sock, line|
+        cb_called = true
+        assert_not_nil(prefixes.delete(line.split.first))
+      end
+
+      start_server do |port|
+        server_connect(port, context) do |ssl|
+          ssl.puts "abc"
+          assert_equal("abc\n", ssl.gets)
+          assert_equal(true, cb_called)
+        end
+        assert_equal(0, prefixes.size)
+      end
+    end
+  end
+
   def test_tlsext_hostname
     fooctx = OpenSSL::SSL::SSLContext.new
-    fooctx.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") }
     fooctx.cert = @cli_cert
     fooctx.key = @cli_key
 
@@ -760,7 +1024,6 @@
     ctx2 = OpenSSL::SSL::SSLContext.new
     ctx2.cert = @svr_cert
     ctx2.key = @svr_key
-    ctx2.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") }
     ctx2.servername_cb = lambda { |args| Object.new }
 
     sock1, sock2 = socketpair
@@ -787,19 +1050,47 @@
     sock2.close if sock2
   end
 
+  def test_accept_errors_include_peeraddr
+    context = OpenSSL::SSL::SSLContext.new
+    context.cert = @svr_cert
+    context.key = @svr_key
+
+    server = TCPServer.new("127.0.0.1", 0)
+    port = server.connect_address.ip_port
+
+    ssl_server = OpenSSL::SSL::SSLServer.new(server, context)
+
+    t = Thread.new do
+      assert_raise_with_message(OpenSSL::SSL::SSLError, /peeraddr=127\.0\.0\.1/) do
+        ssl_server.accept
+      end
+    end
+
+    sock = TCPSocket.new("127.0.0.1", port)
+    sock << "\x00" * 1024
+
+    assert t.join
+  ensure
+    sock&.close
+    server.close
+  end
+
   def test_verify_hostname_on_connect
     ctx_proc = proc { |ctx|
+      san = "DNS:a.example.com,DNS:*.b.example.com"
+      san += ",DNS:c*.example.com,DNS:d.*.example.com" unless libressl?(3, 2, 2)
       exts = [
         ["keyUsage", "keyEncipherment,digitalSignature", true],
-        ["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \
-                           "DNS:c*.example.com,DNS:d.*.example.com"],
+        ["subjectAltName", san],
       ]
+
       ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key)
       ctx.key = @svr_key
     }
 
     start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
       ctx = OpenSSL::SSL::SSLContext.new
+      assert_equal false, ctx.verify_hostname
       ctx.verify_hostname = true
       ctx.cert_store = OpenSSL::X509::Store.new
       ctx.cert_store.add_cert(@ca_cert)
@@ -814,6 +1105,7 @@
         ["cx.example.com", true],
         ["d.x.example.com", false],
       ].each do |name, expected_ok|
+        next if name.start_with?('cx') if libressl?(3, 2, 2)
         begin
           sock = TCPSocket.new("127.0.0.1", port)
           ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
@@ -832,18 +1124,58 @@
     end
   end
 
+  def test_verify_hostname_failure_error_code
+    ctx_proc = proc { |ctx|
+      exts = [
+        ["keyUsage", "keyEncipherment,digitalSignature", true],
+        ["subjectAltName", "DNS:a.example.com"],
+      ]
+      ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key)
+      ctx.key = @svr_key
+    }
+
+    start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+      verify_callback_ok = verify_callback_err = nil
+
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.verify_hostname = true
+      ctx.cert_store = OpenSSL::X509::Store.new
+      ctx.cert_store.add_cert(@ca_cert)
+      ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
+      ctx.verify_callback = -> (preverify_ok, store_ctx) {
+        verify_callback_ok = preverify_ok
+        verify_callback_err = store_ctx.error
+        preverify_ok
+      }
+
+      begin
+        sock = TCPSocket.new("127.0.0.1", port)
+        ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+        ssl.hostname = "b.example.com"
+        assert_handshake_error { ssl.connect }
+        assert_equal false, verify_callback_ok
+        assert_equal OpenSSL::X509::V_ERR_HOSTNAME_MISMATCH, verify_callback_err
+      ensure
+        sock&.close
+      end
+    end
+  end
+
   def test_connect_certificate_verify_failed_exception_message
     start_server(ignore_listener_error: true) { |port|
       ctx = OpenSSL::SSL::SSLContext.new
       ctx.set_params
-      assert_raise_with_message(OpenSSL::SSL::SSLError, /self signed/) {
+      # OpenSSL <= 1.1.0: "self signed certificate in certificate chain"
+      # OpenSSL >= 3.0.0: "self-signed certificate in certificate chain"
+      assert_raise_with_message(OpenSSL::SSL::SSLError, /self.signed/) {
         server_connect(port, ctx)
       }
     }
 
     ctx_proc = proc { |ctx|
+      now = Time.now
       ctx.cert = issue_cert(@svr, @svr_key, 30, [], @ca_cert, @ca_key,
-                            not_before: Time.now-100, not_after: Time.now-10)
+                            not_before: now - 7200, not_after: now - 3600)
     }
     start_server(ignore_listener_error: true, ctx_proc: ctx_proc) { |port|
       store = OpenSSL::X509::Store.new
@@ -1050,46 +1382,51 @@
   end
 
   def test_options_disable_versions
-    # Note: Use of these OP_* flags has been deprecated since OpenSSL 1.1.0.
+    # It's recommended to use SSLContext#{min,max}_version= instead in real
+    # applications. The purpose of this test case is to check that SSL options
+    # are properly propagated to OpenSSL library.
     supported = check_supported_protocol_versions
+    if !defined?(OpenSSL::SSL::TLS1_3_VERSION) ||
+        !supported.include?(OpenSSL::SSL::TLS1_2_VERSION) ||
+        !supported.include?(OpenSSL::SSL::TLS1_3_VERSION) ||
+        !defined?(OpenSSL::SSL::OP_NO_TLSv1_3) # LibreSSL < 3.4
+      pend "this test case requires both TLS 1.2 and TLS 1.3 to be supported " \
+        "and enabled by default"
+    end
 
-    if supported.include?(OpenSSL::SSL::TLS1_1_VERSION) &&
-        supported.include?(OpenSSL::SSL::TLS1_2_VERSION)
-      # Server disables ~ TLS 1.1
-      ctx_proc = proc { |ctx|
-        ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 |
-          OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1
-      }
-      start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
-        # Client only supports TLS 1.1
-        ctx1 = OpenSSL::SSL::SSLContext.new
-        ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_1_VERSION
-        assert_handshake_error { server_connect(port, ctx1) { } }
+    # Server disables TLS 1.2 and earlier
+    ctx_proc = proc { |ctx|
+      ctx.options |= OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3 |
+        OpenSSL::SSL::OP_NO_TLSv1 | OpenSSL::SSL::OP_NO_TLSv1_1 |
+        OpenSSL::SSL::OP_NO_TLSv1_2
+    }
+    start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
+      # Client only supports TLS 1.2
+      ctx1 = OpenSSL::SSL::SSLContext.new
+      ctx1.min_version = ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION
+      assert_handshake_error { server_connect(port, ctx1) { } }
 
-        # Client only supports TLS 1.2
-        ctx2 = OpenSSL::SSL::SSLContext.new
-        ctx2.min_version = ctx2.max_version = OpenSSL::SSL::TLS1_2_VERSION
-        assert_nothing_raised { server_connect(port, ctx2) { } }
-      }
+      # Client only supports TLS 1.3
+      ctx2 = OpenSSL::SSL::SSLContext.new
+      ctx2.min_version = ctx2.max_version = OpenSSL::SSL::TLS1_3_VERSION
+      assert_nothing_raised { server_connect(port, ctx2) { } }
+    }
 
-      # Server only supports TLS 1.1
-      ctx_proc = proc { |ctx|
-        ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_1_VERSION
-      }
-      start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
-        # Client disables TLS 1.1
-        ctx1 = OpenSSL::SSL::SSLContext.new
-        ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_1
-        assert_handshake_error { server_connect(port, ctx1) { } }
+    # Server only supports TLS 1.2
+    ctx_proc = proc { |ctx|
+      ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+    }
+    start_server(ctx_proc: ctx_proc, ignore_listener_error: true) { |port|
+      # Client doesn't support TLS 1.2
+      ctx1 = OpenSSL::SSL::SSLContext.new
+      ctx1.options |= OpenSSL::SSL::OP_NO_TLSv1_2
+      assert_handshake_error { server_connect(port, ctx1) { } }
 
-        # Client disables TLS 1.2
-        ctx2 = OpenSSL::SSL::SSLContext.new
-        ctx2.options |= OpenSSL::SSL::OP_NO_TLSv1_2
-        assert_nothing_raised { server_connect(port, ctx2) { } }
-      }
-    else
-      pend "TLS 1.1 and TLS 1.2 must be supported; skipping"
-    end
+      # Client supports TLS 1.2 by default
+      ctx2 = OpenSSL::SSL::SSLContext.new
+      ctx2.options |= OpenSSL::SSL::OP_NO_TLSv1_3
+      assert_nothing_raised { server_connect(port, ctx2) { } }
+    }
   end
 
   def test_ssl_methods_constant
@@ -1115,7 +1452,6 @@
     }
   end
 
-if openssl?(1, 0, 2) || libressl?
   def test_alpn_protocol_selection_ary
     advertised = ["http/1.1", "spdy/2"]
     ctx_proc = Proc.new { |ctx|
@@ -1140,7 +1476,6 @@
     ctx1 = OpenSSL::SSL::SSLContext.new
     ctx1.cert = @svr_cert
     ctx1.key = @svr_key
-    ctx1.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") }
     ctx1.alpn_select_cb = -> (protocols) { nil }
     ssl1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
 
@@ -1161,13 +1496,9 @@
     t&.kill
     t&.join
   end
-end
 
   def test_npn_protocol_selection_ary
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-    pend "NPN is not supported" unless \
-      OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
-    pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+    return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
 
     advertised = ["http/1.1", "spdy/2"]
     ctx_proc = proc { |ctx| ctx.npn_protocols = advertised }
@@ -1185,10 +1516,7 @@
   end
 
   def test_npn_protocol_selection_enum
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-    pend "NPN is not supported" unless \
-      OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
-    pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+    return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
 
     advertised = Object.new
     def advertised.each
@@ -1210,10 +1538,7 @@
   end
 
   def test_npn_protocol_selection_cancel
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-    pend "NPN is not supported" unless \
-      OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
-    pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+    return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
 
     ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
     start_server_version(:TLSv1_2, ctx_proc) { |port|
@@ -1224,10 +1549,7 @@
   end
 
   def test_npn_advertised_protocol_too_long
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-    pend "NPN is not supported" unless \
-      OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
-    pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+    return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
 
     ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["a" * 256] }
     start_server_version(:TLSv1_2, ctx_proc) { |port|
@@ -1238,10 +1560,7 @@
   end
 
   def test_npn_selected_protocol_too_long
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-    pend "NPN is not supported" unless \
-      OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
-    pend "LibreSSL 2.6 has broken NPN functions" if libressl?(2, 6, 1)
+    return unless OpenSSL::SSL::SSLContext.method_defined?(:npn_select_cb)
 
     ctx_proc = Proc.new { |ctx| ctx.npn_protocols = ["http/1.1"] }
     start_server_version(:TLSv1_2, ctx_proc) { |port|
@@ -1251,8 +1570,13 @@
     }
   end
 
+  def readwrite_loop_safe(ctx, ssl)
+    readwrite_loop(ctx, ssl)
+  rescue OpenSSL::SSL::SSLError
+  end
+
   def test_close_after_socket_close
-    start_server { |port|
+    start_server(server_proc: method(:readwrite_loop_safe)) { |port|
       sock = TCPSocket.new("127.0.0.1", port)
       ssl = OpenSSL::SSL::SSLSocket.new(sock)
       ssl.connect
@@ -1274,60 +1598,59 @@
   end
 
   def test_get_ephemeral_key
-    # OpenSSL >= 1.0.2
-    unless OpenSSL::SSL::SSLSocket.method_defined?(:tmp_key)
-      pend "SSL_get_server_tmp_key() is not supported"
-    end
-
-    if tls12_supported?
-      # kRSA
-      ctx_proc1 = proc { |ctx|
-        ctx.ssl_version = :TLSv1_2
-        ctx.ciphers = "kRSA"
-      }
-      start_server(ctx_proc: ctx_proc1) do |port|
-        ctx = OpenSSL::SSL::SSLContext.new
-        ctx.ssl_version = :TLSv1_2
-        ctx.ciphers = "kRSA"
+    # kRSA
+    ctx_proc1 = proc { |ctx|
+      ctx.ssl_version = :TLSv1_2
+      ctx.ciphers = "kRSA"
+    }
+    start_server(ctx_proc: ctx_proc1, ignore_listener_error: true) do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ssl_version = :TLSv1_2
+      ctx.ciphers = "kRSA"
+      begin
         server_connect(port, ctx) { |ssl| assert_nil ssl.tmp_key }
+      rescue OpenSSL::SSL::SSLError
+        # kRSA seems disabled
+        raise unless $!.message =~ /no cipher/
       end
     end
 
-    if defined?(OpenSSL::PKey::DH) && tls12_supported?
-      # DHE
-      # TODO: How to test this with TLS 1.3?
-      ctx_proc2 = proc { |ctx|
-        ctx.ssl_version = :TLSv1_2
-        ctx.ciphers = "EDH"
+    # DHE
+    # TODO: How to test this with TLS 1.3?
+    ctx_proc2 = proc { |ctx|
+      ctx.ssl_version = :TLSv1_2
+      ctx.ciphers = "EDH"
+      ctx.tmp_dh = Fixtures.pkey("dh-1")
+    }
+    start_server(ctx_proc: ctx_proc2) do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ssl_version = :TLSv1_2
+      ctx.ciphers = "EDH"
+      server_connect(port, ctx) { |ssl|
+        assert_instance_of OpenSSL::PKey::DH, ssl.tmp_key
       }
-      start_server(ctx_proc: ctx_proc2) do |port|
-        ctx = OpenSSL::SSL::SSLContext.new
-        ctx.ssl_version = :TLSv1_2
-        ctx.ciphers = "EDH"
-        server_connect(port, ctx) { |ssl|
-          assert_instance_of OpenSSL::PKey::DH, ssl.tmp_key
-        }
-      end
     end
 
-    if defined?(OpenSSL::PKey::EC)
-      # ECDHE
-      ctx_proc3 = proc { |ctx|
-        ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
-        ctx.ecdh_curves = "P-256"
+    # ECDHE
+    ctx_proc3 = proc { |ctx|
+      ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
+      ctx.ecdh_curves = "P-256"
+    }
+    start_server(ctx_proc: ctx_proc3) do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
+      server_connect(port, ctx) { |ssl|
+        assert_instance_of OpenSSL::PKey::EC, ssl.tmp_key
+        ssl.puts "abc"; assert_equal "abc\n", ssl.gets
       }
-      start_server(ctx_proc: ctx_proc3) do |port|
-        ctx = OpenSSL::SSL::SSLContext.new
-        ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
-        server_connect(port, ctx) { |ssl|
-          assert_instance_of OpenSSL::PKey::EC, ssl.tmp_key
-          ssl.puts "abc"; assert_equal "abc\n", ssl.gets
-        }
-      end
     end
   end
 
   def test_fallback_scsv
+    supported = check_supported_protocol_versions
+    return unless supported.include?(OpenSSL::SSL::TLS1_1_VERSION) &&
+      supported.include?(OpenSSL::SSL::TLS1_2_VERSION)
+
     pend "Fallback SCSV is not supported" unless \
       OpenSSL::SSL::SSLContext.method_defined?(:enable_fallback_scsv)
 
@@ -1357,7 +1680,12 @@
     # Server support better, so refuse the connection
     sock1, sock2 = socketpair
     begin
+      # This test is for the downgrade protection mechanism of TLS1.2.
+      # This is why ctx1 bounds max_version == TLS1.2.
+      # Otherwise, this test fails when using openssl 1.1.1 (or later) that supports TLS1.3.
+      # TODO: We may need another test for TLS1.3 because it seems to have a different mechanism.
       ctx1 = OpenSSL::SSL::SSLContext.new
+      ctx1.max_version = OpenSSL::SSL::TLS1_2_VERSION
       s1 = OpenSSL::SSL::SSLSocket.new(sock1, ctx1)
 
       ctx2 = OpenSSL::SSL::SSLContext.new
@@ -1379,33 +1707,125 @@
     end
   end
 
-  def test_dh_callback
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-
+  def test_tmp_dh_callback
+    dh = Fixtures.pkey("dh-1")
     called = false
     ctx_proc = -> ctx {
-      ctx.ssl_version = :TLSv1_2
+      ctx.max_version = :TLS1_2
       ctx.ciphers = "DH:!NULL"
       ctx.tmp_dh_callback = ->(*args) {
         called = true
-        Fixtures.pkey_dh("dh1024")
+        dh
       }
     }
     start_server(ctx_proc: ctx_proc) do |port|
       server_connect(port) { |ssl|
         assert called, "dh callback should be called"
-        if ssl.respond_to?(:tmp_key)
-          assert_equal Fixtures.pkey_dh("dh1024").to_der, ssl.tmp_key.to_der
-        end
+        assert_equal dh.to_der, ssl.tmp_key.to_der
       }
     end
   end
 
-  def test_connect_works_when_setting_dh_callback_to_nil
-    pend "TLS 1.2 is not supported" unless tls12_supported?
+  def test_ciphersuites_method_tls_connection
+    ssl_ctx = OpenSSL::SSL::SSLContext.new
+    if !tls13_supported? || !ssl_ctx.respond_to?(:ciphersuites=)
+      pend 'TLS 1.3 not supported'
+    end
+
+    csuite = ['TLS_AES_128_GCM_SHA256', 'TLSv1.3', 128, 128]
+    inputs = [csuite[0], [csuite[0]], [csuite]]
+
+    start_server do |port|
+      inputs.each do |input|
+        cli_ctx = OpenSSL::SSL::SSLContext.new
+        cli_ctx.min_version = cli_ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION
+        cli_ctx.ciphersuites = input
+
+        server_connect(port, cli_ctx) do |ssl|
+          assert_equal('TLSv1.3', ssl.ssl_version)
+          if libressl?(3, 4, 0) && !libressl?(3, 5, 0)
+            assert_equal("AEAD-AES128-GCM-SHA256", ssl.cipher[0])
+          else
+            assert_equal(csuite[0], ssl.cipher[0])
+          end
+          ssl.puts('abc'); assert_equal("abc\n", ssl.gets)
+        end
+      end
+    end
+  end
+
+  def test_ciphersuites_method_nil_argument
+    ssl_ctx = OpenSSL::SSL::SSLContext.new
+    pend 'ciphersuites= method is missing' unless ssl_ctx.respond_to?(:ciphersuites=)
+
+    assert_nothing_raised { ssl_ctx.ciphersuites = nil }
+  end
+
+  def test_ciphersuites_method_frozen_object
+    ssl_ctx = OpenSSL::SSL::SSLContext.new
+    pend 'ciphersuites= method is missing' unless ssl_ctx.respond_to?(:ciphersuites=)
+
+    ssl_ctx.freeze
+    assert_raise(FrozenError) { ssl_ctx.ciphersuites = 'TLS_AES_256_GCM_SHA384' }
+  end
+
+  def test_ciphersuites_method_bogus_csuite
+    ssl_ctx = OpenSSL::SSL::SSLContext.new
+    pend 'ciphersuites= method is missing' unless ssl_ctx.respond_to?(:ciphersuites=)
+
+    assert_raise_with_message(
+      OpenSSL::SSL::SSLError,
+      /SSL_CTX_set_ciphersuites: no cipher match/i
+    ) { ssl_ctx.ciphersuites = 'BOGUS' }
+  end
+
+  def test_ciphers_method_tls_connection
+    csuite = ['ECDHE-RSA-AES256-GCM-SHA384', 'TLSv1.2', 256, 256]
+    inputs = [csuite[0], [csuite[0]], [csuite]]
+
+    start_server do |port|
+      inputs.each do |input|
+        cli_ctx = OpenSSL::SSL::SSLContext.new
+        cli_ctx.min_version = cli_ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+        cli_ctx.ciphers = input
+
+        server_connect(port, cli_ctx) do |ssl|
+          assert_equal('TLSv1.2', ssl.ssl_version)
+          assert_equal(csuite[0], ssl.cipher[0])
+          ssl.puts('abc'); assert_equal("abc\n", ssl.gets)
+        end
+      end
+    end
+  end
+
+  def test_ciphers_method_nil_argument
+    ssl_ctx = OpenSSL::SSL::SSLContext.new
+    assert_nothing_raised { ssl_ctx.ciphers = nil }
+  end
+
+  def test_ciphers_method_frozen_object
+    ssl_ctx = OpenSSL::SSL::SSLContext.new
+
+    ssl_ctx.freeze
+    assert_raise(FrozenError) { ssl_ctx.ciphers = 'ECDHE-RSA-AES128-SHA' }
+  end
 
+  def test_ciphers_method_bogus_csuite
+    omit "Old #{OpenSSL::OPENSSL_LIBRARY_VERSION}" if
+      year = OpenSSL::OPENSSL_LIBRARY_VERSION[/\A OpenSSL\s+[01]\..*\s\K\d+\z/x] and
+      year.to_i <= 2018
+
+    ssl_ctx = OpenSSL::SSL::SSLContext.new
+
+    assert_raise_with_message(
+      OpenSSL::SSL::SSLError,
+      /SSL_CTX_set_cipher_list: no cipher match/i
+    ) { ssl_ctx.ciphers = 'BOGUS' }
+  end
+
+  def test_connect_works_when_setting_dh_callback_to_nil
     ctx_proc = -> ctx {
-      ctx.ssl_version = :TLSv1_2
+      ctx.max_version = :TLS1_2
       ctx.ciphers = "DH:!NULL" # use DH
       ctx.tmp_dh_callback = nil
     }
@@ -1418,71 +1838,71 @@
     end
   end
 
-  def test_tmp_ecdh_callback
-    pend "EC is disabled" unless defined?(OpenSSL::PKey::EC)
-    pend "tmp_ecdh_callback is not supported" unless \
-      OpenSSL::SSL::SSLContext.method_defined?(:tmp_ecdh_callback)
-    pend "LibreSSL 2.6 has broken SSL_CTX_set_tmp_ecdh_callback()" \
-      if libressl?(2, 6, 1)
-
-    EnvUtil.suppress_warning do # tmp_ecdh_callback is deprecated (2016-05)
-      called = false
-      ctx_proc = -> ctx {
-        ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
-        ctx.tmp_ecdh_callback = -> (*args) {
-          called = true
-          OpenSSL::PKey::EC.new "prime256v1"
-        }
+  def test_tmp_dh
+    dh = Fixtures.pkey("dh-1")
+    ctx_proc = -> ctx {
+      ctx.max_version = :TLS1_2
+      ctx.ciphers = "DH:!NULL" # use DH
+      ctx.tmp_dh = dh
+    }
+    start_server(ctx_proc: ctx_proc) do |port|
+      server_connect(port) { |ssl|
+        assert_equal dh.to_der, ssl.tmp_key.to_der
       }
-      start_server(ctx_proc: ctx_proc) do |port|
-        server_connect(port) { |s|
-          assert called, "tmp_ecdh_callback should be called"
-        }
-      end
     end
   end
 
-  def test_ecdh_curves
-    pend "EC is disabled" unless defined?(OpenSSL::PKey::EC)
-
+  def test_ecdh_curves_tls12
     ctx_proc = -> ctx {
       # Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3
-      ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
+      ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+      ctx.ciphers = "kEECDH"
       ctx.ecdh_curves = "P-384:P-521"
     }
     start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+      # Test 1: Client=P-256:P-384, Server=P-384:P-521 --> P-384
       ctx = OpenSSL::SSL::SSLContext.new
-      ctx.ecdh_curves = "P-256:P-384" # disable P-521 for OpenSSL >= 1.0.2
-
+      ctx.ecdh_curves = "P-256:P-384"
       server_connect(port, ctx) { |ssl|
         cs = ssl.cipher[0]
-        if /\ATLS/ =~ cs # Is TLS 1.3 is used?
-          assert_equal "secp384r1", ssl.tmp_key.group.curve_name
-        else
-          assert_match (/\AECDH/), cs
-          if ssl.respond_to?(:tmp_key)
-            assert_equal "secp384r1", ssl.tmp_key.group.curve_name
-          end
-        end
+        assert_match (/\AECDH/), cs
+        assert_equal "secp384r1", ssl.tmp_key.group.curve_name
         ssl.puts "abc"; assert_equal "abc\n", ssl.gets
       }
 
-      if openssl?(1, 0, 2) || libressl?(2, 5, 1)
-        ctx = OpenSSL::SSL::SSLContext.new
-        ctx.ecdh_curves = "P-256"
+      # Test 2: Client=P-256, Server=P-521:P-384 --> Fail
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ecdh_curves = "P-256"
+      assert_raise(OpenSSL::SSL::SSLError) {
+        server_connect(port, ctx) { }
+      }
 
-        assert_raise(OpenSSL::SSL::SSLError) {
-          server_connect(port, ctx) { }
-        }
+      # Test 3: Client=P-521:P-384, Server=P-521:P-384 --> P-521
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ecdh_curves = "P-521:P-384"
+      server_connect(port, ctx) { |ssl|
+        assert_equal "secp521r1", ssl.tmp_key.group.curve_name
+        ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+      }
+    end
+  end
 
-        ctx = OpenSSL::SSL::SSLContext.new
-        ctx.ecdh_curves = "P-521:P-384"
+  def test_ecdh_curves_tls13
+    pend "TLS 1.3 not supported" unless tls13_supported?
 
-        server_connect(port, ctx) { |ssl|
-          assert_equal "secp521r1", ssl.tmp_key.group.curve_name
-          ssl.puts "abc"; assert_equal "abc\n", ssl.gets
-        }
-      end
+    ctx_proc = -> ctx {
+      # Assume TLS 1.3 is enabled and chosen by default
+      ctx.ecdh_curves = "P-384:P-521"
+    }
+    start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.ecdh_curves = "P-256:P-384" # disable P-521
+
+      server_connect(port, ctx) { |ssl|
+        assert_equal "TLSv1.3", ssl.ssl_version
+        assert_equal "secp384r1", ssl.tmp_key.group.curve_name
+        ssl.puts "abc"; assert_equal "abc\n", ssl.gets
+      }
     end
   end
 
@@ -1540,6 +1960,33 @@
     }
   end
 
+  def test_fileno
+    ctx = OpenSSL::SSL::SSLContext.new
+    sock1, sock2 = socketpair
+
+    socket = OpenSSL::SSL::SSLSocket.new(sock1)
+    server = OpenSSL::SSL::SSLServer.new(sock2, ctx)
+
+    assert_equal socket.fileno, socket.to_io.fileno
+    assert_equal server.fileno, server.to_io.fileno
+  ensure
+    sock1.close
+    sock2.close
+  end
+
+  def test_export_keying_material
+    start_server do |port|
+      cli_ctx = OpenSSL::SSL::SSLContext.new
+      server_connect(port, cli_ctx) do |ssl|
+        assert_instance_of(String, ssl.export_keying_material('ttls keying material', 64))
+        assert_operator(64, :==, ssl.export_keying_material('ttls keying material', 64).b.length)
+        assert_operator(8, :==, ssl.export_keying_material('ttls keying material', 8).b.length)
+        assert_operator(5, :==, ssl.export_keying_material('test', 5, 'context').b.length)
+        ssl.puts "abc"; ssl.gets # workaround to make tests work on windows
+      end
+    end
+  end
+
   private
 
   def start_server_version(version, ctx_proc = nil,
@@ -1572,8 +2019,8 @@
 
   def assert_handshake_error
     # different OpenSSL versions react differently when facing a SSL/TLS version
-    # that has been marked as forbidden, therefore either of these may be raised
-    assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET) {
+    # that has been marked as forbidden, therefore any of these may be raised
+    assert_raise(OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::EPIPE) {
       yield
     }
   end
diff -ruN ruby-2.5.9.orig/test/openssl/test_ssl_session.rb ruby-2.5.9/test/openssl/test_ssl_session.rb
--- ruby-2.5.9.orig/test/openssl/test_ssl_session.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_ssl_session.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,12 +1,10 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
-if defined?(OpenSSL)
+if defined?(OpenSSL::SSL)
 
 class OpenSSL::TestSSLSession < OpenSSL::SSLTestCase
   def test_session
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-
     ctx_proc = proc { |ctx| ctx.ssl_version = :TLSv1_2 }
     start_server(ctx_proc: ctx_proc) do |port|
       server_connect_with_session(port, nil, nil) { |ssl|
@@ -24,7 +22,7 @@
         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
@@ -122,6 +120,7 @@
       ctx.options &= ~OpenSSL::SSL::OP_NO_TICKET
       # Disable server-side session cache which is enabled by default
       ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF
+      ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
     }
     start_server(ctx_proc: ctx_proc) do |port|
       sess1 = server_connect_with_session(port, nil, nil) { |ssl|
@@ -143,8 +142,6 @@
   end
 
   def test_server_session_cache
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-
     ctx_proc = Proc.new do |ctx|
       ctx.ssl_version = :TLSv1_2
       ctx.options |= OpenSSL::SSL::OP_NO_TICKET
@@ -222,13 +219,11 @@
   # deadlock.
   TEST_SESSION_REMOVE_CB = ENV["OSSL_TEST_ALL"] == "1"
 
-  def test_ctx_client_session_cb
-    pend "TLS 1.2 is not supported" unless tls12_supported?
-
-    ctx_proc = proc { |ctx| ctx.ssl_version = :TLSv1_2 }
-    start_server(ctx_proc: ctx_proc) do |port|
+  def test_ctx_client_session_cb_tls12
+    start_server do |port|
       called = {}
       ctx = OpenSSL::SSL::SSLContext.new
+      ctx.min_version = ctx.max_version = :TLS1_2
       ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
       ctx.session_new_cb = lambda { |ary|
         sock, sess = ary
@@ -238,7 +233,6 @@
         ctx.session_remove_cb = lambda { |ary|
           ctx, sess = ary
           called[:remove] = [ctx, sess]
-          # any resulting value is OK (ignored)
         }
       end
 
@@ -246,8 +240,8 @@
         assert_equal(1, ctx.session_cache_stats[:cache_num])
         assert_equal(1, ctx.session_cache_stats[:connect_good])
         assert_equal([ssl, ssl.session], called[:new])
-        assert(ctx.session_remove(ssl.session))
-        assert(!ctx.session_remove(ssl.session))
+        assert_equal(true, ctx.session_remove(ssl.session))
+        assert_equal(false, ctx.session_remove(ssl.session))
         if TEST_SESSION_REMOVE_CB
           assert_equal([ctx, ssl.session], called[:remove])
         end
@@ -255,9 +249,55 @@
     end
   end
 
-  def test_ctx_server_session_cb
-    pend "TLS 1.2 is not supported" unless tls12_supported?
+  def test_ctx_client_session_cb_tls13
+    omit "TLS 1.3 not supported" unless tls13_supported?
+    omit "LibreSSL does not call session_new_cb in TLS 1.3" if libressl?
+
+    start_server do |port|
+      called = {}
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.min_version = :TLS1_3
+      ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+      ctx.session_new_cb = lambda { |ary|
+        sock, sess = ary
+        called[:new] = [sock, sess]
+      }
+
+      server_connect_with_session(port, ctx, nil) { |ssl|
+        ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
+
+        assert_operator(1, :<=, ctx.session_cache_stats[:cache_num])
+        assert_operator(1, :<=, ctx.session_cache_stats[:connect_good])
+        assert_equal([ssl, ssl.session], called[:new])
+      }
+    end
+  end
+
+  def test_ctx_client_session_cb_tls13_exception
+    omit "TLS 1.3 not supported" unless tls13_supported?
+    omit "LibreSSL does not call session_new_cb in TLS 1.3" if libressl?
+
+    server_proc = lambda do |ctx, ssl|
+      readwrite_loop(ctx, ssl)
+    rescue SystemCallError, OpenSSL::SSL::SSLError
+    end
+    start_server(server_proc: server_proc) do |port|
+      ctx = OpenSSL::SSL::SSLContext.new
+      ctx.min_version = :TLS1_3
+      ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
+      ctx.session_new_cb = lambda { |ary|
+        raise "in session_new_cb"
+      }
 
+      server_connect_with_session(port, ctx, nil) { |ssl|
+        assert_raise_with_message(RuntimeError, /in session_new_cb/) {
+          ssl.puts("abc"); assert_equal("abc\n", ssl.gets)
+        }
+      }
+    end
+  end
+
+  def test_ctx_server_session_cb
     connections = nil
     called = {}
     cctx = OpenSSL::SSL::SSLContext.new
diff -ruN ruby-2.5.9.orig/test/openssl/test_ts.rb ruby-2.5.9/test/openssl/test_ts.rb
--- ruby-2.5.9.orig/test/openssl/test_ts.rb	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/test/openssl/test_ts.rb	2025-01-29 19:08:29.936064255 +0100
@@ -0,0 +1,682 @@
+require_relative "utils"
+
+if defined?(OpenSSL) && defined?(OpenSSL::Timestamp)
+
+class OpenSSL::TestTimestamp < OpenSSL::TestCase
+  def intermediate_key
+    @intermediate_key ||= OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCcyODxH+oTrr7l7MITWcGaYnnBma6vidCCJjuSzZpaRmXZHAyH
+0YcY4ttC0BdJ4uV+cE05IySVC7tyvVfFb8gFQ6XJV+AEktP+XkLbcxZgj9d2NVu1
+ziXdI+ldXkPnMhyWpMS5E7SD6gflv9NhUYEsmAGsUgdK6LDmm2W2/4TlewIDAQAB
+AoGAYgx6KDFWONLqjW3f/Sv/mGYHUNykUyDzpcD1Npyf797gqMMSzwlo3FZa2tC6
+D7n23XirwpTItvEsW9gvgMikJDPlThAeGLZ+L0UbVNNBHVxGP998Nda1kxqKvhRE
+pfZCKc7PLM9ZXc6jBTmgxdcAYfVCCVUoa2mEf9Ktr3BlI4kCQQDQAM09+wHDXGKP
+o2UnCwCazGtyGU2r0QCzHlh9BVY+KD2KjjhuWh86rEbdWN7hEW23Je1vXIhuM6Pa
+/Ccd+XYnAkEAwPZ91PK6idEONeGQ4I3dyMKV2SbaUjfq3MDL4iIQPQPuj7QsBO/5
+3Nf9ReSUUTRFCUVwoC8k4Z1KAJhR/K/ejQJANE7PTnPuGJQGETs09+GTcFpR9uqY
+FspDk8fg1ufdrVnvSAXF+TJewiGK3KU5v33jinhWQngRsyz3Wt2odKhEZwJACbjh
+oicQqvzzgFd7GzVKpWDYd/ZzLY1PsgusuhoJQ2m9TVRAm4cTycLAKhNYPbcqe0sa
+X5fAffWU0u7ZwqeByQJAOUAbYET4RU3iymAvAIDFj8LiQnizG9t5Ty3HXlijKQYv
+y8gsvWd4CdxwOPatWpBUX9L7IXcMJmD44xXTUvpbfQ==
+-----END RSA PRIVATE KEY-----
+_end_of_pem_
+  end
+
+  def ee_key
+    @ee_key ||= OpenSSL::PKey::RSA.new <<-_end_of_pem_
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDA6eB5r2O5KOKNbKMBhzadl43lgpwqq28m+G0gH38kKCL1f3o9
+P8xUZm7sZqcWEervZMSSXMGBV9DgeoSR+U6FMJywgQGx/JNRx7wZTMNym3PvgLkl
+xCXh6ZA0/xbtJtcNI+UUv0ENBkTIuUWBhkAf3jQclAr9aQ0ktYBuHAcRcQIDAQAB
+AoGAKNhcAuezwZx6e18pFEXAtpVEIfgJgK9TlXi8AjUpAkrNPBWFmDpN1QDrM3p4
+nh+lEpLPW/3vqqchPqYyM4YJraMLpS3KUG+s7+m9QIia0ri2WV5Cig7WL+Tl9p7K
+b3oi2Aj/wti8GfOLFQXOQQ4Ea4GoCv2Sxe0GZR39UBxzTsECQQD1zuVIwBvqU2YR
+8innsoa+j4u2hulRmQO6Zgpzj5vyRYfA9uZxQ9nKbfJvzuWwUv+UzyS9RqxarqrP
+5nQw5EmVAkEAyOmJg6+AfGrgvSWfSpXEds/WA/sHziCO3rE4/sd6cnDc6XcTgeMs
+mT8Z3kAYGpqFDew5orUylPfJJa+PUueJbQJAY+gkvw3+Cp69FLw1lgu0wo07fwOU
+n2qu3jsNMm0DOFRUWfTAMvcd9S385L7WEnWZldUfnKK1+OGXYYrMXPbchQJAChU2
+UoaHQzc16iguM1cK0g+iJPb/MEgQA3sPajHmokGpxIm2T+lvvo0dJjs/Om6QyN8X
+EWRYkoNQ8/Q4lCeMjQJAfvDIGtyqF4PieFHYgluQAv5pGgYpakdc8SYyeRH9NKey
+GaL27FRs4fRWf9OmxPhUVgIyGzLGXrueemvQUDHObA==
+-----END RSA PRIVATE KEY-----
+_end_of_pem_
+  end
+
+  def ca_cert
+    @ca_cert ||= OpenSSL::Certs.ca_cert
+  end
+
+  def ca_store
+    @ca_store ||= OpenSSL::X509::Store.new.tap { |s| s.add_cert(ca_cert) }
+  end
+
+  def ts_cert_direct
+    @ts_cert_direct ||= OpenSSL::Certs.ts_cert_direct(ee_key, ca_cert)
+  end
+
+  def intermediate_cert
+    @intermediate_cert ||= OpenSSL::Certs.intermediate_cert(intermediate_key, ca_cert)
+  end
+
+  def intermediate_store
+    @intermediate_store ||= OpenSSL::X509::Store.new.tap { |s| s.add_cert(intermediate_cert) }
+  end
+
+  def ts_cert_ee
+    @ts_cert_ee ||= OpenSSL::Certs.ts_cert_ee(ee_key, intermediate_cert, intermediate_key)
+  end
+
+  def test_request_mandatory_fields
+    req = OpenSSL::Timestamp::Request.new
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      tmp = req.to_der
+      pp OpenSSL::ASN1.decode(tmp)
+    end
+    req.algorithm = "sha1"
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      req.to_der
+    end
+    req.message_imprint = OpenSSL::Digest.digest('SHA1', "data")
+    req.to_der
+  end
+
+  def test_request_assignment
+    req = OpenSSL::Timestamp::Request.new
+
+    req.version = 2
+    assert_equal(2, req.version)
+    assert_raise(TypeError) { req.version = nil }
+    assert_raise(TypeError) { req.version = "foo" }
+
+    req.algorithm = "SHA1"
+    assert_equal("SHA1", req.algorithm)
+    assert_raise(TypeError) { req.algorithm = nil }
+    assert_raise(OpenSSL::ASN1::ASN1Error) { req.algorithm = "xxx" }
+
+    req.message_imprint = "test"
+    assert_equal("test", req.message_imprint)
+    assert_raise(TypeError) { req.message_imprint = nil }
+
+    req.policy_id = "1.2.3.4.5"
+    assert_equal("1.2.3.4.5", req.policy_id)
+    assert_raise(TypeError) { req.policy_id = 123 }
+    assert_raise(TypeError) { req.policy_id = nil }
+
+    req.nonce = 42
+    assert_equal(42, req.nonce)
+    assert_raise(TypeError) { req.nonce = "foo" }
+    assert_raise(TypeError) { req.nonce = nil }
+
+    req.cert_requested = false
+    assert_equal(false, req.cert_requested?)
+    req.cert_requested = nil
+    assert_equal(false, req.cert_requested?)
+    req.cert_requested = 123
+    assert_equal(true, req.cert_requested?)
+    req.cert_requested = "asdf"
+    assert_equal(true, req.cert_requested?)
+  end
+
+  def test_request_serialization
+    req = OpenSSL::Timestamp::Request.new
+
+    req.version = 2
+    req.algorithm = "SHA1"
+    req.message_imprint = "test"
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+    req.cert_requested = true
+
+    req = OpenSSL::Timestamp::Request.new(req.to_der)
+
+    assert_equal(2, req.version)
+    assert_equal("SHA1", req.algorithm)
+    assert_equal("test", req.message_imprint)
+    assert_equal("1.2.3.4.5", req.policy_id)
+    assert_equal(42, req.nonce)
+    assert_equal(true, req.cert_requested?)
+
+  end
+
+  def test_request_re_assignment
+    #tests whether the potential 'freeing' of previous values in C works properly
+    req = OpenSSL::Timestamp::Request.new
+    req.version = 2
+    req.version = 3
+    req.algorithm = "SHA1"
+    req.algorithm = "SHA256"
+    req.message_imprint = "test"
+    req.message_imprint = "test2"
+    req.policy_id = "1.2.3.4.5"
+    req.policy_id = "1.2.3.4.6"
+    req.nonce = 42
+    req.nonce = 24
+    req.cert_requested = false
+    req.cert_requested = true
+    req.to_der
+  end
+
+  def test_request_encode_decode
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+
+    qer = OpenSSL::Timestamp::Request.new(req.to_der)
+    assert_equal(1, qer.version)
+    assert_equal("SHA1", qer.algorithm)
+    assert_equal(digest, qer.message_imprint)
+    assert_equal("1.2.3.4.5", qer.policy_id)
+    assert_equal(42, qer.nonce)
+
+    #put OpenSSL::ASN1.decode inbetween
+    qer2 = OpenSSL::Timestamp::Request.new(OpenSSL::ASN1.decode(req.to_der))
+    assert_equal(1, qer2.version)
+    assert_equal("SHA1", qer2.algorithm)
+    assert_equal(digest, qer2.message_imprint)
+    assert_equal("1.2.3.4.5", qer2.policy_id)
+    assert_equal(42, qer2.nonce)
+  end
+
+  def test_request_invalid_asn1
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      OpenSSL::Timestamp::Request.new("*" * 44)
+    end
+  end
+
+  def test_response_constants
+    assert_equal(0, OpenSSL::Timestamp::Response::GRANTED)
+    assert_equal(1, OpenSSL::Timestamp::Response::GRANTED_WITH_MODS)
+    assert_equal(2, OpenSSL::Timestamp::Response::REJECTION)
+    assert_equal(3, OpenSSL::Timestamp::Response::WAITING)
+    assert_equal(4, OpenSSL::Timestamp::Response::REVOCATION_WARNING)
+    assert_equal(5, OpenSSL::Timestamp::Response::REVOCATION_NOTIFICATION)
+  end
+
+  def test_response_creation
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+
+    fac = OpenSSL::Timestamp::Factory.new
+    time = Time.now
+    fac.gen_time = time
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    resp = OpenSSL::Timestamp::Response.new(resp)
+    assert_equal(OpenSSL::Timestamp::Response::GRANTED, resp.status)
+    assert_nil(resp.failure_info)
+    assert_equal([], resp.status_text)
+    assert_equal(1, resp.token_info.version)
+    assert_equal("1.2.3.4.5", resp.token_info.policy_id)
+    assert_equal("SHA1", resp.token_info.algorithm)
+    assert_equal(digest, resp.token_info.message_imprint)
+    assert_equal(1, resp.token_info.serial_number)
+    assert_equal(time.to_i, resp.token_info.gen_time.to_i)
+    assert_equal(false, resp.token_info.ordering)
+    assert_nil(resp.token_info.nonce)
+    assert_cert(ts_cert_ee, resp.tsa_certificate)
+    #compare PKCS7
+    token = OpenSSL::ASN1.decode(resp.to_der).value[1]
+    assert_equal(token.to_der, resp.token.to_der)
+  end
+
+  def test_response_failure_info
+    resp = OpenSSL::Timestamp::Response.new("0\"0 \x02\x01\x020\x17\f\x15Invalid TimeStampReq.\x03\x02\x06\x80")
+    assert_equal(:BAD_ALG, resp.failure_info)
+  end
+
+  def test_response_mandatory_fields
+    fac = OpenSSL::Timestamp::Factory.new
+    req = OpenSSL::Timestamp::Request.new
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      fac.create_timestamp(ee_key, ts_cert_ee, req)
+    end
+    req.algorithm = "sha1"
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      fac.create_timestamp(ee_key, ts_cert_ee, req)
+    end
+    req.message_imprint = OpenSSL::Digest.digest('SHA1', "data")
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      fac.create_timestamp(ee_key, ts_cert_ee, req)
+    end
+    fac.gen_time = Time.now
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      fac.create_timestamp(ee_key, ts_cert_ee, req)
+    end
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      fac.create_timestamp(ee_key, ts_cert_ee, req)
+    end
+    fac.default_policy_id = "1.2.3.4.5"
+    assert_equal OpenSSL::Timestamp::Response::GRANTED, fac.create_timestamp(ee_key, ts_cert_ee, req).status
+    fac.default_policy_id = nil
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      fac.create_timestamp(ee_key, ts_cert_ee, req)
+    end
+    req.policy_id = "1.2.3.4.5"
+    assert_equal OpenSSL::Timestamp::Response::GRANTED, fac.create_timestamp(ee_key, ts_cert_ee, req).status
+  end
+
+  def test_response_allowed_digests
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    req.message_imprint = OpenSSL::Digest.digest('SHA1', "test")
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.default_policy_id = "1.2.3.4.6"
+
+    # None allowed by default
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+    # Explicitly allow SHA1 (string)
+    fac.allowed_digests = ["sha1"]
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal OpenSSL::Timestamp::Response::GRANTED, resp.status
+
+    # Explicitly allow SHA1 (object)
+    fac.allowed_digests = [OpenSSL::Digest.new('SHA1')]
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal OpenSSL::Timestamp::Response::GRANTED, resp.status
+
+    # Others not allowed
+    req.algorithm = "SHA256"
+    req.message_imprint = OpenSSL::Digest.digest('SHA256', "test")
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+    # Non-Array
+    fac.allowed_digests = 123
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal OpenSSL::Timestamp::Response::REJECTION, resp.status
+
+    # Non-String, non-Digest Array element
+    fac.allowed_digests = ["sha1", OpenSSL::Digest.new('SHA1'), 123]
+    assert_raise(TypeError) do
+      fac.create_timestamp(ee_key, ts_cert_ee, req)
+    end
+  end
+
+  def test_response_default_policy
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    fac.default_policy_id = "1.2.3.4.6"
+
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal(OpenSSL::Timestamp::Response::GRANTED, resp.status)
+    assert_equal("1.2.3.4.6", resp.token_info.policy_id)
+
+    assert_match(/1\.2\.3\.4\.6/, resp.to_text)
+  end
+
+  def test_response_bad_purpose
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+
+
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      fac.create_timestamp(ee_key, intermediate_cert, req)
+    end
+  end
+
+  def test_response_invalid_asn1
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      OpenSSL::Timestamp::Response.new("*" * 44)
+    end
+  end
+
+  def test_no_cert_requested
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.cert_requested = false
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    fac.default_policy_id = "1.2.3.4.5"
+
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal(OpenSSL::Timestamp::Response::GRANTED, resp.status)
+    assert_nil(resp.tsa_certificate)
+  end
+
+  def test_response_no_policy_defined
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      req = OpenSSL::Timestamp::Request.new
+      req.algorithm = "SHA1"
+      digest = OpenSSL::Digest.digest('SHA1', "test")
+      req.message_imprint = digest
+
+      fac = OpenSSL::Timestamp::Factory.new
+      fac.gen_time = Time.now
+      fac.serial_number = 1
+      fac.allowed_digests = ["sha1"]
+
+      fac.create_timestamp(ee_key, ts_cert_ee, req)
+    end
+  end
+
+  def test_verify_ee_no_req
+    assert_raise(TypeError) do
+      ts, _ = timestamp_ee
+      ts.verify(nil, ca_cert)
+    end
+  end
+
+  def test_verify_ee_no_store
+    assert_raise(TypeError) do
+      ts, req = timestamp_ee
+      ts.verify(req, nil)
+    end
+  end
+
+  def test_verify_ee_wrong_root_no_intermediate
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      ts, req = timestamp_ee
+      ts.verify(req, intermediate_store)
+    end
+  end
+
+  def test_verify_ee_wrong_root_wrong_intermediate
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      ts, req = timestamp_ee
+      ts.verify(req, intermediate_store, [ca_cert])
+    end
+  end
+
+  def test_verify_ee_nonce_mismatch
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      ts, req = timestamp_ee
+      req.nonce = 1
+      ts.verify(req, ca_store, [intermediate_cert])
+    end
+  end
+
+  def test_verify_ee_intermediate_missing
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      ts, req = timestamp_ee
+      ts.verify(req, ca_store)
+    end
+  end
+
+  def test_verify_ee_intermediate
+    ts, req = timestamp_ee
+    ts.verify(req, ca_store, [intermediate_cert])
+  end
+
+  def test_verify_ee_intermediate_type_error
+    ts, req = timestamp_ee
+    assert_raise(TypeError) { ts.verify(req, [ca_cert], 123) }
+  end
+
+  def test_verify_ee_def_policy
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.nonce = 42
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    fac.default_policy_id = "1.2.3.4.5"
+
+    ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    ts.verify(req, ca_store, [intermediate_cert])
+  end
+
+  def test_verify_direct
+    ts, req = timestamp_direct
+    ts.verify(req, ca_store)
+  end
+
+  def test_verify_direct_redundant_untrusted
+    ts, req = timestamp_direct
+    ts.verify(req, ca_store, [ts.tsa_certificate, ts.tsa_certificate])
+  end
+
+  def test_verify_direct_unrelated_untrusted
+    ts, req = timestamp_direct
+    ts.verify(req, ca_store, [intermediate_cert])
+  end
+
+  def test_verify_direct_wrong_root
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      ts, req = timestamp_direct
+      ts.verify(req, intermediate_store)
+    end
+  end
+
+  def test_verify_direct_no_cert_no_intermediate
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      ts, req = timestamp_direct_no_cert
+      ts.verify(req, ca_store)
+    end
+  end
+
+  def test_verify_ee_no_cert
+    ts, req = timestamp_ee_no_cert
+    ts.verify(req, ca_store, [ts_cert_ee, intermediate_cert])
+  end
+
+  def test_verify_ee_no_cert_no_intermediate
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      ts, req = timestamp_ee_no_cert
+      ts.verify(req, ca_store, [ts_cert_ee])
+    end
+  end
+
+  def test_verify_ee_additional_certs_array
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    fac.additional_certs = [intermediate_cert]
+    ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal(2, ts.token.certificates.size)
+    fac.additional_certs = nil
+    ts.verify(req, ca_store)
+    ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal(1, ts.token.certificates.size)
+  end
+
+  def test_verify_ee_additional_certs_with_root
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    fac.additional_certs = [intermediate_cert, ca_cert]
+    ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_equal(3, ts.token.certificates.size)
+    ts.verify(req, ca_store)
+  end
+
+  def test_verify_ee_cert_inclusion_not_requested
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.nonce = 42
+    req.cert_requested = false
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    #needed because the Request contained no policy identifier
+    fac.default_policy_id = '1.2.3.4.5'
+    fac.additional_certs = [ ts_cert_ee, intermediate_cert ]
+    ts = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    assert_nil(ts.token.certificates) #since cert_requested? == false
+    ts.verify(req, ca_store, [ts_cert_ee, intermediate_cert])
+  end
+
+  def test_reusable
+    #test if req and faq are reusable, i.e. the internal
+    #CTX_free methods don't mess up e.g. the certificates
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    fac.additional_certs = [ intermediate_cert ]
+    ts1 = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    ts1.verify(req, ca_store)
+    ts2 = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    ts2.verify(req, ca_store)
+    refute_nil(ts1.tsa_certificate)
+    refute_nil(ts2.tsa_certificate)
+  end
+
+  def test_token_info_creation
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = OpenSSL::BN.new(123)
+
+    fac = OpenSSL::Timestamp::Factory.new
+    time = Time.now
+    fac.gen_time = time
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+
+    resp = fac.create_timestamp(ee_key, ts_cert_ee, req)
+    info = resp.token_info
+    info = OpenSSL::Timestamp::TokenInfo.new(info.to_der)
+
+    assert_equal(1, info.version)
+    assert_equal("1.2.3.4.5", info.policy_id)
+    assert_equal("SHA1", info.algorithm)
+    assert_equal(digest, info.message_imprint)
+    assert_equal(1, info.serial_number)
+    assert_equal(time.to_i, info.gen_time.to_i)
+    assert_equal(false, info.ordering)
+    assert_equal(123, info.nonce)
+  end
+
+  def test_token_info_invalid_asn1
+    assert_raise(OpenSSL::Timestamp::TimestampError) do
+      OpenSSL::Timestamp::TokenInfo.new("*" * 44)
+    end
+  end
+
+  private
+
+  def assert_cert expected, actual
+    assert_equal expected.to_der, actual.to_der
+  end
+
+  def timestamp_ee
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    return fac.create_timestamp(ee_key, ts_cert_ee, req), req
+  end
+
+  def timestamp_ee_no_cert
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+    req.cert_requested = false
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    return fac.create_timestamp(ee_key, ts_cert_ee, req), req
+  end
+
+  def timestamp_direct
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    return fac.create_timestamp(ee_key, ts_cert_direct, req), req
+  end
+
+  def timestamp_direct_no_cert
+    req = OpenSSL::Timestamp::Request.new
+    req.algorithm = "SHA1"
+    digest = OpenSSL::Digest.digest('SHA1', "test")
+    req.message_imprint = digest
+    req.policy_id = "1.2.3.4.5"
+    req.nonce = 42
+    req.cert_requested = false
+
+    fac = OpenSSL::Timestamp::Factory.new
+    fac.gen_time = Time.now
+    fac.serial_number = 1
+    fac.allowed_digests = ["sha1"]
+    return fac.create_timestamp(ee_key, ts_cert_direct, req), req
+  end
+end
+
+end
diff -ruN ruby-2.5.9.orig/test/openssl/test_x509attr.rb ruby-2.5.9/test/openssl/test_x509attr.rb
--- ruby-2.5.9.orig/test/openssl/test_x509attr.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_x509attr.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
@@ -48,6 +48,10 @@
     assert_raise(TypeError) {
       attr.value = "1234"
     }
+    assert_raise(OpenSSL::X509::AttributeError) {
+      v = OpenSSL::ASN1::Set([OpenSSL::ASN1::UTF8String("1234")], 17, :EXPLICIT)
+      attr.value = v
+    }
     assert_equal(test_der, attr.to_der)
     assert_raise(OpenSSL::X509::AttributeError) {
       attr.oid = "abc123"
@@ -79,6 +83,16 @@
     assert_equal true, attr1 == attr2
     assert_equal false, attr1 == attr3
   end
+
+  def test_marshal
+    val = OpenSSL::ASN1::Set([
+      OpenSSL::ASN1::UTF8String("abc123")
+    ])
+    attr = OpenSSL::X509::Attribute.new("challengePassword", val)
+    deserialized = Marshal.load(Marshal.dump(attr))
+
+    assert_equal attr.to_der, deserialized.to_der
+  end
 end
 
 end
diff -ruN ruby-2.5.9.orig/test/openssl/test_x509cert.rb ruby-2.5.9/test/openssl/test_x509cert.rb
--- ruby-2.5.9.orig/test/openssl/test_x509cert.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_x509cert.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
@@ -68,12 +68,12 @@
     assert_equal(now.getutc, cert.not_after)
   end
 
-  def test_extension
+  def test_extension_factory
     ca_exts = [
       ["basicConstraints","CA:TRUE",true],
       ["keyUsage","keyCertSign, cRLSign",true],
       ["subjectKeyIdentifier","hash",false],
-      ["authorityKeyIdentifier","keyid:always",false],
+      ["authorityKeyIdentifier","issuer:always,keyid:always",false],
     ]
     ca_cert = issue_cert(@ca, @rsa2048, 1, ca_exts, nil, nil)
     ca_cert.extensions.each_with_index{|ext, i|
@@ -84,7 +84,7 @@
     ee1_exts = [
       ["keyUsage","Non Repudiation, Digital Signature, Key Encipherment",true],
       ["subjectKeyIdentifier","hash",false],
-      ["authorityKeyIdentifier","keyid:always",false],
+      ["authorityKeyIdentifier","issuer:always,keyid:always",false],
       ["extendedKeyUsage","clientAuth, emailProtection, codeSigning",false],
       ["subjectAltName","email:ee1@ruby-lang.org",false],
     ]
@@ -96,14 +96,160 @@
     }
   end
 
+  def test_akiski
+    ca_cert = generate_cert(@ca, @rsa2048, 4, nil)
+    ef = OpenSSL::X509::ExtensionFactory.new(ca_cert, ca_cert)
+    ca_cert.add_extension(
+      ef.create_extension("subjectKeyIdentifier", "hash", false))
+    ca_cert.add_extension(
+      ef.create_extension("authorityKeyIdentifier", "issuer:always,keyid:always", false))
+    ca_cert.sign(@rsa2048, "sha256")
+
+    ca_keyid = get_subject_key_id(ca_cert.to_der, hex: false)
+    assert_equal ca_keyid, ca_cert.authority_key_identifier
+    assert_equal ca_keyid, ca_cert.subject_key_identifier
+
+    ee_cert = generate_cert(@ee1, Fixtures.pkey("p256"), 5, ca_cert)
+    ef = OpenSSL::X509::ExtensionFactory.new(ca_cert, ee_cert)
+    ee_cert.add_extension(
+      ef.create_extension("subjectKeyIdentifier", "hash", false))
+    ee_cert.add_extension(
+      ef.create_extension("authorityKeyIdentifier", "issuer:always,keyid:always", false))
+    ee_cert.sign(@rsa2048, "sha256")
+
+    ee_keyid = get_subject_key_id(ee_cert.to_der, hex: false)
+    assert_equal ca_keyid, ee_cert.authority_key_identifier
+    assert_equal ee_keyid, ee_cert.subject_key_identifier
+  end
+
+  def test_akiski_missing
+    cert = issue_cert(@ee1, @rsa2048, 1, [], nil, nil)
+    assert_nil(cert.authority_key_identifier)
+    assert_nil(cert.subject_key_identifier)
+  end
+
+  def test_crl_uris_no_crl_distribution_points
+    cert = issue_cert(@ee1, @rsa2048, 1, [], nil, nil)
+    assert_nil(cert.crl_uris)
+  end
+
+  def test_crl_uris
+    # Multiple DistributionPoint contains a single general name each
+    ef = OpenSSL::X509::ExtensionFactory.new
+    ef.config = OpenSSL::Config.parse(<<~_cnf_)
+      [crlDistPts]
+      URI.1 = http://www.example.com/crl
+      URI.2 = ldap://ldap.example.com/cn=ca?certificateRevocationList;binary
+    _cnf_
+    cdp_cert = generate_cert(@ee1, @rsa2048, 3, nil)
+    ef.subject_certificate = cdp_cert
+    cdp_cert.add_extension(ef.create_extension("crlDistributionPoints", "@crlDistPts"))
+    cdp_cert.sign(@rsa2048, "sha256")
+    assert_equal(
+      ["http://www.example.com/crl", "ldap://ldap.example.com/cn=ca?certificateRevocationList;binary"],
+      cdp_cert.crl_uris
+    )
+  end
+
+  def test_crl_uris_multiple_general_names
+    # Single DistributionPoint contains multiple general names of type URI
+    ef = OpenSSL::X509::ExtensionFactory.new
+    ef.config = OpenSSL::Config.parse(<<~_cnf_)
+      [crlDistPts_section]
+      fullname = URI:http://www.example.com/crl, URI:ldap://ldap.example.com/cn=ca?certificateRevocationList;binary
+    _cnf_
+    cdp_cert = generate_cert(@ee1, @rsa2048, 3, nil)
+    ef.subject_certificate = cdp_cert
+    cdp_cert.add_extension(ef.create_extension("crlDistributionPoints", "crlDistPts_section"))
+    cdp_cert.sign(@rsa2048, "sha256")
+    assert_equal(
+      ["http://www.example.com/crl", "ldap://ldap.example.com/cn=ca?certificateRevocationList;binary"],
+      cdp_cert.crl_uris
+    )
+  end
+
+  def test_crl_uris_no_uris
+    # The only DistributionPointName is a directoryName
+    ef = OpenSSL::X509::ExtensionFactory.new
+    ef.config = OpenSSL::Config.parse(<<~_cnf_)
+      [crlDistPts_section]
+      fullname = dirName:dirname_section
+      [dirname_section]
+      CN = dirname
+    _cnf_
+    cdp_cert = generate_cert(@ee1, @rsa2048, 3, nil)
+    ef.subject_certificate = cdp_cert
+    cdp_cert.add_extension(ef.create_extension("crlDistributionPoints", "crlDistPts_section"))
+    cdp_cert.sign(@rsa2048, "sha256")
+    assert_nil(cdp_cert.crl_uris)
+  end
+
+  def test_aia_missing
+    cert = issue_cert(@ee1, @rsa2048, 1, [], nil, nil)
+    assert_nil(cert.ca_issuer_uris)
+    assert_nil(cert.ocsp_uris)
+  end
+
+  def test_aia
+    ef = OpenSSL::X509::ExtensionFactory.new
+    aia_cert = generate_cert(@ee1, @rsa2048, 4, nil)
+    ef.subject_certificate = aia_cert
+    aia_cert.add_extension(
+      ef.create_extension(
+        "authorityInfoAccess",
+        "caIssuers;URI:http://www.example.com/caIssuers," \
+        "caIssuers;URI:ldap://ldap.example.com/cn=ca?authorityInfoAccessCaIssuers;binary," \
+        "OCSP;URI:http://www.example.com/ocsp," \
+        "OCSP;URI:ldap://ldap.example.com/cn=ca?authorityInfoAccessOcsp;binary",
+        false
+      )
+    )
+    aia_cert.sign(@rsa2048, "sha256")
+    assert_equal(
+      ["http://www.example.com/caIssuers", "ldap://ldap.example.com/cn=ca?authorityInfoAccessCaIssuers;binary"],
+      aia_cert.ca_issuer_uris
+    )
+    assert_equal(
+      ["http://www.example.com/ocsp", "ldap://ldap.example.com/cn=ca?authorityInfoAccessOcsp;binary"],
+      aia_cert.ocsp_uris
+    )
+  end
+
+  def test_invalid_extension
+    integer = OpenSSL::ASN1::Integer.new(0)
+    invalid_exts_cert = generate_cert(@ee1, @rsa1024, 1, nil)
+    ["subjectKeyIdentifier", "authorityKeyIdentifier", "crlDistributionPoints", "authorityInfoAccess"].each do |ext|
+      invalid_exts_cert.add_extension(
+        OpenSSL::X509::Extension.new(ext, integer.to_der)
+      )
+    end
+
+    assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+      invalid_exts_cert.authority_key_identifier
+    }
+    assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+      invalid_exts_cert.subject_key_identifier
+    }
+    assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+      invalid_exts_cert.crl_uris
+    }
+    assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+      invalid_exts_cert.ca_issuer_uris
+    }
+    assert_raise(OpenSSL::ASN1::ASN1Error, "invalid extension") {
+      invalid_exts_cert.ocsp_uris
+    }
+  end
+
   def test_sign_and_verify_rsa_sha1
-    cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: "sha1")
+    cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: "SHA1")
     assert_equal(false, cert.verify(@rsa1024))
     assert_equal(true,  cert.verify(@rsa2048))
     assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
     assert_equal(false, certificate_error_returns_false { cert.verify(@dsa512) })
     cert.serial = 2
     assert_equal(false, cert.verify(@rsa2048))
+  rescue OpenSSL::X509::CertificateError # RHEL 9 disables SHA1
   end
 
   def test_sign_and_verify_rsa_md5
@@ -129,7 +275,7 @@
   end
 
   def test_sign_and_verify_rsa_dss1
-    cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: OpenSSL::Digest::DSS1.new)
+    cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil, digest: OpenSSL::Digest.new('DSS1'))
     assert_equal(false, cert.verify(@rsa1024))
     assert_equal(true, cert.verify(@rsa2048))
     assert_equal(false, certificate_error_returns_false { cert.verify(@dsa256) })
@@ -145,6 +291,16 @@
     }
   end
 
+  def test_sign_and_verify_ed25519
+    # Ed25519 is not FIPS-approved.
+    omit_on_fips
+    # See ASN1_item_sign_ctx in ChangeLog for 3.8.1: https://github.com/libressl/portable/blob/master/ChangeLog
+    omit "Ed25519 not supported" unless openssl?(1, 1, 1) || libressl?(3, 8, 1)
+    ed25519 = OpenSSL::PKey::generate_key("ED25519")
+    cert = issue_cert(@ca, ed25519, 1, [], nil, nil, digest: nil)
+    assert_equal(true, cert.verify(ed25519))
+  end
+
   def test_dsa_with_sha2
     cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha256")
     assert_equal("dsa_with_SHA256", cert.signature_algorithm)
@@ -153,6 +309,7 @@
     # SHA1 is allowed from OpenSSL 1.0.0 (0.9.8 requires DSS1)
     cert = issue_cert(@ca, @dsa256, 1, [], nil, nil, digest: "sha1")
     assert_equal("dsaWithSHA1", cert.signature_algorithm)
+  rescue OpenSSL::X509::CertificateError # RHEL 9 disables SHA1
   end
 
   def test_check_private_key
@@ -169,6 +326,18 @@
     }
   end
 
+  def test_read_der_then_pem
+    cert1 = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
+    exts = [
+      # A new line before PEM block
+      ["nsComment", "Another certificate:\n" + cert1.to_pem],
+    ]
+    cert2 = issue_cert(@ca, @rsa2048, 2, exts, nil, nil)
+
+    assert_equal cert2, OpenSSL::X509::Certificate.new(cert2.to_der)
+    assert_equal cert2, OpenSSL::X509::Certificate.new(cert2.to_pem)
+  end
+
   def test_eq
     now = Time.now
     cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil,
@@ -189,6 +358,58 @@
     assert_equal false, cert3 == cert4
   end
 
+  def test_marshal
+    now = Time.now
+    cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil,
+      not_before: now, not_after: now + 3600)
+    cert = issue_cert(@ee1, @rsa2048, 2, [], cacert, @rsa1024,
+      not_before: now, not_after: now + 3600)
+    deserialized = Marshal.load(Marshal.dump(cert))
+
+    assert_equal cert.to_der, deserialized.to_der
+  end
+
+  def test_load_file_empty_pem
+    empty_path = Fixtures.file_path("pkey", "empty.pem")
+    assert_raise(OpenSSL::X509::CertificateError) do
+      OpenSSL::X509::Certificate.load_file(empty_path)
+    end
+  end
+
+  def test_load_file_fullchain_pem
+    fullchain_path = Fixtures.file_path("pkey", "fullchain.pem")
+    certificates = OpenSSL::X509::Certificate.load_file(fullchain_path)
+    assert_equal 2, certificates.size
+    assert_equal "/CN=www.codeotaku.com", certificates[0].subject.to_s
+    assert_equal "/C=US/O=Let's Encrypt/CN=R3", certificates[1].subject.to_s
+  end
+
+  def test_load_file_certificate_der
+    fullchain_path = Fixtures.file_path("pkey", "certificate.der")
+    certificates = OpenSSL::X509::Certificate.load_file(fullchain_path)
+
+    # DER encoding can only contain one certificate:
+    assert_equal 1, certificates.size
+    assert_equal "/CN=www.codeotaku.com", certificates[0].subject.to_s
+  end
+
+  def test_load_file_fullchain_garbage
+    fullchain_path = Fixtures.file_path("pkey", "garbage.txt")
+
+    assert_raise(OpenSSL::X509::CertificateError) do
+      OpenSSL::X509::Certificate.load_file(fullchain_path)
+    end
+  end
+
+  def test_tbs_precert_bytes
+    pend "LibreSSL < 3.5 does not have i2d_re_X509_tbs" if libressl? && !libressl?(3, 5, 0)
+
+    cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
+    seq = OpenSSL::ASN1.decode(cert.tbs_bytes)
+
+    assert_equal 7, seq.value.size
+  end
+
   private
 
   def certificate_error_returns_false
diff -ruN ruby-2.5.9.orig/test/openssl/test_x509crl.rb ruby-2.5.9/test/openssl/test_x509crl.rb
--- ruby-2.5.9.orig/test/openssl/test_x509crl.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_x509crl.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
@@ -20,7 +20,7 @@
 
     cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
     crl = issue_crl([], 1, now, now+1600, [],
-                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                    cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
     assert_equal(1, crl.version)
     assert_equal(cert.issuer.to_der, crl.issuer.to_der)
     assert_equal(now, crl.last_update)
@@ -57,7 +57,7 @@
     ]
     cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
     crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
-                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                    cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
     revoked = crl.revoked
     assert_equal(5, revoked.size)
     assert_equal(1, revoked[0].serial)
@@ -98,7 +98,7 @@
 
     revoke_info = (1..1000).collect{|i| [i, now, 0] }
     crl = issue_crl(revoke_info, 1, Time.now, Time.now+1600, [],
-                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                    cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
     revoked = crl.revoked
     assert_equal(1000, revoked.size)
     assert_equal(1, revoked[0].serial)
@@ -118,19 +118,22 @@
       ["keyUsage", "cRLSign, keyCertSign", true],
     ]
     crl_exts = [
-      ["authorityKeyIdentifier", "keyid:always", false],
+      ["authorityKeyIdentifier", "issuer:always,keyid:always", false],
       ["issuerAltName", "issuer:copy", false],
     ]
 
     cert = issue_cert(@ca, @rsa2048, 1, cert_exts, nil, nil)
     crl = issue_crl([], 1, Time.now, Time.now+1600, crl_exts,
-                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                    cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
     exts = crl.extensions
     assert_equal(3, exts.size)
     assert_equal("1", exts[0].value)
     assert_equal("crlNumber", exts[0].oid)
     assert_equal(false, exts[0].critical?)
 
+    expected_keyid = OpenSSL::TestUtils.get_subject_key_id(cert, hex: false)
+    assert_equal expected_keyid, crl.authority_key_identifier
+
     assert_equal("authorityKeyIdentifier", exts[1].oid)
     keyid = OpenSSL::TestUtils.get_subject_key_id(cert)
     assert_match(/^keyid:#{keyid}/, exts[1].value)
@@ -155,22 +158,26 @@
     assert_equal("issuerAltName", exts[2].oid)
     assert_equal("email:xyzzy@ruby-lang.org", exts[2].value)
     assert_equal(false, exts[2].critical?)
+
+    no_ext_crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+      cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
+    assert_equal nil, no_ext_crl.authority_key_identifier
   end
 
   def test_crlnumber
     cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
     crl = issue_crl([], 1, Time.now, Time.now+1600, [],
-                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                    cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
     assert_match(1.to_s, crl.extensions[0].value)
     assert_match(/X509v3 CRL Number:\s+#{1}/m, crl.to_text)
 
     crl = issue_crl([], 2**32, Time.now, Time.now+1600, [],
-                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                    cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
     assert_match((2**32).to_s, crl.extensions[0].value)
     assert_match(/X509v3 CRL Number:\s+#{2**32}/m, crl.to_text)
 
     crl = issue_crl([], 2**100, Time.now, Time.now+1600, [],
-                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                    cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
     assert_match(/X509v3 CRL Number:\s+#{2**100}/m, crl.to_text)
     assert_match((2**100).to_s, crl.extensions[0].value)
   end
@@ -178,7 +185,7 @@
   def test_sign_and_verify
     cert = issue_cert(@ca, @rsa2048, 1, [], nil, nil)
     crl = issue_crl([], 1, Time.now, Time.now+1600, [],
-                    cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                    cert, @rsa2048, OpenSSL::Digest.new('SHA256'))
     assert_equal(false, crl.verify(@rsa1024))
     assert_equal(true,  crl.verify(@rsa2048))
     assert_equal(false, crl_error_returns_false { crl.verify(@dsa256) })
@@ -188,7 +195,7 @@
 
     cert = issue_cert(@ca, @dsa512, 1, [], nil, nil)
     crl = issue_crl([], 1, Time.now, Time.now+1600, [],
-                    cert, @dsa512, OpenSSL::Digest::SHA1.new)
+                    cert, @dsa512, OpenSSL::Digest.new('SHA256'))
     assert_equal(false, crl_error_returns_false { crl.verify(@rsa1024) })
     assert_equal(false, crl_error_returns_false { crl.verify(@rsa2048) })
     assert_equal(false, crl.verify(@dsa256))
@@ -197,6 +204,23 @@
     assert_equal(false, crl.verify(@dsa512))
   end
 
+  def test_sign_and_verify_ed25519
+    # Ed25519 is not FIPS-approved.
+    omit_on_fips
+    # See ASN1_item_sign_ctx in ChangeLog for 3.8.1: https://github.com/libressl/portable/blob/master/ChangeLog
+    omit "Ed25519 not supported" unless openssl?(1, 1, 1) || libressl?(3, 8, 1)
+    ed25519 = OpenSSL::PKey::generate_key("ED25519")
+    cert = issue_cert(@ca, ed25519, 1, [], nil, nil, digest: nil)
+    crl = issue_crl([], 1, Time.now, Time.now+1600, [],
+                    cert, ed25519, nil)
+    assert_equal(false, crl_error_returns_false { crl.verify(@rsa1024) })
+    assert_equal(false, crl_error_returns_false { crl.verify(@rsa2048) })
+    assert_equal(false, crl.verify(OpenSSL::PKey::generate_key("ED25519")))
+    assert_equal(true,  crl.verify(ed25519))
+    crl.version = 0
+    assert_equal(false, crl.verify(ed25519))
+  end
+
   def test_revoked_to_der
     # revokedCertificates     SEQUENCE OF SEQUENCE  {
     #      userCertificate         CertificateSerialNumber,
@@ -249,6 +273,22 @@
     assert_equal true, rev2 == crl2.revoked[1]
   end
 
+  def test_marshal
+    now = Time.now
+
+    cacert = issue_cert(@ca, @rsa1024, 1, [], nil, nil)
+    crl = issue_crl([], 1, now, now + 3600, [], cacert, @rsa1024, "sha256")
+    rev = OpenSSL::X509::Revoked.new.tap { |rev|
+      rev.serial = 1
+      rev.time = now
+    }
+    crl.add_revoked(rev)
+    deserialized = Marshal.load(Marshal.dump(crl))
+
+    assert_equal crl.to_der, deserialized.to_der
+    assert_equal crl.revoked[0].to_der, deserialized.revoked[0].to_der
+  end
+
   private
 
   def crl_error_returns_false
diff -ruN ruby-2.5.9.orig/test/openssl/test_x509ext.rb ruby-2.5.9/test/openssl/test_x509ext.rb
--- ruby-2.5.9.orig/test/openssl/test_x509ext.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_x509ext.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -50,24 +50,41 @@
     cdp = ef.create_extension("crlDistributionPoints", "@crlDistPts")
     assert_equal(false, cdp.critical?)
     assert_equal("crlDistributionPoints", cdp.oid)
-    assert_match(%{URI:http://www\.example\.com/crl}, cdp.value)
-    assert_match(
-      %r{URI:ldap://ldap\.example\.com/cn=ca\?certificateRevocationList;binary},
-      cdp.value)
+    assert_include(cdp.value, "URI:http://www.example.com/crl")
+    assert_include(cdp.value,
+      "URI:ldap://ldap.example.com/cn=ca?certificateRevocationList;binary")
 
     cdp = ef.create_extension("crlDistributionPoints", "critical, @crlDistPts")
     assert_equal(true, cdp.critical?)
     assert_equal("crlDistributionPoints", cdp.oid)
-    assert_match(%{URI:http://www.example.com/crl}, cdp.value)
-    assert_match(
-      %r{URI:ldap://ldap.example.com/cn=ca\?certificateRevocationList;binary},
-      cdp.value)
+    assert_include(cdp.value, "URI:http://www.example.com/crl")
+    assert_include(cdp.value,
+      "URI:ldap://ldap.example.com/cn=ca?certificateRevocationList;binary")
 
     cp = ef.create_extension("certificatePolicies", "@certPolicies")
     assert_equal(false, cp.critical?)
     assert_equal("certificatePolicies", cp.oid)
-    assert_match(%r{2.23.140.1.2.1}, cp.value)
-    assert_match(%r{http://cps.example.com}, cp.value)
+    assert_include(cp.value, "2.23.140.1.2.1")
+    assert_include(cp.value, "http://cps.example.com")
+  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
@@ -86,6 +103,19 @@
     assert_equal true, ext1 == ext2
     assert_equal false, ext1 == ext3
   end
+
+  def test_marshal
+    ef = OpenSSL::X509::ExtensionFactory.new
+    ext = ef.create_extension("basicConstraints", "critical, CA:TRUE, pathlen:2")
+    deserialized = Marshal.load(Marshal.dump(ext))
+
+    assert_equal ext.to_der, deserialized.to_der
+  end
+
+  def test_value_der
+    ext = OpenSSL::X509::Extension.new(@basic_constraints.to_der)
+    assert_equal @basic_constraints_value.to_der, ext.value_der
+  end
 end
 
 end
diff -ruN ruby-2.5.9.orig/test/openssl/test_x509name.rb ruby-2.5.9/test/openssl/test_x509name.rb
--- ruby-2.5.9.orig/test/openssl/test_x509name.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_x509name.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,5 +1,5 @@
 # coding: ASCII-8BIT
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative 'utils'
 
 if defined?(OpenSSL)
@@ -389,7 +389,7 @@
     dn.each { |x| name.add_entry(*x) }
 
     str = name.to_utf8
-    expected = "CN=フー\\, バー,DC=ruby-lang,DC=org".force_encoding("UTF-8")
+    expected = String.new("CN=フー\\, バー,DC=ruby-lang,DC=org").force_encoding("UTF-8")
     assert_equal expected, str
     assert_equal Encoding.find("UTF-8"), str.encoding
 
@@ -402,6 +402,9 @@
     n2 = OpenSSL::X509::Name.parse_rfc2253 'CN=a'
 
     assert_equal n1, n2
+
+    assert_equal(false, n1 == 'abc')
+    assert_equal(false, n2 == nil)
   end
 
   def test_spaceship
@@ -409,12 +412,15 @@
     n2 = OpenSSL::X509::Name.new([["CN", "a"]])
     n3 = OpenSSL::X509::Name.new([["CN", "ab"]])
 
-    assert_equal 0, n1 <=> n2
-    assert_equal -1, n1 <=> n3
-    assert_equal 0, n2 <=> n1
-    assert_equal -1, n2 <=> n3
-    assert_equal 1, n3 <=> n1
-    assert_equal 1, n3 <=> n2
+    assert_equal(0, n1 <=> n2)
+    assert_equal(-1, n1 <=> n3)
+    assert_equal(0, n2 <=> n1)
+    assert_equal(-1, n2 <=> n3)
+    assert_equal(1, n3 <=> n1)
+    assert_equal(1, n3 <=> n2)
+    assert_equal(nil, n1 <=> 'abc')
+    assert_equal(nil, n2 <=> 123)
+    assert_equal(nil, n3 <=> nil)
   end
 
   def name_hash(name)
@@ -426,13 +432,13 @@
   def test_hash
     dn = "/DC=org/DC=ruby-lang/CN=www.ruby-lang.org"
     name = OpenSSL::X509::Name.parse(dn)
-    d = OpenSSL::Digest::MD5.digest(name.to_der)
+    d = OpenSSL::Digest.digest('MD5', name.to_der)
     expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24
     assert_equal(expected, name_hash(name))
     #
     dn = "/DC=org/DC=ruby-lang/CN=baz.ruby-lang.org"
     name = OpenSSL::X509::Name.parse(dn)
-    d = OpenSSL::Digest::MD5.digest(name.to_der)
+    d = OpenSSL::Digest.digest('MD5', name.to_der)
     expected = (d[0].ord & 0xff) | (d[1].ord & 0xff) << 8 | (d[2].ord & 0xff) << 16 | (d[3].ord & 0xff) << 24
     assert_equal(expected, name_hash(name))
   end
@@ -447,6 +453,13 @@
     assert_equal false, name0.eql?(name2)
   end
 
+  def test_marshal
+    name = OpenSSL::X509::Name.new([["DC", "org"], ["DC", "ruby-lang"], ["CN", "bar.ruby-lang.org"]])
+    deserialized = Marshal.load(Marshal.dump(name))
+
+    assert_equal name.to_der, deserialized.to_der
+  end
+
   def test_dup
     name = OpenSSL::X509::Name.parse("/CN=ruby-lang.org")
     assert_equal(name.to_der, name.dup.to_der)
diff -ruN ruby-2.5.9.orig/test/openssl/test_x509req.rb ruby-2.5.9/test/openssl/test_x509req.rb
--- ruby-2.5.9.orig/test/openssl/test_x509req.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_x509req.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,4 +1,4 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
@@ -17,37 +17,32 @@
     req = OpenSSL::X509::Request.new
     req.version = ver
     req.subject = dn
-    req.public_key = key.public_key
+    req.public_key = key
     req.sign(key, digest)
     req
   end
 
   def test_public_key
-    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
-    assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest.new('SHA256'))
+    assert_equal(@rsa1024.public_to_der, req.public_key.public_to_der)
     req = OpenSSL::X509::Request.new(req.to_der)
-    assert_equal(@rsa1024.public_key.to_der, req.public_key.to_der)
+    assert_equal(@rsa1024.public_to_der, req.public_key.public_to_der)
 
-    req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest::SHA1.new)
-    assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
+    req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest.new('SHA256'))
+    assert_equal(@dsa512.public_to_der, req.public_key.public_to_der)
     req = OpenSSL::X509::Request.new(req.to_der)
-    assert_equal(@dsa512.public_key.to_der, req.public_key.to_der)
+    assert_equal(@dsa512.public_to_der, req.public_key.public_to_der)
   end
 
   def test_version
-    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest.new('SHA256'))
     assert_equal(0, req.version)
     req = OpenSSL::X509::Request.new(req.to_der)
     assert_equal(0, req.version)
-
-    req = issue_csr(1, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
-    assert_equal(1, req.version)
-    req = OpenSSL::X509::Request.new(req.to_der)
-    assert_equal(1, req.version)
   end
 
   def test_subject
-    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest.new('SHA256'))
     assert_equal(@dn.to_der, req.subject.to_der)
     req = OpenSSL::X509::Request.new(req.to_der)
     assert_equal(@dn.to_der, req.subject.to_der)
@@ -78,9 +73,9 @@
       OpenSSL::X509::Attribute.new("msExtReq", attrval),
     ]
 
-    req0 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    req0 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest.new('SHA256'))
     attrs.each{|attr| req0.add_attribute(attr) }
-    req1 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    req1 = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest.new('SHA256'))
     req1.attributes = attrs
     assert_equal(req0.to_der, req1.to_der)
 
@@ -101,17 +96,18 @@
   end
 
   def test_sign_and_verify_rsa_sha1
-    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest.new('SHA1'))
     assert_equal(true,  req.verify(@rsa1024))
     assert_equal(false, req.verify(@rsa2048))
     assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
     assert_equal(false, request_error_returns_false { req.verify(@dsa512) })
-    req.version = 1
+    req.subject = OpenSSL::X509::Name.parse("/C=JP/CN=FooBarFooBar")
     assert_equal(false, req.verify(@rsa1024))
+  rescue OpenSSL::X509::RequestError # RHEL 9 disables SHA1
   end
 
   def test_sign_and_verify_rsa_md5
-    req = issue_csr(0, @dn, @rsa2048, OpenSSL::Digest::MD5.new)
+    req = issue_csr(0, @dn, @rsa2048, OpenSSL::Digest.new('MD5'))
     assert_equal(false, req.verify(@rsa1024))
     assert_equal(true,  req.verify(@rsa2048))
     assert_equal(false, request_error_returns_false { req.verify(@dsa256) })
@@ -122,7 +118,7 @@
   end
 
   def test_sign_and_verify_dsa
-    req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest::SHA1.new)
+    req = issue_csr(0, @dn, @dsa512, OpenSSL::Digest.new('SHA256'))
     assert_equal(false, request_error_returns_false { req.verify(@rsa1024) })
     assert_equal(false, request_error_returns_false { req.verify(@rsa2048) })
     assert_equal(false, req.verify(@dsa256))
@@ -133,24 +129,46 @@
 
   def test_sign_and_verify_dsa_md5
     assert_raise(OpenSSL::X509::RequestError){
-      issue_csr(0, @dn, @dsa512, OpenSSL::Digest::MD5.new) }
+      issue_csr(0, @dn, @dsa512, OpenSSL::Digest.new('MD5')) }
+  end
+
+  def test_sign_and_verify_ed25519
+    # Ed25519 is not FIPS-approved.
+    omit_on_fips
+    # See ASN1_item_sign_ctx in ChangeLog for 3.8.1: https://github.com/libressl/portable/blob/master/ChangeLog
+    omit "Ed25519 not supported" unless openssl?(1, 1, 1) || libressl?(3, 8, 1)
+    ed25519 = OpenSSL::PKey::generate_key("ED25519")
+    req = issue_csr(0, @dn, ed25519, nil)
+    assert_equal(false, request_error_returns_false { req.verify(@rsa1024) })
+    assert_equal(false, request_error_returns_false { req.verify(@rsa2048) })
+    assert_equal(false, req.verify(OpenSSL::PKey::generate_key("ED25519")))
+    assert_equal(true, req.verify(ed25519))
+    req.public_key = @rsa1024.public_key
+    assert_equal(false, req.verify(ed25519))
   end
 
   def test_dup
-    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest::SHA1.new)
+    req = issue_csr(0, @dn, @rsa1024, OpenSSL::Digest.new('SHA256'))
     assert_equal(req.to_der, req.dup.to_der)
   end
 
   def test_eq
-    req1 = issue_csr(0, @dn, @rsa1024, "sha1")
-    req2 = issue_csr(0, @dn, @rsa1024, "sha1")
-    req3 = issue_csr(0, @dn, @rsa1024, "sha256")
+    req1 = issue_csr(0, @dn, @rsa1024, "sha256")
+    req2 = issue_csr(0, @dn, @rsa1024, "sha256")
+    req3 = issue_csr(0, @dn, @rsa1024, "sha512")
 
     assert_equal false, req1 == 12345
     assert_equal true, req1 == req2
     assert_equal false, req1 == req3
   end
 
+  def test_marshal
+    req = issue_csr(0, @dn, @rsa1024, "sha256")
+    deserialized = Marshal.load(Marshal.dump(req))
+
+    assert_equal req.to_der, deserialized.to_der
+  end
+
   private
 
   def request_error_returns_false
diff -ruN ruby-2.5.9.orig/test/openssl/test_x509store.rb ruby-2.5.9/test/openssl/test_x509store.rb
--- ruby-2.5.9.orig/test/openssl/test_x509store.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/test_x509store.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,40 +1,30 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require_relative "utils"
 
 if defined?(OpenSSL)
 
 class OpenSSL::TestX509Store < OpenSSL::TestCase
-  def setup
-    super
-    @rsa1024 = Fixtures.pkey("rsa1024")
-    @rsa2048 = Fixtures.pkey("rsa2048")
-    @dsa256  = Fixtures.pkey("dsa256")
-    @dsa512  = Fixtures.pkey("dsa512")
-    @ca1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA1")
-    @ca2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA2")
-    @ee1 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE1")
-    @ee2 = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=EE2")
-  end
-
-  def test_nosegv_on_cleanup
-    cert  = OpenSSL::X509::Certificate.new
-    store = OpenSSL::X509::Store.new
-    ctx   = OpenSSL::X509::StoreContext.new(store, cert, [])
-    EnvUtil.suppress_warning do
-      ctx.cleanup
-    end
-    ctx.verify
+  def test_store_new
+    # v2.3.0 emits explicit warning
+    assert_warning(/new does not take any arguments/) {
+      OpenSSL::X509::Store.new(123)
+    }
   end
 
-  def test_add_file
+  def test_add_file_path
     ca_exts = [
       ["basicConstraints", "CA:TRUE", true],
       ["keyUsage", "cRLSign,keyCertSign", true],
     ]
-    cert1 = issue_cert(@ca1, @rsa1024, 1, ca_exts, nil, nil)
-    cert2 = issue_cert(@ca2, @rsa2048, 1, ca_exts, nil, nil)
-    tmpfile = Tempfile.open { |f| f << cert1.to_pem << cert2.to_pem; f }
+    cert1_subj = OpenSSL::X509::Name.parse_rfc2253("CN=Cert 1")
+    cert1_key = Fixtures.pkey("rsa-1")
+    cert1 = issue_cert(cert1_subj, cert1_key, 1, ca_exts, nil, nil)
+    cert2_subj = OpenSSL::X509::Name.parse_rfc2253("CN=Cert 2")
+    cert2_key = Fixtures.pkey("rsa-2")
+    cert2 = issue_cert(cert2_subj, cert2_key, 1, ca_exts, nil, nil)
 
+    # X509::Store#add_file reads concatenated PEM file
+    tmpfile = Tempfile.open { |f| f << cert1.to_pem << cert2.to_pem; f }
     store = OpenSSL::X509::Store.new
     assert_equal false, store.verify(cert1)
     assert_equal false, store.verify(cert2)
@@ -42,188 +32,322 @@
     assert_equal true, store.verify(cert1)
     assert_equal true, store.verify(cert2)
 
+    # X509::Store#add_path
+    Dir.mktmpdir do |dir|
+      hash1 = "%08x.%d" % [cert1_subj.hash, 0]
+      File.write(File.join(dir, hash1), cert1.to_pem)
+      store = OpenSSL::X509::Store.new
+      store.add_path(dir)
+
+      assert_equal true, store.verify(cert1)
+      assert_equal false, store.verify(cert2)
+    end
+
     # OpenSSL < 1.1.1 leaks an error on a duplicate certificate
     assert_nothing_raised { store.add_file(tmpfile.path) }
     assert_equal [], OpenSSL.errors
+
+    # Non-String is given
+    assert_raise(TypeError) { store.add_file(nil) }
   ensure
     tmpfile and tmpfile.close!
   end
 
-  def test_verify
-    # OpenSSL uses time(2) while Time.now uses clock_gettime(CLOCK_REALTIME),
-    # and there may be difference.
-    now = Time.now - 3
+  def test_verify_simple
     ca_exts = [
-      ["basicConstraints","CA:TRUE",true],
-      ["keyUsage","cRLSign,keyCertSign",true],
+      ["basicConstraints", "CA:TRUE", true],
+      ["keyUsage", "cRLSign,keyCertSign", true],
     ]
+    ca1 = OpenSSL::X509::Name.parse_rfc2253("CN=Root CA")
+    ca1_key = Fixtures.pkey("rsa-1")
+    ca1_cert = issue_cert(ca1, ca1_key, 1, ca_exts, nil, nil)
+    ca2 = OpenSSL::X509::Name.parse_rfc2253("CN=Intermediate CA")
+    ca2_key = Fixtures.pkey("rsa-2")
+    ca2_cert = issue_cert(ca2, ca2_key, 2, ca_exts, ca1_cert, ca1_key)
+
     ee_exts = [
-      ["keyUsage","keyEncipherment,digitalSignature",true],
+      ["keyUsage", "keyEncipherment,digitalSignature", true],
     ]
-    ca1_cert = issue_cert(@ca1, @rsa2048, 1, ca_exts, nil, nil)
-    ca2_cert = issue_cert(@ca2, @rsa1024, 2, ca_exts, ca1_cert, @rsa2048,
-                          not_after: now+1800)
-    ee1_cert = issue_cert(@ee1, @dsa256, 10, ee_exts, ca2_cert, @rsa1024)
-    ee2_cert = issue_cert(@ee2, @dsa512, 20, ee_exts, ca2_cert, @rsa1024)
-    ee3_cert = issue_cert(@ee2, @dsa512, 30,  ee_exts, ca2_cert, @rsa1024,
-                          not_before: now-100, not_after: now-1)
-    ee4_cert = issue_cert(@ee2, @dsa512, 40, ee_exts, ca2_cert, @rsa1024,
-                          not_before: now+1000, not_after: now+2000,)
+    ee1 = OpenSSL::X509::Name.parse_rfc2253("CN=EE 1")
+    ee1_key = Fixtures.pkey("rsa-3")
+    ee1_cert = issue_cert(ee1, ee1_key, 10, ee_exts, ca2_cert, ca2_key)
 
-    revoke_info = []
-    crl1   = issue_crl(revoke_info, 1, now, now+1800, [],
-                       ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
-    revoke_info = [ [2, now, 1], ]
-    crl1_2 = issue_crl(revoke_info, 2, now, now+1800, [],
-                       ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
-    revoke_info = [ [20, now, 1], ]
-    crl2   = issue_crl(revoke_info, 1, now, now+1800, [],
-                       ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
-    revoke_info = []
-    crl2_2 = issue_crl(revoke_info, 2, now-100, now-1, [],
-                       ca2_cert, @rsa1024, OpenSSL::Digest::SHA1.new)
+    # Nothing trusted
+    store = OpenSSL::X509::Store.new
+    assert_equal(false, store.verify(ee1_cert, [ca2_cert, ca1_cert]))
+    assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, store.error)
+    assert_match(/self.signed/i, store.error_string)
 
-    assert_equal(true, ca1_cert.verify(ca1_cert.public_key))   # self signed
-    assert_equal(true, ca2_cert.verify(ca1_cert.public_key))   # issued by ca1
-    assert_equal(true, ee1_cert.verify(ca2_cert.public_key))   # issued by ca2
-    assert_equal(true, ee2_cert.verify(ca2_cert.public_key))   # issued by ca2
-    assert_equal(true, ee3_cert.verify(ca2_cert.public_key))   # issued by ca2
-    assert_equal(true, crl1.verify(ca1_cert.public_key))       # issued by ca1
-    assert_equal(true, crl1_2.verify(ca1_cert.public_key))     # issued by ca1
-    assert_equal(true, crl2.verify(ca2_cert.public_key))       # issued by ca2
-    assert_equal(true, crl2_2.verify(ca2_cert.public_key))     # issued by ca2
+    # CA1 trusted, CA2 missing
+    store = OpenSSL::X509::Store.new
+    store.add_cert(ca1_cert)
+    assert_equal(false, store.verify(ee1_cert))
+    assert_equal(OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, store.error)
 
+    # CA1 trusted, CA2 supplied
     store = OpenSSL::X509::Store.new
-    assert_equal(false, store.verify(ca1_cert))
-    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+    store.add_cert(ca1_cert)
+    assert_equal(true, store.verify(ee1_cert, [ca2_cert]))
+    assert_match(/ok/i, store.error_string)
+    assert_equal(OpenSSL::X509::V_OK, store.error)
+    assert_equal([ee1_cert, ca2_cert, ca1_cert], store.chain)
+  end
 
-    assert_equal(false, store.verify(ca2_cert))
-    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+  def test_verify_callback
+    ca_exts = [
+      ["basicConstraints", "CA:TRUE", true],
+      ["keyUsage", "cRLSign,keyCertSign", true],
+    ]
+    ca1 = OpenSSL::X509::Name.parse_rfc2253("CN=Root CA")
+    ca1_key = Fixtures.pkey("rsa-1")
+    ca1_cert = issue_cert(ca1, ca1_key, 1, ca_exts, nil, nil)
+    ca2 = OpenSSL::X509::Name.parse_rfc2253("CN=Intermediate CA")
+    ca2_key = Fixtures.pkey("rsa-2")
+    ca2_cert = issue_cert(ca2, ca2_key, 2, ca_exts, ca1_cert, ca1_key)
 
+    ee_exts = [
+      ["keyUsage", "keyEncipherment,digitalSignature", true],
+    ]
+    ee1 = OpenSSL::X509::Name.parse_rfc2253("CN=EE 1")
+    ee1_key = Fixtures.pkey("rsa-3")
+    ee1_cert = issue_cert(ee1, ee1_key, 10, ee_exts, ca2_cert, ca2_key)
+
+    # verify_callback on X509::Store is called with proper arguments
+    cb_calls = []
+    store = OpenSSL::X509::Store.new
+    store.verify_callback = -> (preverify_ok, sctx) {
+      cb_calls << [preverify_ok, sctx.current_cert]
+      preverify_ok
+    }
     store.add_cert(ca1_cert)
-    assert_equal(true, store.verify(ca2_cert))
-    assert_equal(OpenSSL::X509::V_OK, store.error)
-    assert_equal("ok", store.error_string)
-    chain = store.chain
-    assert_equal(2, chain.size)
-    assert_equal(@ca2.to_der, chain[0].subject.to_der)
-    assert_equal(@ca1.to_der, chain[1].subject.to_der)
+    assert_equal(true, store.verify(ee1_cert, [ca2_cert]))
+    assert_include([2, 3, 4, 5], cb_calls.size)
+    cb_calls.each do |pre_ok, cert|
+      assert_equal(true, pre_ok)
+      assert_include([ca1_cert, ca2_cert, ee1_cert], cert)
+    end
 
-    store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
-    assert_equal(false, store.verify(ca2_cert))
-    assert_not_equal(OpenSSL::X509::V_OK, store.error)
+    # verify_callback can change verification result
+    store = OpenSSL::X509::Store.new
+    store.verify_callback = -> (preverify_ok, sctx) {
+      next preverify_ok if sctx.current_cert != ee1_cert
+      sctx.error = OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION
+      false
+    }
+    store.add_cert(ca1_cert)
+    assert_equal(false, store.verify(ee1_cert, [ca2_cert]))
+    assert_equal(OpenSSL::X509::V_ERR_APPLICATION_VERIFICATION, store.error)
 
-    store.purpose = OpenSSL::X509::PURPOSE_CRL_SIGN
-    assert_equal(true, store.verify(ca2_cert))
-    assert_equal(OpenSSL::X509::V_OK, store.error)
+    # Exception raised by verify_callback is currently suppressed, and is
+    # treated as a non-truthy return (with warning)
+    store = OpenSSL::X509::Store.new
+    store.verify_callback = -> (preverify_ok, sctx) {
+      raise "suppressed"
+    }
+    store.add_cert(ca1_cert)
+    assert_warning(/exception in verify_callback/) {
+      assert_equal(false, store.verify(ee1_cert, [ca2_cert]))
+    }
 
-    store.add_cert(ca2_cert)
-    store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
-    assert_equal(true, store.verify(ee1_cert))
-    assert_equal(true, store.verify(ee2_cert))
-    assert_equal(OpenSSL::X509::V_OK, store.error)
-    assert_equal("ok", store.error_string)
-    chain = store.chain
-    assert_equal(3, chain.size)
-    assert_equal(@ee2.to_der, chain[0].subject.to_der)
-    assert_equal(@ca2.to_der, chain[1].subject.to_der)
-    assert_equal(@ca1.to_der, chain[2].subject.to_der)
-    assert_equal(false, store.verify(ee3_cert))
-    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
-    assert_match(/expire/i, store.error_string)
-    assert_equal(false, store.verify(ee4_cert))
-    assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
-    assert_match(/not yet valid/i, store.error_string)
+    # The block given to X509::Store#verify replaces it
+    called = nil
+    store = OpenSSL::X509::Store.new
+    store.verify_callback = -> (preverify_ok, sctx) { called = :store; preverify_ok }
+    store.add_cert(ca1_cert)
+    blk = proc { |preverify_ok, sctx| called = :block; preverify_ok }
+    assert_equal(true, store.verify(ee1_cert, [ca2_cert], &blk))
+    assert_equal(:block, called)
+  end
+
+  def test_verify_purpose
+    ca_exts = [
+      ["basicConstraints", "CA:TRUE", true],
+      ["keyUsage", "cRLSign,keyCertSign", true],
+    ]
+    ca1 = OpenSSL::X509::Name.parse_rfc2253("CN=Root CA")
+    ca1_key = Fixtures.pkey("rsa-1")
+    ca1_cert = issue_cert(ca1, ca1_key, 1, ca_exts, nil, nil)
+
+    ee_exts = [
+      ["keyUsage", "keyEncipherment,digitalSignature", true],
+    ]
+    ee1 = OpenSSL::X509::Name.parse_rfc2253("CN=EE 1")
+    ee1_key = Fixtures.pkey("rsa-3")
+    ee1_cert = issue_cert(ee1, ee1_key, 10, ee_exts, ca1_cert, ca1_key)
 
+    # Purpose not set
     store = OpenSSL::X509::Store.new
     store.add_cert(ca1_cert)
-    store.add_cert(ca2_cert)
-    store.time = now + 1500
     assert_equal(true, store.verify(ca1_cert))
-    assert_equal(true, store.verify(ca2_cert))
-    assert_equal(true, store.verify(ee4_cert))
-    store.time = now + 1900
+    assert_equal(true, store.verify(ee1_cert))
+
+    # Purpose set to X509::PURPOSE_SSL_CLIENT; keyUsage is checked
+    store = OpenSSL::X509::Store.new
+    store.purpose = OpenSSL::X509::PURPOSE_CRL_SIGN
+    store.add_cert(ca1_cert)
     assert_equal(true, store.verify(ca1_cert))
-    assert_equal(false, store.verify(ca2_cert))
-    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
-    assert_equal(false, store.verify(ee4_cert))
-    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
-    store.time = now + 4000
     assert_equal(false, store.verify(ee1_cert))
-    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
-    assert_equal(false, store.verify(ee4_cert))
-    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+  end
 
-    # the underlying X509 struct caches the result of the last
-    # verification for signature and not-before. so the following code
-    # rebuilds new objects to avoid site effect.
-    store.time = Time.now - 4000
-    assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ca2_cert)))
+  def test_verify_validity_period
+    # Creating test certificates with validity periods:
+    #
+    #  now-5000                 now-1000    now+1000                  now+5000
+    # CA1:|---------------------------------------------------------------|
+    # EE1:|---------------------------------------------------------------|
+    # EE2:|-------------------------|
+    # EE3:                                      |-------------------------|
+    now = Time.now
+    ca_exts = [
+      ["basicConstraints", "CA:TRUE", true],
+      ["keyUsage", "cRLSign,keyCertSign", true],
+    ]
+    ca1 = OpenSSL::X509::Name.parse_rfc2253("CN=Root CA")
+    ca1_key = Fixtures.pkey("rsa-1")
+    ca1_cert = issue_cert(ca1, ca1_key, 1, ca_exts, nil, nil,
+                          not_before: now - 5000, not_after: now + 5000)
+
+    ee_exts = [
+      ["keyUsage", "keyEncipherment,digitalSignature", true],
+    ]
+    ee1 = OpenSSL::X509::Name.parse_rfc2253("CN=EE 1")
+    ee1_key = Fixtures.pkey("rsa-1")
+    ee1_cert = issue_cert(ee1, ee1_key, 11, ee_exts, ca1_cert, ca1_key,
+                          not_before: now - 5000, not_after: now + 5000)
+    ee2 = OpenSSL::X509::Name.parse_rfc2253("CN=EE 2")
+    ee2_key = Fixtures.pkey("rsa-2")
+    ee2_cert = issue_cert(ee2, ee2_key, 12, ee_exts, ca1_cert, ca1_key,
+                          not_before: now - 5000, not_after: now - 1000)
+    ee3 = OpenSSL::X509::Name.parse_rfc2253("CN=EE 3")
+    ee3_key = Fixtures.pkey("rsa-3")
+    ee3_cert = issue_cert(ee3, ee3_key, 13, ee_exts, ca1_cert, ca1_key,
+                          not_before: now + 1000, not_after: now + 5000)
+
+    # Using system time
+    store = OpenSSL::X509::Store.new
+    store.add_cert(ca1_cert)
+    assert_equal(true, store.verify(ee1_cert))
+    assert_equal(false, store.verify(ee2_cert))
+    assert_equal(OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED, store.error)
+    assert_equal(false, store.verify(ee3_cert))
     assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
-    assert_equal(false, store.verify(OpenSSL::X509::Certificate.new(ee1_cert)))
+
+    # Time set to now-2000; EE2 is still valid
+    store = OpenSSL::X509::Store.new
+    store.time = now - 2000
+    store.add_cert(ca1_cert)
+    assert_equal(true, store.verify(ee1_cert))
+    assert_equal(true, store.verify(ee2_cert))
+    assert_equal(false, store.verify(ee3_cert))
     assert_equal(OpenSSL::X509::V_ERR_CERT_NOT_YET_VALID, store.error)
+  end
+
+  def test_verify_with_crl
+    ca_exts = [
+      ["basicConstraints", "CA:TRUE", true],
+      ["keyUsage", "cRLSign,keyCertSign", true],
+    ]
+    ca1 = OpenSSL::X509::Name.parse_rfc2253("CN=Root CA")
+    ca1_key = Fixtures.pkey("rsa-1")
+    ca1_cert = issue_cert(ca1, ca1_key, 1, ca_exts, nil, nil)
+    ca2 = OpenSSL::X509::Name.parse_rfc2253("CN=Intermediate CA")
+    ca2_key = Fixtures.pkey("rsa-2")
+    ca2_cert = issue_cert(ca2, ca2_key, 2, ca_exts, ca1_cert, ca1_key)
+
+    ee_exts = [
+      ["keyUsage", "keyEncipherment,digitalSignature", true],
+    ]
+    ee1 = OpenSSL::X509::Name.parse_rfc2253("CN=EE 1")
+    ee1_key = Fixtures.pkey("rsa-3")
+    ee1_cert = issue_cert(ee1, ee1_key, 10, ee_exts, ca2_cert, ca2_key)
+    ee2 = OpenSSL::X509::Name.parse_rfc2253("CN=EE 2")
+    ee2_key = Fixtures.pkey("rsa-3")
+    ee2_cert = issue_cert(ee2, ee2_key, 20, ee_exts, ca2_cert, ca2_key)
+
+    # OpenSSL uses time(2) while Time.now uses clock_gettime(CLOCK_REALTIME),
+    # and there may be difference, so giving 50 seconds margin.
+    now = Time.now - 50
+    revoke_info = []
+    ca1_crl1 = issue_crl(revoke_info, 1, now, now+1800, [], ca1_cert, ca1_key, "sha256")
+    revoke_info = [ [2, now, 1], ]
+    ca1_crl2 = issue_crl(revoke_info, 2, now, now+1800, [], ca1_cert, ca1_key, "sha256")
+
+    revoke_info = [ [20, now, 1], ]
+    ca2_crl1 = issue_crl(revoke_info, 1, now, now+1800, [], ca2_cert, ca2_key, "sha256")
+    revoke_info = []
+    ca2_crl2 = issue_crl(revoke_info, 2, now-100, now-1, [], ca2_cert, ca2_key, "sha256")
 
+    # CRL check required, but no CRL supplied
     store = OpenSSL::X509::Store.new
-    store.purpose = OpenSSL::X509::PURPOSE_ANY
     store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
     store.add_cert(ca1_cert)
-    store.add_crl(crl1)   # revoke no cert
-    store.add_crl(crl2)   # revoke ee2_cert
-    assert_equal(true,  store.verify(ca1_cert))
-    assert_equal(true,  store.verify(ca2_cert))
-    assert_equal(true,  store.verify(ee1_cert, [ca2_cert]))
+    assert_equal(false, store.verify(ca2_cert))
+    assert_equal(OpenSSL::X509::V_ERR_UNABLE_TO_GET_CRL, store.error)
+
+    # Intermediate CA revoked EE2
+    store = OpenSSL::X509::Store.new
+    store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
+    store.add_cert(ca1_cert)
+    store.add_crl(ca1_crl1) # revoke no cert
+    store.add_crl(ca2_crl1) # revoke ee2_cert
+    assert_equal(true, store.verify(ca2_cert))
+    assert_equal(true, store.verify(ee1_cert, [ca2_cert]))
     assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
 
+    # Root CA revoked Intermediate CA; Intermediate CA revoked EE2
     store = OpenSSL::X509::Store.new
-    store.purpose = OpenSSL::X509::PURPOSE_ANY
     store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
     store.add_cert(ca1_cert)
-    store.add_crl(crl1_2) # revoke ca2_cert
-    store.add_crl(crl2)   # revoke ee2_cert
-    assert_equal(true,  store.verify(ca1_cert))
+    store.add_crl(ca1_crl2) # revoke ca2_cert
+    store.add_crl(ca2_crl1) # revoke ee2_cert
     assert_equal(false, store.verify(ca2_cert))
-    assert_equal(true,  store.verify(ee1_cert, [ca2_cert]),
-      "This test is expected to be success with OpenSSL 0.9.7c or later.")
+    # Validity of intermediate CAs is not checked by default
+    assert_equal(true, store.verify(ee1_cert, [ca2_cert]))
     assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
 
-    store.flags =
-      OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
-    assert_equal(true,  store.verify(ca1_cert))
+    # Same as above, but with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+    store = OpenSSL::X509::Store.new
+    store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+    store.add_cert(ca1_cert)
+    store.add_crl(ca1_crl2) # revoke ca2_cert
+    store.add_crl(ca2_crl1) # revoke ee2_cert
     assert_equal(false, store.verify(ca2_cert))
     assert_equal(false, store.verify(ee1_cert, [ca2_cert]))
     assert_equal(false, store.verify(ee2_cert, [ca2_cert]))
 
+    # Expired CRL supplied
     store = OpenSSL::X509::Store.new
-    store.purpose = OpenSSL::X509::PURPOSE_ANY
-    store.flags =
-      OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
+    store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK|OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
     store.add_cert(ca1_cert)
     store.add_cert(ca2_cert)
-    store.add_crl(crl1)
-    store.add_crl(crl2_2) # issued by ca2 but expired.
-    assert_equal(true, store.verify(ca1_cert))
+    store.add_crl(ca1_crl1)
+    store.add_crl(ca2_crl2) # issued by ca2 but expired
     assert_equal(true, store.verify(ca2_cert))
     assert_equal(false, store.verify(ee1_cert))
     assert_equal(OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED, store.error)
     assert_equal(false, store.verify(ee2_cert))
   end
 
-  def test_set_errors
+  def test_add_cert_duplicate
+    # Up until OpenSSL 1.1.0, X509_STORE_add_{cert,crl}() returned an error
+    # if the given certificate is already in the X509_STORE
     return if openssl?(1, 1, 0) || libressl?
-    now = Time.now
-    ca1_cert = issue_cert(@ca1, @rsa2048, 1, [], nil, nil)
+    ca1 = OpenSSL::X509::Name.parse_rfc2253("CN=Root CA")
+    ca1_key = Fixtures.pkey("rsa-1")
+    ca1_cert = issue_cert(ca1, ca1_key, 1, [], nil, nil)
     store = OpenSSL::X509::Store.new
     store.add_cert(ca1_cert)
     assert_raise(OpenSSL::X509::StoreError){
       store.add_cert(ca1_cert)  # add same certificate twice
     }
 
+    now = Time.now
     revoke_info = []
     crl1 = issue_crl(revoke_info, 1, now, now+1800, [],
-                     ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                     ca1_cert, ca1_key, "sha256")
     revoke_info = [ [2, now, 1], ]
     crl2 = issue_crl(revoke_info, 2, now+1800, now+3600, [],
-                     ca1_cert, @rsa2048, OpenSSL::Digest::SHA1.new)
+                     ca1_cert, ca1_key, "sha256")
     store.add_crl(crl1)
     assert_raise(OpenSSL::X509::StoreError){
       store.add_crl(crl2) # add CRL issued by same CA twice.
@@ -236,6 +360,14 @@
     ctx = OpenSSL::X509::StoreContext.new(store)
     assert_raise(NoMethodError) { ctx.dup }
   end
+
+  def test_ctx_cleanup
+    # Deprecated in Ruby 1.9.3
+    cert  = OpenSSL::X509::Certificate.new
+    store = OpenSSL::X509::Store.new
+    ctx   = OpenSSL::X509::StoreContext.new(store, cert, [])
+    assert_warning(/cleanup/) { ctx.cleanup }
+  end
 end
 
 end
diff -ruN ruby-2.5.9.orig/test/openssl/ut_eof.rb ruby-2.5.9/test/openssl/ut_eof.rb
--- ruby-2.5.9.orig/test/openssl/ut_eof.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/ut_eof.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,9 +1,17 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
 require 'test/unit'
 
 if defined?(OpenSSL)
 
 module OpenSSL::TestEOF
+  def test_getbyte_eof
+    open_file("") {|f| assert_nil f.getbyte }
+  end
+
+  def test_readbyte_eof
+    open_file("") {|f| assert_raise(EOFError) { f.readbyte } }
+  end
+
   def test_eof_0
     open_file("") {|f|
       assert_equal("", f.read(0))
@@ -18,12 +26,12 @@
       assert_nil(f.read(1))
     }
     open_file("") {|f|
-      s = "x"
+      s = +"x"
       assert_equal("", f.read(nil, s))
       assert_equal("", s)
     }
     open_file("") {|f|
-      s = "x"
+      s = +"x"
       assert_nil(f.read(10, s))
       assert_equal("", s)
     }
@@ -75,12 +83,12 @@
       assert_equal("", f.read(0))
     }
     open_file("a") {|f|
-      s = "x"
+      s = +"x"
       assert_equal("a", f.read(nil, s))
       assert_equal("a", s)
     }
     open_file("a") {|f|
-      s = "x"
+      s = +"x"
       assert_equal("a", f.read(10, s))
       assert_equal("a", s)
     }
diff -ruN ruby-2.5.9.orig/test/openssl/utils.rb ruby-2.5.9/test/openssl/utils.rb
--- ruby-2.5.9.orig/test/openssl/utils.rb	2021-04-05 13:46:35.000000000 +0200
+++ ruby-2.5.9/test/openssl/utils.rb	2025-01-29 19:08:29.936064255 +0100
@@ -1,38 +1,13 @@
-# frozen_string_literal: false
+# 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)
 
@@ -44,25 +19,23 @@
       OpenSSL::PKey.read(read_file("pkey", name))
     end
 
-    def pkey_dh(name)
-      # DH parameters can be read by OpenSSL::PKey.read atm
-      OpenSSL::PKey::DH.new(read_file("pkey", name))
-    end
-
     def read_file(category, name)
       @file_cache ||= {}
       @file_cache[[category, name]] ||=
         File.read(File.join(__dir__, "fixtures", category, name + ".pem"))
     end
+
+    def file_path(category, name)
+      File.join(__dir__, "fixtures", category, name)
+    end
   end
 
   module_function
 
-  def issue_cert(dn, key, serial, extensions, issuer, issuer_key,
-                 not_before: nil, not_after: nil, digest: "sha256")
+  def generate_cert(dn, key, serial, issuer,
+                    not_before: nil, not_after: nil)
     cert = OpenSSL::X509::Certificate.new
     issuer = cert unless issuer
-    issuer_key = key unless issuer_key
     cert.version = 2
     cert.serial = serial
     cert.subject = dn
@@ -71,6 +44,16 @@
     now = Time.now
     cert.not_before = not_before || now - 3600
     cert.not_after = not_after || now + 3600
+    cert
+  end
+
+
+  def issue_cert(dn, key, serial, extensions, issuer, issuer_key,
+                 not_before: nil, not_after: nil, digest: "sha256")
+    cert = generate_cert(dn, key, serial, issuer,
+                         not_before: not_before, not_after: not_after)
+    issuer = cert unless issuer
+    issuer_key = key unless issuer_key
     ef = OpenSSL::X509::ExtensionFactory.new
     ef.subject_certificate = cert
     ef.issuer_certificate = issuer
@@ -109,20 +92,26 @@
     crl
   end
 
-  def get_subject_key_id(cert)
+  def get_subject_key_id(cert, hex: true)
     asn1_cert = OpenSSL::ASN1.decode(cert)
     tbscert   = asn1_cert.value[0]
     pkinfo    = tbscert.value[6]
     publickey = pkinfo.value[1]
     pkvalue   = publickey.value
-    OpenSSL::Digest::SHA1.hexdigest(pkvalue).scan(/../).join(":").upcase
+    digest = OpenSSL::Digest.digest('SHA1', pkvalue)
+    if hex
+      digest.unpack("H2"*20).join(":").upcase
+    else
+      digest
+    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)
@@ -135,6 +124,7 @@
 class OpenSSL::TestCase < Test::Unit::TestCase
   include OpenSSL::TestUtils
   extend OpenSSL::TestUtils
+  include Test::Unit::CoreAssertions
 
   def setup
     if ENV["OSSL_GC_STRESS"] == "1"
@@ -149,6 +139,30 @@
     # 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 <<~MESSAGE
+      Only for OpenSSL non-FIPS with the following possible reasons:
+      * A testing logic is non-FIPS specific.
+      * An encryption used in the test is not FIPS-approved.
+    MESSAGE
+  end
+
+  def omit_on_non_fips
+    return if OpenSSL.fips_mode
+
+    omit "Only for OpenSSL FIPS"
+  end
 end
 
 class OpenSSL::SSLTestCase < OpenSSL::TestCase
@@ -157,9 +171,9 @@
 
   def setup
     super
-    @ca_key  = Fixtures.pkey("rsa2048")
-    @svr_key = Fixtures.pkey("rsa1024")
-    @cli_key = Fixtures.pkey("rsa2048")
+    @ca_key  = Fixtures.pkey("rsa-1")
+    @svr_key = Fixtures.pkey("rsa-2")
+    @cli_key = Fixtures.pkey("rsa-3")
     @ca  = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=CA")
     @svr = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
     @cli = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
@@ -176,9 +190,10 @@
     @server = nil
   end
 
-  def tls12_supported?
+  def tls13_supported?
+    return false unless defined?(OpenSSL::SSL::TLS1_3_VERSION)
     ctx = OpenSSL::SSL::SSLContext.new
-    ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
+    ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION
     true
   rescue
   end
@@ -191,6 +206,7 @@
 
   def start_server(verify_mode: OpenSSL::SSL::VERIFY_NONE, start_immediately: true,
                    ctx_proc: nil, server_proc: method(:readwrite_loop),
+                   accept_proc: proc{},
                    ignore_listener_error: false, &block)
     IO.pipe {|stop_pipe_r, stop_pipe_w|
       store = OpenSSL::X509::Store.new
@@ -200,7 +216,6 @@
       ctx.cert_store = store
       ctx.cert = @svr_cert
       ctx.key = @svr_key
-      ctx.tmp_dh_callback = proc { Fixtures.pkey_dh("dh1024") }
       ctx.verify_mode = verify_mode
       ctx_proc.call(ctx) if ctx_proc
 
@@ -214,9 +229,7 @@
       threads = []
       begin
         server_thread = Thread.new do
-          if Thread.method_defined?(:report_on_exception=) # Ruby >= 2.4
-            Thread.current.report_on_exception = false
-          end
+          Thread.current.report_on_exception = false
 
           begin
             loop do
@@ -224,6 +237,7 @@
                 readable, = IO.select([ssls, stop_pipe_r])
                 break if readable.include? stop_pipe_r
                 ssl = ssls.accept
+                accept_proc.call(ssl)
               rescue OpenSSL::SSL::SSLError, IOError, Errno::EBADF, Errno::EINVAL,
                      Errno::ECONNABORTED, Errno::ENOTSOCK, Errno::ECONNRESET
                 retry if ignore_listener_error
@@ -231,9 +245,7 @@
               end
 
               th = Thread.new do
-                if Thread.method_defined?(:report_on_exception=)
-                  Thread.current.report_on_exception = false
-                end
+                Thread.current.report_on_exception = false
 
                 begin
                   server_proc.call(ctx, ssl)
@@ -250,9 +262,7 @@
         end
 
         client_thread = Thread.new do
-          if Thread.method_defined?(:report_on_exception=)
-            Thread.current.report_on_exception = false
-          end
+          Thread.current.report_on_exception = false
 
           begin
             block.call(port)
@@ -268,10 +278,10 @@
         pend = nil
         threads.each { |th|
           begin
-            th.join(10) or
-              th.raise(RuntimeError, "[start_server] thread did not exit in 10 secs")
-          rescue (defined?(MiniTest::Skip) ? MiniTest::Skip : Test::Unit::PendedError)
-            # MiniTest::Skip is for the Ruby tree
+            timeout = EnvUtil.apply_timeout_scale(30)
+            th.join(timeout) or
+              th.raise(RuntimeError, "[start_server] thread did not exit in #{timeout} secs")
+          rescue Test::Unit::PendedError
             pend = $!
           rescue Exception
           end
@@ -289,31 +299,63 @@
       assert_equal base.send(comp), test.send(comp)
     }
   end
+end
 
-  def dup_public(key)
-    case key
-    when OpenSSL::PKey::RSA
-      rsa = OpenSSL::PKey::RSA.new
-      rsa.set_key(key.n, key.e, nil)
-      rsa
-    when OpenSSL::PKey::DSA
-      dsa = OpenSSL::PKey::DSA.new
-      dsa.set_pqg(key.p, key.q, key.g)
-      dsa.set_key(key.pub_key, nil)
-      dsa
-    when OpenSSL::PKey::DH
-      dh = OpenSSL::PKey::DH.new
-      dh.set_pqg(key.p, nil, key.g)
-      dh
-    else
-      if defined?(OpenSSL::PKey::EC) && OpenSSL::PKey::EC === key
-        ec = OpenSSL::PKey::EC.new(key.group)
-        ec.public_key = key.public_key
-        ec
-      else
-        raise "unknown key type"
-      end
-    end
+module OpenSSL::Certs
+  include OpenSSL::TestUtils
+
+  module_function
+
+  def ca_cert
+    ca = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=Timestamp Root CA")
+
+    ca_exts = [
+      ["basicConstraints","CA:TRUE,pathlen:1",true],
+      ["keyUsage","keyCertSign, cRLSign",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+    ]
+    OpenSSL::TestUtils.issue_cert(ca, Fixtures.pkey("rsa2048"), 1, ca_exts, nil, nil)
+  end
+
+  def ts_cert_direct(key, ca_cert)
+    dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Server Direct")
+
+    exts = [
+      ["basicConstraints","CA:FALSE",true],
+      ["keyUsage","digitalSignature, nonRepudiation", true],
+      ["subjectKeyIdentifier", "hash",false],
+      ["authorityKeyIdentifier","keyid,issuer", false],
+      ["extendedKeyUsage", "timeStamping", true]
+    ]
+
+    OpenSSL::TestUtils.issue_cert(dn, key, 2, exts, ca_cert, Fixtures.pkey("rsa2048"))
+  end
+
+  def intermediate_cert(key, ca_cert)
+    dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Timestamp Intermediate CA")
+
+    exts = [
+      ["basicConstraints","CA:TRUE,pathlen:0",true],
+      ["keyUsage","keyCertSign, cRLSign",true],
+      ["subjectKeyIdentifier","hash",false],
+      ["authorityKeyIdentifier","keyid:always",false],
+    ]
+
+    OpenSSL::TestUtils.issue_cert(dn, key, 3, exts, ca_cert, Fixtures.pkey("rsa2048"))
+  end
+
+  def ts_cert_ee(key, intermediate, im_key)
+    dn = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/OU=Timestamp/CN=Server End Entity")
+
+    exts = [
+      ["keyUsage","digitalSignature, nonRepudiation", true],
+      ["subjectKeyIdentifier", "hash",false],
+      ["authorityKeyIdentifier","keyid,issuer", false],
+      ["extendedKeyUsage", "timeStamping", true]
+    ]
+
+    OpenSSL::TestUtils.issue_cert(dn, key, 4, exts, intermediate, im_key)
   end
 end
 
diff -ruN ruby-2.5.9.orig/tool/openssl_fips.cnf.tmpl ruby-2.5.9/tool/openssl_fips.cnf.tmpl
--- ruby-2.5.9.orig/tool/openssl_fips.cnf.tmpl	1970-01-01 01:00:00.000000000 +0100
+++ ruby-2.5.9/tool/openssl_fips.cnf.tmpl	2025-01-29 19:09:32.898541869 +0100
@@ -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
openSUSE Build Service is sponsored by