2sha: Add a vb2_hash type to make it easier to work with hashes

I'm prototyping some coreboot code to closer integrate vboot with CBFS
(per-file hashing and that stuff). While doing that, I noticed that it
would be neat to have a standardized serializable representation for any
kind of vboot hash. We already have something like that in CBFS
attributes, but if we want to use it more generally it makes more sense
to put it in vboot. This patch adds a suitable structure defintion to
2sha.h and two utility functions that can be used to work with it.

Also add alloca() because I need it and fix the return types of
vb2_..._size(), because those are just plain wrong.

BRANCH=None
BUG=None
TEST=make runtests

Change-Id: I4b535ad43704693463fb114d6a81d2b5689a87b9
Signed-off-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/1963614
Reviewed-by: Joel Kitching <kitching@chromium.org>
This commit is contained in:
Julius Werner 2019-09-30 17:52:19 -07:00 committed by Commit Bot
parent b597ea7a01
commit ac75f65b96
5 changed files with 199 additions and 4 deletions

View File

@ -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

View File

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

View File

@ -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
*/

View File

@ -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_ */

115
tests/vb2_sha_api_tests.c Normal file
View File

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