77import collections
88import contextlib
99import itertools
10+ import warnings
1011from contextlib import contextmanager
1112
1213import six
@@ -119,21 +120,59 @@ class Backend(object):
119120 """
120121 name = "openssl"
121122
123+ # FIPS has opinions about acceptable algorithms and key sizes, but the
124+ # disallowed algorithms are still present in OpenSSL. They just error if
125+ # you try to use them. To avoid that we allowlist the algorithms in
126+ # FIPS 140-3. This isn't ideal, but FIPS 140-3 is trash so here we are.
127+ _fips_aead = {
128+ b'aes-128-ccm' , b'aes-192-ccm' , b'aes-256-ccm' ,
129+ b'aes-128-gcm' , b'aes-192-gcm' , b'aes-256-gcm' ,
130+ }
131+ _fips_ciphers = (
132+ AES , TripleDES
133+ )
134+ _fips_hashes = (
135+ hashes .SHA1 , hashes .SHA224 , hashes .SHA256 , hashes .SHA384 ,
136+ hashes .SHA512 , hashes .SHA512_224 , hashes .SHA512_256 , hashes .SHA3_224 ,
137+ hashes .SHA3_256 , hashes .SHA3_384 , hashes .SHA3_512 , hashes .SHAKE128 ,
138+ hashes .SHAKE256 ,
139+ )
140+ _fips_rsa_min_key_size = 2048
141+ _fips_rsa_min_public_exponent = 65537
142+ _fips_dsa_min_modulus = 1 << 2048
143+ _fips_dh_min_key_size = 2048
144+ _fips_dh_min_modulus = 1 << _fips_dh_min_key_size
145+
122146 def __init__ (self ):
123147 self ._binding = binding .Binding ()
124148 self ._ffi = self ._binding .ffi
125149 self ._lib = self ._binding .lib
150+ self ._fips_enabled = self ._is_fips_enabled ()
126151
127152 self ._cipher_registry = {}
128153 self ._register_default_ciphers ()
129- self .activate_osrandom_engine ()
154+ if self ._fips_enabled and self ._lib .CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE :
155+ warnings .warn (
156+ "OpenSSL FIPS mode is enabled. Can't enable DRBG fork safety." ,
157+ UserWarning
158+ )
159+ else :
160+ self .activate_osrandom_engine ()
130161 self ._dh_types = [self ._lib .EVP_PKEY_DH ]
131162 if self ._lib .Cryptography_HAS_EVP_PKEY_DHX :
132163 self ._dh_types .append (self ._lib .EVP_PKEY_DHX )
133164
134165 def openssl_assert (self , ok ):
135166 return binding ._openssl_assert (self ._lib , ok )
136167
168+ def _is_fips_enabled (self ):
169+ fips_mode = getattr (self ._lib , "FIPS_mode" , lambda : 0 )
170+ mode = fips_mode ()
171+ if mode == 0 :
172+ # OpenSSL without FIPS pushes an error on the error stack
173+ self ._lib .ERR_clear_error ()
174+ return bool (mode )
175+
137176 def activate_builtin_random (self ):
138177 if self ._lib .CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE :
139178 # Obtain a new structural reference.
@@ -222,6 +261,9 @@ def _evp_md_non_null_from_algorithm(self, algorithm):
222261 return evp_md
223262
224263 def hash_supported (self , algorithm ):
264+ if self ._fips_enabled and not isinstance (algorithm , self ._fips_hashes ):
265+ return False
266+
225267 evp_md = self ._evp_md_from_algorithm (algorithm )
226268 return evp_md != self ._ffi .NULL
227269
@@ -232,6 +274,8 @@ def create_hash_ctx(self, algorithm):
232274 return _HashContext (self , algorithm )
233275
234276 def cipher_supported (self , cipher , mode ):
277+ if self ._fips_enabled and not isinstance (cipher , self ._fips_ciphers ):
278+ return False
235279 try :
236280 adapter = self ._cipher_registry [type (cipher ), type (mode )]
237281 except KeyError :
@@ -1380,6 +1424,11 @@ def elliptic_curve_supported(self, curve):
13801424 errors [0 ]._lib_reason_match (
13811425 self ._lib .ERR_LIB_EC ,
13821426 self ._lib .EC_R_UNKNOWN_GROUP
1427+ ) or
1428+ # This occurs in FIPS mode for unsupported curves on RHEL
1429+ errors [0 ]._lib_reason_match (
1430+ self ._lib .ERR_LIB_EC ,
1431+ self ._lib .EC_R_NOT_A_NIST_PRIME
13831432 )
13841433 )
13851434 return False
@@ -1777,6 +1826,16 @@ def _private_key_bytes(self, encoding, format, encryption_algorithm,
17771826
17781827 # TraditionalOpenSSL + PEM/DER
17791828 if format is serialization .PrivateFormat .TraditionalOpenSSL :
1829+ if (
1830+ self ._fips_enabled and
1831+ not isinstance (
1832+ encryption_algorithm , serialization .NoEncryption
1833+ )
1834+ ):
1835+ raise ValueError (
1836+ "Encrypted traditional OpenSSL format is not "
1837+ "supported in FIPS mode."
1838+ )
17801839 key_type = self ._lib .EVP_PKEY_id (evp_pkey )
17811840
17821841 if encoding is serialization .Encoding .PEM :
@@ -2170,6 +2229,8 @@ def x25519_generate_key(self):
21702229 return _X25519PrivateKey (self , evp_pkey )
21712230
21722231 def x25519_supported (self ):
2232+ if self ._fips_enabled :
2233+ return False
21732234 return self ._lib .CRYPTOGRAPHY_OPENSSL_110_OR_GREATER
21742235
21752236 def x448_load_public_bytes (self , data ):
@@ -2200,9 +2261,13 @@ def x448_generate_key(self):
22002261 return _X448PrivateKey (self , evp_pkey )
22012262
22022263 def x448_supported (self ):
2264+ if self ._fips_enabled :
2265+ return False
22032266 return not self ._lib .CRYPTOGRAPHY_OPENSSL_LESS_THAN_111
22042267
22052268 def ed25519_supported (self ):
2269+ if self ._fips_enabled :
2270+ return False
22062271 return not self ._lib .CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B
22072272
22082273 def ed25519_load_public_bytes (self , data ):
@@ -2238,6 +2303,8 @@ def ed25519_generate_key(self):
22382303 return _Ed25519PrivateKey (self , evp_pkey )
22392304
22402305 def ed448_supported (self ):
2306+ if self ._fips_enabled :
2307+ return False
22412308 return not self ._lib .CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B
22422309
22432310 def ed448_load_public_bytes (self , data ):
@@ -2304,6 +2371,8 @@ def derive_scrypt(self, key_material, salt, length, n, r, p):
23042371
23052372 def aead_cipher_supported (self , cipher ):
23062373 cipher_name = aead ._aead_cipher_name (cipher )
2374+ if self ._fips_enabled and cipher_name not in self ._fips_aead :
2375+ return False
23072376 return (
23082377 self ._lib .EVP_get_cipherbyname (cipher_name ) != self ._ffi .NULL
23092378 )
@@ -2453,6 +2522,8 @@ def serialize_key_and_certificates_to_pkcs12(self, name, key, cert, cas,
24532522 return self ._read_mem_bio (bio )
24542523
24552524 def poly1305_supported (self ):
2525+ if self ._fips_enabled :
2526+ return False
24562527 return self ._lib .Cryptography_HAS_POLY1305 == 1
24572528
24582529 def create_poly1305_ctx (self , key ):
0 commit comments