mirror of
https://github.com/OpenKMIP/PyKMIP.git
synced 2025-07-21 13:04:22 +02:00
Add MAC operation support in cryptography engine
This commit is contained in:
parent
51ec018b35
commit
e8b63eb9d3
@ -66,3 +66,18 @@ class CryptographicEngine(object):
|
|||||||
dict: A dictionary containing the private key data, identical in
|
dict: A dictionary containing the private key data, identical in
|
||||||
structure to the public key dictionary.
|
structure to the public key dictionary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def mac(self, algorithm, key, data):
|
||||||
|
"""
|
||||||
|
Generate message authentication code.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
algorithm(CryptographicAlgorithm): An enumeration specifying the
|
||||||
|
algorithm for which the MAC operation will use.
|
||||||
|
key(bytes): secret key used in the MAC operation
|
||||||
|
data(bytes): The data to be MACed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes: The MAC data
|
||||||
|
"""
|
||||||
|
@ -17,7 +17,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization, hashes, hmac, cmac
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||||
from cryptography.hazmat.primitives.ciphers import algorithms
|
from cryptography.hazmat.primitives.ciphers import algorithms
|
||||||
|
|
||||||
@ -50,6 +50,14 @@ class CryptographyEngine(api.CryptographicEngine):
|
|||||||
self._asymetric_key_algorithms = {
|
self._asymetric_key_algorithms = {
|
||||||
enums.CryptographicAlgorithm.RSA: self._create_rsa_key_pair
|
enums.CryptographicAlgorithm.RSA: self._create_rsa_key_pair
|
||||||
}
|
}
|
||||||
|
self._hash_algorithms = {
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA1: hashes.SHA1,
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA224: hashes.SHA224,
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA256: hashes.SHA256,
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA384: hashes.SHA384,
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA512: hashes.SHA512,
|
||||||
|
enums.CryptographicAlgorithm.HMAC_MD5: hashes.MD5
|
||||||
|
}
|
||||||
|
|
||||||
def create_symmetric_key(self, algorithm, length):
|
def create_symmetric_key(self, algorithm, length):
|
||||||
"""
|
"""
|
||||||
@ -148,6 +156,74 @@ class CryptographyEngine(api.CryptographicEngine):
|
|||||||
engine_method = self._asymetric_key_algorithms.get(algorithm)
|
engine_method = self._asymetric_key_algorithms.get(algorithm)
|
||||||
return engine_method(length)
|
return engine_method(length)
|
||||||
|
|
||||||
|
def mac(self, algorithm, key, data):
|
||||||
|
"""
|
||||||
|
Generate message authentication code.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
algorithm(CryptographicAlgorithm): An enumeration specifying the
|
||||||
|
algorithm for which the MAC operation will use.
|
||||||
|
key(bytes): secret key used in the MAC operation
|
||||||
|
data(bytes): The data to be MACed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bytes: The MACed data
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
InvalidField: Raised when the algorithm is unsupported or the
|
||||||
|
length is incompatible with the algorithm.
|
||||||
|
CryptographicFailure: Raised when the key generation process
|
||||||
|
fails.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> engine = CryptographyEngine()
|
||||||
|
>>> mac_data = engine.mac(
|
||||||
|
... CryptographicAlgorithm.HMAC-SHA256, b'\x01\x02\x03\x04',
|
||||||
|
... b'\x05\x06\x07\x08')
|
||||||
|
"""
|
||||||
|
|
||||||
|
mac_data = None
|
||||||
|
|
||||||
|
if algorithm in self._hash_algorithms.keys():
|
||||||
|
self.logger.info(
|
||||||
|
"Generating a hash-based message authentication code using "
|
||||||
|
"{0}".format(algorithm.name)
|
||||||
|
)
|
||||||
|
hash_algorithm = self._hash_algorithms.get(algorithm)
|
||||||
|
try:
|
||||||
|
h = hmac.HMAC(key, hash_algorithm(), backend=default_backend())
|
||||||
|
h.update(data)
|
||||||
|
mac_data = h.finalize()
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.exception(e)
|
||||||
|
raise exceptions.CryptographicFailure(
|
||||||
|
"An error occurred while computing an HMAC. "
|
||||||
|
"See the server log for more information."
|
||||||
|
)
|
||||||
|
elif algorithm in self._symmetric_key_algorithms.keys():
|
||||||
|
self.logger.info(
|
||||||
|
"Generating a cipher-based message authentication code using "
|
||||||
|
"{0}".format(algorithm.name)
|
||||||
|
)
|
||||||
|
cipher_algorithm = self._symmetric_key_algorithms.get(algorithm)
|
||||||
|
try:
|
||||||
|
# ARC4 and IDEA algorithms will raise exception as CMAC
|
||||||
|
# requires block ciphers
|
||||||
|
c = cmac.CMAC(cipher_algorithm(key), backend=default_backend())
|
||||||
|
c.update(data)
|
||||||
|
mac_data = c.finalize()
|
||||||
|
except Exception as e:
|
||||||
|
raise exceptions.CryptographicFailure(
|
||||||
|
"An error occurred while computing a CMAC. "
|
||||||
|
"See the server log for more information."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise exceptions.InvalidField(
|
||||||
|
"The cryptographic algorithm ({0}) is not a supported "
|
||||||
|
"for a MAC operation.".format(algorithm)
|
||||||
|
)
|
||||||
|
return mac_data
|
||||||
|
|
||||||
def _create_rsa_key_pair(self, length, public_exponent=65537):
|
def _create_rsa_key_pair(self, length, public_exponent=65537):
|
||||||
"""
|
"""
|
||||||
Create an RSA key pair.
|
Create an RSA key pair.
|
||||||
|
@ -146,3 +146,114 @@ class TestCryptographyEngine(testtools.TestCase):
|
|||||||
engine.create_asymmetric_key_pair,
|
engine.create_asymmetric_key_pair,
|
||||||
*args
|
*args
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_mac(self):
|
||||||
|
"""
|
||||||
|
Test that MAC operation can be done with valid arguments.
|
||||||
|
"""
|
||||||
|
key1 = (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00')
|
||||||
|
key2 = (b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00')
|
||||||
|
key3 = (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00')
|
||||||
|
data = (b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B'
|
||||||
|
b'\x0C\x0D\x0E\x0F')
|
||||||
|
|
||||||
|
engine = crypto.CryptographyEngine()
|
||||||
|
|
||||||
|
# test cmac
|
||||||
|
mac_data1 = engine.mac(
|
||||||
|
enums.CryptographicAlgorithm.AES,
|
||||||
|
key1,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
mac_data2 = engine.mac(
|
||||||
|
enums.CryptographicAlgorithm.AES,
|
||||||
|
key2,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
mac_data3 = engine.mac(
|
||||||
|
enums.CryptographicAlgorithm.AES,
|
||||||
|
key3,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
self.assertNotEqual(mac_data1, mac_data2)
|
||||||
|
self.assertEqual(mac_data1, mac_data3)
|
||||||
|
|
||||||
|
# test hmac
|
||||||
|
mac_data1 = engine.mac(
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA256,
|
||||||
|
key1,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
mac_data2 = engine.mac(
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA256,
|
||||||
|
key2,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
mac_data3 = engine.mac(
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA256,
|
||||||
|
key3,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
self.assertNotEqual(mac_data1, mac_data2)
|
||||||
|
self.assertEqual(mac_data1, mac_data3)
|
||||||
|
|
||||||
|
def test_mac_with_invalid_algorithm(self):
|
||||||
|
"""
|
||||||
|
Test that an InvalidField error is raised when doing the MAC
|
||||||
|
with an invalid algorithm.
|
||||||
|
"""
|
||||||
|
engine = crypto.CryptographyEngine()
|
||||||
|
|
||||||
|
key = (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00')
|
||||||
|
data = (b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B'
|
||||||
|
b'\x0C\x0D\x0E\x0F')
|
||||||
|
args = ['invalid', key, data]
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.InvalidField,
|
||||||
|
engine.mac,
|
||||||
|
*args
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_mac_with_cryptographic_failure(self):
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
Test that an CryptographicFailure error is raised when the MAC
|
||||||
|
process fails.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create dummy hash algorithm that always fails on instantiation.
|
||||||
|
class DummyHashAlgorithm(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
raise Exception()
|
||||||
|
|
||||||
|
key = (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00')
|
||||||
|
data = (b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B'
|
||||||
|
b'\x0C\x0D\x0E\x0F')
|
||||||
|
|
||||||
|
engine = crypto.CryptographyEngine()
|
||||||
|
|
||||||
|
# IDEA is not block cipher so cmac should raise exception
|
||||||
|
args = [enums.CryptographicAlgorithm.IDEA, key, data]
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CryptographicFailure,
|
||||||
|
engine.mac,
|
||||||
|
*args
|
||||||
|
)
|
||||||
|
|
||||||
|
engine._hash_algorithms.update([(
|
||||||
|
enums.CryptographicAlgorithm.HMAC_SHA256,
|
||||||
|
DummyHashAlgorithm
|
||||||
|
)])
|
||||||
|
|
||||||
|
args = [enums.CryptographicAlgorithm.HMAC_SHA256, key, data]
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.CryptographicFailure,
|
||||||
|
engine.mac,
|
||||||
|
*args
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user