Skip to content

Commit 0e0e46f

Browse files
authored
backport: initialize openssl's legacy provider in rust (#10323) (#10333)
* initialize openssl's legacy provider in rust (#10323) * initialize openssl's legacy provider in rust as we oxidize we need to do this here to ensure it actually happens * alex is a comment format pedant * remove the memleak tests (#10322) they are fragile, haven't caught regressions, and increasingly pointless as we oxidize.
1 parent 2202123 commit 0e0e46f

File tree

8 files changed

+69
-483
lines changed

8 files changed

+69
-483
lines changed

src/_cffi_src/openssl/crypto.py

-45
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
"""
1010

1111
TYPES = """
12-
static const long Cryptography_HAS_MEM_FUNCTIONS;
13-
1412
static const int OPENSSL_VERSION;
1513
static const int OPENSSL_CFLAGS;
1614
static const int OPENSSL_BUILT_ON;
@@ -26,50 +24,7 @@
2624
2725
void *OPENSSL_malloc(size_t);
2826
void OPENSSL_free(void *);
29-
30-
31-
/* Signature is significantly different in LibreSSL, so expose via different
32-
symbol name */
33-
int Cryptography_CRYPTO_set_mem_functions(
34-
void *(*)(size_t, const char *, int),
35-
void *(*)(void *, size_t, const char *, int),
36-
void (*)(void *, const char *, int));
37-
38-
void *Cryptography_malloc_wrapper(size_t, const char *, int);
39-
void *Cryptography_realloc_wrapper(void *, size_t, const char *, int);
40-
void Cryptography_free_wrapper(void *, const char *, int);
4127
"""
4228

4329
CUSTOMIZATIONS = """
44-
#if CRYPTOGRAPHY_IS_LIBRESSL || CRYPTOGRAPHY_IS_BORINGSSL
45-
static const long Cryptography_HAS_MEM_FUNCTIONS = 0;
46-
int (*Cryptography_CRYPTO_set_mem_functions)(
47-
void *(*)(size_t, const char *, int),
48-
void *(*)(void *, size_t, const char *, int),
49-
void (*)(void *, const char *, int)) = NULL;
50-
51-
#else
52-
static const long Cryptography_HAS_MEM_FUNCTIONS = 1;
53-
54-
int Cryptography_CRYPTO_set_mem_functions(
55-
void *(*m)(size_t, const char *, int),
56-
void *(*r)(void *, size_t, const char *, int),
57-
void (*f)(void *, const char *, int)
58-
) {
59-
return CRYPTO_set_mem_functions(m, r, f);
60-
}
61-
#endif
62-
63-
void *Cryptography_malloc_wrapper(size_t size, const char *path, int line) {
64-
return malloc(size);
65-
}
66-
67-
void *Cryptography_realloc_wrapper(void *ptr, size_t size, const char *path,
68-
int line) {
69-
return realloc(ptr, size);
70-
}
71-
72-
void Cryptography_free_wrapper(void *ptr, const char *path, int line) {
73-
free(ptr);
74-
}
7530
"""

src/cryptography/hazmat/backends/openssl/backend.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def __repr__(self) -> str:
138138
return "<OpenSSLBackend(version: {}, FIPS: {}, Legacy: {})>".format(
139139
self.openssl_version_text(),
140140
self._fips_enabled,
141-
self._binding._legacy_provider_loaded,
141+
rust_openssl._legacy_provider_loaded,
142142
)
143143

144144
def openssl_assert(
@@ -277,7 +277,7 @@ def _register_default_ciphers(self) -> None:
277277
# we get an EVP_CIPHER * in the _CipherContext __init__, but OpenSSL 3
278278
# will return a valid pointer even though the cipher is unavailable.
279279
if (
280-
self._binding._legacy_provider_loaded
280+
rust_openssl._legacy_provider_loaded
281281
or not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
282282
):
283283
for mode_cls in [CBC, CFB, OFB, ECB]:

src/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ __all__ = [
4242
"x25519",
4343
]
4444

45+
_legacy_provider_loaded: bool
46+
4547
def openssl_version() -> int: ...
4648
def raise_openssl_error() -> typing.NoReturn: ...
4749
def capture_error_stack() -> list[OpenSSLError]: ...

src/cryptography/hazmat/bindings/openssl/_conditional.py

-7
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,6 @@ def cryptography_has_tls_st() -> list[str]:
2828
]
2929

3030

31-
def cryptography_has_mem_functions() -> list[str]:
32-
return [
33-
"Cryptography_CRYPTO_set_mem_functions",
34-
]
35-
36-
3731
def cryptography_has_ed448() -> list[str]:
3832
return [
3933
"EVP_PKEY_ED448",
@@ -202,7 +196,6 @@ def cryptography_has_get_extms_support() -> list[str]:
202196
"Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb,
203197
"Cryptography_HAS_SSL_ST": cryptography_has_ssl_st,
204198
"Cryptography_HAS_TLS_ST": cryptography_has_tls_st,
205-
"Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions,
206199
"Cryptography_HAS_ED448": cryptography_has_ed448,
207200
"Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs,
208201
"Cryptography_HAS_PSK": cryptography_has_psk,

src/cryptography/hazmat/bindings/openssl/binding.py

-31
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,6 @@ def _openssl_assert(
3737
)
3838

3939

40-
def _legacy_provider_error(loaded: bool) -> None:
41-
if not loaded:
42-
raise RuntimeError(
43-
"OpenSSL 3.0's legacy provider failed to load. This is a fatal "
44-
"error by default, but cryptography supports running without "
45-
"legacy algorithms by setting the environment variable "
46-
"CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error,"
47-
" you have likely made a mistake with your OpenSSL configuration."
48-
)
49-
50-
5140
def build_conditional_library(
5241
lib: typing.Any,
5342
conditional_names: dict[str, typing.Callable[[], list[str]]],
@@ -76,7 +65,6 @@ class Binding:
7665
_lib_loaded = False
7766
_init_lock = threading.Lock()
7867
_legacy_provider: typing.Any = ffi.NULL
79-
_legacy_provider_loaded = False
8068
_default_provider: typing.Any = ffi.NULL
8169

8270
def __init__(self) -> None:
@@ -106,25 +94,6 @@ def _ensure_ffi_initialized(cls) -> None:
10694
_openssl.lib, CONDITIONAL_NAMES
10795
)
10896
cls._lib_loaded = True
109-
# As of OpenSSL 3.0.0 we must register a legacy cipher provider
110-
# to get RC2 (needed for junk asymmetric private key
111-
# serialization), RC4, Blowfish, IDEA, SEED, etc. These things
112-
# are ugly legacy, but we aren't going to get rid of them
113-
# any time soon.
114-
if cls.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER:
115-
if not os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY"):
116-
cls._legacy_provider = cls.lib.OSSL_PROVIDER_load(
117-
cls.ffi.NULL, b"legacy"
118-
)
119-
cls._legacy_provider_loaded = (
120-
cls._legacy_provider != cls.ffi.NULL
121-
)
122-
_legacy_provider_error(cls._legacy_provider_loaded)
123-
124-
cls._default_provider = cls.lib.OSSL_PROVIDER_load(
125-
cls.ffi.NULL, b"default"
126-
)
127-
_openssl_assert(cls._default_provider != cls.ffi.NULL)
12897

12998
@classmethod
13099
def init_static_locks(cls) -> None:

src/rust/src/lib.rs

+65
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
#![deny(rust_2018_idioms, clippy::undocumented_unsafe_blocks)]
66

7+
use crate::error::CryptographyResult;
8+
#[cfg(CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)]
9+
use openssl::provider;
10+
use std::env;
11+
712
mod asn1;
813
mod backend;
914
mod buf;
@@ -15,6 +20,12 @@ mod pkcs7;
1520
pub(crate) mod types;
1621
mod x509;
1722

23+
#[cfg(CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)]
24+
#[pyo3::prelude::pyclass(frozen, module = "cryptography.hazmat.bindings._rust")]
25+
struct LoadedProviders {
26+
legacy: Option<provider::Provider>,
27+
}
28+
1829
#[pyo3::prelude::pyfunction]
1930
fn openssl_version() -> i64 {
2031
openssl::version::number()
@@ -25,6 +36,35 @@ fn is_fips_enabled() -> bool {
2536
cryptography_openssl::fips::is_enabled()
2637
}
2738

39+
#[cfg(CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)]
40+
fn _initialize_legacy_provider() -> CryptographyResult<LoadedProviders> {
41+
// As of OpenSSL 3.0.0 we must register a legacy cipher provider
42+
// to get RC2 (needed for junk asymmetric private key
43+
// serialization), RC4, Blowfish, IDEA, SEED, etc. These things
44+
// are ugly legacy, but we aren't going to get rid of them
45+
// any time soon.
46+
let load_legacy = env::var("CRYPTOGRAPHY_OPENSSL_NO_LEGACY")
47+
.map(|v| v.is_empty() || v == "0")
48+
.unwrap_or(true);
49+
let legacy = if load_legacy {
50+
let legacy_result = provider::Provider::try_load(None, "legacy", true);
51+
_legacy_provider_error(legacy_result.is_ok())?;
52+
Some(legacy_result?)
53+
} else {
54+
None
55+
};
56+
Ok(LoadedProviders { legacy })
57+
}
58+
59+
fn _legacy_provider_error(success: bool) -> pyo3::PyResult<()> {
60+
if !success {
61+
return Err(pyo3::exceptions::PyRuntimeError::new_err(
62+
"OpenSSL 3.0's legacy provider failed to load. This is a fatal error by default, but cryptography supports running without legacy algorithms by setting the environment variable CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error, you have likely made a mistake with your OpenSSL configuration."
63+
));
64+
}
65+
Ok(())
66+
}
67+
2868
#[pyo3::prelude::pymodule]
2969
fn _rust(py: pyo3::Python<'_>, m: &pyo3::types::PyModule) -> pyo3::PyResult<()> {
3070
m.add_function(pyo3::wrap_pyfunction!(padding::check_pkcs7_padding, m)?)?;
@@ -52,6 +92,20 @@ fn _rust(py: pyo3::Python<'_>, m: &pyo3::types::PyModule) -> pyo3::PyResult<()>
5292
m.add_submodule(cryptography_cffi::create_module(py)?)?;
5393

5494
let openssl_mod = pyo3::prelude::PyModule::new(py, "openssl")?;
95+
cfg_if::cfg_if! {
96+
if #[cfg(CRYPTOGRAPHY_OPENSSL_300_OR_GREATER)] {
97+
let providers = _initialize_legacy_provider()?;
98+
if providers.legacy.is_some() {
99+
openssl_mod.add("_legacy_provider_loaded", true)?;
100+
openssl_mod.add("_providers", providers)?;
101+
} else {
102+
openssl_mod.add("_legacy_provider_loaded", false)?;
103+
}
104+
} else {
105+
// default value for non-openssl 3+
106+
openssl_mod.add("_legacy_provider_loaded", false)?;
107+
}
108+
}
55109
openssl_mod.add_function(pyo3::wrap_pyfunction!(openssl_version, m)?)?;
56110
openssl_mod.add_function(pyo3::wrap_pyfunction!(error::raise_openssl_error, m)?)?;
57111
openssl_mod.add_function(pyo3::wrap_pyfunction!(error::capture_error_stack, m)?)?;
@@ -62,3 +116,14 @@ fn _rust(py: pyo3::Python<'_>, m: &pyo3::types::PyModule) -> pyo3::PyResult<()>
62116

63117
Ok(())
64118
}
119+
120+
#[cfg(test)]
121+
mod tests {
122+
use super::_legacy_provider_error;
123+
124+
#[test]
125+
fn test_legacy_provider_error() {
126+
assert!(_legacy_provider_error(true).is_ok());
127+
assert!(_legacy_provider_error(false).is_err());
128+
}
129+
}

0 commit comments

Comments
 (0)