summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2019-09-30 17:52:19 -0700
committerCommit Bot <commit-bot@chromium.org>2020-01-16 03:07:34 +0000
commitac75f65b96aa886c85385fc7fff54340071b9851 (patch)
treed086c61fcc89469374ec1293844bf7ead34865d6
parentb597ea7a016baa1a1416ca3f78aea2220479691c (diff)
downloadvboot-ac75f65b96aa886c85385fc7fff54340071b9851.tar.gz
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>
-rw-r--r--Makefile2
-rw-r--r--firmware/2lib/2sha_utility.c19
-rw-r--r--firmware/2lib/include/2return_codes.h3
-rw-r--r--firmware/2lib/include/2sha.h64
-rw-r--r--tests/vb2_sha_api_tests.c115
5 files changed, 199 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index 28b6460b..c71f5e32 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
+}