summaryrefslogtreecommitdiff
path: root/src/cryptography
diff options
context:
space:
mode:
authorPaul Kehrer <paul.l.kehrer@gmail.com>2021-08-29 10:05:32 -0400
committerGitHub <noreply@github.com>2021-08-29 10:05:32 -0400
commit27374c62c7b2854ea22a01e9dd5894fa2ec77722 (patch)
tree63b8a92525e04282bb101d1e1ccebfbcb9cd6d6a /src/cryptography
parentcd4ae74ef123f8ce14f00a0c85f48a4b3b1a7d1f (diff)
downloadcryptography-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.py67
-rw-r--r--src/cryptography/hazmat/bindings/openssl/_conditional.py8
-rw-r--r--src/cryptography/hazmat/bindings/openssl/binding.py16
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