blob: c9aea0846ce947369f7d034d4c23a4f9ef44e4d1 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "blob-verifier.h"
#include <random>
#include <zxtest/zxtest.h>
#include "utils.h"
namespace blobfs {
namespace {
class BlobVerifierTest : public zxtest::Test {
public:
BlobfsMetrics* Metrics() { return &metrics_; }
void SetUp() override {
srand(zxtest::Runner::GetInstance()->random_seed());
}
private:
BlobfsMetrics metrics_;
};
void GenerateTree(const uint8_t* data, size_t len, Digest* out_digest,
fbl::Array<uint8_t>* out_tree) {
digest::MerkleTreeCreator mtc;
ASSERT_OK(mtc.SetDataLength(len));
size_t merkle_size = mtc.GetTreeLength();
fbl::Array<uint8_t> merkle_buf(new uint8_t[merkle_size], merkle_size);
uint8_t root[digest::kSha256Length];
ASSERT_OK(mtc.SetTree(merkle_buf.get(), merkle_size, root, sizeof(root)));
ASSERT_OK(mtc.Append(data, len));
*out_digest = Digest(root);
*out_tree = std::move(merkle_buf);
}
void FillWithRandom(uint8_t* buf, size_t len) {
for (unsigned i = 0; i < len; ++i) {
buf[i] = (uint8_t)rand();
}
}
TEST_F(BlobVerifierTest, CreateAndVerify_NullBlob) {
fbl::Array<uint8_t> unused_merkle_buf;
Digest digest;
GenerateTree(nullptr, 0, &digest, &unused_merkle_buf);
std::unique_ptr<BlobVerifier> verifier;
ASSERT_OK(BlobVerifier::CreateWithoutTree(std::move(digest), Metrics(), 0ul, &verifier));
EXPECT_OK(verifier->Verify(nullptr, 0ul));
EXPECT_OK(verifier->VerifyPartial(nullptr, 0ul, 0ul));
}
TEST_F(BlobVerifierTest, CreateAndVerify_SmallBlob) {
uint8_t buf[8192];
FillWithRandom(buf, sizeof(buf));
fbl::Array<uint8_t> unused_merkle_buf;
Digest digest;
GenerateTree(buf, sizeof(buf), &digest, &unused_merkle_buf);
std::unique_ptr<BlobVerifier> verifier;
ASSERT_OK(BlobVerifier::CreateWithoutTree(std::move(digest), Metrics(), sizeof(buf), &verifier));
EXPECT_OK(verifier->Verify(buf, sizeof(buf)));
EXPECT_OK(verifier->VerifyPartial(buf, 8192, 0));
// Partial ranges
EXPECT_EQ(verifier->VerifyPartial(buf, 8191, 0), ZX_ERR_INVALID_ARGS);
// Verify past the end
EXPECT_EQ(verifier->VerifyPartial(buf, 2 * 8192, 0), ZX_ERR_INVALID_ARGS);
}
TEST_F(BlobVerifierTest, CreateAndVerify_SmallBlob_DataCorrupted) {
uint8_t buf[8192];
FillWithRandom(buf, sizeof(buf));
fbl::Array<uint8_t> unused_merkle_buf;
Digest digest;
GenerateTree(buf, sizeof(buf), &digest, &unused_merkle_buf);
// Invert one character
buf[42] = ~(buf[42]);
std::unique_ptr<BlobVerifier> verifier;
ASSERT_OK(BlobVerifier::CreateWithoutTree(std::move(digest), Metrics(), sizeof(buf), &verifier));
EXPECT_EQ(verifier->Verify(buf, sizeof(buf)), ZX_ERR_IO_DATA_INTEGRITY);
EXPECT_EQ(verifier->VerifyPartial(buf, 8192, 0), ZX_ERR_IO_DATA_INTEGRITY);
}
TEST_F(BlobVerifierTest, CreateAndVerify_BigBlob) {
size_t sz = 1 << 16;
fbl::Array<uint8_t> buf(new uint8_t[sz], sz);
FillWithRandom(buf.get(), sz);
fbl::Array<uint8_t> merkle_buf;
Digest digest;
GenerateTree(buf.get(), sz, &digest, &merkle_buf);
std::unique_ptr<BlobVerifier> verifier;
ASSERT_OK(BlobVerifier::Create(std::move(digest), Metrics(), merkle_buf.get(), merkle_buf.size(),
sz, &verifier));
EXPECT_OK(verifier->Verify(buf.get(), sz));
EXPECT_OK(verifier->VerifyPartial(buf.get(), sz, 0));
// Block-by-block
for (size_t i = 0; i < sz; i += 8192) {
EXPECT_OK(verifier->VerifyPartial(buf.get() + i, 8192, i));
}
// Partial ranges
EXPECT_EQ(verifier->VerifyPartial(buf.data(), 8191, 0), ZX_ERR_INVALID_ARGS);
// Verify past the end
EXPECT_EQ(verifier->VerifyPartial(buf.data() + (sz - 8192), 2 * 8192, sz - 8192),
ZX_ERR_INVALID_ARGS);
}
TEST_F(BlobVerifierTest, CreateAndVerify_BigBlob_DataCorrupted) {
size_t sz = 1 << 16;
fbl::Array<uint8_t> buf(new uint8_t[sz], sz);
FillWithRandom(buf.get(), sz);
fbl::Array<uint8_t> merkle_buf;
Digest digest;
GenerateTree(buf.get(), sz, &digest, &merkle_buf);
// Invert a char in the first block. All other blocks are still valid.
buf.get()[42] = ~(buf.get()[42]);
std::unique_ptr<BlobVerifier> verifier;
ASSERT_OK(BlobVerifier::Create(std::move(digest), Metrics(), merkle_buf.get(), merkle_buf.size(),
sz, &verifier));
EXPECT_EQ(verifier->Verify(buf.get(), sz), ZX_ERR_IO_DATA_INTEGRITY);
EXPECT_EQ(verifier->VerifyPartial(buf.get(), sz, 0), ZX_ERR_IO_DATA_INTEGRITY);
// Block-by-block -- first block fails, rest succeed
for (size_t i = 0; i < sz; i += 8192) {
zx_status_t expected_status = i == 0 ? ZX_ERR_IO_DATA_INTEGRITY : ZX_OK;
EXPECT_EQ(verifier->VerifyPartial(buf.get() + i, 8192, i), expected_status);
}
}
TEST_F(BlobVerifierTest, CreateAndVerify_BigBlob_MerkleCorrupted) {
size_t sz = 1 << 16;
fbl::Array<uint8_t> buf(new uint8_t[sz], sz);
FillWithRandom(buf.get(), sz);
fbl::Array<uint8_t> merkle_buf;
Digest digest;
GenerateTree(buf.get(), sz, &digest, &merkle_buf);
// Invert a char in the tree.
merkle_buf.get()[0] = ~(merkle_buf.get()[0]);
std::unique_ptr<BlobVerifier> verifier;
ASSERT_OK(BlobVerifier::Create(std::move(digest), Metrics(), merkle_buf.get(), merkle_buf.size(),
sz, &verifier));
EXPECT_EQ(verifier->Verify(buf.get(), sz), ZX_ERR_IO_DATA_INTEGRITY);
EXPECT_EQ(verifier->VerifyPartial(buf.get(), sz, 0), ZX_ERR_IO_DATA_INTEGRITY);
// Block-by-block -- everything fails
for (size_t i = 0; i < sz; i += 8192) {
EXPECT_EQ(verifier->VerifyPartial(buf.get() + i, 8192, i), ZX_ERR_IO_DATA_INTEGRITY);
}
}
} // namespace
} // namespace blobfs