blob: dba9c8c498c8c083587088120f2b5525a79a4e7d [file] [log] [blame] [edit]
// Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by the GPL v2 license that can
// be found in the LICENSE file.
//
// Basic unittesting of dm-bht using google-gtest.
#include <gtest/gtest.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include "verity/logging.h"
// Pull in dm-bht.c so that we can access static functions.
// But disable verbose logging.
extern "C" {
#ifndef NDEBUG
#undef NDEBUG
#include "dm-bht.c"
#define NDEBUG 1
#else
#include "dm-bht.c"
#endif
}
#include "verity/dm-bht-userspace.h"
void* my_memalign(size_t boundary, size_t size) {
void* memptr;
if (posix_memalign(&memptr, boundary, size))
return NULL;
return memptr;
}
TEST(DmBht, CreateFailOnOverflow) {
struct dm_bht bht;
// This should fail.
EXPECT_EQ(-EINVAL, dm_bht_create(&bht, UINT_MAX, "sha1"));
}
// Simple test to help valgrind/tcmalloc catch bad mem management
TEST(DmBht, CreateZeroPopulateDestroy) {
struct dm_bht bht;
sector_t sectors;
// This should fail.
unsigned int blocks, total_blocks = 16384;
u8* data = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
u8* hash_data;
blocks = total_blocks;
// Store all the block hashes of blocks of 0.
memset(reinterpret_cast<void*>(data), 0, sizeof(data));
EXPECT_EQ(0, dm_bht_create(&bht, blocks, "sha256"));
dm_bht_set_read_cb(&bht, dm_bht_zeroread_callback);
sectors = dm_bht_sectors(&bht);
hash_data = new u8[verity_to_bytes(sectors)];
dm_bht_set_buffer(&bht, hash_data);
do {
EXPECT_EQ(dm_bht_store_block(&bht, blocks - 1, data), 0);
} while (--blocks > 0);
// Load the tree from the pre-populated hash data
for (blocks = 0; blocks < total_blocks; blocks += bht.node_count)
EXPECT_GE(dm_bht_populate(&bht, reinterpret_cast<void*>(this), blocks), 0);
EXPECT_EQ(0, dm_bht_compute(&bht));
EXPECT_EQ(0, dm_bht_destroy(&bht));
delete hash_data;
free(data);
}
class MemoryBhtTest : public ::testing::Test {
public:
void SetUp() { bht_ = NULL; }
void TearDown() {
hash_data_.clear();
if (bht_)
delete bht_;
bht_ = NULL;
}
int Read(sector_t start, u8* dst, sector_t count) {
EXPECT_LT(start, sectors_);
EXPECT_EQ(verity_to_bytes(count), PAGE_SIZE);
u8* src = &hash_data_[verity_to_bytes(start)];
memcpy(dst, src, verity_to_bytes(count));
return 0;
}
static int ReadCallback(void* mbht_instance,
sector_t start,
u8* dst,
sector_t count,
struct dm_bht_entry* entry) {
MemoryBhtTest* mbht = reinterpret_cast<MemoryBhtTest*>(mbht_instance);
mbht->Read(start, dst, count);
dm_bht_read_completed(entry, 0);
return 0;
}
protected:
// Creates a new dm_bht and sets it in the existing MemoryBht.
void SetupHash(const unsigned int total_blocks,
const char* digest_algorithm,
const char* salt,
void* hash_data) {
struct dm_bht bht;
u8* data = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(data, 0, PAGE_SIZE);
EXPECT_EQ(0, dm_bht_create(&bht, total_blocks, digest_algorithm));
if (salt)
dm_bht_set_salt(&bht, salt);
dm_bht_set_buffer(&bht, hash_data);
unsigned int blocks = total_blocks;
do {
EXPECT_EQ(dm_bht_store_block(&bht, blocks - 1, data), 0);
} while (--blocks > 0);
EXPECT_EQ(0, dm_bht_compute(&bht));
u8 digest[1024];
dm_bht_root_hexdigest(&bht, digest, sizeof(digest));
LOG(INFO) << "MemoryBhtTest root is " << digest;
free(data);
}
void SetupBht(const unsigned int total_blocks,
const char* digest_algorithm,
const char* salt) {
if (bht_)
delete bht_;
bht_ = new dm_bht;
EXPECT_EQ(0, dm_bht_create(bht_, total_blocks, digest_algorithm));
sectors_ = dm_bht_sectors(bht_);
hash_data_.resize(verity_to_bytes(sectors_));
if (salt)
dm_bht_set_salt(bht_, salt);
SetupHash(total_blocks, digest_algorithm, salt, &hash_data_[0]);
dm_bht_set_read_cb(bht_, MemoryBhtTest::ReadCallback);
// Load the tree from the pre-populated hash data
unsigned int blocks;
for (blocks = 0; blocks < total_blocks; blocks += bht_->node_count)
EXPECT_GE(dm_bht_populate(bht_, reinterpret_cast<void*>(this), blocks),
0);
}
struct dm_bht* bht_;
std::vector<u8> hash_data_;
sector_t sectors_;
};
TEST_F(MemoryBhtTest, CreateThenVerifyOk) {
static const unsigned int total_blocks = 16384;
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"45d65d6f9e5a962f4d80b5f1bd7a918152251c27bdad8c5f52b590c129833372";
// A page of all zeros
u8* zero_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(zero_page, 0, PAGE_SIZE);
SetupBht(total_blocks, "sha256", NULL);
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) {
DLOG(INFO) << "verifying block: " << blocks;
EXPECT_EQ(0, dm_bht_verify_block(bht_, blocks, zero_page, 0));
}
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(zero_page);
}
TEST_F(MemoryBhtTest, CreateThenVerifySingleLevel) {
static const unsigned int total_blocks = 32;
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"2d3a43008286f56536fa24dcdbf14d342f0548827e374210415c7be0b610d2ba";
// A page of all zeros
u8* zero_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(zero_page, 0, PAGE_SIZE);
SetupBht(total_blocks, "sha256", NULL);
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) {
DLOG(INFO) << "verifying block: " << blocks;
EXPECT_EQ(0, dm_bht_verify_block(bht_, blocks, zero_page, 0));
}
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(zero_page);
}
TEST_F(MemoryBhtTest, CreateThenVerifyRealParameters) {
static const unsigned int total_blocks = 217600;
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"15d5a180b5080a1d43e3fbd1f2cd021d0fc3ea91a8e330bad468b980c2fd4d8b";
// A page of all zeros
u8* zero_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(zero_page, 0, PAGE_SIZE);
SetupBht(total_blocks, "sha256", NULL);
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) {
DLOG(INFO) << "verifying block: " << blocks;
EXPECT_EQ(0, dm_bht_verify_block(bht_, blocks, zero_page, 0));
}
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(zero_page);
}
TEST_F(MemoryBhtTest, CreateThenVerifyOddLeafCount) {
static const unsigned int total_blocks = 16383;
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"dc8cec4220d388b05ba75c853f858bb8cc25edfb1d5d2f3be6bdf9edfa66dc6a";
// A page of all zeros
u8* zero_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(zero_page, 0, PAGE_SIZE);
SetupBht(total_blocks, "sha256", NULL);
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) {
DLOG(INFO) << "verifying block: " << blocks;
EXPECT_EQ(0, dm_bht_verify_block(bht_, blocks, zero_page, 0));
}
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(zero_page);
}
TEST_F(MemoryBhtTest, CreateThenVerifyOddNodeCount) {
static const unsigned int total_blocks = 16000;
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"10832dd62c427bcf68c56c8de0d1f9c32b61d9e5ddf43c77c56a97b372ad4b07";
// A page of all zeros
u8* zero_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(zero_page, 0, PAGE_SIZE);
SetupBht(total_blocks, "sha256", NULL);
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) {
DLOG(INFO) << "verifying block: " << blocks;
EXPECT_EQ(0, dm_bht_verify_block(bht_, blocks, zero_page, 0));
}
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(zero_page);
}
TEST_F(MemoryBhtTest, CreateThenVerifyBadHashBlock) {
static const unsigned int total_blocks = 16384;
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"45d65d6f9e5a962f4d80b5f1bd7a918152251c27bdad8c5f52b590c129833372";
// A page of all zeros
u8* zero_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(zero_page, 0, PAGE_SIZE);
SetupBht(total_blocks, "sha256", NULL);
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
// TODO(wad) add tests for partial tree validity/verification
// Corrupt one has hblock
static const unsigned int kBadBlock = 256;
u8* bad_hash_block = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(bad_hash_block, 'A', PAGE_SIZE);
EXPECT_EQ(dm_bht_store_block(bht_, kBadBlock, bad_hash_block), 0);
// Attempt to verify both the bad block and all the neighbors.
EXPECT_LT(dm_bht_verify_block(bht_, kBadBlock + 1, zero_page, 0), 0);
EXPECT_LT(dm_bht_verify_block(bht_, kBadBlock + 2, zero_page, 0), 0);
EXPECT_LT(dm_bht_verify_block(bht_, kBadBlock + (bht_->node_count / 2),
zero_page, 0),
0);
EXPECT_LT(dm_bht_verify_block(bht_, kBadBlock, zero_page, 0), 0);
// Verify that the prior entry is untouched and still safe
EXPECT_EQ(dm_bht_verify_block(bht_, kBadBlock - 1, zero_page, 0), 0);
// Same for the next entry
EXPECT_EQ(
dm_bht_verify_block(bht_, kBadBlock + bht_->node_count, zero_page, 0), 0);
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(bad_hash_block);
free(zero_page);
}
TEST_F(MemoryBhtTest, CreateThenVerifyBadDataBlock) {
static const unsigned int total_blocks = 384;
SetupBht(total_blocks, "sha256", NULL);
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"45d65d6f9e5a962f4d80b5f1bd7a918152251c27bdad8c5f52b590c129833372";
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
// A corrupt page
u8* bad_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(bad_page, 'A', PAGE_SIZE);
EXPECT_LT(dm_bht_verify_block(bht_, 0, bad_page, 0), 0);
EXPECT_LT(dm_bht_verify_block(bht_, 127, bad_page, 0), 0);
EXPECT_LT(dm_bht_verify_block(bht_, 128, bad_page, 0), 0);
EXPECT_LT(dm_bht_verify_block(bht_, 255, bad_page, 0), 0);
EXPECT_LT(dm_bht_verify_block(bht_, 256, bad_page, 0), 0);
EXPECT_LT(dm_bht_verify_block(bht_, 383, bad_page, 0), 0);
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(bad_page);
}
TEST_F(MemoryBhtTest, CreateThenVerifyOkSalt) {
static const unsigned int total_blocks = 16384;
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"8015fea349568f5135ecc833bbc79c9179377207382b53c68d93190b286b1256";
static const char salt[] =
"01ad1f06255d452d91337bf037953053cc3e452541db4b8ca05811bf3e2b6027";
// A page of all zeros
u8* zero_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(zero_page, 0, PAGE_SIZE);
SetupBht(total_blocks, "sha256", salt);
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) {
DLOG(INFO) << "verifying block: " << blocks;
EXPECT_EQ(0, dm_bht_verify_block(bht_, blocks, zero_page, 0));
}
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(zero_page);
}
TEST_F(MemoryBhtTest, CreateThenVerifyOkLongSalt) {
static const unsigned int total_blocks = 16384;
// Set the root hash for a 0-filled image
static const char kRootDigest[] =
"8015fea349568f5135ecc833bbc79c9179377207382b53c68d93190b286b1256";
static const char salt[] =
"01ad1f06255d452d91337bf037953053cc3e452541db4b8ca05811bf3e2b6027b2188a1"
"d";
// A page of all zeros
u8* zero_page = static_cast<u8*>(my_memalign(PAGE_SIZE, PAGE_SIZE));
memset(zero_page, 0, PAGE_SIZE);
SetupBht(total_blocks, "sha256", salt);
dm_bht_set_root_hexdigest(bht_, reinterpret_cast<const u8*>(kRootDigest));
for (unsigned int blocks = 0; blocks < total_blocks; ++blocks) {
DLOG(INFO) << "verifying block: " << blocks;
EXPECT_EQ(0, dm_bht_verify_block(bht_, blocks, zero_page, 0));
}
EXPECT_EQ(0, dm_bht_destroy(bht_));
free(zero_page);
}