-
Notifications
You must be signed in to change notification settings - Fork 791
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[cryptolib, testing] Add testing support to HMAC streaming
This PR adds a big testing harness for HMAC. Namely, it brings: * A py script to generate a HMAC/SHA2 vector for varying sizes (using Crypto.Hash lib) * Another py script to determine random parameters and run the prev script for to obtain 20 vecs * tpl/set scripts to conver generated hjson headers to C headers * hmac_functest to drive all variants of SHA2/HMAC functions from the generated vectors. Signed-off-by: Fatih Balli <[email protected]>
- Loading branch information
1 parent
3f703b6
commit 8e4b002
Showing
8 changed files
with
581 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Copyright lowRISC contributors (OpenTitan project). | ||
// Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#include "sw/device/lib/crypto/drivers/entropy.h" | ||
#include "sw/device/lib/crypto/drivers/hmac.h" | ||
#include "sw/device/lib/crypto/impl/integrity.h" | ||
#include "sw/device/lib/crypto/include/datatypes.h" | ||
#include "sw/device/lib/crypto/include/hash.h" | ||
#include "sw/device/lib/crypto/include/mac.h" | ||
#include "sw/device/lib/runtime/log.h" | ||
#include "sw/device/lib/testing/test_framework/check.h" | ||
#include "sw/device/lib/testing/test_framework/ottf_main.h" | ||
|
||
// The autogen rule that creates this header creates it in a directory named | ||
// after the rule, then manipulates the include path in the | ||
// cc_compilation_context to include that directory, so the compiler will find | ||
// the version of this file matching the Bazel rule under test. | ||
#include "hmac_testvectors.h" | ||
|
||
// Module ID for status codes. | ||
#define MODULE_ID MAKE_MODULE_ID('h', 'm', 'a') | ||
|
||
// Global pointer to the current test vector. | ||
static hmac_test_vector_t *current_test_vector = NULL; | ||
|
||
/** | ||
* Determines `hash_mode` for given SHA-2 test vectors. | ||
* | ||
* Note that for HMAC operations, mode information is part of the key struct, | ||
* hence this function is only used for hash vectors. | ||
* | ||
* @param test_vec The pointer to the test vector. | ||
* @param[out] hash_mode The determined hash_mode of the given test vector. | ||
*/ | ||
static status_t get_hash_mode(hmac_test_vector_t *test_vec, | ||
otcrypto_hash_mode_t *hash_mode) { | ||
switch (test_vec->test_operation) { | ||
case kHmacTestOperationSha256: | ||
*hash_mode = kOtcryptoHashModeSha256; | ||
return OTCRYPTO_OK; | ||
case kHmacTestOperationSha384: | ||
*hash_mode = kOtcryptoHashModeSha384; | ||
return OTCRYPTO_OK; | ||
case kHmacTestOperationSha512: | ||
*hash_mode = kOtcryptoHashModeSha512; | ||
return OTCRYPTO_OK; | ||
default: | ||
return OTCRYPTO_BAD_ARGS; | ||
} | ||
} | ||
|
||
/** | ||
* Run the test pointed to by `current_test_vector`. | ||
*/ | ||
static status_t run_test_vector(void) { | ||
// Populate `checksum` and `config.security_level` fields. | ||
current_test_vector->key.checksum = | ||
integrity_blinded_checksum(¤t_test_vector->key); | ||
|
||
otcrypto_hmac_context_t hmac_ctx; | ||
otcrypto_hash_context_t hash_ctx; | ||
// The test vectors already have the correct digest sizes hardcoded. | ||
size_t digest_len = current_test_vector->digest.len; | ||
// Allocate the buffer for the maximum digest size. | ||
uint32_t act_tag[kHmacMaxDigestBits]; | ||
otcrypto_word32_buf_t tag_buf = { | ||
.data = act_tag, | ||
.len = digest_len, | ||
}; | ||
otcrypto_hash_digest_t hash_digest = { | ||
// .mode is to be determined below in switch-case block. | ||
.data = act_tag, | ||
.len = digest_len, | ||
}; | ||
size_t break_index = current_test_vector->message.len / 2; | ||
otcrypto_const_byte_buf_t msg_part1 = { | ||
.data = current_test_vector->message.data, | ||
.len = break_index, | ||
}; | ||
otcrypto_const_byte_buf_t msg_part2 = { | ||
.data = ¤t_test_vector->message.data[break_index], | ||
.len = current_test_vector->message.len - break_index, | ||
}; | ||
switch (current_test_vector->test_operation) { | ||
case kHmacTestOperationSha256: | ||
OT_FALLTHROUGH_INTENDED; | ||
case kHmacTestOperationSha384: | ||
OT_FALLTHROUGH_INTENDED; | ||
case kHmacTestOperationSha512: | ||
TRY(get_hash_mode(current_test_vector, &hash_digest.mode)); | ||
TRY(otcrypto_hash_init(&hash_ctx, hash_digest.mode)); | ||
TRY(otcrypto_hash_update(&hash_ctx, msg_part1)); | ||
TRY(otcrypto_hash_update(&hash_ctx, msg_part2)); | ||
TRY(otcrypto_hash_final(&hash_ctx, hash_digest)); | ||
break; | ||
case kHmacTestOperationHmacSha256: | ||
OT_FALLTHROUGH_INTENDED; | ||
case kHmacTestOperationHmacSha384: | ||
OT_FALLTHROUGH_INTENDED; | ||
case kHmacTestOperationHmacSha512: | ||
TRY(otcrypto_hmac_init(&hmac_ctx, ¤t_test_vector->key)); | ||
TRY(otcrypto_hmac_update(&hmac_ctx, msg_part1)); | ||
TRY(otcrypto_hmac_update(&hmac_ctx, msg_part2)); | ||
TRY(otcrypto_hmac_final(&hmac_ctx, tag_buf)); | ||
break; | ||
default: | ||
return OTCRYPTO_BAD_ARGS; | ||
} | ||
TRY_CHECK_ARRAYS_EQ(act_tag, current_test_vector->digest.data, digest_len); | ||
return OTCRYPTO_OK; | ||
} | ||
|
||
OTTF_DEFINE_TEST_CONFIG(); | ||
bool test_main(void) { | ||
LOG_INFO("Testing cryptolib HMAC driver."); | ||
CHECK_STATUS_OK(entropy_complex_init()); | ||
status_t test_result = OK_STATUS(); | ||
for (size_t i = 0; i < ARRAYSIZE(kHmacTestVectors); i++) { | ||
current_test_vector = &kHmacTestVectors[i]; | ||
LOG_INFO("Running test %d of %d, test vector identifier: %s", i + 1, | ||
ARRAYSIZE(kHmacTestVectors), | ||
current_test_vector->vector_identifier); | ||
EXECUTE_TEST(test_result, run_test_vector); | ||
} | ||
return status_ok(test_result); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright lowRISC contributors (OpenTitan project). | ||
# Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import argparse | ||
import random | ||
import sys | ||
|
||
import hjson | ||
import hmac_gen_single_testvector | ||
''' | ||
Generate multiple test vectos | ||
''' | ||
|
||
|
||
def gen_random_test(idx): | ||
random_instance = random.Random(idx) | ||
operation = random_instance.choice(["SHA256", "HMAC256", "SHA384", "HMAC384", | ||
"SHA512", "HMAC512"]) | ||
input_msg_len = 8 * random_instance.randint(0, 1000) | ||
# Ensure that the key length is larger or equal to security parameter. | ||
# Also, KMAC HWIP only supports a discrete set of key lengths. | ||
if operation == "HMAC256": | ||
key_len = 256 | ||
elif operation == "HMAC384": | ||
key_len = 384 | ||
elif operation == "HMAC512": | ||
key_len = 512 | ||
else: | ||
key_len = 0 | ||
|
||
return hmac_gen_single_testvector.gen_random_test(idx, operation, input_msg_len, key_len) | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('n', | ||
type=int, | ||
help='Number of random test vectors to generate.') | ||
parser.add_argument('outfile', | ||
metavar='FILE', | ||
type=argparse.FileType('w'), | ||
help='Write output to this file.') | ||
|
||
args = parser.parse_args() | ||
|
||
testvecs = [] | ||
for i in range(args.n): | ||
testvecs.append(gen_random_test(i)) | ||
|
||
hjson.dump(testvecs, args.outfile) | ||
args.outfile.close() | ||
|
||
return 0 | ||
|
||
|
||
if __name__ == '__main__': | ||
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
#!/usr/bin/env python3 | ||
# Copyright lowRISC contributors (OpenTitan project). | ||
# Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import argparse | ||
import random | ||
import sys | ||
|
||
import hjson | ||
from Crypto.Hash import HMAC, SHA256, SHA384, SHA512 | ||
|
||
''' | ||
Generate a HMAC/SHA2 vectors for specified sizes. | ||
''' | ||
|
||
|
||
def validate_inputs(operation, input_msg_len, key_len): | ||
if operation in ["SHA256", "SHA384", "SHA512"] and key_len != 0: | ||
raise ValueError("SHA operations should have no key_len inputs") | ||
if operation in ["HMAC256", "HMAC384", "HMAC512"] and key_len == 0: | ||
raise ValueError("key_len needs to be larger than 0 for HMAC") | ||
if input_msg_len % 8 != 0: | ||
raise ValueError("input_msg_len needs to be divisible by 8") | ||
|
||
|
||
# Generate a HMAC test vector and return it as string in hjson format. | ||
# The input length arguments are all in bit size. | ||
def gen_random_test(seed, operation, input_msg_len, key_len): | ||
|
||
validate_inputs(operation, input_msg_len, key_len) | ||
|
||
random_instance = random.Random(seed) | ||
random_instance.seed(seed) | ||
input_msg = random_instance.randbytes(input_msg_len // 8) | ||
key = random_instance.randbytes(key_len // 8) | ||
|
||
if operation == "HMAC256": | ||
mac = HMAC.new(key=key, digestmod=SHA256) | ||
elif operation == "HMAC384": | ||
mac = HMAC.new(key=key, digestmod=SHA384) | ||
elif operation == "HMAC512": | ||
mac = HMAC.new(key=key, digestmod=SHA512) | ||
elif operation == "SHA256": | ||
mac = SHA256.new() | ||
elif operation == "SHA384": | ||
mac = SHA384.new() | ||
elif operation == "SHA512": | ||
mac = SHA512.new() | ||
|
||
mac.update(input_msg) | ||
digest = mac.hexdigest() | ||
vector_identifier = \ | ||
"./sw/device/tests/crypto/hmac_gen_single_testvector.py "\ | ||
"--seed={} --key_len={} --operation={} --input_msg_len={} "\ | ||
"<output-file>"\ | ||
.format(seed, key_len, operation, input_msg_len) | ||
|
||
print(vector_identifier) | ||
testvec = { | ||
'vector_identifier': vector_identifier, | ||
'operation': operation, | ||
'input_msg': '0x' + input_msg.hex(), | ||
'digest': '0x' + digest, | ||
} | ||
|
||
if operation in ["HMAC256", "HMAC384", "HMAC512"]: | ||
testvec['key'] = '0x' + key.hex() | ||
return testvec | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument('--seed', | ||
required=True, | ||
type=int, | ||
help='Seed for randomness.') | ||
parser.add_argument('--key_len', | ||
required=True, | ||
type=int, | ||
choices=[0, 128, 256, 384, 512, 1024], | ||
default=0, | ||
help='Key length (in bits).') | ||
parser.add_argument('--operation', | ||
required=True, | ||
choices=['SHA256', 'SHA384', 'SHA512', 'HMAC256', 'HMAC384', 'HMAC512'], | ||
help='SHA/HMAC mode (e.g. SHA256 or HMAC256).') | ||
parser.add_argument('--input_msg_len', | ||
required=True, | ||
type=int, | ||
help='Input message length (in bits).') | ||
parser.add_argument('outfile', | ||
metavar='FILE', | ||
type=argparse.FileType('w'), | ||
help='Write output to this file.') | ||
|
||
args = parser.parse_args() | ||
|
||
testvecs = gen_random_test(args.seed, args.operation, args.input_msg_len, | ||
args.key_len) | ||
|
||
hjson.dump(testvecs, args.outfile) | ||
args.outfile.close() | ||
|
||
return 0 | ||
|
||
|
||
if __name__ == '__main__': | ||
sys.exit(main()) |
Oops, something went wrong.