2.4. Key Derivation Functions
Key derivation functions (KDFs) can be used to deterministically generate multiple keys from a single key (via crypto.HashKDF
) to derive a single key from a password (via crypto.PasswordKDF
). These functions can help (1) simplify your key management schemes and (2) avoid dangerous design patterns, such as reusing the same key for multiple purposes.
Important
One key, one purpose. If we use a key for symmetric encryption or HMAC, we should not use the same key to derive other keys via crypto.HashKDF
. Similarly, we should not use the same key for both encryption and HMAC.
2.4.1. Password-Based Key Derivation
- crypto.PasswordKDF(password: str, salt: bytes, key_length: int) bytes
Takes a
password
and asalt
and returns a new key. The size of the output equalskey_length
(in bytes).This function is built on top of PBKDF2, which is a deliberately slow and memory-intensive KDF as to make brute-force attacks infeasible for the adversary. As such, this function is a cryptographically secure way to derive a key from a password as long as the password has at least a medium level of entropy (approximately 40 bits). (However, if
password
has low entropy, brute-force attacks are still feasible, and attempting to derive a cryptographic key from a low-entropypassword
will produce an insecure key.)Users may happen to choose the same password for their account, which means that a salt-less password-based key derivation function will produce the same key for each user. Thus, the
salt
should be different for each user, which makes it more infeasible for adversaries to launch a brute-force attack against keys derived from passwords.- Parameters:
password (str) – A password with at least a medium level of entropy
salt (bytes) – A salt value
key_length (int) – Desired length of the key to derive
- Returns:
A new key
- Return type:
bytes
Warning
Using the same constant
salt
for every user may enable the adversary to use a rainbow table to break keys derived from this function; similarly, using asalt
derived deterministically from a user’s username enables precomputation of rainbow tables against specific high-value, targeted users.
2.4.2. Hash-Based Key Derivation
- crypto.HashKDF(key: bytes, purpose: str) bytes
Given a
key
and apurpose
, derives a new key from the input parent keykey
that has the same length as the inputkey
.This function is deterministic, so it will always return the same key given the same
key
andpurpose
. As such, if the inputkey
is insecure because it has insufficient entropy, then keys derived fromkey
will be insecure for the same reason.- Parameters:
key (bytes) – The “parent” key to derive from
purpose (str) – An arbitrary, human-readable string that explains the purpose of the newly derived “child” key
- Returns:
A new key that is the same length as the input
key
- Return type:
bytes
Example usage:
base_key = crypto.SecureRandom(16) derived_key_1 = crypto.HashKDF(base_key, "encryption") derived_key_2 = crypto.HashKDF(base_key, "mac") # Derived keys are the same length as the input key: assert(len(base_key) == len(derived_key_1)) assert(len(base_key) == len(derived_key_2)) derived_key_3 = crypto.HashKDF(base_key, "encryption") # Using the same base key and purpose results in the same derived key: assert(derived_key_1 == derived_key_3)
Note
purpose
is an arbitrary string. Changingpurpose
to different values allows you to generate multiple keys from a singlekey
, which can help simplify your key management schemes.Danger
You should not use HashKDF to create a key from a low-entropy input like a password, as this will create another low-entropy key. Instead, you can derive an initial key from a password using
crypto.PasswordKDF
and then use HashKDF to derive additional keys from this initial key.