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() and final_verify(), both of which require init() and update(). For a simpler interface, where only one function is required, see HMAC.

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 and resilience options. SHA-3 functions are not covered by NIST vectors (see HmacVectors).

Parameters:
  • hmac (HMAC | HMAC_IUF) – The implementation to test. Must conform to either the HMAC interface or the HMAC_IUF interface.

  • hash_function (Hash) – The hash function to use with this HMAC implementation.

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 and verify.

>>> 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:
  • hmac (HMAC | HMAC_IUF) – The implementation to test. Must conform to either the HMAC interface or the HMAC_IUF interface.

  • hash_function (Hash) – The hash function to use with HMAC.

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:
  • hmac (HMAC | HMAC_IUF) – The implementation to test. Must conform to either the HMAC interface or the HMAC_IUF interface.

  • hash_function (Hash) – The hash function to use with HMAC.

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:
  • hmac (HMAC | HMAC_IUF) – The implementation to test. Must conform to either the HMAC interface or the HMAC_IUF interface.

  • hash_function (Hash) – The hash function to use with HMAC.

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:
  • hmac (HMAC | HMAC_IUF) – The implementation to test. Must conform to either the HMAC interface or the HMAC_IUF interface.

  • hash_function (Hash) – The hash function to use with HMAC.

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#

crypto_condor.primitives.HMAC.is_hmac_iuf(hmac)#

Checks if a class conforms to the HMAC_IUF interface.

Parameters:

hmac (Any) – The class to check.

Returns:

True if the class conforms to the HMAC_IUF interface, False is it conforms to the HMAC interface, and None if it doesn’t conform to neither.

Return type:

bool | None

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:

HMAC test vectors#

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;
}