diff --git a/sw/device/tests/crypto/cryptotest/firmware/hmac.c b/sw/device/tests/crypto/cryptotest/firmware/hmac.c index 0a56deba5e773..52b000fb74190 100644 --- a/sw/device/tests/crypto/cryptotest/firmware/hmac.c +++ b/sw/device/tests/crypto/cryptotest/firmware/hmac.c @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 +#include "sw/device/lib/base/math.h" #include "sw/device/lib/base/memory.h" #include "sw/device/lib/base/status.h" #include "sw/device/lib/crypto/impl/integrity.h" @@ -13,11 +14,7 @@ #include "sw/device/lib/ujson/ujson.h" #include "sw/device/tests/crypto/cryptotest/json/hmac_commands.h" -const unsigned int kOtcryptoHmacTagBytesSha256 = 32; -const unsigned int kOtcryptoHmacTagBytesSha384 = 48; -const unsigned int kOtcryptoHmacTagBytesSha512 = 64; - -const int MaxTagBytes = kOtcryptoHmacTagBytesSha512; +const int MaxTagBytes = kSha512DigestBytes; const int MaxTagWords = MaxTagBytes / sizeof(uint32_t); // Random value for masking, as large as the longest test key. This value @@ -48,15 +45,15 @@ status_t handle_hmac(ujson_t *uj) { switch (uj_hash_alg) { case kCryptotestHmacHashAlgSha256: key_mode = kOtcryptoKeyModeHmacSha256; - tag_bytes = kOtcryptoHmacTagBytesSha256; + tag_bytes = kSha256DigestBytes; break; case kCryptotestHmacHashAlgSha384: key_mode = kOtcryptoKeyModeHmacSha384; - tag_bytes = kOtcryptoHmacTagBytesSha384; + tag_bytes = kSha384DigestBytes; break; case kCryptotestHmacHashAlgSha512: key_mode = kOtcryptoKeyModeHmacSha512; - tag_bytes = kOtcryptoHmacTagBytesSha512; + tag_bytes = kSha512DigestBytes; break; default: LOG_ERROR("Unsupported HMAC key mode: %d", uj_hash_alg); @@ -97,16 +94,51 @@ status_t handle_hmac(ujson_t *uj) { .len = tag_bytes / sizeof(uint32_t), .data = tag_buf, }; + // Test oneshot API otcrypto_status_t status = otcrypto_hmac(&key, input_message, tag); if (status.value != kOtcryptoStatusValueOk) { return INTERNAL(status.value); } - // Copy tag to uJSON type + // Copy oneshot tag to uJSON type cryptotest_hmac_tag_t uj_tag; - memcpy(uj_tag.tag, tag_buf, tag_bytes); + memcpy(uj_tag.oneshot_tag, tag_buf, tag_bytes); uj_tag.tag_len = tag_bytes; - // Send tag to host via UART + // Zero out tag_buf to mitigate chance of a false positive in the + // stepwise test + memset(tag_buf, 0, tag_bytes); + // Test streaming API + otcrypto_hmac_context_t ctx; + status = otcrypto_hmac_init(&ctx, &key); + if (status.value != kOtcryptoStatusValueOk) { + return INTERNAL(status.value); + } + // Split up input message into 2 shares for better coverage of + // streaming API + otcrypto_const_byte_buf_t input_message_share1 = { + .len = uj_message.message_len / 2, + .data = msg_buf, + }; + otcrypto_const_byte_buf_t input_message_share2 = { + .len = ceil_div(uj_message.message_len, 2), + .data = &msg_buf[uj_message.message_len / 2], + }; + status = otcrypto_hmac_update(&ctx, input_message_share1); + if (status.value != kOtcryptoStatusValueOk) { + return INTERNAL(status.value); + } + status = otcrypto_hmac_update(&ctx, input_message_share2); + if (status.value != kOtcryptoStatusValueOk) { + return INTERNAL(status.value); + } + status = otcrypto_hmac_final(&ctx, tag); + if (status.value != kOtcryptoStatusValueOk) { + return INTERNAL(status.value); + } + // Copy streaming tag to uJSON output + memcpy(uj_tag.streaming_tag, tag_buf, tag_bytes); + + // Send tags to host via UART RESP_OK(ujson_serialize_cryptotest_hmac_tag_t, uj, &uj_tag); return OK_STATUS(0); } diff --git a/sw/device/tests/crypto/cryptotest/json/hmac_commands.h b/sw/device/tests/crypto/cryptotest/json/hmac_commands.h index f57edfe7e033e..8bcd58faaa9dc 100644 --- a/sw/device/tests/crypto/cryptotest/json/hmac_commands.h +++ b/sw/device/tests/crypto/cryptotest/json/hmac_commands.h @@ -35,7 +35,8 @@ UJSON_SERDE_STRUCT(CryptotestHmacMessage, cryptotest_hmac_message_t, HMAC_MESSAG UJSON_SERDE_STRUCT(CryptotestHmacKey, cryptotest_hmac_key_t, HMAC_KEY); #define HMAC_TAG(field, string) \ - field(tag, uint8_t, HMAC_CMD_MAX_TAG_BYTES) \ + field(oneshot_tag, uint8_t, HMAC_CMD_MAX_TAG_BYTES) \ + field(streaming_tag, uint8_t, HMAC_CMD_MAX_TAG_BYTES) \ field(tag_len, size_t) UJSON_SERDE_STRUCT(CryptotestHmacTag, cryptotest_hmac_tag_t, HMAC_TAG); diff --git a/sw/host/tests/crypto/hmac_kat/src/main.rs b/sw/host/tests/crypto/hmac_kat/src/main.rs index 06b5647174782..00371601e2497 100644 --- a/sw/host/tests/crypto/hmac_kat/src/main.rs +++ b/sw/host/tests/crypto/hmac_kat/src/main.rs @@ -102,24 +102,32 @@ fn run_hmac_testcase( .send(&*uart)?; let hmac_tag = CryptotestHmacTag::recv(&*uart, opts.timeout, false)?; - let success = if test_case.tag.len() > hmac_tag.tag_len { - // If we got a shorter tag back then the test asks for, we can't accept the tag, even if - // the beginning bytes match. - false - } else { - // Some of the NIST test cases only specify the beginning bytes of the expected tag, so we - // only check up to what the test specifies. - test_case.tag[..] == hmac_tag.tag[..test_case.tag.len()] - }; - if test_case.result != success { - log::info!( - "FAILED test #{}: expected = {}, actual = {}", - test_case.test_case_id, - test_case.result, - success - ); + let mut failed = false; + for (mode, actual_tag) in [ + ("oneshot", hmac_tag.oneshot_tag), + ("streaming", hmac_tag.streaming_tag), + ] { + let success = if test_case.tag.len() > hmac_tag.tag_len { + // If we got a shorter tag back then the test asks for, we can't accept the tag, even if + // the beginning bytes match. + false + } else { + // Some of the NIST test cases only specify the beginning bytes of the expected tag, so we + // only check up to what the test specifies. + test_case.tag[..] == actual_tag[..test_case.tag.len()] + }; + if test_case.result != success { + log::info!( + "FAILED test #{} in {} mode: expected = {}, actual = {}", + test_case.test_case_id, + mode, + test_case.result, + success + ); + failed = true; + } } - if success != test_case.result { + if failed { *fail_counter += 1; } Ok(())