HMAC#
Module to test HMAC implementations.
The crypto_condor.primitives.HMAC
module provides the test_hmac()
function
to test classes that implement HMAC. There are two types of interfaces that can be
tested, described by the HMAC
and HMAC_IUF
protocols below.
Supported hash functions are defined by the Hash
enum.
This module also exposes internal test functions, mostly to illustrate where the
individual test results come from. We recommend using test_hmac()
and its options
to select which the modes to test and test vectors to use.
Parameters#
- enum crypto_condor.primitives.HMAC.Hash(value)#
A hash function that can be used with HMAC.
- Member Type:
str
Valid values are as follows:
- SHA_1 = <Hash.SHA_1: 'SHA-1'>#
- SHA_224 = <Hash.SHA_224: 'SHA-224'>#
- SHA_256 = <Hash.SHA_256: 'SHA-256'>#
- SHA_384 = <Hash.SHA_384: 'SHA-384'>#
- SHA_512 = <Hash.SHA_512: 'SHA-512'>#
- SHA3_224 = <Hash.SHA3_224: 'SHA3-224'>#
- SHA3_256 = <Hash.SHA3_256: 'SHA3-256'>#
- SHA3_384 = <Hash.SHA3_384: 'SHA3-384'>#
- SHA3_512 = <Hash.SHA3_512: 'SHA3-512'>#
Protocols#
- protocol crypto_condor.primitives.HMAC.HMAC#
Class that implements HMAC methods.
This class represents a simpler interface, where everything is processed in a single function. For the common init/update/final interface, see
HMAC_IUF
.Raising
NotImplementedError
is allowed for methods you do not want to test but all methods should be present.Classes that implement this protocol must have the following methods / attributes:
- digest(key, message)#
Computes the MAC of a message.
- Parameters:
key (bytes) – The secret HMAC key.
message (bytes) – The entire message to authenticate.
- Returns:
The MAC tag.
- Return type:
bytes
- verify(key, message, mac)#
Verifies a MAC tag.
- Parameters:
key (bytes) – The secret HMAC key.
message (bytes) – The entire message to authenticate.
mac (bytes) – The MAC tag to verify.
- Returns:
True if the MAC tag is valid.
- Return type:
bool
- protocol crypto_condor.primitives.HMAC.HMAC_IUF#
Class that implements HMAC methods.
This class represents the commonly used init/update/final interface. This interface has two final methods,
final_digest()
andfinal_verify()
, both of which requireinit()
andupdate()
. For a simpler interface, where only one function is required, seeHMAC
.Raising
NotImplementedError
is allowed for (final) methods you do not want to test but all methods should be present.Classes that implement this protocol must have the following methods / attributes:
- classmethod init(key)#
Initializes an instance with a key.
- Parameters:
key (bytes) – The secret HMAC key.
- Returns:
An instance of the class that has been initialized with the given key.
- update(message)#
Processes a new chunk.
- Parameters:
message (bytes) – The next part of the message to process.
- final_digest()#
Finalizes the processing.
- Returns:
The MAC tag.
- Return type:
bytes
- final_verify(mac)#
Finalizes the processing.
- Returns:
True if the MAC is valid for the given key and message.
- Return type:
bool
Main test#
- crypto_condor.primitives.HMAC.test_hmac(
- hmac,
- hash_function,
- *,
- compliance=True,
- resilience=False,
- skip_digest=False,
- skip_verify=False,
Tests an implementation of HMAC using test vectors.
Test vectors are selected with the
compliance
andresilience
options. SHA-3 functions are not covered by NIST vectors (seeHmacVectors
).- Parameters:
- Keyword Arguments:
compliance – Whether to use compliance test vectors.
resilience – Whether to use resilience test vectors.
skip_digest – If True, skip testing the digest function.
skip_verify – If True, skip testing the verify function.
Examples
Let’s test PyCryptodome’s HMAC-SHA256 implementation.
>>> from Crypto.Hash import HMAC as pyHMAC >>> from Crypto.Hash import SHA256
To test the implementation we have to create a class that conforms to either of the two interfaces. We’ll test the simple
HMAC
interface first. For this, we create two methods:digest
andverify
.>>> class MyHmac: ... def digest(self, key: bytes, message: bytes) -> bytes: ... h = pyHMAC.new(key, message, digestmod=SHA256) ... return h.digest() ... def verify(self, key: bytes, message: bytes, mac: bytes) -> bool: ... h = pyHMAC.new(key, message, digestmod=SHA256) ... try: ... h.verify(mac) ... return True ... except ValueError: ... return False
We pass an instance of this class to this function.
>>> from crypto_condor.primitives import HMAC >>> hash_function = HMAC.Hash.SHA_256 >>> rd = HMAC.test_hmac(MyHmac(), hash_function) [NIST] Generating MACs ... >>> assert rd.check()
We can also test the more complex init/update/final interface.
>>> class MyHmacIuf: ... _obj: pyHMAC.HMAC ... @classmethod ... def init(cls, key: bytes): ... h = cls() ... h._obj = pyHMAC.new(key, digestmod=SHA256) ... return h ... def update(self, data: bytes): ... self._obj.update(data) ... def final_digest(self) -> bytes: ... return self._obj.digest() ... def final_verify(self, mac: bytes) -> bool: ... try: ... self._obj.verify(mac) ... return True ... except ValueError: ... return False
This time we enable Wycheproof vectors, for illustration purposes.
>>> rd = HMAC.test_hmac(MyHmacIuf(), hash_function, resilience=True) [NIST] Generating MACs ... >>> assert rd.check()
Specific tests#
- crypto_condor.primitives.HMAC.test_digest_nist(hmac, hash_function)#
Tests an implementation of HMAC digest with NIST test vectors.
- Parameters:
- Returns:
The results of testing the implementation, or None if there are no NIST vectors for the given hash function.
- Return type:
Results | None
Notes
Some NIST vectors have truncated MACs. To use them, we truncate the output of the implementation to compare them.
- crypto_condor.primitives.HMAC.test_digest_wycheproof(hmac, hash_function)#
Tests an implementation of HMAC digest with Wycheproof test vectors.
- Parameters:
- Returns:
The results of testing the implementation, or None if there are no Wycheproof vectors for the given hash function.
- Return type:
Results | None
Notes
Some Wycheproof vectors have truncated MACs. To use them, we truncate the output of the implementation to compare them.
- crypto_condor.primitives.HMAC.test_verify_nist(hmac, hash_function)#
Tests an implementation of HMAC verify with NIST vectors.
- Parameters:
- Returns:
The results or None if there are no NIST vectors for the given hash function.
- Return type:
Results | None
Notes
Some NIST vectors have truncated MACs. As implementations usually expect the entire tag for verification, we skip these vectors.
- crypto_condor.primitives.HMAC.test_verify_wycheproof(hmac, hash_function)#
Tests an implementation of HMAC verify with Wycheproof test vectors.
- Parameters:
- Returns:
The results of testing the implementation, or None if there are no Wycheproof vectors for the given hash function.
- Return type:
Results | None
Notes
Some Wycheproof vectors have truncated MACs. As implementations usually expect the entire tag for verification, we skip these vectors.
Other functions#
Vectors#
HMAC test vectors.
There are NIST and Wycheproof test vectors available. These are parametrized by the hash function used with HMAC. Not all hash functions are covered by both sources:
Hash function |
NIST |
Wycheproof |
---|---|---|
SHA-1 |
Y |
Y |
SHA-224 |
Y |
Y |
SHA-256 |
Y |
Y |
SHA-384 |
Y |
Y |
SHA-512 |
Y |
Y |
SHA3-224 |
N |
Y |
SHA3-256 |
N |
Y |
SHA3-384 |
N |
Y |
SHA3-512 |
N |
Y |
- class crypto_condor.vectors.HMAC.HmacVectors(hash_function, nist, wycheproof)#
A class to load HMAC test vectors.
Use
load()
to instantiate.- Parameters:
hash_function (Hash) – The hash function used by the HMAC implementation to test.
nist (HmacNistVectors | None) – NIST vectors, if available.
wycheproof (HmacWycheproofVectors | None) – Wycheproof vectors, if available.
- classmethod load(hash_function)#
Loads test vectors for a given hash function.
- Parameters:
hash_function (Hash) – The hash function used by the HMAC implementation.
- Returns:
An instance of
HmacVectors
with NIST and Wycheproof vectors loaded, if available.- Raises:
HmacVectorsError – If an error occurred when loading the vectors.
Internal vectors#
The following section describes the internal test vectors classes, which are protobuf Python classes.
Hint
The autodoc extension can’t properly document these clases so we include the
.proto
file to show the different fields each class has. IDEs should be able to
use the included .pyi
files to provide auto-completion and type checking.
- class crypto_condor.vectors._HMAC.HMAC_pb2.HmacNistVectors#
Protobuf class to store NIST vectors, see below.
- class crypto_condor.vectors._HMAC.HMAC_pb2.HmacWycheproofVectors#
Protobuf class to store Wycheproofvectors, see below.
syntax = "proto3";
package crypto_condor;
message HmacNistTest {
int32 count = 1;
int32 klen = 2;
int32 tlen = 3;
bytes key = 4;
bytes msg = 5;
bytes mac = 6;
// Points to the line where the count value of this vector is defined.
int32 line_number = 15;
}
message HmacNistVectors {
string filename = 1;
string hashname = 2;
repeated HmacNistTest tests = 3;
}
message HmacWycheproofTest {
int32 count = 1;
string comment = 2;
bytes key = 3;
bytes msg = 4;
bytes mac = 5;
string result = 6;
repeated string flags = 7;
}
message HmacWycheproofGroup {
int32 key_size = 1;
int32 tag_size = 2;
repeated HmacWycheproofTest tests = 3;
}
message HmacWycheproofVectors {
string filename = 1;
string algorithm = 2;
string version = 3;
repeated string header = 4;
int32 number_of_tests = 5;
map<string, string> notes = 6;
repeated HmacWycheproofGroup groups = 7;
}