diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | firmware/2lib/2sha_utility.c | 19 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 3 | ||||
-rw-r--r-- | firmware/2lib/include/2sha.h | 64 | ||||
-rw-r--r-- | tests/vb2_sha_api_tests.c | 115 |
5 files changed, 199 insertions, 4 deletions
@@ -756,6 +756,7 @@ TEST2X_NAMES = \ tests/vb2_secdata_firmware_tests \ tests/vb2_secdata_fwmp_tests \ tests/vb2_secdata_kernel_tests \ + tests/vb2_sha_api_tests \ tests/vb2_sha_tests \ tests/hmac_test @@ -1317,6 +1318,7 @@ run2tests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_firmware_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_fwmp_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_secdata_kernel_tests + ${RUNTEST} ${BUILD_RUN}/tests/vb2_sha_api_tests ${RUNTEST} ${BUILD_RUN}/tests/vb2_sha_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_api_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vb20_kernel_tests diff --git a/firmware/2lib/2sha_utility.c b/firmware/2lib/2sha_utility.c index a267edde..8c6f4b80 100644 --- a/firmware/2lib/2sha_utility.c +++ b/firmware/2lib/2sha_utility.c @@ -56,7 +56,7 @@ enum vb2_hash_algorithm vb2_crypto_to_hash(uint32_t algorithm) return VB2_HASH_INVALID; } -vb2_error_t vb2_digest_size(enum vb2_hash_algorithm hash_alg) +size_t vb2_digest_size(enum vb2_hash_algorithm hash_alg) { switch (hash_alg) { #if VB2_SUPPORT_SHA1 @@ -76,7 +76,7 @@ vb2_error_t vb2_digest_size(enum vb2_hash_algorithm hash_alg) } } -vb2_error_t vb2_hash_block_size(enum vb2_hash_algorithm alg) +size_t vb2_hash_block_size(enum vb2_hash_algorithm alg) { switch (alg) { #if VB2_SUPPORT_SHA1 @@ -211,3 +211,18 @@ vb2_error_t vb2_digest_buffer(const uint8_t *buf, uint32_t size, return vb2_digest_finalize(&dc, digest, digest_size); } + +vb2_error_t vb2_hash_verify(const void *buf, uint32_t size, + const struct vb2_hash *hash) +{ + uint8_t hash_buf[VB2_MAX_DIGEST_SIZE]; + size_t hash_size = vb2_digest_size(hash->algo); + vb2_error_t rv = vb2_digest_buffer(buf, size, hash->algo, + hash_buf, hash_size); + if (rv) + return rv; + if (memcmp(hash_buf, hash->bytes.raw, hash_size)) + return VB2_ERROR_SHA_MISMATCH; + else + return VB2_SUCCESS; +} diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h index b504a087..fa6bf68a 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -110,6 +110,9 @@ enum vb2_return_code { /* Digest size buffer too small in vb2_digest_finalize() */ VB2_ERROR_SHA_FINALIZE_DIGEST_SIZE, + /* Hash mismatch in vb2_hash_verify() */ + VB2_ERROR_SHA_MISMATCH, + /********************************************************************** * RSA errors */ diff --git a/firmware/2lib/include/2sha.h b/firmware/2lib/include/2sha.h index 646c7c17..32af9f74 100644 --- a/firmware/2lib/include/2sha.h +++ b/firmware/2lib/include/2sha.h @@ -97,6 +97,36 @@ struct vb2_digest_context { int using_hwcrypto; }; +/* + * Serializable data structure that can store any vboot hash. Layout used in + * CBFS attributes that need to be backwards-compatible -- do not change! + * When serializing/deserizaling this, you should store/load (offsetof(bytes) + + * vb2_digest_size(algo)), not the full size of this structure. + */ +struct vb2_hash { + /* enum vb2_hash_algorithm. Fixed width for serialization. + Single byte to avoid endianness issues. */ + uint8_t algo; + /* Padding to align and to match existing CBFS attribute. */ + uint8_t reserved[3]; + /* The actual digest. Can add new types here as required. */ + union { + uint8_t raw[0]; +#if VB2_SUPPORT_SHA1 + uint8_t sha1[VB2_SHA1_DIGEST_SIZE]; +#endif +#if VB2_SUPPORT_SHA256 + uint8_t sha256[VB2_SHA256_DIGEST_SIZE]; +#endif +#if VB2_SUPPORT_SHA512 + uint8_t sha512[VB2_SHA512_DIGEST_SIZE]; +#endif + } bytes; /* This has a name so that it's easy to sizeof(). */ +}; +_Static_assert(sizeof(((struct vb2_hash *)0)->bytes) <= VB2_MAX_DIGEST_SIZE, + "Must update VB2_MAX_DIGEST_SIZE for new digests!"); +_Static_assert(VB2_HASH_ALG_COUNT <= UINT8_MAX, "vb2_hash.algo overflow!"); + /** * Initialize a hash context. * @@ -159,7 +189,7 @@ enum vb2_hash_algorithm vb2_crypto_to_hash(uint32_t algorithm); * @param hash_alg Hash algorithm * @return The size of the digest, or 0 if error. */ -vb2_error_t vb2_digest_size(enum vb2_hash_algorithm hash_alg); +size_t vb2_digest_size(enum vb2_hash_algorithm hash_alg); /** * Return the block size of a hash algorithm. @@ -167,7 +197,7 @@ vb2_error_t vb2_digest_size(enum vb2_hash_algorithm hash_alg); * @param hash_alg Hash algorithm * @return The block size of the algorithm, or 0 if error. */ -vb2_error_t vb2_hash_block_size(enum vb2_hash_algorithm alg); +size_t vb2_hash_block_size(enum vb2_hash_algorithm alg); /** * Return the name of a hash algorithm @@ -226,4 +256,34 @@ vb2_error_t vb2_digest_buffer(const uint8_t *buf, uint32_t size, enum vb2_hash_algorithm hash_alg, uint8_t *digest, uint32_t digest_size); +/** + * Fill a vb2_hash structure with the hash of a buffer. + * + * @param buf Buffer to hash + * @param size Size of |buf| in bytes + * @param algo The hash algorithm to use (and store in |hash|) + * @param hash vb2_hash structure to fill with the hash of |buf| + * @return VB2_SUCCESS, or non-zero on error. + */ +static inline vb2_error_t vb2_hash_calculate(const void *buf, uint32_t size, + enum vb2_hash_algorithm algo, + struct vb2_hash *hash) +{ + hash->algo = algo; + return vb2_digest_buffer(buf, size, algo, hash->bytes.raw, + vb2_digest_size(algo)); +} + +/** + * Verify that a vb2_hash matches a buffer. + * + * @param buf Buffer to hash and match to |hash| + * @param size Size of |buf| in bytes + * @param hash Hash to compare to the buffer + * @return VB2_SUCCESS if hash matches, VB2_ERROR_SHA_MISMATCH if hash doesn't + * match, or non-zero on other error. + */ +vb2_error_t vb2_hash_verify(const void *buf, uint32_t size, + const struct vb2_hash *hash); + #endif /* VBOOT_REFERENCE_2SHA_H_ */ diff --git a/tests/vb2_sha_api_tests.c b/tests/vb2_sha_api_tests.c new file mode 100644 index 00000000..21e32684 --- /dev/null +++ b/tests/vb2_sha_api_tests.c @@ -0,0 +1,115 @@ +/* Copyright 2019 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Tests for vb2_hash_(calculate|verify) functions. + */ + +#include "2return_codes.h" +#include "2sha.h" +#include "2sysincludes.h" +#include "test_common.h" + +uint8_t mock_sha1[] = {0x1, 0x3, 0x5, 0x2, 0x4, 0x6, 0xa, 0xb, 0xc, 0xd, + 0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf, 0x0, 0xf0}; +_Static_assert(sizeof(mock_sha1) == VB2_SHA1_DIGEST_SIZE, ""); + +struct vb2_hash mock_hash; +uint8_t mock_buffer[] = "Mock Buffer"; + +vb2_error_t mock_init_rv; +vb2_error_t mock_extend_rv; +vb2_error_t mock_finalize_rv; + +static void reset_common_data(void) +{ + memset(&mock_hash, 0xaa, sizeof(mock_hash)); + + mock_init_rv = VB2_SUCCESS; + mock_extend_rv = VB2_SUCCESS; + mock_finalize_rv = VB2_SUCCESS; +} + +vb2_error_t vb2_digest_init(struct vb2_digest_context *dc, + enum vb2_hash_algorithm hash_alg) +{ + if (hash_alg != VB2_HASH_SHA1) + return VB2_ERROR_MOCK; + return mock_init_rv; +} + +vb2_error_t vb2_digest_extend(struct vb2_digest_context *dc, const uint8_t *buf, + uint32_t size) +{ + TEST_PTR_EQ(buf, mock_buffer, "digest_extend unexpected buf"); + TEST_EQ(size, sizeof(mock_buffer), "digest_extend unexpected size"); + return mock_extend_rv; +} + +vb2_error_t vb2_digest_finalize(struct vb2_digest_context *dc, uint8_t *digest, + uint32_t size) +{ + TEST_EQ(size, VB2_SHA1_DIGEST_SIZE, "digest_finalize unexpected size"); + memcpy(digest, mock_sha1, size); + return mock_finalize_rv; +} + +static void vb2_hash_calculate_tests(void) +{ + reset_common_data(); + TEST_SUCC(vb2_hash_calculate(&mock_buffer, sizeof(mock_buffer), + VB2_HASH_SHA1, &mock_hash), + "hash_calculate success"); + TEST_SUCC(memcmp(mock_hash.bytes.sha1, mock_sha1, sizeof(mock_sha1)), + " got the right hash"); + TEST_EQ(mock_hash.algo, VB2_HASH_SHA1, " set algo correctly"); + + reset_common_data(); + mock_init_rv = VB2_ERROR_MOCK; + TEST_EQ(vb2_hash_calculate(mock_buffer, sizeof(mock_buffer), + VB2_HASH_SHA1, &mock_hash), + VB2_ERROR_MOCK, "hash_calculate init error"); + + reset_common_data(); + mock_extend_rv = VB2_ERROR_MOCK; + TEST_EQ(vb2_hash_calculate(mock_buffer, sizeof(mock_buffer), + VB2_HASH_SHA1, &mock_hash), + VB2_ERROR_MOCK, "hash_calculate extend error"); + + reset_common_data(); + mock_finalize_rv = VB2_ERROR_MOCK; + TEST_EQ(vb2_hash_calculate(mock_buffer, sizeof(mock_buffer), + VB2_HASH_SHA1, &mock_hash), + VB2_ERROR_MOCK, "hash_calculate finalize error"); +} + +static void vb2_hash_verify_tests(void) +{ + reset_common_data(); + + memcpy(mock_hash.bytes.sha1, mock_sha1, sizeof(mock_sha1)); + mock_hash.algo = VB2_HASH_SHA1; + TEST_SUCC(vb2_hash_verify(mock_buffer, sizeof(mock_buffer), + &mock_hash), "hash_verify success"); + + memcpy(mock_hash.bytes.sha1, mock_sha1, sizeof(mock_sha1)); + mock_hash.algo = VB2_HASH_SHA256; + TEST_EQ(vb2_hash_verify(mock_buffer, sizeof(mock_buffer), + &mock_hash), VB2_ERROR_MOCK, + "hash_verify wrong algo"); + + memcpy(mock_hash.bytes.sha1, mock_sha1, sizeof(mock_sha1)); + mock_hash.bytes.raw[5] = 0xfe; + mock_hash.algo = VB2_HASH_SHA1; + TEST_EQ(vb2_hash_verify(mock_buffer, sizeof(mock_buffer), + &mock_hash), VB2_ERROR_SHA_MISMATCH, + "hash_verify mismatch"); +} + +int main(int argc, char *argv[]) +{ + vb2_hash_calculate_tests(); + vb2_hash_verify_tests(); + + return gTestSuccess ? 0 : 255; +} |