Upgrade openssl to 0.10.64

This project was upgraded with external_updater.
Usage: tools/external_updater/updater.sh update external/rust/crates/openssl
For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md

Test: TreeHugger
Change-Id: I34603ece2b7dd40a0320c5e4484a16b6da433745
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 5f331c9..7788324 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "7df56869c5e1e32369091ab106750d644d3aa0c4"
+    "sha1": "4a19cd48259e0755d9a9067f4c1a51ee63844c66"
   },
   "path_in_vcs": "openssl"
 }
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index dce55fb..86faf15 100644
--- a/Android.bp
+++ b/Android.bp
@@ -50,7 +50,7 @@
     host_supported: true,
     crate_name: "openssl",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.10.45",
+    cargo_pkg_version: "0.10.64",
     srcs: ["src/lib.rs"],
     edition: "2018",
     features: ["unstable_boringssl"],
@@ -59,7 +59,7 @@
         "soong",
     ],
     rustlibs: [
-        "libbitflags-1.3.2",
+        "libbitflags",
         "libbssl_sys",
         "libcfg_if",
         "libforeign_types",
@@ -79,7 +79,7 @@
     name: "libopenssl_static",
     crate_name: "openssl",
     cargo_env_compat: true,
-    cargo_pkg_version: "0.10.45",
+    cargo_pkg_version: "0.10.64",
     srcs: ["src/lib.rs"],
     edition: "2018",
     features: ["unstable_boringssl"],
@@ -88,7 +88,7 @@
         "soong",
     ],
     rustlibs: [
-        "libbitflags-1.3.2",
+        "libbitflags",
         "libcfg_if",
         "libforeign_types",
         "liblibc",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0af50bc..2f72808 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,235 @@
 
 ## [Unreleased]
 
+## [v0.10.64] - 2024-02-19
+
+### Added
+
+* Added `PkeyCtxRef::{nonce_type, set_nonce_type}`.
+* Added `X509Ref::alias`.
+
+
+## [v0.10.63] - 2024-01-19
+
+### Added
+
+* Added `Pkcs7Ref::{type_,signed}`.
+* Added `Pkcs7SignedRef::certificates`.
+* Added `Cipher::{aes_256_xts,des_ede3_ecb,des_ede3_cfb8,des_ede3_ofb,camellia128_ofb,camellia192_ofb,camellia256_ofb,cast5_ofb,idea_ofb}`
+* Added `PKey::from_dhx`
+* Added `PKey::{public_key_from_pem_passphrase,public_key_from_pem_callback}`.
+
+### Changed
+
+* `Cipher::aes_128_ofb` is now available on BoringSSL
+* `Nid::{BRAINPOOL_P256R1,BRAINPOOL_P320R1,BRAINPOOL_P384R1,BRAINPOOL_P512R1}` are now available on LibreSSL.
+
+## [v0.10.62] - 2023-12-22
+
+### Added
+
+* Added `Nid::BRAINPOOL_P320R1`
+* Added `rand_priv_bytes`
+
+### Fixed
+
+* Fixed building on the latest version of BoringSSL
+
+## [v0.10.61] - 2023-12-04
+
+### Changed
+
+* `SslStream` now uses `SSL_read_ex`, `SSL_write_ex`, and `SSL_peek_ex` when available
+
+### Added
+
+* Added `SslStream::{read_uninit, ssl_read_uninit}`.
+
+## [v0.10.60] - 2023-11-22
+
+### Deprecated
+
+* Deprecated `X509StoreRef::objects`. It is unsound. All callers should migrate to using `X509StoreRef::all_certificates` instead.
+
+### Fixed
+
+* Fixed a memory leak when calling `SslContextBuilder::set_ex_data` and `SslRef::set_ex_data` multiple times with the same index.
+
+### Added
+
+* Added `X509StoreRef::all_certificates`
+* Added `cipher::Cipher::{camellia128_cbc,camellia192_cbc,camellia256_cbc,cast5_cbc,idea_cbc}`
+* Added `symm::Cipher::{des_ede3_ecb,des_ede3_cfb8,des_ede3_ofb,camellia_128_ecb,camellia_128_ofb,camellia_128_cfb128,camellia_192_ecb,camellia_192_ofb,camellia_192_cfb128,camellia_256_ecb,camellia_256_ofb,camellia_256_cfb128,cast5_ecb,cast5_ofb,cast5_cfb64,idea_ecb,idea_ofb,idea_cfb64}`
+* Added `Crypter::update_unchecked`
+* Added `SslRef::{peer_tmp_key,tmp_key}`
+
+### Changed
+
+* `cipher::Cipher::chacha20` is now available on LibreSSL
+* `symm::Cipher::chacha20` is now available on LibreSSL
+
+## [v0.10.59] - 2023-11-03
+
+### Added
+
+* Added `Nid::CHACHA20_POLY1305`
+
+### Changed
+
+* Fixed the availability of `Id::RSA_PSS` on OpenSSL
+
+## [v0.10.58] - 2023-11-01
+
+### Added
+
+* Added `Id::{RSA_PSS,DHX}` constants
+* Added `SslContextBuilder::set_security_level`
+* Added `SslContextRef::security_level`
+* Added `SslRef::set_security_level`, `SslRef::security_level`
+* Added `Cipher::{camellia_128_cbc, camellia_192_cbc, camellia_256_cbc, cast5_cbc, idea_cbc}`
+* Added `X509CrlRef::extension`
+* Added `X509PurposeId::CODE_SIGN`
+
+### Changed
+
+* `Pkey` HKDF functionality now works on LibreSSL
+* `BigNum::mod_sqrt` is now available on all OpenSSLs
+* `MessageDigest::sha3*` are now available on LibreSSL
+
+## [v0.10.57] - 2023-08-27
+
+### Added
+* Added `X509VerifyParam::set_email`
+* `Cipher::chacha20_poly1305` is now available on LibreSSL
+* Added `CipherCtx::copy`
+
+### Changed
+* Updated `bitflags` dependecy to the 2.x series
+
+## [v0.10.56] - 2023-08-06
+
+## Added
+
+* Added `BigNumRef::mod_sqrt`.
+* Added `PkeyCtxRef::set_signature_md` and `PkeyCtxRef::set_rsa_pss_saltlen`.
+* Added `PkeyCtxRef::verify_recover_init` and `PkeyCtxRef::verify_recover`.
+* Added `BigNumRef::is_even` and `BigNumRef::is_odd`.
+* Added `EcPointRef::to_hex_str` and `EcPoint::from_hex_str`.
+* Added support for AES key wrap and wrap pad.
+
+## [v0.10.55] - 2023-06-20
+
+### Fixed
+
+* Fixed compilation with the latest version of BoringSSL.
+* Fixed compilation when OpenSSL is compiled with `OPENSSL_NO_OCB`.
+* Fixed a segfault in `X509VerifyParamRef::set_host` when called with an empty string.
+
+### Added
+
+* Added `Deriver::set_peer_ex`.
+* Added `EcGroupRef::asn1_flag`.
+* Exposed `EcPointRef::affine_coordinates` on BoringSSL and LibreSSL.
+* Added `Nid::SM2` and `Id::SM2`
+
+## [v0.10.54] - 2023-05-31
+
+### Fixed
+
+* `PKey::private_key_to_pkcs8_passphrase` no longer panics if a `passphrase` contains a NUL byte.
+
+## [v0.10.53] - 2023-05-30
+
+### Added
+
+* Added `Dsa::from_pqg`, `Dsa::generate_key`, and `Dsa::generate_params`.
+* Added `SslRef::bytes_to_cipher_list`.
+* Added `SubjectAlternativeName::other_name2`
+
+## [v0.10.52] - 2023-04-24
+
+### Added
+
+* Added `DhRef::check_key`.
+* Added `Id::POLY1305`.
+* Added `X509Ref::subject_key_id`, `X509Ref::authority_key_id`, `X509Ref::authority_issuer`, and `X509Ref::authority_serial`.
+
+
+## [v0.10.51] - 2023-04-20
+
+### Added
+
+* Added `X509RevokedRef::issuer_name` and `X509RevokedRef::reason_code`.
+* Added `Dh::set_key` and `Dh::set_public_key`
+* Added `Asn1OctetString` and `Asn1OctetStringRef1`
+* Added `X509Extension::new_from_der`
+
+### Deprecated
+
+* Deprecated `X509Extension::new` and `X509Extension::new_nid` in favor of `X509Extension::new_from_der` and the `extensions` module.
+* Deprecated `X509Extension::add_alias`, it is not required with `new_from_der` or the `extensions` module.
+
+## [v0.10.50] - 2023-04-09
+
+### Added
+
+* Added `CipherCtxRef::cipher_update_inplace`.
+
+## [v0.10.49] - 2023-04-01
+
+### Fixed
+
+* `SslConnector` no longer sets the SNI extension when connecting to an IP address.
+
+### Added
+
+* Implemented `Ord`, `PartialOrd`, `Eq`, and `PartialEq` for `Asn1Integer` and `Asn1IntegerRef`.
+* Added `X509Ref::crl_distribution_points`, and `DistPoint`.
+
+## [v0.10.48] - 2023-03-23
+
+### Fixed
+
+* Fixed injection vulnerabilities where OpenSSL's configuration mini-language could be used via `x509::extension::SubjectAlternativeName` and `x509::extension::ExtendedKeyUsage`. The mini-language can read arbitrary files amongst other things.
+  * As part of fixing this `SubjectAlternativeName::dir_name` and `SubjectAlternativeName::other_name` are deprecated and their implementations always `panic!`. If you have a use case for these, please file an issue.
+* Fixed several NULL pointer dereferences in OpenSSL that could be triggered via `x509::X509Extension::new` and `x509::X509Extension::new_nid`. Note that these methods still accept OpenSSL's configuration mini-language, and therefore should not be used with untrusted data.
+* Fixed a data-race with `x509::X509Name` that are created with `x509::X509NameBuilder` and then used concurrently.
+* Fixed LibreSSL version checking. More functions should now be correctly available on LibreSSL.
+
+## [v0.10.47] - 2023-03-19
+
+### Added
+
+* Added support for X25519 and Ed25519 on LibreSSL and BoringSSL.
+* Added `Error::library_code` and `Error::reason_code`.
+
+## [v0.10.46] - 2023-03-14
+
+### Fixed
+
+* Fixed a potential null-pointer deref when parsing a PKCS#12 archive with no identity.
+* Fixed builds against OpenSSL built with `no-cast`.
+* Fixed debug formatting of `GeneralName`.
+
+### Deprecated
+
+* Deprecated `PKcs12Ref::parse` in favor of `Pkcs12Ref::parse2`.
+* Deprecated `ParsedPkcs12` in favor of `ParsedPkcs12_2`.
+* Deprecated `Pkcs12Builder::build` in favor of `Pkcs12Builder::build2`.
+
+### Added
+
+* Added `X509VerifyParamRef::set_auth_level`, `X509VerifyParamRef::auth_level`, and `X509VerifyParamRef::set_purpose`.
+* Added `X509PurposeId` and `X509Purpose`.
+* Added `X509NameBuilder::append_entry`.
+* Added `PKeyRef::private_key_to_pkcs8`.
+* Added `X509LookupRef::load_crl_file`.
+* Added `Pkcs12Builder::name`, `Pkcs12Builder::pkey`, and `Pkcs12Builder::cert`.
+* Added `SslRef::set_method`, `SslRef::set_private_key_file`, `SslRef::set_private_key`, `SslRef::set_certificate`, `SslRef::set_certificate_chain_file`, `SslRef::add_client_ca`, `SslRef::set_client_ca_list`, `SslRef::set_min_proto_version`, `SslREf::set_max_proto_version`, `SslRef::set_ciphersuites`, `SslRef::set_cipher_list`, `SslRef::set_verify_cert_store`.
+* Added `X509NameRef::to_owned`.
+* Added `SslContextBuilder::set_num_tickets`, `SslContextRef::num_tickets`, `SslRef::set_num_tickets`, and `SslRef::num_tickets`.
+* Added `CmsContentInfo::verify`.
+
 ## [v0.10.45] - 2022-12-20
 
 ### Fixed
@@ -663,7 +892,26 @@
 
 Look at the [release tags] for information about older releases.
 
-[Unreleased]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.45...master
+[Unreleased]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.64...master
+[v0.10.64]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.63...openssl-v0.10.64
+[v0.10.63]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.62...openssl-v0.10.63
+[v0.10.62]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.61...openssl-v0.10.62
+[v0.10.61]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.60...openssl-v0.10.61
+[v0.10.60]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.59...openssl-v0.10.60
+[v0.10.59]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.58...openssl-v0.10.59
+[v0.10.58]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.57...openssl-v0.10.58
+[v0.10.57]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.56...openssl-v0.10.57
+[v0.10.56]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.55...openssl-v0.10.56
+[v0.10.55]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.54...openssl-v0.10.55
+[v0.10.54]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.53...openssl-v0.10.54
+[v0.10.53]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.52...openssl-v0.10.53
+[v0.10.52]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.51...openssl-v0.10.52
+[v0.10.51]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.50...openssl-v0.10.51
+[v0.10.50]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.49...openssl-v0.10.50
+[v0.10.49]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.48...openssl-v0.10.49
+[v0.10.48]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.47...openssl-v0.10.48
+[v0.10.47]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.46...openssl-v0.10.47
+[v0.10.46]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.45...openssl-v0.10.46
 [v0.10.45]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.44...openssl-v0.10.45
 [v0.10.44]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.43...openssl-v0.10.44
 [v0.10.43]: https://github.com/sfackler/rust-openssl/compare/openssl-v0.10.42...openssl-v0.10.43
diff --git a/Cargo.lock b/Cargo.lock
index ad61be1..d08a6b7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,51 +3,36 @@
 version = 3
 
 [[package]]
-name = "aho-corasick"
-version = "0.7.20"
+name = "annotate-snippets"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e"
 dependencies = [
- "memchr",
+ "unicode-width",
+ "yansi-term",
 ]
 
 [[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi",
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "autocfg"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
-
-[[package]]
 name = "bindgen"
-version = "0.60.1"
+version = "0.65.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
+checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5"
 dependencies = [
- "bitflags",
+ "annotate-snippets",
+ "bitflags 1.3.2",
  "cexpr",
  "clang-sys",
- "clap",
- "env_logger",
  "lazy_static",
  "lazycell",
  "log",
  "peeking_take_while",
+ "prettyplease",
  "proc-macro2",
  "quote",
  "regex",
  "rustc-hash",
  "shlex",
+ "syn",
  "which",
 ]
 
@@ -58,14 +43,25 @@
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
 name = "bssl-sys"
 version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "312d12393c060384f2e6ed14c7b4be37b3dd90249857485613c1a91b9a1abb5c"
 
 [[package]]
 name = "cc"
-version = "1.0.78"
+version = "1.0.83"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "cexpr"
@@ -84,9 +80,9 @@
 
 [[package]]
 name = "clang-sys"
-version = "1.4.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3"
+checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
 dependencies = [
  "glob",
  "libc",
@@ -94,47 +90,10 @@
 ]
 
 [[package]]
-name = "clap"
-version = "3.2.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
-dependencies = [
- "atty",
- "bitflags",
- "clap_lex",
- "indexmap",
- "strsim",
- "termcolor",
- "textwrap",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
-dependencies = [
- "os_str_bytes",
-]
-
-[[package]]
 name = "either"
-version = "1.8.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
-
-[[package]]
-name = "env_logger"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
-dependencies = [
- "atty",
- "humantime",
- "log",
- "regex",
- "termcolor",
-]
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
 
 [[package]]
 name = "foreign-types"
@@ -153,24 +112,9 @@
 
 [[package]]
 name = "glob"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
-
-[[package]]
-name = "hashbrown"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "hex"
@@ -179,22 +123,6 @@
 checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
 
 [[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
-[[package]]
-name = "indexmap"
-version = "1.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
-dependencies = [
- "autocfg",
- "hashbrown",
-]
-
-[[package]]
 name = "lazy_static"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -208,9 +136,9 @@
 
 [[package]]
 name = "libc"
-version = "0.2.138"
+version = "0.2.150"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
 
 [[package]]
 name = "libloading"
@@ -224,12 +152,9 @@
 
 [[package]]
 name = "log"
-version = "0.4.17"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 [[package]]
 name = "memchr"
@@ -245,9 +170,9 @@
 
 [[package]]
 name = "nom"
-version = "7.1.1"
+version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
 dependencies = [
  "memchr",
  "minimal-lexical",
@@ -255,15 +180,15 @@
 
 [[package]]
 name = "once_cell"
-version = "1.16.0"
+version = "1.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
 
 [[package]]
 name = "openssl"
-version = "0.10.45"
+version = "0.10.64"
 dependencies = [
- "bitflags",
+ "bitflags 2.4.1",
  "cfg-if",
  "foreign-types",
  "hex",
@@ -275,9 +200,9 @@
 
 [[package]]
 name = "openssl-macros"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -286,20 +211,19 @@
 
 [[package]]
 name = "openssl-src"
-version = "111.24.0+1.1.1s"
+version = "300.1.6+3.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3498f259dab01178c6228c6b00dcef0ed2a2d5e20d648c017861227773ea4abd"
+checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085"
 dependencies = [
  "cc",
 ]
 
 [[package]]
 name = "openssl-sys"
-version = "0.9.80"
+version = "0.9.100"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
+checksum = "ae94056a791d0e1217d18b6cbdccb02c61e3054fc69893607f4067e3bb0b1fd1"
 dependencies = [
- "autocfg",
  "bindgen",
  "bssl-sys",
  "cc",
@@ -310,12 +234,6 @@
 ]
 
 [[package]]
-name = "os_str_bytes"
-version = "6.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
-
-[[package]]
 name = "peeking_take_while"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -323,44 +241,52 @@
 
 [[package]]
 name = "pkg-config"
-version = "0.3.26"
+version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.49"
+version = "1.0.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
+checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.23"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "regex"
-version = "1.7.0"
+version = "1.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
 dependencies = [
- "aho-corasick",
- "memchr",
  "regex-syntax",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.28"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
 
 [[package]]
 name = "rustc-hash"
@@ -370,21 +296,15 @@
 
 [[package]]
 name = "shlex"
-version = "1.1.0"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
-
-[[package]]
-name = "strsim"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
 
 [[package]]
 name = "syn"
-version = "1.0.107"
+version = "2.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -392,25 +312,16 @@
 ]
 
 [[package]]
-name = "termcolor"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
-
-[[package]]
 name = "unicode-ident"
-version = "1.0.6"
+version = "1.0.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
 
 [[package]]
 name = "vcpkg"
@@ -420,9 +331,9 @@
 
 [[package]]
 name = "which"
-version = "4.3.0"
+version = "4.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
 dependencies = [
  "either",
  "libc",
@@ -446,16 +357,16 @@
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
-name = "winapi-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi",
-]
-
-[[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "yansi-term"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
+dependencies = [
+ "winapi",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 43bdd3b..d62f90b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
 [package]
 edition = "2018"
 name = "openssl"
-version = "0.10.45"
+version = "0.10.64"
 authors = ["Steven Fackler <sfackler@gmail.com>"]
 description = "OpenSSL bindings"
 readme = "README.md"
@@ -30,13 +30,13 @@
 repository = "https://github.com/sfackler/rust-openssl"
 
 [dependencies.bitflags]
-version = "1.0"
+version = "2.2.1"
 
 [dependencies.cfg-if]
 version = "1.0"
 
 [dependencies.ffi]
-version = "0.9.80"
+version = "0.9.100"
 package = "openssl-sys"
 
 [dependencies.foreign-types]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 1fd2444..b852549 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "openssl"
-version = "0.10.45"
+version = "0.10.64"
 authors = ["Steven Fackler <sfackler@gmail.com>"]
 license = "Apache-2.0"
 description = "OpenSSL bindings"
@@ -23,14 +23,14 @@
 default = []
 
 [dependencies]
-bitflags = "1.0"
+bitflags = "2.2.1"
 cfg-if = "1.0"
 foreign-types = "0.3.1"
 libc = "0.2"
 once_cell = "1.5.2"
 
 openssl-macros = { version = "0.1.0", path = "../openssl-macros" }
-ffi = { package = "openssl-sys", version = "0.9.80", path = "../openssl-sys" }
+ffi = { package = "openssl-sys", version = "0.9.100", path = "../openssl-sys" }
 
 [dev-dependencies]
 hex = "0.3"
diff --git a/METADATA b/METADATA
index 72a81ff..e3d86cb 100644
--- a/METADATA
+++ b/METADATA
@@ -1,23 +1,20 @@
 # This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update rust/crates/openssl
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+# Usage: tools/external_updater/updater.sh update external/rust/crates/openssl
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
 
 name: "openssl"
 description: "OpenSSL bindings"
 third_party {
-  url {
-    type: HOMEPAGE
-    value: "https://crates.io/crates/openssl"
-  }
-  url {
-    type: ARCHIVE
-    value: "https://static.crates.io/crates/openssl/openssl-0.10.43.crate"
-  }
-  version: "0.10.43"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 12
-    day: 1
+    year: 2024
+    month: 3
+    day: 6
+  }
+  homepage: "https://crates.io/crates/openssl"
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/openssl/openssl-0.10.64.crate"
+    version: "0.10.64"
   }
 }
diff --git a/build.rs b/build.rs
index fc64922..7677abc 100644
--- a/build.rs
+++ b/build.rs
@@ -1,4 +1,8 @@
-#![allow(clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings)]
+#![allow(
+    clippy::inconsistent_digit_grouping,
+    clippy::uninlined_format_args,
+    clippy::unusual_byte_groupings
+)]
 
 use std::env;
 
@@ -7,13 +11,70 @@
         println!("cargo:rustc-cfg=libressl");
     }
 
-    if env::var("CARGO_FEATURE_UNSTABLE_BORINGSSL").is_ok() {
+    if env::var("DEP_OPENSSL_BORINGSSL").is_ok() {
         println!("cargo:rustc-cfg=boringssl");
-        return;
     }
 
-    if let Ok(v) = env::var("DEP_OPENSSL_LIBRESSL_VERSION") {
-        println!("cargo:rustc-cfg=libressl{}", v);
+    if let Ok(v) = env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER") {
+        let version = u64::from_str_radix(&v, 16).unwrap();
+
+        if version >= 0x2_05_00_00_0 {
+            println!("cargo:rustc-cfg=libressl250");
+        }
+        if version >= 0x2_05_01_00_0 {
+            println!("cargo:rustc-cfg=libressl251");
+        }
+        if version >= 0x2_06_01_00_0 {
+            println!("cargo:rustc-cfg=libressl261");
+        }
+        if version >= 0x2_07_00_00_0 {
+            println!("cargo:rustc-cfg=libressl270");
+        }
+        if version >= 0x2_07_01_00_0 {
+            println!("cargo:rustc-cfg=libressl271");
+        }
+        if version >= 0x2_07_03_00_0 {
+            println!("cargo:rustc-cfg=libressl273");
+        }
+        if version >= 0x2_08_00_00_0 {
+            println!("cargo:rustc-cfg=libressl280");
+        }
+        if version >= 0x2_09_01_00_0 {
+            println!("cargo:rustc-cfg=libressl291");
+        }
+        if version >= 0x3_01_00_00_0 {
+            println!("cargo:rustc-cfg=libressl310");
+        }
+        if version >= 0x3_02_01_00_0 {
+            println!("cargo:rustc-cfg=libressl321");
+        }
+        if version >= 0x3_03_02_00_0 {
+            println!("cargo:rustc-cfg=libressl332");
+        }
+        if version >= 0x3_04_00_00_0 {
+            println!("cargo:rustc-cfg=libressl340");
+        }
+        if version >= 0x3_05_00_00_0 {
+            println!("cargo:rustc-cfg=libressl350");
+        }
+        if version >= 0x3_06_00_00_0 {
+            println!("cargo:rustc-cfg=libressl360");
+        }
+        if version >= 0x3_06_01_00_0 {
+            println!("cargo:rustc-cfg=libressl361");
+        }
+        if version >= 0x3_07_00_00_0 {
+            println!("cargo:rustc-cfg=libressl370");
+        }
+        if version >= 0x3_08_00_00_0 {
+            println!("cargo:rustc-cfg=libressl380");
+        }
+        if version >= 0x3_08_02_00_0 {
+            println!("cargo:rustc-cfg=libressl382");
+        }
+        if version >= 0x3_09_00_00_0 {
+            println!("cargo:rustc-cfg=libressl390");
+        }
     }
 
     if let Ok(vars) = env::var("DEP_OPENSSL_CONF") {
@@ -46,61 +107,11 @@
         if version >= 0x3_00_00_00_0 {
             println!("cargo:rustc-cfg=ossl300");
         }
-    }
-
-    if let Ok(version) = env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER") {
-        let version = u64::from_str_radix(&version, 16).unwrap();
-
-        if version >= 0x2_05_01_00_0 {
-            println!("cargo:rustc-cfg=libressl251");
+        if version >= 0x3_01_00_00_0 {
+            println!("cargo:rustc-cfg=ossl310");
         }
-
-        if version >= 0x2_06_01_00_0 {
-            println!("cargo:rustc-cfg=libressl261");
-        }
-
-        if version >= 0x2_07_00_00_0 {
-            println!("cargo:rustc-cfg=libressl270");
-        }
-
-        if version >= 0x2_07_01_00_0 {
-            println!("cargo:rustc-cfg=libressl271");
-        }
-
-        if version >= 0x2_07_03_00_0 {
-            println!("cargo:rustc-cfg=libressl273");
-        }
-
-        if version >= 0x2_08_00_00_0 {
-            println!("cargo:rustc-cfg=libressl280");
-        }
-
-        if version >= 0x2_09_01_00_0 {
-            println!("cargo:rustc-cfg=libressl291");
-        }
-
-        if version >= 0x3_02_01_00_0 {
-            println!("cargo:rustc-cfg=libressl321");
-        }
-
-        if version >= 0x3_03_02_00_0 {
-            println!("cargo:rustc-cfg=libressl332");
-        }
-
-        if version >= 0x3_04_00_00_0 {
-            println!("cargo:rustc-cfg=libressl340");
-        }
-
-        if version >= 0x3_05_00_00_0 {
-            println!("cargo:rustc-cfg=libressl350");
-        }
-
-        if version >= 0x3_06_00_00_0 {
-            println!("cargo:rustc-cfg=libressl360");
-        }
-
-        if version >= 0x3_06_01_00_0 {
-            println!("cargo:rustc-cfg=libressl361");
+        if version >= 0x3_02_00_00_0 {
+            println!("cargo:rustc-cfg=ossl320");
         }
     }
 }
diff --git a/examples/mk_certs.rs b/examples/mk_certs.rs
index e944af0..48538c7 100644
--- a/examples/mk_certs.rs
+++ b/examples/mk_certs.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::uninlined_format_args)]
+
 //! A program that generates ca certs, certs verified by the ca, and public
 //! and private keys.
 
diff --git a/src/asn1.rs b/src/asn1.rs
index 939a173..8618be0 100644
--- a/src/asn1.rs
+++ b/src/asn1.rs
@@ -27,8 +27,8 @@
 use cfg_if::cfg_if;
 use foreign_types::{ForeignType, ForeignTypeRef};
 use libc::{c_char, c_int, c_long, time_t};
-#[cfg(ossl102)]
 use std::cmp::Ordering;
+use std::convert::TryInto;
 use std::ffi::CString;
 use std::fmt;
 use std::ptr;
@@ -39,6 +39,7 @@
 use crate::bn::{BigNum, BigNumRef};
 use crate::error::ErrorStack;
 use crate::nid::Nid;
+use crate::stack::Stackable;
 use crate::string::OpensslString;
 use crate::{cvt, cvt_p};
 use openssl_macros::corresponds;
@@ -165,7 +166,7 @@
 /// [`diff`]: struct.Asn1TimeRef.html#method.diff
 /// [`Asn1TimeRef`]: struct.Asn1TimeRef.html
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 pub struct TimeDiff {
     /// Difference in days
     pub days: c_int,
@@ -187,7 +188,7 @@
     /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementation
     /// used by OpenSSL.
     ///
-    /// [ASN_TIME_set]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_set.html
+    /// [ASN_TIME_set]: https://www.openssl.org/docs/manmaster/crypto/ASN1_TIME_set.html
     pub struct Asn1Time;
     /// Reference to an [`Asn1Time`]
     ///
@@ -198,7 +199,7 @@
 impl Asn1TimeRef {
     /// Find difference between two times
     #[corresponds(ASN1_TIME_diff)]
-    #[cfg(ossl102)]
+    #[cfg(any(ossl102, boringssl))]
     pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> {
         let mut days = 0;
         let mut secs = 0;
@@ -214,7 +215,7 @@
 
     /// Compare two times
     #[corresponds(ASN1_TIME_compare)]
-    #[cfg(ossl102)]
+    #[cfg(any(ossl102, boringssl))]
     pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> {
         let d = self.diff(other)?;
         if d.days > 0 || d.secs > 0 {
@@ -228,7 +229,7 @@
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl PartialEq for Asn1TimeRef {
     fn eq(&self, other: &Asn1TimeRef) -> bool {
         self.diff(other)
@@ -237,7 +238,7 @@
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl PartialEq<Asn1Time> for Asn1TimeRef {
     fn eq(&self, other: &Asn1Time) -> bool {
         self.diff(other)
@@ -246,7 +247,7 @@
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef {
     fn eq(&self, other: &Asn1Time) -> bool {
         self.diff(other)
@@ -255,21 +256,21 @@
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl PartialOrd for Asn1TimeRef {
     fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
         self.compare(other).ok()
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl PartialOrd<Asn1Time> for Asn1TimeRef {
     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
         self.compare(other).ok()
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
         self.compare(other).ok()
@@ -351,9 +352,9 @@
 
     /// Creates a new time corresponding to the specified X509 time string.
     ///
-    /// Requires OpenSSL 1.1.1 or newer.
+    /// Requires BoringSSL or OpenSSL 1.1.1 or newer.
     #[corresponds(ASN1_TIME_set_string_X509)]
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, boringssl))]
     pub fn from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack> {
         unsafe {
             let s = CString::new(s).unwrap();
@@ -366,7 +367,7 @@
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl PartialEq for Asn1Time {
     fn eq(&self, other: &Asn1Time) -> bool {
         self.diff(other)
@@ -375,7 +376,7 @@
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl PartialEq<Asn1TimeRef> for Asn1Time {
     fn eq(&self, other: &Asn1TimeRef) -> bool {
         self.diff(other)
@@ -384,7 +385,7 @@
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
     fn eq(&self, other: &&'a Asn1TimeRef) -> bool {
         self.diff(other)
@@ -393,21 +394,21 @@
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl PartialOrd for Asn1Time {
     fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> {
         self.compare(other).ok()
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl PartialOrd<Asn1TimeRef> for Asn1Time {
     fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> {
         self.compare(other).ok()
     }
 }
 
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
 impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time {
     fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> {
         self.compare(other).ok()
@@ -423,7 +424,7 @@
     /// structures.  This implementation uses [ASN1_STRING-to_UTF8] to preserve
     /// compatibility with Rust's String.
     ///
-    /// [ASN1_STRING-to_UTF8]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_STRING_to_UTF8.html
+    /// [ASN1_STRING-to_UTF8]: https://www.openssl.org/docs/manmaster/crypto/ASN1_STRING_to_UTF8.html
     pub struct Asn1String;
     /// A reference to an [`Asn1String`].
     pub struct Asn1StringRef;
@@ -492,7 +493,7 @@
     /// OpenSSL documentation includes [`ASN1_INTEGER_set`].
     ///
     /// [`bn`]: ../bn/index.html
-    /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html
+    /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/manmaster/crypto/ASN1_INTEGER_set.html
     pub struct Asn1Integer;
     /// A reference to an [`Asn1Integer`].
     pub struct Asn1IntegerRef;
@@ -504,13 +505,30 @@
     /// Corresponds to [`BN_to_ASN1_INTEGER`]. Also see
     /// [`BigNumRef::to_asn1_integer`].
     ///
-    /// [`BN_to_ASN1_INTEGER`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_to_ASN1_INTEGER.html
+    /// [`BN_to_ASN1_INTEGER`]: https://www.openssl.org/docs/manmaster/crypto/BN_to_ASN1_INTEGER.html
     /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer
     pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> {
         bn.to_asn1_integer()
     }
 }
 
+impl Ord for Asn1Integer {
+    fn cmp(&self, other: &Self) -> Ordering {
+        Asn1IntegerRef::cmp(self, other)
+    }
+}
+impl PartialOrd for Asn1Integer {
+    fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+impl Eq for Asn1Integer {}
+impl PartialEq for Asn1Integer {
+    fn eq(&self, other: &Asn1Integer) -> bool {
+        Asn1IntegerRef::eq(self, other)
+    }
+}
+
 impl Asn1IntegerRef {
     #[allow(missing_docs, clippy::unnecessary_cast)]
     #[deprecated(since = "0.10.6", note = "use to_bn instead")]
@@ -535,6 +553,30 @@
     pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> {
         unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) }
     }
+
+    /// Creates a new Asn1Integer with the same value.
+    #[corresponds(ASN1_INTEGER_dup)]
+    pub fn to_owned(&self) -> Result<Asn1Integer, ErrorStack> {
+        unsafe { cvt_p(ffi::ASN1_INTEGER_dup(self.as_ptr())).map(|p| Asn1Integer::from_ptr(p)) }
+    }
+}
+
+impl Ord for Asn1IntegerRef {
+    fn cmp(&self, other: &Self) -> Ordering {
+        let res = unsafe { ffi::ASN1_INTEGER_cmp(self.as_ptr(), other.as_ptr()) };
+        res.cmp(&0)
+    }
+}
+impl PartialOrd for Asn1IntegerRef {
+    fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+impl Eq for Asn1IntegerRef {}
+impl PartialEq for Asn1IntegerRef {
+    fn eq(&self, other: &Asn1IntegerRef) -> bool {
+        self.cmp(other) == Ordering::Equal
+    }
 }
 
 foreign_type_and_impl_send_sync! {
@@ -571,8 +613,49 @@
 }
 
 foreign_type_and_impl_send_sync! {
+    type CType = ffi::ASN1_OCTET_STRING;
+    fn drop = ffi::ASN1_OCTET_STRING_free;
+    /// ASN.1 OCTET STRING type
+    pub struct Asn1OctetString;
+    /// A reference to an [`Asn1OctetString`].
+    pub struct Asn1OctetStringRef;
+}
+
+impl Asn1OctetString {
+    /// Creates an Asn1OctetString from bytes
+    pub fn new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack> {
+        ffi::init();
+        unsafe {
+            let s = cvt_p(ffi::ASN1_OCTET_STRING_new())?;
+            ffi::ASN1_OCTET_STRING_set(s, value.as_ptr(), value.len().try_into().unwrap());
+            Ok(Self::from_ptr(s))
+        }
+    }
+}
+
+impl Asn1OctetStringRef {
+    /// Returns the octet string as an array of bytes.
+    #[corresponds(ASN1_STRING_get0_data)]
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr().cast()), self.len()) }
+    }
+
+    /// Returns the number of bytes in the octet string.
+    #[corresponds(ASN1_STRING_length)]
+    pub fn len(&self) -> usize {
+        unsafe { ffi::ASN1_STRING_length(self.as_ptr().cast()) as usize }
+    }
+
+    /// Determines if the string is empty.
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+}
+
+foreign_type_and_impl_send_sync! {
     type CType = ffi::ASN1_OBJECT;
     fn drop = ffi::ASN1_OBJECT_free;
+    fn clone = ffi::OBJ_dup;
 
     /// Object Identifier
     ///
@@ -586,12 +669,16 @@
     ///
     /// [`Nid`]: ../nid/index.html
     /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html
-    /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_obj2nid.html
+    /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/manmaster/crypto/OBJ_obj2nid.html
     pub struct Asn1Object;
     /// A reference to an [`Asn1Object`].
     pub struct Asn1ObjectRef;
 }
 
+impl Stackable for Asn1Object {
+    type StackType = ffi::stack_st_ASN1_OBJECT;
+}
+
 impl Asn1Object {
     /// Constructs an ASN.1 Object Identifier from a string representation of the OID.
     #[corresponds(OBJ_txt2obj)]
@@ -661,6 +748,32 @@
     }
 }
 
+foreign_type_and_impl_send_sync! {
+    type CType = ffi::ASN1_ENUMERATED;
+    fn drop = ffi::ASN1_ENUMERATED_free;
+
+    /// An ASN.1 enumerated.
+    pub struct Asn1Enumerated;
+    /// A reference to an [`Asn1Enumerated`].
+    pub struct Asn1EnumeratedRef;
+}
+
+impl Asn1EnumeratedRef {
+    /// Get the value, if it fits in the required bounds.
+    #[corresponds(ASN1_ENUMERATED_get_int64)]
+    #[cfg(ossl110)]
+    pub fn get_i64(&self) -> Result<i64, ErrorStack> {
+        let mut crl_reason = 0;
+        unsafe {
+            cvt(ffi::ASN1_ENUMERATED_get_int64(
+                &mut crl_reason,
+                self.as_ptr(),
+            ))?;
+        }
+        Ok(crl_reason)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -696,7 +809,7 @@
     }
 
     #[test]
-    #[cfg(ossl102)]
+    #[cfg(any(ossl102, boringssl))]
     fn time_eq() {
         let a = Asn1Time::from_str("99991231235959Z").unwrap();
         let b = Asn1Time::from_str("99991231235959Z").unwrap();
@@ -715,7 +828,7 @@
     }
 
     #[test]
-    #[cfg(ossl102)]
+    #[cfg(any(ossl102, boringssl))]
     fn time_ord() {
         let a = Asn1Time::from_str("99991231235959Z").unwrap();
         let b = Asn1Time::from_str("99991231235959Z").unwrap();
@@ -745,6 +858,28 @@
     }
 
     #[test]
+    fn integer_to_owned() {
+        let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
+        let b = a.to_owned().unwrap();
+        assert_eq!(
+            a.to_bn().unwrap().to_dec_str().unwrap().to_string(),
+            b.to_bn().unwrap().to_dec_str().unwrap().to_string(),
+        );
+        assert_ne!(a.as_ptr(), b.as_ptr());
+    }
+
+    #[test]
+    fn integer_cmp() {
+        let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
+        let b = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap();
+        let c = Asn1Integer::from_bn(&BigNum::from_dec_str("43").unwrap()).unwrap();
+        assert!(a == b);
+        assert!(a != c);
+        assert!(a < c);
+        assert!(c > b);
+    }
+
+    #[test]
     fn object_from_str() {
         let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap();
         assert_eq!(object.nid(), Nid::SHA256);
@@ -766,4 +901,11 @@
             &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01],
         );
     }
+
+    #[test]
+    fn asn1_octet_string() {
+        let octet_string = Asn1OctetString::new_from_bytes(b"hello world").unwrap();
+        assert_eq!(octet_string.as_slice(), b"hello world");
+        assert_eq!(octet_string.len(), 11);
+    }
 }
diff --git a/src/bio.rs b/src/bio.rs
index 0324218..0f54935 100644
--- a/src/bio.rs
+++ b/src/bio.rs
@@ -4,7 +4,7 @@
 use std::ptr;
 use std::slice;
 
-use crate::{cvt_p, SignedLenType};
+use crate::cvt_p;
 use crate::error::ErrorStack;
 
 pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>);
@@ -25,7 +25,7 @@
         let bio = unsafe {
             cvt_p(BIO_new_mem_buf(
                 buf.as_ptr() as *const _,
-                buf.len() as SignedLenType,
+                buf.len() as crate::SLenType,
             ))?
         };
 
@@ -74,11 +74,11 @@
 }
 
 cfg_if! {
-    if #[cfg(ossl102)] {
+    if #[cfg(any(ossl102, boringssl))] {
         use ffi::BIO_new_mem_buf;
     } else {
         #[allow(bad_style)]
-        unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: SignedLenType) -> *mut ffi::BIO {
+        unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: ::libc::c_int) -> *mut ffi::BIO {
             ffi::BIO_new_mem_buf(buf as *mut _, len)
         }
     }
diff --git a/src/bn.rs b/src/bn.rs
index dbd7ae9..1ae450b 100644
--- a/src/bn.rs
+++ b/src/bn.rs
@@ -91,7 +91,7 @@
     /// to allocate.  BigNumContext and the OpenSSL [`BN_CTX`] structure are used
     /// internally when passing BigNum values between subroutines.
     ///
-    /// [`BN_CTX`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_CTX_new.html
+    /// [`BN_CTX`]: https://www.openssl.org/docs/manmaster/crypto/BN_CTX_new.html
     pub struct BigNumContext;
     /// Reference to [`BigNumContext`]
     ///
@@ -134,7 +134,7 @@
     ///
     /// [`new`]: struct.BigNum.html#method.new
     /// [`Dref<Target = BigNumRef>`]: struct.BigNum.html#deref-methods
-    /// [`BN_new`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_new.html
+    /// [`BN_new`]: https://www.openssl.org/docs/manmaster/crypto/BN_new.html
     ///
     /// # Examples
     /// ```
@@ -335,6 +335,20 @@
         unsafe { BN_is_negative(self.as_ptr()) == 1 }
     }
 
+    /// Returns `true` is `self` is even.
+    #[corresponds(BN_is_even)]
+    #[cfg(any(ossl110, boringssl, libressl350))]
+    pub fn is_even(&self) -> bool {
+        !self.is_odd()
+    }
+
+    /// Returns `true` is `self` is odd.
+    #[corresponds(BN_is_odd)]
+    #[cfg(any(ossl110, boringssl, libressl350))]
+    pub fn is_odd(&self) -> bool {
+        unsafe { ffi::BN_is_odd(self.as_ptr()) == 1 }
+    }
+
     /// Returns the number of significant bits in `self`.
     #[corresponds(BN_num_bits)]
     #[allow(clippy::unnecessary_cast)]
@@ -639,6 +653,25 @@
         }
     }
 
+    /// Places into `self` the modular square root of `a` such that `self^2 = a (mod p)`
+    #[corresponds(BN_mod_sqrt)]
+    pub fn mod_sqrt(
+        &mut self,
+        a: &BigNumRef,
+        p: &BigNumRef,
+        ctx: &mut BigNumContextRef,
+    ) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt_p(ffi::BN_mod_sqrt(
+                self.as_ptr(),
+                a.as_ptr(),
+                p.as_ptr(),
+                ctx.as_ptr(),
+            ))
+            .map(|_| ())
+        }
+    }
+
     /// Places the result of `a^p` in `self`.
     #[corresponds(BN_exp)]
     pub fn exp(
@@ -814,7 +847,7 @@
     /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]);
     /// ```
     #[corresponds(BN_bn2binpad)]
-    #[cfg(any(boringssl, ossl110))]
+    #[cfg(any(ossl110, libressl340, boringssl))]
     pub fn to_vec_padded(&self, pad_to: i32) -> Result<Vec<u8>, ErrorStack> {
         let mut v = Vec::with_capacity(pad_to as usize);
         unsafe {
@@ -1063,7 +1096,7 @@
     ///
     /// OpenSSL documentation at [`BN_bin2bn`]
     ///
-    /// [`BN_bin2bn`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_bin2bn.html
+    /// [`BN_bin2bn`]: https://www.openssl.org/docs/manmaster/crypto/BN_bin2bn.html
     ///
     /// ```
     /// # use openssl::bn::BigNum;
@@ -1195,7 +1228,7 @@
 
 impl PartialOrd for BigNum {
     fn partial_cmp(&self, oth: &BigNum) -> Option<Ordering> {
-        self.deref().partial_cmp(oth.deref())
+        Some(self.cmp(oth))
     }
 }
 
@@ -1455,4 +1488,36 @@
         b.set_const_time();
         assert!(b.is_const_time())
     }
+
+    #[test]
+    fn test_mod_sqrt() {
+        let mut ctx = BigNumContext::new().unwrap();
+
+        let s = BigNum::from_hex_str("2").unwrap();
+        let p = BigNum::from_hex_str("7DEB1").unwrap();
+        let mut sqrt = BigNum::new().unwrap();
+        let mut out = BigNum::new().unwrap();
+
+        // Square the root because OpenSSL randomly returns one of 2E42C or 4FA85
+        sqrt.mod_sqrt(&s, &p, &mut ctx).unwrap();
+        out.mod_sqr(&sqrt, &p, &mut ctx).unwrap();
+        assert!(out == s);
+
+        let s = BigNum::from_hex_str("3").unwrap();
+        let p = BigNum::from_hex_str("5").unwrap();
+        assert!(out.mod_sqrt(&s, &p, &mut ctx).is_err());
+    }
+
+    #[test]
+    #[cfg(any(ossl110, boringssl, libressl350))]
+    fn test_odd_even() {
+        let a = BigNum::from_u32(17).unwrap();
+        let b = BigNum::from_u32(18).unwrap();
+
+        assert!(a.is_odd());
+        assert!(!b.is_odd());
+
+        assert!(!a.is_even());
+        assert!(b.is_even());
+    }
 }
diff --git a/src/cipher.rs b/src/cipher.rs
index 84a8265..6869a4b 100644
--- a/src/cipher.rs
+++ b/src/cipher.rs
@@ -12,6 +12,7 @@
 use openssl_macros::corresponds;
 #[cfg(ossl300)]
 use std::ffi::CString;
+use std::ops::{Deref, DerefMut};
 #[cfg(ossl300)]
 use std::ptr;
 
@@ -41,7 +42,6 @@
 cfg_if! {
     if #[cfg(ossl300)] {
         use foreign_types::ForeignType;
-        use std::ops::{Deref, DerefMut};
 
         type Inner = *mut ffi::EVP_CIPHER;
 
@@ -90,6 +90,22 @@
         }
     } else {
         enum Inner {}
+
+        impl Deref for Cipher {
+            type Target = CipherRef;
+
+            #[inline]
+            fn deref(&self) -> &Self::Target {
+                match self.0 {}
+            }
+        }
+
+        impl DerefMut for Cipher {
+            #[inline]
+            fn deref_mut(&mut self) -> &mut Self::Target {
+                match self.0 {}
+            }
+        }
     }
 }
 
@@ -151,6 +167,10 @@
     }
 
     #[cfg(not(boringssl))]
+    pub fn aes_256_xts() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_xts() as *mut _) }
+    }
+
     pub fn aes_128_ctr() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ctr() as *mut _) }
     }
@@ -170,7 +190,6 @@
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_cfb8() as *mut _) }
     }
 
-    #[cfg(not(boringssl))]
     pub fn aes_128_gcm() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_gcm() as *mut _) }
     }
@@ -180,17 +199,28 @@
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ccm() as *mut _) }
     }
 
-    #[cfg(not(boringssl))]
     pub fn aes_128_ofb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ofb() as *mut _) }
     }
 
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     pub fn aes_128_ocb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ocb() as *mut _) }
     }
 
+    /// Requires OpenSSL 1.0.2 or newer.
+    #[cfg(ossl102)]
+    pub fn aes_128_wrap() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_wrap() as *mut _) }
+    }
+
+    /// Requires OpenSSL 1.1.0 or newer.
+    #[cfg(ossl110)]
+    pub fn aes_128_wrap_pad() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_wrap_pad() as *mut _) }
+    }
+
     pub fn aes_192_ecb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_ecb() as *mut _) }
     }
@@ -232,11 +262,23 @@
     }
 
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     pub fn aes_192_ocb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_ocb() as *mut _) }
     }
 
+    /// Requires OpenSSL 1.0.2 or newer.
+    #[cfg(ossl102)]
+    pub fn aes_192_wrap() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_wrap() as *mut _) }
+    }
+
+    /// Requires OpenSSL 1.1.0 or newer.
+    #[cfg(ossl110)]
+    pub fn aes_192_wrap_pad() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_wrap_pad() as *mut _) }
+    }
+
     pub fn aes_256_ecb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_ecb() as *mut _) }
     }
@@ -278,11 +320,23 @@
     }
 
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     pub fn aes_256_ocb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_ocb() as *mut _) }
     }
 
+    /// Requires OpenSSL 1.0.2 or newer.
+    #[cfg(ossl102)]
+    pub fn aes_256_wrap() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_wrap() as *mut _) }
+    }
+
+    /// Requires OpenSSL 1.1.0 or newer.
+    #[cfg(ossl110)]
+    pub fn aes_256_wrap_pad() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_wrap_pad() as *mut _) }
+    }
+
     #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
     #[cfg(not(boringssl))]
     pub fn bf_cbc() -> &'static CipherRef {
@@ -319,75 +373,160 @@
         unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3() as *mut _) }
     }
 
+    pub fn des_ede3_ecb() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3_ecb() as *mut _) }
+    }
+
     pub fn des_ede3_cbc() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3_cbc() as *mut _) }
     }
 
     #[cfg(not(boringssl))]
+    pub fn des_ede3_cfb8() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3_cfb8() as *mut _) }
+    }
+
+    #[cfg(not(boringssl))]
     pub fn des_ede3_cfb64() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3_cfb64() as *mut _) }
     }
 
+    #[cfg(not(boringssl))]
+    pub fn des_ede3_ofb() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3_ofb() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_RC4"))]
     pub fn rc4() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_rc4() as *mut _) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
     pub fn camellia128_cfb128() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_camellia_128_cfb128() as *mut _) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
     pub fn camellia128_ecb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_camellia_128_ecb() as *mut _) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia128_cbc() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_camellia_128_cbc() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia128_ofb() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_camellia_128_ofb() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
     pub fn camellia192_cfb128() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_camellia_192_cfb128() as *mut _) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
     pub fn camellia192_ecb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_camellia_192_ecb() as *mut _) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia192_cbc() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_camellia_192_cbc() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia192_ofb() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_camellia_192_ofb() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
     pub fn camellia256_cfb128() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_camellia_256_cfb128() as *mut _) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
     pub fn camellia256_ecb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_camellia_256_ecb() as *mut _) }
     }
 
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia256_cbc() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_camellia_256_cbc() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia256_ofb() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_camellia_256_ofb() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAST"))]
     #[cfg(not(boringssl))]
     pub fn cast5_cfb64() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_cast5_cfb64() as *mut _) }
     }
 
+    #[cfg(not(osslconf = "OPENSSL_NO_CAST"))]
     #[cfg(not(boringssl))]
     pub fn cast5_ecb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_cast5_ecb() as *mut _) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_IDEA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_CAST"))]
+    #[cfg(not(boringssl))]
+    pub fn cast5_cbc() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_cast5_cbc() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAST"))]
+    #[cfg(not(boringssl))]
+    pub fn cast5_ofb() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_cast5_ofb() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_IDEA"))]
+    #[cfg(not(boringssl))]
     pub fn idea_cfb64() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_idea_cfb64() as *mut _) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_IDEA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_IDEA"))]
+    #[cfg(not(boringssl))]
     pub fn idea_ecb() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_idea_ecb() as *mut _) }
     }
 
-    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_CHACHA")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_IDEA"))]
+    #[cfg(not(boringssl))]
+    pub fn idea_cbc() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_idea_cbc() as *mut _) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_IDEA"))]
+    #[cfg(not(boringssl))]
+    pub fn idea_ofb() -> &'static CipherRef {
+        unsafe { CipherRef::from_ptr(ffi::EVP_idea_ofb() as *mut _) }
+    }
+
+    #[cfg(all(any(ossl110, libressl310), not(osslconf = "OPENSSL_NO_CHACHA")))]
     pub fn chacha20() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_chacha20() as *mut _) }
     }
 
-    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_CHACHA")))]
+    #[cfg(all(any(ossl110, libressl360), not(osslconf = "OPENSSL_NO_CHACHA")))]
     pub fn chacha20_poly1305() -> &'static CipherRef {
         unsafe { CipherRef::from_ptr(ffi::EVP_chacha20_poly1305() as *mut _) }
     }
diff --git a/src/cipher_ctx.rs b/src/cipher_ctx.rs
index 379f83a..abb1f11 100644
--- a/src/cipher_ctx.rs
+++ b/src/cipher_ctx.rs
@@ -55,6 +55,8 @@
 #[cfg(not(boringssl))]
 use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef};
 use crate::{cvt, cvt_p};
+#[cfg(ossl102)]
+use bitflags::bitflags;
 use cfg_if::cfg_if;
 use foreign_types::{ForeignType, ForeignTypeRef};
 use libc::{c_int, c_uchar};
@@ -80,6 +82,15 @@
     pub struct CipherCtxRef;
 }
 
+#[cfg(ossl102)]
+bitflags! {
+    /// Flags for `EVP_CIPHER_CTX`.
+    pub struct CipherCtxFlags : c_int {
+        /// The flag used to opt into AES key wrap ciphers.
+        const FLAG_WRAP_ALLOW = ffi::EVP_CIPHER_CTX_FLAG_WRAP_ALLOW;
+    }
+}
+
 impl CipherCtx {
     /// Creates a new context.
     #[corresponds(EVP_CIPHER_CTX_new)]
@@ -94,6 +105,14 @@
 }
 
 impl CipherCtxRef {
+    #[corresponds(EVP_CIPHER_CTX_copy)]
+    pub fn copy(&mut self, src: &CipherCtxRef) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::EVP_CIPHER_CTX_copy(self.as_ptr(), src.as_ptr()))?;
+            Ok(())
+        }
+    }
+
     /// Initializes the context for encryption.
     ///
     /// Normally this is called once to set all of the cipher, key, and IV. However, this process can be split up
@@ -386,7 +405,7 @@
     /// # Panics
     ///
     /// Panics if the context has not been initialized with a cipher.
-    #[corresponds(EVP_CIHPER_CTX_ctrl)]
+    #[corresponds(EVP_CIPHER_CTX_ctrl)]
     pub fn set_iv_length(&mut self, len: usize) -> Result<(), ErrorStack> {
         self.assert_cipher();
 
@@ -509,6 +528,17 @@
         Ok(())
     }
 
+    /// Set ctx flags.
+    ///
+    /// This function is currently used to enable AES key wrap feature supported by OpenSSL 1.0.2 or newer.
+    #[corresponds(EVP_CIPHER_CTX_set_flags)]
+    #[cfg(ossl102)]
+    pub fn set_flags(&mut self, flags: CipherCtxFlags) {
+        unsafe {
+            ffi::EVP_CIPHER_CTX_set_flags(self.as_ptr(), flags.bits());
+        }
+    }
+
     /// Writes data into the context.
     ///
     /// Providing no output buffer will cause the input to be considered additional authenticated data (AAD).
@@ -518,7 +548,7 @@
     /// # Panics
     ///
     /// Panics if `output` doesn't contain enough space for data to be
-    /// written as specified by [`Self::minimal_output_size`].
+    /// written.
     #[corresponds(EVP_CipherUpdate)]
     pub fn cipher_update(
         &mut self,
@@ -551,7 +581,9 @@
     /// output size check removed. It can be used when the exact
     /// buffer size control is maintained by the caller.
     ///
-    /// SAFETY: The caller is expected to provide `output` buffer
+    /// # Safety
+    ///
+    /// The caller is expected to provide `output` buffer
     /// large enough to contain correct number of bytes. For streaming
     /// ciphers the output buffer size should be at least as big as
     /// the input buffer. For block ciphers the size of the output
@@ -591,6 +623,50 @@
         Ok(len)
     }
 
+    /// Like [`Self::cipher_update`] except that it writes output into the
+    /// `data` buffer. The `inlen` parameter specifies the number of bytes in
+    /// `data` that are considered the input. For streaming ciphers, the size of
+    /// `data` must be at least the input size. Otherwise, it must be at least
+    /// an additional block size larger.
+    ///
+    /// Note: Use [`Self::cipher_update`] with no output argument to write AAD.
+    ///
+    /// # Panics
+    ///
+    /// This function panics if the input size cannot be represented as `int` or
+    /// exceeds the buffer size, or if the output buffer does not contain enough
+    /// additional space.
+    #[corresponds(EVP_CipherUpdate)]
+    pub fn cipher_update_inplace(
+        &mut self,
+        data: &mut [u8],
+        inlen: usize,
+    ) -> Result<usize, ErrorStack> {
+        assert!(inlen <= data.len(), "Input size may not exceed buffer size");
+        let block_size = self.block_size();
+        if block_size != 1 {
+            assert!(
+                data.len() >= inlen + block_size,
+                "Output buffer size must be at least {} bytes.",
+                inlen + block_size
+            );
+        }
+
+        let inlen = c_int::try_from(inlen).unwrap();
+        let mut outlen = 0;
+        unsafe {
+            cvt(ffi::EVP_CipherUpdate(
+                self.as_ptr(),
+                data.as_mut_ptr(),
+                &mut outlen,
+                data.as_ptr(),
+                inlen,
+            ))
+        }?;
+
+        Ok(outlen as usize)
+    }
+
     /// Finalizes the encryption or decryption process.
     ///
     /// Any remaining data will be written to the output buffer.
@@ -619,7 +695,9 @@
     /// This function is the same as [`Self::cipher_final`] but with
     /// the output buffer size check removed.
     ///
-    /// SAFETY: The caller is expected to provide `output` buffer
+    /// # Safety
+    ///
+    /// The caller is expected to provide `output` buffer
     /// large enough to contain correct number of bytes. For streaming
     /// ciphers the output buffer can be empty, for block ciphers the
     /// output buffer should be at least as big as the block.
@@ -778,6 +856,26 @@
 
         ctx.cipher_final_vec(&mut vec![0; 0]).unwrap();
 
+        // encrypt again, but use in-place encryption this time
+        // First reset the IV
+        ctx.encrypt_init(None, None, Some(&iv)).unwrap();
+        ctx.set_padding(false);
+        let mut data_inplace: [u8; 32] = [1; 32];
+        let outlen = ctx
+            .cipher_update_inplace(&mut data_inplace[0..15], 15)
+            .unwrap();
+        assert_eq!(15, outlen);
+
+        let outlen = ctx
+            .cipher_update_inplace(&mut data_inplace[15..32], 17)
+            .unwrap();
+        assert_eq!(17, outlen);
+
+        ctx.cipher_final(&mut [0u8; 0]).unwrap();
+
+        // Check that the resulting data is encrypted in the same manner
+        assert_eq!(data_inplace.as_slice(), output.as_slice());
+
         // try to decrypt
         ctx.decrypt_init(Some(cipher), Some(&key), Some(&iv))
             .unwrap();
@@ -800,6 +898,19 @@
         ctx.cipher_final_vec(&mut vec![0; 0]).unwrap();
         // check if the decrypted blocks are the same as input (all ones)
         assert_eq!(output_decrypted, vec![1; 32]);
+
+        // decrypt again, but now the output in-place
+        ctx.decrypt_init(None, None, Some(&iv)).unwrap();
+        ctx.set_padding(false);
+
+        let outlen = ctx.cipher_update_inplace(&mut output[0..15], 15).unwrap();
+        assert_eq!(15, outlen);
+
+        let outlen = ctx.cipher_update_inplace(&mut output[15..], 17).unwrap();
+        assert_eq!(17, outlen);
+
+        ctx.cipher_final_vec(&mut vec![0; 0]).unwrap();
+        assert_eq!(output_decrypted, output);
     }
 
     #[test]
@@ -838,4 +949,162 @@
         ctx.cipher_update(&vec![0; block_size + 1], Some(&mut vec![0; block_size - 1]))
             .unwrap();
     }
+
+    #[cfg(ossl102)]
+    fn cipher_wrap_test(cipher: &CipherRef, pt: &str, ct: &str, key: &str, iv: Option<&str>) {
+        let pt = hex::decode(pt).unwrap();
+        let key = hex::decode(key).unwrap();
+        let expected = hex::decode(ct).unwrap();
+        let iv = iv.map(|v| hex::decode(v).unwrap());
+        let padding = 8 - pt.len() % 8;
+        let mut computed = vec![0; pt.len() + padding + cipher.block_size() * 2];
+        let mut ctx = CipherCtx::new().unwrap();
+
+        ctx.set_flags(CipherCtxFlags::FLAG_WRAP_ALLOW);
+        ctx.encrypt_init(Some(cipher), Some(&key), iv.as_deref())
+            .unwrap();
+
+        let count = ctx.cipher_update(&pt, Some(&mut computed)).unwrap();
+        let rest = ctx.cipher_final(&mut computed[count..]).unwrap();
+        computed.truncate(count + rest);
+
+        if computed != expected {
+            println!("Computed: {}", hex::encode(&computed));
+            println!("Expected: {}", hex::encode(&expected));
+            if computed.len() != expected.len() {
+                println!(
+                    "Lengths differ: {} in computed vs {} expected",
+                    computed.len(),
+                    expected.len()
+                );
+            }
+            panic!("test failure");
+        }
+    }
+
+    #[test]
+    #[cfg(ossl102)]
+    fn test_aes128_wrap() {
+        let pt = "00112233445566778899aabbccddeeff";
+        let ct = "7940ff694448b5bb5139c959a4896832e55d69aa04daa27e";
+        let key = "2b7e151628aed2a6abf7158809cf4f3c";
+        let iv = "0001020304050607";
+
+        cipher_wrap_test(Cipher::aes_128_wrap(), pt, ct, key, Some(iv));
+    }
+
+    #[test]
+    #[cfg(ossl102)]
+    fn test_aes128_wrap_default_iv() {
+        let pt = "00112233445566778899aabbccddeeff";
+        let ct = "38f1215f0212526f8a70b51955b9fbdc9fe3041d9832306e";
+        let key = "2b7e151628aed2a6abf7158809cf4f3c";
+
+        cipher_wrap_test(Cipher::aes_128_wrap(), pt, ct, key, None);
+    }
+
+    #[test]
+    #[cfg(ossl110)]
+    fn test_aes128_wrap_pad() {
+        let pt = "00112233445566778899aabbccddee";
+        let ct = "f13998f5ab32ef82a1bdbcbe585e1d837385b529572a1e1b";
+        let key = "2b7e151628aed2a6abf7158809cf4f3c";
+        let iv = "00010203";
+
+        cipher_wrap_test(Cipher::aes_128_wrap_pad(), pt, ct, key, Some(iv));
+    }
+
+    #[test]
+    #[cfg(ossl110)]
+    fn test_aes128_wrap_pad_default_iv() {
+        let pt = "00112233445566778899aabbccddee";
+        let ct = "3a501085fb8cf66f4186b7df851914d471ed823411598add";
+        let key = "2b7e151628aed2a6abf7158809cf4f3c";
+
+        cipher_wrap_test(Cipher::aes_128_wrap_pad(), pt, ct, key, None);
+    }
+
+    #[test]
+    #[cfg(ossl102)]
+    fn test_aes192_wrap() {
+        let pt = "9f6dee187d35302116aecbfd059657efd9f7589c4b5e7f5b";
+        let ct = "83b89142dfeeb4871e078bfb81134d33e23fedc19b03a1cf689973d3831b6813";
+        let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b";
+        let iv = "0001020304050607";
+
+        cipher_wrap_test(Cipher::aes_192_wrap(), pt, ct, key, Some(iv));
+    }
+
+    #[test]
+    #[cfg(ossl102)]
+    fn test_aes192_wrap_default_iv() {
+        let pt = "9f6dee187d35302116aecbfd059657efd9f7589c4b5e7f5b";
+        let ct = "c02c2cf11505d3e4851030d5534cbf5a1d7eca7ba8839adbf239756daf1b43e6";
+        let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b";
+
+        cipher_wrap_test(Cipher::aes_192_wrap(), pt, ct, key, None);
+    }
+
+    #[test]
+    #[cfg(ossl110)]
+    fn test_aes192_wrap_pad() {
+        let pt = "00112233445566778899aabbccddee";
+        let ct = "b4f6bb167ef7caf061a74da82b36ad038ca057ab51e98d3a";
+        let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b";
+        let iv = "00010203";
+
+        cipher_wrap_test(Cipher::aes_192_wrap_pad(), pt, ct, key, Some(iv));
+    }
+
+    #[test]
+    #[cfg(ossl110)]
+    fn test_aes192_wrap_pad_default_iv() {
+        let pt = "00112233445566778899aabbccddee";
+        let ct = "b2c37a28cc602753a7c944a4c2555a2df9c98b2eded5312e";
+        let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b";
+
+        cipher_wrap_test(Cipher::aes_192_wrap_pad(), pt, ct, key, None);
+    }
+
+    #[test]
+    #[cfg(ossl102)]
+    fn test_aes256_wrap() {
+        let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51";
+        let ct = "cc05da2a7f56f7dd0c144231f90bce58648fa20a8278f5a6b7d13bba6aa57a33229d4333866b7fd6";
+        let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
+        let iv = "0001020304050607";
+
+        cipher_wrap_test(Cipher::aes_256_wrap(), pt, ct, key, Some(iv));
+    }
+
+    #[test]
+    #[cfg(ossl102)]
+    fn test_aes256_wrap_default_iv() {
+        let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51";
+        let ct = "0b24f068b50e52bc6987868411c36e1b03900866ed12af81eb87cef70a8d1911731c1d7abf789d88";
+        let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
+
+        cipher_wrap_test(Cipher::aes_256_wrap(), pt, ct, key, None);
+    }
+
+    #[test]
+    #[cfg(ossl110)]
+    fn test_aes256_wrap_pad() {
+        let pt = "00112233445566778899aabbccddee";
+        let ct = "91594e044ccc06130d60e6c84a996aa4f96a9faff8c5f6e7";
+        let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
+        let iv = "00010203";
+
+        cipher_wrap_test(Cipher::aes_256_wrap_pad(), pt, ct, key, Some(iv));
+    }
+
+    #[test]
+    #[cfg(ossl110)]
+    fn test_aes256_wrap_pad_default_iv() {
+        let pt = "00112233445566778899aabbccddee";
+        let ct = "dc3c166a854afd68aea624a4272693554bf2e4fcbae602cd";
+        let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
+
+        cipher_wrap_test(Cipher::aes_256_wrap_pad(), pt, ct, key, None);
+    }
 }
diff --git a/src/cms.rs b/src/cms.rs
index 185c4df..a946230 100644
--- a/src/cms.rs
+++ b/src/cms.rs
@@ -15,11 +15,13 @@
 use crate::pkey::{HasPrivate, PKeyRef};
 use crate::stack::StackRef;
 use crate::symm::Cipher;
-use crate::x509::{X509Ref, X509};
+use crate::x509::{store::X509StoreRef, X509Ref, X509};
 use crate::{cvt, cvt_p};
 use openssl_macros::corresponds;
 
 bitflags! {
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct CMSOptions : c_uint {
         const TEXT = ffi::CMS_TEXT;
         const CMS_NOCERTS = ffi::CMS_NOCERTS;
@@ -227,14 +229,65 @@
             Ok(CmsContentInfo::from_ptr(cms))
         }
     }
+
+    /// Verify this CmsContentInfo's signature,
+    /// This will search the 'certs' list for the signing certificate.      
+    /// Additional certificates, needed for building the certificate chain, may be
+    /// given in 'store' as well as additional CRLs.
+    /// A detached signature may be passed in `detached_data`. The signed content
+    /// without signature, will be copied into output_data if it is present.
+    ///
+    #[corresponds(CMS_verify)]
+    pub fn verify(
+        &mut self,
+        certs: Option<&StackRef<X509>>,
+        store: Option<&X509StoreRef>,
+        detached_data: Option<&[u8]>,
+        output_data: Option<&mut Vec<u8>>,
+        flags: CMSOptions,
+    ) -> Result<(), ErrorStack> {
+        unsafe {
+            let certs_ptr = certs.map_or(ptr::null_mut(), |p| p.as_ptr());
+            let store_ptr = store.map_or(ptr::null_mut(), |p| p.as_ptr());
+            let detached_data_bio = match detached_data {
+                Some(data) => Some(MemBioSlice::new(data)?),
+                None => None,
+            };
+            let detached_data_bio_ptr = detached_data_bio
+                .as_ref()
+                .map_or(ptr::null_mut(), |p| p.as_ptr());
+            let out_bio = MemBio::new()?;
+
+            cvt(ffi::CMS_verify(
+                self.as_ptr(),
+                certs_ptr,
+                store_ptr,
+                detached_data_bio_ptr,
+                out_bio.as_ptr(),
+                flags.bits(),
+            ))?;
+
+            if let Some(data) = output_data {
+                data.clear();
+                data.extend_from_slice(out_bio.get_buf());
+            };
+
+            Ok(())
+        }
+    }
 }
 
 #[cfg(test)]
 mod test {
     use super::*;
+
     use crate::pkcs12::Pkcs12;
+    use crate::pkey::PKey;
     use crate::stack::Stack;
-    use crate::x509::X509;
+    use crate::x509::{
+        store::{X509Store, X509StoreBuilder},
+        X509,
+    };
 
     #[test]
     fn cms_encrypt_decrypt() {
@@ -249,7 +302,7 @@
         let priv_cert_bytes = include_bytes!("../test/cms.p12");
         let priv_cert = Pkcs12::from_der(priv_cert_bytes).expect("failed to load priv cert");
         let priv_cert = priv_cert
-            .parse("mypass")
+            .parse2("mypass")
             .expect("failed to parse priv cert");
 
         // encrypt cms message using public key cert
@@ -274,13 +327,16 @@
                 CmsContentInfo::from_der(&encrypted_der).expect("failed read cms from der");
 
             let decrypt_with_cert_check = decrypt
-                .decrypt(&priv_cert.pkey, &priv_cert.cert)
+                .decrypt(
+                    priv_cert.pkey.as_ref().unwrap(),
+                    priv_cert.cert.as_ref().unwrap(),
+                )
                 .expect("failed to decrypt cms");
             let decrypt_with_cert_check = String::from_utf8(decrypt_with_cert_check)
                 .expect("failed to create string from cms content");
 
             let decrypt_without_cert_check = decrypt
-                .decrypt_without_cert_check(&priv_cert.pkey)
+                .decrypt_without_cert_check(priv_cert.pkey.as_ref().unwrap())
                 .expect("failed to decrypt cms");
             let decrypt_without_cert_check = String::from_utf8(decrypt_without_cert_check)
                 .expect("failed to create string from cms content");
@@ -296,13 +352,16 @@
                 CmsContentInfo::from_pem(&encrypted_pem).expect("failed read cms from pem");
 
             let decrypt_with_cert_check = decrypt
-                .decrypt(&priv_cert.pkey, &priv_cert.cert)
+                .decrypt(
+                    priv_cert.pkey.as_ref().unwrap(),
+                    priv_cert.cert.as_ref().unwrap(),
+                )
                 .expect("failed to decrypt cms");
             let decrypt_with_cert_check = String::from_utf8(decrypt_with_cert_check)
                 .expect("failed to create string from cms content");
 
             let decrypt_without_cert_check = decrypt
-                .decrypt_without_cert_check(&priv_cert.pkey)
+                .decrypt_without_cert_check(priv_cert.pkey.as_ref().unwrap())
                 .expect("failed to decrypt cms");
             let decrypt_without_cert_check = String::from_utf8(decrypt_without_cert_check)
                 .expect("failed to create string from cms content");
@@ -311,4 +370,115 @@
             assert_eq!(input, decrypt_without_cert_check);
         }
     }
+
+    fn cms_sign_verify_generic_helper(is_detached: bool) {
+        // load cert with private key
+        let cert_bytes = include_bytes!("../test/cert.pem");
+        let cert = X509::from_pem(cert_bytes).expect("failed to load cert.pem");
+
+        let key_bytes = include_bytes!("../test/key.pem");
+        let key = PKey::private_key_from_pem(key_bytes).expect("failed to load key.pem");
+
+        let root_bytes = include_bytes!("../test/root-ca.pem");
+        let root = X509::from_pem(root_bytes).expect("failed to load root-ca.pem");
+
+        // sign cms message using public key cert
+        let data = b"Hello world!";
+
+        let (opt, ext_data): (CMSOptions, Option<&[u8]>) = if is_detached {
+            (CMSOptions::DETACHED | CMSOptions::BINARY, Some(data))
+        } else {
+            (CMSOptions::empty(), None)
+        };
+
+        let mut cms = CmsContentInfo::sign(Some(&cert), Some(&key), None, Some(data), opt)
+            .expect("failed to CMS sign a message");
+
+        // check CMS signature length
+        let pem_cms = cms
+            .to_pem()
+            .expect("failed to pack CmsContentInfo into PEM");
+        assert!(!pem_cms.is_empty());
+
+        // verify CMS signature
+        let mut builder = X509StoreBuilder::new().expect("failed to create X509StoreBuilder");
+        builder
+            .add_cert(root)
+            .expect("failed to add root-ca into X509StoreBuilder");
+        let store: X509Store = builder.build();
+        let mut out_data: Vec<u8> = Vec::new();
+        let res = cms.verify(
+            None,
+            Some(&store),
+            ext_data,
+            Some(&mut out_data),
+            CMSOptions::empty(),
+        );
+
+        // check verification result -  valid signature
+        res.unwrap();
+        assert_eq!(data.to_vec(), out_data);
+    }
+
+    #[test]
+    fn cms_sign_verify_ok() {
+        cms_sign_verify_generic_helper(false);
+    }
+
+    #[test]
+    fn cms_sign_verify_detached_ok() {
+        cms_sign_verify_generic_helper(true);
+    }
+
+    #[test]
+    fn cms_sign_verify_error() {
+        #[cfg(ossl300)]
+        let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap();
+
+        // load cert with private key
+        let priv_cert_bytes = include_bytes!("../test/cms.p12");
+        let priv_cert = Pkcs12::from_der(priv_cert_bytes).expect("failed to load priv cert");
+        let priv_cert = priv_cert
+            .parse2("mypass")
+            .expect("failed to parse priv cert");
+
+        // sign cms message using public key cert
+        let data = b"Hello world!";
+        let mut cms = CmsContentInfo::sign(
+            Some(&priv_cert.cert.unwrap()),
+            Some(&priv_cert.pkey.unwrap()),
+            None,
+            Some(data),
+            CMSOptions::empty(),
+        )
+        .expect("failed to CMS sign a message");
+
+        // check CMS signature length
+        let pem_cms = cms
+            .to_pem()
+            .expect("failed to pack CmsContentInfo into PEM");
+        assert!(!pem_cms.is_empty());
+
+        let empty_store = X509StoreBuilder::new()
+            .expect("failed to create X509StoreBuilder")
+            .build();
+
+        // verify CMS signature
+        let res = cms.verify(
+            None,
+            Some(&empty_store),
+            Some(data),
+            None,
+            CMSOptions::empty(),
+        );
+
+        // check verification result - this is an invalid signature
+        // defined in openssl crypto/cms/cms.h
+        const CMS_R_CERTIFICATE_VERIFY_ERROR: i32 = 100;
+        let es = res.unwrap_err();
+        let error_array = es.errors();
+        assert_eq!(1, error_array.len());
+        let code = error_array[0].reason_code();
+        assert_eq!(code, CMS_R_CERTIFICATE_VERIFY_ERROR);
+    }
 }
diff --git a/src/derive.rs b/src/derive.rs
index 87a04a1..424c5f9 100644
--- a/src/derive.rs
+++ b/src/derive.rs
@@ -56,6 +56,7 @@
 use crate::error::ErrorStack;
 use crate::pkey::{HasPrivate, HasPublic, PKeyRef};
 use crate::{cvt, cvt_p};
+use openssl_macros::corresponds;
 
 /// A type used to derive a shared secret between two keys.
 pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>);
@@ -69,7 +70,7 @@
     ///
     /// This corresponds to [`EVP_PKEY_derive_init`].
     ///
-    /// [`EVP_PKEY_derive_init`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
+    /// [`EVP_PKEY_derive_init`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_derive_init.html
     pub fn new<T>(key: &'a PKeyRef<T>) -> Result<Deriver<'a>, ErrorStack>
     where
         T: HasPrivate,
@@ -82,10 +83,7 @@
     }
 
     /// Sets the peer key used for secret derivation.
-    ///
-    /// This corresponds to [`EVP_PKEY_derive_set_peer`]:
-    ///
-    /// [`EVP_PKEY_derive_set_peer`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
+    #[corresponds(EVP_PKEY_derive_set_peer)]
     pub fn set_peer<T>(&mut self, key: &'a PKeyRef<T>) -> Result<(), ErrorStack>
     where
         T: HasPublic,
@@ -93,6 +91,29 @@
         unsafe { cvt(ffi::EVP_PKEY_derive_set_peer(self.0, key.as_ptr())).map(|_| ()) }
     }
 
+    /// Sets the peer key used for secret derivation along with optionally validating the peer public key.
+    ///
+    /// Requires OpenSSL 3.0.0 or newer.
+    #[corresponds(EVP_PKEY_derive_set_peer_ex)]
+    #[cfg(ossl300)]
+    pub fn set_peer_ex<T>(
+        &mut self,
+        key: &'a PKeyRef<T>,
+        validate_peer: bool,
+    ) -> Result<(), ErrorStack>
+    where
+        T: HasPublic,
+    {
+        unsafe {
+            cvt(ffi::EVP_PKEY_derive_set_peer_ex(
+                self.0,
+                key.as_ptr(),
+                validate_peer as i32,
+            ))
+            .map(|_| ())
+        }
+    }
+
     /// Returns the size of the shared secret.
     ///
     /// It can be used to size the buffer passed to [`Deriver::derive`].
@@ -100,7 +121,7 @@
     /// This corresponds to [`EVP_PKEY_derive`].
     ///
     /// [`Deriver::derive`]: #method.derive
-    /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
+    /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_derive_init.html
     pub fn len(&mut self) -> Result<usize, ErrorStack> {
         unsafe {
             let mut len = 0;
@@ -114,7 +135,7 @@
     ///
     /// This corresponds to [`EVP_PKEY_derive`].
     ///
-    /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html
+    /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_derive_init.html
     pub fn derive(&mut self, buf: &mut [u8]) -> Result<usize, ErrorStack> {
         let mut len = buf.len();
         unsafe {
@@ -179,4 +200,18 @@
         let shared = deriver.derive_to_vec().unwrap();
         assert!(!shared.is_empty());
     }
+
+    #[test]
+    #[cfg(ossl300)]
+    fn test_ec_key_derive_ex() {
+        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
+        let ec_key = EcKey::generate(&group).unwrap();
+        let ec_key2 = EcKey::generate(&group).unwrap();
+        let pkey = PKey::from_ec_key(ec_key).unwrap();
+        let pkey2 = PKey::from_ec_key(ec_key2).unwrap();
+        let mut deriver = Deriver::new(&pkey).unwrap();
+        deriver.set_peer_ex(&pkey2, true).unwrap();
+        let shared = deriver.derive_to_vec().unwrap();
+        assert!(!shared.is_empty());
+    }
 }
diff --git a/src/dh.rs b/src/dh.rs
index e781543..c8d135f 100644
--- a/src/dh.rs
+++ b/src/dh.rs
@@ -7,7 +7,7 @@
 
 use crate::bn::{BigNum, BigNumRef};
 use crate::error::ErrorStack;
-use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private};
+use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public};
 use crate::{cvt, cvt_p};
 use openssl_macros::corresponds;
 
@@ -39,6 +39,16 @@
         params_to_der,
         ffi::i2d_DHparams
     }
+
+    /// Validates DH parameters for correctness
+    #[corresponds(DH_check_key)]
+    pub fn check_key(&self) -> Result<bool, ErrorStack> {
+        unsafe {
+            let mut codes = 0;
+            cvt(ffi::DH_check(self.as_ptr(), &mut codes))?;
+            Ok(codes == 0)
+        }
+    }
 }
 
 impl Dh<Params> {
@@ -66,6 +76,16 @@
         }
     }
 
+    /// Sets the public key on the DH object.
+    pub fn set_public_key(self, pub_key: BigNum) -> Result<Dh<Public>, ErrorStack> {
+        unsafe {
+            let dh_ptr = self.0;
+            cvt(DH_set0_key(dh_ptr, pub_key.as_ptr(), ptr::null_mut()))?;
+            mem::forget((self, pub_key));
+            Ok(Dh::from_ptr(dh_ptr))
+        }
+    }
+
     /// Sets the private key on the DH object and recomputes the public key.
     pub fn set_private_key(self, priv_key: BigNum) -> Result<Dh<Private>, ErrorStack> {
         unsafe {
@@ -79,6 +99,16 @@
         }
     }
 
+    /// Sets the public and private keys on the DH object.
+    pub fn set_key(self, pub_key: BigNum, priv_key: BigNum) -> Result<Dh<Private>, ErrorStack> {
+        unsafe {
+            let dh_ptr = self.0;
+            cvt(DH_set0_key(dh_ptr, pub_key.as_ptr(), priv_key.as_ptr()))?;
+            mem::forget((self, pub_key, priv_key));
+            Ok(Dh::from_ptr(dh_ptr))
+        }
+    }
+
     /// Generates DH params based on the given `prime_len` and a fixed `generator` value.
     #[corresponds(DH_generate_parameters_ex)]
     pub fn generate_params(prime_len: u32, generator: u32) -> Result<Dh<Params>, ErrorStack> {
@@ -304,6 +334,8 @@
 mod tests {
     use crate::bn::BigNum;
     use crate::dh::Dh;
+    #[cfg(all(not(boringssl), ossl110))]
+    use crate::pkey::PKey;
     use crate::ssl::{SslContext, SslMethod};
 
     #[test]
@@ -353,6 +385,23 @@
     }
 
     #[test]
+    #[cfg(all(not(boringssl), ossl110))]
+    fn test_from_dhx_serializes_q() {
+        let p = BigNum::from_hex_str("00ad107e1e9123a9d0d660faa79559c51fa20d64e5683b9fd1b54b1597b61d0a75e6fa141df95a56dbaf9a3c407ba1df15eb3d688a309c180e1de6b85a1274a0a66d3f8152ad6ac2129037c9edefda4df8d91e8fef55b7394b7ad5b7d0b6c12207c9f98d11ed34dbf6c6ba0b2c8bbc27be6a00e0a0b9c49708b3bf8a317091883681286130bc8985db1602e714415d9330278273c7de31efdc7310f7121fd5a07415987d9adc0a486dcdf93acc44328387315d75e198c641a480cd86a1b9e587e8be60e69cc928b2b9c52172e413042e9b23f10b0e16e79763c9b53dcf4ba80a29e3fb73c16b8e75b97ef363e2ffa31f71cf9de5384e71b81c0ac4dffe0c10e64f").unwrap();
+        let g = BigNum::from_hex_str("00ac4032ef4f2d9ae39df30b5c8ffdac506cdebe7b89998caf74866a08cfe4ffe3a6824a4e10b9a6f0dd921f01a70c4afaab739d7700c29f52c57db17c620a8652be5e9001a8d66ad7c17669101999024af4d027275ac1348bb8a762d0521bc98ae247150422ea1ed409939d54da7460cdb5f6c6b250717cbef180eb34118e98d119529a45d6f834566e3025e316a330efbb77a86f0c1ab15b051ae3d428c8f8acb70a8137150b8eeb10e183edd19963ddd9e263e4770589ef6aa21e7f5f2ff381b539cce3409d13cd566afbb48d6c019181e1bcfe94b30269edfe72fe9b6aa4bd7b5a0f1c71cfff4c19c418e1f6ec017981bc087f2a7065b384b890d3191f2bfa").unwrap();
+        let q = BigNum::from_hex_str("00801c0d34c58d93fe997177101f80535a4738cebcbf389a99b36371eb")
+            .unwrap();
+        let y = BigNum::from_hex_str("0082c165bb576243ecf46d58c3d1501616955fca0320fa95ea11d2e6c1b9cf217676720dc1c08c85bf20c4d232b60a29a1e51c7b773bc645014587c525c86151b30d75486ec7b6c98efb5f74955b83116d01d0af1232af89213c2de574369d701aba9357300b920d3d8b98252d46c46952c16a5f33554b38317809c7b9add4701f5c158c1b7035e9fe39366ececb90d2896b78c523c4a577287ef5ba7a2663ed58aa20b5ec66e30f316610dfaa38583e495ab6af771c284387e660edbef4edb872e2e80e1d244ee95622e76d028e61c1e887c2aa792717362139f4dd26eafd49b2366eeb2350b01fe1b56022a2809e379559c37b375ba01c4eaacc14fd1b247837").unwrap();
+
+        let dh = Dh::from_params(p, g, q).unwrap();
+        let dh = dh.set_public_key(y).unwrap();
+
+        // Verify that 'q' is serialized in the public key.
+        let pkey = PKey::from_dhx(dh).unwrap();
+        assert_eq!(pkey.public_key_to_der().unwrap(), b"\x30\x82\x03\x44\x30\x82\x02\x36\x06\x07\x2a\x86\x48\xce\x3e\x02\x01\x30\x82\x02\x29\x02\x82\x01\x01\x00\xad\x10\x7e\x1e\x91\x23\xa9\xd0\xd6\x60\xfa\xa7\x95\x59\xc5\x1f\xa2\x0d\x64\xe5\x68\x3b\x9f\xd1\xb5\x4b\x15\x97\xb6\x1d\x0a\x75\xe6\xfa\x14\x1d\xf9\x5a\x56\xdb\xaf\x9a\x3c\x40\x7b\xa1\xdf\x15\xeb\x3d\x68\x8a\x30\x9c\x18\x0e\x1d\xe6\xb8\x5a\x12\x74\xa0\xa6\x6d\x3f\x81\x52\xad\x6a\xc2\x12\x90\x37\xc9\xed\xef\xda\x4d\xf8\xd9\x1e\x8f\xef\x55\xb7\x39\x4b\x7a\xd5\xb7\xd0\xb6\xc1\x22\x07\xc9\xf9\x8d\x11\xed\x34\xdb\xf6\xc6\xba\x0b\x2c\x8b\xbc\x27\xbe\x6a\x00\xe0\xa0\xb9\xc4\x97\x08\xb3\xbf\x8a\x31\x70\x91\x88\x36\x81\x28\x61\x30\xbc\x89\x85\xdb\x16\x02\xe7\x14\x41\x5d\x93\x30\x27\x82\x73\xc7\xde\x31\xef\xdc\x73\x10\xf7\x12\x1f\xd5\xa0\x74\x15\x98\x7d\x9a\xdc\x0a\x48\x6d\xcd\xf9\x3a\xcc\x44\x32\x83\x87\x31\x5d\x75\xe1\x98\xc6\x41\xa4\x80\xcd\x86\xa1\xb9\xe5\x87\xe8\xbe\x60\xe6\x9c\xc9\x28\xb2\xb9\xc5\x21\x72\xe4\x13\x04\x2e\x9b\x23\xf1\x0b\x0e\x16\xe7\x97\x63\xc9\xb5\x3d\xcf\x4b\xa8\x0a\x29\xe3\xfb\x73\xc1\x6b\x8e\x75\xb9\x7e\xf3\x63\xe2\xff\xa3\x1f\x71\xcf\x9d\xe5\x38\x4e\x71\xb8\x1c\x0a\xc4\xdf\xfe\x0c\x10\xe6\x4f\x02\x82\x01\x01\x00\xac\x40\x32\xef\x4f\x2d\x9a\xe3\x9d\xf3\x0b\x5c\x8f\xfd\xac\x50\x6c\xde\xbe\x7b\x89\x99\x8c\xaf\x74\x86\x6a\x08\xcf\xe4\xff\xe3\xa6\x82\x4a\x4e\x10\xb9\xa6\xf0\xdd\x92\x1f\x01\xa7\x0c\x4a\xfa\xab\x73\x9d\x77\x00\xc2\x9f\x52\xc5\x7d\xb1\x7c\x62\x0a\x86\x52\xbe\x5e\x90\x01\xa8\xd6\x6a\xd7\xc1\x76\x69\x10\x19\x99\x02\x4a\xf4\xd0\x27\x27\x5a\xc1\x34\x8b\xb8\xa7\x62\xd0\x52\x1b\xc9\x8a\xe2\x47\x15\x04\x22\xea\x1e\xd4\x09\x93\x9d\x54\xda\x74\x60\xcd\xb5\xf6\xc6\xb2\x50\x71\x7c\xbe\xf1\x80\xeb\x34\x11\x8e\x98\xd1\x19\x52\x9a\x45\xd6\xf8\x34\x56\x6e\x30\x25\xe3\x16\xa3\x30\xef\xbb\x77\xa8\x6f\x0c\x1a\xb1\x5b\x05\x1a\xe3\xd4\x28\xc8\xf8\xac\xb7\x0a\x81\x37\x15\x0b\x8e\xeb\x10\xe1\x83\xed\xd1\x99\x63\xdd\xd9\xe2\x63\xe4\x77\x05\x89\xef\x6a\xa2\x1e\x7f\x5f\x2f\xf3\x81\xb5\x39\xcc\xe3\x40\x9d\x13\xcd\x56\x6a\xfb\xb4\x8d\x6c\x01\x91\x81\xe1\xbc\xfe\x94\xb3\x02\x69\xed\xfe\x72\xfe\x9b\x6a\xa4\xbd\x7b\x5a\x0f\x1c\x71\xcf\xff\x4c\x19\xc4\x18\xe1\xf6\xec\x01\x79\x81\xbc\x08\x7f\x2a\x70\x65\xb3\x84\xb8\x90\xd3\x19\x1f\x2b\xfa\x02\x1d\x00\x80\x1c\x0d\x34\xc5\x8d\x93\xfe\x99\x71\x77\x10\x1f\x80\x53\x5a\x47\x38\xce\xbc\xbf\x38\x9a\x99\xb3\x63\x71\xeb\x03\x82\x01\x06\x00\x02\x82\x01\x01\x00\x82\xc1\x65\xbb\x57\x62\x43\xec\xf4\x6d\x58\xc3\xd1\x50\x16\x16\x95\x5f\xca\x03\x20\xfa\x95\xea\x11\xd2\xe6\xc1\xb9\xcf\x21\x76\x76\x72\x0d\xc1\xc0\x8c\x85\xbf\x20\xc4\xd2\x32\xb6\x0a\x29\xa1\xe5\x1c\x7b\x77\x3b\xc6\x45\x01\x45\x87\xc5\x25\xc8\x61\x51\xb3\x0d\x75\x48\x6e\xc7\xb6\xc9\x8e\xfb\x5f\x74\x95\x5b\x83\x11\x6d\x01\xd0\xaf\x12\x32\xaf\x89\x21\x3c\x2d\xe5\x74\x36\x9d\x70\x1a\xba\x93\x57\x30\x0b\x92\x0d\x3d\x8b\x98\x25\x2d\x46\xc4\x69\x52\xc1\x6a\x5f\x33\x55\x4b\x38\x31\x78\x09\xc7\xb9\xad\xd4\x70\x1f\x5c\x15\x8c\x1b\x70\x35\xe9\xfe\x39\x36\x6e\xce\xcb\x90\xd2\x89\x6b\x78\xc5\x23\xc4\xa5\x77\x28\x7e\xf5\xba\x7a\x26\x63\xed\x58\xaa\x20\xb5\xec\x66\xe3\x0f\x31\x66\x10\xdf\xaa\x38\x58\x3e\x49\x5a\xb6\xaf\x77\x1c\x28\x43\x87\xe6\x60\xed\xbe\xf4\xed\xb8\x72\xe2\xe8\x0e\x1d\x24\x4e\xe9\x56\x22\xe7\x6d\x02\x8e\x61\xc1\xe8\x87\xc2\xaa\x79\x27\x17\x36\x21\x39\xf4\xdd\x26\xea\xfd\x49\xb2\x36\x6e\xeb\x23\x50\xb0\x1f\xe1\xb5\x60\x22\xa2\x80\x9e\x37\x95\x59\xc3\x7b\x37\x5b\xa0\x1c\x4e\xaa\xcc\x14\xfd\x1b\x24\x78\x37");
+    }
+
+    #[test]
     #[cfg(ossl102)]
     fn test_dh_stored_restored() {
         let dh1 = Dh::get_2048_256().unwrap();
@@ -368,6 +417,30 @@
     }
 
     #[test]
+    #[cfg(ossl102)]
+    fn test_set_keys() {
+        let dh1 = Dh::get_2048_256().unwrap();
+        let key1 = dh1.generate_key().unwrap();
+
+        let dh2 = Dh::get_2048_256().unwrap();
+        let key2 = dh2
+            .set_public_key(key1.public_key().to_owned().unwrap())
+            .unwrap();
+
+        assert_eq!(key1.public_key(), key2.public_key());
+
+        let dh3 = Dh::get_2048_256().unwrap();
+        let key3 = dh3
+            .set_key(
+                key1.public_key().to_owned().unwrap(),
+                key1.private_key().to_owned().unwrap(),
+            )
+            .unwrap();
+        assert_eq!(key1.public_key(), key3.public_key());
+        assert_eq!(key1.private_key(), key3.private_key());
+    }
+
+    #[test]
     fn test_dh_from_pem() {
         let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
         let params = include_bytes!("../test/dhparams.pem");
@@ -413,4 +486,14 @@
 
         assert_eq!(shared_a, shared_b);
     }
+
+    #[test]
+    fn test_dh_check_key() {
+        let dh1 = Dh::generate_params(512, 2).unwrap();
+        let p = BigNum::from_hex_str("04").unwrap();
+        let g = BigNum::from_hex_str("02").unwrap();
+        let dh2 = Dh::from_pqg(p, None, g).unwrap();
+        assert!(dh1.check_key().unwrap());
+        assert!(matches!(dh2.check_key(), Ok(false) | Err(_)));
+    }
 }
diff --git a/src/dsa.rs b/src/dsa.rs
index 0aceeb5..1a63e8a 100644
--- a/src/dsa.rs
+++ b/src/dsa.rs
@@ -15,7 +15,7 @@
 
 use crate::bn::{BigNum, BigNumRef};
 use crate::error::ErrorStack;
-use crate::pkey::{HasParams, HasPrivate, HasPublic, Private, Public};
+use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public};
 use crate::util::ForeignTypeRefExt;
 use crate::{cvt, cvt_p};
 use openssl_macros::corresponds;
@@ -38,7 +38,7 @@
     ///
     /// OpenSSL documentation at [`DSA_new`]
     ///
-    /// [`DSA_new`]: https://www.openssl.org/docs/man1.1.0/crypto/DSA_new.html
+    /// [`DSA_new`]: https://www.openssl.org/docs/manmaster/crypto/DSA_new.html
     ///
     /// # Examples
     ///
@@ -128,6 +128,13 @@
         ffi::PEM_write_bio_DSAPrivateKey
     }
 
+    to_der! {
+        /// Serializes the private_key to a DER-encoded `DSAPrivateKey` structure.
+        #[corresponds(i2d_DSAPrivateKey)]
+        private_key_to_der,
+        ffi::i2d_DSAPrivateKey
+    }
+
     /// Returns a reference to the private key component of `self`.
     #[corresponds(DSA_get0_key)]
     pub fn priv_key(&self) -> &BigNumRef {
@@ -184,17 +191,21 @@
 #[cfg(not(boringssl))]
 type BitType = c_int;
 
-impl Dsa<Private> {
-    /// Generate a DSA key pair.
-    ///
-    /// Calls [`DSA_generate_parameters_ex`] to populate the `p`, `g`, and `q` values.
-    /// These values are used to generate the key pair with [`DSA_generate_key`].
-    ///
-    /// The `bits` parameter corresponds to the length of the prime `p`.
-    ///
-    /// [`DSA_generate_parameters_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/DSA_generate_parameters_ex.html
-    /// [`DSA_generate_key`]: https://www.openssl.org/docs/man1.1.0/crypto/DSA_generate_key.html
-    pub fn generate(bits: u32) -> Result<Dsa<Private>, ErrorStack> {
+impl Dsa<Params> {
+    /// Creates a DSA params based upon the given parameters.
+    #[corresponds(DSA_set0_pqg)]
+    pub fn from_pqg(p: BigNum, q: BigNum, g: BigNum) -> Result<Dsa<Params>, ErrorStack> {
+        unsafe {
+            let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?);
+            cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?;
+            mem::forget((p, q, g));
+            Ok(dsa)
+        }
+    }
+
+    /// Generates DSA params based on the given number of bits.
+    #[corresponds(DSA_generate_parameters_ex)]
+    pub fn generate_params(bits: u32) -> Result<Dsa<Params>, ErrorStack> {
         ffi::init();
         unsafe {
             let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?);
@@ -207,11 +218,31 @@
                 ptr::null_mut(),
                 ptr::null_mut(),
             ))?;
-            cvt(ffi::DSA_generate_key(dsa.0))?;
             Ok(dsa)
         }
     }
 
+    /// Generates a private key based on the DSA params.
+    #[corresponds(DSA_generate_key)]
+    pub fn generate_key(self) -> Result<Dsa<Private>, ErrorStack> {
+        unsafe {
+            let dsa_ptr = self.0;
+            cvt(ffi::DSA_generate_key(dsa_ptr))?;
+            mem::forget(self);
+            Ok(Dsa::from_ptr(dsa_ptr))
+        }
+    }
+}
+
+impl Dsa<Private> {
+    /// Generate a DSA key pair.
+    ///
+    /// The `bits` parameter corresponds to the length of the prime `p`.
+    pub fn generate(bits: u32) -> Result<Dsa<Private>, ErrorStack> {
+        let params = Dsa::generate_params(bits)?;
+        params.generate_key()
+    }
+
     /// Create a DSA key pair with the given parameters
     ///
     /// `p`, `q` and `g` are the common parameters.
@@ -558,6 +589,24 @@
     }
 
     #[test]
+    fn test_params() {
+        let params = Dsa::generate_params(1024).unwrap();
+        let p = params.p().to_owned().unwrap();
+        let q = params.q().to_owned().unwrap();
+        let g = params.g().to_owned().unwrap();
+        let key = params.generate_key().unwrap();
+        let params2 = Dsa::from_pqg(
+            key.p().to_owned().unwrap(),
+            key.q().to_owned().unwrap(),
+            key.g().to_owned().unwrap(),
+        )
+        .unwrap();
+        assert_eq!(p, *params2.p());
+        assert_eq!(q, *params2.q());
+        assert_eq!(g, *params2.g());
+    }
+
+    #[test]
     #[cfg(not(boringssl))]
     fn test_signature() {
         const TEST_DATA: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
diff --git a/src/ec.rs b/src/ec.rs
index 2078542..578cf51 100644
--- a/src/ec.rs
+++ b/src/ec.rs
@@ -15,6 +15,7 @@
 //! [`EcGroup`]: struct.EcGroup.html
 //! [`Nid`]: ../nid/struct.Nid.html
 //! [Elliptic Curve Cryptography]: https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography
+use cfg_if::cfg_if;
 use foreign_types::{ForeignType, ForeignTypeRef};
 use libc::c_int;
 use std::fmt;
@@ -28,6 +29,13 @@
 use crate::{cvt, cvt_n, cvt_p, init};
 use openssl_macros::corresponds;
 
+cfg_if! {
+    if #[cfg(not(boringssl))] {
+        use std::ffi::CString;
+        use crate::string::OpensslString;
+    }
+}
+
 /// Compressed or Uncompressed conversion
 ///
 /// Conversion from the binary value of the point on the curve is performed in one of
@@ -57,7 +65,7 @@
 /// Named Curve or Explicit
 ///
 /// This type acts as a boolean as to whether the `EcGroup` is named or explicit.
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub struct Asn1Flag(c_int);
 
 impl Asn1Flag {
@@ -73,7 +81,7 @@
     ///
     /// OpenSSL documentation at [`EC_GROUP`]
     ///
-    /// [`EC_GROUP`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_seed_len.html
+    /// [`EC_GROUP`]: https://www.openssl.org/docs/manmaster/crypto/EC_GROUP_get_seed_len.html
     pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0);
 
     /// Standard Curves
@@ -187,7 +195,8 @@
     /// a term in the polynomial.  It will be set to 3 `1`s or 5 `1`s depending on
     /// using a trinomial or pentanomial.
     #[corresponds(EC_GROUP_get_curve_GF2m)]
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_EC2M")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))]
+    #[cfg(not(boringssl))]
     pub fn components_gf2m(
         &self,
         p: &mut BigNumRef,
@@ -294,6 +303,12 @@
         }
     }
 
+    /// Gets the flag determining if the group corresponds to a named curve.
+    #[corresponds(EC_GROUP_get_asn1_flag)]
+    pub fn asn1_flag(&self) -> Asn1Flag {
+        unsafe { Asn1Flag(ffi::EC_GROUP_get_asn1_flag(self.as_ptr())) }
+    }
+
     /// Returns the name of the curve, if a name is associated.
     #[corresponds(EC_GROUP_get_curve_name)]
     pub fn curve_name(&self) -> Option<Nid> {
@@ -457,6 +472,26 @@
         }
     }
 
+    /// Serializes the point to a hexadecimal string representation.
+    #[corresponds(EC_POINT_point2hex)]
+    #[cfg(not(boringssl))]
+    pub fn to_hex_str(
+        &self,
+        group: &EcGroupRef,
+        form: PointConversionForm,
+        ctx: &mut BigNumContextRef,
+    ) -> Result<OpensslString, ErrorStack> {
+        unsafe {
+            let buf = cvt_p(ffi::EC_POINT_point2hex(
+                group.as_ptr(),
+                self.as_ptr(),
+                form.0,
+                ctx.as_ptr(),
+            ))?;
+            Ok(OpensslString::from_ptr(buf))
+        }
+    }
+
     /// Creates a new point on the specified curve with the same value.
     #[corresponds(EC_POINT_dup)]
     pub fn to_owned(&self, group: &EcGroupRef) -> Result<EcPoint, ErrorStack> {
@@ -485,7 +520,7 @@
     /// Places affine coordinates of a curve over a prime field in the provided
     /// `x` and `y` `BigNum`s.
     #[corresponds(EC_POINT_get_affine_coordinates)]
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, boringssl, libressl350))]
     pub fn affine_coordinates(
         &self,
         group: &EcGroupRef,
@@ -552,7 +587,8 @@
     /// Places affine coordinates of a curve over a binary field in the provided
     /// `x` and `y` `BigNum`s
     #[corresponds(EC_POINT_get_affine_coordinates_GF2m)]
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_EC2M")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))]
+    #[cfg(not(boringssl))]
     pub fn affine_coordinates_gf2m(
         &self,
         group: &EcGroupRef,
@@ -625,6 +661,27 @@
         }
         Ok(point)
     }
+
+    /// Creates point from a hexadecimal string representation
+    #[corresponds(EC_POINT_hex2point)]
+    #[cfg(not(boringssl))]
+    pub fn from_hex_str(
+        group: &EcGroupRef,
+        s: &str,
+        ctx: &mut BigNumContextRef,
+    ) -> Result<EcPoint, ErrorStack> {
+        let point = EcPoint::new(group)?;
+        unsafe {
+            let c_str = CString::new(s.as_bytes()).unwrap();
+            cvt_p(ffi::EC_POINT_hex2point(
+                group.as_ptr(),
+                c_str.as_ptr() as *const _,
+                point.as_ptr(),
+                ctx.as_ptr(),
+            ))?;
+        }
+        Ok(point)
+    }
 }
 
 generic_foreign_type_and_impl_send_sync! {
@@ -1136,6 +1193,20 @@
     }
 
     #[test]
+    #[cfg(not(boringssl))]
+    fn point_hex_str() {
+        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
+        let key = EcKey::generate(&group).unwrap();
+        let point = key.public_key();
+        let mut ctx = BigNumContext::new().unwrap();
+        let hex = point
+            .to_hex_str(&group, PointConversionForm::COMPRESSED, &mut ctx)
+            .unwrap();
+        let point2 = EcPoint::from_hex_str(&group, &hex, &mut ctx).unwrap();
+        assert!(point.eq(&group, &point2, &mut ctx).unwrap());
+    }
+
+    #[test]
     fn point_owned() {
         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
         let key = EcKey::generate(&group).unwrap();
@@ -1211,7 +1282,7 @@
         assert!(ec_key.check_key().is_ok());
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, boringssl, libressl350))]
     #[test]
     fn get_affine_coordinates() {
         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
@@ -1275,7 +1346,7 @@
     }
 
     #[test]
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_EC2M")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))]
     fn is_on_curve() {
         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
         let mut ctx = BigNumContext::new().unwrap();
@@ -1285,4 +1356,12 @@
         let group2 = EcGroup::from_curve_name(Nid::X9_62_PRIME239V3).unwrap();
         assert!(!g.is_on_curve(&group2, &mut ctx).unwrap());
     }
+
+    #[test]
+    #[cfg(any(boringssl, ossl111, libressl350))]
+    fn asn1_flag() {
+        let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
+        let flag = group.asn1_flag();
+        assert_eq!(flag, Asn1Flag::NAMED_CURVE);
+    }
 }
diff --git a/src/ecdsa.rs b/src/ecdsa.rs
index f3b27b3..3dc17c6 100644
--- a/src/ecdsa.rs
+++ b/src/ecdsa.rs
@@ -158,7 +158,7 @@
     }
 
     #[test]
-    #[cfg_attr(osslconf = "OPENSSL_NO_EC2M", ignore)]
+    #[cfg_attr(osslconf = "OPENSSL_NO_EC", ignore)]
     fn sign_and_verify() {
         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
         let private_key = EcKey::generate(&group).unwrap();
@@ -186,7 +186,7 @@
     }
 
     #[test]
-    #[cfg_attr(osslconf = "OPENSSL_NO_EC2M", ignore)]
+    #[cfg_attr(osslconf = "OPENSSL_NO_EC", ignore)]
     fn check_private_components() {
         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
         let private_key = EcKey::generate(&group).unwrap();
@@ -206,7 +206,7 @@
     }
 
     #[test]
-    #[cfg_attr(osslconf = "OPENSSL_NO_EC2M", ignore)]
+    #[cfg_attr(osslconf = "OPENSSL_NO_EC", ignore)]
     fn serialize_deserialize() {
         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
         let private_key = EcKey::generate(&group).unwrap();
diff --git a/src/encrypt.rs b/src/encrypt.rs
index 34a9eb8..2e02041 100644
--- a/src/encrypt.rs
+++ b/src/encrypt.rs
@@ -40,7 +40,7 @@
 //! assert_eq!(&*decrypted, data);
 //! ```
 #[cfg(any(ossl102, libressl310))]
-use libc::{c_int, c_void};
+use libc::c_int;
 use std::{marker::PhantomData, ptr};
 
 use crate::error::ErrorStack;
@@ -113,7 +113,7 @@
     ///
     /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`].
     ///
-    /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html
+    /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_padding.html
     pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(
@@ -174,7 +174,7 @@
 
             cvt(ffi::EVP_PKEY_CTX_set0_rsa_oaep_label(
                 self.pctx,
-                p as *mut c_void,
+                p,
                 label.len() as c_int,
             ))
             .map(|_| ())
@@ -317,7 +317,7 @@
     ///
     /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`].
     ///
-    /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html
+    /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_padding.html
     pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(
@@ -378,7 +378,7 @@
 
             cvt(ffi::EVP_PKEY_CTX_set0_rsa_oaep_label(
                 self.pctx,
-                p as *mut c_void,
+                p,
                 label.len() as c_int,
             ))
             .map(|_| ())
diff --git a/src/error.rs b/src/error.rs
index 58b4d70..e097ce6 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -198,11 +198,7 @@
                 self.line,
                 self.func.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
             );
-            ffi::ERR_set_error(
-                ffi::ERR_GET_LIB(self.code),
-                ffi::ERR_GET_REASON(self.code),
-                ptr::null(),
-            );
+            ffi::ERR_set_error(self.library_code(), self.reason_code(), ptr::null());
         }
     }
 
@@ -214,9 +210,9 @@
         let line = self.line.try_into().unwrap();
         unsafe {
             ffi::ERR_put_error(
-                ffi::ERR_GET_LIB(self.code),
+                self.library_code(),
                 ffi::ERR_GET_FUNC(self.code),
-                ffi::ERR_GET_REASON(self.code),
+                self.reason_code(),
                 self.file.as_ptr(),
                 line,
             );
@@ -240,6 +236,15 @@
         }
     }
 
+    /// Returns the raw OpenSSL error constant for the library reporting the
+    /// error.
+    // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on
+    // OpenSSL/LibreSSL they're safe.
+    #[allow(unused_unsafe)]
+    pub fn library_code(&self) -> libc::c_int {
+        unsafe { ffi::ERR_GET_LIB(self.code) }
+    }
+
     /// Returns the name of the function reporting the error.
     pub fn function(&self) -> Option<RetStr<'_>> {
         self.func.as_ref().map(|s| s.as_str())
@@ -257,6 +262,14 @@
         }
     }
 
+    /// Returns the raw OpenSSL error constant for the reason for the error.
+    // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on
+    // OpenSSL/LibreSSL they're safe.
+    #[allow(unused_unsafe)]
+    pub fn reason_code(&self) -> libc::c_int {
+        unsafe { ffi::ERR_GET_REASON(self.code) }
+    }
+
     /// Returns the name of the source file which encountered the error.
     pub fn file(&self) -> RetStr<'_> {
         self.file.as_str()
@@ -297,19 +310,22 @@
 }
 
 impl fmt::Display for Error {
+    // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on
+    // OpenSSL/LibreSSL they're safe.
+    #[allow(unused_unsafe)]
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(fmt, "error:{:08X}", self.code())?;
         match self.library() {
             Some(l) => write!(fmt, ":{}", l)?,
-            None => write!(fmt, ":lib({})", ffi::ERR_GET_LIB(self.code()))?,
+            None => write!(fmt, ":lib({})", self.library_code())?,
         }
         match self.function() {
             Some(f) => write!(fmt, ":{}", f)?,
-            None => write!(fmt, ":func({})", ffi::ERR_GET_FUNC(self.code()))?,
+            None => write!(fmt, ":func({})", unsafe { ffi::ERR_GET_FUNC(self.code()) })?,
         }
         match self.reason() {
             Some(r) => write!(fmt, ":{}", r)?,
-            None => write!(fmt, ":reason({})", ffi::ERR_GET_REASON(self.code()))?,
+            None => write!(fmt, ":reason({})", self.reason_code())?,
         }
         write!(
             fmt,
@@ -382,3 +398,21 @@
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    #[cfg(not(ossl310))]
+    use crate::nid::Nid;
+
+    #[test]
+    // Due to a bug in OpenSSL 3.1.0, this test can hang there. Skip for now.
+    #[cfg(not(ossl310))]
+    fn test_error_library_code() {
+        let stack = Nid::create("not-an-oid", "invalid", "invalid").unwrap_err();
+        let errors = stack.errors();
+        #[cfg(not(boringssl))]
+        assert_eq!(errors[0].library_code(), ffi::ERR_LIB_ASN1);
+        #[cfg(boringssl)]
+        assert_eq!(errors[0].library_code(), ffi::ERR_LIB_OBJ as libc::c_int);
+    }
+}
diff --git a/src/hash.rs b/src/hash.rs
index 7f6fa89..4caa251 100644
--- a/src/hash.rs
+++ b/src/hash.rs
@@ -43,7 +43,7 @@
 use crate::{cvt, cvt_p};
 
 cfg_if! {
-    if #[cfg(any(ossl110, boringssl))] {
+    if #[cfg(any(ossl110, boringssl, libressl382))] {
         use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new};
     } else {
         use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free};
@@ -68,7 +68,7 @@
     ///
     /// This corresponds to [`EVP_get_digestbynid`].
     ///
-    /// [`EVP_get_digestbynid`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestInit.html
+    /// [`EVP_get_digestbynid`]: https://www.openssl.org/docs/manmaster/crypto/EVP_DigestInit.html
     pub fn from_nid(type_: Nid) -> Option<MessageDigest> {
         unsafe {
             let ptr = ffi::EVP_get_digestbynid(type_.as_raw());
@@ -84,7 +84,7 @@
     ///
     /// This corresponds to [`EVP_get_digestbyname`].
     ///
-    /// [`EVP_get_digestbyname`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestInit.html
+    /// [`EVP_get_digestbyname`]: https://www.openssl.org/docs/manmaster/crypto/EVP_DigestInit.html
     pub fn from_name(name: &str) -> Option<MessageDigest> {
         ffi::init();
         let name = CString::new(name).ok()?;
@@ -127,22 +127,22 @@
         unsafe { MessageDigest(ffi::EVP_sha512()) }
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     pub fn sha3_224() -> MessageDigest {
         unsafe { MessageDigest(ffi::EVP_sha3_224()) }
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     pub fn sha3_256() -> MessageDigest {
         unsafe { MessageDigest(ffi::EVP_sha3_256()) }
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     pub fn sha3_384() -> MessageDigest {
         unsafe { MessageDigest(ffi::EVP_sha3_384()) }
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     pub fn sha3_512() -> MessageDigest {
         unsafe { MessageDigest(ffi::EVP_sha3_512()) }
     }
@@ -157,7 +157,8 @@
         unsafe { MessageDigest(ffi::EVP_shake256()) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_RMD160")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_RMD160"))]
+    #[cfg(not(boringssl))]
     pub fn ripemd160() -> MessageDigest {
         unsafe { MessageDigest(ffi::EVP_ripemd160()) }
     }
@@ -624,7 +625,7 @@
         );
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     #[test]
     fn test_sha3_224() {
         let tests = [(
@@ -644,7 +645,7 @@
         );
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     #[test]
     fn test_sha3_256() {
         let tests = [(
@@ -664,7 +665,7 @@
         );
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     #[test]
     fn test_sha3_384() {
         let tests = [("416c6c20796f75722062617365206172652062656c6f6e6720746f207573",
@@ -684,7 +685,7 @@
         );
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     #[test]
     fn test_sha3_512() {
         let tests = [("416c6c20796f75722062617365206172652062656c6f6e6720746f207573",
@@ -745,7 +746,7 @@
     }
 
     #[test]
-    #[cfg(not(boringssl))]
+    #[cfg(not(osslconf = "OPENSSL_NO_RMD160"))]
     #[cfg_attr(ossl300, ignore)]
     fn test_ripemd160() {
         #[cfg(ossl300)]
diff --git a/src/lib.rs b/src/lib.rs
index a5d3523..42f289b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,7 @@
 //! Bindings to OpenSSL
 //!
 //! This crate provides a safe interface to the popular OpenSSL cryptography library. OpenSSL versions 1.0.1 through
-//! 3.x.x and LibreSSL versions 2.5 through 3.4.1 are supported.
+//! 3.x.x and LibreSSL versions 2.5 through 3.7.x are supported.
 //!
 //! # Building
 //!
@@ -29,7 +29,7 @@
 //!
 //! ```not_rust
 //! # macOS (Homebrew)
-//! $ brew install openssl@1.1
+//! $ brew install openssl@3
 //!
 //! # macOS (MacPorts)
 //! $ sudo port install openssl
@@ -44,10 +44,13 @@
 //! $ sudo apt-get install pkg-config libssl-dev
 //!
 //! # Fedora
-//! $ sudo dnf install pkg-config openssl-devel
+//! $ sudo dnf install pkg-config perl-FindBin openssl-devel
 //!
 //! # Alpine Linux
 //! $ apk add pkgconfig openssl-dev
+//!
+//! # openSUSE
+//! $ sudo zypper in libopenssl-devel
 //! ```
 //!
 //! ## Manual
@@ -119,6 +122,7 @@
 //! ```
 #![doc(html_root_url = "https://docs.rs/openssl/0.10")]
 #![warn(rust_2018_idioms)]
+#![allow(clippy::uninlined_format_args, clippy::needless_doctest_main)]
 
 #[cfg(all(soong, boringssl))]
 extern crate bssl_sys as ffi;
@@ -127,6 +131,8 @@
 pub use ffi::init;
 
 use libc::c_int;
+#[cfg(ossl300)]
+use libc::c_long;
 
 use crate::error::ErrorStack;
 
@@ -142,7 +148,7 @@
 pub mod bn;
 pub mod cipher;
 pub mod cipher_ctx;
-#[cfg(all(not(boringssl), not(libressl), not(osslconf = "OPENSSL_NO_CMS")))]
+#[cfg(all(not(libressl), not(osslconf = "OPENSSL_NO_CMS"), not(boringssl)))]
 pub mod cms;
 pub mod conf;
 pub mod derive;
@@ -168,10 +174,9 @@
 pub mod md_ctx;
 pub mod memcmp;
 pub mod nid;
-#[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_OCSP")))]
+#[cfg(all(not(osslconf = "OPENSSL_NO_OCSP"), not(boringssl)))]
 pub mod ocsp;
 pub mod pkcs12;
-#[cfg(not(boringssl))]
 pub mod pkcs5;
 #[cfg(not(boringssl))]
 pub mod pkcs7;
@@ -197,9 +202,9 @@
 type LenType = libc::c_int;
 
 #[cfg(boringssl)]
-type SignedLenType = libc::ssize_t;
+type SLenType = libc::ssize_t;
 #[cfg(not(boringssl))]
-type SignedLenType = libc::c_int;
+type SLenType = libc::c_int;
 
 #[inline]
 fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> {
@@ -228,6 +233,19 @@
     }
 }
 
+// cvt_long is currently only used in functions that require openssl >= 3.0.0,
+// so this cfg statement is used to avoid "unused function" errors when
+// compiling with openssl < 3.0.0
+#[inline]
+#[cfg(ossl300)]
+fn cvt_long(r: c_long) -> Result<c_long, ErrorStack> {
+    if r <= 0 {
+        Err(ErrorStack::get())
+    } else {
+        Ok(r)
+    }
+}
+
 #[inline]
 fn cvt_n(r: c_int) -> Result<c_int, ErrorStack> {
     if r < 0 {
diff --git a/src/md.rs b/src/md.rs
index 4ade8e8..3ce3c25 100644
--- a/src/md.rs
+++ b/src/md.rs
@@ -150,25 +150,25 @@
         unsafe { MdRef::from_ptr(ffi::EVP_sha512() as *mut _) }
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     #[inline]
     pub fn sha3_224() -> &'static MdRef {
         unsafe { MdRef::from_ptr(ffi::EVP_sha3_224() as *mut _) }
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     #[inline]
     pub fn sha3_256() -> &'static MdRef {
         unsafe { MdRef::from_ptr(ffi::EVP_sha3_256() as *mut _) }
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     #[inline]
     pub fn sha3_384() -> &'static MdRef {
         unsafe { MdRef::from_ptr(ffi::EVP_sha3_384() as *mut _) }
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     #[inline]
     pub fn sha3_512() -> &'static MdRef {
         unsafe { MdRef::from_ptr(ffi::EVP_sha3_512() as *mut _) }
@@ -187,15 +187,14 @@
     }
 
     #[cfg(not(osslconf = "OPENSSL_NO_RMD160"))]
-    #[inline]
     #[cfg(not(boringssl))]
+    #[inline]
     pub fn ripemd160() -> &'static MdRef {
         unsafe { MdRef::from_ptr(ffi::EVP_ripemd160() as *mut _) }
     }
 
     #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM3")))]
     #[inline]
-    #[cfg(not(boringssl))]
     pub fn sm3() -> &'static MdRef {
         unsafe { MdRef::from_ptr(ffi::EVP_sm3() as *mut _) }
     }
diff --git a/src/md_ctx.rs b/src/md_ctx.rs
index 156f3c2..30e0337 100644
--- a/src/md_ctx.rs
+++ b/src/md_ctx.rs
@@ -93,7 +93,7 @@
 use std::ptr;
 
 cfg_if! {
-    if #[cfg(any(ossl110, boringssl))] {
+    if #[cfg(any(ossl110, boringssl, libressl382))] {
         use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new};
     } else {
         use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free};
diff --git a/src/nid.rs b/src/nid.rs
index eadae31..e50feb0 100644
--- a/src/nid.rs
+++ b/src/nid.rs
@@ -44,20 +44,20 @@
 /// The following documentation provides context about `Nid`s and their usage
 /// in OpenSSL.
 ///
-/// - [Obj_nid2obj](https://www.openssl.org/docs/man1.1.0/crypto/OBJ_create.html)
+/// - [Obj_nid2obj](https://www.openssl.org/docs/manmaster/crypto/OBJ_create.html)
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct Nid(c_int);
 
 #[allow(non_snake_case)]
 impl Nid {
     /// Create a `Nid` from an integer representation.
-    pub fn from_raw(raw: c_int) -> Nid {
+    pub const fn from_raw(raw: c_int) -> Nid {
         Nid(raw)
     }
 
     /// Return the integer representation of a `Nid`.
     #[allow(clippy::trivially_copy_pass_by_ref)]
-    pub fn as_raw(&self) -> c_int {
+    pub const fn as_raw(&self) -> c_int {
         self.0
     }
 
@@ -215,11 +215,13 @@
     pub const SECT409R1: Nid = Nid(ffi::NID_sect409r1);
     pub const SECT571K1: Nid = Nid(ffi::NID_sect571k1);
     pub const SECT571R1: Nid = Nid(ffi::NID_sect571r1);
-    #[cfg(ossl110)]
+    #[cfg(any(ossl110, libressl))]
     pub const BRAINPOOL_P256R1: Nid = Nid(ffi::NID_brainpoolP256r1);
-    #[cfg(ossl110)]
+    #[cfg(any(ossl110, libressl))]
+    pub const BRAINPOOL_P320R1: Nid = Nid(ffi::NID_brainpoolP320r1);
+    #[cfg(any(ossl110, libressl))]
     pub const BRAINPOOL_P384R1: Nid = Nid(ffi::NID_brainpoolP384r1);
-    #[cfg(ossl110)]
+    #[cfg(any(ossl110, libressl))]
     pub const BRAINPOOL_P512R1: Nid = Nid(ffi::NID_brainpoolP512r1);
     pub const WAP_WSG_IDM_ECID_WTLS1: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls1);
     pub const WAP_WSG_IDM_ECID_WTLS3: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls3);
@@ -1074,20 +1076,24 @@
     pub const AES_128_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_128_cbc_hmac_sha1);
     pub const AES_192_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_192_cbc_hmac_sha1);
     pub const AES_256_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_256_cbc_hmac_sha1);
+    #[cfg(ossl111)]
+    pub const SM2: Nid = Nid(ffi::NID_sm2);
     #[cfg(any(ossl111, libressl291))]
     pub const SM3: Nid = Nid(ffi::NID_sm3);
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     pub const SHA3_224: Nid = Nid(ffi::NID_sha3_224);
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     pub const SHA3_256: Nid = Nid(ffi::NID_sha3_256);
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     pub const SHA3_384: Nid = Nid(ffi::NID_sha3_384);
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl380))]
     pub const SHA3_512: Nid = Nid(ffi::NID_sha3_512);
     #[cfg(ossl111)]
     pub const SHAKE128: Nid = Nid(ffi::NID_shake128);
     #[cfg(ossl111)]
     pub const SHAKE256: Nid = Nid(ffi::NID_shake256);
+    #[cfg(any(ossl110, libressl271))]
+    pub const CHACHA20_POLY1305: Nid = Nid(ffi::NID_chacha20_poly1305);
 }
 
 #[cfg(test)]
@@ -1165,10 +1171,13 @@
         assert_eq!(nid.short_name().unwrap(), "foo");
         assert_eq!(nid.long_name().unwrap(), "foobar");
 
-        let invalid_oid = Nid::create("invalid_oid", "invalid", "invalid");
-        assert!(
-            invalid_oid.is_err(),
-            "invalid_oid should not return a valid value"
-        );
+        // Due to a bug in OpenSSL 3.1.0, this test crashes on Windows
+        if !cfg!(ossl310) {
+            let invalid_oid = Nid::create("invalid_oid", "invalid", "invalid");
+            assert!(
+                invalid_oid.is_err(),
+                "invalid_oid should not return a valid value"
+            );
+        }
     }
 }
diff --git a/src/ocsp.rs b/src/ocsp.rs
index 7506d34..93a5d36 100644
--- a/src/ocsp.rs
+++ b/src/ocsp.rs
@@ -15,6 +15,8 @@
 use openssl_macros::corresponds;
 
 bitflags! {
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct OcspFlag: c_ulong {
         const NO_CERTS = ffi::OCSP_NOCERTS;
         const NO_INTERN = ffi::OCSP_NOINTERN;
diff --git a/src/pkcs12.rs b/src/pkcs12.rs
index d4e19dc..5f171da 100644
--- a/src/pkcs12.rs
+++ b/src/pkcs12.rs
@@ -32,30 +32,42 @@
         ffi::i2d_PKCS12
     }
 
+    /// Deprecated.
+    #[deprecated(note = "Use parse2 instead", since = "0.10.46")]
+    #[allow(deprecated)]
+    pub fn parse(&self, pass: &str) -> Result<ParsedPkcs12, ErrorStack> {
+        let parsed = self.parse2(pass)?;
+
+        Ok(ParsedPkcs12 {
+            pkey: parsed.pkey.unwrap(),
+            cert: parsed.cert.unwrap(),
+            chain: parsed.ca,
+        })
+    }
+
     /// Extracts the contents of the `Pkcs12`.
     #[corresponds(PKCS12_parse)]
-    pub fn parse(&self, pass: &str) -> Result<ParsedPkcs12, ErrorStack> {
+    pub fn parse2(&self, pass: &str) -> Result<ParsedPkcs12_2, ErrorStack> {
         unsafe {
             let pass = CString::new(pass.as_bytes()).unwrap();
 
             let mut pkey = ptr::null_mut();
             let mut cert = ptr::null_mut();
-            let mut chain = ptr::null_mut();
+            let mut ca = ptr::null_mut();
 
             cvt(ffi::PKCS12_parse(
                 self.as_ptr(),
                 pass.as_ptr(),
                 &mut pkey,
                 &mut cert,
-                &mut chain,
+                &mut ca,
             ))?;
 
-            let pkey = PKey::from_ptr(pkey);
-            let cert = X509::from_ptr(cert);
+            let pkey = PKey::from_ptr_opt(pkey);
+            let cert = X509::from_ptr_opt(cert);
+            let ca = Stack::from_ptr_opt(ca);
 
-            let chain = Stack::from_ptr_opt(chain);
-
-            Ok(ParsedPkcs12 { pkey, cert, chain })
+            Ok(ParsedPkcs12_2 { pkey, cert, ca })
         }
     }
 }
@@ -82,34 +94,78 @@
         ffi::init();
 
         Pkcs12Builder {
+            name: None,
+            pkey: None,
+            cert: None,
+            ca: None,
             nid_key: Nid::UNDEF,
             nid_cert: Nid::UNDEF,
             iter: ffi::PKCS12_DEFAULT_ITER,
             mac_iter: ffi::PKCS12_DEFAULT_ITER,
             #[cfg(not(boringssl))]
             mac_md: None,
-            ca: None,
         }
     }
 }
 
+#[deprecated(note = "Use ParsedPkcs12_2 instead", since = "0.10.46")]
 pub struct ParsedPkcs12 {
     pub pkey: PKey<Private>,
     pub cert: X509,
     pub chain: Option<Stack<X509>>,
 }
 
+pub struct ParsedPkcs12_2 {
+    pub pkey: Option<PKey<Private>>,
+    pub cert: Option<X509>,
+    pub ca: Option<Stack<X509>>,
+}
+
 pub struct Pkcs12Builder {
+    // FIXME borrow
+    name: Option<CString>,
+    pkey: Option<PKey<Private>>,
+    cert: Option<X509>,
+    ca: Option<Stack<X509>>,
     nid_key: Nid,
     nid_cert: Nid,
     iter: c_int,
     mac_iter: c_int,
+    // FIXME remove
     #[cfg(not(boringssl))]
     mac_md: Option<MessageDigest>,
-    ca: Option<Stack<X509>>,
 }
 
 impl Pkcs12Builder {
+    /// The `friendlyName` used for the certificate and private key.
+    pub fn name(&mut self, name: &str) -> &mut Self {
+        self.name = Some(CString::new(name).unwrap());
+        self
+    }
+
+    /// The private key.
+    pub fn pkey<T>(&mut self, pkey: &PKeyRef<T>) -> &mut Self
+    where
+        T: HasPrivate,
+    {
+        let new_pkey = unsafe { PKeyRef::from_ptr(pkey.as_ptr()) };
+        self.pkey = Some(new_pkey.to_owned());
+        self
+    }
+
+    /// The certificate.
+    pub fn cert(&mut self, cert: &X509Ref) -> &mut Self {
+        self.cert = Some(cert.to_owned());
+        self
+    }
+
+    /// An additional set of certificates to include in the archive beyond the one provided to
+    /// `build`.
+    pub fn ca(&mut self, ca: Stack<X509>) -> &mut Self {
+        self.ca = Some(ca);
+        self
+    }
+
     /// The encryption algorithm that should be used for the key
     pub fn key_algorithm(&mut self, nid: Nid) -> &mut Self {
         self.nid_key = nid;
@@ -144,24 +200,13 @@
         self
     }
 
-    /// An additional set of certificates to include in the archive beyond the one provided to
-    /// `build`.
-    pub fn ca(&mut self, ca: Stack<X509>) -> &mut Self {
-        self.ca = Some(ca);
-        self
-    }
-
-    /// Builds the PKCS #12 object
-    ///
-    /// # Arguments
-    ///
-    /// * `password` - the password used to encrypt the key and certificate
-    /// * `friendly_name` - user defined name for the certificate
-    /// * `pkey` - key to store
-    /// * `cert` - certificate to store
-    #[corresponds(PKCS12_create)]
+    /// Deprecated.
+    #[deprecated(
+        note = "Use Self::{name, pkey, cert, build2} instead.",
+        since = "0.10.46"
+    )]
     pub fn build<T>(
-        self,
+        mut self,
         password: &str,
         friendly_name: &str,
         pkey: &PKeyRef<T>,
@@ -170,11 +215,21 @@
     where
         T: HasPrivate,
     {
+        self.name(friendly_name)
+            .pkey(pkey)
+            .cert(cert)
+            .build2(password)
+    }
+
+    /// Builds the PKCS#12 object.
+    #[corresponds(PKCS12_create)]
+    pub fn build2(&self, password: &str) -> Result<Pkcs12, ErrorStack> {
         unsafe {
             let pass = CString::new(password).unwrap();
-            let friendly_name = CString::new(friendly_name).unwrap();
-            let pkey = pkey.as_ptr();
-            let cert = cert.as_ptr();
+            let pass = pass.as_ptr();
+            let friendly_name = self.name.as_ref().map_or(ptr::null(), |p| p.as_ptr());
+            let pkey = self.pkey.as_ref().map_or(ptr::null(), |p| p.as_ptr());
+            let cert = self.cert.as_ref().map_or(ptr::null(), |p| p.as_ptr());
             let ca = self
                 .ca
                 .as_ref()
@@ -185,14 +240,14 @@
 
             // According to the OpenSSL docs, keytype is a non-standard extension for MSIE,
             // It's values are KEY_SIG or KEY_EX, see the OpenSSL docs for more information:
-            // https://www.openssl.org/docs/man1.0.2/crypto/PKCS12_create.html
+            // https://www.openssl.org/docs/manmaster/crypto/PKCS12_create.html
             let keytype = 0;
 
             let pkcs12 = cvt_p(ffi::PKCS12_create(
-                pass.as_ptr() as *const _ as *mut _,
-                friendly_name.as_ptr() as *const _ as *mut _,
-                pkey,
-                cert,
+                pass as *mut _,
+                friendly_name as *mut _,
+                pkey as *mut _,
+                cert as *mut _,
                 ca,
                 nid_key,
                 nid_cert,
@@ -213,7 +268,7 @@
 
                 cvt(ffi::PKCS12_set_mac(
                     pkcs12.as_ptr(),
-                    pass.as_ptr(),
+                    pass,
                     -1,
                     ptr::null_mut(),
                     0,
@@ -246,14 +301,25 @@
 
         let der = include_bytes!("../test/identity.p12");
         let pkcs12 = Pkcs12::from_der(der).unwrap();
-        let parsed = pkcs12.parse("mypass").unwrap();
+        let parsed = pkcs12.parse2("mypass").unwrap();
 
         assert_eq!(
-            hex::encode(parsed.cert.digest(MessageDigest::sha1()).unwrap()),
+            hex::encode(
+                parsed
+                    .cert
+                    .as_ref()
+                    .unwrap()
+                    .digest(MessageDigest::sha1())
+                    .unwrap()
+            ),
             "59172d9313e84459bcff27f967e79e6e9217e584"
         );
+        assert_eq!(
+            parsed.cert.as_ref().unwrap().alias(),
+            Some(b"foobar.com" as &[u8])
+        );
 
-        let chain = parsed.chain.unwrap();
+        let chain = parsed.ca.unwrap();
         assert_eq!(chain.len(), 1);
         assert_eq!(
             hex::encode(chain[0].digest(MessageDigest::sha1()).unwrap()),
@@ -268,8 +334,8 @@
 
         let der = include_bytes!("../test/keystore-empty-chain.p12");
         let pkcs12 = Pkcs12::from_der(der).unwrap();
-        let parsed = pkcs12.parse("cassandra").unwrap();
-        if let Some(stack) = parsed.chain {
+        let parsed = pkcs12.parse2("cassandra").unwrap();
+        if let Some(stack) = parsed.ca {
             assert_eq!(stack.len(), 0);
         }
     }
@@ -302,19 +368,36 @@
         builder.sign(&pkey, MessageDigest::sha256()).unwrap();
         let cert = builder.build();
 
-        let pkcs12_builder = Pkcs12::builder();
-        let pkcs12 = pkcs12_builder
-            .build("mypass", subject_name, &pkey, &cert)
+        let pkcs12 = Pkcs12::builder()
+            .name(subject_name)
+            .pkey(&pkey)
+            .cert(&cert)
+            .build2("mypass")
             .unwrap();
         let der = pkcs12.to_der().unwrap();
 
         let pkcs12 = Pkcs12::from_der(&der).unwrap();
-        let parsed = pkcs12.parse("mypass").unwrap();
+        let parsed = pkcs12.parse2("mypass").unwrap();
 
         assert_eq!(
-            &*parsed.cert.digest(MessageDigest::sha1()).unwrap(),
+            &*parsed.cert.unwrap().digest(MessageDigest::sha1()).unwrap(),
             &*cert.digest(MessageDigest::sha1()).unwrap()
         );
-        assert!(parsed.pkey.public_eq(&pkey));
+        assert!(parsed.pkey.unwrap().public_eq(&pkey));
+    }
+
+    #[test]
+    fn create_only_ca() {
+        let ca = include_bytes!("../test/root-ca.pem");
+        let ca = X509::from_pem(ca).unwrap();
+        let mut chain = Stack::new().unwrap();
+        chain.push(ca).unwrap();
+
+        let pkcs12 = Pkcs12::builder().ca(chain).build2("hunter2").unwrap();
+        let parsed = pkcs12.parse2("hunter2").unwrap();
+
+        assert!(parsed.cert.is_none());
+        assert!(parsed.pkey.is_none());
+        assert_eq!(parsed.ca.unwrap().len(), 1);
     }
 }
diff --git a/src/pkcs5.rs b/src/pkcs5.rs
index c15ce47..afaae55 100644
--- a/src/pkcs5.rs
+++ b/src/pkcs5.rs
@@ -1,9 +1,13 @@
+#[cfg(not(boringssl))]
 use libc::c_int;
+use std::convert::TryInto;
+#[cfg(not(boringssl))]
 use std::ptr;
 
 use crate::cvt;
 use crate::error::ErrorStack;
 use crate::hash::MessageDigest;
+#[cfg(not(boringssl))]
 use crate::symm::Cipher;
 use openssl_macros::corresponds;
 
@@ -25,6 +29,7 @@
 /// `pbkdf2_hmac` or another more modern key derivation algorithm.
 #[corresponds(EVP_BytesToKey)]
 #[allow(clippy::useless_conversion)]
+#[cfg(not(boringssl))]
 pub fn bytes_to_key(
     cipher: Cipher,
     digest: MessageDigest,
@@ -91,19 +96,15 @@
     key: &mut [u8],
 ) -> Result<(), ErrorStack> {
     unsafe {
-        assert!(pass.len() <= c_int::max_value() as usize);
-        assert!(salt.len() <= c_int::max_value() as usize);
-        assert!(key.len() <= c_int::max_value() as usize);
-
         ffi::init();
         cvt(ffi::PKCS5_PBKDF2_HMAC(
             pass.as_ptr() as *const _,
-            pass.len() as c_int,
+            pass.len().try_into().unwrap(),
             salt.as_ptr(),
-            salt.len() as c_int,
-            iter as c_int,
+            salt.len().try_into().unwrap(),
+            iter.try_into().unwrap(),
             hash.as_ptr(),
-            key.len() as c_int,
+            key.len().try_into().unwrap(),
             key.as_mut_ptr(),
         ))
         .map(|_| ())
@@ -114,7 +115,8 @@
 ///
 /// Requires OpenSSL 1.1.0 or newer.
 #[corresponds(EVP_PBE_scrypt)]
-#[cfg(any(ossl110))]
+#[cfg(all(any(ossl110, boringssl), not(osslconf = "OPENSSL_NO_SCRYPT")))]
+#[allow(clippy::useless_conversion)]
 pub fn scrypt(
     pass: &[u8],
     salt: &[u8],
@@ -134,7 +136,7 @@
             n,
             r,
             p,
-            maxmem,
+            maxmem.try_into().unwrap(),
             key.as_mut_ptr() as *mut _,
             key.len(),
         ))
@@ -145,6 +147,7 @@
 #[cfg(test)]
 mod tests {
     use crate::hash::MessageDigest;
+    #[cfg(not(boringssl))]
     use crate::symm::Cipher;
 
     // Test vectors from
@@ -246,6 +249,7 @@
     }
 
     #[test]
+    #[cfg(not(boringssl))]
     fn bytes_to_key() {
         let salt = [16_u8, 34_u8, 19_u8, 23_u8, 141_u8, 4_u8, 207_u8, 221_u8];
 
@@ -282,7 +286,7 @@
     }
 
     #[test]
-    #[cfg(any(ossl110))]
+    #[cfg(any(ossl110, boringssl))]
     fn scrypt() {
         let pass = "pleaseletmein";
         let salt = "SodiumChloride";
diff --git a/src/pkcs7.rs b/src/pkcs7.rs
index ae4571d..65a6e73 100644
--- a/src/pkcs7.rs
+++ b/src/pkcs7.rs
@@ -4,17 +4,32 @@
 use std::mem;
 use std::ptr;
 
+use crate::asn1::Asn1ObjectRef;
 use crate::bio::{MemBio, MemBioSlice};
 use crate::error::ErrorStack;
+use crate::nid::Nid;
 use crate::pkey::{HasPrivate, PKeyRef};
-use crate::stack::{Stack, StackRef};
+use crate::stack::{Stack, StackRef, Stackable};
 use crate::symm::Cipher;
+use crate::util::ForeignTypeRefExt;
 use crate::x509::store::X509StoreRef;
 use crate::x509::{X509Ref, X509};
 use crate::{cvt, cvt_p};
 use openssl_macros::corresponds;
 
 foreign_type_and_impl_send_sync! {
+    type CType = ffi::PKCS7_SIGNER_INFO;
+    fn drop = ffi::PKCS7_SIGNER_INFO_free;
+
+    pub struct Pkcs7SignerInfo;
+    pub struct Pkcs7SignerInfoRef;
+}
+
+impl Stackable for Pkcs7SignerInfo {
+    type StackType = ffi::stack_st_PKCS7_SIGNER_INFO;
+}
+
+foreign_type_and_impl_send_sync! {
     type CType = ffi::PKCS7;
     fn drop = ffi::PKCS7_free;
 
@@ -27,7 +42,22 @@
     pub struct Pkcs7Ref;
 }
 
+foreign_type_and_impl_send_sync! {
+    type CType = ffi::PKCS7_SIGNED;
+    fn drop = ffi::PKCS7_SIGNED_free;
+
+    /// A PKCS#7 signed data structure.
+    ///
+    /// Contains signed data.
+    pub struct Pkcs7Signed;
+
+    /// Reference to `Pkcs7Signed`
+    pub struct Pkcs7SignedRef;
+}
+
 bitflags! {
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct Pkcs7Flags: c_int {
         const TEXT = ffi::PKCS7_TEXT;
         const NOCERTS = ffi::PKCS7_NOCERTS;
@@ -111,7 +141,7 @@
                 certs.as_ptr(),
                 input_bio.as_ptr(),
                 cipher.as_ptr(),
-                flags.bits,
+                flags.bits(),
             ))
             .map(Pkcs7)
         }
@@ -141,7 +171,7 @@
                 pkey.as_ptr(),
                 certs.as_ptr(),
                 input_bio.as_ptr(),
-                flags.bits,
+                flags.bits(),
             ))
             .map(Pkcs7)
         }
@@ -159,7 +189,7 @@
                 output.as_ptr(),
                 self.as_ptr(),
                 input_bio.as_ptr(),
-                flags.bits,
+                flags.bits(),
             ))
             .map(|_| output.get_buf().to_owned())
         }
@@ -205,7 +235,7 @@
                 pkey.as_ptr(),
                 cert.as_ptr(),
                 output.as_ptr(),
-                flags.bits,
+                flags.bits(),
             ))
             .map(|_| output.get_buf().to_owned())
         }
@@ -241,7 +271,7 @@
                 store.as_ptr(),
                 indata_bio_ptr,
                 out_bio.as_ptr(),
-                flags.bits,
+                flags.bits(),
             ))
             .map(|_| ())?
         }
@@ -265,7 +295,7 @@
             let ptr = cvt_p(ffi::PKCS7_get0_signers(
                 self.as_ptr(),
                 certs.as_ptr(),
-                flags.bits,
+                flags.bits(),
             ))?;
 
             // The returned stack is owned by the caller, but the certs inside are not! Our stack interface can't deal
@@ -279,11 +309,43 @@
             Ok(stack)
         }
     }
+
+    /// Return the type of a PKCS#7 structure as an Asn1Object
+    pub fn type_(&self) -> Option<&Asn1ObjectRef> {
+        unsafe {
+            let ptr = (*self.as_ptr()).type_;
+            Asn1ObjectRef::from_const_ptr_opt(ptr)
+        }
+    }
+
+    /// Get the signed data of a PKCS#7 structure of type PKCS7_SIGNED
+    pub fn signed(&self) -> Option<&Pkcs7SignedRef> {
+        unsafe {
+            if self.type_().map(|x| x.nid()) != Some(Nid::PKCS7_SIGNED) {
+                return None;
+            }
+            let signed_data = (*self.as_ptr()).d.sign;
+            Pkcs7SignedRef::from_const_ptr_opt(signed_data)
+        }
+    }
+}
+
+impl Pkcs7SignedRef {
+    /// Get the stack of certificates from the PKCS7_SIGNED object
+    pub fn certificates(&self) -> Option<&StackRef<X509>> {
+        unsafe {
+            self.as_ptr()
+                .as_ref()
+                .and_then(|x| x.cert.as_mut())
+                .and_then(|x| StackRef::<X509>::from_const_ptr_opt(x))
+        }
+    }
 }
 
 #[cfg(test)]
 mod tests {
     use crate::hash::MessageDigest;
+    use crate::nid::Nid;
     use crate::pkcs7::{Pkcs7, Pkcs7Flags};
     use crate::pkey::PKey;
     use crate::stack::Stack;
@@ -305,6 +367,10 @@
 
         let pkcs7 =
             Pkcs7::encrypt(&certs, message.as_bytes(), cipher, flags).expect("should succeed");
+        assert_eq!(
+            pkcs7.type_().expect("PKCS7 should have a type").nid(),
+            Nid::PKCS7_ENVELOPED
+        );
 
         let encrypted = pkcs7
             .to_smime(message.as_bytes(), flags)
@@ -338,6 +404,10 @@
 
         let pkcs7 =
             Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
+        assert_eq!(
+            pkcs7.type_().expect("PKCS7 should have a type").nid(),
+            Nid::PKCS7_SIGNED
+        );
 
         let signed = pkcs7
             .to_smime(message.as_bytes(), flags)
@@ -382,6 +452,10 @@
 
         let pkcs7 =
             Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
+        assert_eq!(
+            pkcs7.type_().expect("PKCS7 should have a type").nid(),
+            Nid::PKCS7_SIGNED
+        );
 
         let signed = pkcs7
             .to_smime(message.as_bytes(), flags)
@@ -419,6 +493,10 @@
 
         let pkcs7 =
             Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed");
+        assert_eq!(
+            pkcs7.type_().expect("PKCS7 should have a type").nid(),
+            Nid::PKCS7_SIGNED
+        );
 
         let signed = pkcs7
             .to_smime(message.as_bytes(), flags)
@@ -443,4 +521,53 @@
 
         assert!(result.is_err());
     }
+
+    #[test]
+    fn signed_data_certificates() {
+        let cert = include_bytes!("../test/cert.pem");
+        let cert = X509::from_pem(cert).unwrap();
+        let mut extra_certs = Stack::<X509>::new().unwrap();
+        for cert in
+            X509::stack_from_pem(include_bytes!("../test/certs.pem")).expect("should succeed")
+        {
+            extra_certs.push(cert).expect("should succeed");
+        }
+
+        let message = "foo";
+        let flags = Pkcs7Flags::STREAM;
+        let pkey = include_bytes!("../test/key.pem");
+        let pkey = PKey::private_key_from_pem(pkey).unwrap();
+
+        let pkcs7 = Pkcs7::sign(&cert, &pkey, &extra_certs, message.as_bytes(), flags)
+            .expect("should succeed");
+        assert_eq!(
+            pkcs7.type_().expect("PKCS7 should have a type").nid(),
+            Nid::PKCS7_SIGNED
+        );
+        let signed_data_certs = pkcs7.signed().and_then(|x| x.certificates());
+        assert_eq!(signed_data_certs.expect("should succeed").len(), 3);
+    }
+
+    #[test]
+    fn signed_data_certificates_no_signed_data() {
+        let cert = include_bytes!("../test/certs.pem");
+        let cert = X509::from_pem(cert).unwrap();
+        let mut certs = Stack::new().unwrap();
+        certs.push(cert).unwrap();
+        let message: String = String::from("foo");
+        let cipher = Cipher::des_ede3_cbc();
+        let flags = Pkcs7Flags::STREAM;
+
+        // Use `Pkcs7::encrypt` since it populates the PKCS7_ENVELOPE struct rather than
+        // PKCS7_SIGNED
+        let pkcs7 =
+            Pkcs7::encrypt(&certs, message.as_bytes(), cipher, flags).expect("should succeed");
+        assert_eq!(
+            pkcs7.type_().expect("PKCS7 should have a type").nid(),
+            Nid::PKCS7_ENVELOPED
+        );
+
+        let signed_data_certs = pkcs7.signed().and_then(|x| x.certificates());
+        assert!(signed_data_certs.is_none())
+    }
 }
diff --git a/src/pkey.rs b/src/pkey.rs
index 21ba711..7892e65 100644
--- a/src/pkey.rs
+++ b/src/pkey.rs
@@ -47,7 +47,7 @@
 use crate::dsa::Dsa;
 use crate::ec::EcKey;
 use crate::error::ErrorStack;
-#[cfg(any(boringssl, ossl110))]
+#[cfg(any(ossl110, boringssl, libressl370))]
 use crate::pkey_ctx::PkeyCtx;
 use crate::rsa::Rsa;
 use crate::symm::Cipher;
@@ -57,7 +57,7 @@
 use foreign_types::{ForeignType, ForeignTypeRef};
 use libc::{c_int, c_long};
 use openssl_macros::corresponds;
-use std::convert::TryFrom;
+use std::convert::{TryFrom, TryInto};
 use std::ffi::CString;
 use std::fmt;
 use std::mem;
@@ -78,25 +78,33 @@
 
 impl Id {
     pub const RSA: Id = Id(ffi::EVP_PKEY_RSA);
+    #[cfg(any(ossl111, libressl310, boringssl))]
+    pub const RSA_PSS: Id = Id(ffi::EVP_PKEY_RSA_PSS);
     #[cfg(not(boringssl))]
     pub const HMAC: Id = Id(ffi::EVP_PKEY_HMAC);
     #[cfg(not(boringssl))]
     pub const CMAC: Id = Id(ffi::EVP_PKEY_CMAC);
     pub const DSA: Id = Id(ffi::EVP_PKEY_DSA);
     pub const DH: Id = Id(ffi::EVP_PKEY_DH);
+    #[cfg(ossl110)]
+    pub const DHX: Id = Id(ffi::EVP_PKEY_DHX);
     pub const EC: Id = Id(ffi::EVP_PKEY_EC);
+    #[cfg(ossl111)]
+    pub const SM2: Id = Id(ffi::EVP_PKEY_SM2);
 
-    #[cfg(any(boringssl, ossl110))]
+    #[cfg(any(ossl110, boringssl, libressl360))]
     pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF);
 
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
     #[cfg(ossl111)]
     pub const ED448: Id = Id(ffi::EVP_PKEY_ED448);
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub const X25519: Id = Id(ffi::EVP_PKEY_X25519);
     #[cfg(ossl111)]
     pub const X448: Id = Id(ffi::EVP_PKEY_X448);
+    #[cfg(ossl111)]
+    pub const POLY1305: Id = Id(ffi::EVP_PKEY_POLY1305);
 
     /// Creates a `Id` from an integer representation.
     pub fn from_raw(value: c_int) -> Id {
@@ -244,7 +252,11 @@
     where
         U: HasPublic,
     {
-        unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }
+        let res = unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 };
+        // Clear the stack. OpenSSL will put an error on the stack when the
+        // keys are different types in some situations.
+        let _ = ErrorStack::get();
+        res
     }
 
     /// Raw byte representation of a public key.
@@ -252,7 +264,7 @@
     /// This function only works for algorithms that support raw public keys.
     /// Currently this is: [`Id::X25519`], [`Id::ED25519`], [`Id::X448`] or [`Id::ED448`].
     #[corresponds(EVP_PKEY_get_raw_public_key)]
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn raw_public_key(&self) -> Result<Vec<u8>, ErrorStack> {
         unsafe {
             let mut len = 0;
@@ -303,7 +315,7 @@
     /// This function only works for algorithms that support raw private keys.
     /// Currently this is: [`Id::HMAC`], [`Id::X25519`], [`Id::ED25519`], [`Id::X448`] or [`Id::ED448`].
     #[corresponds(EVP_PKEY_get_raw_private_key)]
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn raw_private_key(&self) -> Result<Vec<u8>, ErrorStack> {
         unsafe {
             let mut len = 0;
@@ -323,12 +335,27 @@
         }
     }
 
+    /// Serializes a private key into an unencrypted DER-formatted PKCS#8
+    #[corresponds(i2d_PKCS8PrivateKey_bio)]
+    pub fn private_key_to_pkcs8(&self) -> Result<Vec<u8>, ErrorStack> {
+        unsafe {
+            let bio = MemBio::new()?;
+            cvt(ffi::i2d_PKCS8PrivateKey_bio(
+                bio.as_ptr(),
+                self.as_ptr(),
+                ptr::null(),
+                ptr::null_mut(),
+                0,
+                None,
+                ptr::null_mut(),
+            ))?;
+
+            Ok(bio.get_buf().to_owned())
+        }
+    }
+
     /// Serializes a private key into a DER-formatted PKCS#8, using the supplied password to
     /// encrypt the key.
-    ///
-    /// # Panics
-    ///
-    /// Panics if `passphrase` contains an embedded null.
     #[corresponds(i2d_PKCS8PrivateKey_bio)]
     pub fn private_key_to_pkcs8_passphrase(
         &self,
@@ -337,14 +364,12 @@
     ) -> Result<Vec<u8>, ErrorStack> {
         unsafe {
             let bio = MemBio::new()?;
-            let len = passphrase.len();
-            let passphrase = CString::new(passphrase).unwrap();
             cvt(ffi::i2d_PKCS8PrivateKey_bio(
                 bio.as_ptr(),
                 self.as_ptr(),
                 cipher.as_ptr(),
                 passphrase.as_ptr() as *const _ as *mut _,
-                len as ::libc::c_int,
+                passphrase.len().try_into().unwrap(),
                 None,
                 ptr::null_mut(),
             ))?;
@@ -387,11 +412,7 @@
         unsafe {
             let evp = cvt_p(ffi::EVP_PKEY_new())?;
             let pkey = PKey::from_ptr(evp);
-            cvt(ffi::EVP_PKEY_assign(
-                pkey.0,
-                ffi::EVP_PKEY_RSA,
-                rsa.as_ptr() as *mut _,
-            ))?;
+            cvt(ffi::EVP_PKEY_assign_RSA(pkey.0, rsa.as_ptr()))?;
             mem::forget(rsa);
             Ok(pkey)
         }
@@ -403,11 +424,7 @@
         unsafe {
             let evp = cvt_p(ffi::EVP_PKEY_new())?;
             let pkey = PKey::from_ptr(evp);
-            cvt(ffi::EVP_PKEY_assign(
-                pkey.0,
-                ffi::EVP_PKEY_DSA,
-                dsa.as_ptr() as *mut _,
-            ))?;
+            cvt(ffi::EVP_PKEY_assign_DSA(pkey.0, dsa.as_ptr()))?;
             mem::forget(dsa);
             Ok(pkey)
         }
@@ -415,14 +432,27 @@
 
     /// Creates a new `PKey` containing a Diffie-Hellman key.
     #[corresponds(EVP_PKEY_assign_DH)]
+    #[cfg(not(boringssl))]
     pub fn from_dh(dh: Dh<T>) -> Result<PKey<T>, ErrorStack> {
         unsafe {
             let evp = cvt_p(ffi::EVP_PKEY_new())?;
             let pkey = PKey::from_ptr(evp);
+            cvt(ffi::EVP_PKEY_assign_DH(pkey.0, dh.as_ptr()))?;
+            mem::forget(dh);
+            Ok(pkey)
+        }
+    }
+
+    /// Creates a new `PKey` containing a Diffie-Hellman key with type DHX.
+    #[cfg(all(not(boringssl), ossl110))]
+    pub fn from_dhx(dh: Dh<T>) -> Result<PKey<T>, ErrorStack> {
+        unsafe {
+            let evp = cvt_p(ffi::EVP_PKEY_new())?;
+            let pkey = PKey::from_ptr(evp);
             cvt(ffi::EVP_PKEY_assign(
                 pkey.0,
-                ffi::EVP_PKEY_DH,
-                dh.as_ptr() as *mut _,
+                ffi::EVP_PKEY_DHX,
+                dh.as_ptr().cast(),
             ))?;
             mem::forget(dh);
             Ok(pkey)
@@ -435,11 +465,7 @@
         unsafe {
             let evp = cvt_p(ffi::EVP_PKEY_new())?;
             let pkey = PKey::from_ptr(evp);
-            cvt(ffi::EVP_PKEY_assign(
-                pkey.0,
-                ffi::EVP_PKEY_EC,
-                ec_key.as_ptr() as *mut _,
-            ))?;
+            cvt(ffi::EVP_PKEY_assign_EC_KEY(pkey.0, ec_key.as_ptr()))?;
             mem::forget(ec_key);
             Ok(pkey)
         }
@@ -484,7 +510,7 @@
         ctx.keygen()
     }
 
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     fn generate_eddsa(id: Id) -> Result<PKey<Private>, ErrorStack> {
         let mut ctx = PkeyCtx::new_id(id)?;
         ctx.keygen_init()?;
@@ -514,7 +540,7 @@
     /// assert_eq!(secret.len(), 32);
     /// # Ok(()) }
     /// ```
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn generate_x25519() -> Result<PKey<Private>, ErrorStack> {
         PKey::generate_eddsa(Id::X25519)
     }
@@ -568,7 +594,7 @@
     /// assert_eq!(signature.len(), 64);
     /// # Ok(()) }
     /// ```
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn generate_ed25519() -> Result<PKey<Private>, ErrorStack> {
         PKey::generate_eddsa(Id::ED25519)
     }
@@ -718,7 +744,7 @@
     ///
     /// Algorithm types that support raw private keys are HMAC, X25519, ED25519, X448 or ED448
     #[corresponds(EVP_PKEY_new_raw_private_key)]
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn private_key_from_raw_bytes(
         bytes: &[u8],
         key_type: Id,
@@ -737,12 +763,22 @@
 }
 
 impl PKey<Public> {
-    from_pem! {
+    private_key_from_pem! {
         /// Decodes a PEM-encoded SubjectPublicKeyInfo structure.
         ///
         /// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
         #[corresponds(PEM_read_bio_PUBKEY)]
         public_key_from_pem,
+
+        /// Decodes a PEM-encoded SubjectPublicKeyInfo structure.
+        #[corresponds(PEM_read_bio_PUBKEY)]
+        public_key_from_pem_passphrase,
+
+        /// Decodes a PEM-encoded SubjectPublicKeyInfo structure.
+        ///
+        /// The callback should fill the password into the provided buffer and return its length.
+        #[corresponds(PEM_read_bio_PrivateKey)]
+        public_key_from_pem_callback,
         PKey<Public>,
         ffi::PEM_read_bio_PUBKEY
     }
@@ -759,7 +795,7 @@
     ///
     /// Algorithm types that support raw public keys are X25519, ED25519, X448 or ED448
     #[corresponds(EVP_PKEY_new_raw_public_key)]
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn public_key_from_raw_bytes(
         bytes: &[u8],
         key_type: Id,
@@ -842,6 +878,7 @@
     }
 }
 
+#[cfg(not(boringssl))]
 impl<T> TryFrom<Dh<T>> for PKey<T> {
     type Error = ErrorStack;
 
@@ -866,6 +903,7 @@
     use crate::dh::Dh;
     use crate::dsa::Dsa;
     use crate::ec::EcKey;
+    use crate::error::Error;
     use crate::nid::Nid;
     use crate::rsa::Rsa;
     use crate::symm::Cipher;
@@ -889,7 +927,14 @@
     #[test]
     fn test_unencrypted_pkcs8() {
         let key = include_bytes!("../test/pkcs8-nocrypt.der");
-        PKey::private_key_from_pkcs8(key).unwrap();
+        let pkey = PKey::private_key_from_pkcs8(key).unwrap();
+        let serialized = pkey.private_key_to_pkcs8().unwrap();
+        let pkey2 = PKey::private_key_from_pkcs8(&serialized).unwrap();
+
+        assert_eq!(
+            pkey2.private_key_to_der().unwrap(),
+            pkey.private_key_to_der().unwrap()
+        );
     }
 
     #[test]
@@ -1058,7 +1103,7 @@
         assert_eq!(&g, dh_.generator());
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     fn test_raw_public_key(gen: fn() -> Result<PKey<Private>, ErrorStack>, key_type: Id) {
         // Generate a new key
         let key = gen().unwrap();
@@ -1074,7 +1119,7 @@
         );
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     fn test_raw_private_key(gen: fn() -> Result<PKey<Private>, ErrorStack>, key_type: Id) {
         // Generate a new key
         let key = gen().unwrap();
@@ -1085,26 +1130,30 @@
 
         // Compare the der encoding of the original and raw / restored public key
         assert_eq!(
-            key.private_key_to_der().unwrap(),
-            from_raw.private_key_to_der().unwrap()
+            key.private_key_to_pkcs8().unwrap(),
+            from_raw.private_key_to_pkcs8().unwrap()
         );
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     #[test]
     fn test_raw_public_key_bytes() {
         test_raw_public_key(PKey::generate_x25519, Id::X25519);
         test_raw_public_key(PKey::generate_ed25519, Id::ED25519);
+        #[cfg(all(not(boringssl), not(libressl370)))]
         test_raw_public_key(PKey::generate_x448, Id::X448);
+        #[cfg(all(not(boringssl), not(libressl370)))]
         test_raw_public_key(PKey::generate_ed448, Id::ED448);
     }
 
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     #[test]
     fn test_raw_private_key_bytes() {
         test_raw_private_key(PKey::generate_x25519, Id::X25519);
         test_raw_private_key(PKey::generate_ed25519, Id::ED25519);
+        #[cfg(all(not(boringssl), not(libressl370)))]
         test_raw_private_key(PKey::generate_x448, Id::X448);
+        #[cfg(all(not(boringssl), not(libressl370)))]
         test_raw_private_key(PKey::generate_ed448, Id::ED448);
     }
 
@@ -1138,4 +1187,17 @@
         let key = PKey::ec_gen("prime256v1").unwrap();
         assert!(key.ec_key().is_ok());
     }
+
+    #[test]
+    fn test_public_eq() {
+        let rsa = Rsa::generate(2048).unwrap();
+        let pkey1 = PKey::from_rsa(rsa).unwrap();
+
+        let group = crate::ec::EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
+        let ec_key = EcKey::generate(&group).unwrap();
+        let pkey2 = PKey::from_ec_key(ec_key).unwrap();
+
+        assert!(!pkey1.public_eq(&pkey2));
+        assert!(Error::get().is_none());
+    }
 }
diff --git a/src/pkey_ctx.rs b/src/pkey_ctx.rs
index 3d4203f..add7830 100644
--- a/src/pkey_ctx.rs
+++ b/src/pkey_ctx.rs
@@ -70,25 +70,60 @@
 use crate::md::MdRef;
 use crate::pkey::{HasPrivate, HasPublic, Id, PKey, PKeyRef, Private};
 use crate::rsa::Padding;
-use crate::{cvt, cvt_n, cvt_p};
+use crate::sign::RsaPssSaltlen;
+use crate::{cvt, cvt_p};
 use foreign_types::{ForeignType, ForeignTypeRef};
 #[cfg(not(boringssl))]
 use libc::c_int;
+#[cfg(ossl320)]
+use libc::c_uint;
 use openssl_macros::corresponds;
 use std::convert::TryFrom;
+#[cfg(ossl320)]
+use std::ffi::CStr;
 use std::ptr;
 
 /// HKDF modes of operation.
-#[cfg(ossl111)]
+#[cfg(any(ossl111, libressl360))]
 pub struct HkdfMode(c_int);
 
-#[cfg(ossl111)]
+#[cfg(any(ossl111, libressl360))]
 impl HkdfMode {
+    /// This is the default mode. Calling [`derive`][PkeyCtxRef::derive] on a [`PkeyCtxRef`] set up
+    /// for HKDF will perform an extract followed by an expand operation in one go. The derived key
+    /// returned will be the result after the expand operation. The intermediate fixed-length
+    /// pseudorandom key K is not returned.
     pub const EXTRACT_THEN_EXPAND: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND);
+
+    /// In this mode calling [`derive`][PkeyCtxRef::derive] will just perform the extract operation.
+    /// The value returned will be the intermediate fixed-length pseudorandom key K.
+    ///
+    /// The digest, key and salt values must be set before a key is derived or an error occurs.
     pub const EXTRACT_ONLY: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY);
+
+    /// In this mode calling [`derive`][PkeyCtxRef::derive] will just perform the expand operation.
+    /// The input key should be set to the intermediate fixed-length pseudorandom key K returned
+    /// from a previous extract operation.
+    ///
+    /// The digest, key and info values must be set before a key is derived or an error occurs.
     pub const EXPAND_ONLY: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
 }
 
+/// Nonce type for ECDSA and DSA.
+#[cfg(ossl320)]
+#[derive(Debug, PartialEq)]
+pub struct NonceType(c_uint);
+
+#[cfg(ossl320)]
+impl NonceType {
+    /// This is the default mode. It uses a random value for the nonce k as defined in FIPS 186-4 Section 6.3
+    /// “Secret Number Generation”.
+    pub const RANDOM_K: Self = NonceType(0);
+
+    /// Uses a deterministic value for the nonce k as defined in RFC #6979 (See Section 3.2 “Generation of k”).
+    pub const DETERMINISTIC_K: Self = NonceType(1);
+}
+
 generic_foreign_type_and_impl_send_sync! {
     type CType = ffi::EVP_PKEY_CTX;
     fn drop = ffi::EVP_PKEY_CTX_free;
@@ -149,6 +184,17 @@
         Ok(())
     }
 
+    /// Prepares the context for signature recovery using the public key.
+    #[corresponds(EVP_PKEY_verify_recover_init)]
+    #[inline]
+    pub fn verify_recover_init(&mut self) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::EVP_PKEY_verify_recover_init(self.as_ptr()))?;
+        }
+
+        Ok(())
+    }
+
     /// Encrypts data using the public key.
     ///
     /// If `to` is set to `None`, an upper bound on the number of bytes required for the output buffer will be
@@ -194,16 +240,54 @@
     #[inline]
     pub fn verify(&mut self, data: &[u8], sig: &[u8]) -> Result<bool, ErrorStack> {
         unsafe {
-            let r = cvt_n(ffi::EVP_PKEY_verify(
+            let r = ffi::EVP_PKEY_verify(
                 self.as_ptr(),
                 sig.as_ptr(),
                 sig.len(),
                 data.as_ptr(),
                 data.len(),
-            ))?;
+            );
+            // `EVP_PKEY_verify` is not terribly consistent about how it,
+            // reports errors. It does not clearly distinguish between 0 and
+            // -1, and may put errors on the stack in both cases. If there's
+            // errors on the stack, we return `Err()`, else we return
+            // `Ok(false)`.
+            if r <= 0 {
+                let errors = ErrorStack::get();
+                if !errors.errors().is_empty() {
+                    return Err(errors);
+                }
+            }
+
             Ok(r == 1)
         }
     }
+
+    /// Recovers the original data signed by the private key. You almost
+    /// always want `verify` instead.
+    ///
+    /// Returns the number of bytes written to `to`, or the number of bytes
+    /// that would be written, if `to` is `None.
+    #[corresponds(EVP_PKEY_verify_recover)]
+    #[inline]
+    pub fn verify_recover(
+        &mut self,
+        sig: &[u8],
+        to: Option<&mut [u8]>,
+    ) -> Result<usize, ErrorStack> {
+        let mut written = to.as_ref().map_or(0, |b| b.len());
+        unsafe {
+            cvt(ffi::EVP_PKEY_verify_recover(
+                self.as_ptr(),
+                to.map_or(ptr::null_mut(), |b| b.as_mut_ptr()),
+                &mut written,
+                sig.as_ptr(),
+                sig.len(),
+            ))?;
+        }
+
+        Ok(written)
+    }
 }
 
 impl<T> PkeyCtxRef<T>
@@ -336,6 +420,22 @@
         Ok(())
     }
 
+    /// Sets which algorithm was used to compute the digest used in a
+    /// signature. With RSA signatures this causes the signature to be wrapped
+    /// in a `DigestInfo` structure. This is almost always what you want with
+    /// RSA signatures.
+    #[corresponds(EVP_PKEY_CTX_set_signature_md)]
+    #[inline]
+    pub fn set_signature_md(&self, md: &MdRef) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::EVP_PKEY_CTX_set_signature_md(
+                self.as_ptr(),
+                md.as_ptr(),
+            ))?;
+        }
+        Ok(())
+    }
+
     /// Returns the RSA padding mode in use.
     ///
     /// This is only useful for RSA keys.
@@ -366,6 +466,21 @@
         Ok(())
     }
 
+    /// Sets the RSA PSS salt length.
+    ///
+    /// This is only useful for RSA keys.
+    #[corresponds(EVP_PKEY_CTX_set_rsa_pss_saltlen)]
+    #[inline]
+    pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen(
+                self.as_ptr(),
+                len.as_raw(),
+            ))
+            .map(|_| ())
+        }
+    }
+
     /// Sets the RSA MGF1 algorithm.
     ///
     /// This is only useful for RSA keys.
@@ -386,7 +501,7 @@
     ///
     /// This is only useful for RSA keys.
     #[corresponds(EVP_PKEY_CTX_set_rsa_oaep_md)]
-    #[cfg(any(ossl102, libressl310))]
+    #[cfg(any(ossl102, libressl310, boringssl))]
     #[inline]
     pub fn set_rsa_oaep_md(&mut self, md: &MdRef) -> Result<(), ErrorStack> {
         unsafe {
@@ -470,7 +585,7 @@
     ///
     /// Requires OpenSSL 1.1.0 or newer.
     #[corresponds(EVP_PKEY_CTX_set_hkdf_md)]
-    #[cfg(any(ossl110, boringssl))]
+    #[cfg(any(ossl110, boringssl, libressl360))]
     #[inline]
     pub fn set_hkdf_md(&mut self, digest: &MdRef) -> Result<(), ErrorStack> {
         unsafe {
@@ -487,9 +602,13 @@
     ///
     /// Defaults to [`HkdfMode::EXTRACT_THEN_EXPAND`].
     ///
+    /// WARNING: Although this API calls it a "mode", HKDF-Extract and HKDF-Expand are distinct
+    /// operations with distinct inputs and distinct kinds of keys. Callers should not pass input
+    /// secrets for one operation into the other.
+    ///
     /// Requires OpenSSL 1.1.1 or newer.
     #[corresponds(EVP_PKEY_CTX_set_hkdf_mode)]
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl360))]
     #[inline]
     pub fn set_hkdf_mode(&mut self, mode: HkdfMode) -> Result<(), ErrorStack> {
         unsafe {
@@ -499,11 +618,16 @@
         Ok(())
     }
 
-    /// Sets the input keying material for HKDF generation.
+    /// Sets the input material for HKDF generation as the "key".
+    ///
+    /// Which input is the key depends on the "mode" (see [`set_hkdf_mode`][Self::set_hkdf_mode]).
+    /// If [`HkdfMode::EXTRACT_THEN_EXPAND`] or [`HkdfMode::EXTRACT_ONLY`], this function specifies
+    /// the input keying material (IKM) for HKDF-Extract. If [`HkdfMode::EXPAND_ONLY`], it instead
+    /// specifies the pseudorandom key (PRK) for HKDF-Expand.
     ///
     /// Requires OpenSSL 1.1.0 or newer.
     #[corresponds(EVP_PKEY_CTX_set1_hkdf_key)]
-    #[cfg(any(ossl110, boringssl))]
+    #[cfg(any(ossl110, boringssl, libressl360))]
     #[inline]
     pub fn set_hkdf_key(&mut self, key: &[u8]) -> Result<(), ErrorStack> {
         #[cfg(not(boringssl))]
@@ -524,9 +648,11 @@
 
     /// Sets the salt value for HKDF generation.
     ///
+    /// If performing HKDF-Expand only, this parameter is ignored.
+    ///
     /// Requires OpenSSL 1.1.0 or newer.
     #[corresponds(EVP_PKEY_CTX_set1_hkdf_salt)]
-    #[cfg(any(ossl110, boringssl))]
+    #[cfg(any(ossl110, boringssl, libressl360))]
     #[inline]
     pub fn set_hkdf_salt(&mut self, salt: &[u8]) -> Result<(), ErrorStack> {
         #[cfg(not(boringssl))]
@@ -547,9 +673,11 @@
 
     /// Appends info bytes for HKDF generation.
     ///
+    /// If performing HKDF-Extract only, this parameter is ignored.
+    ///
     /// Requires OpenSSL 1.1.0 or newer.
     #[corresponds(EVP_PKEY_CTX_add1_hkdf_info)]
-    #[cfg(any(ossl110, boringssl))]
+    #[cfg(any(ossl110, boringssl, libressl360))]
     #[inline]
     pub fn add_hkdf_info(&mut self, info: &[u8]) -> Result<(), ErrorStack> {
         #[cfg(not(boringssl))]
@@ -605,6 +733,53 @@
             Ok(PKey::from_ptr(key))
         }
     }
+
+    /// Sets the nonce type for a private key context.
+    ///
+    /// The nonce for DSA and ECDSA can be either random (the default) or deterministic (as defined by RFC 6979).
+    ///
+    /// This is only useful for DSA and ECDSA.
+    /// Requires OpenSSL 3.2.0 or newer.
+    #[cfg(ossl320)]
+    #[corresponds(EVP_PKEY_CTX_set_params)]
+    pub fn set_nonce_type(&mut self, nonce_type: NonceType) -> Result<(), ErrorStack> {
+        let nonce_field_name = CStr::from_bytes_with_nul(b"nonce-type\0").unwrap();
+        let mut nonce_type = nonce_type.0;
+        unsafe {
+            let param_nonce =
+                ffi::OSSL_PARAM_construct_uint(nonce_field_name.as_ptr(), &mut nonce_type);
+            let param_end = ffi::OSSL_PARAM_construct_end();
+
+            let params = [param_nonce, param_end];
+            cvt(ffi::EVP_PKEY_CTX_set_params(self.as_ptr(), params.as_ptr()))?;
+        }
+        Ok(())
+    }
+
+    /// Gets the nonce type for a private key context.
+    ///
+    /// The nonce for DSA and ECDSA can be either random (the default) or deterministic (as defined by RFC 6979).
+    ///
+    /// This is only useful for DSA and ECDSA.
+    /// Requires OpenSSL 3.2.0 or newer.
+    #[cfg(ossl320)]
+    #[corresponds(EVP_PKEY_CTX_get_params)]
+    pub fn nonce_type(&mut self) -> Result<NonceType, ErrorStack> {
+        let nonce_field_name = CStr::from_bytes_with_nul(b"nonce-type\0").unwrap();
+        let mut nonce_type: c_uint = 0;
+        unsafe {
+            let param_nonce =
+                ffi::OSSL_PARAM_construct_uint(nonce_field_name.as_ptr(), &mut nonce_type);
+            let param_end = ffi::OSSL_PARAM_construct_end();
+
+            let mut params = [param_nonce, param_end];
+            cvt(ffi::EVP_PKEY_CTX_get_params(
+                self.as_ptr(),
+                params.as_mut_ptr(),
+            ))?;
+        }
+        Ok(NonceType(nonce_type))
+    }
 }
 
 #[cfg(test)]
@@ -613,11 +788,12 @@
     #[cfg(not(boringssl))]
     use crate::cipher::Cipher;
     use crate::ec::{EcGroup, EcKey};
-    #[cfg(any(ossl102, libressl310, boringssl))]
+    use crate::hash::{hash, MessageDigest};
     use crate::md::Md;
     use crate::nid::Nid;
     use crate::pkey::PKey;
     use crate::rsa::Rsa;
+    use crate::sign::Verifier;
 
     #[test]
     fn rsa() {
@@ -643,7 +819,7 @@
     }
 
     #[test]
-    #[cfg(any(ossl102, libressl310))]
+    #[cfg(any(ossl102, libressl310, boringssl))]
     fn rsa_oaep() {
         let key = include_bytes!("../test/rsa.pem");
         let rsa = Rsa::private_key_from_pem(key).unwrap();
@@ -671,6 +847,53 @@
     }
 
     #[test]
+    fn rsa_sign() {
+        let key = include_bytes!("../test/rsa.pem");
+        let rsa = Rsa::private_key_from_pem(key).unwrap();
+        let pkey = PKey::from_rsa(rsa).unwrap();
+
+        let mut ctx = PkeyCtx::new(&pkey).unwrap();
+        ctx.sign_init().unwrap();
+        ctx.set_rsa_padding(Padding::PKCS1).unwrap();
+        ctx.set_signature_md(Md::sha384()).unwrap();
+
+        let msg = b"hello world";
+        let digest = hash(MessageDigest::sha384(), msg).unwrap();
+        let mut signature = vec![];
+        ctx.sign_to_vec(&digest, &mut signature).unwrap();
+
+        let mut verifier = Verifier::new(MessageDigest::sha384(), &pkey).unwrap();
+        verifier.update(msg).unwrap();
+        assert!(matches!(verifier.verify(&signature), Ok(true)));
+    }
+
+    #[test]
+    fn rsa_sign_pss() {
+        let key = include_bytes!("../test/rsa.pem");
+        let rsa = Rsa::private_key_from_pem(key).unwrap();
+        let pkey = PKey::from_rsa(rsa).unwrap();
+
+        let mut ctx = PkeyCtx::new(&pkey).unwrap();
+        ctx.sign_init().unwrap();
+        ctx.set_rsa_padding(Padding::PKCS1_PSS).unwrap();
+        ctx.set_signature_md(Md::sha384()).unwrap();
+        ctx.set_rsa_pss_saltlen(RsaPssSaltlen::custom(14)).unwrap();
+
+        let msg = b"hello world";
+        let digest = hash(MessageDigest::sha384(), msg).unwrap();
+        let mut signature = vec![];
+        ctx.sign_to_vec(&digest, &mut signature).unwrap();
+
+        let mut verifier = Verifier::new(MessageDigest::sha384(), &pkey).unwrap();
+        verifier.set_rsa_padding(Padding::PKCS1_PSS).unwrap();
+        verifier
+            .set_rsa_pss_saltlen(RsaPssSaltlen::custom(14))
+            .unwrap();
+        verifier.update(msg).unwrap();
+        assert!(matches!(verifier.verify(&signature), Ok(true)));
+    }
+
+    #[test]
     fn derive() {
         let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
         let key1 = EcKey::generate(&group).unwrap();
@@ -698,7 +921,7 @@
     }
 
     #[test]
-    #[cfg(any(ossl110, boringssl))]
+    #[cfg(any(ossl110, boringssl, libressl360))]
     fn hkdf() {
         let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap();
         ctx.derive_init().unwrap();
@@ -720,7 +943,7 @@
     }
 
     #[test]
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl360))]
     fn hkdf_expand() {
         let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap();
         ctx.derive_init().unwrap();
@@ -744,7 +967,7 @@
     }
 
     #[test]
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, libressl360))]
     fn hkdf_extract() {
         let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap();
         ctx.derive_init().unwrap();
@@ -779,7 +1002,109 @@
         let bad_data = b"Some Crypto text";
 
         ctx.verify_init().unwrap();
-        let valid = ctx.verify(bad_data, &signature).unwrap();
-        assert!(!valid);
+        let valid = ctx.verify(bad_data, &signature);
+        assert!(matches!(valid, Ok(false) | Err(_)));
+        assert!(ErrorStack::get().errors().is_empty());
+    }
+
+    #[test]
+    fn verify_fail_ec() {
+        let key1 =
+            EcKey::generate(&EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap()).unwrap();
+        let key1 = PKey::from_ec_key(key1).unwrap();
+
+        let data = b"Some Crypto Text";
+        let mut ctx = PkeyCtx::new(&key1).unwrap();
+        ctx.verify_init().unwrap();
+        assert!(matches!(ctx.verify(data, &[0; 64]), Ok(false) | Err(_)));
+        assert!(ErrorStack::get().errors().is_empty());
+    }
+
+    #[test]
+    fn test_verify_recover() {
+        let key = Rsa::generate(2048).unwrap();
+        let key = PKey::from_rsa(key).unwrap();
+
+        let digest = [
+            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+            24, 25, 26, 27, 28, 29, 30, 31,
+        ];
+
+        let mut ctx = PkeyCtx::new(&key).unwrap();
+        ctx.sign_init().unwrap();
+        ctx.set_rsa_padding(Padding::PKCS1).unwrap();
+        ctx.set_signature_md(Md::sha256()).unwrap();
+        let mut signature = vec![];
+        ctx.sign_to_vec(&digest, &mut signature).unwrap();
+
+        // Attempt recovery of just the digest.
+        let mut ctx = PkeyCtx::new(&key).unwrap();
+        ctx.verify_recover_init().unwrap();
+        ctx.set_rsa_padding(Padding::PKCS1).unwrap();
+        ctx.set_signature_md(Md::sha256()).unwrap();
+        let length = ctx.verify_recover(&signature, None).unwrap();
+        let mut result_buf = vec![0; length];
+        let length = ctx
+            .verify_recover(&signature, Some(&mut result_buf))
+            .unwrap();
+        assert_eq!(length, digest.len());
+        // result_buf contains the digest
+        assert_eq!(result_buf[..length], digest);
+
+        // Attempt recovery of teh entire DigestInfo
+        let mut ctx = PkeyCtx::new(&key).unwrap();
+        ctx.verify_recover_init().unwrap();
+        ctx.set_rsa_padding(Padding::PKCS1).unwrap();
+        let length = ctx.verify_recover(&signature, None).unwrap();
+        let mut result_buf = vec![0; length];
+        let length = ctx
+            .verify_recover(&signature, Some(&mut result_buf))
+            .unwrap();
+        // 32-bytes of SHA256 digest + the ASN.1 DigestInfo structure == 51 bytes
+        assert_eq!(length, 51);
+        // The digest is the end of the DigestInfo structure.
+        assert_eq!(result_buf[length - digest.len()..length], digest);
+    }
+
+    #[test]
+    #[cfg(ossl320)]
+    fn set_nonce_type() {
+        let key1 =
+            EcKey::generate(&EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap()).unwrap();
+        let key1 = PKey::from_ec_key(key1).unwrap();
+
+        let mut ctx = PkeyCtx::new(&key1).unwrap();
+        ctx.sign_init().unwrap();
+        ctx.set_nonce_type(NonceType::DETERMINISTIC_K).unwrap();
+        let nonce_type = ctx.nonce_type().unwrap();
+        assert_eq!(nonce_type, NonceType::DETERMINISTIC_K);
+        assert!(ErrorStack::get().errors().is_empty());
+    }
+
+    // Test vector from
+    // https://github.com/openssl/openssl/blob/openssl-3.2.0/test/recipes/30-test_evp_data/evppkey_ecdsa_rfc6979.txt
+    #[test]
+    #[cfg(ossl320)]
+    fn ecdsa_deterministic_signature() {
+        let private_key_pem = "-----BEGIN PRIVATE KEY-----
+MDkCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEHzAdAgEBBBhvqwNJNOTA/Jrmf1tWWanX0f79GH7g
+n9Q=
+-----END PRIVATE KEY-----";
+
+        let key1 = EcKey::private_key_from_pem(private_key_pem.as_bytes()).unwrap();
+        let key1 = PKey::from_ec_key(key1).unwrap();
+        let input = "sample";
+        let expected_output = hex::decode("303502190098C6BD12B23EAF5E2A2045132086BE3EB8EBD62ABF6698FF021857A22B07DEA9530F8DE9471B1DC6624472E8E2844BC25B64").unwrap();
+
+        let hashed_input = hash(MessageDigest::sha1(), input.as_bytes()).unwrap();
+        let mut ctx = PkeyCtx::new(&key1).unwrap();
+        ctx.sign_init().unwrap();
+        ctx.set_signature_md(Md::sha1()).unwrap();
+        ctx.set_nonce_type(NonceType::DETERMINISTIC_K).unwrap();
+
+        let mut output = vec![];
+        ctx.sign_to_vec(&hashed_input, &mut output).unwrap();
+        assert_eq!(output, expected_output);
+        assert!(ErrorStack::get().errors().is_empty());
     }
 }
diff --git a/src/provider.rs b/src/provider.rs
index 147fadf..01b5820 100644
--- a/src/provider.rs
+++ b/src/provider.rs
@@ -55,6 +55,10 @@
                 retain_fallbacks as _,
             ))?;
 
+            // OSSL_PROVIDER_try_load seems to leave errors on the stack, even
+            // when it succeeds.
+            let _ = ErrorStack::get();
+
             Ok(Provider::from_ptr(p))
         }
     }
diff --git a/src/rand.rs b/src/rand.rs
index 8317951..ef0f768 100644
--- a/src/rand.rs
+++ b/src/rand.rs
@@ -37,6 +37,31 @@
     }
 }
 
+/// Fill buffer with cryptographically strong pseudo-random bytes. It is
+/// intended to be used for generating values that should remain private.
+///
+/// # Examples
+///
+/// To generate a buffer with cryptographically strong random bytes:
+///
+/// ```
+/// use openssl::rand::rand_priv_bytes;
+///
+/// let mut buf = [0; 256];
+/// rand_priv_bytes(&mut buf).unwrap();
+/// ```
+///
+/// Requires OpenSSL 1.1.1 or newer.
+#[corresponds(RAND_priv_bytes)]
+#[cfg(ossl111)]
+pub fn rand_priv_bytes(buf: &mut [u8]) -> Result<(), ErrorStack> {
+    unsafe {
+        ffi::init();
+        assert!(buf.len() <= c_int::max_value() as usize);
+        cvt(ffi::RAND_priv_bytes(buf.as_mut_ptr(), buf.len() as LenType)).map(|_| ())
+    }
+}
+
 /// Controls random device file descriptor behavior.
 ///
 /// Requires OpenSSL 1.1.1 or newer.
@@ -50,11 +75,16 @@
 
 #[cfg(test)]
 mod tests {
-    use super::rand_bytes;
-
     #[test]
     fn test_rand_bytes() {
         let mut buf = [0; 32];
-        rand_bytes(&mut buf).unwrap();
+        super::rand_bytes(&mut buf).unwrap();
+    }
+
+    #[test]
+    #[cfg(ossl111)]
+    fn test_rand_priv_bytes() {
+        let mut buf = [0; 32];
+        super::rand_priv_bytes(&mut buf).unwrap();
     }
 }
diff --git a/src/sha.rs b/src/sha.rs
index dd02667..2412890 100644
--- a/src/sha.rs
+++ b/src/sha.rs
@@ -57,7 +57,7 @@
 }
 
 /// Computes the SHA224 hash of some data.
-#[corresponds(SH224)]
+#[corresponds(SHA224)]
 #[inline]
 pub fn sha224(data: &[u8]) -> [u8; 28] {
     unsafe {
diff --git a/src/sign.rs b/src/sign.rs
index e5e8060..0154b1d 100644
--- a/src/sign.rs
+++ b/src/sign.rs
@@ -81,7 +81,7 @@
 use crate::{cvt, cvt_p};
 
 cfg_if! {
-    if #[cfg(ossl110)] {
+    if #[cfg(any(ossl110, libressl382))] {
         use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new};
     } else {
         use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free};
@@ -93,7 +93,7 @@
 
 impl RsaPssSaltlen {
     /// Returns the integer representation of `RsaPssSaltlen`.
-    fn as_raw(&self) -> c_int {
+    pub(crate) fn as_raw(&self) -> c_int {
         self.0
     }
 
@@ -117,10 +117,10 @@
     _p: PhantomData<&'a ()>,
 }
 
-unsafe impl<'a> Sync for Signer<'a> {}
-unsafe impl<'a> Send for Signer<'a> {}
+unsafe impl Sync for Signer<'_> {}
+unsafe impl Send for Signer<'_> {}
 
-impl<'a> Drop for Signer<'a> {
+impl Drop for Signer<'_> {
     fn drop(&mut self) {
         // pkey_ctx is owned by the md_ctx, so no need to explicitly free it.
         unsafe {
@@ -130,7 +130,7 @@
 }
 
 #[allow(clippy::len_without_is_empty)]
-impl<'a> Signer<'a> {
+impl Signer<'_> {
     /// Creates a new `Signer`.
     ///
     /// This cannot be used with Ed25519 or Ed448 keys. Please refer to
@@ -139,7 +139,7 @@
     /// OpenSSL documentation at [`EVP_DigestSignInit`].
     ///
     /// [`EVP_DigestSignInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestSignInit.html
-    pub fn new<T>(type_: MessageDigest, pkey: &'a PKeyRef<T>) -> Result<Signer<'a>, ErrorStack>
+    pub fn new<'a, T>(type_: MessageDigest, pkey: &PKeyRef<T>) -> Result<Signer<'a>, ErrorStack>
     where
         T: HasPrivate,
     {
@@ -154,16 +154,16 @@
     /// OpenSSL documentation at [`EVP_DigestSignInit`].
     ///
     /// [`EVP_DigestSignInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestSignInit.html
-    pub fn new_without_digest<T>(pkey: &'a PKeyRef<T>) -> Result<Signer<'a>, ErrorStack>
+    pub fn new_without_digest<'a, T>(pkey: &PKeyRef<T>) -> Result<Signer<'a>, ErrorStack>
     where
         T: HasPrivate,
     {
         Self::new_intern(None, pkey)
     }
 
-    fn new_intern<T>(
+    fn new_intern<'a, T>(
         type_: Option<MessageDigest>,
-        pkey: &'a PKeyRef<T>,
+        pkey: &PKeyRef<T>,
     ) -> Result<Signer<'a>, ErrorStack>
     where
         T: HasPrivate,
@@ -214,7 +214,7 @@
     ///
     /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`].
     ///
-    /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html
+    /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_padding.html
     pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(
@@ -231,7 +231,7 @@
     ///
     /// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`].
     ///
-    /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html
+    /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html
     pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen(
@@ -285,12 +285,12 @@
     ///
     /// OpenSSL documentation at [`EVP_DigestSignFinal`].
     ///
-    /// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestSignFinal.html
+    /// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/manmaster/crypto/EVP_DigestSignFinal.html
     pub fn len(&self) -> Result<usize, ErrorStack> {
         self.len_intern()
     }
 
-    #[cfg(not(any(boringssl, ossl111)))]
+    #[cfg(all(not(ossl111), not(boringssl), not(libressl370)))]
     fn len_intern(&self) -> Result<usize, ErrorStack> {
         unsafe {
             let mut len = 0;
@@ -303,7 +303,7 @@
         }
     }
 
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     fn len_intern(&self) -> Result<usize, ErrorStack> {
         unsafe {
             let mut len = 0;
@@ -325,7 +325,7 @@
     ///
     /// OpenSSL documentation at [`EVP_DigestSignFinal`].
     ///
-    /// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestSignFinal.html
+    /// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/manmaster/crypto/EVP_DigestSignFinal.html
     pub fn sign(&self, buf: &mut [u8]) -> Result<usize, ErrorStack> {
         unsafe {
             let mut len = buf.len();
@@ -360,7 +360,7 @@
     /// OpenSSL documentation at [`EVP_DigestSign`].
     ///
     /// [`EVP_DigestSign`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestSign.html
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn sign_oneshot(
         &mut self,
         sig_buf: &mut [u8],
@@ -382,7 +382,7 @@
     /// Returns the signature.
     ///
     /// This is a simple convenience wrapper over `len` and `sign_oneshot`.
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn sign_oneshot_to_vec(&mut self, data_buf: &[u8]) -> Result<Vec<u8>, ErrorStack> {
         let mut sig_buf = vec![0; self.len()?];
         let len = self.sign_oneshot(&mut sig_buf, data_buf)?;
@@ -507,7 +507,7 @@
     ///
     /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`].
     ///
-    /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html
+    /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_padding.html
     pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::EVP_PKEY_CTX_set_rsa_padding(
@@ -524,7 +524,7 @@
     ///
     /// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`].
     ///
-    /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html
+    /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html
     pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen(
@@ -596,7 +596,7 @@
     /// OpenSSL documentation at [`EVP_DigestVerify`].
     ///
     /// [`EVP_DigestVerify`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestVerify.html
-    #[cfg(any(boringssl, ossl111))]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     pub fn verify_oneshot(&mut self, signature: &[u8], buf: &[u8]) -> Result<bool, ErrorStack> {
         unsafe {
             let r = ffi::EVP_DigestVerify(
@@ -711,7 +711,7 @@
 
     #[cfg(not(boringssl))]
     fn test_hmac(ty: MessageDigest, tests: &[(Vec<u8>, Vec<u8>, Vec<u8>)]) {
-        for &(ref key, ref data, ref res) in tests.iter() {
+        for (key, data, res) in tests.iter() {
             let pkey = PKey::hmac(key).unwrap();
             let mut signer = Signer::new(ty, &pkey).unwrap();
             signer.update(data).unwrap();
@@ -846,7 +846,7 @@
     }
 
     #[test]
-    #[cfg(ossl111)]
+    #[cfg(any(ossl111, boringssl, libressl370))]
     fn eddsa() {
         let key = PKey::generate_ed25519().unwrap();
 
diff --git a/src/srtp.rs b/src/srtp.rs
index 7ed3135..595757d 100644
--- a/src/srtp.rs
+++ b/src/srtp.rs
@@ -46,10 +46,12 @@
         SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_32 as c_ulong);
     pub const SRTP_NULL_SHA1_80: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_80 as c_ulong);
     pub const SRTP_NULL_SHA1_32: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_32 as c_ulong);
-    #[cfg(ossl110)]
-    pub const SRTP_AEAD_AES_128_GCM: SrtpProfileId = SrtpProfileId(ffi::SRTP_AEAD_AES_128_GCM);
-    #[cfg(ossl110)]
-    pub const SRTP_AEAD_AES_256_GCM: SrtpProfileId = SrtpProfileId(ffi::SRTP_AEAD_AES_256_GCM);
+    #[cfg(any(boringssl, ossl110))]
+    pub const SRTP_AEAD_AES_128_GCM: SrtpProfileId =
+        SrtpProfileId(ffi::SRTP_AEAD_AES_128_GCM as c_ulong);
+    #[cfg(any(boringssl, ossl110))]
+    pub const SRTP_AEAD_AES_256_GCM: SrtpProfileId =
+        SrtpProfileId(ffi::SRTP_AEAD_AES_256_GCM as c_ulong);
 
     /// Creates a `SrtpProfileId` from an integer representation.
     pub fn from_raw(value: c_ulong) -> SrtpProfileId {
diff --git a/src/ssl/callbacks.rs b/src/ssl/callbacks.rs
index 091b1fb..c6414fb 100644
--- a/src/ssl/callbacks.rs
+++ b/src/ssl/callbacks.rs
@@ -86,6 +86,7 @@
         };
         // Give the callback mutable slices into which it can write the identity and psk.
         let identity_sl = slice::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize);
+        #[allow(clippy::unnecessary_cast)]
         let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
         match (*callback)(ssl, hint, identity_sl, psk_sl) {
             Ok(psk_len) => psk_len as u32,
@@ -124,6 +125,7 @@
             Some(CStr::from_ptr(identity).to_bytes())
         };
         // Give the callback mutable slices into which it can write the psk.
+        #[allow(clippy::unnecessary_cast)]
         let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
         match (*callback)(ssl, identity, psk_sl) {
             Ok(psk_len) => psk_len as u32,
@@ -194,6 +196,7 @@
             .ssl_context()
             .ex_data(SslContext::cached_ex_index::<F>())
             .expect("BUG: alpn callback missing") as *const F;
+        #[allow(clippy::unnecessary_cast)]
         let protos = slice::from_raw_parts(inbuf as *const u8, inlen as usize);
 
         match (*callback)(ssl, protos) {
@@ -412,6 +415,7 @@
         .expect("BUG: session context missing")
         .ex_data(SslContext::cached_ex_index::<F>())
         .expect("BUG: get session callback missing") as *const F;
+    #[allow(clippy::unnecessary_cast)]
     let data = slice::from_raw_parts(data as *const u8, len as usize);
 
     match (*callback)(ssl, data) {
@@ -455,6 +459,7 @@
         .ssl_context()
         .ex_data(SslContext::cached_ex_index::<F>())
         .expect("BUG: stateless cookie generate callback missing") as *const F;
+    #[allow(clippy::unnecessary_cast)]
     let slice = slice::from_raw_parts_mut(cookie as *mut u8, ffi::SSL_COOKIE_LENGTH as usize);
     match (*callback)(ssl, slice) {
         Ok(len) => {
@@ -482,6 +487,7 @@
         .ssl_context()
         .ex_data(SslContext::cached_ex_index::<F>())
         .expect("BUG: stateless cookie verify callback missing") as *const F;
+    #[allow(clippy::unnecessary_cast)]
     let slice = slice::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len);
     (*callback)(ssl, slice) as c_int
 }
@@ -503,6 +509,7 @@
             .expect("BUG: cookie generate callback missing") as *const F;
         // We subtract 1 from DTLS1_COOKIE_LENGTH as the ostensible value, 256, is erroneous but retained for
         // compatibility. See comments in dtls1.h.
+        #[allow(clippy::unnecessary_cast)]
         let slice =
             slice::from_raw_parts_mut(cookie as *mut u8, ffi::DTLS1_COOKIE_LENGTH as usize - 1);
         match (*callback)(ssl, slice) {
@@ -542,6 +549,7 @@
             .ssl_context()
             .ex_data(SslContext::cached_ex_index::<F>())
             .expect("BUG: cookie verify callback missing") as *const F;
+        #[allow(clippy::unnecessary_cast)]
         let slice =
             slice::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len as usize);
         (*callback)(ssl, slice) as c_int
@@ -654,6 +662,7 @@
             .ex_data(SslContext::cached_ex_index::<F>())
             .expect("BUG: custom ext parse callback missing") as *const F;
         let ectx = ExtensionContext::from_bits_truncate(context);
+        #[allow(clippy::unnecessary_cast)]
         let slice = slice::from_raw_parts(input as *const u8, inlen);
         let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) {
             Some((chainidx, X509Ref::from_ptr(x)))
diff --git a/src/ssl/connector.rs b/src/ssl/connector.rs
index 39f729d..66d1bd8 100644
--- a/src/ssl/connector.rs
+++ b/src/ssl/connector.rs
@@ -11,6 +11,7 @@
     SslOptions, SslRef, SslStream, SslVerifyMode,
 };
 use crate::version;
+use std::net::IpAddr;
 
 const FFDHE_2048: &str = "
 -----BEGIN DH PARAMETERS-----
@@ -177,9 +178,9 @@
 
     /// Returns an `Ssl` configured to connect to the provided domain.
     ///
-    /// The domain is used for SNI and hostname verification if enabled.
+    /// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled.
     pub fn into_ssl(mut self, domain: &str) -> Result<Ssl, ErrorStack> {
-        if self.sni {
+        if self.sni && domain.parse::<IpAddr>().is_err() {
             self.ssl.set_hostname(domain)?;
         }
 
diff --git a/src/ssl/mod.rs b/src/ssl/mod.rs
index aba6062..2ff9dac 100644
--- a/src/ssl/mod.rs
+++ b/src/ssl/mod.rs
@@ -57,6 +57,8 @@
 //!     }
 //! }
 //! ```
+#[cfg(ossl300)]
+use crate::cvt_long;
 use crate::dh::{Dh, DhRef};
 #[cfg(all(ossl101, not(ossl110)))]
 use crate::ec::EcKey;
@@ -68,14 +70,16 @@
 #[cfg(any(ossl110, libressl270))]
 use crate::nid::Nid;
 use crate::pkey::{HasPrivate, PKeyRef, Params, Private};
+#[cfg(ossl300)]
+use crate::pkey::{PKey, Public};
 use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef};
 use crate::ssl::bio::BioMethod;
 use crate::ssl::callbacks::*;
 use crate::ssl::error::InnerError;
-use crate::stack::{Stack, StackRef};
+use crate::stack::{Stack, StackRef, Stackable};
 use crate::util::{ForeignTypeExt, ForeignTypeRefExt};
 use crate::x509::store::{X509Store, X509StoreBuilderRef, X509StoreRef};
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 use crate::x509::verify::X509VerifyParamRef;
 use crate::x509::{X509Name, X509Ref, X509StoreContextRef, X509VerifyResult, X509};
 use crate::{cvt, cvt_n, cvt_p, init};
@@ -86,14 +90,13 @@
 use once_cell::sync::{Lazy, OnceCell};
 use openssl_macros::corresponds;
 use std::any::TypeId;
-use std::cmp;
 use std::collections::HashMap;
 use std::ffi::{CStr, CString};
 use std::fmt;
 use std::io;
 use std::io::prelude::*;
 use std::marker::PhantomData;
-use std::mem::{self, ManuallyDrop};
+use std::mem::{self, ManuallyDrop, MaybeUninit};
 use std::ops::{Deref, DerefMut};
 use std::panic::resume_unwind;
 use std::path::Path;
@@ -143,6 +146,8 @@
 
 bitflags! {
     /// Options controlling the behavior of an `SslContext`.
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct SslOptions: SslOptionsRepr {
         /// Disables a countermeasure against an SSLv3/TLSv1.0 vulnerability affecting CBC ciphers.
         const DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS as SslOptionsRepr;
@@ -281,6 +286,8 @@
 
 bitflags! {
     /// Options controlling the behavior of an `SslContext`.
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct SslMode: SslBitType {
         /// Enables "short writes".
         ///
@@ -378,6 +385,8 @@
 
 bitflags! {
     /// Options controlling the behavior of certificate verification.
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct SslVerifyMode: i32 {
         /// Verifies that the peer's certificate is trusted.
         ///
@@ -410,6 +419,8 @@
 
 bitflags! {
     /// Options controlling the behavior of session caching.
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct SslSessionCacheMode: SslBitType {
         /// No session caching for the client or server takes place.
         const OFF = ffi::SSL_SESS_CACHE_OFF;
@@ -447,6 +458,8 @@
 #[cfg(ossl111)]
 bitflags! {
     /// Which messages and under which conditions an extension should be added or expected.
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct ExtensionContext: c_uint {
         /// This extension is only allowed in TLS
         const TLS_ONLY = ffi::SSL_EXT_TLS_ONLY;
@@ -599,7 +612,7 @@
     /// Terminate the handshake with a fatal alert.
     ///
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(any(ossl110))]
+    #[cfg(ossl110)]
     pub const ALERT_FATAL: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL);
 
     /// Do not select a protocol, but continue the handshake.
@@ -641,9 +654,20 @@
 
     /// TLSv1.3
     ///
-    /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
-    #[cfg(any(ossl111, libressl340))]
+    /// Requires BoringSSL or OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+    #[cfg(any(ossl111, libressl340, boringssl))]
     pub const TLS1_3: SslVersion = SslVersion(ffi::TLS1_3_VERSION);
+
+    /// DTLSv1.0
+    ///
+    /// DTLS 1.0 corresponds to TLS 1.1.
+    pub const DTLS1: SslVersion = SslVersion(ffi::DTLS1_VERSION);
+
+    /// DTLSv1.2
+    ///
+    /// DTLS 1.2 corresponds to TLS 1.2 to harmonize versions. There was never a DTLS 1.1.
+    #[cfg(any(ossl102, libressl332, boringssl))]
+    pub const DTLS1_2: SslVersion = SslVersion(ffi::DTLS1_2_VERSION);
 }
 
 cfg_if! {
@@ -724,7 +748,7 @@
     #[corresponds(SSL_CTX_set_verify)]
     pub fn set_verify(&mut self, mode: SslVerifyMode) {
         unsafe {
-            ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, None);
+            ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits() as c_int, None);
         }
     }
 
@@ -741,7 +765,7 @@
     {
         unsafe {
             self.set_ex_data(SslContext::cached_ex_index::<F>(), verify);
-            ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits as c_int, Some(raw_verify::<F>));
+            ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits() as c_int, Some(raw_verify::<F>));
         }
     }
 
@@ -828,7 +852,7 @@
     pub fn set_mode(&mut self, mode: SslMode) -> SslMode {
         unsafe {
             let bits = ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits() as MtuTy) as SslBitType;
-            SslMode { bits }
+            SslMode::from_bits_retain(bits)
         }
     }
 
@@ -1047,7 +1071,7 @@
     ///
     /// See [`ciphers`] for details on the format.
     ///
-    /// [`ciphers`]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html
+    /// [`ciphers`]: https://www.openssl.org/docs/manmaster/apps/ciphers.html
     #[corresponds(SSL_CTX_set_cipher_list)]
     pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
         let cipher_list = CString::new(cipher_list).unwrap();
@@ -1100,14 +1124,14 @@
     pub fn set_options(&mut self, option: SslOptions) -> SslOptions {
         let bits =
             unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) } as SslOptionsRepr;
-        SslOptions { bits }
+        SslOptions::from_bits_retain(bits)
     }
 
     /// Returns the options used by the context.
     #[corresponds(SSL_CTX_get_options)]
     pub fn options(&self) -> SslOptions {
         let bits = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) } as SslOptionsRepr;
-        SslOptions { bits }
+        SslOptions::from_bits_retain(bits)
     }
 
     /// Clears the options used by the context, returning the old set.
@@ -1115,17 +1139,17 @@
     pub fn clear_options(&mut self, option: SslOptions) -> SslOptions {
         let bits =
             unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) } as SslOptionsRepr;
-        SslOptions { bits }
+        SslOptions::from_bits_retain(bits)
     }
 
     /// Sets the minimum supported protocol version.
     ///
-    /// A value of `None` will enable protocol versions down the the lowest version supported by
+    /// A value of `None` will enable protocol versions down to the lowest version supported by
     /// OpenSSL.
     ///
-    /// Requires OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer.
+    /// Requires BoringSSL or OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer.
     #[corresponds(SSL_CTX_set_min_proto_version)]
-    #[cfg(any(ossl110, libressl261))]
+    #[cfg(any(ossl110, libressl261, boringssl))]
     pub fn set_min_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::SSL_CTX_set_min_proto_version(
@@ -1138,12 +1162,12 @@
 
     /// Sets the maximum supported protocol version.
     ///
-    /// A value of `None` will enable protocol versions down the the highest version supported by
+    /// A value of `None` will enable protocol versions up to the highest version supported by
     /// OpenSSL.
     ///
-    /// Requires OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer.
+    /// Requires BoringSSL or OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer.
     #[corresponds(SSL_CTX_set_max_proto_version)]
-    #[cfg(any(ossl110, libressl261))]
+    #[cfg(any(ossl110, libressl261, boringssl))]
     pub fn set_max_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::SSL_CTX_set_max_proto_version(
@@ -1156,7 +1180,7 @@
 
     /// Gets the minimum supported protocol version.
     ///
-    /// A value of `None` indicates that all versions down the the lowest version supported by
+    /// A value of `None` indicates that all versions down to the lowest version supported by
     /// OpenSSL are enabled.
     ///
     /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer.
@@ -1175,7 +1199,7 @@
 
     /// Gets the maximum supported protocol version.
     ///
-    /// A value of `None` indicates that all versions down the the highest version supported by
+    /// A value of `None` indicates that all versions up to the highest version supported by
     /// OpenSSL are enabled.
     ///
     /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer.
@@ -1199,16 +1223,16 @@
     /// and `http/1.1` is encoded as `b"\x06spdy/1\x08http/1.1"`. The protocols are ordered by
     /// preference.
     ///
-    /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+    /// Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
     #[corresponds(SSL_CTX_set_alpn_protos)]
-    #[cfg(any(ossl102, libressl261))]
+    #[cfg(any(ossl102, libressl261, boringssl))]
     pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> {
         unsafe {
             assert!(protocols.len() <= c_uint::max_value() as usize);
             let r = ffi::SSL_CTX_set_alpn_protos(
                 self.as_ptr(),
                 protocols.as_ptr(),
-                protocols.len() as c_uint,
+                protocols.len() as _,
             );
             // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D:
             if r == 0 {
@@ -1283,18 +1307,18 @@
 
     /// Returns a reference to the X509 verification configuration.
     ///
-    /// Requires OpenSSL 1.0.2 or newer.
+    /// Requires BoringSSL or OpenSSL 1.0.2 or newer.
     #[corresponds(SSL_CTX_get0_param)]
-    #[cfg(any(ossl102, libressl261))]
+    #[cfg(any(ossl102, boringssl, libressl261))]
     pub fn verify_param(&self) -> &X509VerifyParamRef {
         unsafe { X509VerifyParamRef::from_ptr(ffi::SSL_CTX_get0_param(self.as_ptr())) }
     }
 
     /// Returns a mutable reference to the X509 verification configuration.
     ///
-    /// Requires OpenSSL 1.0.2 or newer.
+    /// Requires BoringSSL or OpenSSL 1.0.2 or newer.
     #[corresponds(SSL_CTX_get0_param)]
-    #[cfg(any(ossl102, libressl261))]
+    #[cfg(any(ossl102, boringssl, libressl261))]
     pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef {
         unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_CTX_get0_param(self.as_ptr())) }
     }
@@ -1464,7 +1488,7 @@
     pub fn set_session_cache_mode(&mut self, mode: SslSessionCacheMode) -> SslSessionCacheMode {
         unsafe {
             let bits = ffi::SSL_CTX_set_session_cache_mode(self.as_ptr(), mode.bits());
-            SslSessionCacheMode { bits }
+            SslSessionCacheMode::from_bits_retain(bits)
         }
     }
 
@@ -1547,16 +1571,34 @@
     ///
     /// This can be used to provide data to callbacks registered with the context. Use the
     /// `SslContext::new_ex_index` method to create an `Index`.
+    // FIXME should return a result
     #[corresponds(SSL_CTX_set_ex_data)]
     pub fn set_ex_data<T>(&mut self, index: Index<SslContext, T>, data: T) {
         self.set_ex_data_inner(index, data);
     }
 
     fn set_ex_data_inner<T>(&mut self, index: Index<SslContext, T>, data: T) -> *mut c_void {
+        match self.ex_data_mut(index) {
+            Some(v) => {
+                *v = data;
+                (v as *mut T).cast()
+            }
+            _ => unsafe {
+                let data = Box::into_raw(Box::new(data)) as *mut c_void;
+                ffi::SSL_CTX_set_ex_data(self.as_ptr(), index.as_raw(), data);
+                data
+            },
+        }
+    }
+
+    fn ex_data_mut<T>(&mut self, index: Index<SslContext, T>) -> Option<&mut T> {
         unsafe {
-            let data = Box::into_raw(Box::new(data)) as *mut c_void;
-            ffi::SSL_CTX_set_ex_data(self.as_ptr(), index.as_raw(), data);
-            data
+            let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw());
+            if data.is_null() {
+                None
+            } else {
+                Some(&mut *data.cast())
+            }
         }
     }
 
@@ -1677,9 +1719,9 @@
 
     /// Sets the context's supported elliptic curve groups.
     ///
-    /// Requires OpenSSL 1.1.1 or LibreSSL 2.5.1 or newer.
+    /// Requires BoringSSL or OpenSSL 1.1.1 or LibreSSL 2.5.1 or newer.
     #[corresponds(SSL_CTX_set1_groups_list)]
-    #[cfg(any(ossl111, libressl251))]
+    #[cfg(any(ossl111, boringssl, libressl251))]
     pub fn set_groups_list(&mut self, groups: &str) -> Result<(), ErrorStack> {
         let groups = CString::new(groups).unwrap();
         unsafe {
@@ -1687,6 +1729,26 @@
         }
     }
 
+    /// Sets the number of TLS 1.3 session tickets that will be sent to a client after a full
+    /// handshake.
+    ///
+    /// Requires OpenSSL 1.1.1 or newer.
+    #[corresponds(SSL_CTX_set_num_tickets)]
+    #[cfg(ossl111)]
+    pub fn set_num_tickets(&mut self, num_tickets: usize) -> Result<(), ErrorStack> {
+        unsafe { cvt(ffi::SSL_CTX_set_num_tickets(self.as_ptr(), num_tickets)).map(|_| ()) }
+    }
+
+    /// Set the context's security level to a value between 0 and 5, inclusive.
+    /// A security value of 0 allows allows all parameters and algorithms.
+    ///
+    /// Requires OpenSSL 1.1.0 or newer.
+    #[corresponds(SSL_CTX_set_security_level)]
+    #[cfg(any(ossl110, libressl360))]
+    pub fn set_security_level(&mut self, level: u32) {
+        unsafe { ffi::SSL_CTX_set_security_level(self.as_ptr(), level as c_int) }
+    }
+
     /// Consumes the builder, returning a new `SslContext`.
     pub fn build(self) -> SslContext {
         self.0
@@ -1880,6 +1942,26 @@
         let mode = unsafe { ffi::SSL_CTX_get_verify_mode(self.as_ptr()) };
         SslVerifyMode::from_bits(mode).expect("SSL_CTX_get_verify_mode returned invalid mode")
     }
+
+    /// Gets the number of TLS 1.3 session tickets that will be sent to a client after a full
+    /// handshake.
+    ///
+    /// Requires OpenSSL 1.1.1 or newer.
+    #[corresponds(SSL_CTX_get_num_tickets)]
+    #[cfg(ossl111)]
+    pub fn num_tickets(&self) -> usize {
+        unsafe { ffi::SSL_CTX_get_num_tickets(self.as_ptr()) }
+    }
+
+    /// Get the context's security level, which controls the allowed parameters
+    /// and algorithms.
+    ///
+    /// Requires OpenSSL 1.1.0 or newer.
+    #[corresponds(SSL_CTX_get_security_level)]
+    #[cfg(any(ossl110, libressl360))]
+    pub fn security_level(&self) -> u32 {
+        unsafe { ffi::SSL_CTX_get_security_level(self.as_ptr()) as u32 }
+    }
 }
 
 /// Information about the state of a cipher.
@@ -1909,6 +1991,10 @@
     }
 }
 
+impl Stackable for SslCipher {
+    type StackType = ffi::stack_st_SSL_CIPHER;
+}
+
 impl Deref for SslCipher {
     type Target = SslCipherRef;
 
@@ -2025,6 +2111,19 @@
     }
 }
 
+impl fmt::Debug for SslCipherRef {
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(fmt, "{}", self.name())
+    }
+}
+
+/// A stack of selected ciphers, and a stack of selected signalling cipher suites
+#[derive(Debug)]
+pub struct CipherLists {
+    pub suites: Stack<SslCipher>,
+    pub signalling_suites: Stack<SslCipher>,
+}
+
 foreign_type_and_impl_send_sync! {
     type CType = ffi::SSL_SESSION;
     fn drop = ffi::SSL_SESSION_free;
@@ -2074,6 +2173,7 @@
         unsafe {
             let mut len = 0;
             let p = ffi::SSL_SESSION_get_id(self.as_ptr(), &mut len);
+            #[allow(clippy::unnecessary_cast)]
             slice::from_raw_parts(p as *const u8, len as usize)
         }
     }
@@ -2200,7 +2300,7 @@
     ///
     /// This corresponds to [`SSL_new`].
     ///
-    /// [`SSL_new`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_new.html
+    /// [`SSL_new`]: https://www.openssl.org/docs/manmaster/ssl/SSL_new.html
     #[corresponds(SSL_new)]
     pub fn new(ctx: &SslContextRef) -> Result<Ssl, ErrorStack> {
         let session_ctx_index = try_get_session_ctx_index()?;
@@ -2266,21 +2366,6 @@
         unsafe { ffi::SSL_get_rbio(self.as_ptr()) }
     }
 
-    fn read(&mut self, buf: &mut [u8]) -> c_int {
-        let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int;
-        unsafe { ffi::SSL_read(self.as_ptr(), buf.as_ptr() as *mut c_void, len) }
-    }
-
-    fn peek(&mut self, buf: &mut [u8]) -> c_int {
-        let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int;
-        unsafe { ffi::SSL_peek(self.as_ptr(), buf.as_ptr() as *mut c_void, len) }
-    }
-
-    fn write(&mut self, buf: &[u8]) -> c_int {
-        let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int;
-        unsafe { ffi::SSL_write(self.as_ptr(), buf.as_ptr() as *const c_void, len) }
-    }
-
     fn get_error(&self, ret: c_int) -> ErrorCode {
         unsafe { ErrorCode::from_raw(ffi::SSL_get_error(self.as_ptr(), ret)) }
     }
@@ -2302,7 +2387,7 @@
     /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify
     #[corresponds(SSL_set_verify)]
     pub fn set_verify(&mut self, mode: SslVerifyMode) {
-        unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, None) }
+        unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits() as c_int, None) }
     }
 
     /// Returns the verify mode that was set using `set_verify`.
@@ -2323,7 +2408,11 @@
         unsafe {
             // this needs to be in an Arc since the callback can register a new callback!
             self.set_ex_data(Ssl::cached_ex_index(), Arc::new(verify));
-            ffi::SSL_set_verify(self.as_ptr(), mode.bits as c_int, Some(ssl_raw_verify::<F>));
+            ffi::SSL_set_verify(
+                self.as_ptr(),
+                mode.bits() as c_int,
+                Some(ssl_raw_verify::<F>),
+            );
         }
     }
 
@@ -2365,7 +2454,7 @@
     ///
     /// Requires OpenSSL 1.0.1 or 1.0.2.
     #[corresponds(SSL_set_tmp_ecdh_callback)]
-    #[cfg(any(all(ossl101, not(ossl110))))]
+    #[cfg(all(ossl101, not(ossl110)))]
     #[deprecated(note = "this function leaks memory and does not exist on newer OpenSSL versions")]
     pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F)
     where
@@ -2391,19 +2480,16 @@
 
     /// Like [`SslContextBuilder::set_alpn_protos`].
     ///
-    /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+    /// Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
     ///
     /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos
     #[corresponds(SSL_set_alpn_protos)]
-    #[cfg(any(ossl102, libressl261))]
+    #[cfg(any(ossl102, libressl261, boringssl))]
     pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> {
         unsafe {
             assert!(protocols.len() <= c_uint::max_value() as usize);
-            let r = ffi::SSL_set_alpn_protos(
-                self.as_ptr(),
-                protocols.as_ptr(),
-                protocols.len() as c_uint,
-            );
+            let r =
+                ffi::SSL_set_alpn_protos(self.as_ptr(), protocols.as_ptr(), protocols.len() as _);
             // fun fact, SSL_set_alpn_protos has a reversed return code D:
             if r == 0 {
                 Ok(())
@@ -2507,10 +2593,8 @@
 
     /// Like [`SslContext::private_key`].
     ///
-    /// This corresponds to `SSL_get_privatekey`.
-    ///
     /// [`SslContext::private_key`]: struct.SslContext.html#method.private_key
-    #[corresponds(SSL_get_certificate)]
+    #[corresponds(SSL_get_privatekey)]
     pub fn private_key(&self) -> Option<&PKeyRef<Private>> {
         unsafe {
             let ptr = ffi::SSL_get_privatekey(self.as_ptr());
@@ -2552,9 +2636,9 @@
     /// The protocol's name is returned is an opaque sequence of bytes. It is up to the client
     /// to interpret it.
     ///
-    /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
+    /// Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
     #[corresponds(SSL_get0_alpn_selected)]
-    #[cfg(any(ossl102, libressl261))]
+    #[cfg(any(ossl102, libressl261, boringssl))]
     pub fn selected_alpn_protocol(&self) -> Option<&[u8]> {
         unsafe {
             let mut data: *const c_uchar = ptr::null();
@@ -2685,9 +2769,9 @@
 
     /// Returns a mutable reference to the X509 verification configuration.
     ///
-    /// Requires OpenSSL 1.0.2 or newer.
+    /// Requires BoringSSL or OpenSSL 1.0.2 or newer.
     #[corresponds(SSL_get0_param)]
-    #[cfg(any(ossl102, libressl261))]
+    #[cfg(any(ossl102, boringssl, libressl261))]
     pub fn param_mut(&mut self) -> &mut X509VerifyParamRef {
         unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) }
     }
@@ -2863,6 +2947,10 @@
                 response.len() as c_long,
             ) as c_int)
             .map(|_| ())
+            .map_err(|e| {
+                ffi::OPENSSL_free(p);
+                e
+            })
         }
     }
 
@@ -2876,15 +2964,19 @@
     ///
     /// This can be used to provide data to callbacks registered with the context. Use the
     /// `Ssl::new_ex_index` method to create an `Index`.
+    // FIXME should return a result
     #[corresponds(SSL_set_ex_data)]
     pub fn set_ex_data<T>(&mut self, index: Index<Ssl, T>, data: T) {
-        unsafe {
-            let data = Box::new(data);
-            ffi::SSL_set_ex_data(
-                self.as_ptr(),
-                index.as_raw(),
-                Box::into_raw(data) as *mut c_void,
-            );
+        match self.ex_data_mut(index) {
+            Some(v) => *v = data,
+            None => unsafe {
+                let data = Box::new(data);
+                ffi::SSL_set_ex_data(
+                    self.as_ptr(),
+                    index.as_raw(),
+                    Box::into_raw(data) as *mut c_void,
+                );
+            },
         }
     }
 
@@ -3050,6 +3142,41 @@
         }
     }
 
+    /// Decodes a slice of wire-format cipher suite specification bytes. Unsupported cipher suites
+    /// are ignored.
+    ///
+    /// Requires OpenSSL 1.1.1 or newer.
+    #[corresponds(SSL_bytes_to_cipher_list)]
+    #[cfg(ossl111)]
+    pub fn bytes_to_cipher_list(
+        &self,
+        bytes: &[u8],
+        isv2format: bool,
+    ) -> Result<CipherLists, ErrorStack> {
+        unsafe {
+            let ptr = bytes.as_ptr();
+            let len = bytes.len();
+            let mut sk = ptr::null_mut();
+            let mut scsvs = ptr::null_mut();
+            let res = ffi::SSL_bytes_to_cipher_list(
+                self.as_ptr(),
+                ptr,
+                len,
+                isv2format as c_int,
+                &mut sk,
+                &mut scsvs,
+            );
+            if res == 1 {
+                Ok(CipherLists {
+                    suites: Stack::from_ptr(sk),
+                    signalling_suites: Stack::from_ptr(scsvs),
+                })
+            } else {
+                Err(ErrorStack::get())
+            }
+        }
+    }
+
     /// Returns the compression methods field of the client's hello message.
     ///
     /// This can only be used inside of the client hello callback. Otherwise, `None` is returned.
@@ -3114,6 +3241,249 @@
         }
         Ok(())
     }
+
+    /// Sets a new default TLS/SSL method for SSL objects
+    #[cfg(not(boringssl))]
+    pub fn set_method(&mut self, method: SslMethod) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::SSL_set_ssl_method(self.as_ptr(), method.as_ptr()))?;
+        };
+        Ok(())
+    }
+
+    /// Loads the private key from a file.
+    #[corresponds(SSL_use_Private_Key_file)]
+    pub fn set_private_key_file<P: AsRef<Path>>(
+        &mut self,
+        path: P,
+        ssl_file_type: SslFiletype,
+    ) -> Result<(), ErrorStack> {
+        let p = path.as_ref().as_os_str().to_str().unwrap();
+        let key_file = CString::new(p).unwrap();
+        unsafe {
+            cvt(ffi::SSL_use_PrivateKey_file(
+                self.as_ptr(),
+                key_file.as_ptr(),
+                ssl_file_type.as_raw(),
+            ))?;
+        };
+        Ok(())
+    }
+
+    /// Sets the private key.
+    #[corresponds(SSL_use_PrivateKey)]
+    pub fn set_private_key(&mut self, pkey: &PKeyRef<Private>) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::SSL_use_PrivateKey(self.as_ptr(), pkey.as_ptr()))?;
+        };
+        Ok(())
+    }
+
+    /// Sets the certificate
+    #[corresponds(SSL_use_certificate)]
+    pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::SSL_use_certificate(self.as_ptr(), cert.as_ptr()))?;
+        };
+        Ok(())
+    }
+
+    /// Loads a certificate chain from a file.
+    ///
+    /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf
+    /// certificate, and the remainder forming the chain of certificates up to and including the
+    /// trusted root certificate.
+    #[corresponds(SSL_use_certificate_chain_file)]
+    #[cfg(any(ossl110, libressl332))]
+    pub fn set_certificate_chain_file<P: AsRef<Path>>(
+        &mut self,
+        path: P,
+    ) -> Result<(), ErrorStack> {
+        let p = path.as_ref().as_os_str().to_str().unwrap();
+        let cert_file = CString::new(p).unwrap();
+        unsafe {
+            cvt(ffi::SSL_use_certificate_chain_file(
+                self.as_ptr(),
+                cert_file.as_ptr(),
+            ))?;
+        };
+        Ok(())
+    }
+
+    /// Sets ca certificate that client trusted
+    #[corresponds(SSL_add_client_CA)]
+    pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::SSL_add_client_CA(self.as_ptr(), cacert.as_ptr()))?;
+        };
+        Ok(())
+    }
+
+    // Sets the list of CAs sent to the client when requesting a client certificate for the chosen ssl
+    #[corresponds(SSL_set_client_CA_list)]
+    pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) {
+        unsafe { ffi::SSL_set_client_CA_list(self.as_ptr(), list.as_ptr()) }
+        mem::forget(list);
+    }
+
+    /// Sets the minimum supported protocol version.
+    ///
+    /// A value of `None` will enable protocol versions down to the lowest version supported by
+    /// OpenSSL.
+    ///
+    /// Requires BoringSSL or OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer.
+    #[corresponds(SSL_set_min_proto_version)]
+    #[cfg(any(ossl110, libressl261, boringssl))]
+    pub fn set_min_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::SSL_set_min_proto_version(
+                self.as_ptr(),
+                version.map_or(0, |v| v.0 as _),
+            ))
+            .map(|_| ())
+        }
+    }
+
+    /// Sets the maximum supported protocol version.
+    ///
+    /// A value of `None` will enable protocol versions up to the highest version supported by
+    /// OpenSSL.
+    ///
+    /// Requires BoringSSL or OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer.
+    #[corresponds(SSL_set_max_proto_version)]
+    #[cfg(any(ossl110, libressl261, boringssl))]
+    pub fn set_max_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::SSL_set_max_proto_version(
+                self.as_ptr(),
+                version.map_or(0, |v| v.0 as _),
+            ))
+            .map(|_| ())
+        }
+    }
+
+    /// Sets the list of supported ciphers for the TLSv1.3 protocol.
+    ///
+    /// The `set_cipher_list` method controls the cipher suites for protocols before TLSv1.3.
+    ///
+    /// The format consists of TLSv1.3 cipher suite names separated by `:` characters in order of
+    /// preference.
+    ///
+    /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
+    #[corresponds(SSL_set_ciphersuites)]
+    #[cfg(any(ossl111, libressl340))]
+    pub fn set_ciphersuites(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
+        let cipher_list = CString::new(cipher_list).unwrap();
+        unsafe {
+            cvt(ffi::SSL_set_ciphersuites(
+                self.as_ptr(),
+                cipher_list.as_ptr() as *const _,
+            ))
+            .map(|_| ())
+        }
+    }
+
+    /// Sets the list of supported ciphers for protocols before TLSv1.3.
+    ///
+    /// The `set_ciphersuites` method controls the cipher suites for TLSv1.3.
+    ///
+    /// See [`ciphers`] for details on the format.
+    ///
+    /// [`ciphers`]: https://www.openssl.org/docs/manmaster/apps/ciphers.html
+    #[corresponds(SSL_set_cipher_list)]
+    pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> {
+        let cipher_list = CString::new(cipher_list).unwrap();
+        unsafe {
+            cvt(ffi::SSL_set_cipher_list(
+                self.as_ptr(),
+                cipher_list.as_ptr() as *const _,
+            ))
+            .map(|_| ())
+        }
+    }
+
+    /// Set the certificate store used for certificate verification
+    #[corresponds(SSL_set_cert_store)]
+    #[cfg(ossl102)]
+    pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::SSL_set0_verify_cert_store(self.as_ptr(), cert_store.as_ptr()) as c_int)?;
+            mem::forget(cert_store);
+            Ok(())
+        }
+    }
+
+    /// Sets the number of TLS 1.3 session tickets that will be sent to a client after a full
+    /// handshake.
+    ///
+    /// Requires OpenSSL 1.1.1 or newer.
+    #[corresponds(SSL_set_num_tickets)]
+    #[cfg(ossl111)]
+    pub fn set_num_tickets(&mut self, num_tickets: usize) -> Result<(), ErrorStack> {
+        unsafe { cvt(ffi::SSL_set_num_tickets(self.as_ptr(), num_tickets)).map(|_| ()) }
+    }
+
+    /// Gets the number of TLS 1.3 session tickets that will be sent to a client after a full
+    /// handshake.
+    ///
+    /// Requires OpenSSL 1.1.1 or newer.
+    #[corresponds(SSL_get_num_tickets)]
+    #[cfg(ossl111)]
+    pub fn num_tickets(&self) -> usize {
+        unsafe { ffi::SSL_get_num_tickets(self.as_ptr()) }
+    }
+
+    /// Set the context's security level to a value between 0 and 5, inclusive.
+    /// A security value of 0 allows allows all parameters and algorithms.
+    ///
+    /// Requires OpenSSL 1.1.0 or newer.
+    #[corresponds(SSL_set_security_level)]
+    #[cfg(any(ossl110, libressl360))]
+    pub fn set_security_level(&mut self, level: u32) {
+        unsafe { ffi::SSL_set_security_level(self.as_ptr(), level as c_int) }
+    }
+
+    /// Get the connection's security level, which controls the allowed parameters
+    /// and algorithms.
+    ///
+    /// Requires OpenSSL 1.1.0 or newer.
+    #[corresponds(SSL_get_security_level)]
+    #[cfg(any(ossl110, libressl360))]
+    pub fn security_level(&self) -> u32 {
+        unsafe { ffi::SSL_get_security_level(self.as_ptr()) as u32 }
+    }
+
+    /// Get the temporary key provided by the peer that is used during key
+    /// exchange.
+    // We use an owned value because EVP_KEY free need to be called when it is
+    // dropped
+    #[corresponds(SSL_get_peer_tmp_key)]
+    #[cfg(ossl300)]
+    pub fn peer_tmp_key(&self) -> Result<PKey<Public>, ErrorStack> {
+        unsafe {
+            let mut key = ptr::null_mut();
+            match cvt_long(ffi::SSL_get_peer_tmp_key(self.as_ptr(), &mut key)) {
+                Ok(_) => Ok(PKey::<Public>::from_ptr(key)),
+                Err(e) => Err(e),
+            }
+        }
+    }
+
+    /// Returns the temporary key from the local end of the connection that is
+    /// used during key exchange.
+    // We use an owned value because EVP_KEY free need to be called when it is
+    // dropped
+    #[corresponds(SSL_get_tmp_key)]
+    #[cfg(ossl300)]
+    pub fn tmp_key(&self) -> Result<PKey<Private>, ErrorStack> {
+        unsafe {
+            let mut key = ptr::null_mut();
+            match cvt_long(ffi::SSL_get_tmp_key(self.as_ptr(), &mut key)) {
+                Ok(_) => Ok(PKey::<Private>::from_ptr(key)),
+                Err(e) => Err(e),
+            }
+        }
+    }
 }
 
 /// An SSL stream midway through the handshake process.
@@ -3361,26 +3731,86 @@
         }
     }
 
+    /// Like `read`, but takes a possibly-uninitialized slice.
+    ///
+    /// # Safety
+    ///
+    /// No portion of `buf` will be de-initialized by this method. If the method returns `Ok(n)`,
+    /// then the first `n` bytes of `buf` are guaranteed to be initialized.
+    #[corresponds(SSL_read_ex)]
+    pub fn read_uninit(&mut self, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
+        loop {
+            match self.ssl_read_uninit(buf) {
+                Ok(n) => return Ok(n),
+                Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => return Ok(0),
+                Err(ref e) if e.code() == ErrorCode::SYSCALL && e.io_error().is_none() => {
+                    return Ok(0);
+                }
+                Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {}
+                Err(e) => {
+                    return Err(e
+                        .into_io_error()
+                        .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e)));
+                }
+            }
+        }
+    }
+
     /// Like `read`, but returns an `ssl::Error` rather than an `io::Error`.
     ///
     /// It is particularly useful with a non-blocking socket, where the error value will identify if
     /// OpenSSL is waiting on read or write readiness.
-    #[corresponds(SSL_read)]
+    #[corresponds(SSL_read_ex)]
     pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
-        // The interpretation of the return code here is a little odd with a
-        // zero-length write. OpenSSL will likely correctly report back to us
-        // that it read zero bytes, but zero is also the sentinel for "error".
-        // To avoid that confusion short-circuit that logic and return quickly
-        // if `buf` has a length of zero.
-        if buf.is_empty() {
-            return Ok(0);
+        // SAFETY: `ssl_read_uninit` does not de-initialize the buffer.
+        unsafe {
+            self.ssl_read_uninit(slice::from_raw_parts_mut(
+                buf.as_mut_ptr().cast::<MaybeUninit<u8>>(),
+                buf.len(),
+            ))
         }
+    }
 
-        let ret = self.ssl.read(buf);
-        if ret > 0 {
-            Ok(ret as usize)
-        } else {
-            Err(self.make_error(ret))
+    /// Like `read_ssl`, but takes a possibly-uninitialized slice.
+    ///
+    /// # Safety
+    ///
+    /// No portion of `buf` will be de-initialized by this method. If the method returns `Ok(n)`,
+    /// then the first `n` bytes of `buf` are guaranteed to be initialized.
+    #[corresponds(SSL_read_ex)]
+    pub fn ssl_read_uninit(&mut self, buf: &mut [MaybeUninit<u8>]) -> Result<usize, Error> {
+        cfg_if! {
+            if #[cfg(any(ossl111, libressl350))] {
+                let mut readbytes = 0;
+                let ret = unsafe {
+                    ffi::SSL_read_ex(
+                        self.ssl().as_ptr(),
+                        buf.as_mut_ptr().cast(),
+                        buf.len(),
+                        &mut readbytes,
+                    )
+                };
+
+                if ret > 0 {
+                    Ok(readbytes)
+                } else {
+                    Err(self.make_error(ret))
+                }
+            } else {
+                if buf.is_empty() {
+                    return Ok(0);
+                }
+
+                let len = usize::min(c_int::max_value() as usize, buf.len()) as c_int;
+                let ret = unsafe {
+                    ffi::SSL_read(self.ssl().as_ptr(), buf.as_mut_ptr().cast(), len)
+                };
+                if ret > 0 {
+                    Ok(ret as usize)
+                } else {
+                    Err(self.make_error(ret))
+                }
+            }
         }
     }
 
@@ -3388,34 +3818,78 @@
     ///
     /// It is particularly useful with a non-blocking socket, where the error value will identify if
     /// OpenSSL is waiting on read or write readiness.
-    #[corresponds(SSL_write)]
+    #[corresponds(SSL_write_ex)]
     pub fn ssl_write(&mut self, buf: &[u8]) -> Result<usize, Error> {
-        // See above for why we short-circuit on zero-length buffers
-        if buf.is_empty() {
-            return Ok(0);
-        }
+        cfg_if! {
+            if #[cfg(any(ossl111, libressl350))] {
+                let mut written = 0;
+                let ret = unsafe {
+                    ffi::SSL_write_ex(
+                        self.ssl().as_ptr(),
+                        buf.as_ptr().cast(),
+                        buf.len(),
+                        &mut written,
+                    )
+                };
 
-        let ret = self.ssl.write(buf);
-        if ret > 0 {
-            Ok(ret as usize)
-        } else {
-            Err(self.make_error(ret))
+                if ret > 0 {
+                    Ok(written)
+                } else {
+                    Err(self.make_error(ret))
+                }
+            } else {
+                if buf.is_empty() {
+                    return Ok(0);
+                }
+
+                let len = usize::min(c_int::max_value() as usize, buf.len()) as c_int;
+                let ret = unsafe {
+                    ffi::SSL_write(self.ssl().as_ptr(), buf.as_ptr().cast(), len)
+                };
+                if ret > 0 {
+                    Ok(ret as usize)
+                } else {
+                    Err(self.make_error(ret))
+                }
+            }
         }
     }
 
     /// Reads data from the stream, without removing it from the queue.
-    #[corresponds(SSL_peek)]
+    #[corresponds(SSL_peek_ex)]
     pub fn ssl_peek(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
-        // See above for why we short-circuit on zero-length buffers
-        if buf.is_empty() {
-            return Ok(0);
-        }
+        cfg_if! {
+            if #[cfg(any(ossl111, libressl350))] {
+                let mut readbytes = 0;
+                let ret = unsafe {
+                    ffi::SSL_peek_ex(
+                        self.ssl().as_ptr(),
+                        buf.as_mut_ptr().cast(),
+                        buf.len(),
+                        &mut readbytes,
+                    )
+                };
 
-        let ret = self.ssl.peek(buf);
-        if ret > 0 {
-            Ok(ret as usize)
-        } else {
-            Err(self.make_error(ret))
+                if ret > 0 {
+                    Ok(readbytes)
+                } else {
+                    Err(self.make_error(ret))
+                }
+            } else {
+                if buf.is_empty() {
+                    return Ok(0);
+                }
+
+                let len = usize::min(c_int::max_value() as usize, buf.len()) as c_int;
+                let ret = unsafe {
+                    ffi::SSL_peek(self.ssl().as_ptr(), buf.as_mut_ptr().cast(), len)
+                };
+                if ret > 0 {
+                    Ok(ret as usize)
+                } else {
+                    Err(self.make_error(ret))
+                }
+            }
         }
     }
 
@@ -3442,7 +3916,7 @@
     pub fn get_shutdown(&mut self) -> ShutdownState {
         unsafe {
             let bits = ffi::SSL_get_shutdown(self.ssl.as_ptr());
-            ShutdownState { bits }
+            ShutdownState::from_bits_retain(bits)
         }
     }
 
@@ -3521,20 +3995,12 @@
 
 impl<S: Read + Write> Read for SslStream<S> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        loop {
-            match self.ssl_read(buf) {
-                Ok(n) => return Ok(n),
-                Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => return Ok(0),
-                Err(ref e) if e.code() == ErrorCode::SYSCALL && e.io_error().is_none() => {
-                    return Ok(0);
-                }
-                Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {}
-                Err(e) => {
-                    return Err(e
-                        .into_io_error()
-                        .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e)));
-                }
-            }
+        // SAFETY: `read_uninit` does not de-initialize the buffer
+        unsafe {
+            self.read_uninit(slice::from_raw_parts_mut(
+                buf.as_mut_ptr().cast::<MaybeUninit<u8>>(),
+                buf.len(),
+            ))
         }
     }
 }
@@ -3775,6 +4241,8 @@
 
 bitflags! {
     /// The shutdown state of a session.
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct ShutdownState: c_int {
         /// A close notify message has been sent to the peer.
         const SENT = ffi::SSL_SENT_SHUTDOWN;
diff --git a/src/ssl/test/mod.rs b/src/ssl/test/mod.rs
index dc9cc78..a98bc56 100644
--- a/src/ssl/test/mod.rs
+++ b/src/ssl/test/mod.rs
@@ -10,7 +10,7 @@
 use std::net::{SocketAddr, TcpListener, TcpStream};
 use std::path::Path;
 use std::process::{Child, ChildStdin, Command, Stdio};
-use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
 use std::thread;
 use std::time::Duration;
 
@@ -19,12 +19,12 @@
 use crate::hash::MessageDigest;
 #[cfg(not(boringssl))]
 use crate::ocsp::{OcspResponse, OcspResponseStatus};
-use crate::pkey::PKey;
+use crate::pkey::{Id, PKey};
 use crate::srtp::SrtpProfileId;
-use crate::ssl;
 use crate::ssl::test::server::Server;
 #[cfg(any(ossl110, ossl111, libressl261))]
 use crate::ssl::SslVersion;
+use crate::ssl::{self, NameType, SslConnectorBuilder};
 #[cfg(ossl111)]
 use crate::ssl::{ClientHelloResponse, ExtensionContext};
 use crate::ssl::{
@@ -84,17 +84,21 @@
 
 #[test]
 fn verify_untrusted_callback_override_ok() {
+    static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
     let server = Server::builder().build();
 
     let mut client = server.client();
     client
         .ctx()
         .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+            CALLED_BACK.store(true, Ordering::SeqCst);
             assert!(x509.current_cert().is_some());
             true
         });
 
     client.connect();
+    assert!(CALLED_BACK.load(Ordering::SeqCst));
 }
 
 #[test]
@@ -113,6 +117,8 @@
 
 #[test]
 fn verify_trusted_callback_override_ok() {
+    static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
     let server = Server::builder().build();
 
     let mut client = server.client();
@@ -120,11 +126,13 @@
     client
         .ctx()
         .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+            CALLED_BACK.store(true, Ordering::SeqCst);
             assert!(x509.current_cert().is_some());
             true
         });
 
     client.connect();
+    assert!(CALLED_BACK.load(Ordering::SeqCst));
 }
 
 #[test]
@@ -144,21 +152,27 @@
 
 #[test]
 fn verify_callback_load_certs() {
+    static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
     let server = Server::builder().build();
 
     let mut client = server.client();
     client
         .ctx()
         .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+            CALLED_BACK.store(true, Ordering::SeqCst);
             assert!(x509.current_cert().is_some());
             true
         });
 
     client.connect();
+    assert!(CALLED_BACK.load(Ordering::SeqCst));
 }
 
 #[test]
 fn verify_trusted_get_error_ok() {
+    static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
     let server = Server::builder().build();
 
     let mut client = server.client();
@@ -166,11 +180,13 @@
     client
         .ctx()
         .set_verify_callback(SslVerifyMode::PEER, |_, x509| {
+            CALLED_BACK.store(true, Ordering::SeqCst);
             assert_eq!(x509.error(), X509VerifyResult::OK);
             true
         });
 
     client.connect();
+    assert!(CALLED_BACK.load(Ordering::SeqCst));
 }
 
 #[test]
@@ -306,6 +322,56 @@
     );
 }
 
+// when a connection uses ECDHE P-384 key exchange, then the temp key APIs
+// return P-384 keys, and the peer and local keys are different.
+#[test]
+#[cfg(ossl300)]
+fn peer_tmp_key_p384() {
+    let mut server = Server::builder();
+    server.ctx().set_groups_list("P-384").unwrap();
+    let server = server.build();
+    let s = server.client().connect();
+    let peer_temp = s.ssl().peer_tmp_key().unwrap();
+    assert_eq!(peer_temp.id(), Id::EC);
+    assert_eq!(peer_temp.bits(), 384);
+
+    let local_temp = s.ssl().tmp_key().unwrap();
+    assert_eq!(local_temp.id(), Id::EC);
+    assert_eq!(local_temp.bits(), 384);
+
+    assert_ne!(
+        peer_temp.ec_key().unwrap().public_key_to_der().unwrap(),
+        local_temp.ec_key().unwrap().public_key_to_der().unwrap(),
+    );
+}
+
+// when a connection uses RSA key exchange, then the peer (server) temp key is
+// an Error because there is no temp key, and the local (client) temp key is the
+// temp key sent in the initial key share.
+#[test]
+#[cfg(ossl300)]
+fn peer_tmp_key_rsa() {
+    let mut server = Server::builder();
+    server.ctx().set_cipher_list("RSA").unwrap();
+    // RSA key exchange is not allowed in TLS 1.3, so force the connection
+    // to negotiate TLS 1.2
+    server
+        .ctx()
+        .set_max_proto_version(Some(SslVersion::TLS1_2))
+        .unwrap();
+    let server = server.build();
+    let mut client = server.client();
+    client.ctx().set_groups_list("P-521").unwrap();
+    let s = client.connect();
+    let peer_temp = s.ssl().peer_tmp_key();
+    assert!(peer_temp.is_err());
+
+    // this is the temp key that the client sent in the initial key share
+    let local_temp = s.ssl().tmp_key().unwrap();
+    assert_eq!(local_temp.id(), Id::EC);
+    assert_eq!(local_temp.bits(), 521);
+}
+
 /// Tests that when both the client as well as the server use SRTP and their
 /// lists of supported protocols have an overlap -- with only ONE protocol
 /// being valid for both.
@@ -451,7 +517,7 @@
 }
 
 #[test]
-#[cfg(any(ossl110))]
+#[cfg(ossl110)]
 fn test_alpn_server_select_none_fatal() {
     let mut server = Server::builder();
     server.ctx().set_alpn_select_callback(|_, client| {
@@ -469,8 +535,11 @@
 #[test]
 #[cfg(any(ossl102, libressl261))]
 fn test_alpn_server_select_none() {
+    static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
     let mut server = Server::builder();
     server.ctx().set_alpn_select_callback(|_, client| {
+        CALLED_BACK.store(true, Ordering::SeqCst);
         ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK)
     });
     let server = server.build();
@@ -479,10 +548,11 @@
     client.ctx().set_alpn_protos(b"\x06http/2").unwrap();
     let s = client.connect();
     assert_eq!(None, s.ssl().selected_alpn_protocol());
+    assert!(CALLED_BACK.load(Ordering::SeqCst));
 }
 
 #[test]
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(boringssl, ossl102, libressl261))]
 fn test_alpn_server_unilateral() {
     let server = Server::builder().build();
 
@@ -595,7 +665,7 @@
 
     {
         let new_ctx_a = SslContext::builder(SslMethod::tls()).unwrap().build();
-        let _new_ctx_b = ssl.set_ssl_context(&new_ctx_a);
+        ssl.set_ssl_context(&new_ctx_a).unwrap();
     }
 }
 
@@ -731,7 +801,7 @@
 }
 
 #[test]
-fn connector_no_hostname_can_disable_verify() {
+fn connector_can_disable_verify() {
     let server = Server::builder().build();
 
     let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
@@ -742,10 +812,64 @@
     let mut s = connector
         .configure()
         .unwrap()
-        .verify_hostname(false)
+        .connect("fizzbuzz.com", s)
+        .unwrap();
+    s.read_exact(&mut [0]).unwrap();
+}
+
+#[test]
+fn connector_does_use_sni_with_dnsnames() {
+    static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+    let mut builder = Server::builder();
+    builder.ctx().set_servername_callback(|ssl, _| {
+        assert_eq!(ssl.servername(NameType::HOST_NAME), Some("foobar.com"));
+        CALLED_BACK.store(true, Ordering::SeqCst);
+        Ok(())
+    });
+    let server = builder.build();
+
+    let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+    connector.set_ca_file("test/root-ca.pem").unwrap();
+
+    let s = server.connect_tcp();
+    let mut s = connector
+        .build()
+        .configure()
+        .unwrap()
         .connect("foobar.com", s)
         .unwrap();
     s.read_exact(&mut [0]).unwrap();
+
+    assert!(CALLED_BACK.load(Ordering::SeqCst));
+}
+
+#[test]
+fn connector_doesnt_use_sni_with_ips() {
+    static CALLED_BACK: AtomicBool = AtomicBool::new(false);
+
+    let mut builder = Server::builder();
+    builder.ctx().set_servername_callback(|ssl, _| {
+        assert_eq!(ssl.servername(NameType::HOST_NAME), None);
+        CALLED_BACK.store(true, Ordering::SeqCst);
+        Ok(())
+    });
+    let server = builder.build();
+
+    let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
+    // The server's cert isn't issued for 127.0.0.1 but we don't care for this test.
+    connector.set_verify(SslVerifyMode::NONE);
+
+    let s = server.connect_tcp();
+    let mut s = connector
+        .build()
+        .configure()
+        .unwrap()
+        .connect("127.0.0.1", s)
+        .unwrap();
+    s.read_exact(&mut [0]).unwrap();
+
+    assert!(CALLED_BACK.load(Ordering::SeqCst));
 }
 
 fn test_mozilla_server(new: fn(SslMethod) -> Result<SslAcceptorBuilder, ErrorStack>) {
@@ -949,7 +1073,9 @@
     assert!(ssl.session().is_none());
 }
 
-/// possible LibreSSL bug since 3.2.1
+/// LibreSSL 3.2.1 enabled TLSv1.3 by default for clients and sessions do
+/// not work due to lack of PSK support. The test passes with NO_TLSV1_3,
+/// but let's ignore it until LibreSSL supports it out of the box.
 #[test]
 #[cfg_attr(libressl321, ignore)]
 fn active_session() {
@@ -1007,7 +1133,9 @@
     assert!(CALLED_BACK_CLIENT.load(Ordering::SeqCst));
 }
 
-/// possible LibreSSL bug since 3.2.1
+/// LibreSSL 3.2.1 enabled TLSv1.3 by default for clients and sessions do
+/// not work due to lack of PSK support. The test passes with NO_TLSV1_3,
+/// but let's ignore it until LibreSSL supports it out of the box.
 #[test]
 #[cfg_attr(libressl321, ignore)]
 fn new_session_callback() {
@@ -1032,7 +1160,9 @@
     assert!(CALLED_BACK.load(Ordering::SeqCst));
 }
 
-/// possible LibreSSL bug since 3.2.1
+/// LibreSSL 3.2.1 enabled TLSv1.3 by default for clients and sessions do
+/// not work due to lack of PSK support. The test passes with NO_TLSV1_3,
+/// but let's ignore it until LibreSSL supports it out of the box.
 #[test]
 #[cfg_attr(libressl321, ignore)]
 fn new_session_callback_swapped_ctx() {
@@ -1384,6 +1514,9 @@
         assert!(ssl.client_hello_session_id().is_some());
         assert!(ssl.client_hello_ciphers().is_some());
         assert!(ssl.client_hello_compression_methods().is_some());
+        assert!(ssl
+            .bytes_to_cipher_list(ssl.client_hello_ciphers().unwrap(), ssl.client_hello_isv2())
+            .is_ok());
 
         CALLED_BACK.store(true, Ordering::SeqCst);
         Ok(ClientHelloResponse::SUCCESS)
@@ -1422,3 +1555,133 @@
     let mut ssl = Ssl::new(&ctx).unwrap();
     assert!(ssl.add_chain_cert(cert).is_ok());
 }
+#[test]
+#[cfg(ossl111)]
+fn set_ssl_certificate_key_related_api() {
+    let cert_str: &str = include_str!("../../../test/cert.pem");
+    let key_str: &str = include_str!("../../../test/key.pem");
+    let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
+    let cert_x509 = X509::from_pem(CERT).unwrap();
+    let mut ssl = Ssl::new(&ctx).unwrap();
+    assert!(ssl.set_method(SslMethod::tls()).is_ok());
+    ssl.set_private_key_file("test/key.pem", SslFiletype::PEM)
+        .unwrap();
+    {
+        let pkey = String::from_utf8(
+            ssl.private_key()
+                .unwrap()
+                .private_key_to_pem_pkcs8()
+                .unwrap(),
+        )
+        .unwrap();
+        assert!(pkey.lines().eq(key_str.lines()));
+    }
+    let pkey = PKey::private_key_from_pem(KEY).unwrap();
+    ssl.set_private_key(pkey.as_ref()).unwrap();
+    {
+        let pkey = String::from_utf8(
+            ssl.private_key()
+                .unwrap()
+                .private_key_to_pem_pkcs8()
+                .unwrap(),
+        )
+        .unwrap();
+        assert!(pkey.lines().eq(key_str.lines()));
+    }
+    ssl.set_certificate(cert_x509.as_ref()).unwrap();
+    let cert = String::from_utf8(ssl.certificate().unwrap().to_pem().unwrap()).unwrap();
+    assert!(cert.lines().eq(cert_str.lines()));
+    ssl.add_client_ca(cert_x509.as_ref()).unwrap();
+    ssl.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap();
+    ssl.set_max_proto_version(Some(SslVersion::TLS1_3)).unwrap();
+    ssl.set_cipher_list("HIGH:!aNULL:!MD5").unwrap();
+    ssl.set_ciphersuites("TLS_AES_128_GCM_SHA256").unwrap();
+    let x509 = X509::from_pem(ROOT_CERT).unwrap();
+    let mut builder = X509StoreBuilder::new().unwrap();
+    builder.add_cert(x509).unwrap();
+    let store = builder.build();
+    ssl.set_verify_cert_store(store).unwrap();
+}
+
+#[test]
+#[cfg(ossl110)]
+fn test_ssl_set_cert_chain_file() {
+    let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
+    let mut ssl = Ssl::new(&ctx).unwrap();
+    ssl.set_certificate_chain_file("test/cert.pem").unwrap();
+}
+
+#[test]
+#[cfg(ossl111)]
+fn set_num_tickets() {
+    let mut ctx = SslContext::builder(SslMethod::tls_server()).unwrap();
+    ctx.set_num_tickets(3).unwrap();
+    let ctx = ctx.build();
+    assert_eq!(3, ctx.num_tickets());
+
+    let mut ssl = Ssl::new(&ctx).unwrap();
+    ssl.set_num_tickets(5).unwrap();
+    let ssl = ssl;
+    assert_eq!(5, ssl.num_tickets());
+}
+
+#[test]
+#[cfg(ossl110)]
+fn set_security_level() {
+    let mut ctx = SslContext::builder(SslMethod::tls_server()).unwrap();
+    ctx.set_security_level(3);
+    let ctx = ctx.build();
+    assert_eq!(3, ctx.security_level());
+
+    let mut ssl = Ssl::new(&ctx).unwrap();
+    ssl.set_security_level(4);
+    let ssl = ssl;
+    assert_eq!(4, ssl.security_level());
+}
+
+#[test]
+fn ssl_ctx_ex_data_leak() {
+    static DROPS: AtomicUsize = AtomicUsize::new(0);
+
+    struct DropTest;
+
+    impl Drop for DropTest {
+        fn drop(&mut self) {
+            DROPS.fetch_add(1, Ordering::Relaxed);
+        }
+    }
+
+    let idx = SslContext::new_ex_index().unwrap();
+
+    let mut ctx = SslContext::builder(SslMethod::tls()).unwrap();
+    ctx.set_ex_data(idx, DropTest);
+    ctx.set_ex_data(idx, DropTest);
+    assert_eq!(DROPS.load(Ordering::Relaxed), 1);
+
+    drop(ctx);
+    assert_eq!(DROPS.load(Ordering::Relaxed), 2);
+}
+
+#[test]
+fn ssl_ex_data_leak() {
+    static DROPS: AtomicUsize = AtomicUsize::new(0);
+
+    struct DropTest;
+
+    impl Drop for DropTest {
+        fn drop(&mut self) {
+            DROPS.fetch_add(1, Ordering::Relaxed);
+        }
+    }
+
+    let idx = Ssl::new_ex_index().unwrap();
+
+    let ctx = SslContext::builder(SslMethod::tls()).unwrap().build();
+    let mut ssl = Ssl::new(&ctx).unwrap();
+    ssl.set_ex_data(idx, DropTest);
+    ssl.set_ex_data(idx, DropTest);
+    assert_eq!(DROPS.load(Ordering::Relaxed), 1);
+
+    drop(ssl);
+    assert_eq!(DROPS.load(Ordering::Relaxed), 2);
+}
diff --git a/src/symm.rs b/src/symm.rs
index beff5fc..23b9ce4 100644
--- a/src/symm.rs
+++ b/src/symm.rs
@@ -68,7 +68,7 @@
 ///
 /// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms.
 ///
-/// [`EVP_EncryptInit`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html
+/// [`EVP_EncryptInit`]: https://www.openssl.org/docs/manmaster/crypto/EVP_EncryptInit.html
 #[derive(Copy, Clone, PartialEq, Eq)]
 pub struct Cipher(*const ffi::EVP_CIPHER);
 
@@ -77,7 +77,7 @@
     ///
     /// This corresponds to [`EVP_get_cipherbynid`]
     ///
-    /// [`EVP_get_cipherbynid`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_get_cipherbyname.html
+    /// [`EVP_get_cipherbynid`]: https://www.openssl.org/docs/manmaster/crypto/EVP_get_cipherbyname.html
     pub fn from_nid(nid: Nid) -> Option<Cipher> {
         let ptr = unsafe { ffi::EVP_get_cipherbyname(ffi::OBJ_nid2sn(nid.as_raw())) };
         if ptr.is_null() {
@@ -91,7 +91,7 @@
     ///
     /// This corresponds to [`EVP_CIPHER_nid`]
     ///
-    /// [`EVP_CIPHER_nid`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_CIPHER_nid.html
+    /// [`EVP_CIPHER_nid`]: https://www.openssl.org/docs/manmaster/crypto/EVP_CIPHER_nid.html
     pub fn nid(&self) -> Nid {
         let nid = unsafe { ffi::EVP_CIPHER_nid(self.0) };
         Nid::from_raw(nid)
@@ -143,7 +143,7 @@
     }
 
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     pub fn aes_128_ocb() -> Cipher {
         unsafe { Cipher(ffi::EVP_aes_128_ocb()) }
     }
@@ -189,7 +189,7 @@
     }
 
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     pub fn aes_192_ocb() -> Cipher {
         unsafe { Cipher(ffi::EVP_aes_192_ocb()) }
     }
@@ -240,7 +240,7 @@
     }
 
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     pub fn aes_256_ocb() -> Cipher {
         unsafe { Cipher(ffi::EVP_aes_256_ocb()) }
     }
@@ -255,12 +255,14 @@
         unsafe { Cipher(ffi::EVP_bf_ecb()) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
+    #[cfg(not(boringssl))]
     pub fn bf_cfb64() -> Cipher {
         unsafe { Cipher(ffi::EVP_bf_cfb64()) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_BF"))]
+    #[cfg(not(boringssl))]
     pub fn bf_ofb() -> Cipher {
         unsafe { Cipher(ffi::EVP_bf_ofb()) }
     }
@@ -281,43 +283,182 @@
         unsafe { Cipher(ffi::EVP_des_ede3_cbc()) }
     }
 
+    pub fn des_ede3_ecb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_des_ede3_ecb()) }
+    }
+
     #[cfg(not(boringssl))]
     pub fn des_ede3_cfb64() -> Cipher {
         unsafe { Cipher(ffi::EVP_des_ede3_cfb64()) }
     }
 
+    #[cfg(not(boringssl))]
+    pub fn des_ede3_cfb8() -> Cipher {
+        unsafe { Cipher(ffi::EVP_des_ede3_cfb8()) }
+    }
+
+    #[cfg(not(boringssl))]
+    pub fn des_ede3_ofb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_des_ede3_ofb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_RC4"))]
     pub fn rc4() -> Cipher {
         unsafe { Cipher(ffi::EVP_rc4()) }
     }
 
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_128_cbc() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_128_cbc()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_128_ecb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_128_ecb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_128_ofb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_128_ofb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_128_cfb128() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_128_cfb128()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_192_cbc() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_192_cbc()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_192_ecb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_192_ecb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_192_ofb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_192_ofb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_192_cfb128() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_192_cfb128()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_256_cbc() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_256_cbc()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_256_ecb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_256_ecb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_256_ofb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_256_ofb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAMELLIA"))]
+    #[cfg(not(boringssl))]
+    pub fn camellia_256_cfb128() -> Cipher {
+        unsafe { Cipher(ffi::EVP_camellia_256_cfb128()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAST"))]
+    #[cfg(not(boringssl))]
+    pub fn cast5_cbc() -> Cipher {
+        unsafe { Cipher(ffi::EVP_cast5_cbc()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAST"))]
+    #[cfg(not(boringssl))]
+    pub fn cast5_ecb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_cast5_ecb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAST"))]
+    #[cfg(not(boringssl))]
+    pub fn cast5_ofb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_cast5_ofb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_CAST"))]
+    #[cfg(not(boringssl))]
+    pub fn cast5_cfb64() -> Cipher {
+        unsafe { Cipher(ffi::EVP_cast5_cfb64()) }
+    }
+
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_CHACHA")))]
+    #[cfg(all(any(ossl110, libressl310), not(osslconf = "OPENSSL_NO_CHACHA")))]
     pub fn chacha20() -> Cipher {
         unsafe { Cipher(ffi::EVP_chacha20()) }
     }
 
     /// Requires OpenSSL 1.1.0 or newer.
-    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_CHACHA")))]
+    #[cfg(all(any(ossl110, libressl360), not(osslconf = "OPENSSL_NO_CHACHA")))]
     pub fn chacha20_poly1305() -> Cipher {
         unsafe { Cipher(ffi::EVP_chacha20_poly1305()) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_IDEA"))]
+    #[cfg(not(boringssl))]
+    pub fn idea_cbc() -> Cipher {
+        unsafe { Cipher(ffi::EVP_idea_cbc()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_IDEA"))]
+    #[cfg(not(boringssl))]
+    pub fn idea_ecb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_idea_ecb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_IDEA"))]
+    #[cfg(not(boringssl))]
+    pub fn idea_ofb() -> Cipher {
+        unsafe { Cipher(ffi::EVP_idea_ofb()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_IDEA"))]
+    #[cfg(not(boringssl))]
+    pub fn idea_cfb64() -> Cipher {
+        unsafe { Cipher(ffi::EVP_idea_cfb64()) }
+    }
+
+    #[cfg(not(osslconf = "OPENSSL_NO_SEED"))]
+    #[cfg(not(boringssl))]
     pub fn seed_cbc() -> Cipher {
         unsafe { Cipher(ffi::EVP_seed_cbc()) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_SEED"))]
+    #[cfg(not(boringssl))]
     pub fn seed_cfb128() -> Cipher {
         unsafe { Cipher(ffi::EVP_seed_cfb128()) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_SEED"))]
+    #[cfg(not(boringssl))]
     pub fn seed_ecb() -> Cipher {
         unsafe { Cipher(ffi::EVP_seed_ecb()) }
     }
 
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED")))]
+    #[cfg(not(osslconf = "OPENSSL_NO_SEED"))]
+    #[cfg(not(boringssl))]
     pub fn seed_ofb() -> Cipher {
         unsafe { Cipher(ffi::EVP_seed_ofb()) }
     }
@@ -404,14 +545,14 @@
     }
 
     /// Determines whether the cipher is using OCB mode
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     fn is_ocb(self) -> bool {
         self == Cipher::aes_128_ocb()
             || self == Cipher::aes_192_ocb()
             || self == Cipher::aes_256_ocb()
     }
 
-    #[cfg(not(ossl110))]
+    #[cfg(any(not(ossl110), osslconf = "OPENSSL_NO_OCB"))]
     const fn is_ocb(self) -> bool {
         false
     }
@@ -584,6 +725,27 @@
         self.ctx.cipher_update(input, Some(output))
     }
 
+    /// Feeds data from `input` through the cipher, writing encrypted/decrypted
+    /// bytes into `output`.
+    ///
+    /// The number of bytes written to `output` is returned. Note that this may
+    /// not be equal to the length of `input`.
+    ///
+    /// # Safety
+    ///
+    /// The caller must provide an `output` buffer large enough to contain
+    /// correct number of bytes. For streaming ciphers the output buffer size
+    /// should be at least as big as the input buffer. For block ciphers the
+    /// size of the output buffer depends on the state of partially updated
+    /// blocks.
+    pub unsafe fn update_unchecked(
+        &mut self,
+        input: &[u8],
+        output: &mut [u8],
+    ) -> Result<usize, ErrorStack> {
+        self.ctx.cipher_update_unchecked(input, Some(output))
+    }
+
     /// Finishes the encryption/decryption process, writing any remaining data
     /// to `output`.
     ///
@@ -1424,7 +1586,7 @@
     }
 
     #[test]
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     fn test_aes_128_ocb() {
         let key = "000102030405060708090a0b0c0d0e0f";
         let aad = "0001020304050607";
@@ -1460,7 +1622,7 @@
     }
 
     #[test]
-    #[cfg(ossl110)]
+    #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))]
     fn test_aes_128_ocb_fail() {
         let key = "000102030405060708090a0b0c0d0e0f";
         let aad = "0001020304050607";
@@ -1480,7 +1642,7 @@
     }
 
     #[test]
-    #[cfg(any(ossl110))]
+    #[cfg(any(ossl110, libressl310))]
     fn test_chacha20() {
         let key = "0000000000000000000000000000000000000000000000000000000000000000";
         let iv = "00000000000000000000000000000000";
@@ -1495,7 +1657,7 @@
     }
 
     #[test]
-    #[cfg(any(ossl110))]
+    #[cfg(any(ossl110, libressl360))]
     fn test_chacha20_poly1305() {
         let key = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f";
         let iv = "070000004041424344454647";
@@ -1536,7 +1698,7 @@
     }
 
     #[test]
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED", ossl300)))]
+    #[cfg(not(any(osslconf = "OPENSSL_NO_SEED", ossl300)))]
     fn test_seed_cbc() {
         #[cfg(ossl300)]
         let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap();
@@ -1550,7 +1712,7 @@
     }
 
     #[test]
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED", ossl300)))]
+    #[cfg(not(any(osslconf = "OPENSSL_NO_SEED", ossl300)))]
     fn test_seed_cfb128() {
         #[cfg(ossl300)]
         let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap();
@@ -1564,7 +1726,7 @@
     }
 
     #[test]
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED", ossl300)))]
+    #[cfg(not(any(osslconf = "OPENSSL_NO_SEED", ossl300)))]
     fn test_seed_ecb() {
         #[cfg(ossl300)]
         let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap();
@@ -1578,7 +1740,7 @@
     }
 
     #[test]
-    #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED", ossl300)))]
+    #[cfg(not(any(osslconf = "OPENSSL_NO_SEED", ossl300)))]
     fn test_seed_ofb() {
         #[cfg(ossl300)]
         let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap();
diff --git a/src/x509/extension.rs b/src/x509/extension.rs
index ebbea1c..11e0151 100644
--- a/src/x509/extension.rs
+++ b/src/x509/extension.rs
@@ -18,9 +18,11 @@
 //! ```
 use std::fmt::Write;
 
+use crate::asn1::Asn1Object;
 use crate::error::ErrorStack;
 use crate::nid::Nid;
-use crate::x509::{X509Extension, X509v3Context};
+use crate::x509::{GeneralName, Stack, X509Extension, X509v3Context};
+use foreign_types::ForeignType;
 
 /// An extension which indicates whether a certificate is a CA certificate.
 pub struct BasicConstraints {
@@ -65,6 +67,9 @@
     }
 
     /// Return the `BasicConstraints` extension as an `X509Extension`.
+    // Temporarily silence the deprecation warning - this should be ported to
+    // `X509Extension::new_internal`.
+    #[allow(deprecated)]
     pub fn build(&self) -> Result<X509Extension, ErrorStack> {
         let mut value = String::new();
         if self.critical {
@@ -181,6 +186,9 @@
     }
 
     /// Return the `KeyUsage` extension as an `X509Extension`.
+    // Temporarily silence the deprecation warning - this should be ported to
+    // `X509Extension::new_internal`.
+    #[allow(deprecated)]
     pub fn build(&self) -> Result<X509Extension, ErrorStack> {
         let mut value = String::new();
         let mut first = true;
@@ -222,18 +230,7 @@
 /// for which the certificate public key can be used for.
 pub struct ExtendedKeyUsage {
     critical: bool,
-    server_auth: bool,
-    client_auth: bool,
-    code_signing: bool,
-    email_protection: bool,
-    time_stamping: bool,
-    ms_code_ind: bool,
-    ms_code_com: bool,
-    ms_ctl_sign: bool,
-    ms_sgc: bool,
-    ms_efs: bool,
-    ns_sgc: bool,
-    other: Vec<String>,
+    items: Vec<String>,
 }
 
 impl Default for ExtendedKeyUsage {
@@ -247,18 +244,7 @@
     pub fn new() -> ExtendedKeyUsage {
         ExtendedKeyUsage {
             critical: false,
-            server_auth: false,
-            client_auth: false,
-            code_signing: false,
-            email_protection: false,
-            time_stamping: false,
-            ms_code_ind: false,
-            ms_code_com: false,
-            ms_ctl_sign: false,
-            ms_sgc: false,
-            ms_efs: false,
-            ns_sgc: false,
-            other: vec![],
+            items: vec![],
         }
     }
 
@@ -270,101 +256,74 @@
 
     /// Sets the `serverAuth` flag to `true`.
     pub fn server_auth(&mut self) -> &mut ExtendedKeyUsage {
-        self.server_auth = true;
-        self
+        self.other("serverAuth")
     }
 
     /// Sets the `clientAuth` flag to `true`.
     pub fn client_auth(&mut self) -> &mut ExtendedKeyUsage {
-        self.client_auth = true;
-        self
+        self.other("clientAuth")
     }
 
     /// Sets the `codeSigning` flag to `true`.
     pub fn code_signing(&mut self) -> &mut ExtendedKeyUsage {
-        self.code_signing = true;
-        self
+        self.other("codeSigning")
     }
 
     /// Sets the `emailProtection` flag to `true`.
     pub fn email_protection(&mut self) -> &mut ExtendedKeyUsage {
-        self.email_protection = true;
-        self
+        self.other("emailProtection")
     }
 
     /// Sets the `timeStamping` flag to `true`.
     pub fn time_stamping(&mut self) -> &mut ExtendedKeyUsage {
-        self.time_stamping = true;
-        self
+        self.other("timeStamping")
     }
 
     /// Sets the `msCodeInd` flag to `true`.
     pub fn ms_code_ind(&mut self) -> &mut ExtendedKeyUsage {
-        self.ms_code_ind = true;
-        self
+        self.other("msCodeInd")
     }
 
     /// Sets the `msCodeCom` flag to `true`.
     pub fn ms_code_com(&mut self) -> &mut ExtendedKeyUsage {
-        self.ms_code_com = true;
-        self
+        self.other("msCodeCom")
     }
 
     /// Sets the `msCTLSign` flag to `true`.
     pub fn ms_ctl_sign(&mut self) -> &mut ExtendedKeyUsage {
-        self.ms_ctl_sign = true;
-        self
+        self.other("msCTLSign")
     }
 
     /// Sets the `msSGC` flag to `true`.
     pub fn ms_sgc(&mut self) -> &mut ExtendedKeyUsage {
-        self.ms_sgc = true;
-        self
+        self.other("msSGC")
     }
 
     /// Sets the `msEFS` flag to `true`.
     pub fn ms_efs(&mut self) -> &mut ExtendedKeyUsage {
-        self.ms_efs = true;
-        self
+        self.other("msEFS")
     }
 
     /// Sets the `nsSGC` flag to `true`.
     pub fn ns_sgc(&mut self) -> &mut ExtendedKeyUsage {
-        self.ns_sgc = true;
-        self
+        self.other("nsSGC")
     }
 
     /// Sets a flag not already defined.
     pub fn other(&mut self, other: &str) -> &mut ExtendedKeyUsage {
-        self.other.push(other.to_owned());
+        self.items.push(other.to_string());
         self
     }
 
     /// Return the `ExtendedKeyUsage` extension as an `X509Extension`.
     pub fn build(&self) -> Result<X509Extension, ErrorStack> {
-        let mut value = String::new();
-        let mut first = true;
-        append(&mut value, &mut first, self.critical, "critical");
-        append(&mut value, &mut first, self.server_auth, "serverAuth");
-        append(&mut value, &mut first, self.client_auth, "clientAuth");
-        append(&mut value, &mut first, self.code_signing, "codeSigning");
-        append(
-            &mut value,
-            &mut first,
-            self.email_protection,
-            "emailProtection",
-        );
-        append(&mut value, &mut first, self.time_stamping, "timeStamping");
-        append(&mut value, &mut first, self.ms_code_ind, "msCodeInd");
-        append(&mut value, &mut first, self.ms_code_com, "msCodeCom");
-        append(&mut value, &mut first, self.ms_ctl_sign, "msCTLSign");
-        append(&mut value, &mut first, self.ms_sgc, "msSGC");
-        append(&mut value, &mut first, self.ms_efs, "msEFS");
-        append(&mut value, &mut first, self.ns_sgc, "nsSGC");
-        for other in &self.other {
-            append(&mut value, &mut first, true, other);
+        let mut stack = Stack::new()?;
+        for item in &self.items {
+            stack.push(Asn1Object::from_str(item)?)?;
         }
-        X509Extension::new_nid(None, None, Nid::EXT_KEY_USAGE, &value)
+        unsafe {
+            X509Extension::new_internal(Nid::EXT_KEY_USAGE, self.critical, stack.as_ptr().cast())
+        }
     }
 }
 
@@ -393,6 +352,9 @@
     }
 
     /// Return a `SubjectKeyIdentifier` extension as an `X509Extension`.
+    // Temporarily silence the deprecation warning - this should be ported to
+    // `X509Extension::new_internal`.
+    #[allow(deprecated)]
     pub fn build(&self, ctx: &X509v3Context<'_>) -> Result<X509Extension, ErrorStack> {
         let mut value = String::new();
         let mut first = true;
@@ -445,6 +407,9 @@
     }
 
     /// Return a `AuthorityKeyIdentifier` extension as an `X509Extension`.
+    // Temporarily silence the deprecation warning - this should be ported to
+    // `X509Extension::new_internal`.
+    #[allow(deprecated)]
     pub fn build(&self, ctx: &X509v3Context<'_>) -> Result<X509Extension, ErrorStack> {
         let mut value = String::new();
         let mut first = true;
@@ -463,11 +428,20 @@
     }
 }
 
+enum RustGeneralName {
+    Dns(String),
+    Email(String),
+    Uri(String),
+    Ip(String),
+    Rid(String),
+    OtherName(Asn1Object, Vec<u8>),
+}
+
 /// An extension that allows additional identities to be bound to the subject
 /// of the certificate.
 pub struct SubjectAlternativeName {
     critical: bool,
-    names: Vec<String>,
+    items: Vec<RustGeneralName>,
 }
 
 impl Default for SubjectAlternativeName {
@@ -481,7 +455,7 @@
     pub fn new() -> SubjectAlternativeName {
         SubjectAlternativeName {
             critical: false,
-            names: vec![],
+            items: vec![],
         }
     }
 
@@ -493,55 +467,85 @@
 
     /// Sets the `email` flag.
     pub fn email(&mut self, email: &str) -> &mut SubjectAlternativeName {
-        self.names.push(format!("email:{}", email));
+        self.items.push(RustGeneralName::Email(email.to_string()));
         self
     }
 
     /// Sets the `uri` flag.
     pub fn uri(&mut self, uri: &str) -> &mut SubjectAlternativeName {
-        self.names.push(format!("URI:{}", uri));
+        self.items.push(RustGeneralName::Uri(uri.to_string()));
         self
     }
 
     /// Sets the `dns` flag.
     pub fn dns(&mut self, dns: &str) -> &mut SubjectAlternativeName {
-        self.names.push(format!("DNS:{}", dns));
+        self.items.push(RustGeneralName::Dns(dns.to_string()));
         self
     }
 
     /// Sets the `rid` flag.
     pub fn rid(&mut self, rid: &str) -> &mut SubjectAlternativeName {
-        self.names.push(format!("RID:{}", rid));
+        self.items.push(RustGeneralName::Rid(rid.to_string()));
         self
     }
 
     /// Sets the `ip` flag.
     pub fn ip(&mut self, ip: &str) -> &mut SubjectAlternativeName {
-        self.names.push(format!("IP:{}", ip));
+        self.items.push(RustGeneralName::Ip(ip.to_string()));
         self
     }
 
     /// Sets the `dirName` flag.
-    pub fn dir_name(&mut self, dir_name: &str) -> &mut SubjectAlternativeName {
-        self.names.push(format!("dirName:{}", dir_name));
-        self
+    ///
+    /// Not currently actually supported, always panics.
+    #[deprecated = "dir_name is deprecated and always panics. Please file a bug if you have a use case for this."]
+    pub fn dir_name(&mut self, _dir_name: &str) -> &mut SubjectAlternativeName {
+        unimplemented!(
+            "This has not yet been adapted for the new internals. File a bug if you need this."
+        );
     }
 
     /// Sets the `otherName` flag.
-    pub fn other_name(&mut self, other_name: &str) -> &mut SubjectAlternativeName {
-        self.names.push(format!("otherName:{}", other_name));
+    ///
+    /// Not currently actually supported, always panics. Please use other_name2
+    #[deprecated = "other_name is deprecated and always panics. Please use other_name2."]
+    pub fn other_name(&mut self, _other_name: &str) -> &mut SubjectAlternativeName {
+        unimplemented!("This has not yet been adapted for the new internals. Use other_name2.");
+    }
+
+    /// Sets the `otherName` flag.
+    ///
+    /// `content` must be a valid der encoded ASN1_TYPE
+    ///
+    /// If you want to add just a ia5string use `other_name_ia5string`
+    pub fn other_name2(&mut self, oid: Asn1Object, content: &[u8]) -> &mut SubjectAlternativeName {
+        self.items
+            .push(RustGeneralName::OtherName(oid, content.into()));
         self
     }
 
     /// Return a `SubjectAlternativeName` extension as an `X509Extension`.
-    pub fn build(&self, ctx: &X509v3Context<'_>) -> Result<X509Extension, ErrorStack> {
-        let mut value = String::new();
-        let mut first = true;
-        append(&mut value, &mut first, self.critical, "critical");
-        for name in &self.names {
-            append(&mut value, &mut first, true, name);
+    pub fn build(&self, _ctx: &X509v3Context<'_>) -> Result<X509Extension, ErrorStack> {
+        let mut stack = Stack::new()?;
+        for item in &self.items {
+            let gn = match item {
+                RustGeneralName::Dns(s) => GeneralName::new_dns(s.as_bytes())?,
+                RustGeneralName::Email(s) => GeneralName::new_email(s.as_bytes())?,
+                RustGeneralName::Uri(s) => GeneralName::new_uri(s.as_bytes())?,
+                RustGeneralName::Ip(s) => {
+                    GeneralName::new_ip(s.parse().map_err(|_| ErrorStack::get())?)?
+                }
+                RustGeneralName::Rid(s) => GeneralName::new_rid(Asn1Object::from_str(s)?)?,
+                RustGeneralName::OtherName(oid, content) => {
+                    GeneralName::new_other_name(oid.clone(), content)?
+                }
+            };
+            stack.push(gn)?;
         }
-        X509Extension::new_nid(None, Some(ctx), Nid::SUBJECT_ALT_NAME, &value)
+
+        unsafe {
+            X509Extension::new_internal(Nid::SUBJECT_ALT_NAME, self.critical, stack.as_ptr().cast())
+        }
     }
 }
 
diff --git a/src/x509/mod.rs b/src/x509/mod.rs
index 40e5022..e7a139b 100644
--- a/src/x509/mod.rs
+++ b/src/x509/mod.rs
@@ -8,21 +8,24 @@
 //! the secure protocol for browsing the web.
 
 use cfg_if::cfg_if;
-use foreign_types::{ForeignType, ForeignTypeRef};
-use libc::{c_int, c_long, c_uint};
+use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
+use libc::{c_int, c_long, c_uint, c_void};
 use std::cmp::{self, Ordering};
+use std::convert::{TryFrom, TryInto};
 use std::error::Error;
 use std::ffi::{CStr, CString};
 use std::fmt;
 use std::marker::PhantomData;
 use std::mem;
+use std::net::IpAddr;
 use std::path::Path;
 use std::ptr;
 use std::slice;
 use std::str;
 
 use crate::asn1::{
-    Asn1BitStringRef, Asn1IntegerRef, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
+    Asn1BitStringRef, Asn1Enumerated, Asn1IntegerRef, Asn1Object, Asn1ObjectRef,
+    Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type,
 };
 use crate::bio::MemBioSlice;
 use crate::conf::ConfRef;
@@ -38,7 +41,7 @@
 use crate::{cvt, cvt_n, cvt_p, cvt_p_const};
 use openssl_macros::corresponds;
 
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 pub mod verify;
 
 pub mod extension;
@@ -47,6 +50,16 @@
 #[cfg(test)]
 mod tests;
 
+/// A type of X509 extension.
+///
+/// # Safety
+/// The value of NID and Output must match those in OpenSSL so that
+/// `Output::from_ptr_opt(*_get_ext_d2i(*, NID, ...))` is valid.
+pub unsafe trait ExtensionType {
+    const NID: Nid;
+    type Output: ForeignType;
+}
+
 foreign_type_and_impl_send_sync! {
     type CType = ffi::X509_STORE_CTX;
     fn drop = ffi::X509_STORE_CTX_free;
@@ -109,8 +122,8 @@
     /// This corresponds to [`X509_STORE_CTX_init`] before calling `with_context` and to
     /// [`X509_STORE_CTX_cleanup`] after calling `with_context`.
     ///
-    /// [`X509_STORE_CTX_init`]:  https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_init.html
-    /// [`X509_STORE_CTX_cleanup`]:  https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_cleanup.html
+    /// [`X509_STORE_CTX_init`]:  https://www.openssl.org/docs/manmaster/crypto/X509_STORE_CTX_init.html
+    /// [`X509_STORE_CTX_cleanup`]:  https://www.openssl.org/docs/manmaster/crypto/X509_STORE_CTX_cleanup.html
     pub fn init<F, T>(
         &mut self,
         trust: &store::X509StoreRef,
@@ -227,6 +240,7 @@
     /// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of
     /// the X.509 standard should pass `2` to this method.
     #[corresponds(X509_set_version)]
+    #[allow(clippy::useless_conversion)]
     pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
         unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) }
     }
@@ -382,11 +396,6 @@
     pub struct X509Ref;
 }
 
-#[cfg(boringssl)]
-type X509LenTy = c_uint;
-#[cfg(not(boringssl))]
-type X509LenTy = c_int;
-
 impl X509Ref {
     /// Returns this certificate's subject name.
     #[corresponds(X509_get_subject_name)]
@@ -400,7 +409,10 @@
     /// Returns the hash of the certificates subject
     #[corresponds(X509_subject_name_hash)]
     pub fn subject_name_hash(&self) -> u32 {
-        unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 }
+        #[allow(clippy::unnecessary_cast)]
+        unsafe {
+            ffi::X509_subject_name_hash(self.as_ptr()) as u32
+        }
     }
 
     /// Returns this certificate's issuer name.
@@ -415,7 +427,10 @@
     /// Returns the hash of the certificates issuer
     #[corresponds(X509_issuer_name_hash)]
     pub fn issuer_name_hash(&self) -> u32 {
-        unsafe { ffi::X509_issuer_name_hash(self.as_ptr()) as u32 }
+        #[allow(clippy::unnecessary_cast)]
+        unsafe {
+            ffi::X509_issuer_name_hash(self.as_ptr()) as u32
+        }
     }
 
     /// Returns this certificate's subject alternative name entries, if they exist.
@@ -432,6 +447,20 @@
         }
     }
 
+    /// Returns this certificate's CRL distribution points, if they exist.
+    #[corresponds(X509_get_ext_d2i)]
+    pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> {
+        unsafe {
+            let stack = ffi::X509_get_ext_d2i(
+                self.as_ptr(),
+                ffi::NID_crl_distribution_points,
+                ptr::null_mut(),
+                ptr::null_mut(),
+            );
+            Stack::from_ptr_opt(stack as *mut _)
+        }
+    }
+
     /// Returns this certificate's issuer alternative name entries, if they exist.
     #[corresponds(X509_get_ext_d2i)]
     pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> {
@@ -462,6 +491,54 @@
         }
     }
 
+    /// Retrieves the path length extension from a certificate, if it exists.
+    #[corresponds(X509_get_pathlen)]
+    #[cfg(any(ossl110, boringssl))]
+    pub fn pathlen(&self) -> Option<u32> {
+        let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) };
+        u32::try_from(v).ok()
+    }
+
+    /// Returns this certificate's subject key id, if it exists.
+    #[corresponds(X509_get0_subject_key_id)]
+    #[cfg(any(ossl110, boringssl))]
+    pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> {
+        unsafe {
+            let data = ffi::X509_get0_subject_key_id(self.as_ptr());
+            Asn1OctetStringRef::from_const_ptr_opt(data)
+        }
+    }
+
+    /// Returns this certificate's authority key id, if it exists.
+    #[corresponds(X509_get0_authority_key_id)]
+    #[cfg(any(ossl110, boringssl))]
+    pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> {
+        unsafe {
+            let data = ffi::X509_get0_authority_key_id(self.as_ptr());
+            Asn1OctetStringRef::from_const_ptr_opt(data)
+        }
+    }
+
+    /// Returns this certificate's authority issuer name entries, if they exist.
+    #[corresponds(X509_get0_authority_issuer)]
+    #[cfg(ossl111d)]
+    pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> {
+        unsafe {
+            let stack = ffi::X509_get0_authority_issuer(self.as_ptr());
+            StackRef::from_const_ptr_opt(stack)
+        }
+    }
+
+    /// Returns this certificate's authority serial number, if it exists.
+    #[corresponds(X509_get0_authority_serial)]
+    #[cfg(ossl111d)]
+    pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> {
+        unsafe {
+            let r = ffi::X509_get0_authority_serial(self.as_ptr());
+            Asn1IntegerRef::from_const_ptr_opt(r)
+        }
+    }
+
     #[corresponds(X509_get_pubkey)]
     pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
         unsafe {
@@ -557,6 +634,7 @@
     /// Note that `0` return value stands for version 1, `1` for version 2 and so on.
     #[corresponds(X509_get_version)]
     #[cfg(ossl110)]
+    #[allow(clippy::unnecessary_cast)]
     pub fn version(&self) -> i32 {
         unsafe { ffi::X509_get_version(self.as_ptr()) as i32 }
     }
@@ -584,6 +662,24 @@
         }
     }
 
+    /// Returns this certificate's "alias". This field is populated by
+    /// OpenSSL in some situations -- specifically OpenSSL will store a
+    /// PKCS#12 `friendlyName` in this field. This is not a part of the X.509
+    /// certificate itself, OpenSSL merely attaches it to this structure in
+    /// memory.
+    #[corresponds(X509_alias_get0)]
+    pub fn alias(&self) -> Option<&[u8]> {
+        unsafe {
+            let mut len = 0;
+            let ptr = ffi::X509_alias_get0(self.as_ptr(), &mut len);
+            if ptr.is_null() {
+                None
+            } else {
+                Some(slice::from_raw_parts(ptr, len as usize))
+            }
+        }
+    }
+
     to_pem! {
         /// Serializes the certificate into a PEM-encoded X509 structure.
         ///
@@ -690,15 +786,17 @@
                 let r =
                     ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut());
                 if r.is_null() {
-                    let err = ffi::ERR_peek_last_error();
-                    if ffi::ERR_GET_LIB(err) as X509LenTy == ffi::ERR_LIB_PEM
-                        && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE
-                    {
-                        ffi::ERR_clear_error();
-                        break;
+                    let e = ErrorStack::get();
+
+                    if let Some(err) = e.errors().last() {
+                        if err.library_code() == ffi::ERR_LIB_PEM as libc::c_int
+                            && err.reason_code() == ffi::PEM_R_NO_START_LINE as libc::c_int
+                        {
+                            break;
+                        }
                     }
 
-                    return Err(ErrorStack::get());
+                    return Err(e);
                 } else {
                     certs.push(X509(r));
                 }
@@ -762,7 +860,7 @@
 
 impl PartialOrd for X509 {
     fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
-        X509Ref::partial_cmp(self, other)
+        Some(self.cmp(other))
     }
 }
 
@@ -816,7 +914,17 @@
     /// Some extension types, such as `subjectAlternativeName`, require an `X509v3Context` to be
     /// provided.
     ///
+    /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL
+    /// mini-language that can read arbitrary files.
+    ///
     /// See the extension module for builder types which will construct certain common extensions.
+    ///
+    /// This function is deprecated, `X509Extension::new_from_der` or the
+    /// types in `x509::extension` should be used in its place.
+    #[deprecated(
+        note = "Use x509::extension types or new_from_der instead",
+        since = "0.10.51"
+    )]
     pub fn new(
         conf: Option<&ConfRef>,
         context: Option<&X509v3Context<'_>>,
@@ -825,14 +933,30 @@
     ) -> Result<X509Extension, ErrorStack> {
         let name = CString::new(name).unwrap();
         let value = CString::new(value).unwrap();
+        let mut ctx;
         unsafe {
             ffi::init();
             let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
-            let context = context.map_or(ptr::null_mut(), X509v3Context::as_ptr);
+            let context_ptr = match context {
+                Some(c) => c.as_ptr(),
+                None => {
+                    ctx = mem::zeroed();
+
+                    ffi::X509V3_set_ctx(
+                        &mut ctx,
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        0,
+                    );
+                    &mut ctx
+                }
+            };
             let name = name.as_ptr() as *mut _;
             let value = value.as_ptr() as *mut _;
 
-            cvt_p(ffi::X509V3_EXT_nconf(conf, context, name, value)).map(X509Extension)
+            cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension)
         }
     }
 
@@ -842,7 +966,17 @@
     /// Some extension types, such as `nid::SUBJECT_ALTERNATIVE_NAME`, require an `X509v3Context` to
     /// be provided.
     ///
+    /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL
+    /// mini-language that can read arbitrary files.
+    ///
     /// See the extension module for builder types which will construct certain common extensions.
+    ///
+    /// This function is deprecated, `X509Extension::new_from_der` or the
+    /// types in `x509::extension` should be used in its place.
+    #[deprecated(
+        note = "Use x509::extension types or new_from_der instead",
+        since = "0.10.51"
+    )]
     pub fn new_nid(
         conf: Option<&ConfRef>,
         context: Option<&X509v3Context<'_>>,
@@ -850,29 +984,93 @@
         value: &str,
     ) -> Result<X509Extension, ErrorStack> {
         let value = CString::new(value).unwrap();
+        let mut ctx;
         unsafe {
             ffi::init();
             let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr);
-            let context = context.map_or(ptr::null_mut(), X509v3Context::as_ptr);
+            let context_ptr = match context {
+                Some(c) => c.as_ptr(),
+                None => {
+                    ctx = mem::zeroed();
+
+                    ffi::X509V3_set_ctx(
+                        &mut ctx,
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        0,
+                    );
+                    &mut ctx
+                }
+            };
             let name = name.as_raw();
             let value = value.as_ptr() as *mut _;
 
-            cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context, name, value)).map(X509Extension)
+            cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension)
         }
     }
 
+    /// Constructs a new X509 extension value from its OID, whether it's
+    /// critical, and its DER contents.
+    ///
+    /// The extent structure of the DER value will vary based on the
+    /// extension type, and can generally be found in the RFC defining the
+    /// extension.
+    ///
+    /// For common extension types, there are Rust APIs provided in
+    /// `openssl::x509::extensions` which are more ergonomic.
+    pub fn new_from_der(
+        oid: &Asn1ObjectRef,
+        critical: bool,
+        der_contents: &Asn1OctetStringRef,
+    ) -> Result<X509Extension, ErrorStack> {
+        unsafe {
+            cvt_p(ffi::X509_EXTENSION_create_by_OBJ(
+                ptr::null_mut(),
+                oid.as_ptr(),
+                critical as _,
+                der_contents.as_ptr(),
+            ))
+            .map(X509Extension)
+        }
+    }
+
+    pub(crate) unsafe fn new_internal(
+        nid: Nid,
+        critical: bool,
+        value: *mut c_void,
+    ) -> Result<X509Extension, ErrorStack> {
+        ffi::init();
+        cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension)
+    }
+
     /// Adds an alias for an extension
     ///
     /// # Safety
     ///
     /// This method modifies global state without locking and therefore is not thread safe
+    #[cfg(not(libressl390))]
     #[corresponds(X509V3_EXT_add_alias)]
+    #[deprecated(
+        note = "Use x509::extension types or new_from_der and then this is not necessary",
+        since = "0.10.51"
+    )]
     pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> {
         ffi::init();
         cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ())
     }
 }
 
+impl X509ExtensionRef {
+    to_der! {
+        /// Serializes the Extension to its standard DER encoding.
+        #[corresponds(i2d_X509_EXTENSION)]
+        to_der,
+        ffi::i2d_X509_EXTENSION
+    }
+}
+
 /// A builder used to construct an `X509Name`.
 pub struct X509NameBuilder(X509Name);
 
@@ -885,21 +1083,36 @@
         }
     }
 
+    /// Add a name entry
+    #[corresponds(X509_NAME_add_entry)]
+    #[cfg(any(ossl101, libressl350))]
+    pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> {
+        unsafe {
+            cvt(ffi::X509_NAME_add_entry(
+                self.0.as_ptr(),
+                ne.as_ptr(),
+                -1,
+                0,
+            ))
+            .map(|_| ())
+        }
+    }
+
     /// Add a field entry by str.
     ///
     /// This corresponds to [`X509_NAME_add_entry_by_txt`].
     ///
-    /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_txt.html
+    /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_txt.html
     pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> {
         unsafe {
             let field = CString::new(field).unwrap();
-            assert!(value.len() <= isize::max_value() as usize);
+            assert!(value.len() <= crate::SLenType::max_value() as usize);
             cvt(ffi::X509_NAME_add_entry_by_txt(
                 self.0.as_ptr(),
                 field.as_ptr() as *mut _,
                 ffi::MBSTRING_UTF8,
                 value.as_ptr(),
-                value.len() as isize,
+                value.len() as crate::SLenType,
                 -1,
                 0,
             ))
@@ -911,7 +1124,7 @@
     ///
     /// This corresponds to [`X509_NAME_add_entry_by_txt`].
     ///
-    /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_txt.html
+    /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_txt.html
     pub fn append_entry_by_text_with_type(
         &mut self,
         field: &str,
@@ -920,13 +1133,13 @@
     ) -> Result<(), ErrorStack> {
         unsafe {
             let field = CString::new(field).unwrap();
-            assert!(value.len() <= isize::max_value() as usize);
+            assert!(value.len() <= crate::SLenType::max_value() as usize);
             cvt(ffi::X509_NAME_add_entry_by_txt(
                 self.0.as_ptr(),
                 field.as_ptr() as *mut _,
                 ty.as_raw(),
                 value.as_ptr(),
-                value.len() as isize,
+                value.len() as crate::SLenType,
                 -1,
                 0,
             ))
@@ -938,16 +1151,16 @@
     ///
     /// This corresponds to [`X509_NAME_add_entry_by_NID`].
     ///
-    /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_NID.html
+    /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_NID.html
     pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> {
         unsafe {
-            assert!(value.len() <= isize::max_value() as usize);
+            assert!(value.len() <= crate::SLenType::max_value() as usize);
             cvt(ffi::X509_NAME_add_entry_by_NID(
                 self.0.as_ptr(),
                 field.as_raw(),
                 ffi::MBSTRING_UTF8,
                 value.as_ptr() as *mut _,
-                value.len() as isize,
+                value.len() as crate::SLenType,
                 -1,
                 0,
             ))
@@ -959,7 +1172,7 @@
     ///
     /// This corresponds to [`X509_NAME_add_entry_by_NID`].
     ///
-    /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_NID.html
+    /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_NID.html
     pub fn append_entry_by_nid_with_type(
         &mut self,
         field: Nid,
@@ -967,13 +1180,13 @@
         ty: Asn1Type,
     ) -> Result<(), ErrorStack> {
         unsafe {
-            assert!(value.len() <= isize::max_value() as usize);
+            assert!(value.len() <= crate::SLenType::max_value() as usize);
             cvt(ffi::X509_NAME_add_entry_by_NID(
                 self.0.as_ptr(),
                 field.as_raw(),
                 ty.as_raw(),
                 value.as_ptr() as *mut _,
-                value.len() as isize,
+                value.len() as crate::SLenType,
                 -1,
                 0,
             ))
@@ -983,7 +1196,10 @@
 
     /// Return an `X509Name`.
     pub fn build(self) -> X509Name {
-        self.0
+        // Round-trip through bytes because OpenSSL is not const correct and
+        // names in a "modified" state compute various things lazily. This can
+        // lead to data-races because OpenSSL doesn't have locks or anything.
+        X509Name::from_der(&self.0.to_der().unwrap()).unwrap()
     }
 }
 
@@ -1061,12 +1277,19 @@
         Ok(cmp.cmp(&0))
     }
 
+    /// Copies the name to a new `X509Name`.
+    #[corresponds(X509_NAME_dup)]
+    #[cfg(any(boringssl, ossl110, libressl270))]
+    pub fn to_owned(&self) -> Result<X509Name, ErrorStack> {
+        unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) }
+    }
+
     to_der! {
         /// Serializes the certificate into a DER-encoded X509 name structure.
         ///
         /// This corresponds to [`i2d_X509_NAME`].
         ///
-        /// [`i2d_X509_NAME`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_X509_NAME.html
+        /// [`i2d_X509_NAME`]: https://www.openssl.org/docs/manmaster/crypto/i2d_X509_NAME.html
         to_der,
         ffi::i2d_X509_NAME
     }
@@ -1130,7 +1353,7 @@
     ///
     /// This corresponds to [`X509_NAME_ENTRY_get_data`].
     ///
-    /// [`X509_NAME_ENTRY_get_data`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_ENTRY_get_data.html
+    /// [`X509_NAME_ENTRY_get_data`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_ENTRY_get_data.html
     pub fn data(&self) -> &Asn1StringRef {
         unsafe {
             let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr());
@@ -1143,7 +1366,7 @@
     ///
     /// This corresponds to [`X509_NAME_ENTRY_get_object`].
     ///
-    /// [`X509_NAME_ENTRY_get_object`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_ENTRY_get_object.html
+    /// [`X509_NAME_ENTRY_get_object`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_ENTRY_get_object.html
     pub fn object(&self) -> &Asn1ObjectRef {
         unsafe {
             let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr());
@@ -1166,7 +1389,7 @@
     ///
     /// This corresponds to [`X509_REQ_new`].
     ///
-    ///[`X509_REQ_new`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_new.html
+    ///[`X509_REQ_new`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_new.html
     pub fn new() -> Result<X509ReqBuilder, ErrorStack> {
         unsafe {
             ffi::init();
@@ -1178,7 +1401,8 @@
     ///
     /// This corresponds to [`X509_REQ_set_version`].
     ///
-    ///[`X509_REQ_set_version`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_set_version.html
+    ///[`X509_REQ_set_version`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_version.html
+    #[allow(clippy::useless_conversion)]
     pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::X509_REQ_set_version(
@@ -1193,7 +1417,7 @@
     ///
     /// This corresponds to [`X509_REQ_set_subject_name`].
     ///
-    /// [`X509_REQ_set_subject_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_set_subject_name.html
+    /// [`X509_REQ_set_subject_name`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_subject_name.html
     pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> {
         unsafe {
             cvt(ffi::X509_REQ_set_subject_name(
@@ -1208,7 +1432,7 @@
     ///
     /// This corresponds to [`X509_REQ_set_pubkey`].
     ///
-    /// [`X509_REQ_set_pubkey`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_set_pubkey.html
+    /// [`X509_REQ_set_pubkey`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_pubkey.html
     pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack>
     where
         T: HasPublic,
@@ -1258,7 +1482,7 @@
     ///
     /// This corresponds to [`X509_REQ_sign`].
     ///
-    /// [`X509_REQ_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_sign.html
+    /// [`X509_REQ_sign`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_sign.html
     pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack>
     where
         T: HasPrivate,
@@ -1325,7 +1549,7 @@
         ///
         /// This corresponds to [`PEM_read_bio_X509_REQ`].
         ///
-        /// [`PEM_read_bio_X509_REQ`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_X509_REQ.html
+        /// [`PEM_read_bio_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/PEM_read_bio_X509_REQ.html
         from_pem,
         X509Req,
         ffi::PEM_read_bio_X509_REQ
@@ -1336,7 +1560,7 @@
         ///
         /// This corresponds to [`d2i_X509_REQ`].
         ///
-        /// [`d2i_X509_REQ`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_X509_REQ.html
+        /// [`d2i_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/d2i_X509_REQ.html
         from_der,
         X509Req,
         ffi::d2i_X509_REQ
@@ -1351,7 +1575,7 @@
         ///
         /// This corresponds to [`PEM_write_bio_X509_REQ`].
         ///
-        /// [`PEM_write_bio_X509_REQ`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_X509_REQ.html
+        /// [`PEM_write_bio_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/PEM_write_bio_X509_REQ.html
         to_pem,
         ffi::PEM_write_bio_X509_REQ
     }
@@ -1361,7 +1585,7 @@
         ///
         /// This corresponds to [`i2d_X509_REQ`].
         ///
-        /// [`i2d_X509_REQ`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_X509_REQ.html
+        /// [`i2d_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/i2d_X509_REQ.html
         to_der,
         ffi::i2d_X509_REQ
     }
@@ -1377,7 +1601,8 @@
     ///
     /// This corresponds to [`X509_REQ_get_version`]
     ///
-    /// [`X509_REQ_get_version`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_get_version.html
+    /// [`X509_REQ_get_version`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_version.html
+    #[allow(clippy::unnecessary_cast)]
     pub fn version(&self) -> i32 {
         unsafe { X509_REQ_get_version(self.as_ptr()) as i32 }
     }
@@ -1386,7 +1611,7 @@
     ///
     /// This corresponds to [`X509_REQ_get_subject_name`]
     ///
-    /// [`X509_REQ_get_subject_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_get_subject_name.html
+    /// [`X509_REQ_get_subject_name`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_subject_name.html
     pub fn subject_name(&self) -> &X509NameRef {
         unsafe {
             let name = X509_REQ_get_subject_name(self.as_ptr());
@@ -1398,7 +1623,7 @@
     ///
     /// This corresponds to [`X509_REQ_get_pubkey"]
     ///
-    /// [`X509_REQ_get_pubkey`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_get_pubkey.html
+    /// [`X509_REQ_get_pubkey`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_pubkey.html
     pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> {
         unsafe {
             let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?;
@@ -1412,7 +1637,7 @@
     ///
     /// This corresponds to [`X509_REQ_verify"].
     ///
-    /// [`X509_REQ_verify`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_verify.html
+    /// [`X509_REQ_verify`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_verify.html
     pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
     where
         T: HasPublic,
@@ -1431,6 +1656,360 @@
     }
 }
 
+/// The reason that a certificate was revoked.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct CrlReason(c_int);
+
+#[allow(missing_docs)] // no need to document the constants
+impl CrlReason {
+    pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED);
+    pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE);
+    pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE);
+    pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED);
+    pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED);
+    pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION);
+    pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD);
+    pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL);
+    pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN);
+    pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE);
+
+    /// Constructs an `CrlReason` from a raw OpenSSL value.
+    pub const fn from_raw(value: c_int) -> Self {
+        CrlReason(value)
+    }
+
+    /// Returns the raw OpenSSL value represented by this type.
+    pub const fn as_raw(&self) -> c_int {
+        self.0
+    }
+}
+
+foreign_type_and_impl_send_sync! {
+    type CType = ffi::X509_REVOKED;
+    fn drop = ffi::X509_REVOKED_free;
+
+    /// An `X509` certificate revocation status.
+    pub struct X509Revoked;
+    /// Reference to `X509Revoked`.
+    pub struct X509RevokedRef;
+}
+
+impl Stackable for X509Revoked {
+    type StackType = ffi::stack_st_X509_REVOKED;
+}
+
+impl X509Revoked {
+    from_der! {
+        /// Deserializes a DER-encoded certificate revocation status
+        #[corresponds(d2i_X509_REVOKED)]
+        from_der,
+        X509Revoked,
+        ffi::d2i_X509_REVOKED
+    }
+}
+
+impl X509RevokedRef {
+    to_der! {
+        /// Serializes the certificate request to a DER-encoded certificate revocation status
+        #[corresponds(d2i_X509_REVOKED)]
+        to_der,
+        ffi::i2d_X509_REVOKED
+    }
+
+    /// Copies the entry to a new `X509Revoked`.
+    #[corresponds(X509_NAME_dup)]
+    #[cfg(any(boringssl, ossl110, libressl270))]
+    pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> {
+        unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) }
+    }
+
+    /// Get the date that the certificate was revoked
+    #[corresponds(X509_REVOKED_get0_revocationDate)]
+    pub fn revocation_date(&self) -> &Asn1TimeRef {
+        unsafe {
+            let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _);
+            assert!(!r.is_null());
+            Asn1TimeRef::from_ptr(r as *mut _)
+        }
+    }
+
+    /// Get the serial number of the revoked certificate
+    #[corresponds(X509_REVOKED_get0_serialNumber)]
+    pub fn serial_number(&self) -> &Asn1IntegerRef {
+        unsafe {
+            let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _);
+            assert!(!r.is_null());
+            Asn1IntegerRef::from_ptr(r as *mut _)
+        }
+    }
+
+    /// Get the criticality and value of an extension.
+    ///
+    /// This returns None if the extension is not present or occurs multiple times.
+    #[corresponds(X509_REVOKED_get_ext_d2i)]
+    pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
+        let mut critical = -1;
+        let out = unsafe {
+            // SAFETY: self.as_ptr() is a valid pointer to an X509_REVOKED.
+            let ext = ffi::X509_REVOKED_get_ext_d2i(
+                self.as_ptr(),
+                T::NID.as_raw(),
+                &mut critical as *mut _,
+                ptr::null_mut(),
+            );
+            // SAFETY: Extensions's contract promises that the type returned by
+            // OpenSSL here is T::Output.
+            T::Output::from_ptr_opt(ext as *mut _)
+        };
+        match (critical, out) {
+            (0, Some(out)) => Ok(Some((false, out))),
+            (1, Some(out)) => Ok(Some((true, out))),
+            // -1 means the extension wasn't found, -2 means multiple were found.
+            (-1 | -2, _) => Ok(None),
+            // A critical value of 0 or 1 suggests success, but a null pointer
+            // was returned so something went wrong.
+            (0 | 1, None) => Err(ErrorStack::get()),
+            (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
+        }
+    }
+}
+
+/// The CRL entry extension identifying the reason for revocation see [`CrlReason`],
+/// this is as defined in RFC 5280 Section 5.3.1.
+pub enum ReasonCode {}
+
+// SAFETY: CertificateIssuer is defined to be a stack of GeneralName in the RFC
+// and in OpenSSL.
+unsafe impl ExtensionType for ReasonCode {
+    const NID: Nid = Nid::from_raw(ffi::NID_crl_reason);
+
+    type Output = Asn1Enumerated;
+}
+
+/// The CRL entry extension identifying the issuer of a certificate used in
+/// indirect CRLs, as defined in RFC 5280 Section 5.3.3.
+pub enum CertificateIssuer {}
+
+// SAFETY: CertificateIssuer is defined to be a stack of GeneralName in the RFC
+// and in OpenSSL.
+unsafe impl ExtensionType for CertificateIssuer {
+    const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer);
+
+    type Output = Stack<GeneralName>;
+}
+
+/// The CRL extension identifying how to access information and services for the issuer of the CRL
+pub enum AuthorityInformationAccess {}
+
+// SAFETY: AuthorityInformationAccess is defined to be a stack of AccessDescription in the RFC
+// and in OpenSSL.
+unsafe impl ExtensionType for AuthorityInformationAccess {
+    const NID: Nid = Nid::from_raw(ffi::NID_info_access);
+
+    type Output = Stack<AccessDescription>;
+}
+
+foreign_type_and_impl_send_sync! {
+    type CType = ffi::X509_CRL;
+    fn drop = ffi::X509_CRL_free;
+
+    /// An `X509` certificate revocation list.
+    pub struct X509Crl;
+    /// Reference to `X509Crl`.
+    pub struct X509CrlRef;
+}
+
+/// The status of a certificate in a revoction list
+///
+/// Corresponds to the return value from the [`X509_CRL_get0_by_*`] methods.
+///
+/// [`X509_CRL_get0_by_*`]: https://www.openssl.org/docs/man1.1.0/man3/X509_CRL_get0_by_serial.html
+pub enum CrlStatus<'a> {
+    /// The certificate is not present in the list
+    NotRevoked,
+    /// The certificate is in the list and is revoked
+    Revoked(&'a X509RevokedRef),
+    /// The certificate is in the list, but has the "removeFromCrl" status.
+    ///
+    /// This can occur if the certificate was revoked with the "CertificateHold"
+    /// reason, and has since been unrevoked.
+    RemoveFromCrl(&'a X509RevokedRef),
+}
+
+impl<'a> CrlStatus<'a> {
+    // Helper used by the X509_CRL_get0_by_* methods to convert their return
+    // value to the status enum.
+    // Safety note: the returned CrlStatus must not outlive the owner of the
+    // revoked_entry pointer.
+    unsafe fn from_ffi_status(
+        status: c_int,
+        revoked_entry: *mut ffi::X509_REVOKED,
+    ) -> CrlStatus<'a> {
+        match status {
+            0 => CrlStatus::NotRevoked,
+            1 => {
+                assert!(!revoked_entry.is_null());
+                CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry))
+            }
+            2 => {
+                assert!(!revoked_entry.is_null());
+                CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry))
+            }
+            _ => unreachable!(
+                "{}",
+                "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2."
+            ),
+        }
+    }
+}
+
+impl X509Crl {
+    from_pem! {
+        /// Deserializes a PEM-encoded Certificate Revocation List
+        ///
+        /// The input should have a header of `-----BEGIN X509 CRL-----`.
+        #[corresponds(PEM_read_bio_X509_CRL)]
+        from_pem,
+        X509Crl,
+        ffi::PEM_read_bio_X509_CRL
+    }
+
+    from_der! {
+        /// Deserializes a DER-encoded Certificate Revocation List
+        #[corresponds(d2i_X509_CRL)]
+        from_der,
+        X509Crl,
+        ffi::d2i_X509_CRL
+    }
+}
+
+impl X509CrlRef {
+    to_pem! {
+        /// Serializes the certificate request to a PEM-encoded Certificate Revocation List.
+        ///
+        /// The output will have a header of `-----BEGIN X509 CRL-----`.
+        #[corresponds(PEM_write_bio_X509_CRL)]
+        to_pem,
+        ffi::PEM_write_bio_X509_CRL
+    }
+
+    to_der! {
+        /// Serializes the certificate request to a DER-encoded Certificate Revocation List.
+        #[corresponds(i2d_X509_CRL)]
+        to_der,
+        ffi::i2d_X509_CRL
+    }
+
+    /// Get the stack of revocation entries
+    pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> {
+        unsafe {
+            let revoked = X509_CRL_get_REVOKED(self.as_ptr());
+            if revoked.is_null() {
+                None
+            } else {
+                Some(StackRef::from_ptr(revoked))
+            }
+        }
+    }
+
+    /// Returns the CRL's `lastUpdate` time.
+    #[corresponds(X509_CRL_get0_lastUpdate)]
+    pub fn last_update(&self) -> &Asn1TimeRef {
+        unsafe {
+            let date = X509_CRL_get0_lastUpdate(self.as_ptr());
+            assert!(!date.is_null());
+            Asn1TimeRef::from_ptr(date as *mut _)
+        }
+    }
+
+    /// Returns the CRL's `nextUpdate` time.
+    ///
+    /// If the `nextUpdate` field is missing, returns `None`.
+    #[corresponds(X509_CRL_get0_nextUpdate)]
+    pub fn next_update(&self) -> Option<&Asn1TimeRef> {
+        unsafe {
+            let date = X509_CRL_get0_nextUpdate(self.as_ptr());
+            Asn1TimeRef::from_const_ptr_opt(date)
+        }
+    }
+
+    /// Get the revocation status of a certificate by its serial number
+    #[corresponds(X509_CRL_get0_by_serial)]
+    pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> {
+        unsafe {
+            let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
+            let status =
+                ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr());
+            CrlStatus::from_ffi_status(status, ret)
+        }
+    }
+
+    /// Get the revocation status of a certificate
+    #[corresponds(X509_CRL_get0_by_cert)]
+    pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> {
+        unsafe {
+            let mut ret = ptr::null_mut::<ffi::X509_REVOKED>();
+            let status =
+                ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr());
+            CrlStatus::from_ffi_status(status, ret)
+        }
+    }
+
+    /// Get the issuer name from the revocation list.
+    #[corresponds(X509_CRL_get_issuer)]
+    pub fn issuer_name(&self) -> &X509NameRef {
+        unsafe {
+            let name = X509_CRL_get_issuer(self.as_ptr());
+            assert!(!name.is_null());
+            X509NameRef::from_ptr(name)
+        }
+    }
+
+    /// Check if the CRL is signed using the given public key.
+    ///
+    /// Only the signature is checked: no other checks (such as certificate chain validity)
+    /// are performed.
+    ///
+    /// Returns `true` if verification succeeds.
+    #[corresponds(X509_CRL_verify)]
+    pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack>
+    where
+        T: HasPublic,
+    {
+        unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) }
+    }
+
+    /// Get the criticality and value of an extension.
+    ///
+    /// This returns None if the extension is not present or occurs multiple times.
+    #[corresponds(X509_CRL_get_ext_d2i)]
+    pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> {
+        let mut critical = -1;
+        let out = unsafe {
+            // SAFETY: self.as_ptr() is a valid pointer to an X509_CRL.
+            let ext = ffi::X509_CRL_get_ext_d2i(
+                self.as_ptr(),
+                T::NID.as_raw(),
+                &mut critical as *mut _,
+                ptr::null_mut(),
+            );
+            // SAFETY: Extensions's contract promises that the type returned by
+            // OpenSSL here is T::Output.
+            T::Output::from_ptr_opt(ext as *mut _)
+        };
+        match (critical, out) {
+            (0, Some(out)) => Ok(Some((false, out))),
+            (1, Some(out)) => Ok(Some((true, out))),
+            // -1 means the extension wasn't found, -2 means multiple were found.
+            (-1 | -2, _) => Ok(None),
+            // A critical value of 0 or 1 suggests success, but a null pointer
+            // was returned so something went wrong.
+            (0 | 1, None) => Err(ErrorStack::get()),
+            (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical),
+        }
+    }
+}
+
 /// The result of peer certificate verification.
 #[derive(Copy, Clone, PartialEq, Eq)]
 pub struct X509VerifyResult(c_int);
@@ -1473,7 +2052,7 @@
     ///
     /// This corresponds to [`X509_verify_cert_error_string`].
     ///
-    /// [`X509_verify_cert_error_string`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_verify_cert_error_string.html
+    /// [`X509_verify_cert_error_string`]: https://www.openssl.org/docs/manmaster/crypto/X509_verify_cert_error_string.html
     #[allow(clippy::trivially_copy_pass_by_ref)]
     pub fn error_string(&self) -> &'static str {
         ffi::init();
@@ -1501,6 +2080,103 @@
     pub struct GeneralNameRef;
 }
 
+impl GeneralName {
+    unsafe fn new(
+        type_: c_int,
+        asn1_type: Asn1Type,
+        value: &[u8],
+    ) -> Result<GeneralName, ErrorStack> {
+        ffi::init();
+        let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?);
+        (*gn.as_ptr()).type_ = type_;
+        let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?;
+        ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap());
+
+        #[cfg(boringssl)]
+        {
+            (*gn.as_ptr()).d.ptr = s.cast();
+        }
+        #[cfg(not(boringssl))]
+        {
+            (*gn.as_ptr()).d = s.cast();
+        }
+
+        Ok(gn)
+    }
+
+    pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> {
+        unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) }
+    }
+
+    pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> {
+        unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) }
+    }
+
+    pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> {
+        unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) }
+    }
+
+    pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> {
+        match ip {
+            IpAddr::V4(addr) => unsafe {
+                GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
+            },
+            IpAddr::V6(addr) => unsafe {
+                GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets())
+            },
+        }
+    }
+
+    pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> {
+        unsafe {
+            ffi::init();
+            let gn = cvt_p(ffi::GENERAL_NAME_new())?;
+            (*gn).type_ = ffi::GEN_RID;
+
+            #[cfg(boringssl)]
+            {
+                (*gn).d.registeredID = oid.as_ptr();
+            }
+            #[cfg(not(boringssl))]
+            {
+                (*gn).d = oid.as_ptr().cast();
+            }
+
+            mem::forget(oid);
+
+            Ok(GeneralName::from_ptr(gn))
+        }
+    }
+
+    pub(crate) fn new_other_name(oid: Asn1Object, value: &[u8]) -> Result<GeneralName, ErrorStack> {
+        unsafe {
+            ffi::init();
+
+            let typ = cvt_p(ffi::d2i_ASN1_TYPE(
+                ptr::null_mut(),
+                &mut value.as_ptr().cast(),
+                value.len().try_into().unwrap(),
+            ))?;
+
+            let gn = cvt_p(ffi::GENERAL_NAME_new())?;
+            (*gn).type_ = ffi::GEN_OTHERNAME;
+
+            if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername(
+                gn,
+                oid.as_ptr().cast(),
+                typ,
+            )) {
+                ffi::GENERAL_NAME_free(gn);
+                return Err(e);
+            }
+
+            mem::forget(oid);
+
+            Ok(GeneralName::from_ptr(gn))
+        }
+    }
+}
+
 impl GeneralNameRef {
     fn ia5_string(&self, ffi_type: c_int) -> Option<&str> {
         unsafe {
@@ -1516,6 +2192,7 @@
             let ptr = ASN1_STRING_get0_data(d as *mut _);
             let len = ffi::ASN1_STRING_length(d as *mut _);
 
+            #[allow(clippy::unnecessary_cast)]
             let slice = slice::from_raw_parts(ptr as *const u8, len as usize);
             // IA5Strings are stated to be ASCII (specifically IA5). Hopefully
             // OpenSSL checks that when loading a certificate but if not we'll
@@ -1529,6 +2206,22 @@
         self.ia5_string(ffi::GEN_EMAIL)
     }
 
+    /// Returns the contents of this `GeneralName` if it is a `directoryName`.
+    pub fn directory_name(&self) -> Option<&X509NameRef> {
+        unsafe {
+            if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME {
+                return None;
+            }
+
+            #[cfg(boringssl)]
+            let d = (*self.as_ptr()).d.ptr;
+            #[cfg(not(boringssl))]
+            let d = (*self.as_ptr()).d;
+
+            Some(X509NameRef::from_const_ptr(d as *const _))
+        }
+    }
+
     /// Returns the contents of this `GeneralName` if it is a `dNSName`.
     pub fn dnsname(&self) -> Option<&str> {
         self.ia5_string(ffi::GEN_DNS)
@@ -1553,6 +2246,7 @@
             let ptr = ASN1_STRING_get0_data(d as *mut _);
             let len = ffi::ASN1_STRING_length(d as *mut _);
 
+            #[allow(clippy::unnecessary_cast)]
             Some(slice::from_raw_parts(ptr as *const u8, len as usize))
         }
     }
@@ -1567,8 +2261,13 @@
         } else if let Some(uri) = self.uri() {
             formatter.write_str(uri)
         } else if let Some(ipaddress) = self.ipaddress() {
-            let result = String::from_utf8_lossy(ipaddress);
-            formatter.write_str(&result)
+            let address = <[u8; 16]>::try_from(ipaddress)
+                .map(IpAddr::from)
+                .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from));
+            match address {
+                Ok(a) => fmt::Debug::fmt(&a, formatter),
+                Err(_) => fmt::Debug::fmt(ipaddress, formatter),
+            }
         } else {
             formatter.write_str("(empty)")
         }
@@ -1580,6 +2279,49 @@
 }
 
 foreign_type_and_impl_send_sync! {
+    type CType = ffi::DIST_POINT;
+    fn drop = ffi::DIST_POINT_free;
+
+    /// A `X509` distribution point.
+    pub struct DistPoint;
+    /// Reference to `DistPoint`.
+    pub struct DistPointRef;
+}
+
+impl DistPointRef {
+    /// Returns the name of this distribution point if it exists
+    pub fn distpoint(&self) -> Option<&DistPointNameRef> {
+        unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) }
+    }
+}
+
+foreign_type_and_impl_send_sync! {
+    type CType = ffi::DIST_POINT_NAME;
+    fn drop = ffi::DIST_POINT_NAME_free;
+
+    /// A `X509` distribution point.
+    pub struct DistPointName;
+    /// Reference to `DistPointName`.
+    pub struct DistPointNameRef;
+}
+
+impl DistPointNameRef {
+    /// Returns the contents of this DistPointName if it is a fullname.
+    pub fn fullname(&self) -> Option<&StackRef<GeneralName>> {
+        unsafe {
+            if (*self.as_ptr()).type_ != 0 {
+                return None;
+            }
+            StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname)
+        }
+    }
+}
+
+impl Stackable for DistPoint {
+    type StackType = ffi::stack_st_DIST_POINT;
+}
+
+foreign_type_and_impl_send_sync! {
     type CType = ffi::ACCESS_DESCRIPTION;
     fn drop = ffi::ACCESS_DESCRIPTION_free;
 
@@ -1746,10 +2488,8 @@
 }
 
 cfg_if! {
-    if #[cfg(any(ossl110, libressl350))] {
+    if #[cfg(any(ossl110, libressl350, boringssl))] {
         use ffi::X509_OBJECT_free;
-    } else if #[cfg(boringssl)] {
-        use ffi::X509_OBJECT_free_contents as X509_OBJECT_free;
     } else {
         #[allow(bad_style)]
         unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) {
@@ -1758,3 +2498,134 @@
         }
     }
 }
+
+cfg_if! {
+    if #[cfg(any(ossl110, libressl350, boringssl))] {
+        use ffi::{
+            X509_CRL_get_issuer, X509_CRL_get0_nextUpdate, X509_CRL_get0_lastUpdate,
+            X509_CRL_get_REVOKED,
+            X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber,
+        };
+    } else {
+        #[allow(bad_style)]
+        unsafe fn X509_CRL_get0_lastUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME {
+            (*(*x).crl).lastUpdate
+        }
+        #[allow(bad_style)]
+        unsafe fn X509_CRL_get0_nextUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME {
+            (*(*x).crl).nextUpdate
+        }
+        #[allow(bad_style)]
+        unsafe fn X509_CRL_get_issuer(x: *const ffi::X509_CRL) -> *mut ffi::X509_NAME {
+            (*(*x).crl).issuer
+        }
+        #[allow(bad_style)]
+        unsafe fn X509_CRL_get_REVOKED(x: *const ffi::X509_CRL) -> *mut ffi::stack_st_X509_REVOKED {
+            (*(*x).crl).revoked
+        }
+        #[allow(bad_style)]
+        unsafe fn X509_REVOKED_get0_serialNumber(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_INTEGER {
+            (*x).serialNumber
+        }
+        #[allow(bad_style)]
+        unsafe fn X509_REVOKED_get0_revocationDate(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_TIME {
+            (*x).revocationDate
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct X509PurposeId(c_int);
+
+impl X509PurposeId {
+    pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT);
+    pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER);
+    pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER);
+    pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN);
+    pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT);
+    pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN);
+    pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY);
+    pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER);
+    pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN);
+    #[cfg(ossl320)]
+    pub const CODE_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CODE_SIGN);
+
+    /// Constructs an `X509PurposeId` from a raw OpenSSL value.
+    pub fn from_raw(id: c_int) -> Self {
+        X509PurposeId(id)
+    }
+
+    /// Returns the raw OpenSSL value represented by this type.
+    pub fn as_raw(&self) -> c_int {
+        self.0
+    }
+}
+
+/// A reference to an [`X509_PURPOSE`].
+pub struct X509PurposeRef(Opaque);
+
+/// Implements a wrapper type for the static `X509_PURPOSE` table in OpenSSL.
+impl ForeignTypeRef for X509PurposeRef {
+    type CType = ffi::X509_PURPOSE;
+}
+
+impl X509PurposeRef {
+    /// Get the internal table index of an X509_PURPOSE for a given short name. Valid short
+    /// names include
+    ///  - "sslclient",
+    ///  - "sslserver",
+    ///  - "nssslserver",
+    ///  - "smimesign",
+    ///  - "smimeencrypt",
+    ///  - "crlsign",
+    ///  - "any",
+    ///  - "ocsphelper",
+    ///  - "timestampsign"
+    /// The index can be used with `X509PurposeRef::from_idx()` to get the purpose.
+    #[allow(clippy::unnecessary_cast)]
+    pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> {
+        unsafe {
+            let sname = CString::new(sname).unwrap();
+            cfg_if! {
+                if #[cfg(any(ossl110, libressl280, boringssl))] {
+                    let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?;
+                } else {
+                    let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *mut _))?;
+                }
+            }
+            Ok(purpose)
+        }
+    }
+    /// Get an `X509PurposeRef` for a given index value. The index can be obtained from e.g.
+    /// `X509PurposeRef::get_by_sname()`.
+    #[corresponds(X509_PURPOSE_get0)]
+    pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> {
+        unsafe {
+            let ptr = cvt_p_const(ffi::X509_PURPOSE_get0(idx))?;
+            Ok(X509PurposeRef::from_const_ptr(ptr))
+        }
+    }
+
+    /// Get the purpose value from an X509Purpose structure. This value is one of
+    /// - `X509_PURPOSE_SSL_CLIENT`
+    /// - `X509_PURPOSE_SSL_SERVER`
+    /// - `X509_PURPOSE_NS_SSL_SERVER`
+    /// - `X509_PURPOSE_SMIME_SIGN`
+    /// - `X509_PURPOSE_SMIME_ENCRYPT`
+    /// - `X509_PURPOSE_CRL_SIGN`
+    /// - `X509_PURPOSE_ANY`
+    /// - `X509_PURPOSE_OCSP_HELPER`
+    /// - `X509_PURPOSE_TIMESTAMP_SIGN`
+    pub fn purpose(&self) -> X509PurposeId {
+        unsafe {
+            cfg_if! {
+                if #[cfg(any(ossl110, libressl280, boringssl))] {
+                    let x509_purpose = self.as_ptr() as *const ffi::X509_PURPOSE;
+                } else {
+                    let x509_purpose = self.as_ptr() as *mut ffi::X509_PURPOSE;
+                }
+            }
+            X509PurposeId::from_raw(ffi::X509_PURPOSE_get_id(x509_purpose))
+        }
+    }
+}
diff --git a/src/x509/store.rs b/src/x509/store.rs
index 2219cfc..3a173be 100644
--- a/src/x509/store.rs
+++ b/src/x509/store.rs
@@ -42,17 +42,19 @@
 //! ```
 
 use cfg_if::cfg_if;
-use foreign_types::ForeignTypeRef;
+use foreign_types::{ForeignType, ForeignTypeRef};
 use std::mem;
 
 use crate::error::ErrorStack;
 #[cfg(not(boringssl))]
 use crate::ssl::SslFiletype;
+#[cfg(ossl300)]
+use crate::stack::Stack;
 use crate::stack::StackRef;
 use crate::util::ForeignTypeRefExt;
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef};
-use crate::x509::{X509Object, X509};
+use crate::x509::{X509Object, X509PurposeId, X509};
 use crate::{cvt, cvt_p};
 use openssl_macros::corresponds;
 #[cfg(not(boringssl))]
@@ -121,14 +123,21 @@
 
     /// Sets certificate chain validation related flags.
     #[corresponds(X509_STORE_set_flags)]
-    #[cfg(any(ossl102, libressl261))]
+    #[cfg(any(ossl102, boringssl, libressl261))]
     pub fn set_flags(&mut self, flags: X509VerifyFlags) -> Result<(), ErrorStack> {
         unsafe { cvt(ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits())).map(|_| ()) }
     }
 
+    /// Sets the certificate purpose.
+    /// The purpose value can be obtained by `X509PurposeRef::get_by_sname()`
+    #[corresponds(X509_STORE_set_purpose)]
+    pub fn set_purpose(&mut self, purpose: X509PurposeId) -> Result<(), ErrorStack> {
+        unsafe { cvt(ffi::X509_STORE_set_purpose(self.as_ptr(), purpose.as_raw())).map(|_| ()) }
+    }
+
     /// Sets certificate chain validation related parameters.
     #[corresponds[X509_STORE_set1_param]]
-    #[cfg(any(ossl102, libressl261))]
+    #[cfg(any(ossl102, boringssl, libressl261))]
     pub fn set_param(&mut self, param: &X509VerifyParamRef) -> Result<(), ErrorStack> {
         unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())).map(|_| ()) }
     }
@@ -146,7 +155,7 @@
 
 /// Marker type corresponding to the [`X509_LOOKUP_hash_dir`] lookup method.
 ///
-/// [`X509_LOOKUP_hash_dir`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_LOOKUP_hash_dir.html
+/// [`X509_LOOKUP_hash_dir`]: https://www.openssl.org/docs/manmaster/crypto/X509_LOOKUP_hash_dir.html
 // FIXME should be an enum
 pub struct HashDir;
 
@@ -195,8 +204,9 @@
 
 #[cfg(not(boringssl))]
 impl X509LookupRef<File> {
-    #[corresponds(X509_load_cert_file)]
     /// Specifies a file from which certificates will be loaded
+    #[corresponds(X509_load_cert_file)]
+    // FIXME should return 'Result<i32, ErrorStack' like load_crl_file
     pub fn load_cert_file<P: AsRef<Path>>(
         &mut self,
         file: P,
@@ -212,6 +222,23 @@
             .map(|_| ())
         }
     }
+
+    /// Specifies a file from which certificate revocation lists will be loaded
+    #[corresponds(X509_load_crl_file)]
+    pub fn load_crl_file<P: AsRef<Path>>(
+        &mut self,
+        file: P,
+        file_type: SslFiletype,
+    ) -> Result<i32, ErrorStack> {
+        let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
+        unsafe {
+            cvt(ffi::X509_load_crl_file(
+                self.as_ptr(),
+                file.as_ptr(),
+                file_type.as_raw(),
+            ))
+        }
+    }
 }
 
 generic_foreign_type_and_impl_send_sync! {
@@ -236,10 +263,24 @@
 
 impl X509StoreRef {
     /// Get a reference to the cache of certificates in this store.
+    ///
+    /// This method is deprecated. It is **unsound** and will be removed in a
+    /// future version of rust-openssl. `X509StoreRef::all_certificates`
+    /// should be used instead.
+    #[deprecated(
+        note = "This method is unsound, and will be removed in a future version of rust-openssl. X509StoreRef::all_certificates should be used instead."
+    )]
     #[corresponds(X509_STORE_get0_objects)]
     pub fn objects(&self) -> &StackRef<X509Object> {
         unsafe { StackRef::from_ptr(X509_STORE_get0_objects(self.as_ptr())) }
     }
+
+    /// Returns a stack of all the certificates in this store.
+    #[corresponds(X509_STORE_get1_all_certs)]
+    #[cfg(ossl300)]
+    pub fn all_certificates(&self) -> Stack<X509> {
+        unsafe { Stack::from_ptr(ffi::X509_STORE_get1_all_certs(self.as_ptr())) }
+    }
 }
 
 cfg_if! {
diff --git a/src/x509/tests.rs b/src/x509/tests.rs
index 336de3c..ae61a2a 100644
--- a/src/x509/tests.rs
+++ b/src/x509/tests.rs
@@ -1,6 +1,6 @@
 use std::cmp::Ordering;
 
-use crate::asn1::Asn1Time;
+use crate::asn1::{Asn1Object, Asn1OctetString, Asn1Time};
 use crate::bn::{BigNum, MsbOption};
 use crate::hash::MessageDigest;
 use crate::nid::Nid;
@@ -16,15 +16,26 @@
 #[cfg(not(boringssl))]
 use crate::x509::store::X509Lookup;
 use crate::x509::store::X509StoreBuilder;
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 use crate::x509::verify::{X509VerifyFlags, X509VerifyParam};
+#[cfg(any(ossl102, boringssl))]
+use crate::x509::X509PurposeId;
+#[cfg(any(ossl102, boringssl, libressl261))]
+use crate::x509::X509PurposeRef;
 #[cfg(ossl110)]
-use crate::x509::X509Builder;
-use crate::x509::{X509Name, X509Req, X509StoreContext, X509VerifyResult, X509};
+use crate::x509::{CrlReason, X509Builder};
+use crate::x509::{
+    CrlStatus, X509Crl, X509Extension, X509Name, X509Req, X509StoreContext, X509VerifyResult, X509,
+};
+
+#[cfg(ossl110)]
+use foreign_types::ForeignType;
 use hex::{self, FromHex};
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 use libc::time_t;
 
+use super::{AuthorityInformationAccess, CertificateIssuer, ReasonCode};
+
 fn pkey() -> PKey<Private> {
     let rsa = Rsa::generate(2048).unwrap();
     PKey::from_rsa(rsa).unwrap()
@@ -161,6 +172,70 @@
 }
 
 #[test]
+#[cfg(any(ossl110, boringssl))]
+fn test_retrieve_pathlen() {
+    let cert = include_bytes!("../../test/root-ca.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    assert_eq!(cert.pathlen(), None);
+
+    let cert = include_bytes!("../../test/intermediate-ca.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    assert_eq!(cert.pathlen(), Some(0));
+
+    let cert = include_bytes!("../../test/alt_name_cert.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    assert_eq!(cert.pathlen(), None);
+}
+
+#[test]
+#[cfg(any(ossl110, boringssl))]
+fn test_subject_key_id() {
+    let cert = include_bytes!("../../test/certv3.pem");
+    let cert = X509::from_pem(cert).unwrap();
+
+    let subject_key_id = cert.subject_key_id().unwrap();
+    assert_eq!(
+        subject_key_id.as_slice(),
+        &b"\xB6\x73\x2F\x61\xA5\x4B\xA1\xEF\x48\x2C\x15\xB1\x9F\xF3\xDC\x34\x2F\xBC\xAC\x30"[..]
+    );
+}
+
+#[test]
+#[cfg(any(ossl110, boringssl))]
+fn test_authority_key_id() {
+    let cert = include_bytes!("../../test/certv3.pem");
+    let cert = X509::from_pem(cert).unwrap();
+
+    let authority_key_id = cert.authority_key_id().unwrap();
+    assert_eq!(
+        authority_key_id.as_slice(),
+        &b"\x6C\xD3\xA5\x03\xAB\x0D\x5F\x2C\xC9\x8D\x8A\x9C\x88\xA7\x88\x77\xB8\x37\xFD\x9A"[..]
+    );
+}
+
+#[test]
+#[cfg(ossl111d)]
+fn test_authority_issuer_and_serial() {
+    let cert = include_bytes!("../../test/authority_key_identifier.pem");
+    let cert = X509::from_pem(cert).unwrap();
+
+    let authority_issuer = cert.authority_issuer().unwrap();
+    assert_eq!(1, authority_issuer.len());
+    let dn = authority_issuer[0].directory_name().unwrap();
+    let mut o = dn.entries_by_nid(Nid::ORGANIZATIONNAME);
+    let o = o.next().unwrap().data().as_utf8().unwrap();
+    assert_eq!(o.as_bytes(), b"PyCA");
+    let mut cn = dn.entries_by_nid(Nid::COMMONNAME);
+    let cn = cn.next().unwrap().data().as_utf8().unwrap();
+    assert_eq!(cn.as_bytes(), b"cryptography.io");
+
+    let authority_serial = cert.authority_serial().unwrap();
+    let serial = authority_serial.to_bn().unwrap();
+    let expected = BigNum::from_u32(3).unwrap();
+    assert_eq!(serial, expected);
+}
+
+#[test]
 fn test_subject_alt_name_iter() {
     let cert = include_bytes!("../../test/alt_name_cert.pem");
     let cert = X509::from_pem(cert).unwrap();
@@ -282,6 +357,76 @@
 }
 
 #[test]
+// This tests `X509Extension::new`, even though its deprecated.
+#[allow(deprecated)]
+fn x509_extension_new() {
+    assert!(X509Extension::new(None, None, "crlDistributionPoints", "section").is_err());
+    assert!(X509Extension::new(None, None, "proxyCertInfo", "").is_err());
+    assert!(X509Extension::new(None, None, "certificatePolicies", "").is_err());
+    assert!(X509Extension::new(None, None, "subjectAltName", "dirName:section").is_err());
+}
+
+#[test]
+fn x509_extension_new_from_der() {
+    let ext = X509Extension::new_from_der(
+        &Asn1Object::from_str("2.5.29.19").unwrap(),
+        true,
+        &Asn1OctetString::new_from_bytes(b"\x30\x03\x01\x01\xff").unwrap(),
+    )
+    .unwrap();
+    assert_eq!(
+        ext.to_der().unwrap(),
+        b"0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff"
+    );
+}
+
+#[test]
+fn x509_extension_to_der() {
+    let builder = X509::builder().unwrap();
+
+    for (ext, expected) in [
+        (
+            BasicConstraints::new().critical().ca().build().unwrap(),
+            b"0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff" as &[u8],
+        ),
+        (
+            SubjectAlternativeName::new()
+                .dns("example.com,DNS:example2.com")
+                .build(&builder.x509v3_context(None, None))
+                .unwrap(),
+            b"0'\x06\x03U\x1d\x11\x04 0\x1e\x82\x1cexample.com,DNS:example2.com",
+        ),
+        (
+            SubjectAlternativeName::new()
+                .rid("1.2.3.4")
+                .uri("https://example.com")
+                .build(&builder.x509v3_context(None, None))
+                .unwrap(),
+            b"0#\x06\x03U\x1d\x11\x04\x1c0\x1a\x88\x03*\x03\x04\x86\x13https://example.com",
+        ),
+        (
+            ExtendedKeyUsage::new()
+                .server_auth()
+                .other("2.999.1")
+                .other("clientAuth")
+                .build()
+                .unwrap(),
+            b"0\x22\x06\x03U\x1d%\x04\x1b0\x19\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x03\x887\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x02",
+        ),
+    ] {
+        assert_eq!(&ext.to_der().unwrap(), expected);
+    }
+}
+
+#[test]
+fn eku_invalid_other() {
+    assert!(ExtendedKeyUsage::new()
+        .other("1.1.1.1.1,2.2.2.2.2")
+        .build()
+        .is_err());
+}
+
+#[test]
 fn x509_req_builder() {
     let pkey = pkey();
 
@@ -412,7 +557,7 @@
 }
 
 #[test]
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 fn test_verify_fails_with_crl_flag_set_and_no_crl() {
     let cert = include_bytes!("../../test/cert.pem");
     let cert = X509::from_pem(cert).unwrap();
@@ -438,6 +583,68 @@
     )
 }
 
+#[test]
+#[cfg(any(ossl102, boringssl, libressl261))]
+fn test_verify_cert_with_purpose() {
+    let cert = include_bytes!("../../test/cert.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    let ca = include_bytes!("../../test/root-ca.pem");
+    let ca = X509::from_pem(ca).unwrap();
+    let chain = Stack::new().unwrap();
+
+    let mut store_bldr = X509StoreBuilder::new().unwrap();
+    let purpose_idx = X509PurposeRef::get_by_sname("sslserver")
+        .expect("Getting certificate purpose 'sslserver' failed");
+    let x509_purposeref =
+        X509PurposeRef::from_idx(purpose_idx).expect("Getting certificate purpose failed");
+    store_bldr
+        .set_purpose(x509_purposeref.purpose())
+        .expect("Setting certificate purpose failed");
+    store_bldr.add_cert(ca).unwrap();
+
+    let store = store_bldr.build();
+
+    let mut context = X509StoreContext::new().unwrap();
+    assert!(context
+        .init(&store, &cert, &chain, |c| c.verify_cert())
+        .unwrap());
+}
+
+#[test]
+#[cfg(any(ossl102, boringssl, libressl261))]
+fn test_verify_cert_with_wrong_purpose_fails() {
+    let cert = include_bytes!("../../test/cert.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    let ca = include_bytes!("../../test/root-ca.pem");
+    let ca = X509::from_pem(ca).unwrap();
+    let chain = Stack::new().unwrap();
+
+    let mut store_bldr = X509StoreBuilder::new().unwrap();
+    let purpose_idx = X509PurposeRef::get_by_sname("timestampsign")
+        .expect("Getting certificate purpose 'timestampsign' failed");
+    let x509_purpose =
+        X509PurposeRef::from_idx(purpose_idx).expect("Getting certificate purpose failed");
+    store_bldr
+        .set_purpose(x509_purpose.purpose())
+        .expect("Setting certificate purpose failed");
+    store_bldr.add_cert(ca).unwrap();
+
+    let store = store_bldr.build();
+
+    let expected_error = ffi::X509_V_ERR_INVALID_PURPOSE;
+    let mut context = X509StoreContext::new().unwrap();
+    assert_eq!(
+        context
+            .init(&store, &cert, &chain, |c| {
+                c.verify_cert()?;
+                Ok(c.error())
+            })
+            .unwrap()
+            .as_raw(),
+        expected_error
+    )
+}
+
 #[cfg(ossl110)]
 #[test]
 fn x509_ref_version() {
@@ -466,6 +673,84 @@
 }
 
 #[test]
+fn test_load_crl() {
+    let ca = include_bytes!("../../test/crl-ca.crt");
+    let ca = X509::from_pem(ca).unwrap();
+
+    let crl = include_bytes!("../../test/test.crl");
+    let crl = X509Crl::from_der(crl).unwrap();
+    assert!(crl.verify(&ca.public_key().unwrap()).unwrap());
+
+    let cert = include_bytes!("../../test/subca.crt");
+    let cert = X509::from_pem(cert).unwrap();
+
+    let revoked = match crl.get_by_cert(&cert) {
+        CrlStatus::Revoked(revoked) => revoked,
+        _ => panic!("cert should be revoked"),
+    };
+
+    assert_eq!(
+        revoked.serial_number().to_bn().unwrap(),
+        cert.serial_number().to_bn().unwrap(),
+        "revoked and cert serial numbers should match"
+    );
+}
+
+#[test]
+fn test_crl_entry_extensions() {
+    let crl = include_bytes!("../../test/entry_extensions.crl");
+    let crl = X509Crl::from_pem(crl).unwrap();
+
+    let (critical, access_info) = crl
+        .extension::<AuthorityInformationAccess>()
+        .unwrap()
+        .expect("Authority Information Access extension should be present");
+    assert!(
+        !critical,
+        "Authority Information Access extension is not critical"
+    );
+    assert_eq!(
+        access_info.len(),
+        1,
+        "Authority Information Access should have one entry"
+    );
+    assert_eq!(access_info[0].method().to_string(), "CA Issuers");
+    assert_eq!(
+        access_info[0].location().uri(),
+        Some("http://www.example.com/ca.crt")
+    );
+    let revoked_certs = crl.get_revoked().unwrap();
+    let entry = &revoked_certs[0];
+
+    let (critical, issuer) = entry
+        .extension::<CertificateIssuer>()
+        .unwrap()
+        .expect("Certificate issuer extension should be present");
+    assert!(critical, "Certificate issuer extension is critical");
+    assert_eq!(issuer.len(), 1, "Certificate issuer should have one entry");
+    let issuer = issuer[0]
+        .directory_name()
+        .expect("Issuer should be a directory name");
+    assert_eq!(
+        format!("{:?}", issuer),
+        r#"[countryName = "GB", commonName = "Test CA"]"#
+    );
+
+    // reason_code can't be inspected without ossl110
+    #[allow(unused_variables)]
+    let (critical, reason_code) = entry
+        .extension::<ReasonCode>()
+        .unwrap()
+        .expect("Reason code extension should be present");
+    assert!(!critical, "Reason code extension is not critical");
+    #[cfg(ossl110)]
+    assert_eq!(
+        CrlReason::KEY_COMPROMISE,
+        CrlReason::from_raw(reason_code.get_i64().unwrap() as ffi::c_int)
+    );
+}
+
+#[test]
 fn test_save_subject_der() {
     let cert = include_bytes!("../../test/cert.pem");
     let cert = X509::from_pem(cert).unwrap();
@@ -551,7 +836,17 @@
 }
 
 #[test]
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(boringssl, ossl110, libressl270))]
+fn test_name_to_owned() {
+    let cert = include_bytes!("../../test/cert.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    let name = cert.subject_name();
+    let copied_name = name.to_owned().unwrap();
+    assert_eq!(Ordering::Equal, name.try_cmp(&copied_name).unwrap());
+}
+
+#[test]
+#[cfg(any(ossl102, boringssl, libressl261))]
 fn test_verify_param_set_time_fails_verification() {
     const TEST_T_2030: time_t = 1893456000;
 
@@ -582,7 +877,7 @@
 }
 
 #[test]
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 fn test_verify_param_set_time() {
     const TEST_T_2020: time_t = 1577836800;
 
@@ -606,7 +901,7 @@
 }
 
 #[test]
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 fn test_verify_param_set_depth() {
     let cert = include_bytes!("../../test/leaf.pem");
     let cert = X509::from_pem(cert).unwrap();
@@ -633,7 +928,7 @@
 }
 
 #[test]
-#[cfg(any(ossl102, libressl261))]
+#[cfg(any(ossl102, boringssl, libressl261))]
 #[allow(clippy::bool_to_int_with_if)]
 fn test_verify_param_set_depth_fails_verification() {
     let cert = include_bytes!("../../test/leaf.pem");
@@ -693,3 +988,207 @@
         .init(&store, &cert, &chain, |c| c.verify_cert())
         .unwrap());
 }
+
+#[test]
+#[cfg(ossl110)]
+fn test_verify_param_auth_level() {
+    let mut param = X509VerifyParam::new().unwrap();
+    let auth_lvl = 2;
+    let auth_lvl_default = -1;
+
+    assert_eq!(param.auth_level(), auth_lvl_default);
+
+    param.set_auth_level(auth_lvl);
+    assert_eq!(param.auth_level(), auth_lvl);
+}
+
+#[test]
+#[cfg(any(ossl102, boringssl))]
+fn test_set_purpose() {
+    let cert = include_bytes!("../../test/leaf.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem");
+    let intermediate_ca = X509::from_pem(intermediate_ca).unwrap();
+    let ca = include_bytes!("../../test/root-ca.pem");
+    let ca = X509::from_pem(ca).unwrap();
+    let mut chain = Stack::new().unwrap();
+    chain.push(intermediate_ca).unwrap();
+
+    let mut store_bldr = X509StoreBuilder::new().unwrap();
+    store_bldr.add_cert(ca).unwrap();
+    let mut verify_params = X509VerifyParam::new().unwrap();
+    verify_params.set_purpose(X509PurposeId::ANY).unwrap();
+    store_bldr.set_param(&verify_params).unwrap();
+    let store = store_bldr.build();
+    let mut context = X509StoreContext::new().unwrap();
+
+    assert!(context
+        .init(&store, &cert, &chain, |c| c.verify_cert())
+        .unwrap());
+}
+
+#[test]
+#[cfg(any(ossl102, boringssl))]
+fn test_set_purpose_fails_verification() {
+    let cert = include_bytes!("../../test/leaf.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem");
+    let intermediate_ca = X509::from_pem(intermediate_ca).unwrap();
+    let ca = include_bytes!("../../test/root-ca.pem");
+    let ca = X509::from_pem(ca).unwrap();
+    let mut chain = Stack::new().unwrap();
+    chain.push(intermediate_ca).unwrap();
+
+    let mut store_bldr = X509StoreBuilder::new().unwrap();
+    store_bldr.add_cert(ca).unwrap();
+    let mut verify_params = X509VerifyParam::new().unwrap();
+    verify_params
+        .set_purpose(X509PurposeId::TIMESTAMP_SIGN)
+        .unwrap();
+    store_bldr.set_param(&verify_params).unwrap();
+    let store = store_bldr.build();
+
+    let expected_error = ffi::X509_V_ERR_INVALID_PURPOSE;
+    let mut context = X509StoreContext::new().unwrap();
+    assert_eq!(
+        context
+            .init(&store, &cert, &chain, |c| {
+                c.verify_cert()?;
+                Ok(c.error())
+            })
+            .unwrap()
+            .as_raw(),
+        expected_error
+    )
+}
+
+#[test]
+#[cfg(any(ossl101, libressl350))]
+fn test_add_name_entry() {
+    let cert = include_bytes!("../../test/cert.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    let inp_name = cert.subject_name().entries().next().unwrap();
+
+    let mut names = X509Name::builder().unwrap();
+    names.append_entry(inp_name).unwrap();
+    let names = names.build();
+
+    let mut entries = names.entries();
+    let outp_name = entries.next().unwrap();
+    assert_eq!(outp_name.object().nid(), inp_name.object().nid());
+    assert_eq!(outp_name.data().as_slice(), inp_name.data().as_slice());
+    assert!(entries.next().is_none());
+}
+
+#[test]
+#[cfg(not(boringssl))]
+fn test_load_crl_file_fail() {
+    let mut store_bldr = X509StoreBuilder::new().unwrap();
+    let lookup = store_bldr.add_lookup(X509Lookup::file()).unwrap();
+    let res = lookup.load_crl_file("test/root-ca.pem", SslFiletype::PEM);
+    assert!(res.is_err());
+}
+
+#[cfg(ossl110)]
+fn ipaddress_as_subject_alternative_name_is_formatted_in_debug<T>(expected_ip: T)
+where
+    T: Into<std::net::IpAddr>,
+{
+    let expected_ip = format!("{:?}", expected_ip.into());
+    let mut builder = X509Builder::new().unwrap();
+    let san = SubjectAlternativeName::new()
+        .ip(&expected_ip)
+        .build(&builder.x509v3_context(None, None))
+        .unwrap();
+    builder.append_extension(san).unwrap();
+    let cert = builder.build();
+    let actual_ip = cert
+        .subject_alt_names()
+        .into_iter()
+        .flatten()
+        .map(|n| format!("{:?}", *n))
+        .next()
+        .unwrap();
+    assert_eq!(actual_ip, expected_ip);
+}
+
+#[cfg(ossl110)]
+#[test]
+fn ipv4_as_subject_alternative_name_is_formatted_in_debug() {
+    ipaddress_as_subject_alternative_name_is_formatted_in_debug([8u8, 8, 8, 128]);
+}
+
+#[cfg(ossl110)]
+#[test]
+fn ipv6_as_subject_alternative_name_is_formatted_in_debug() {
+    ipaddress_as_subject_alternative_name_is_formatted_in_debug([
+        8u8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 128,
+    ]);
+}
+
+#[cfg(ossl110)]
+#[test]
+fn other_name_as_subject_alternative_name() {
+    let oid = Asn1Object::from_str("1.3.6.1.5.5.7.8.11").unwrap();
+    // this is the hex representation of "test" encoded as a ia5string
+    let content = [0x16, 0x04, 0x74, 0x65, 0x73, 0x74];
+
+    let mut builder = X509Builder::new().unwrap();
+    let san = SubjectAlternativeName::new()
+        .other_name2(oid, &content)
+        .build(&builder.x509v3_context(None, None))
+        .unwrap();
+    builder.append_extension(san).unwrap();
+    let cert = builder.build();
+    let general_name = cert
+        .subject_alt_names()
+        .into_iter()
+        .flatten()
+        .next()
+        .unwrap();
+    unsafe {
+        assert_eq!((*general_name.as_ptr()).type_, 0);
+    }
+}
+
+#[test]
+fn test_dist_point() {
+    let cert = include_bytes!("../../test/certv3.pem");
+    let cert = X509::from_pem(cert).unwrap();
+
+    let dps = cert.crl_distribution_points().unwrap();
+    let dp = dps.get(0).unwrap();
+    let dp_nm = dp.distpoint().unwrap();
+    let dp_gns = dp_nm.fullname().unwrap();
+    let dp_gn = dp_gns.get(0).unwrap();
+    assert_eq!(dp_gn.uri().unwrap(), "http://example.com/crl.pem");
+
+    let dp = dps.get(1).unwrap();
+    let dp_nm = dp.distpoint().unwrap();
+    let dp_gns = dp_nm.fullname().unwrap();
+    let dp_gn = dp_gns.get(0).unwrap();
+    assert_eq!(dp_gn.uri().unwrap(), "http://example.com/crl2.pem");
+    assert!(dps.get(2).is_none())
+}
+
+#[test]
+fn test_dist_point_null() {
+    let cert = include_bytes!("../../test/cert.pem");
+    let cert = X509::from_pem(cert).unwrap();
+    assert!(cert.crl_distribution_points().is_none());
+}
+
+#[test]
+#[cfg(ossl300)]
+fn test_store_all_certificates() {
+    let cert = include_bytes!("../../test/cert.pem");
+    let cert = X509::from_pem(cert).unwrap();
+
+    let store = {
+        let mut b = X509StoreBuilder::new().unwrap();
+        b.add_cert(cert).unwrap();
+        b.build()
+    };
+
+    assert_eq!(store.all_certificates().len(), 1);
+}
diff --git a/src/x509/verify.rs b/src/x509/verify.rs
index 20dd4be..2cde93f 100644
--- a/src/x509/verify.rs
+++ b/src/x509/verify.rs
@@ -4,58 +4,64 @@
 use std::net::IpAddr;
 
 use crate::error::ErrorStack;
+#[cfg(any(ossl102, boringssl))]
+use crate::x509::X509PurposeId;
 use crate::{cvt, cvt_p};
 use openssl_macros::corresponds;
 
 bitflags! {
     /// Flags used to check an `X509` certificate.
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct X509CheckFlags: c_uint {
-        const ALWAYS_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT;
-        const NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS;
-        const NO_PARTIAL_WILDCARDS = ffi::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
-        const MULTI_LABEL_WILDCARDS = ffi::X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS;
-        const SINGLE_LABEL_SUBDOMAINS = ffi::X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS;
+        const ALWAYS_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT as _;
+        const NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS as _;
+        const NO_PARTIAL_WILDCARDS = ffi::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS as _;
+        const MULTI_LABEL_WILDCARDS = ffi::X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS as _;
+        const SINGLE_LABEL_SUBDOMAINS = ffi::X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS as _;
         /// Requires OpenSSL 1.1.0 or newer.
         #[cfg(any(ossl110))]
         const NEVER_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_NEVER_CHECK_SUBJECT;
 
         #[deprecated(since = "0.10.6", note = "renamed to NO_WILDCARDS")]
-        const FLAG_NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS;
+        const FLAG_NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS as _;
     }
 }
 
 bitflags! {
     /// Flags used to verify an `X509` certificate chain.
+    #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+    #[repr(transparent)]
     pub struct X509VerifyFlags: c_ulong {
-        const CB_ISSUER_CHECK = ffi::X509_V_FLAG_CB_ISSUER_CHECK;
-        const USE_CHECK_TIME = ffi::X509_V_FLAG_USE_CHECK_TIME;
-        const CRL_CHECK = ffi::X509_V_FLAG_CRL_CHECK;
-        const CRL_CHECK_ALL = ffi::X509_V_FLAG_CRL_CHECK_ALL;
-        const IGNORE_CRITICAL = ffi::X509_V_FLAG_IGNORE_CRITICAL;
-        const X509_STRICT = ffi::X509_V_FLAG_X509_STRICT;
-        const ALLOW_PROXY_CERTS = ffi::X509_V_FLAG_ALLOW_PROXY_CERTS;
-        const POLICY_CHECK = ffi::X509_V_FLAG_POLICY_CHECK;
-        const EXPLICIT_POLICY = ffi::X509_V_FLAG_EXPLICIT_POLICY;
-        const INHIBIT_ANY = ffi::X509_V_FLAG_INHIBIT_ANY;
-        const INHIBIT_MAP = ffi::X509_V_FLAG_INHIBIT_MAP;
-        const NOTIFY_POLICY = ffi::X509_V_FLAG_NOTIFY_POLICY;
-        const EXTENDED_CRL_SUPPORT = ffi::X509_V_FLAG_EXTENDED_CRL_SUPPORT;
-        const USE_DELTAS = ffi::X509_V_FLAG_USE_DELTAS;
-        const CHECK_SS_SIGNATURE = ffi::X509_V_FLAG_CHECK_SS_SIGNATURE;
-        #[cfg(ossl102)]
-        const TRUSTED_FIRST = ffi::X509_V_FLAG_TRUSTED_FIRST;
+        const CB_ISSUER_CHECK = ffi::X509_V_FLAG_CB_ISSUER_CHECK as _;
+        const USE_CHECK_TIME = ffi::X509_V_FLAG_USE_CHECK_TIME as _;
+        const CRL_CHECK = ffi::X509_V_FLAG_CRL_CHECK as _;
+        const CRL_CHECK_ALL = ffi::X509_V_FLAG_CRL_CHECK_ALL as _;
+        const IGNORE_CRITICAL = ffi::X509_V_FLAG_IGNORE_CRITICAL as _;
+        const X509_STRICT = ffi::X509_V_FLAG_X509_STRICT as _;
+        const ALLOW_PROXY_CERTS = ffi::X509_V_FLAG_ALLOW_PROXY_CERTS as _;
+        const POLICY_CHECK = ffi::X509_V_FLAG_POLICY_CHECK as _;
+        const EXPLICIT_POLICY = ffi::X509_V_FLAG_EXPLICIT_POLICY as _;
+        const INHIBIT_ANY = ffi::X509_V_FLAG_INHIBIT_ANY as _;
+        const INHIBIT_MAP = ffi::X509_V_FLAG_INHIBIT_MAP as _;
+        const NOTIFY_POLICY = ffi::X509_V_FLAG_NOTIFY_POLICY as _;
+        const EXTENDED_CRL_SUPPORT = ffi::X509_V_FLAG_EXTENDED_CRL_SUPPORT as _;
+        const USE_DELTAS = ffi::X509_V_FLAG_USE_DELTAS as _;
+        const CHECK_SS_SIGNATURE = ffi::X509_V_FLAG_CHECK_SS_SIGNATURE as _;
+        #[cfg(any(ossl102, boringssl))]
+        const TRUSTED_FIRST = ffi::X509_V_FLAG_TRUSTED_FIRST as _;
         #[cfg(ossl102)]
         const SUITEB_128_LOS_ONLY = ffi::X509_V_FLAG_SUITEB_128_LOS_ONLY;
         #[cfg(ossl102)]
         const SUITEB_192_LOS = ffi::X509_V_FLAG_SUITEB_128_LOS;
         #[cfg(ossl102)]
         const SUITEB_128_LOS = ffi::X509_V_FLAG_SUITEB_192_LOS;
-        #[cfg(ossl102)]
-        const PARTIAL_CHAIN = ffi::X509_V_FLAG_PARTIAL_CHAIN;
-        #[cfg(ossl110)]
-        const NO_ALT_CHAINS = ffi::X509_V_FLAG_NO_ALT_CHAINS;
-        #[cfg(ossl110)]
-        const NO_CHECK_TIME = ffi::X509_V_FLAG_NO_CHECK_TIME;
+        #[cfg(any(ossl102, boringssl))]
+        const PARTIAL_CHAIN = ffi::X509_V_FLAG_PARTIAL_CHAIN as _;
+        #[cfg(any(ossl110, boringssl))]
+        const NO_ALT_CHAINS = ffi::X509_V_FLAG_NO_ALT_CHAINS as _;
+        #[cfg(any(ossl110, boringssl))]
+        const NO_CHECK_TIME = ffi::X509_V_FLAG_NO_CHECK_TIME as _;
     }
 }
 
@@ -85,14 +91,20 @@
     #[corresponds(X509_VERIFY_PARAM_set_hostflags)]
     pub fn set_hostflags(&mut self, hostflags: X509CheckFlags) {
         unsafe {
-            ffi::X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags.bits);
+            ffi::X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags.bits());
         }
     }
 
     /// Set verification flags.
     #[corresponds(X509_VERIFY_PARAM_set_flags)]
     pub fn set_flags(&mut self, flags: X509VerifyFlags) -> Result<(), ErrorStack> {
-        unsafe { cvt(ffi::X509_VERIFY_PARAM_set_flags(self.as_ptr(), flags.bits)).map(|_| ()) }
+        unsafe {
+            cvt(ffi::X509_VERIFY_PARAM_set_flags(
+                self.as_ptr(),
+                flags.bits(),
+            ))
+            .map(|_| ())
+        }
     }
 
     /// Clear verification flags.
@@ -101,7 +113,7 @@
         unsafe {
             cvt(ffi::X509_VERIFY_PARAM_clear_flags(
                 self.as_ptr(),
-                flags.bits,
+                flags.bits(),
             ))
             .map(|_| ())
         }
@@ -111,22 +123,39 @@
     #[corresponds(X509_VERIFY_PARAM_get_flags)]
     pub fn flags(&mut self) -> X509VerifyFlags {
         let bits = unsafe { ffi::X509_VERIFY_PARAM_get_flags(self.as_ptr()) };
-        X509VerifyFlags { bits }
+        X509VerifyFlags::from_bits_retain(bits)
     }
 
     /// Set the expected DNS hostname.
     #[corresponds(X509_VERIFY_PARAM_set1_host)]
     pub fn set_host(&mut self, host: &str) -> Result<(), ErrorStack> {
         unsafe {
+            // len == 0 means "run strlen" :(
+            let raw_host = if host.is_empty() { "\0" } else { host };
             cvt(ffi::X509_VERIFY_PARAM_set1_host(
                 self.as_ptr(),
-                host.as_ptr() as *const _,
+                raw_host.as_ptr() as *const _,
                 host.len(),
             ))
             .map(|_| ())
         }
     }
 
+    /// Set the expected email address.
+    #[corresponds(X509_VERIFY_PARAM_set1_email)]
+    pub fn set_email(&mut self, email: &str) -> Result<(), ErrorStack> {
+        unsafe {
+            // len == 0 means "run strlen" :(
+            let raw_email = if email.is_empty() { "\0" } else { email };
+            cvt(ffi::X509_VERIFY_PARAM_set1_email(
+                self.as_ptr(),
+                raw_email.as_ptr() as *const _,
+                email.len(),
+            ))
+            .map(|_| ())
+        }
+    }
+
     /// Set the expected IPv4 or IPv6 address.
     #[corresponds(X509_VERIFY_PARAM_set1_ip)]
     pub fn set_ip(&mut self, ip: IpAddr) -> Result<(), ErrorStack> {
@@ -162,4 +191,25 @@
     pub fn set_depth(&mut self, depth: c_int) {
         unsafe { ffi::X509_VERIFY_PARAM_set_depth(self.as_ptr(), depth) }
     }
+
+    /// Sets the authentication security level to auth_level
+    #[corresponds(X509_VERIFY_PARAM_set_auth_level)]
+    #[cfg(ossl110)]
+    pub fn set_auth_level(&mut self, lvl: c_int) {
+        unsafe { ffi::X509_VERIFY_PARAM_set_auth_level(self.as_ptr(), lvl) }
+    }
+
+    /// Gets the current authentication security level
+    #[corresponds(X509_VERIFY_PARAM_get_auth_level)]
+    #[cfg(ossl110)]
+    pub fn auth_level(&self) -> i32 {
+        unsafe { ffi::X509_VERIFY_PARAM_get_auth_level(self.as_ptr()) }
+    }
+
+    /// Sets the verification purpose
+    #[corresponds(X509_VERIFY_PARAM_set_purpose)]
+    #[cfg(any(ossl102, boringssl))]
+    pub fn set_purpose(&mut self, purpose: X509PurposeId) -> Result<(), ErrorStack> {
+        unsafe { cvt(ffi::X509_VERIFY_PARAM_set_purpose(self.as_ptr(), purpose.0)).map(|_| ()) }
+    }
 }
diff --git a/test/authority_key_identifier.pem b/test/authority_key_identifier.pem
new file mode 100644
index 0000000..cbe9169
--- /dev/null
+++ b/test/authority_key_identifier.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDIjCCAgqgAwIBAgIBAzANBgkqhkiG9w0BAQUFADApMQ0wCwYDVQQKDARQeUNB
+MRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW8wHhcNMTUwNTAzMDk0OTU2WhcNMTYw
+NTAyMDk0OTU2WjApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFw
+aHkuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCadi1UZioxdnP
+ajqlRZHeKsSxvXXhgrWvlt91P3gV0dBThRFhJsLOhjNLz6PO6KeRbjz9GhTA2hdk
+xtIpXrjvTv9dEJ1/k0xebsHWgFC43aTlgekw0U4cMwMe5NGeeg1tfzbJwldIN+cK
+vabc08ADlkmM6DMnUArkzA2yii0DErRFMSIGrkDr6E9puord3h6Mh8Jfnc3TDAq8
+Qo1DI2XM7oFSWNfecQ9KbIC5wzzT+7Shoyz7QmCk/XhRzt8Xcfc3yAXIwazvLf8b
+YP1auaSG11a5E+w6onj91h8UHKKOXu+rdq5YYPZ+qUYpxA7ZJ/VAGadMulYbXaO8
+Syi39HTpAgMBAAGjVTBTMFEGA1UdIwRKMEiAFDlFPso9Yh3qhkn2WqtAt6RwmPHs
+oS2kKzApMQ0wCwYDVQQKDARQeUNBMRgwFgYDVQQDDA9jcnlwdG9ncmFwaHkuaW+C
+AQMwDQYJKoZIhvcNAQEFBQADggEBAFbZYy6aZJUK/f7nJx2Rs/ht6hMbM32/RoXZ
+JGbYapNVqVu/vymcfc/se3FHS5OVmPsnRlo/FIKDn/r5DGl73Sn/FvDJiLJZFucT
+msyYuHZ+ZRYWzWmN2fcB3cfxj0s3qps6f5OoCOqoINOSe4HRGlw4X9keZSD+3xAt
+vHNwQdlPC7zWbPdrzLT+FqR0e/O81vFJJS6drHJWqPcR3NQVtZw+UF7A/HKwbfeL
+Nu2zj6165hzOi9HUxa2/mPr/eLUUV1sTzXp2+TFjt3rVCjW1XnpMLdwNBHzjpyAB
+dTOX3iw0+BPy3s2jtnCW1PLpc74kvSTaBwhg74sq39EXfIKax00=
+-----END CERTIFICATE-----
diff --git a/test/ca.crt b/test/ca.crt
new file mode 100644
index 0000000..a0a8ab2
--- /dev/null
+++ b/test/ca.crt
@@ -0,0 +1,88 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            13:ae:da:d8:f4:18:d7:73:b8:bd:35:c9:ce:8e:b3:fc
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=TestCA
+        Validity
+            Not Before: Jun  6 19:11:19 2019 GMT
+            Not After : May 21 19:11:19 2022 GMT
+        Subject: CN=SubCA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                RSA Public-Key: (2048 bit)
+                Modulus:
+                    00:b0:09:fc:54:e7:6a:9f:0c:bd:ad:5a:8d:ef:94:
+                    4e:11:a6:87:19:4f:bf:a6:e1:62:a5:2d:b7:17:df:
+                    67:53:70:da:fe:7d:99:17:ee:13:47:0b:40:0b:a2:
+                    34:32:a9:d3:bf:20:fc:13:77:a1:d5:26:60:1f:f0:
+                    d4:be:dc:76:7c:1e:6c:b4:4c:01:7c:56:cd:5c:53:
+                    ec:81:b3:81:2a:b2:35:26:06:5a:79:e0:b3:9e:e4:
+                    57:e1:09:de:ad:7f:c8:cd:87:ee:49:93:30:52:58:
+                    b2:bc:0f:c1:b6:10:44:f8:85:d5:5b:0a:9b:28:fe:
+                    f4:f4:4a:16:a6:f7:25:e9:96:47:69:73:5b:33:77:
+                    92:7d:61:8d:2a:3d:d5:04:89:40:bf:6b:d2:fd:5d:
+                    e2:1a:80:a9:8e:c8:92:f6:e5:4c:00:84:f9:6e:2a:
+                    93:a3:23:ee:28:23:81:f4:54:f0:18:2c:ee:32:8e:
+                    38:9c:a0:c8:33:04:b0:fc:4c:43:1a:5c:04:84:9f:
+                    73:c6:08:c7:1d:64:39:fe:72:19:3b:cc:a5:fd:0b:
+                    43:25:0d:2b:a9:88:77:9e:62:e6:ac:c2:9a:60:42:
+                    4f:4a:54:47:bc:a0:29:72:7c:38:52:c9:ea:27:c5:
+                    3d:d0:81:4a:3e:b8:78:79:4b:89:b8:4e:6d:1b:24:
+                    15:bd
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://127.0.0.1:8081/pki/test.crl
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+            X509v3 Subject Key Identifier: 
+                FD:82:45:39:A1:91:41:F2:66:CC:0D:75:D5:0D:40:D5:81:A7:A1:43
+            X509v3 Authority Key Identifier: 
+                keyid:C5:CC:F5:A1:8C:D9:E4:A7:BA:EC:21:F5:D1:84:23:EA:0D:C2:C7:30
+                DirName:/CN=TestCA
+                serial:33:E7:04:87:09:32:87:21:D9:CD:7C:AA:4C:5A:BB:2C:6C:7B:54:28
+
+            X509v3 Key Usage: 
+                Certificate Sign, CRL Sign
+    Signature Algorithm: sha256WithRSAEncryption
+         96:a0:ff:8a:4b:bd:45:96:c9:72:3c:63:e3:48:c4:ab:ef:7e:
+         db:76:3f:d9:02:9e:69:c8:d9:36:55:e1:f5:9b:c9:69:d8:69:
+         02:ac:50:8c:60:94:2c:2e:b9:a8:65:ac:f5:00:b0:8b:96:25:
+         0b:8a:ef:94:21:57:e2:04:c2:c3:86:bf:06:4e:91:5c:e6:bc:
+         1b:03:31:8b:64:ea:c5:79:c3:5c:94:e5:aa:67:7e:74:12:07:
+         14:fd:cd:32:02:26:26:c9:0a:ed:d4:da:ee:2a:84:e3:f1:60:
+         b3:09:77:27:a1:3c:ac:ec:61:18:30:b5:6d:1f:16:0a:24:1a:
+         cf:1c:1b:60:a5:60:e5:2c:8b:cf:37:83:0c:15:e7:79:30:3f:
+         ee:50:45:7c:4b:c6:2c:cd:2c:81:0a:98:f1:65:44:7a:ca:2a:
+         20:1a:de:19:d9:4b:ca:a1:e2:a4:b5:14:47:bf:b4:68:15:03:
+         c0:55:e5:f4:47:0e:55:9f:fe:85:d8:2c:7d:d0:1a:96:11:b9:
+         68:b7:74:1e:61:94:c1:ae:87:52:2d:c6:26:ba:51:ed:f1:91:
+         c0:e6:4c:f8:ad:02:23:75:51:fc:f8:69:05:ec:cf:31:50:5a:
+         41:78:eb:3d:27:4d:9b:68:ef:ba:0e:ba:3a:7d:60:00:9d:53:
+         a5:08:3d:c6
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIQE67a2PQY13O4vTXJzo6z/DANBgkqhkiG9w0BAQsFADAR
+MQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMTkwNjA2MTkxMTE5WhcNMjIwNTIxMTkxMTE5
+WjAQMQ4wDAYDVQQDDAVTdWJDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALAJ/FTnap8Mva1aje+UThGmhxlPv6bhYqUttxffZ1Nw2v59mRfuE0cLQAui
+NDKp078g/BN3odUmYB/w1L7cdnwebLRMAXxWzVxT7IGzgSqyNSYGWnngs57kV+EJ
+3q1/yM2H7kmTMFJYsrwPwbYQRPiF1VsKmyj+9PRKFqb3JemWR2lzWzN3kn1hjSo9
+1QSJQL9r0v1d4hqAqY7IkvblTACE+W4qk6Mj7igjgfRU8Bgs7jKOOJygyDMEsPxM
+QxpcBISfc8YIxx1kOf5yGTvMpf0LQyUNK6mId55i5qzCmmBCT0pUR7ygKXJ8OFLJ
+6ifFPdCBSj64eHlLibhObRskFb0CAwEAAaOBwDCBvTAzBgNVHR8ELDAqMCigJqAk
+hiJodHRwOi8vMTI3LjAuMC4xOjgwODEvcGtpL3Rlc3QuY3JsMAwGA1UdEwQFMAMB
+Af8wHQYDVR0OBBYEFP2CRTmhkUHyZswNddUNQNWBp6FDMEwGA1UdIwRFMEOAFMXM
+9aGM2eSnuuwh9dGEI+oNwscwoRWkEzARMQ8wDQYDVQQDDAZUZXN0Q0GCFDPnBIcJ
+Moch2c18qkxauyxse1QoMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA
+lqD/iku9RZbJcjxj40jEq+9+23Y/2QKeacjZNlXh9ZvJadhpAqxQjGCULC65qGWs
+9QCwi5YlC4rvlCFX4gTCw4a/Bk6RXOa8GwMxi2TqxXnDXJTlqmd+dBIHFP3NMgIm
+JskK7dTa7iqE4/Fgswl3J6E8rOxhGDC1bR8WCiQazxwbYKVg5SyLzzeDDBXneTA/
+7lBFfEvGLM0sgQqY8WVEesoqIBreGdlLyqHipLUUR7+0aBUDwFXl9EcOVZ/+hdgs
+fdAalhG5aLd0HmGUwa6HUi3GJrpR7fGRwOZM+K0CI3VR/PhpBezPMVBaQXjrPSdN
+m2jvug66On1gAJ1TpQg9xg==
+-----END CERTIFICATE-----
diff --git a/test/certv3.pem b/test/certv3.pem
new file mode 100644
index 0000000..8194091
--- /dev/null
+++ b/test/certv3.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIIDwTCCAqmgAwIBAgIUDeCGNunyJfBd3U/qUtmCcvbMyZwwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzAxMjMxMzMzNTJaFw0zMzAx
+MjAxMzMzNTJaMFoxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEzARBgNVBAMMCmZvb2Jh
+ci5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo9CWMRLMXo1CF
+/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0
+kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS
+wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM
+jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT
+Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt
+OzboKgs1AgMBAAGjgZMwgZAwTgYDVR0fBEcwRTAgoB6gHIYaaHR0cDovL2V4YW1w
+bGUuY29tL2NybC5wZW0wIaAfoB2GG2h0dHA6Ly9leGFtcGxlLmNvbS9jcmwyLnBl
+bTAdBgNVHQ4EFgQUtnMvYaVLoe9ILBWxn/PcNC+8rDAwHwYDVR0jBBgwFoAUbNOl
+A6sNXyzJjYqciKeId7g3/ZowDQYJKoZIhvcNAQELBQADggEBAJZyk6Eo4p3JIyOt
+7t6ET3K18BKvlRilze+zrGkaQYvKRsP6YzbZWgcIq59hy5VeFCX5O2WP91CPG3MU
+I9eRiih66/ry3G4I8QEdpRKnn0N5unbGjb5qPT5wXrhU4IO+vn3sGZGM4uIM1/3K
+N/bOh9CTsu9YqrdHSGeDyNzCy/XZ/j5bP4aNm31ZDNCZDFsbjr3/yTLcpHPL0UP3
+mCX8D16BDu1Nep+wK9VRuOEw6Z9tlT/VjTImzoOUoJO/o2UHfSHahX+n2aC5OpI6
+BdhaFBuJ1vn+yTWf3zIjhWUdp9TlzgRyFiyetP2FcKwremVVGdDq/Y6dfXaq8CA1
+6Fr9KTY=
+-----END CERTIFICATE-----
diff --git a/test/certv3_extfile b/test/certv3_extfile
new file mode 100644
index 0000000..1b3df49
--- /dev/null
+++ b/test/certv3_extfile
@@ -0,0 +1 @@
+crlDistributionPoints=URI:http://example.com/crl.pem,URI:http://example.com/crl2.pem
diff --git a/test/crl-ca.crt b/test/crl-ca.crt
new file mode 100644
index 0000000..a4a9075
--- /dev/null
+++ b/test/crl-ca.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPDCCAiSgAwIBAgIUM+cEhwkyhyHZzXyqTFq7LGx7VCgwDQYJKoZIhvcNAQEL
+BQAwETEPMA0GA1UEAwwGVGVzdENBMB4XDTE5MDYwNjE5MTA1NVoXDTI5MDYwMzE5
+MTA1NVowETEPMA0GA1UEAwwGVGVzdENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAtNcFPtD1MHcolhgTHIAx/b9OyawCbVzvgasv8R9+94ZMhoGc/tNc
+dVg271pCSmj+zYAFYsIwjxW+iq2e5A/fiBc6uqtNfEbU7+77QzxFG5wIbXtmmqEb
+dVbqBT28NeKTR6X+EHlNgbw90CHy7byA7LMewxbTt2q1eY1RnB0ji8zdGZmIUPeC
+WxzkxXEd0fg+KwBFN3YHV9CJX2KJ10qv7DvbKHeIVBU7osm6tzvNglNnnT90GFSY
+zc59b+zS00axcY3Kn08Vt+1qWB9Sl8tixCTGqR538y/ambDr3NCWsiQYWys9KE1L
+g0nEaIjb84R7b+qNmPtOezd9tanx7j9UzQIDAQABo4GLMIGIMB0GA1UdDgQWBBTF
+zPWhjNnkp7rsIfXRhCPqDcLHMDBMBgNVHSMERTBDgBTFzPWhjNnkp7rsIfXRhCPq
+DcLHMKEVpBMwETEPMA0GA1UEAwwGVGVzdENBghQz5wSHCTKHIdnNfKpMWrssbHtU
+KDAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA
+gdyQq6F8DO5rn7rZSLehTFx6tbtfncC/BOXZEGLZO0ciTrQ9Q8xHwRhz0W09QE1A
+/GsBzb++PuvAl9i82WvunyPB5KZh+GPiaaqf466MdQrXj+IyqxeC9Lg9wEUjwRgp
+ANVd3moKap5IZ9WDvhyEng2Oy8/btP2iqVEmd58rGAodd671eOPD8QkIxSquiIwy
+Cu5s3IBZ0BOuSG9fWoyPTGMKAhzQPFiXGvWOabCkMz3TsPYVY5ENpq2K8cWn2D/r
+TD1yPPdINg6HrALGD3S0sD+k588oS7U5oj1L8V4KJQTLSbh6/XcBpasa5Jdv7ZZe
+lVgt69Gsn5Cf2BkbwhbF2Q==
+-----END CERTIFICATE-----
diff --git a/test/entry_extensions.crl b/test/entry_extensions.crl
new file mode 100644
index 0000000..5b0ee29
--- /dev/null
+++ b/test/entry_extensions.crl
@@ -0,0 +1,11 @@
+-----BEGIN X509 CRL-----
+MIIBojCCAUkCAQEwCgYIKoZIzj0EAwIwHTEbMBkGA1UEAwwSY3J5cHRvZ3JhcGh5
+LmlvIENBFw0yMzA3MjUxNDA1MzlaFw0yMzA4MDExNDA1MzlaMIGAMH4CFE+Y95/1
+pOqa6c9fUEJ8c04kxu2PFw0yMzA3MjUxNDA1MzlaMFcwLwYDVR0dAQH/BCUwI6Qh
+MB8xCzAJBgNVBAYTAkdCMRAwDgYDVQQDDAdUZXN0IENBMAoGA1UdFQQDCgEBMBgG
+A1UdGAQRGA8yMDIzMDcyNTE0MDUzOVqgeDB2MB8GA1UdIwQYMBaAFK6qKNgsGefh
+XexO9WsIwiQ/73R8MAoGA1UdFAQDAgEUMAwGA1UdHAQFMAOEAf8wOQYIKwYBBQUH
+AQEELTArMCkGCCsGAQUFBzAChh1odHRwOi8vd3d3LmV4YW1wbGUuY29tL2NhLmNy
+dDAKBggqhkjOPQQDAgNHADBEAiB22SXxFnQUB41uxfyCvg2dAs2nFiR0r8jft/cd
+G8zcKAIgeYkNOzRn4lyopK6J94rhm8jIIuJRj3Ns9XcH+91N370=
+-----END X509 CRL-----
diff --git a/test/subca.crt b/test/subca.crt
new file mode 100644
index 0000000..a0a8ab2
--- /dev/null
+++ b/test/subca.crt
@@ -0,0 +1,88 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number:
+            13:ae:da:d8:f4:18:d7:73:b8:bd:35:c9:ce:8e:b3:fc
+        Signature Algorithm: sha256WithRSAEncryption
+        Issuer: CN=TestCA
+        Validity
+            Not Before: Jun  6 19:11:19 2019 GMT
+            Not After : May 21 19:11:19 2022 GMT
+        Subject: CN=SubCA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                RSA Public-Key: (2048 bit)
+                Modulus:
+                    00:b0:09:fc:54:e7:6a:9f:0c:bd:ad:5a:8d:ef:94:
+                    4e:11:a6:87:19:4f:bf:a6:e1:62:a5:2d:b7:17:df:
+                    67:53:70:da:fe:7d:99:17:ee:13:47:0b:40:0b:a2:
+                    34:32:a9:d3:bf:20:fc:13:77:a1:d5:26:60:1f:f0:
+                    d4:be:dc:76:7c:1e:6c:b4:4c:01:7c:56:cd:5c:53:
+                    ec:81:b3:81:2a:b2:35:26:06:5a:79:e0:b3:9e:e4:
+                    57:e1:09:de:ad:7f:c8:cd:87:ee:49:93:30:52:58:
+                    b2:bc:0f:c1:b6:10:44:f8:85:d5:5b:0a:9b:28:fe:
+                    f4:f4:4a:16:a6:f7:25:e9:96:47:69:73:5b:33:77:
+                    92:7d:61:8d:2a:3d:d5:04:89:40:bf:6b:d2:fd:5d:
+                    e2:1a:80:a9:8e:c8:92:f6:e5:4c:00:84:f9:6e:2a:
+                    93:a3:23:ee:28:23:81:f4:54:f0:18:2c:ee:32:8e:
+                    38:9c:a0:c8:33:04:b0:fc:4c:43:1a:5c:04:84:9f:
+                    73:c6:08:c7:1d:64:39:fe:72:19:3b:cc:a5:fd:0b:
+                    43:25:0d:2b:a9:88:77:9e:62:e6:ac:c2:9a:60:42:
+                    4f:4a:54:47:bc:a0:29:72:7c:38:52:c9:ea:27:c5:
+                    3d:d0:81:4a:3e:b8:78:79:4b:89:b8:4e:6d:1b:24:
+                    15:bd
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 CRL Distribution Points: 
+
+                Full Name:
+                  URI:http://127.0.0.1:8081/pki/test.crl
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+            X509v3 Subject Key Identifier: 
+                FD:82:45:39:A1:91:41:F2:66:CC:0D:75:D5:0D:40:D5:81:A7:A1:43
+            X509v3 Authority Key Identifier: 
+                keyid:C5:CC:F5:A1:8C:D9:E4:A7:BA:EC:21:F5:D1:84:23:EA:0D:C2:C7:30
+                DirName:/CN=TestCA
+                serial:33:E7:04:87:09:32:87:21:D9:CD:7C:AA:4C:5A:BB:2C:6C:7B:54:28
+
+            X509v3 Key Usage: 
+                Certificate Sign, CRL Sign
+    Signature Algorithm: sha256WithRSAEncryption
+         96:a0:ff:8a:4b:bd:45:96:c9:72:3c:63:e3:48:c4:ab:ef:7e:
+         db:76:3f:d9:02:9e:69:c8:d9:36:55:e1:f5:9b:c9:69:d8:69:
+         02:ac:50:8c:60:94:2c:2e:b9:a8:65:ac:f5:00:b0:8b:96:25:
+         0b:8a:ef:94:21:57:e2:04:c2:c3:86:bf:06:4e:91:5c:e6:bc:
+         1b:03:31:8b:64:ea:c5:79:c3:5c:94:e5:aa:67:7e:74:12:07:
+         14:fd:cd:32:02:26:26:c9:0a:ed:d4:da:ee:2a:84:e3:f1:60:
+         b3:09:77:27:a1:3c:ac:ec:61:18:30:b5:6d:1f:16:0a:24:1a:
+         cf:1c:1b:60:a5:60:e5:2c:8b:cf:37:83:0c:15:e7:79:30:3f:
+         ee:50:45:7c:4b:c6:2c:cd:2c:81:0a:98:f1:65:44:7a:ca:2a:
+         20:1a:de:19:d9:4b:ca:a1:e2:a4:b5:14:47:bf:b4:68:15:03:
+         c0:55:e5:f4:47:0e:55:9f:fe:85:d8:2c:7d:d0:1a:96:11:b9:
+         68:b7:74:1e:61:94:c1:ae:87:52:2d:c6:26:ba:51:ed:f1:91:
+         c0:e6:4c:f8:ad:02:23:75:51:fc:f8:69:05:ec:cf:31:50:5a:
+         41:78:eb:3d:27:4d:9b:68:ef:ba:0e:ba:3a:7d:60:00:9d:53:
+         a5:08:3d:c6
+-----BEGIN CERTIFICATE-----
+MIIDbDCCAlSgAwIBAgIQE67a2PQY13O4vTXJzo6z/DANBgkqhkiG9w0BAQsFADAR
+MQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMTkwNjA2MTkxMTE5WhcNMjIwNTIxMTkxMTE5
+WjAQMQ4wDAYDVQQDDAVTdWJDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALAJ/FTnap8Mva1aje+UThGmhxlPv6bhYqUttxffZ1Nw2v59mRfuE0cLQAui
+NDKp078g/BN3odUmYB/w1L7cdnwebLRMAXxWzVxT7IGzgSqyNSYGWnngs57kV+EJ
+3q1/yM2H7kmTMFJYsrwPwbYQRPiF1VsKmyj+9PRKFqb3JemWR2lzWzN3kn1hjSo9
+1QSJQL9r0v1d4hqAqY7IkvblTACE+W4qk6Mj7igjgfRU8Bgs7jKOOJygyDMEsPxM
+QxpcBISfc8YIxx1kOf5yGTvMpf0LQyUNK6mId55i5qzCmmBCT0pUR7ygKXJ8OFLJ
+6ifFPdCBSj64eHlLibhObRskFb0CAwEAAaOBwDCBvTAzBgNVHR8ELDAqMCigJqAk
+hiJodHRwOi8vMTI3LjAuMC4xOjgwODEvcGtpL3Rlc3QuY3JsMAwGA1UdEwQFMAMB
+Af8wHQYDVR0OBBYEFP2CRTmhkUHyZswNddUNQNWBp6FDMEwGA1UdIwRFMEOAFMXM
+9aGM2eSnuuwh9dGEI+oNwscwoRWkEzARMQ8wDQYDVQQDDAZUZXN0Q0GCFDPnBIcJ
+Moch2c18qkxauyxse1QoMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA
+lqD/iku9RZbJcjxj40jEq+9+23Y/2QKeacjZNlXh9ZvJadhpAqxQjGCULC65qGWs
+9QCwi5YlC4rvlCFX4gTCw4a/Bk6RXOa8GwMxi2TqxXnDXJTlqmd+dBIHFP3NMgIm
+JskK7dTa7iqE4/Fgswl3J6E8rOxhGDC1bR8WCiQazxwbYKVg5SyLzzeDDBXneTA/
+7lBFfEvGLM0sgQqY8WVEesoqIBreGdlLyqHipLUUR7+0aBUDwFXl9EcOVZ/+hdgs
+fdAalhG5aLd0HmGUwa6HUi3GJrpR7fGRwOZM+K0CI3VR/PhpBezPMVBaQXjrPSdN
+m2jvug66On1gAJ1TpQg9xg==
+-----END CERTIFICATE-----
diff --git a/test/test.crl b/test/test.crl
new file mode 100644
index 0000000..aead062
--- /dev/null
+++ b/test/test.crl
Binary files differ