diff --git a/Doc/src/protocol/kdf.rst b/Doc/src/protocol/kdf.rst index cb5dd43d..e8c15f72 100644 --- a/Doc/src/protocol/kdf.rst +++ b/Doc/src/protocol/kdf.rst @@ -125,6 +125,24 @@ Example, for deriving two AES256 keys:: .. autofunction:: Crypto.Protocol.KDF.HKDF +.. _hkdfexpand: + +HKDFExpand ++++++ + +HKDF consists of two stages, extract and expand. This class exposes an +expand only version of HKDF that is suitable when the key material is +already cryptographically strong. + +Example, for deriving a key with context:: + + from Crypto.Protocol.KDF import HKDFExpand + from Crypto.Hash import SHA512 + + app_key = HKDFExpand(pseudorandom_key, 32, SHA512, context=b"app key") + +.. autofunction:: Crypto.Protocol.KDF.HKDFExpand + .. _sp800-180-counter: SP 800-180 Counter Mode diff --git a/lib/Crypto/Protocol/KDF.py b/lib/Crypto/Protocol/KDF.py index afc581eb..d0d99884 100644 --- a/lib/Crypto/Protocol/KDF.py +++ b/lib/Crypto/Protocol/KDF.py @@ -315,14 +315,49 @@ def HKDF(master, key_len, salt, hashmod, num_keys=1, context=None): raise ValueError("Too much secret data to derive") if not salt: salt = b'\x00' * hashmod.digest_size - if context is None: - context = b"" # Step 1: extract hmac = HMAC.new(salt, master, digestmod=hashmod) prk = hmac.digest() # Step 2: expand + return HKDFExpand(prk, key_len, hashmod, num_keys, context) + + +def HKDFExpand(prk, key_len, hashmod, num_keys=1, context=None): + """HKDF consists of two stages, extract and expand. + This class exposes an expand only version of HKDF that + is suitable when the key material is already + cryptographically strong. + + Args: + prk (byte string): + Short, cryptographically strong, pseudorandom key material. + It must not be a password. + key_len (integer): + The length in bytes of every derived key. + hashmod (module): + A cryptographic hash algorithm from :mod:`Crypto.Hash`. + :mod:`Crypto.Hash.SHA512` is a good choice. + num_keys (integer): + The number of keys to derive. Every key is :data:`key_len` bytes long. + The maximum cumulative length of all keys is + 255 times the digest size. + context (byte string): + Optional identifier describing what the keys are used for. + + Return: + A byte string or a tuple of byte strings. + + .. _RFC5869: http://tools.ietf.org/html/rfc5869 + """ + + output_len = key_len * num_keys + if output_len > (255 * hashmod.digest_size): + raise ValueError("Too much secret data to derive") + if context is None: + context = b"" + t = [ b"" ] n = 1 tlen = 0 @@ -339,7 +374,6 @@ def HKDF(master, key_len, salt, hashmod, num_keys=1, context=None): return list(kol[:num_keys]) - def scrypt(password, salt, key_len, N, r, p, num_keys=1): """Derive one or more keys from a passphrase. diff --git a/lib/Crypto/Protocol/KDF.pyi b/lib/Crypto/Protocol/KDF.pyi index 745f0197..d944750b 100644 --- a/lib/Crypto/Protocol/KDF.pyi +++ b/lib/Crypto/Protocol/KDF.pyi @@ -19,6 +19,7 @@ class _S2V(object): def derive(self) -> bytes: ... def HKDF(master: bytes, key_len: int, salt: bytes, hashmod: ModuleType, num_keys: Optional[int]=1, context: Optional[bytes]=None) -> Union[bytes, Tuple[bytes, ...]]: ... +def HKDFExpand(prk: bytes, key_len: int, hashmod: ModuleType, num_keys: Optional[int]=1, context: Optional[bytes]=None) -> Union[bytes, Tuple[bytes, ...]]: ... def scrypt(password: str, salt: str, key_len: int, N: int, r: int, p: int, num_keys: Optional[int]=1) -> Union[bytes, Tuple[bytes, ...]]: ...