mirror of
https://github.com/openkmip/pykmip
synced 2025-12-15 15:53:36 +00:00
Add key derivation support to the server cryptography engine
This change adds key derivation functionality to the cryptographic engine, adding support for multiple key derivation methods, including encrypting, hashing, HMACing, and specific algorithms like PBKDF2 and NIST 800-108. Numerous unit tests are included from established cryptographic testing sources to verify the added functionality.
This commit is contained in:
@@ -24,6 +24,9 @@ from cryptography.hazmat.primitives.asymmetric import padding as \
|
||||
asymmetric_padding
|
||||
from cryptography.hazmat.primitives import ciphers
|
||||
from cryptography.hazmat.primitives.ciphers import algorithms, modes
|
||||
from cryptography.hazmat.primitives.kdf import hkdf
|
||||
from cryptography.hazmat.primitives.kdf import kbkdf
|
||||
from cryptography.hazmat.primitives.kdf import pbkdf2
|
||||
|
||||
from kmip.core import enums
|
||||
from kmip.core import exceptions
|
||||
@@ -619,3 +622,189 @@ class CryptographyEngine(api.CryptographicEngine):
|
||||
}
|
||||
|
||||
return public_key, private_key
|
||||
|
||||
def derive_key(self,
|
||||
derivation_method,
|
||||
derivation_length,
|
||||
derivation_data=None,
|
||||
key_material=None,
|
||||
hash_algorithm=None,
|
||||
salt=None,
|
||||
iteration_count=None,
|
||||
encryption_algorithm=None,
|
||||
cipher_mode=None,
|
||||
padding_method=None,
|
||||
iv_nonce=None):
|
||||
"""
|
||||
Derive key data using a variety of key derivation functions.
|
||||
|
||||
Args:
|
||||
derivation_method (DerivationMethod): An enumeration specifying
|
||||
the key derivation method to use. Required.
|
||||
derivation_length (int): An integer specifying the size of the
|
||||
derived key data in bytes. Required.
|
||||
derivation_data (bytes): The non-cryptographic bytes to be used
|
||||
in the key derivation process (e.g., the data to be encrypted,
|
||||
hashed, HMACed). Required in the general case. Optional if the
|
||||
derivation method is Hash and the key material is provided.
|
||||
Optional, defaults to None.
|
||||
key_material (bytes): The bytes of the key material to use for
|
||||
key derivation. Required in the general case. Optional if
|
||||
the derivation_method is HASH and derivation_data is provided.
|
||||
Optional, defaults to None.
|
||||
hash_algorithm (HashingAlgorithm): An enumeration specifying the
|
||||
hashing algorithm to use with the key derivation method.
|
||||
Required in the general case, optional if the derivation
|
||||
method specifies encryption. Optional, defaults to None.
|
||||
salt (bytes): Bytes representing a randomly generated salt.
|
||||
Required if the derivation method is PBKDF2. Optional,
|
||||
defaults to None.
|
||||
iteration_count (int): An integer representing the number of
|
||||
iterations to use when deriving key material. Required if
|
||||
the derivation method is PBKDF2. Optional, defaults to None.
|
||||
encryption_algorithm (CryptographicAlgorithm): An enumeration
|
||||
specifying the symmetric encryption algorithm to use for
|
||||
encryption-based key derivation. Required if the derivation
|
||||
method specifies encryption. Optional, defaults to None.
|
||||
cipher_mode (BlockCipherMode): An enumeration specifying the
|
||||
block cipher mode to use with the encryption algorithm.
|
||||
Required in in the general case if the derivation method
|
||||
specifies encryption and the encryption algorithm is
|
||||
specified. Optional if the encryption algorithm is RC4 (aka
|
||||
ARC4). Optional, defaults to None.
|
||||
padding_method (PaddingMethod): An enumeration specifying the
|
||||
padding method to use on the data before encryption. Required
|
||||
in in the general case if the derivation method specifies
|
||||
encryption and the encryption algorithm is specified. Required
|
||||
if the cipher mode is for block ciphers (e.g., CBC, ECB).
|
||||
Optional otherwise, defaults to None.
|
||||
iv_nonce (bytes): The IV/nonce value to use to initialize the mode
|
||||
of the encryption algorithm. Required in the general case if
|
||||
the derivation method specifies encryption and the encryption
|
||||
algorithm is specified. Optional, defaults to None. If
|
||||
required and not provided, it will be autogenerated.
|
||||
|
||||
Returns:
|
||||
bytes: the bytes of the derived data
|
||||
|
||||
Raises:
|
||||
InvalidField: Raised when cryptographic data and/or settings are
|
||||
unsupported or incompatible with the derivation method.
|
||||
|
||||
Example:
|
||||
>>> engine = CryptographyEngine()
|
||||
>>> result = engine.derive_key(
|
||||
... derivation_method=enums.DerivationMethod.HASH,
|
||||
... derivation_length=16,
|
||||
... derivation_data=b'abc',
|
||||
... hash_algorithm=enums.HashingAlgorithm.MD5
|
||||
... )
|
||||
>>> result
|
||||
b'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr'
|
||||
"""
|
||||
if derivation_method == enums.DerivationMethod.ENCRYPT:
|
||||
result = self.encrypt(
|
||||
encryption_algorithm=encryption_algorithm,
|
||||
encryption_key=key_material,
|
||||
plain_text=derivation_data,
|
||||
cipher_mode=cipher_mode,
|
||||
padding_method=padding_method,
|
||||
iv_nonce=iv_nonce
|
||||
)
|
||||
return result.get('cipher_text')
|
||||
else:
|
||||
# Handle key derivation functions that use hash algorithms
|
||||
|
||||
# Set up the hashing algorithm
|
||||
if hash_algorithm is None:
|
||||
raise exceptions.InvalidField("Hash algorithm is required.")
|
||||
hashing_algorithm = self._encryption_hash_algorithms.get(
|
||||
hash_algorithm,
|
||||
None
|
||||
)
|
||||
if hashing_algorithm is None:
|
||||
raise exceptions.InvalidField(
|
||||
"Hash algorithm '{0}' is not a supported hashing "
|
||||
"algorithm.".format(hash_algorithm)
|
||||
)
|
||||
|
||||
if derivation_method == enums.DerivationMethod.HMAC:
|
||||
df = hkdf.HKDF(
|
||||
algorithm=hashing_algorithm(),
|
||||
length=derivation_length,
|
||||
salt=salt,
|
||||
info=derivation_data,
|
||||
backend=default_backend()
|
||||
)
|
||||
# df = hmac.HMAC(
|
||||
# key_material,
|
||||
# algorithm=hashing_algorithm(),
|
||||
# backend=default_backend()
|
||||
# )
|
||||
# df.update(derivation_data)
|
||||
# derived_data = df.finalize()
|
||||
derived_data = df.derive(key_material)
|
||||
return derived_data
|
||||
elif derivation_method == enums.DerivationMethod.HASH:
|
||||
if None not in [derivation_data, key_material]:
|
||||
raise exceptions.InvalidField(
|
||||
"For hash-based key derivation, specify only "
|
||||
"derivation data or key material, not both."
|
||||
)
|
||||
elif derivation_data is not None:
|
||||
hashing_data = derivation_data
|
||||
elif key_material is not None:
|
||||
hashing_data = key_material
|
||||
else:
|
||||
raise exceptions.InvalidField(
|
||||
"For hash-based key derivation, derivation data or "
|
||||
"key material must be specified."
|
||||
)
|
||||
|
||||
df = hashes.Hash(
|
||||
algorithm=hashing_algorithm(),
|
||||
backend=default_backend()
|
||||
)
|
||||
df.update(hashing_data)
|
||||
derived_data = df.finalize()
|
||||
return derived_data
|
||||
elif derivation_method == enums.DerivationMethod.PBKDF2:
|
||||
if salt is None:
|
||||
raise exceptions.InvalidField(
|
||||
"For PBKDF2 key derivation, salt must be specified."
|
||||
)
|
||||
if iteration_count is None:
|
||||
raise exceptions.InvalidField(
|
||||
"For PBKDF2 key derivation, iteration count must be "
|
||||
"specified."
|
||||
)
|
||||
|
||||
df = pbkdf2.PBKDF2HMAC(
|
||||
algorithm=hashing_algorithm(),
|
||||
length=derivation_length,
|
||||
salt=salt,
|
||||
iterations=iteration_count,
|
||||
backend=default_backend()
|
||||
)
|
||||
derived_data = df.derive(key_material)
|
||||
return derived_data
|
||||
elif derivation_method == enums.DerivationMethod.NIST800_108_C:
|
||||
df = kbkdf.KBKDFHMAC(
|
||||
algorithm=hashing_algorithm(),
|
||||
mode=kbkdf.Mode.CounterMode,
|
||||
length=derivation_length,
|
||||
rlen=4,
|
||||
llen=None,
|
||||
location=kbkdf.CounterLocation.BeforeFixed,
|
||||
label=None,
|
||||
context=None,
|
||||
fixed=derivation_data,
|
||||
backend=default_backend()
|
||||
)
|
||||
derived_data = df.derive(key_material)
|
||||
return derived_data
|
||||
else:
|
||||
raise exceptions.InvalidField(
|
||||
"Derivation method '{0}' is not a supported key "
|
||||
"derivation method.".format(derivation_method)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user