Here is a proposed flowchart to improve usedforsecurity flag decision making process for which backend to initialize for a given hash implementation.
One can click the diagram to go to mermaid online editor to edit the diagram. Both TD and LR directions render small in discourse. Go Full Screen
OpenSSL wants FIPS?
Note that OpenSSL can be default to fips with or without Linux kernel operating in FIPS mode (depends on the vendor of a given OpenSSL). However the below APIs work correctly with all known vendors of OpenSSL on Linux-like platforms, but also Windows & Mac OS.
- For OpenSSL 1.1.1 call
FIPS_mode()
api
- For OpenSSL v3+ call
EVP_default_properties_is_fips_enabled()
api
Example Implementations:
in OpenSSL*?
Note that OpenSSL v3+ has implemented Blake MAC API that allows to specify size, customization, key, salt parameterers. OpenSSL does not have blake tree implementation. I wonder if for blake we can make the choice slight better - i.e. requested constructor sequential (most common case) or not; and then appropriately use OpenSSL v3; or fallback to builtin implementation. This likely needs work to implement using Blake via OpenSSL MAC api. See EVP_MAC-BLAKE2 - OpenSSL Documentation
Load extra providers side-quest
Separately, one can take a side-quest. If one landed at this node of flowchart, and one is using OpenSSL v3+ one can attempt to load additional providers - for example default or legacy; to access more hashes. The reason being that MD5 and SHA1 might not be available in the default library context; if only base+fips
providers or just default
provider are loaded. And it may help loading legacy
or default
to gain access to additional hashes. This is effectively alternative way to access fallbacks.
This also can be used to load other 3rd party providers such as blake3 xxhash gost and so on.
Use OpenSSL
And it may fail at runtime. Note that in case openssl is in FIPS mode and usedforsecurity=True we land on this node, regardless of whether or not a requested digest is available in OpenSSL. The reason being is as follows. If the user went out of their way to configure OpenSSL to be in fips mode, it means they are trying hard to use only things allowed by the OpenSSL policy. In such cases, lets only use openssl irrespective of whether or not it provides MD4, MD5, Blake2B and so on. As it’s not our call to decide what the system configured OpenSSL policy actually is. Also note it can be very selective. For example, HMAC-MD5 could be allowed, whilst pure MD5 is blocked. Similarly for SHA1 and other algorithms.
in Builtin?
Once we are here, check if a builtin fallback was compiled in, and use it if available. Otherwise display a friendly error saying things like
Requested hash was not found in openssl. Please consider installing or loading legacy and/or default providers.
Requested hash was also not found in builtin fallback. Consider compiling python with a builtin fallback
Extra functionality
In golang-openssl and in nodejs they have ability to force loading FIPS provider (i.e. turn off fallbacks, ensure FIPS is enabled in OpenSSL and fail if it is not). I’m not too sure why such things were implemented, and if anybody uses them. All security policies from all vendors suggest that security officers configure the system-wide openssl, rather than each individual application. Thus telling the user to configure system openssl.cnf or provide a custom one is probably best.
Current issues
Whilst most things behave correctly, especially in the case of non-fips and usedforsecurity=False, there are some gaps in the current upstream implementation of hashlib:
- Blake2 is used from builtin, even when OpenSSL wants FIPS and
usedforsecurity=True
- When python compiled without a builtin fallback, when OpenSSL is in FIPS mode and
usedforsecurity=False
default provider is not loaded to gain access to MD5
- Friendly Errors are not very friendly - improving messaging to the user as to what has happened, and why things fail would help
ps. diagram is big in edit preview; but not site. Sorry about this, seems like there are hard caps placed on image size.