blob: e1dee581cdd2b80c6bb4acd05f7f32eabbb445d0 [file] [log] [blame]
//
// Copyright (C) 2018 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 "update_engine/payload_generator/payload_generation_config.h"
#include <base/logging.h>
#include <brillo/secure_blob.h>
#include <libavb/libavb.h>
#include <verity/hash_tree_builder.h>
#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/verity_writer_android.h"
#include "update_engine/payload_generator/extent_ranges.h"
namespace chromeos_update_engine {
namespace {
bool AvbDescriptorCallback(const AvbDescriptor* descriptor, void* user_data) {
PartitionConfig* part = static_cast<PartitionConfig*>(user_data);
AvbDescriptor desc;
TEST_AND_RETURN_FALSE(
avb_descriptor_validate_and_byteswap(descriptor, &desc));
if (desc.tag != AVB_DESCRIPTOR_TAG_HASHTREE)
return true;
AvbHashtreeDescriptor hashtree;
TEST_AND_RETURN_FALSE(avb_hashtree_descriptor_validate_and_byteswap(
reinterpret_cast<const AvbHashtreeDescriptor*>(descriptor), &hashtree));
// We only support version 1 right now, will need to introduce a new
// payload minor version to support new dm verity version.
TEST_AND_RETURN_FALSE(hashtree.dm_verity_version == 1);
part->verity.hash_tree_algorithm =
reinterpret_cast<const char*>(hashtree.hash_algorithm);
const uint8_t* salt = reinterpret_cast<const uint8_t*>(descriptor) +
sizeof(AvbHashtreeDescriptor) +
hashtree.partition_name_len;
part->verity.hash_tree_salt.assign(salt, salt + hashtree.salt_len);
TEST_AND_RETURN_FALSE(hashtree.data_block_size ==
part->fs_interface->GetBlockSize());
part->verity.hash_tree_data_extent =
ExtentForBytes(hashtree.data_block_size, 0, hashtree.image_size);
TEST_AND_RETURN_FALSE(hashtree.hash_block_size ==
part->fs_interface->GetBlockSize());
// Generate hash tree based on the descriptor and verify that it matches
// the hash tree stored in the image.
auto hash_function =
HashTreeBuilder::HashFunction(part->verity.hash_tree_algorithm);
TEST_AND_RETURN_FALSE(hash_function != nullptr);
HashTreeBuilder hash_tree_builder(hashtree.data_block_size, hash_function);
TEST_AND_RETURN_FALSE(hash_tree_builder.Initialize(
hashtree.image_size, part->verity.hash_tree_salt));
TEST_AND_RETURN_FALSE(hash_tree_builder.CalculateSize(hashtree.image_size) ==
hashtree.tree_size);
brillo::Blob buffer;
for (uint64_t offset = 0; offset < hashtree.image_size;) {
constexpr uint64_t kBufferSize = 1024 * 1024;
size_t bytes_to_read = std::min(kBufferSize, hashtree.image_size - offset);
TEST_AND_RETURN_FALSE(
utils::ReadFileChunk(part->path, offset, bytes_to_read, &buffer));
TEST_AND_RETURN_FALSE(
hash_tree_builder.Update(buffer.data(), buffer.size()));
offset += buffer.size();
buffer.clear();
}
TEST_AND_RETURN_FALSE(hash_tree_builder.BuildHashTree());
TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
part->path, hashtree.tree_offset, hashtree.tree_size, &buffer));
TEST_AND_RETURN_FALSE(hash_tree_builder.CheckHashTree(buffer));
part->verity.hash_tree_extent = ExtentForBytes(
hashtree.hash_block_size, hashtree.tree_offset, hashtree.tree_size);
TEST_AND_RETURN_FALSE(VerityWriterAndroid::EncodeFEC(part->path,
0 /* data_offset */,
hashtree.fec_offset,
hashtree.fec_offset,
hashtree.fec_size,
hashtree.fec_num_roots,
hashtree.data_block_size,
true /* verify_mode */));
part->verity.fec_data_extent =
ExtentForBytes(hashtree.data_block_size, 0, hashtree.fec_offset);
part->verity.fec_extent = ExtentForBytes(
hashtree.data_block_size, hashtree.fec_offset, hashtree.fec_size);
part->verity.fec_roots = hashtree.fec_num_roots;
return true;
}
} // namespace
bool ImageConfig::LoadVerityConfig() {
for (PartitionConfig& part : partitions) {
if (part.size < sizeof(AvbFooter))
continue;
uint64_t footer_offset = part.size - sizeof(AvbFooter);
brillo::Blob buffer;
TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
part.path, footer_offset, sizeof(AvbFooter), &buffer));
if (memcmp(buffer.data(), AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0)
continue;
LOG(INFO) << "Parsing verity config from AVB footer for " << part.name;
AvbFooter footer;
TEST_AND_RETURN_FALSE(avb_footer_validate_and_byteswap(
reinterpret_cast<const AvbFooter*>(buffer.data()), &footer));
buffer.clear();
TEST_AND_RETURN_FALSE(footer.vbmeta_offset + sizeof(AvbVBMetaImageHeader) <=
part.size);
TEST_AND_RETURN_FALSE(utils::ReadFileChunk(
part.path, footer.vbmeta_offset, footer.vbmeta_size, &buffer));
TEST_AND_RETURN_FALSE(avb_descriptor_foreach(
buffer.data(), buffer.size(), AvbDescriptorCallback, &part));
}
return true;
}
} // namespace chromeos_update_engine