diff options
| author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2021-08-29 10:05:32 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-29 10:05:32 -0400 |
| commit | 27374c62c7b2854ea22a01e9dd5894fa2ec77722 (patch) | |
| tree | 63b8a92525e04282bb101d1e1ccebfbcb9cd6d6a /src/cryptography | |
| parent | cd4ae74ef123f8ce14f00a0c85f48a4b3b1a7d1f (diff) | |
| download | cryptography-27374c62c7b2854ea22a01e9dd5894fa2ec77722.tar.gz | |
FIPS 3.0.0 support (#6012)
* FIPS 3.0.0 support
* comments
* remove unneeded error clear
* review comments
* small refactor
* black
* flake8 too
* review feedback
* oops
* fix
Diffstat (limited to 'src/cryptography')
| -rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 67 | ||||
| -rw-r--r-- | src/cryptography/hazmat/bindings/openssl/_conditional.py | 8 | ||||
| -rw-r--r-- | src/cryptography/hazmat/bindings/openssl/binding.py | 16 |
3 files changed, 83 insertions, 8 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 3603a1c34..1a1db1ccc 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -157,8 +157,9 @@ class Backend(BackendInterface): b"aes-256-gcm", } _fips_ciphers = (AES, TripleDES) + # Sometimes SHA1 is still permissible. That logic is contained + # within the various *_supported methods. _fips_hashes = ( - hashes.SHA1, hashes.SHA224, hashes.SHA256, hashes.SHA384, @@ -172,6 +173,12 @@ class Backend(BackendInterface): hashes.SHAKE128, hashes.SHAKE256, ) + _fips_ecdh_curves = ( + ec.SECP224R1, + ec.SECP256R1, + ec.SECP384R1, + ec.SECP521R1, + ) _fips_rsa_min_key_size = 2048 _fips_rsa_min_public_exponent = 65537 _fips_dsa_min_modulus = 1 << 2048 @@ -200,17 +207,34 @@ class Backend(BackendInterface): if self._lib.Cryptography_HAS_EVP_PKEY_DHX: self._dh_types.append(self._lib.EVP_PKEY_DHX) + def __repr__(self): + return "<OpenSSLBackend(version: {}, FIPS: {})>".format( + self.openssl_version_text(), self._fips_enabled + ) + def openssl_assert(self, ok, errors=None): return binding._openssl_assert(self._lib, ok, errors=errors) def _is_fips_enabled(self): - fips_mode = getattr(self._lib, "FIPS_mode", lambda: 0) - mode = fips_mode() + if self._lib.Cryptography_HAS_300_FIPS: + mode = self._lib.EVP_default_properties_is_fips_enabled( + self._ffi.NULL + ) + else: + mode = getattr(self._lib, "FIPS_mode", lambda: 0)() + if mode == 0: # OpenSSL without FIPS pushes an error on the error stack self._lib.ERR_clear_error() return bool(mode) + def _enable_fips(self): + # This function enables FIPS mode for OpenSSL 3.0.0 on installs that + # have the FIPS provider installed properly. + self._binding._enable_fips() + assert self._is_fips_enabled() + self._fips_enabled = self._is_fips_enabled() + def activate_builtin_random(self): if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: # Obtain a new structural reference. @@ -306,17 +330,31 @@ class Backend(BackendInterface): return evp_md != self._ffi.NULL def scrypt_supported(self): - return self._lib.Cryptography_HAS_SCRYPT == 1 + if self._fips_enabled: + return False + else: + return self._lib.Cryptography_HAS_SCRYPT == 1 def hmac_supported(self, algorithm): + # FIPS mode still allows SHA1 for HMAC + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return True + return self.hash_supported(algorithm) def create_hash_ctx(self, algorithm): return _HashContext(self, algorithm) def cipher_supported(self, cipher, mode): - if self._fips_enabled and not isinstance(cipher, self._fips_ciphers): - return False + if self._fips_enabled: + # FIPS mode requires AES or TripleDES, but only CBC/ECB allowed + # in TripleDES mode. + if not isinstance(cipher, self._fips_ciphers) or ( + isinstance(cipher, TripleDES) + and not isinstance(mode, (CBC, ECB)) + ): + return False + try: adapter = self._cipher_registry[type(cipher), type(mode)] except KeyError: @@ -720,7 +758,13 @@ class Backend(BackendInterface): if isinstance(padding, PKCS1v15): return True elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): - return self.hash_supported(padding._mgf._algorithm) + # SHA1 is permissible in MGF1 in FIPS + if self._fips_enabled and isinstance( + padding._mgf._algorithm, hashes.SHA1 + ): + return True + else: + return self.hash_supported(padding._mgf._algorithm) elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): return ( self._oaep_hash_supported(padding._mgf._algorithm) @@ -1489,10 +1533,12 @@ class Backend(BackendInterface): raise ValueError("Unsupported public key algorithm.") else: + errors = binding._errors_with_text(errors) raise ValueError( "Could not deserialize key data. The data may be in an " "incorrect format or it may be encrypted with an unsupported " - "algorithm." + "algorithm.", + errors, ) def elliptic_curve_supported(self, curve): @@ -1777,6 +1823,11 @@ class Backend(BackendInterface): return _OCSPResponse(self, ocsp_resp) def elliptic_curve_exchange_algorithm_supported(self, algorithm, curve): + if self._fips_enabled and not isinstance( + curve, self._fips_ecdh_curves + ): + return False + return self.elliptic_curve_supported(curve) and isinstance( algorithm, ec.ECDH ) diff --git a/src/cryptography/hazmat/bindings/openssl/_conditional.py b/src/cryptography/hazmat/bindings/openssl/_conditional.py index 912aff302..5f403e610 100644 --- a/src/cryptography/hazmat/bindings/openssl/_conditional.py +++ b/src/cryptography/hazmat/bindings/openssl/_conditional.py @@ -254,6 +254,13 @@ def cryptography_has_dtls_get_data_mtu(): ] +def cryptography_has_300_fips(): + return [ + "EVP_default_properties_is_fips_enabled", + "EVP_default_properties_enable_fips", + ] + + # This is a mapping of # {condition: function-returning-names-dependent-on-that-condition} so we can # loop over them and delete unsupported names at runtime. It will be removed @@ -305,4 +312,5 @@ CONDITIONAL_NAMES = { cryptography_has_op_no_renegotiation ), "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu, + "Cryptography_HAS_300_FIPS": cryptography_has_300_fips, } diff --git a/src/cryptography/hazmat/bindings/openssl/binding.py b/src/cryptography/hazmat/bindings/openssl/binding.py index f651ab672..92d5b2448 100644 --- a/src/cryptography/hazmat/bindings/openssl/binding.py +++ b/src/cryptography/hazmat/bindings/openssl/binding.py @@ -116,6 +116,22 @@ class Binding(object): def __init__(self): self._ensure_ffi_initialized() + def _enable_fips(self): + # This function enables FIPS mode for OpenSSL 3.0.0 on installs that + # have the FIPS provider installed properly. + _openssl_assert(self.lib, self.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER) + self._base_provider = self.lib.OSSL_PROVIDER_load( + self.ffi.NULL, b"base" + ) + _openssl_assert(self.lib, self._base_provider != self.ffi.NULL) + self.lib._fips_provider = self.lib.OSSL_PROVIDER_load( + self.ffi.NULL, b"fips" + ) + _openssl_assert(self.lib, self.lib._fips_provider != self.ffi.NULL) + + res = self.lib.EVP_default_properties_enable_fips(self.ffi.NULL, 1) + _openssl_assert(self.lib, res == 1) + @classmethod def _register_osrandom_engine(cls): # Clear any errors extant in the queue before we start. In many |
