2021-11-15 09:36:07 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
|
|
|
|
#include <cbfs.h>
|
|
|
|
#include <cbfs_glue.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <mocks/cbfs_util.h>
|
|
|
|
#include <tests/test.h>
|
|
|
|
|
|
|
|
#include "../libcbfs/cbfs.c"
|
|
|
|
|
|
|
|
/* Mocks */
|
|
|
|
|
|
|
|
unsigned long virtual_offset = 0;
|
|
|
|
struct sysinfo_t lib_sysinfo;
|
|
|
|
|
|
|
|
size_t vb2_digest_size(enum vb2_hash_algorithm hash_alg)
|
|
|
|
{
|
|
|
|
if (hash_alg != VB2_HASH_SHA256) {
|
|
|
|
fail_msg("Unsupported hash algorithm: %d\n", hash_alg);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return VB2_SHA256_DIGEST_SIZE;
|
|
|
|
}
|
|
|
|
|
2022-08-09 03:08:35 +02:00
|
|
|
vb2_error_t vb2_hash_verify(bool allow_hwcrypto, const void *buf, uint32_t size,
|
|
|
|
const struct vb2_hash *hash)
|
2021-11-15 09:36:07 +01:00
|
|
|
{
|
2022-08-09 03:08:35 +02:00
|
|
|
assert_true(allow_hwcrypto);
|
2021-11-15 09:36:07 +01:00
|
|
|
check_expected_ptr(buf);
|
|
|
|
check_expected(size);
|
|
|
|
|
|
|
|
assert_int_equal(hash->algo, VB2_HASH_SHA256);
|
|
|
|
|
|
|
|
if (!memcmp(hash->sha256, good_hash, sizeof(good_hash)))
|
|
|
|
return VB2_SUCCESS;
|
|
|
|
|
|
|
|
if (!memcmp(hash->sha256, bad_hash, sizeof(bad_hash)))
|
|
|
|
return VB2_ERROR_SHA_MISMATCH;
|
|
|
|
|
|
|
|
fail_msg("%s called with bad hash", __func__);
|
|
|
|
return VB2_ERROR_SHA_MISMATCH;
|
|
|
|
}
|
|
|
|
|
2023-09-08 15:17:21 +02:00
|
|
|
bool vb2api_hwcrypto_allowed(struct vb2_context *ctx)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct vb2_context *vboot_get_context(void)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2021-11-15 09:36:07 +01:00
|
|
|
unsigned long ulzman(const unsigned char *src, unsigned long srcn, unsigned char *dst,
|
|
|
|
unsigned long dstn)
|
|
|
|
{
|
2022-06-15 09:03:04 +02:00
|
|
|
size_t copy_size = MIN(srcn, dstn);
|
|
|
|
function_called();
|
|
|
|
memcpy(dst, src, copy_size);
|
|
|
|
return copy_size;
|
2021-11-15 09:36:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn)
|
|
|
|
{
|
2022-06-15 09:03:04 +02:00
|
|
|
size_t copy_size = MIN(srcn, dstn);
|
|
|
|
function_called();
|
|
|
|
memcpy(dst, src, copy_size);
|
|
|
|
return copy_size;
|
2021-11-15 09:36:07 +01:00
|
|
|
}
|
|
|
|
|
2022-03-05 02:49:56 +01:00
|
|
|
enum cb_err cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name,
|
|
|
|
union cbfs_mdata *mdata_out, size_t *data_offset_out)
|
2021-11-15 09:36:07 +01:00
|
|
|
{
|
|
|
|
return CB_CBFS_CACHE_FULL;
|
|
|
|
}
|
|
|
|
|
2022-03-05 02:49:56 +01:00
|
|
|
enum cb_err cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out,
|
|
|
|
size_t *data_offset_out, struct vb2_hash *metadata_hash)
|
2021-11-15 09:36:07 +01:00
|
|
|
{
|
|
|
|
assert_non_null(dev);
|
|
|
|
check_expected(name);
|
|
|
|
|
2022-03-05 02:49:56 +01:00
|
|
|
enum cb_err ret = mock_type(enum cb_err);
|
2021-11-15 09:36:07 +01:00
|
|
|
if (ret != CB_SUCCESS)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
memcpy(mdata_out, mock_ptr_type(const union cbfs_mdata *), sizeof(union cbfs_mdata));
|
|
|
|
*data_offset_out = mock_type(size_t);
|
|
|
|
return CB_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2022-03-05 02:49:56 +01:00
|
|
|
static void expect_cbfs_lookup(const char *name, enum cb_err err, const union cbfs_mdata *mdata,
|
2021-11-15 09:36:07 +01:00
|
|
|
size_t data_offset_out)
|
|
|
|
{
|
|
|
|
expect_string(cbfs_lookup, name, name);
|
|
|
|
will_return(cbfs_lookup, err);
|
|
|
|
|
|
|
|
if (err == CB_SUCCESS) {
|
|
|
|
will_return(cbfs_lookup, mdata);
|
|
|
|
will_return(cbfs_lookup, data_offset_out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *cbfs_find_attr(const union cbfs_mdata *mdata, uint32_t attr_tag, size_t size_check)
|
|
|
|
{
|
|
|
|
return mock_ptr_type(void *);
|
|
|
|
}
|
|
|
|
|
2022-03-05 02:49:56 +01:00
|
|
|
enum cb_err fmap_locate_area(const char *name, size_t *offset, size_t *size)
|
2021-11-15 09:36:07 +01:00
|
|
|
{
|
|
|
|
*offset = 0;
|
|
|
|
*size = 0;
|
|
|
|
return CB_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t boot_device_read(void *buf, size_t offset, size_t size)
|
|
|
|
{
|
|
|
|
/* Offset should be based on an address from lib_sysinfo.cbfs_offset */
|
|
|
|
memcpy(buf, (void *)offset, size);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct vb2_hash *cbfs_file_hash(const union cbfs_mdata *mdata)
|
|
|
|
{
|
|
|
|
return mock_ptr_type(const struct vb2_hash *);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Utils */
|
|
|
|
|
|
|
|
static void clear_cbfs_boot_devices(void)
|
|
|
|
{
|
|
|
|
lib_sysinfo.cbfs_ro_mcache_offset = 0;
|
|
|
|
lib_sysinfo.cbfs_ro_mcache_size = 0;
|
|
|
|
lib_sysinfo.cbfs_offset = 0;
|
|
|
|
lib_sysinfo.cbfs_size = 0;
|
|
|
|
lib_sysinfo.cbfs_rw_mcache_offset = 0;
|
|
|
|
lib_sysinfo.cbfs_rw_mcache_size = 0;
|
|
|
|
memset((void *)cbfs_get_boot_device(true), 0, sizeof(struct cbfs_boot_device));
|
|
|
|
memset((void *)cbfs_get_boot_device(false), 0, sizeof(struct cbfs_boot_device));
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_cbfs(uint64_t offset, size_t size)
|
|
|
|
{
|
|
|
|
clear_cbfs_boot_devices();
|
|
|
|
lib_sysinfo.cbfs_offset = offset;
|
|
|
|
lib_sysinfo.cbfs_size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tests */
|
|
|
|
|
|
|
|
static int setup_test_cbfs(void **state)
|
|
|
|
{
|
|
|
|
clear_cbfs_boot_devices();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_cbfs_map_no_hash(void **state)
|
|
|
|
{
|
|
|
|
void *mapping = NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
set_cbfs((uint64_t)&file_no_hash, sizeof(file_no_hash));
|
|
|
|
|
|
|
|
expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS,
|
|
|
|
(const union cbfs_mdata *)&file_no_hash,
|
|
|
|
be32toh(file_no_hash.header.offset));
|
|
|
|
will_return(cbfs_find_attr, NULL);
|
|
|
|
|
|
|
|
if (CONFIG(LP_CBFS_VERIFICATION)) {
|
|
|
|
/* File with no hash. No hash causes hash mismatch by default,
|
|
|
|
so mapping will not be completed successfully. */
|
|
|
|
will_return(cbfs_file_hash, NULL);
|
|
|
|
mapping = cbfs_map(TEST_DATA_1_FILENAME, NULL);
|
|
|
|
assert_null(mapping);
|
|
|
|
} else {
|
|
|
|
mapping = cbfs_map(TEST_DATA_1_FILENAME, &size);
|
|
|
|
assert_non_null(mapping);
|
|
|
|
assert_int_equal(TEST_DATA_1_SIZE, size);
|
|
|
|
assert_memory_equal(test_data_1, mapping, size);
|
|
|
|
cbfs_unmap(mapping);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-15 09:03:04 +02:00
|
|
|
static void test_cbfs_map_valid_hash_impl(void **state, bool lz4_compressed)
|
2021-11-15 09:36:07 +01:00
|
|
|
{
|
|
|
|
void *mapping = NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
struct vb2_hash hash = {
|
|
|
|
.algo = VB2_HASH_SHA256,
|
|
|
|
};
|
|
|
|
memcpy(&hash.sha256, good_hash, VB2_SHA256_DIGEST_SIZE);
|
|
|
|
|
|
|
|
set_cbfs((uint64_t)&file_valid_hash, sizeof(file_valid_hash));
|
|
|
|
|
|
|
|
expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS,
|
|
|
|
(const union cbfs_mdata *)&file_valid_hash,
|
|
|
|
be32toh(file_valid_hash.header.offset));
|
|
|
|
|
2022-06-15 09:03:04 +02:00
|
|
|
if (lz4_compressed) {
|
|
|
|
struct cbfs_file_attr_compression cattr = {
|
|
|
|
.compression = htobe32(CBFS_COMPRESS_LZ4),
|
|
|
|
.decompressed_size = htobe32(TEST_DATA_1_SIZE),
|
|
|
|
};
|
|
|
|
will_return(cbfs_find_attr, &cattr);
|
|
|
|
expect_function_call(ulz4fn);
|
|
|
|
} else {
|
|
|
|
will_return(cbfs_find_attr, NULL);
|
|
|
|
}
|
2021-11-15 09:36:07 +01:00
|
|
|
|
|
|
|
if (CONFIG(LP_CBFS_VERIFICATION)) {
|
|
|
|
will_return(cbfs_file_hash, &hash);
|
|
|
|
expect_memory(vb2_hash_verify, buf,
|
|
|
|
&file_valid_hash.attrs_and_data[HASH_ATTR_SIZE], HASH_ATTR_SIZE);
|
|
|
|
expect_value(vb2_hash_verify, size, TEST_DATA_1_SIZE);
|
|
|
|
mapping = cbfs_map(TEST_DATA_1_FILENAME, &size);
|
|
|
|
assert_non_null(mapping);
|
|
|
|
assert_int_equal(TEST_DATA_1_SIZE, size);
|
|
|
|
assert_memory_equal(mapping, &file_valid_hash.attrs_and_data[HASH_ATTR_SIZE],
|
|
|
|
size);
|
|
|
|
} else {
|
|
|
|
mapping = cbfs_map(TEST_DATA_1_FILENAME, &size);
|
|
|
|
assert_non_null(mapping);
|
|
|
|
assert_int_equal(TEST_DATA_1_SIZE, size);
|
|
|
|
assert_memory_equal(test_data_1, mapping, size);
|
|
|
|
cbfs_unmap(mapping);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-15 09:03:04 +02:00
|
|
|
static void test_cbfs_map_valid_hash(void **state)
|
|
|
|
{
|
|
|
|
test_cbfs_map_valid_hash_impl(state, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_cbfs_map_valid_hash_with_lz4(void **state)
|
|
|
|
{
|
|
|
|
test_cbfs_map_valid_hash_impl(state, true);
|
|
|
|
}
|
|
|
|
|
2021-11-15 09:36:07 +01:00
|
|
|
static void test_cbfs_map_invalid_hash(void **state)
|
|
|
|
{
|
|
|
|
void *mapping = NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
struct vb2_hash hash = {
|
|
|
|
.algo = VB2_HASH_SHA256,
|
|
|
|
};
|
|
|
|
memcpy(&hash.sha256, bad_hash, VB2_SHA256_DIGEST_SIZE);
|
|
|
|
|
|
|
|
set_cbfs((uint64_t)&file_broken_hash, sizeof(file_broken_hash));
|
|
|
|
|
|
|
|
expect_cbfs_lookup(TEST_DATA_1_FILENAME, CB_SUCCESS,
|
|
|
|
(const union cbfs_mdata *)&file_broken_hash,
|
|
|
|
be32toh(file_broken_hash.header.offset));
|
|
|
|
will_return(cbfs_find_attr, NULL);
|
|
|
|
|
|
|
|
if (CONFIG(LP_CBFS_VERIFICATION)) {
|
|
|
|
will_return(cbfs_file_hash, &hash);
|
|
|
|
expect_memory(vb2_hash_verify, buf,
|
|
|
|
&file_broken_hash.attrs_and_data[HASH_ATTR_SIZE], HASH_ATTR_SIZE);
|
|
|
|
expect_value(vb2_hash_verify, size, TEST_DATA_1_SIZE);
|
|
|
|
mapping = cbfs_map(TEST_DATA_1_FILENAME, NULL);
|
|
|
|
assert_null(mapping);
|
|
|
|
} else {
|
|
|
|
mapping = cbfs_map(TEST_DATA_1_FILENAME, &size);
|
|
|
|
assert_non_null(mapping);
|
|
|
|
assert_int_equal(TEST_DATA_1_SIZE, size);
|
|
|
|
assert_memory_equal(test_data_1, mapping, size);
|
|
|
|
cbfs_unmap(mapping);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(void)
|
|
|
|
{
|
|
|
|
const struct CMUnitTest tests[] = {
|
|
|
|
cmocka_unit_test_setup(test_cbfs_map_no_hash, setup_test_cbfs),
|
|
|
|
cmocka_unit_test_setup(test_cbfs_map_valid_hash, setup_test_cbfs),
|
2022-06-15 09:03:04 +02:00
|
|
|
cmocka_unit_test_setup(test_cbfs_map_valid_hash_with_lz4, setup_test_cbfs),
|
2021-11-15 09:36:07 +01:00
|
|
|
cmocka_unit_test_setup(test_cbfs_map_invalid_hash, setup_test_cbfs),
|
|
|
|
};
|
|
|
|
|
|
|
|
return lp_run_group_tests(tests, NULL, NULL);
|
|
|
|
}
|