diff options
author | Tianjie Xu <xunchang@google.com> | 2019-11-21 10:25:17 -0800 |
---|---|---|
committer | Tianjie Xu <xunchang@google.com> | 2019-12-18 11:34:57 -0800 |
commit | fb4066a3900492e46a46b7d2ee09708e3168314f (patch) | |
tree | 4c87bb04b8f0caeb8be4d66e2973523dddcea04a /libfec | |
parent | b577ab32de1cd9bdf672f49b036597b7c9fa3515 (diff) | |
download | extras-fb4066a3900492e46a46b7d2ee09708e3168314f.tar.gz |
Add test to parse verity data
Add a unittest to load and verify a VB1.0 image.
Bug: 144388532
Test: unittest pass
Change-Id: I1bfc93239a6696c58e9220864009759a2468e136
Diffstat (limited to 'libfec')
-rw-r--r-- | libfec/test/Android.bp | 27 | ||||
-rw-r--r-- | libfec/test/fec_unittest.cpp | 169 |
2 files changed, 195 insertions, 1 deletions
diff --git a/libfec/test/Android.bp b/libfec/test/Android.bp index 5cc6d86d..1bbad077 100644 --- a/libfec/test/Android.bp +++ b/libfec/test/Android.bp @@ -1,7 +1,6 @@ cc_defaults { name: "fec_test_defaults", - gtest: false, sanitize: { misc_undefined: ["integer"], }, @@ -20,6 +19,7 @@ cc_defaults { cc_test_host { name: "fec_test_read", defaults: ["fec_test_defaults"], + gtest: false, srcs: ["test_read.cpp"], static_libs: [ "libfec", @@ -35,6 +35,31 @@ cc_test_host { cc_test_host { name: "fec_test_rs", defaults: ["fec_test_defaults"], + gtest: false, srcs: ["test_rs.c"], static_libs: ["libfec_rs"], } + +cc_test_host { + name: "fec_unittest", + defaults: ["fec_test_defaults"], + srcs: ["fec_unittest.cpp"], + + gtest: true, + required: [ + "fec", + ], + static_libs: [ + "libverity_tree", + "libfec", + "libfec_rs", + "libcrypto_utils", + "libext4_utils", + "libsquashfs_utils", + "libgtest_prod", + "libcrypto", + "libcutils", + "liblog", + "libbase", + ], +} diff --git a/libfec/test/fec_unittest.cpp b/libfec/test/fec_unittest.cpp new file mode 100644 index 00000000..f2c1e7dc --- /dev/null +++ b/libfec/test/fec_unittest.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <stdlib.h> + +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/strings.h> +#include <gtest/gtest.h> +#include <verity/hash_tree_builder.h> + +#include "../fec_private.h" +#include "fec/io.h" + +class FecUnitTest : public ::testing::Test { + protected: + void SetUp() override { + // Construct a 1 MiB image as file system. + image_.reserve(1024 * 1024); + for (unsigned i = 0; i <= 255; i++) { + std::vector<uint8_t> tmp_vec(4096, i); + image_.insert(image_.end(), tmp_vec.begin(), tmp_vec.end()); + } + + // Build the hashtree. + HashTreeBuilder builder(4096, HashTreeBuilder::HashFunction("sha256")); + // Use a random salt. + salt_ = std::vector<uint8_t>(64, 10); + ASSERT_TRUE(builder.Initialize(image_.size(), salt_)); + ASSERT_TRUE(builder.Update(image_.data(), image_.size())); + ASSERT_TRUE(builder.BuildHashTree()); + root_hash_ = builder.root_hash(); + + // Append the hashtree to the end of image. + TemporaryFile temp_file; + ASSERT_TRUE(builder.WriteHashTreeToFd(temp_file.fd, 0)); + android::base::ReadFileToString(temp_file.path, &hashtree_content_); + image_.insert(image_.end(), hashtree_content_.begin(), + hashtree_content_.end()); + } + + // Builds the verity metadata and appends the bytes to the image. + void BuildAndAppendsVerityMetadata() { + // The metadata table has the format: "1 block_device, block_device, + // BLOCK_SIZE, BLOCK_SIZE, data_blocks, data_blocks, 'sha256', + // root_hash, salt". + std::vector<std::string> table = { + "1", + "fake_block_device", + "fake_block_device", + "4096", + "4096", + "256", + "256", + "sha256", + HashTreeBuilder::BytesArrayToString(root_hash_), + HashTreeBuilder::BytesArrayToString(salt_), + }; + verity_table_ = android::base::Join(table, ' '); + + verity_header_ = { + 0xb001b001, 0, {}, static_cast<unsigned int>(verity_table_.size()) + }; + + // Construct the verity metadata with header, table, and padding. + constexpr auto VERITY_META_SIZE = 8 * 4096; + image_.insert(image_.end(), + reinterpret_cast<uint8_t *>(&verity_header_), + reinterpret_cast<uint8_t *>(&verity_header_) + + sizeof(verity_header_)); + image_.insert(image_.end(), verity_table_.data(), + verity_table_.data() + verity_table_.size()); + std::vector<uint8_t> padding( + VERITY_META_SIZE - sizeof(verity_header_) - verity_table_.size(), + 0); + image_.insert(image_.end(), padding.begin(), padding.end()); + } + + static void BuildAndAppendsEccImage(const std::string &image_name, + const std::string &fec_name) { + std::vector<std::string> cmd = { "fec", "--encode", "--roots", + "2", image_name, fec_name }; + ASSERT_EQ(0, std::system(android::base::Join(cmd, ' ').c_str())); + } + + std::vector<uint8_t> image_; + std::vector<uint8_t> salt_; + std::vector<uint8_t> root_hash_; + std::string hashtree_content_; + verity_header verity_header_; + std::string verity_table_; +}; + +TEST_F(FecUnitTest, LoadVerityImage_ParseVerity) { + TemporaryFile verity_image; + BuildAndAppendsVerityMetadata(); + ASSERT_TRUE(android::base::WriteFully(verity_image.fd, image_.data(), + image_.size())); + + struct fec_handle *handle = nullptr; + ASSERT_EQ(0, fec_open(&handle, verity_image.path, O_RDONLY, FEC_FS_EXT4, 2)); + std::unique_ptr<fec_handle> guard(handle); + + ASSERT_EQ(image_.size(), handle->size); + ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size + + ASSERT_EQ(1024 * 1024 + hashtree_content_.size(), + handle->verity.metadata_start); + ASSERT_EQ(verity_header_.length, handle->verity.header.length); + ASSERT_EQ(verity_table_, handle->verity.table); + + // check the hashtree. + ASSERT_EQ(salt_, handle->hashtree().salt); + ASSERT_EQ(1024 * 1024, handle->hashtree().hash_start); + // the fec hashtree only stores the hash of the lowest level. + ASSERT_EQ(std::vector<uint8_t>(hashtree_content_.begin() + 4096, + hashtree_content_.end()), + handle->hashtree().hash); + ASSERT_EQ(hashtree_content_.size(), handle->hashtree().hash_size); +} + +TEST_F(FecUnitTest, LoadVerityImage_ParseEcc) { + TemporaryFile verity_image; + BuildAndAppendsVerityMetadata(); + ASSERT_TRUE(android::base::WriteFully(verity_image.fd, image_.data(), + image_.size())); + TemporaryFile ecc_image; + BuildAndAppendsEccImage(verity_image.path, ecc_image.path); + std::string ecc_content; + ASSERT_TRUE(android::base::ReadFileToString(ecc_image.path, &ecc_content)); + ASSERT_TRUE(android::base::WriteStringToFd(ecc_content, verity_image.fd)); + struct fec_handle *handle = nullptr; + ASSERT_EQ(0, fec_open(&handle, verity_image.path, O_RDONLY, FEC_FS_EXT4, 2)); + std::unique_ptr<fec_handle> guard(handle); + + ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size + ASSERT_EQ(1024 * 1024 + hashtree_content_.size(), + handle->verity.metadata_start); + + fec_verity_metadata verity_metadata{}; + ASSERT_EQ(0, fec_verity_get_metadata(handle, &verity_metadata)); + ASSERT_FALSE(verity_metadata.disabled); + ASSERT_EQ(1024 * 1024, verity_metadata.data_size); + ASSERT_EQ(verity_table_, verity_metadata.table); + + fec_ecc_metadata ecc_metadata{}; + ASSERT_EQ(0, fec_ecc_get_metadata(handle, &ecc_metadata)); + ASSERT_TRUE(ecc_metadata.valid); + ASSERT_EQ(handle->verity.metadata_start + 8 * 4096, ecc_metadata.start); + ASSERT_EQ(2, ecc_metadata.roots); + // 256 (data) + 3 (hashtree) + 8 (verity meta) + ASSERT_EQ(267, ecc_metadata.blocks); +} |