aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThiƩbaud Weksteen <tweek@google.com>2024-03-06 15:00:45 +1100
committerThiƩbaud Weksteen <tweek@google.com>2024-03-06 15:19:52 +1100
commit2b699022c2ebeefd293f8e27554cb346fadc21af (patch)
treedaf8bd94cbd892b85eea7ab956953757ffcce66e
parent9ca9c6b36596d00472f75c7b63ce855e38e719cc (diff)
downloadopenssl-2b699022c2ebeefd293f8e27554cb346fadc21af.tar.gz
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
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp8
-rw-r--r--CHANGELOG.md250
-rw-r--r--Cargo.lock263
-rw-r--r--Cargo.toml6
-rw-r--r--Cargo.toml.orig6
-rw-r--r--METADATA25
-rw-r--r--build.rs115
-rw-r--r--examples/mk_certs.rs2
-rw-r--r--src/asn1.rs192
-rw-r--r--src/bio.rs8
-rw-r--r--src/bn.rs75
-rw-r--r--src/cipher.rs171
-rw-r--r--src/cipher_ctx.rs277
-rw-r--r--src/cms.rs184
-rw-r--r--src/derive.rs49
-rw-r--r--src/dh.rs85
-rw-r--r--src/dsa.rs77
-rw-r--r--src/ec.rs93
-rw-r--r--src/ecdsa.rs6
-rw-r--r--src/encrypt.rs10
-rw-r--r--src/error.rs54
-rw-r--r--src/hash.rs27
-rw-r--r--src/lib.rs34
-rw-r--r--src/md.rs11
-rw-r--r--src/md_ctx.rs2
-rw-r--r--src/nid.rs39
-rw-r--r--src/ocsp.rs2
-rw-r--r--src/pkcs12.rs177
-rw-r--r--src/pkcs5.rs26
-rw-r--r--src/pkcs7.rs141
-rw-r--r--src/pkey.rs152
-rw-r--r--src/pkey_ctx.rs363
-rw-r--r--src/provider.rs4
-rw-r--r--src/rand.rs36
-rw-r--r--src/sha.rs2
-rw-r--r--src/sign.rs46
-rw-r--r--src/srtp.rs10
-rw-r--r--src/ssl/callbacks.rs9
-rw-r--r--src/ssl/connector.rs5
-rw-r--r--src/ssl/mod.rs714
-rw-r--r--src/ssl/test/mod.rs285
-rw-r--r--src/symm.rs210
-rw-r--r--src/x509/extension.rs182
-rw-r--r--src/x509/mod.rs991
-rw-r--r--src/x509/store.rs55
-rw-r--r--src/x509/tests.rs519
-rw-r--r--src/x509/verify.rs118
-rw-r--r--test/authority_key_identifier.pem19
-rw-r--r--test/ca.crt88
-rw-r--r--test/certv3.pem23
-rw-r--r--test/certv3_extfile1
-rw-r--r--test/crl-ca.crt20
-rw-r--r--test/entry_extensions.crl11
-rw-r--r--test/subca.crt88
-rw-r--r--test/test.crlbin0 -> 469 bytes
56 files changed, 5443 insertions, 925 deletions
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 @@ rust_library {
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 @@ rust_library {
"soong",
],
rustlibs: [
- "libbitflags-1.3.2",
+ "libbitflags",
"libbssl_sys",
"libcfg_if",
"libforeign_types",
@@ -79,7 +79,7 @@ rust_library_host_rlib {
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 @@ rust_library_host_rlib {
"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 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
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 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[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 @@ dependencies = [
]
[[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"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
-
-[[package]]
-name = "env_logger"
-version = "0.9.3"
+version = "1.9.0"
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 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "glob"
-version = "0.3.0"
-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"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hex"
@@ -179,22 +123,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
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 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[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 @@ dependencies = [
[[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 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[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 @@ dependencies = [
[[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 @@ dependencies = [
[[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 @@ dependencies = [
[[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 @@ dependencies = [
]
[[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 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pkg-config"
-version = "0.3.26"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
+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 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "shlex"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
-
-[[package]]
-name = "strsim"
-version = "0.10.0"
+version = "1.2.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 @@ dependencies = [
]
[[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"
+name = "unicode-ident"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
-name = "unicode-ident"
-version = "1.0.6"
+name = "unicode-width"
+version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "vcpkg"
@@ -420,9 +331,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[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 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
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 @@ license = "Apache-2.0"
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 @@ unstable_boringssl = ["ffi/unstable_boringssl"]
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,100 +11,107 @@ fn main() {
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 let Ok(vars) = env::var("DEP_OPENSSL_CONF") {
- for var in vars.split(',') {
- println!("cargo:rustc-cfg=osslconf=\"{}\"", var);
- }
- }
-
- if let Ok(version) = env::var("DEP_OPENSSL_VERSION_NUMBER") {
- let version = u64::from_str_radix(&version, 16).unwrap();
-
- if version >= 0x1_00_01_00_0 {
- println!("cargo:rustc-cfg=ossl101");
- }
- if version >= 0x1_00_02_00_0 {
- println!("cargo:rustc-cfg=ossl102");
+ if version >= 0x2_05_00_00_0 {
+ println!("cargo:rustc-cfg=libressl250");
}
- if version >= 0x1_01_00_00_0 {
- println!("cargo:rustc-cfg=ossl110");
- }
- if version >= 0x1_01_00_07_0 {
- println!("cargo:rustc-cfg=ossl110g");
- }
- if version >= 0x1_01_00_08_0 {
- println!("cargo:rustc-cfg=ossl110h");
- }
- if version >= 0x1_01_01_00_0 {
- println!("cargo:rustc-cfg=ossl111");
- }
- 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 >= 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") {
+ for var in vars.split(',') {
+ println!("cargo:rustc-cfg=osslconf=\"{}\"", var);
+ }
+ }
+
+ if let Ok(version) = env::var("DEP_OPENSSL_VERSION_NUMBER") {
+ let version = u64::from_str_radix(&version, 16).unwrap();
+
+ if version >= 0x1_00_01_00_0 {
+ println!("cargo:rustc-cfg=ossl101");
+ }
+ if version >= 0x1_00_02_00_0 {
+ println!("cargo:rustc-cfg=ossl102");
+ }
+ if version >= 0x1_01_00_00_0 {
+ println!("cargo:rustc-cfg=ossl110");
+ }
+ if version >= 0x1_01_00_07_0 {
+ println!("cargo:rustc-cfg=ossl110g");
+ }
+ if version >= 0x1_01_00_08_0 {
+ println!("cargo:rustc-cfg=ossl110h");
+ }
+ if version >= 0x1_01_01_00_0 {
+ println!("cargo:rustc-cfg=ossl111");
+ }
+ if version >= 0x3_00_00_00_0 {
+ println!("cargo:rustc-cfg=ossl300");
+ }
+ if version >= 0x3_01_00_00_0 {
+ println!("cargo:rustc-cfg=ossl310");
+ }
+ 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::bio::MemBio;
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 @@ impl Asn1Type {
/// [`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 @@ foreign_type_and_impl_send_sync! {
/// [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 @@ foreign_type_and_impl_send_sync! {
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 @@ impl Asn1TimeRef {
/// 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 @@ impl Asn1TimeRef {
}
}
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
impl PartialEq for Asn1TimeRef {
fn eq(&self, other: &Asn1TimeRef) -> bool {
self.diff(other)
@@ -237,7 +238,7 @@ impl PartialEq for Asn1TimeRef {
}
}
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
impl PartialEq<Asn1Time> for Asn1TimeRef {
fn eq(&self, other: &Asn1Time) -> bool {
self.diff(other)
@@ -246,7 +247,7 @@ impl PartialEq<Asn1Time> for Asn1TimeRef {
}
}
-#[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 @@ impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef {
}
}
-#[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 @@ impl Asn1Time {
/// 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 @@ impl Asn1Time {
}
}
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
impl PartialEq for Asn1Time {
fn eq(&self, other: &Asn1Time) -> bool {
self.diff(other)
@@ -375,7 +376,7 @@ impl PartialEq for Asn1Time {
}
}
-#[cfg(ossl102)]
+#[cfg(any(ossl102, boringssl))]
impl PartialEq<Asn1TimeRef> for Asn1Time {
fn eq(&self, other: &Asn1TimeRef) -> bool {
self.diff(other)
@@ -384,7 +385,7 @@ impl PartialEq<Asn1TimeRef> for Asn1Time {
}
}
-#[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 @@ impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time {
}
}
-#[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 @@ foreign_type_and_impl_send_sync! {
/// 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 @@ foreign_type_and_impl_send_sync! {
/// 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 @@ impl Asn1Integer {
/// 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 @@ impl Asn1IntegerRef {
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 @@ impl Asn1BitStringRef {
}
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 @@ foreign_type_and_impl_send_sync! {
///
/// [`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 @@ cfg_if! {
}
}
+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 @@ mod tests {
}
#[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 @@ mod tests {
}
#[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 @@ mod tests {
}
#[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 @@ mod tests {
&[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::marker::PhantomData;
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 @@ impl<'a> MemBioSlice<'a> {
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 @@ impl MemBio {
}
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 @@ foreign_type_and_impl_send_sync! {
/// 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 @@ foreign_type_and_impl_send_sync! {
///
/// [`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 @@ impl BigNumRef {
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 @@ impl BigNumRef {
}
}
+ /// 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 @@ impl BigNumRef {
/// 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 @@ impl BigNum {
///
/// 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 Ord for BigNumRef {
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 @@ mod tests {
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 foreign_types::{ForeignTypeRef, Opaque};
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! {
cfg_if! {
if #[cfg(ossl300)] {
use foreign_types::ForeignType;
- use std::ops::{Deref, DerefMut};
type Inner = *mut ffi::EVP_CIPHER;
@@ -90,6 +90,22 @@ cfg_if! {
}
} 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 @@ impl Cipher {
}
#[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 @@ impl Cipher {
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 @@ impl Cipher {
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 @@ impl Cipher {
}
/// 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 @@ impl Cipher {
}
/// 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 @@ impl Cipher {
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 @@ use crate::error::ErrorStack;
#[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 @@ foreign_type_and_impl_send_sync! {
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 CipherCtx {
}
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 @@ impl CipherCtxRef {
/// # 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 @@ impl CipherCtxRef {
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 @@ impl CipherCtxRef {
/// # 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 @@ impl CipherCtxRef {
/// 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 @@ impl CipherCtxRef {
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 @@ impl CipherCtxRef {
/// 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 @@ mod test {
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 @@ mod test {
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 @@ mod test {
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::error::ErrorStack;
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 @@ impl CmsContentInfo {
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 @@ mod test {
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 @@ mod test {
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 @@ mod test {
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 @@ mod test {
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 std::ptr;
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 @@ impl<'a> Deriver<'a> {
///
/// 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 @@ impl<'a> Deriver<'a> {
}
/// 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 @@ impl<'a> Deriver<'a> {
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 @@ impl<'a> Deriver<'a> {
/// 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 @@ impl<'a> Deriver<'a> {
///
/// 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 @@ mod test {
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 std::ptr;
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 @@ where
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 @@ impl Dh<Params> {
}
}
+ /// 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 @@ impl Dh<Params> {
}
}
+ /// 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 @@ cfg_if! {
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 @@ mod tests {
}
#[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 @@ mod tests {
}
#[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 @@ mod tests {
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 std::ptr;
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 @@ generic_foreign_type_and_impl_send_sync! {
///
/// 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 @@ where
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 @@ type BitType = libc::c_uint;
#[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 @@ impl Dsa<Private> {
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 @@ mod test {
}
#[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::util::ForeignTypeRefExt;
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 @@ impl PointConversionForm {
/// 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 @@ impl Asn1Flag {
///
/// 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 @@ impl EcGroupRef {
/// 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 @@ impl EcGroupRef {
}
}
+ /// 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 @@ impl EcPointRef {
}
}
+ /// 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 @@ impl EcPointRef {
/// 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 @@ impl EcPointRef {
/// 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 @@ impl EcPoint {
}
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 @@ mod test {
}
#[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 @@ mod test {
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 @@ mod test {
}
#[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 @@ mod test {
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 @@ mod test {
}
#[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 @@ mod test {
}
#[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 @@ mod test {
}
#[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 @@ impl<'a> Encrypter<'a> {
///
/// 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 @@ impl<'a> Encrypter<'a> {
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 @@ impl<'a> Decrypter<'a> {
///
/// 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 @@ impl<'a> Decrypter<'a> {
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 @@ impl Error {
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 @@ impl Error {
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 @@ impl Error {
}
}
+ /// 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 @@ impl Error {
}
}
+ /// 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::Debug for Error {
}
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_if! {
}
}
}
+
+#[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::nid::Nid;
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 @@ impl MessageDigest {
///
/// 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 @@ impl MessageDigest {
///
/// 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 @@ impl MessageDigest {
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 @@ impl MessageDigest {
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 @@ mod tests {
);
}
- #[cfg(ossl111)]
+ #[cfg(any(ossl111, libressl380))]
#[test]
fn test_sha3_224() {
let tests = [(
@@ -644,7 +645,7 @@ mod tests {
);
}
- #[cfg(ossl111)]
+ #[cfg(any(ossl111, libressl380))]
#[test]
fn test_sha3_256() {
let tests = [(
@@ -664,7 +665,7 @@ mod tests {
);
}
- #[cfg(ossl111)]
+ #[cfg(any(ossl111, libressl380))]
#[test]
fn test_sha3_384() {
let tests = [("416c6c20796f75722062617365206172652062656c6f6e6720746f207573",
@@ -684,7 +685,7 @@ mod tests {
);
}
- #[cfg(ossl111)]
+ #[cfg(any(ossl111, libressl380))]
#[test]
fn test_sha3_512() {
let tests = [("416c6c20796f75722062617365206172652062656c6f6e6720746f207573",
@@ -745,7 +746,7 @@ mod tests {
}
#[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 @@ extern crate bssl_sys as ffi;
pub use ffi::init;
use libc::c_int;
+#[cfg(ossl300)]
+use libc::c_long;
use crate::error::ErrorStack;
@@ -142,7 +148,7 @@ pub mod base64;
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;
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::size_t;
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 @@ fn cvt(r: c_int) -> Result<c_int, ErrorStack> {
}
}
+// 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 @@ impl Md {
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 @@ impl Md {
}
#[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::convert::TryFrom;
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 @@ pub struct SignatureAlgorithms {
/// 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 @@ impl Nid {
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 @@ impl Nid {
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 @@ mod test {
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 crate::{cvt, cvt_p};
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 @@ impl Pkcs12Ref {
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 chain = Stack::from_ptr_opt(chain);
+ let pkey = PKey::from_ptr_opt(pkey);
+ let cert = X509::from_ptr_opt(cert);
+ let ca = Stack::from_ptr_opt(ca);
- Ok(ParsedPkcs12 { pkey, cert, chain })
+ Ok(ParsedPkcs12_2 { pkey, cert, ca })
}
}
}
@@ -82,34 +94,78 @@ impl Pkcs12 {
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 @@ impl Pkcs12Builder {
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 @@ impl Pkcs12Builder {
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 @@ impl Pkcs12Builder {
// 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 @@ impl Pkcs12Builder {
cvt(ffi::PKCS12_set_mac(
pkcs12.as_ptr(),
- pass.as_ptr(),
+ pass,
-1,
ptr::null_mut(),
0,
@@ -246,14 +301,25 @@ mod test {
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 @@ mod test {
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 @@ mod test {
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 @@ pub struct KeyIvPair {
/// `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 @@ pub fn pbkdf2_hmac(
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 @@ pub fn pbkdf2_hmac(
///
/// 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 @@ pub fn scrypt(
n,
r,
p,
- maxmem,
+ maxmem.try_into().unwrap(),
key.as_mut_ptr() as *mut _,
key.len(),
))
@@ -145,6 +147,7 @@ pub fn scrypt(
#[cfg(test)]
mod tests {
use crate::hash::MessageDigest;
+ #[cfg(not(boringssl))]
use crate::symm::Cipher;
// Test vectors from
@@ -246,6 +249,7 @@ mod tests {
}
#[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 @@ mod tests {
}
#[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 libc::c_int;
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 @@ foreign_type_and_impl_send_sync! {
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 @@ impl Pkcs7 {
certs.as_ptr(),
input_bio.as_ptr(),
cipher.as_ptr(),
- flags.bits,
+ flags.bits(),
))
.map(Pkcs7)
}
@@ -141,7 +171,7 @@ impl Pkcs7 {
pkey.as_ptr(),
certs.as_ptr(),
input_bio.as_ptr(),
- flags.bits,
+ flags.bits(),
))
.map(Pkcs7)
}
@@ -159,7 +189,7 @@ impl Pkcs7Ref {
output.as_ptr(),
self.as_ptr(),
input_bio.as_ptr(),
- flags.bits,
+ flags.bits(),
))
.map(|_| output.get_buf().to_owned())
}
@@ -205,7 +235,7 @@ impl Pkcs7Ref {
pkey.as_ptr(),
cert.as_ptr(),
output.as_ptr(),
- flags.bits,
+ flags.bits(),
))
.map(|_| output.get_buf().to_owned())
}
@@ -241,7 +271,7 @@ impl Pkcs7Ref {
store.as_ptr(),
indata_bio_ptr,
out_bio.as_ptr(),
- flags.bits,
+ flags.bits(),
))
.map(|_| ())?
}
@@ -265,7 +295,7 @@ impl Pkcs7Ref {
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 @@ impl Pkcs7Ref {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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::dh::Dh;
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 cfg_if::cfg_if;
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 @@ pub struct Id(c_int);
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
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 @@ where
/// 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 @@ where
/// 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 @@ where
}
}
+ /// 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 @@ where
) -> 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 @@ impl<T> PKey<T> {
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 @@ impl<T> PKey<T> {
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 @@ impl<T> PKey<T> {
/// 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 @@ impl<T> PKey<T> {
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 @@ impl PKey<Private> {
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 @@ impl PKey<Private> {
/// 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 @@ impl PKey<Private> {
/// 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 @@ impl PKey<Private> {
///
/// 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<Private> {
}
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 @@ impl PKey<Public> {
///
/// 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 @@ impl<T> TryFrom<PKey<T>> for Dsa<T> {
}
}
+#[cfg(not(boringssl))]
impl<T> TryFrom<Dh<T>> for PKey<T> {
type Error = ErrorStack;
@@ -866,6 +903,7 @@ mod tests {
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 @@ mod tests {
#[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 @@ mod tests {
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 @@ mod tests {
);
}
- #[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 @@ mod tests {
// 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 @@ mod tests {
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::error::ErrorStack;
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 @@ where
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 @@ where
#[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 @@ impl<T> PkeyCtxRef<T> {
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 @@ impl<T> PkeyCtxRef<T> {
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 @@ impl<T> PkeyCtxRef<T> {
///
/// 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 @@ impl<T> PkeyCtxRef<T> {
///
/// 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 @@ impl<T> PkeyCtxRef<T> {
///
/// 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 @@ impl<T> PkeyCtxRef<T> {
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 @@ impl<T> PkeyCtxRef<T> {
/// 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 @@ impl<T> PkeyCtxRef<T> {
/// 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 @@ impl<T> PkeyCtxRef<T> {
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 @@ mod test {
#[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 @@ mod test {
}
#[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 @@ mod test {
}
#[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 @@ mod test {
}
#[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 @@ mod test {
}
#[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 @@ mod test {
}
#[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 @@ mod test {
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 @@ impl Provider {
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 @@ pub fn rand_bytes(buf: &mut [u8]) -> Result<(), ErrorStack> {
}
}
+/// 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 @@ pub fn keep_random_devices_open(keep: bool) {
#[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 @@ pub fn sha1(data: &[u8]) -> [u8; 20] {
}
/// 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::rsa::Padding;
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 @@ pub struct RsaPssSaltlen(c_int);
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 @@ pub struct Signer<'a> {
_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 @@ impl<'a> Drop for Signer<'a> {
}
#[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 @@ impl<'a> Signer<'a> {
/// 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 @@ impl<'a> Signer<'a> {
/// 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 @@ impl<'a> Signer<'a> {
///
/// 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 @@ impl<'a> Signer<'a> {
///
/// 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 @@ impl<'a> Signer<'a> {
///
/// 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 @@ impl<'a> Signer<'a> {
}
}
- #[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 @@ impl<'a> Signer<'a> {
///
/// 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 @@ impl<'a> Signer<'a> {
/// 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 @@ impl<'a> Signer<'a> {
/// 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 @@ impl<'a> Verifier<'a> {
///
/// 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 @@ impl<'a> Verifier<'a> {
///
/// 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 @@ impl<'a> Verifier<'a> {
/// 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 @@ mod test {
#[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 @@ mod test {
}
#[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 @@ impl SrtpProfileId {
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 @@ where
};
// 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 @@ where
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 @@ where
.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 @@ where
.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 @@ where
.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 @@ where
.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 @@ where
.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 @@ where
.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 @@ where
.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 @@ use crate::ssl::{
SslOptions, SslRef, SslStream, SslVerifyMode,
};
use crate::version;
+use std::net::IpAddr;
const FFDHE_2048: &str = "
-----BEGIN DH PARAMETERS-----
@@ -177,9 +178,9 @@ impl ConnectConfiguration {
/// 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 @@ use crate::hash::MessageDigest;
#[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 libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void};
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 @@ cfg_if! {
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! {
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 @@ unsafe impl Send for SslMethod {}
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 @@ type SslTimeTy = c_long;
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 @@ bitflags! {
#[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 @@ impl AlpnError {
/// 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 @@ impl SslVersion {
/// 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 @@ impl SslContextBuilder {
#[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 @@ impl SslContextBuilder {
{
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 @@ impl SslContextBuilder {
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 @@ impl SslContextBuilder {
///
/// 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 @@ impl SslContextBuilder {
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 @@ impl SslContextBuilder {
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 @@ impl SslContextBuilder {
/// 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 @@ impl SslContextBuilder {
/// 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 @@ impl SslContextBuilder {
/// 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 @@ impl SslContextBuilder {
/// 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 @@ impl SslContextBuilder {
/// 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 @@ impl SslContextBuilder {
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 @@ impl SslContextBuilder {
///
/// 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 @@ impl SslContextBuilder {
/// 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 @@ impl SslContextBuilder {
}
}
+ /// 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 @@ impl SslContextRef {
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 ForeignType for SslCipher {
}
}
+impl Stackable for SslCipher {
+ type StackType = ffi::stack_st_SSL_CIPHER;
+}
+
impl Deref for SslCipher {
type Target = SslCipherRef;
@@ -2025,6 +2111,19 @@ impl SslCipherRef {
}
}
+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 @@ impl SslSessionRef {
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 @@ impl Ssl {
///
/// 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 @@ impl SslRef {
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 @@ impl SslRef {
/// [`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 @@ impl SslRef {
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 @@ impl SslRef {
///
/// 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 @@ impl SslRef {
/// 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 @@ impl SslRef {
/// 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 @@ impl SslRef {
/// 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 @@ impl SslRef {
/// 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 @@ impl SslRef {
response.len() as c_long,
) as c_int)
.map(|_| ())
+ .map_err(|e| {
+ ffi::OPENSSL_free(p);
+ e
+ })
}
}
@@ -2876,15 +2964,19 @@ impl SslRef {
///
/// 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 @@ impl SslRef {
}
}
+ /// 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 @@ impl SslRef {
}
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 @@ impl<S: Read + Write> SslStream<S> {
}
}
+ /// 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 @@ impl<S: Read + Write> SslStream<S> {
///
/// 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,
+ )
+ };
+
+ if ret > 0 {
+ Ok(written)
+ } else {
+ Err(self.make_error(ret))
+ }
+ } else {
+ if buf.is_empty() {
+ return Ok(0);
+ }
- let ret = self.ssl.write(buf);
- if ret > 0 {
- Ok(ret as usize)
- } else {
- Err(self.make_error(ret))
+ 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,
+ )
+ };
+
+ if ret > 0 {
+ Ok(readbytes)
+ } else {
+ Err(self.make_error(ret))
+ }
+ } else {
+ if buf.is_empty() {
+ return Ok(0);
+ }
- let ret = self.ssl.peek(buf);
- if ret > 0 {
- Ok(ret as usize)
- } else {
- Err(self.make_error(ret))
+ 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 @@ impl<S: Read + Write> SslStream<S> {
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> SslStream<S> {
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 @@ pub enum ShutdownResult {
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::UdpSocket;
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::error::ErrorStack;
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 @@ fn verify_trusted_with_set_cert() {
#[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 @@ fn verify_untrusted_callback_override_bad() {
#[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 @@ fn verify_trusted_callback_override_ok() {
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 @@ fn verify_trusted_callback_override_bad() {
#[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 @@ fn verify_trusted_get_error_ok() {
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 @@ fn state() {
);
}
+// 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 @@ fn test_alpn_server_advertise_multiple() {
}
#[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 @@ fn test_alpn_server_select_none_fatal() {
#[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 @@ fn test_alpn_server_select_none() {
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 @@ fn refcount_ssl_context() {
{
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 @@ fn connector_no_hostname_still_verifies() {
}
#[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 @@ fn connector_no_hostname_can_disable_verify() {
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 @@ fn idle_session() {
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 @@ fn status_callbacks() {
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 @@ fn new_session_callback() {
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 @@ fn client_hello() {
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 @@ fn add_chain_cert() {
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 @@ pub enum Mode {
///
/// 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 @@ impl Cipher {
///
/// 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 @@ impl Cipher {
///
/// 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 @@ impl Cipher {
}
/// 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 @@ impl Cipher {
}
/// 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 @@ impl Cipher {
}
/// 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 @@ impl Cipher {
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 @@ impl Cipher {
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 @@ impl Cipher {
}
/// 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 @@ impl Crypter {
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 @@ mod tests {
}
#[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 @@ mod tests {
}
#[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 @@ mod tests {
}
#[test]
- #[cfg(any(ossl110))]
+ #[cfg(any(ossl110, libressl310))]
fn test_chacha20() {
let key = "0000000000000000000000000000000000000000000000000000000000000000";
let iv = "00000000000000000000000000000000";
@@ -1495,7 +1657,7 @@ mod tests {
}
#[test]
- #[cfg(any(ossl110))]
+ #[cfg(any(ossl110, libressl360))]
fn test_chacha20_poly1305() {
let key = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f";
let iv = "070000004041424344454647";
@@ -1536,7 +1698,7 @@ mod tests {
}
#[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 @@ mod tests {
}
#[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 @@ mod tests {
}
#[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 @@ mod tests {
}
#[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 @@ impl BasicConstraints {
}
/// 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 @@ impl KeyUsage {
}
/// 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 @@ impl KeyUsage {
/// 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 @@ impl ExtendedKeyUsage {
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 @@ impl ExtendedKeyUsage {
/// 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)?)?;
+ }
+ unsafe {
+ X509Extension::new_internal(Nid::EXT_KEY_USAGE, self.critical, stack.as_ptr().cast())
}
- X509Extension::new_nid(None, None, Nid::EXT_KEY_USAGE, &value)
}
}
@@ -393,6 +352,9 @@ impl SubjectKeyIdentifier {
}
/// 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 @@ impl AuthorityKeyIdentifier {
}
/// 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 @@ impl AuthorityKeyIdentifier {
}
}
+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 @@ impl SubjectAlternativeName {
pub fn new() -> SubjectAlternativeName {
SubjectAlternativeName {
critical: false,
- names: vec![],
+ items: vec![],
}
}
@@ -493,55 +467,85 @@ impl SubjectAlternativeName {
/// 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.
+ ///
+ /// 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.
- pub fn other_name(&mut self, other_name: &str) -> &mut SubjectAlternativeName {
- self.names.push(format!("otherName:{}", other_name));
+ ///
+ /// `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)?;
+ }
+
+ unsafe {
+ X509Extension::new_internal(Nid::SUBJECT_ALT_NAME, self.critical, stack.as_ptr().cast())
}
- X509Extension::new_nid(None, Some(ctx), Nid::SUBJECT_ALT_NAME, &value)
}
}
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::util::{ForeignTypeExt, ForeignTypeRefExt};
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 @@ pub mod store;
#[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 @@ impl X509StoreContextRef {
/// 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 @@ impl X509Builder {
/// 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 @@ foreign_type_and_impl_send_sync! {
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 @@ impl X509Ref {
/// 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 @@ impl X509Ref {
/// 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 @@ impl X509Ref {
}
}
+ /// 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 @@ impl X509Ref {
}
}
+ /// 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 @@ impl X509Ref {
/// 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 @@ impl X509Ref {
}
}
+ /// 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 @@ impl X509 {
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 Ord for X509 {
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 @@ impl X509Extension {
/// 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 @@ impl X509Extension {
) -> 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 @@ impl X509Extension {
/// 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 @@ impl X509Extension {
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 @@ impl X509NameBuilder {
}
}
+ /// 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 @@ impl X509NameBuilder {
///
/// 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 @@ impl X509NameBuilder {
) -> 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 @@ impl X509NameBuilder {
///
/// 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 @@ impl X509NameBuilder {
///
/// 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 @@ impl X509NameBuilder {
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 @@ impl X509NameBuilder {
/// 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 @@ impl X509NameRef {
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 @@ impl X509NameEntryRef {
///
/// 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 @@ impl X509NameEntryRef {
///
/// 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 @@ impl X509ReqBuilder {
///
/// 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 @@ impl X509ReqBuilder {
///
/// 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 @@ impl X509ReqBuilder {
///
/// 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 @@ impl X509ReqBuilder {
///
/// 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 @@ impl X509ReqBuilder {
///
/// 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 @@ impl X509Req {
///
/// 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 @@ impl X509Req {
///
/// 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 @@ impl X509ReqRef {
///
/// 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 @@ impl X509ReqRef {
///
/// 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 @@ impl X509ReqRef {
///
/// 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 @@ impl X509ReqRef {
///
/// 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 @@ impl X509ReqRef {
///
/// 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 @@ impl X509ReqRef {
///
/// 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 @@ impl X509ReqRef {
}
}
+/// 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 @@ impl X509VerifyResult {
///
/// 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 @@ foreign_type_and_impl_send_sync! {
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 @@ impl GeneralNameRef {
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 @@ impl GeneralNameRef {
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 @@ impl GeneralNameRef {
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 @@ impl fmt::Debug for GeneralNameRef {
} 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 @@ impl Stackable for GeneralName {
}
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! {
}
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! {
}
}
}
+
+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 @@ impl X509StoreBuilderRef {
/// 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 @@ generic_foreign_type_and_impl_send_sync! {
/// 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 @@ impl X509Lookup<File> {
#[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 @@ impl X509LookupRef<File> {
.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 @@ foreign_type_and_impl_send_sync! {
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 @@ use crate::x509::extension::{
#[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 @@ fn test_subject_alt_name() {
}
#[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 @@ fn x509_builder() {
}
#[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 @@ fn test_verify_fails() {
}
#[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 @@ fn test_verify_fails_with_crl_flag_set_and_no_crl() {
)
}
+#[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 @@ fn x509_ref_version_no_version_set() {
}
#[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 @@ fn test_name_cmp() {
}
#[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 @@ fn test_verify_param_set_time_fails_verification() {
}
#[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 @@ fn test_verify_param_set_time() {
}
#[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 @@ fn test_verify_param_set_depth() {
}
#[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 @@ fn test_load_cert_file() {
.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 libc::{c_int, c_uint, c_ulong, time_t};
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 @@ impl X509VerifyParamRef {
#[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 @@ impl X509VerifyParamRef {
unsafe {
cvt(ffi::X509_VERIFY_PARAM_clear_flags(
self.as_ptr(),
- flags.bits,
+ flags.bits(),
))
.map(|_| ())
}
@@ -111,22 +123,39 @@ impl X509VerifyParamRef {
#[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 @@ impl X509VerifyParamRef {
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