diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-03-07 02:16:17 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-03-07 02:16:17 +0000 |
commit | 73ddfe4c16a82c51240f74be1f4591c194986fdd (patch) | |
tree | daf8bd94cbd892b85eea7ab956953757ffcce66e | |
parent | 95e17ca9df58e6fa7bdc8f51381c487dca74e4a3 (diff) | |
parent | 3fae50980c68802bc27d6a6fef8a464914713651 (diff) | |
download | openssl-sdk-release.tar.gz |
Snap for 11541002 from 3fae50980c68802bc27d6a6fef8a464914713651 to sdk-releaseplatform-tools-35.0.1sdk-release
Change-Id: I046152f6112ae1e93f1a10b86a38fe01750901e8
66 files changed, 5841 insertions, 1484 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 @@ -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 @@ -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", +] @@ -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" @@ -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" } } @@ -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/cargo_embargo.json b/cargo_embargo.json index d09e89f..c483eb6 100644 --- a/cargo_embargo.json +++ b/cargo_embargo.json @@ -16,13 +16,11 @@ "variants": [ { "module_name_overrides": { - "libbitflags": "libbitflags-1.3.2", "libopenssl_sys": "libbssl_sys" } }, { "module_name_overrides": { - "libbitflags": "libbitflags-1.3.2", "libopenssl": "libopenssl_static" }, "package": { 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/patches/0001-cfgs.diff b/patches/0001-cfgs.diff index 6cc5f29..2cf93c1 100644 --- a/patches/0001-cfgs.diff +++ b/patches/0001-cfgs.diff @@ -87,145 +87,6 @@ index 891651e..f149bfd 100644 #[cfg(ossl300)] pub mod lib_ctx; pub mod md; -diff --git a/src/pkey.rs b/src/pkey.rs -index 7d438eb..7eaf068 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(ossl110)] -+#[cfg(any(boringssl, ossl110))] - use crate::pkey_ctx::PkeyCtx; - use crate::rsa::Rsa; - use crate::symm::Cipher; -@@ -89,11 +89,11 @@ impl Id { - #[cfg(ossl110)] - pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF); - -- #[cfg(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519); - #[cfg(ossl111)] - pub const ED448: Id = Id(ffi::EVP_PKEY_ED448); -- #[cfg(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub const X25519: Id = Id(ffi::EVP_PKEY_X25519); - #[cfg(ossl111)] - pub const X448: Id = Id(ffi::EVP_PKEY_X448); -@@ -243,7 +243,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(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub fn raw_public_key(&self) -> Result<Vec<u8>, ErrorStack> { - unsafe { - let mut len = 0; -@@ -294,7 +294,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(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub fn raw_private_key(&self) -> Result<Vec<u8>, ErrorStack> { - unsafe { - let mut len = 0; -@@ -475,7 +475,7 @@ impl PKey<Private> { - ctx.keygen() - } - -- #[cfg(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - fn generate_eddsa(id: Id) -> Result<PKey<Private>, ErrorStack> { - let mut ctx = PkeyCtx::new_id(id)?; - ctx.keygen_init()?; -@@ -505,7 +505,7 @@ impl PKey<Private> { - /// assert_eq!(secret.len(), 32); - /// # Ok(()) } - /// ``` -- #[cfg(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub fn generate_x25519() -> Result<PKey<Private>, ErrorStack> { - PKey::generate_eddsa(Id::X25519) - } -@@ -559,7 +559,7 @@ impl PKey<Private> { - /// assert_eq!(signature.len(), 64); - /// # Ok(()) } - /// ``` -- #[cfg(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub fn generate_ed25519() -> Result<PKey<Private>, ErrorStack> { - PKey::generate_eddsa(Id::ED25519) - } -@@ -709,7 +709,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(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub fn private_key_from_raw_bytes( - bytes: &[u8], - key_type: Id, -@@ -750,7 +750,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(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub fn public_key_from_raw_bytes( - bytes: &[u8], - key_type: Id, -diff --git a/src/sign.rs b/src/sign.rs -index 457ff12..4de8ad0 100644 ---- a/src/sign.rs -+++ b/src/sign.rs -@@ -290,7 +290,7 @@ impl<'a> Signer<'a> { - self.len_intern() - } - -- #[cfg(not(ossl111))] -+ #[cfg(not(any(boringssl, ossl111)))] - fn len_intern(&self) -> Result<usize, ErrorStack> { - unsafe { - let mut len = 0; -@@ -303,7 +303,7 @@ impl<'a> Signer<'a> { - } - } - -- #[cfg(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - fn len_intern(&self) -> Result<usize, ErrorStack> { - unsafe { - let mut len = 0; -@@ -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(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - 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(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - 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)?; -@@ -594,7 +594,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(ossl111)] -+ #[cfg(any(boringssl, ossl111))] - pub fn verify_oneshot(&mut self, signature: &[u8], buf: &[u8]) -> Result<bool, ErrorStack> { - unsafe { - let r = ffi::EVP_DigestVerify( diff --git a/src/symm.rs b/src/symm.rs index c75bbc0..beff5fc 100644 --- a/src/symm.rs diff --git a/patches/0007-cfgs.diff b/patches/0007-cfgs.diff new file mode 100644 index 0000000..0074303 --- /dev/null +++ b/patches/0007-cfgs.diff @@ -0,0 +1,398 @@ +diff --git a/src/cipher.rs b/src/cipher.rs +index 3926412..6869a4b 100644 +--- a/src/cipher.rs ++++ b/src/cipher.rs +@@ -350,11 +350,13 @@ impl Cipher { + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] ++ #[cfg(not(boringssl))] + pub fn bf_cfb64() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_bf_cfb64() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] ++ #[cfg(not(boringssl))] + pub fn bf_ofb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_bf_ofb() as *mut _) } + } +@@ -400,101 +402,121 @@ impl Cipher { + } + + #[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(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(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(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(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(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(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(osslconf = "OPENSSL_NO_IDEA"))] ++ #[cfg(not(boringssl))] + pub fn idea_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_idea_ecb() as *mut _) } + } + + #[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 _) } + } +@@ -510,21 +532,25 @@ impl Cipher { + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] ++ #[cfg(not(boringssl))] + pub fn seed_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_seed_cbc() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] ++ #[cfg(not(boringssl))] + pub fn seed_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_seed_cfb128() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] ++ #[cfg(not(boringssl))] + pub fn seed_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_seed_ecb() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] ++ #[cfg(not(boringssl))] + pub fn seed_ofb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_seed_ofb() as *mut _) } + } +diff --git a/src/ec.rs b/src/ec.rs +index 67df38f..578cf51 100644 +--- a/src/ec.rs ++++ b/src/ec.rs +@@ -196,6 +196,7 @@ impl EcGroupRef { + /// using a trinomial or pentanomial. + #[corresponds(EC_GROUP_get_curve_GF2m)] + #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] ++ #[cfg(not(boringssl))] + pub fn components_gf2m( + &self, + p: &mut BigNumRef, +@@ -587,6 +588,7 @@ impl EcPointRef { + /// `x` and `y` `BigNum`s + #[corresponds(EC_POINT_get_affine_coordinates_GF2m)] + #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] ++ #[cfg(not(boringssl))] + pub fn affine_coordinates_gf2m( + &self, + group: &EcGroupRef, +diff --git a/src/hash.rs b/src/hash.rs +index 01d7097..4caa251 100644 +--- a/src/hash.rs ++++ b/src/hash.rs +@@ -158,6 +158,7 @@ impl MessageDigest { + } + + #[cfg(not(osslconf = "OPENSSL_NO_RMD160"))] ++ #[cfg(not(boringssl))] + pub fn ripemd160() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_ripemd160()) } + } +diff --git a/src/lib.rs b/src/lib.rs +index aeae361..42f289b 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -148,7 +148,7 @@ pub mod base64; + pub mod bn; + pub mod cipher; + pub mod cipher_ctx; +-#[cfg(all(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; +@@ -174,7 +174,7 @@ pub mod md; + pub mod md_ctx; + pub mod memcmp; + pub mod nid; +-#[cfg(not(osslconf = "OPENSSL_NO_OCSP"))] ++#[cfg(all(not(osslconf = "OPENSSL_NO_OCSP"), not(boringssl)))] + pub mod ocsp; + pub mod pkcs12; + pub mod pkcs5; +diff --git a/src/md.rs b/src/md.rs +index 08e4aac..3ce3c25 100644 +--- a/src/md.rs ++++ b/src/md.rs +@@ -187,6 +187,7 @@ impl Md { + } + + #[cfg(not(osslconf = "OPENSSL_NO_RMD160"))] ++ #[cfg(not(boringssl))] + #[inline] + pub fn ripemd160() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_ripemd160() as *mut _) } +diff --git a/src/symm.rs b/src/symm.rs +index 4d69996..23b9ce4 100644 +--- a/src/symm.rs ++++ b/src/symm.rs +@@ -256,11 +256,13 @@ impl Cipher { + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] ++ #[cfg(not(boringssl))] + pub fn bf_cfb64() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_cfb64()) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] ++ #[cfg(not(boringssl))] + pub fn bf_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_ofb()) } + } +@@ -306,81 +308,97 @@ impl Cipher { + } + + #[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()) } + } +@@ -398,41 +416,49 @@ impl Cipher { + } + + #[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(osslconf = "OPENSSL_NO_SEED"))] ++ #[cfg(not(boringssl))] + pub fn seed_cfb128() -> Cipher { + unsafe { Cipher(ffi::EVP_seed_cfb128()) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] ++ #[cfg(not(boringssl))] + pub fn seed_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_seed_ecb()) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] ++ #[cfg(not(boringssl))] + pub fn seed_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_seed_ofb()) } + } diff --git a/patches/0007-pkey_ctx-hkdf.patch b/patches/0007-pkey_ctx-hkdf.patch deleted file mode 100644 index 6655f0c..0000000 --- a/patches/0007-pkey_ctx-hkdf.patch +++ /dev/null @@ -1,89 +0,0 @@ -diff --git a/src/pkey.rs b/src/pkey.rs -index ebab5fb..21ba711 100644 ---- a/src/pkey.rs -+++ b/src/pkey.rs -@@ -86,7 +86,7 @@ impl Id { - pub const DH: Id = Id(ffi::EVP_PKEY_DH); - pub const EC: Id = Id(ffi::EVP_PKEY_EC); - -- #[cfg(ossl110)] -+ #[cfg(any(boringssl, ossl110))] - pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF); - - #[cfg(any(boringssl, ossl111))] -diff --git a/src/pkey_ctx.rs b/src/pkey_ctx.rs -index f79372f..3d4203f 100644 ---- a/src/pkey_ctx.rs -+++ b/src/pkey_ctx.rs -@@ -470,7 +470,7 @@ impl<T> PkeyCtxRef<T> { - /// - /// Requires OpenSSL 1.1.0 or newer. - #[corresponds(EVP_PKEY_CTX_set_hkdf_md)] -- #[cfg(ossl110)] -+ #[cfg(any(ossl110, boringssl))] - #[inline] - pub fn set_hkdf_md(&mut self, digest: &MdRef) -> Result<(), ErrorStack> { - unsafe { -@@ -503,10 +503,13 @@ impl<T> PkeyCtxRef<T> { - /// - /// Requires OpenSSL 1.1.0 or newer. - #[corresponds(EVP_PKEY_CTX_set1_hkdf_key)] -- #[cfg(ossl110)] -+ #[cfg(any(ossl110, boringssl))] - #[inline] - pub fn set_hkdf_key(&mut self, key: &[u8]) -> Result<(), ErrorStack> { -+ #[cfg(not(boringssl))] - let len = c_int::try_from(key.len()).unwrap(); -+ #[cfg(boringssl)] -+ let len = key.len(); - - unsafe { - cvt(ffi::EVP_PKEY_CTX_set1_hkdf_key( -@@ -523,10 +526,13 @@ impl<T> PkeyCtxRef<T> { - /// - /// Requires OpenSSL 1.1.0 or newer. - #[corresponds(EVP_PKEY_CTX_set1_hkdf_salt)] -- #[cfg(ossl110)] -+ #[cfg(any(ossl110, boringssl))] - #[inline] - pub fn set_hkdf_salt(&mut self, salt: &[u8]) -> Result<(), ErrorStack> { -+ #[cfg(not(boringssl))] - let len = c_int::try_from(salt.len()).unwrap(); -+ #[cfg(boringssl)] -+ let len = salt.len(); - - unsafe { - cvt(ffi::EVP_PKEY_CTX_set1_hkdf_salt( -@@ -543,10 +549,13 @@ impl<T> PkeyCtxRef<T> { - /// - /// Requires OpenSSL 1.1.0 or newer. - #[corresponds(EVP_PKEY_CTX_add1_hkdf_info)] -- #[cfg(ossl110)] -+ #[cfg(any(ossl110, boringssl))] - #[inline] - pub fn add_hkdf_info(&mut self, info: &[u8]) -> Result<(), ErrorStack> { -+ #[cfg(not(boringssl))] - let len = c_int::try_from(info.len()).unwrap(); -+ #[cfg(boringssl)] -+ let len = info.len(); - - unsafe { - cvt(ffi::EVP_PKEY_CTX_add1_hkdf_info( -@@ -604,7 +613,7 @@ mod test { - #[cfg(not(boringssl))] - use crate::cipher::Cipher; - use crate::ec::{EcGroup, EcKey}; -- #[cfg(any(ossl102, libressl310))] -+ #[cfg(any(ossl102, libressl310, boringssl))] - use crate::md::Md; - use crate::nid::Nid; - use crate::pkey::PKey; -@@ -689,7 +698,7 @@ mod test { - } - - #[test] -- #[cfg(ossl110)] -+ #[cfg(any(ossl110, boringssl))] - fn hkdf() { - let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap(); - ctx.derive_init().unwrap(); diff --git a/patches/0008-rsa-dsa-etc.patch b/patches/0008-rsa-dsa-etc.patch deleted file mode 100644 index 7dcfacb..0000000 --- a/patches/0008-rsa-dsa-etc.patch +++ /dev/null @@ -1,95 +0,0 @@ -diff --git a/src/asn1.rs b/src/asn1.rs -index b02f9ac..939a173 100644 ---- a/src/asn1.rs -+++ b/src/asn1.rs -@@ -651,7 +651,7 @@ impl fmt::Debug for Asn1ObjectRef { - } - - cfg_if! { -- if #[cfg(any(ossl110, libressl273))] { -+ if #[cfg(any(ossl110, libressl273, boringssl))] { - use ffi::ASN1_STRING_get0_data; - } else { - #[allow(bad_style)] -diff --git a/src/dsa.rs b/src/dsa.rs -index 5f59ba8..0aceeb5 100644 ---- a/src/dsa.rs -+++ b/src/dsa.rs -@@ -7,6 +7,7 @@ - - use cfg_if::cfg_if; - use foreign_types::{ForeignType, ForeignTypeRef}; -+#[cfg(not(boringssl))] - use libc::c_int; - use std::fmt; - use std::mem; -@@ -283,7 +284,7 @@ impl<T> fmt::Debug for Dsa<T> { - } - - cfg_if! { -- if #[cfg(any(ossl110, libressl273))] { -+ if #[cfg(any(ossl110, libressl273, boringssl))] { - use ffi::{DSA_get0_key, DSA_get0_pqg, DSA_set0_key, DSA_set0_pqg}; - } else { - #[allow(bad_style)] -@@ -462,7 +463,7 @@ impl DsaSigRef { - } - - cfg_if! { -- if #[cfg(any(ossl110, libressl273))] { -+ if #[cfg(any(ossl110, libressl273, boringssl))] { - use ffi::{DSA_SIG_set0, DSA_SIG_get0}; - } else { - #[allow(bad_style)] -diff --git a/src/ecdsa.rs b/src/ecdsa.rs -index 0a960e7..f3b27b3 100644 ---- a/src/ecdsa.rs -+++ b/src/ecdsa.rs -@@ -110,7 +110,7 @@ impl EcdsaSigRef { - } - - cfg_if! { -- if #[cfg(any(ossl110, libressl273))] { -+ if #[cfg(any(ossl110, libressl273, boringssl))] { - use ffi::{ECDSA_SIG_set0, ECDSA_SIG_get0}; - } else { - #[allow(bad_style)] -diff --git a/src/hash.rs b/src/hash.rs -index 8e27505..7f6fa89 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(ossl110)] { -+ if #[cfg(any(ossl110, boringssl))] { - 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/md_ctx.rs b/src/md_ctx.rs -index c4d3f06..156f3c2 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(ossl110)] { -+ if #[cfg(any(ossl110, boringssl))] { - 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/rsa.rs b/src/rsa.rs -index 68cf64b..f155b12 100644 ---- a/src/rsa.rs -+++ b/src/rsa.rs -@@ -581,7 +581,7 @@ impl<T> fmt::Debug for Rsa<T> { - } - - cfg_if! { -- if #[cfg(any(ossl110, libressl273))] { -+ if #[cfg(any(ossl110, libressl273, boringssl))] { - use ffi::{ - RSA_get0_key, RSA_get0_factors, RSA_get0_crt_params, RSA_set0_key, RSA_set0_factors, - RSA_set0_crt_params, diff --git a/patches/0009-type-safety-fix.diff b/patches/0009-type-safety-fix.diff deleted file mode 100644 index a21da62..0000000 --- a/patches/0009-type-safety-fix.diff +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/src/x509/store.rs b/src/x509/store.rs -index a685fa1..418a8f2 100644 ---- a/src/x509/store.rs -+++ b/src/x509/store.rs -@@ -156,7 +156,9 @@ impl X509Lookup<HashDir> { - /// directory. - #[corresponds(X509_LOOKUP_hash_dir)] - pub fn hash_dir() -> &'static X509LookupMethodRef<HashDir> { -- unsafe { X509LookupMethodRef::from_ptr(ffi::X509_LOOKUP_hash_dir()) } -+ // `*mut` cast is needed because BoringSSL returns a `*const`. This is -+ // ok because we only return an immutable reference. -+ unsafe { X509LookupMethodRef::from_ptr(ffi::X509_LOOKUP_hash_dir() as *mut _) } - } - } - -@@ -188,7 +190,9 @@ impl X509Lookup<File> { - /// into memory at the time the file is added as a lookup source. - #[corresponds(X509_LOOKUP_file)] - pub fn file() -> &'static X509LookupMethodRef<File> { -- unsafe { X509LookupMethodRef::from_ptr(ffi::X509_LOOKUP_file()) } -+ // `*mut` cast is needed because BoringSSL returns a `*const`. This is -+ // ok because we only return an immutable reference. -+ unsafe { X509LookupMethodRef::from_ptr(ffi::X509_LOOKUP_file() as *mut _) } - } - } - diff --git a/patches/0010-type-safety-fix.diff b/patches/0010-type-safety-fix.diff deleted file mode 100644 index a3173b4..0000000 --- a/patches/0010-type-safety-fix.diff +++ /dev/null @@ -1,67 +0,0 @@ -diff --git a/src/lib.rs b/src/lib.rs -index e8d07d8..cfc6efc 100644 ---- a/src/lib.rs -+++ b/src/lib.rs -@@ -210,6 +210,15 @@ fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> { - } - } - -+#[inline] -+fn cvt_p_const<T>(r: *const T) -> Result<*const T, ErrorStack> { -+ if r.is_null() { -+ Err(ErrorStack::get()) -+ } else { -+ Ok(r) -+ } -+} -+ - #[inline] - fn cvt(r: c_int) -> Result<c_int, ErrorStack> { - if r <= 0 { -diff --git a/src/x509/mod.rs b/src/x509/mod.rs -index a03a8aa..40e5022 100644 ---- a/src/x509/mod.rs -+++ b/src/x509/mod.rs -@@ -35,7 +35,7 @@ use crate::ssl::SslRef; - use crate::stack::{Stack, StackRef, Stackable}; - use crate::string::OpensslString; - use crate::util::{ForeignTypeExt, ForeignTypeRefExt}; --use crate::{cvt, cvt_n, cvt_p}; -+use crate::{cvt, cvt_n, cvt_p, cvt_p_const}; - use openssl_macros::corresponds; - - #[cfg(any(ossl102, libressl261))] -diff --git a/src/x509/store.rs b/src/x509/store.rs -index 418a8f2..2219cfc 100644 ---- a/src/x509/store.rs -+++ b/src/x509/store.rs -@@ -49,6 +49,7 @@ use crate::error::ErrorStack; - #[cfg(not(boringssl))] - use crate::ssl::SslFiletype; - use crate::stack::StackRef; -+use crate::util::ForeignTypeRefExt; - #[cfg(any(ossl102, libressl261))] - use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef}; - use crate::x509::{X509Object, X509}; -@@ -156,9 +157,7 @@ impl X509Lookup<HashDir> { - /// directory. - #[corresponds(X509_LOOKUP_hash_dir)] - pub fn hash_dir() -> &'static X509LookupMethodRef<HashDir> { -- // `*mut` cast is needed because BoringSSL returns a `*const`. This is -- // ok because we only return an immutable reference. -- unsafe { X509LookupMethodRef::from_ptr(ffi::X509_LOOKUP_hash_dir() as *mut _) } -+ unsafe { X509LookupMethodRef::from_const_ptr(ffi::X509_LOOKUP_hash_dir()) } - } - } - -@@ -190,9 +189,7 @@ impl X509Lookup<File> { - /// into memory at the time the file is added as a lookup source. - #[corresponds(X509_LOOKUP_file)] - pub fn file() -> &'static X509LookupMethodRef<File> { -- // `*mut` cast is needed because BoringSSL returns a `*const`. This is -- // ok because we only return an immutable reference. -- unsafe { X509LookupMethodRef::from_ptr(ffi::X509_LOOKUP_file() as *mut _) } -+ unsafe { X509LookupMethodRef::from_const_ptr(ffi::X509_LOOKUP_file()) } - } - } - diff --git a/patches/bssl-update-apr23.diff b/patches/bssl-update-apr23.diff deleted file mode 100644 index edb9f80..0000000 --- a/patches/bssl-update-apr23.diff +++ /dev/null @@ -1,68 +0,0 @@ -diff --git a/src/x509/mod.rs b/src/x509/mod.rs -index 45f2467..a03a8aa 100644 ---- a/src/x509/mod.rs -+++ b/src/x509/mod.rs -@@ -893,13 +893,13 @@ impl X509NameBuilder { - pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> { - unsafe { - let field = CString::new(field).unwrap(); -- assert!(value.len() <= c_int::max_value() as usize); -+ assert!(value.len() <= isize::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 c_int, -+ value.len() as isize, - -1, - 0, - )) -@@ -920,13 +920,13 @@ impl X509NameBuilder { - ) -> Result<(), ErrorStack> { - unsafe { - let field = CString::new(field).unwrap(); -- assert!(value.len() <= c_int::max_value() as usize); -+ assert!(value.len() <= isize::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 c_int, -+ value.len() as isize, - -1, - 0, - )) -@@ -941,13 +941,13 @@ impl X509NameBuilder { - /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/man1.1.0/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() <= c_int::max_value() as usize); -+ assert!(value.len() <= isize::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 c_int, -+ value.len() as isize, - -1, - 0, - )) -@@ -967,13 +967,13 @@ impl X509NameBuilder { - ty: Asn1Type, - ) -> Result<(), ErrorStack> { - unsafe { -- assert!(value.len() <= c_int::max_value() as usize); -+ assert!(value.len() <= isize::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 c_int, -+ value.len() as isize, - -1, - 0, - )) diff --git a/patches/bssl-update.diff b/patches/bssl-update.diff deleted file mode 100644 index 2eea3ce..0000000 --- a/patches/bssl-update.diff +++ /dev/null @@ -1,60 +0,0 @@ -diff --git a/src/bio.rs b/src/bio.rs -index 6a72552..0324218 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; -+use crate::{cvt_p, SignedLenType}; - 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 c_int, -+ buf.len() as SignedLenType, - ))? - }; - -@@ -78,7 +78,7 @@ cfg_if! { - use ffi::BIO_new_mem_buf; - } else { - #[allow(bad_style)] -- unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: ::libc::c_int) -> *mut ffi::BIO { -+ unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: SignedLenType) -> *mut ffi::BIO { - ffi::BIO_new_mem_buf(buf as *mut _, len) - } - } -diff --git a/src/dh.rs b/src/dh.rs -index 12170b9..e781543 100644 ---- a/src/dh.rs -+++ b/src/dh.rs -@@ -239,7 +239,7 @@ where - } - - cfg_if! { -- if #[cfg(any(ossl110, libressl270))] { -+ if #[cfg(any(ossl110, libressl270, boringssl))] { - use ffi::{DH_set0_pqg, DH_get0_pqg, DH_get0_key, DH_set0_key}; - } else { - #[allow(bad_style)] -diff --git a/src/lib.rs b/src/lib.rs -index f149bfd..e8d07d8 100644 ---- a/src/lib.rs -+++ b/src/lib.rs -@@ -196,6 +196,11 @@ type LenType = libc::size_t; - #[cfg(not(boringssl))] - type LenType = libc::c_int; - -+#[cfg(boringssl)] -+type SignedLenType = libc::ssize_t; -+#[cfg(not(boringssl))] -+type SignedLenType = libc::c_int; -+ - #[inline] - fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> { - if r.is_null() { diff --git a/patches/bssl_to_vec_padded.diff b/patches/bssl_to_vec_padded.diff deleted file mode 100644 index 2782d50..0000000 --- a/patches/bssl_to_vec_padded.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/src/bn.rs b/src/bn.rs -index 1cd00dd..dbd7ae9 100644 ---- a/src/bn.rs -+++ b/src/bn.rs -@@ -814,7 +814,7 @@ impl BigNumRef { - /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]); - /// ``` - #[corresponds(BN_bn2binpad)] -- #[cfg(ossl110)] -+ #[cfg(any(boringssl, ossl110))] - pub fn to_vec_padded(&self, pad_to: i32) -> Result<Vec<u8>, ErrorStack> { - let mut v = Vec::with_capacity(pad_to as usize); - unsafe { 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); + } } @@ -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) } } @@ -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); + } } @@ -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()); + } } @@ -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(_))); + } } @@ -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]; @@ -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)] @@ -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 { @@ -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}; @@ -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(); } } @@ -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 Binary files differnew file mode 100644 index 0000000..aead062 --- /dev/null +++ b/test/test.crl |