Skip to content

Commit 50ea0fa

Browse files
authored
Convert symmetric ciphers to Rust (#9859)
1 parent 88cb4da commit 50ea0fa

File tree

15 files changed

+899
-683
lines changed

15 files changed

+899
-683
lines changed

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

+3-161
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,12 @@
66

77
import collections
88
import contextlib
9-
import itertools
109
import typing
1110

1211
from cryptography import utils, x509
1312
from cryptography.exceptions import UnsupportedAlgorithm
14-
from cryptography.hazmat.backends.openssl.ciphers import _CipherContext
1513
from cryptography.hazmat.bindings._rust import openssl as rust_openssl
1614
from cryptography.hazmat.bindings.openssl import binding
17-
from cryptography.hazmat.decrepit.ciphers.algorithms import (
18-
ARC4,
19-
CAST5,
20-
IDEA,
21-
RC2,
22-
SEED,
23-
Blowfish,
24-
TripleDES,
25-
)
2615
from cryptography.hazmat.primitives import hashes, serialization
2716
from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding
2817
from cryptography.hazmat.primitives.asymmetric import ec
@@ -41,21 +30,9 @@
4130
)
4231
from cryptography.hazmat.primitives.ciphers.algorithms import (
4332
AES,
44-
AES128,
45-
AES256,
46-
SM4,
47-
Camellia,
48-
ChaCha20,
4933
)
5034
from cryptography.hazmat.primitives.ciphers.modes import (
5135
CBC,
52-
CFB,
53-
CFB8,
54-
CTR,
55-
ECB,
56-
GCM,
57-
OFB,
58-
XTS,
5936
Mode,
6037
)
6138
from cryptography.hazmat.primitives.serialization.pkcs12 import (
@@ -113,25 +90,15 @@ def __init__(self) -> None:
11390
self._lib = self._binding.lib
11491
self._fips_enabled = rust_openssl.is_fips_enabled()
11592

116-
self._cipher_registry: dict[
117-
tuple[type[CipherAlgorithm], type[Mode]],
118-
typing.Callable,
119-
] = {}
120-
self._register_default_ciphers()
121-
12293
def __repr__(self) -> str:
12394
return "<OpenSSLBackend(version: {}, FIPS: {}, Legacy: {})>".format(
12495
self.openssl_version_text(),
12596
self._fips_enabled,
12697
rust_openssl._legacy_provider_loaded,
12798
)
12899

129-
def openssl_assert(
130-
self,
131-
ok: bool,
132-
errors: list[rust_openssl.OpenSSLError] | None = None,
133-
) -> None:
134-
return binding._openssl_assert(ok, errors=errors)
100+
def openssl_assert(self, ok: bool) -> None:
101+
return binding._openssl_assert(ok)
135102

136103
def _enable_fips(self) -> None:
137104
# This function enables FIPS mode for OpenSSL 3.0.0 on installs that
@@ -204,102 +171,7 @@ def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool:
204171
if not isinstance(cipher, self._fips_ciphers):
205172
return False
206173

207-
try:
208-
adapter = self._cipher_registry[type(cipher), type(mode)]
209-
except KeyError:
210-
return False
211-
evp_cipher = adapter(self, cipher, mode)
212-
return self._ffi.NULL != evp_cipher
213-
214-
def register_cipher_adapter(self, cipher_cls, mode_cls, adapter) -> None:
215-
if (cipher_cls, mode_cls) in self._cipher_registry:
216-
raise ValueError(
217-
f"Duplicate registration for: {cipher_cls} {mode_cls}."
218-
)
219-
self._cipher_registry[cipher_cls, mode_cls] = adapter
220-
221-
def _register_default_ciphers(self) -> None:
222-
for cipher_cls in [AES, AES128, AES256]:
223-
for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8, GCM]:
224-
self.register_cipher_adapter(
225-
cipher_cls,
226-
mode_cls,
227-
GetCipherByName(
228-
"{cipher.name}-{cipher.key_size}-{mode.name}"
229-
),
230-
)
231-
for mode_cls in [CBC, CTR, ECB, OFB, CFB]:
232-
self.register_cipher_adapter(
233-
Camellia,
234-
mode_cls,
235-
GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"),
236-
)
237-
for mode_cls in [CBC, CFB, CFB8, OFB]:
238-
self.register_cipher_adapter(
239-
TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}")
240-
)
241-
self.register_cipher_adapter(
242-
TripleDES, ECB, GetCipherByName("des-ede3")
243-
)
244-
# ChaCha20 uses the Long Name "chacha20" in OpenSSL, but in LibreSSL
245-
# it uses "chacha"
246-
self.register_cipher_adapter(
247-
ChaCha20,
248-
type(None),
249-
GetCipherByName(
250-
"chacha" if self._lib.CRYPTOGRAPHY_IS_LIBRESSL else "chacha20"
251-
),
252-
)
253-
self.register_cipher_adapter(AES, XTS, _get_xts_cipher)
254-
for mode_cls in [ECB, CBC, OFB, CFB, CTR, GCM]:
255-
self.register_cipher_adapter(
256-
SM4, mode_cls, GetCipherByName("sm4-{mode.name}")
257-
)
258-
# Don't register legacy ciphers if they're unavailable. Hypothetically
259-
# this wouldn't be necessary because we test availability by seeing if
260-
# we get an EVP_CIPHER * in the _CipherContext __init__, but OpenSSL 3
261-
# will return a valid pointer even though the cipher is unavailable.
262-
if (
263-
rust_openssl._legacy_provider_loaded
264-
or not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER
265-
):
266-
for mode_cls in [CBC, CFB, OFB, ECB]:
267-
self.register_cipher_adapter(
268-
Blowfish,
269-
mode_cls,
270-
GetCipherByName("bf-{mode.name}"),
271-
)
272-
for mode_cls in [CBC, CFB, OFB, ECB]:
273-
self.register_cipher_adapter(
274-
SEED,
275-
mode_cls,
276-
GetCipherByName("seed-{mode.name}"),
277-
)
278-
for cipher_cls, mode_cls in itertools.product(
279-
[CAST5, IDEA],
280-
[CBC, OFB, CFB, ECB],
281-
):
282-
self.register_cipher_adapter(
283-
cipher_cls,
284-
mode_cls,
285-
GetCipherByName("{cipher.name}-{mode.name}"),
286-
)
287-
self.register_cipher_adapter(
288-
ARC4, type(None), GetCipherByName("rc4")
289-
)
290-
self.register_cipher_adapter(
291-
RC2, CBC, GetCipherByName("{cipher.name}-{mode.name}")
292-
)
293-
294-
def create_symmetric_encryption_ctx(
295-
self, cipher: CipherAlgorithm, mode: Mode
296-
) -> _CipherContext:
297-
return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT)
298-
299-
def create_symmetric_decryption_ctx(
300-
self, cipher: CipherAlgorithm, mode: Mode
301-
) -> _CipherContext:
302-
return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT)
174+
return rust_openssl.ciphers.cipher_supported(cipher, mode)
303175

304176
def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool:
305177
return self.hmac_supported(algorithm)
@@ -834,34 +706,4 @@ def pkcs7_supported(self) -> bool:
834706
return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL
835707

836708

837-
class GetCipherByName:
838-
def __init__(self, fmt: str):
839-
self._fmt = fmt
840-
841-
def __call__(self, backend: Backend, cipher: CipherAlgorithm, mode: Mode):
842-
cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower()
843-
evp_cipher = backend._lib.EVP_get_cipherbyname(
844-
cipher_name.encode("ascii")
845-
)
846-
847-
# try EVP_CIPHER_fetch if present
848-
if (
849-
evp_cipher == backend._ffi.NULL
850-
and backend._lib.Cryptography_HAS_300_EVP_CIPHER
851-
):
852-
evp_cipher = backend._lib.EVP_CIPHER_fetch(
853-
backend._ffi.NULL,
854-
cipher_name.encode("ascii"),
855-
backend._ffi.NULL,
856-
)
857-
858-
backend._consume_errors()
859-
return evp_cipher
860-
861-
862-
def _get_xts_cipher(backend: Backend, cipher: AES, mode):
863-
cipher_name = f"aes-{cipher.key_size // 2}-xts"
864-
return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
865-
866-
867709
backend = Backend()

0 commit comments

Comments
 (0)