blob: fc6a4b58b061143ad07688bf0b0ee01ed5e9e09b [file] [log] [blame]
//
// Copyright (C) 2012 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_consumer/delta_performer.h"
#include <endian.h>
#include <inttypes.h>
#include <string>
#include <vector>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <gmock/gmock.h>
#include <google/protobuf/repeated_field.h>
#include <gtest/gtest.h>
#include "update_engine/common/constants.h"
#include "update_engine/common/fake_boot_control.h"
#include "update_engine/common/fake_hardware.h"
#include "update_engine/common/fake_prefs.h"
#include "update_engine/common/test_utils.h"
#include "update_engine/common/utils.h"
#include "update_engine/payload_consumer/mock_download_action.h"
#include "update_engine/payload_consumer/payload_constants.h"
#include "update_engine/payload_generator/bzip.h"
#include "update_engine/payload_generator/extent_ranges.h"
#include "update_engine/payload_generator/payload_file.h"
#include "update_engine/payload_generator/payload_signer.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
using std::string;
using std::vector;
using test_utils::GetBuildArtifactsPath;
using test_utils::System;
using test_utils::kRandomString;
using testing::_;
extern const char* kUnittestPrivateKeyPath;
extern const char* kUnittestPublicKeyPath;
namespace {
const char kBogusMetadataSignature1[] =
"awSFIUdUZz2VWFiR+ku0Pj00V7bPQPQFYQSXjEXr3vaw3TE4xHV5CraY3/YrZpBv"
"J5z4dSBskoeuaO1TNC/S6E05t+yt36tE4Fh79tMnJ/z9fogBDXWgXLEUyG78IEQr"
"YH6/eBsQGT2RJtBgXIXbZ9W+5G9KmGDoPOoiaeNsDuqHiBc/58OFsrxskH8E6vMS"
"BmMGGk82mvgzic7ApcoURbCGey1b3Mwne/hPZ/bb9CIyky8Og9IfFMdL2uAweOIR"
"fjoTeLYZpt+WN65Vu7jJ0cQN8e1y+2yka5112wpRf/LLtPgiAjEZnsoYpLUd7CoV"
"pLRtClp97kN2+tXGNBQqkA==";
// Different options that determine what we should fill into the
// install_plan.metadata_signature to simulate the contents received in the
// Omaha response.
enum MetadataSignatureTest {
kEmptyMetadataSignature,
kInvalidMetadataSignature,
kValidMetadataSignature,
};
// Compressed data without checksum, generated with:
// echo -n a | xz -9 --check=none | hexdump -v -e '" " 12/1 "0x%02x, " "\n"'
const uint8_t kXzCompressedData[] = {
0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x00, 0xff, 0x12, 0xd9, 0x41,
0x02, 0x00, 0x21, 0x01, 0x1c, 0x00, 0x00, 0x00, 0x10, 0xcf, 0x58, 0xcc,
0x01, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x11, 0x01,
0xad, 0xa6, 0x58, 0x04, 0x06, 0x72, 0x9e, 0x7a, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x59, 0x5a,
};
} // namespace
class DeltaPerformerTest : public ::testing::Test {
protected:
void SetUp() override {
install_plan_.source_slot = 0;
install_plan_.target_slot = 1;
EXPECT_CALL(mock_delegate_, ShouldCancel(_))
.WillRepeatedly(testing::Return(false));
}
// Test helper placed where it can easily be friended from DeltaPerformer.
void RunManifestValidation(const DeltaArchiveManifest& manifest,
uint64_t major_version,
InstallPayloadType payload_type,
ErrorCode expected) {
install_plan_.payload_type = payload_type;
// The Manifest we are validating.
performer_.manifest_.CopyFrom(manifest);
performer_.major_payload_version_ = major_version;
EXPECT_EQ(expected, performer_.ValidateManifest());
}
brillo::Blob GeneratePayload(const brillo::Blob& blob_data,
const vector<AnnotatedOperation>& aops,
bool sign_payload) {
return GeneratePayload(blob_data, aops, sign_payload,
DeltaPerformer::kSupportedMajorPayloadVersion,
DeltaPerformer::kSupportedMinorPayloadVersion);
}
brillo::Blob GeneratePayload(const brillo::Blob& blob_data,
const vector<AnnotatedOperation>& aops,
bool sign_payload,
uint64_t major_version,
uint32_t minor_version) {
string blob_path;
EXPECT_TRUE(utils::MakeTempFile("Blob-XXXXXX", &blob_path, nullptr));
ScopedPathUnlinker blob_unlinker(blob_path);
EXPECT_TRUE(utils::WriteFile(blob_path.c_str(),
blob_data.data(),
blob_data.size()));
PayloadGenerationConfig config;
config.version.major = major_version;
config.version.minor = minor_version;
PayloadFile payload;
EXPECT_TRUE(payload.Init(config));
PartitionConfig old_part(kLegacyPartitionNameRoot);
if (minor_version != kFullPayloadMinorVersion) {
// When generating a delta payload we need to include the old partition
// information to mark it as a delta payload.
old_part.path = "/dev/null";
old_part.size = 0;
}
PartitionConfig new_part(kLegacyPartitionNameRoot);
new_part.path = "/dev/zero";
new_part.size = 1234;
payload.AddPartition(old_part, new_part, aops);
// We include a kernel partition without operations.
old_part.name = kLegacyPartitionNameKernel;
new_part.name = kLegacyPartitionNameKernel;
new_part.size = 0;
payload.AddPartition(old_part, new_part, {});
string payload_path;
EXPECT_TRUE(utils::MakeTempFile("Payload-XXXXXX", &payload_path, nullptr));
ScopedPathUnlinker payload_unlinker(payload_path);
string private_key =
sign_payload ? GetBuildArtifactsPath(kUnittestPrivateKeyPath) : "";
EXPECT_TRUE(payload.WritePayload(
payload_path, blob_path, private_key, &install_plan_.metadata_size));
brillo::Blob payload_data;
EXPECT_TRUE(utils::ReadFile(payload_path, &payload_data));
return payload_data;
}
// Apply |payload_data| on partition specified in |source_path|.
// Expect result of performer_.Write() to be |expect_success|.
// Returns the result of the payload application.
brillo::Blob ApplyPayload(const brillo::Blob& payload_data,
const string& source_path,
bool expect_success) {
return ApplyPayloadToData(payload_data, source_path, brillo::Blob(),
expect_success);
}
// Apply the payload provided in |payload_data| reading from the |source_path|
// file and writing the contents to a new partition. The existing data in the
// new target file are set to |target_data| before applying the payload.
// Expect result of performer_.Write() to be |expect_success|.
// Returns the result of the payload application.
brillo::Blob ApplyPayloadToData(const brillo::Blob& payload_data,
const string& source_path,
const brillo::Blob& target_data,
bool expect_success) {
string new_part;
EXPECT_TRUE(utils::MakeTempFile("Partition-XXXXXX", &new_part, nullptr));
ScopedPathUnlinker partition_unlinker(new_part);
EXPECT_TRUE(utils::WriteFile(new_part.c_str(), target_data.data(),
target_data.size()));
// We installed the operations only in the rootfs partition, but the
// delta performer needs to access all the partitions.
fake_boot_control_.SetPartitionDevice(
kLegacyPartitionNameRoot, install_plan_.target_slot, new_part);
fake_boot_control_.SetPartitionDevice(
kLegacyPartitionNameRoot, install_plan_.source_slot, source_path);
fake_boot_control_.SetPartitionDevice(
kLegacyPartitionNameKernel, install_plan_.target_slot, "/dev/null");
fake_boot_control_.SetPartitionDevice(
kLegacyPartitionNameKernel, install_plan_.source_slot, "/dev/null");
EXPECT_EQ(expect_success,
performer_.Write(payload_data.data(), payload_data.size()));
EXPECT_EQ(0, performer_.Close());
brillo::Blob partition_data;
EXPECT_TRUE(utils::ReadFile(new_part, &partition_data));
return partition_data;
}
// Calls delta performer's Write method by pretending to pass in bytes from a
// delta file whose metadata size is actual_metadata_size and tests if all
// checks are correctly performed if the install plan contains
// expected_metadata_size and that the result of the parsing are as per
// hash_checks_mandatory flag.
void DoMetadataSizeTest(uint64_t expected_metadata_size,
uint64_t actual_metadata_size,
bool hash_checks_mandatory) {
install_plan_.hash_checks_mandatory = hash_checks_mandatory;
// Set a valid magic string and version number 1.
EXPECT_TRUE(performer_.Write("CrAU", 4));
uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
EXPECT_TRUE(performer_.Write(&version, 8));
install_plan_.metadata_size = expected_metadata_size;
ErrorCode error_code;
// When filling in size in manifest, exclude the size of the 20-byte header.
uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
bool result = performer_.Write(&size_in_manifest, 8, &error_code);
if (expected_metadata_size == actual_metadata_size ||
!hash_checks_mandatory) {
EXPECT_TRUE(result);
} else {
EXPECT_FALSE(result);
EXPECT_EQ(ErrorCode::kDownloadInvalidMetadataSize, error_code);
}
EXPECT_LT(performer_.Close(), 0);
}
// Generates a valid delta file but tests the delta performer by suppling
// different metadata signatures as per metadata_signature_test flag and
// sees if the result of the parsing are as per hash_checks_mandatory flag.
void DoMetadataSignatureTest(MetadataSignatureTest metadata_signature_test,
bool sign_payload,
bool hash_checks_mandatory) {
// Loads the payload and parses the manifest.
brillo::Blob payload = GeneratePayload(brillo::Blob(),
vector<AnnotatedOperation>(), sign_payload,
kChromeOSMajorPayloadVersion, kFullPayloadMinorVersion);
LOG(INFO) << "Payload size: " << payload.size();
install_plan_.hash_checks_mandatory = hash_checks_mandatory;
DeltaPerformer::MetadataParseResult expected_result, actual_result;
ErrorCode expected_error, actual_error;
// Fill up the metadata signature in install plan according to the test.
switch (metadata_signature_test) {
case kEmptyMetadataSignature:
install_plan_.metadata_signature.clear();
expected_result = DeltaPerformer::kMetadataParseError;
expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
break;
case kInvalidMetadataSignature:
install_plan_.metadata_signature = kBogusMetadataSignature1;
expected_result = DeltaPerformer::kMetadataParseError;
expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
break;
case kValidMetadataSignature:
default:
// Set the install plan's metadata size to be the same as the one
// in the manifest so that we pass the metadata size checks. Only
// then we can get to manifest signature checks.
ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
payload.data(),
install_plan_.metadata_size,
GetBuildArtifactsPath(kUnittestPrivateKeyPath),
&install_plan_.metadata_signature));
EXPECT_FALSE(install_plan_.metadata_signature.empty());
expected_result = DeltaPerformer::kMetadataParseSuccess;
expected_error = ErrorCode::kSuccess;
break;
}
// Ignore the expected result/error if hash checks are not mandatory.
if (!hash_checks_mandatory) {
expected_result = DeltaPerformer::kMetadataParseSuccess;
expected_error = ErrorCode::kSuccess;
}
// Use the public key corresponding to the private key used above to
// sign the metadata.
string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
performer_.set_public_key_path(public_key_path);
// Init actual_error with an invalid value so that we make sure
// ParsePayloadMetadata properly populates it in all cases.
actual_error = ErrorCode::kUmaReportedMax;
actual_result = performer_.ParsePayloadMetadata(payload, &actual_error);
EXPECT_EQ(expected_result, actual_result);
EXPECT_EQ(expected_error, actual_error);
// Check that the parsed metadata size is what's expected. This test
// implicitly confirms that the metadata signature is valid, if required.
EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize());
}
void SetSupportedMajorVersion(uint64_t major_version) {
performer_.supported_major_version_ = major_version;
}
FakePrefs prefs_;
InstallPlan install_plan_;
FakeBootControl fake_boot_control_;
FakeHardware fake_hardware_;
MockDownloadActionDelegate mock_delegate_;
DeltaPerformer performer_{&prefs_,
&fake_boot_control_,
&fake_hardware_,
&mock_delegate_,
&install_plan_,
false /* is_interactive*/};
};
TEST_F(DeltaPerformerTest, FullPayloadWriteTest) {
install_plan_.payload_type = InstallPayloadType::kFull;
brillo::Blob expected_data = brillo::Blob(std::begin(kRandomString),
std::end(kRandomString));
expected_data.resize(4096); // block size
vector<AnnotatedOperation> aops;
AnnotatedOperation aop;
*(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
aop.op.set_data_offset(0);
aop.op.set_data_length(expected_data.size());
aop.op.set_type(InstallOperation::REPLACE);
aops.push_back(aop);
brillo::Blob payload_data = GeneratePayload(expected_data, aops, false,
kChromeOSMajorPayloadVersion, kFullPayloadMinorVersion);
EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
}
TEST_F(DeltaPerformerTest, ShouldCancelTest) {
install_plan_.payload_type = InstallPayloadType::kFull;
brillo::Blob expected_data = brillo::Blob(std::begin(kRandomString),
std::end(kRandomString));
expected_data.resize(4096); // block size
vector<AnnotatedOperation> aops;
AnnotatedOperation aop;
*(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
aop.op.set_data_offset(0);
aop.op.set_data_length(expected_data.size());
aop.op.set_type(InstallOperation::REPLACE);
aops.push_back(aop);
brillo::Blob payload_data = GeneratePayload(expected_data, aops, false,
kChromeOSMajorPayloadVersion, kFullPayloadMinorVersion);
testing::Mock::VerifyAndClearExpectations(&mock_delegate_);
EXPECT_CALL(mock_delegate_, ShouldCancel(_))
.WillOnce(
testing::DoAll(testing::SetArgPointee<0>(ErrorCode::kError),
testing::Return(true)));
ApplyPayload(payload_data, "/dev/null", false);
}
TEST_F(DeltaPerformerTest, ReplaceOperationTest) {
brillo::Blob expected_data = brillo::Blob(std::begin(kRandomString),
std::end(kRandomString));
expected_data.resize(4096); // block size
vector<AnnotatedOperation> aops;
AnnotatedOperation aop;
*(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
aop.op.set_data_offset(0);
aop.op.set_data_length(expected_data.size());
aop.op.set_type(InstallOperation::REPLACE);
aops.push_back(aop);
brillo::Blob payload_data = GeneratePayload(expected_data, aops, false);
EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
}
TEST_F(DeltaPerformerTest, ReplaceBzOperationTest) {
brillo::Blob expected_data = brillo::Blob(std::begin(kRandomString),
std::end(kRandomString));
expected_data.resize(4096); // block size
brillo::Blob bz_data;
EXPECT_TRUE(BzipCompress(expected_data, &bz_data));
vector<AnnotatedOperation> aops;
AnnotatedOperation aop;
*(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
aop.op.set_data_offset(0);
aop.op.set_data_length(bz_data.size());
aop.op.set_type(InstallOperation::REPLACE_BZ);
aops.push_back(aop);
brillo::Blob payload_data = GeneratePayload(bz_data, aops, false);
EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
}
TEST_F(DeltaPerformerTest, ReplaceXzOperationTest) {
brillo::Blob xz_data(std::begin(kXzCompressedData),
std::end(kXzCompressedData));
// The compressed xz data contains only a single "a", but the operation should
// pad the rest of the two blocks with zeros.
brillo::Blob expected_data = brillo::Blob(4096, 0);
expected_data[0] = 'a';
AnnotatedOperation aop;
*(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
aop.op.set_data_offset(0);
aop.op.set_data_length(xz_data.size());
aop.op.set_type(InstallOperation::REPLACE_XZ);
vector<AnnotatedOperation> aops = {aop};
brillo::Blob payload_data = GeneratePayload(xz_data, aops, false);
EXPECT_EQ(expected_data, ApplyPayload(payload_data, "/dev/null", true));
}
TEST_F(DeltaPerformerTest, ZeroOperationTest) {
brillo::Blob existing_data = brillo::Blob(4096 * 10, 'a');
brillo::Blob expected_data = existing_data;
// Blocks 4, 5 and 7 should have zeros instead of 'a' after the operation is
// applied.
std::fill(expected_data.data() + 4096 * 4, expected_data.data() + 4096 * 6,
0);
std::fill(expected_data.data() + 4096 * 7, expected_data.data() + 4096 * 8,
0);
AnnotatedOperation aop;
*(aop.op.add_dst_extents()) = ExtentForRange(4, 2);
*(aop.op.add_dst_extents()) = ExtentForRange(7, 1);
aop.op.set_type(InstallOperation::ZERO);
vector<AnnotatedOperation> aops = {aop};
brillo::Blob payload_data = GeneratePayload(brillo::Blob(), aops, false);
EXPECT_EQ(expected_data,
ApplyPayloadToData(payload_data, "/dev/null", existing_data, true));
}
TEST_F(DeltaPerformerTest, SourceCopyOperationTest) {
brillo::Blob expected_data(std::begin(kRandomString),
std::end(kRandomString));
expected_data.resize(4096); // block size
AnnotatedOperation aop;
*(aop.op.add_src_extents()) = ExtentForRange(0, 1);
*(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
aop.op.set_type(InstallOperation::SOURCE_COPY);
brillo::Blob src_hash;
EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
brillo::Blob payload_data = GeneratePayload(brillo::Blob(), {aop}, false);
string source_path;
EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX",
&source_path, nullptr));
ScopedPathUnlinker path_unlinker(source_path);
EXPECT_TRUE(utils::WriteFile(source_path.c_str(),
expected_data.data(),
expected_data.size()));
EXPECT_EQ(expected_data, ApplyPayload(payload_data, source_path, true));
}
TEST_F(DeltaPerformerTest, SourceHashMismatchTest) {
brillo::Blob expected_data = {'f', 'o', 'o'};
brillo::Blob actual_data = {'b', 'a', 'r'};
expected_data.resize(4096); // block size
actual_data.resize(4096); // block size
AnnotatedOperation aop;
*(aop.op.add_src_extents()) = ExtentForRange(0, 1);
*(aop.op.add_dst_extents()) = ExtentForRange(0, 1);
aop.op.set_type(InstallOperation::SOURCE_COPY);
brillo::Blob src_hash;
EXPECT_TRUE(HashCalculator::RawHashOfData(expected_data, &src_hash));
aop.op.set_src_sha256_hash(src_hash.data(), src_hash.size());
brillo::Blob payload_data = GeneratePayload(brillo::Blob(), {aop}, false);
string source_path;
EXPECT_TRUE(utils::MakeTempFile("Source-XXXXXX", &source_path, nullptr));
ScopedPathUnlinker path_unlinker(source_path);
EXPECT_TRUE(utils::WriteFile(source_path.c_str(), actual_data.data(),
actual_data.size()));
EXPECT_EQ(actual_data, ApplyPayload(payload_data, source_path, false));
}
TEST_F(DeltaPerformerTest, ExtentsToByteStringTest) {
uint64_t test[] = {1, 1, 4, 2, 0, 1};
static_assert(arraysize(test) % 2 == 0, "Array size uneven");
const uint64_t block_size = 4096;
const uint64_t file_length = 4 * block_size - 13;
google::protobuf::RepeatedPtrField<Extent> extents;
for (size_t i = 0; i < arraysize(test); i += 2) {
*(extents.Add()) = ExtentForRange(test[i], test[i + 1]);
}
string expected_output = "4096:4096,16384:8192,0:4083";
string actual_output;
EXPECT_TRUE(DeltaPerformer::ExtentsToBsdiffPositionsString(extents,
block_size,
file_length,
&actual_output));
EXPECT_EQ(expected_output, actual_output);
}
TEST_F(DeltaPerformerTest, ValidateManifestFullGoodTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
manifest.mutable_new_kernel_info();
manifest.mutable_new_rootfs_info();
manifest.set_minor_version(kFullPayloadMinorVersion);
RunManifestValidation(manifest,
kChromeOSMajorPayloadVersion,
InstallPayloadType::kFull,
ErrorCode::kSuccess);
}
TEST_F(DeltaPerformerTest, ValidateManifestDeltaGoodTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
manifest.mutable_old_kernel_info();
manifest.mutable_old_rootfs_info();
manifest.mutable_new_kernel_info();
manifest.mutable_new_rootfs_info();
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
RunManifestValidation(manifest,
kChromeOSMajorPayloadVersion,
InstallPayloadType::kDelta,
ErrorCode::kSuccess);
}
TEST_F(DeltaPerformerTest, ValidateManifestFullUnsetMinorVersion) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
RunManifestValidation(manifest,
DeltaPerformer::kSupportedMajorPayloadVersion,
InstallPayloadType::kFull,
ErrorCode::kSuccess);
}
TEST_F(DeltaPerformerTest, ValidateManifestDeltaUnsetMinorVersion) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
// Add an empty old_rootfs_info() to trick the DeltaPerformer into think that
// this is a delta payload manifest with a missing minor version.
manifest.mutable_old_rootfs_info();
RunManifestValidation(manifest,
DeltaPerformer::kSupportedMajorPayloadVersion,
InstallPayloadType::kDelta,
ErrorCode::kUnsupportedMinorPayloadVersion);
}
TEST_F(DeltaPerformerTest, ValidateManifestFullOldKernelTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
manifest.mutable_old_kernel_info();
manifest.mutable_new_kernel_info();
manifest.mutable_new_rootfs_info();
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
RunManifestValidation(manifest,
kChromeOSMajorPayloadVersion,
InstallPayloadType::kFull,
ErrorCode::kPayloadMismatchedType);
}
TEST_F(DeltaPerformerTest, ValidateManifestFullOldRootfsTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
manifest.mutable_old_rootfs_info();
manifest.mutable_new_kernel_info();
manifest.mutable_new_rootfs_info();
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
RunManifestValidation(manifest,
kChromeOSMajorPayloadVersion,
InstallPayloadType::kFull,
ErrorCode::kPayloadMismatchedType);
}
TEST_F(DeltaPerformerTest, ValidateManifestFullPartitionUpdateTest) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
PartitionUpdate* partition = manifest.add_partitions();
partition->mutable_old_partition_info();
partition->mutable_new_partition_info();
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion);
RunManifestValidation(manifest,
kBrilloMajorPayloadVersion,
InstallPayloadType::kFull,
ErrorCode::kPayloadMismatchedType);
}
TEST_F(DeltaPerformerTest, ValidateManifestBadMinorVersion) {
// The Manifest we are validating.
DeltaArchiveManifest manifest;
// Generate a bad version number.
manifest.set_minor_version(DeltaPerformer::kSupportedMinorPayloadVersion +
10000);
// Mark the manifest as a delta payload by setting old_rootfs_info.
manifest.mutable_old_rootfs_info();
RunManifestValidation(manifest,
DeltaPerformer::kSupportedMajorPayloadVersion,
InstallPayloadType::kDelta,
ErrorCode::kUnsupportedMinorPayloadVersion);
}
TEST_F(DeltaPerformerTest, BrilloMetadataSignatureSizeTest) {
EXPECT_TRUE(performer_.Write(kDeltaMagic, sizeof(kDeltaMagic)));
uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
EXPECT_TRUE(performer_.Write(&major_version, 8));
uint64_t manifest_size = rand() % 256;
uint64_t manifest_size_be = htobe64(manifest_size);
EXPECT_TRUE(performer_.Write(&manifest_size_be, 8));
uint32_t metadata_signature_size = rand() % 256;
uint32_t metadata_signature_size_be = htobe32(metadata_signature_size);
EXPECT_TRUE(performer_.Write(&metadata_signature_size_be, 4));
EXPECT_LT(performer_.Close(), 0);
EXPECT_TRUE(performer_.IsHeaderParsed());
EXPECT_EQ(kBrilloMajorPayloadVersion, performer_.GetMajorVersion());
uint64_t manifest_offset;
EXPECT_TRUE(performer_.GetManifestOffset(&manifest_offset));
EXPECT_EQ(24U, manifest_offset); // 4 + 8 + 8 + 4
EXPECT_EQ(manifest_offset + manifest_size, performer_.GetMetadataSize());
EXPECT_EQ(metadata_signature_size, performer_.metadata_signature_size_);
}
TEST_F(DeltaPerformerTest, BrilloVerifyMetadataSignatureTest) {
brillo::Blob payload_data = GeneratePayload({}, {}, true,
kBrilloMajorPayloadVersion,
kSourceMinorPayloadVersion);
install_plan_.hash_checks_mandatory = true;
// Just set these value so that we can use ValidateMetadataSignature directly.
performer_.major_payload_version_ = kBrilloMajorPayloadVersion;
performer_.metadata_size_ = install_plan_.metadata_size;
uint64_t signature_length;
EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
{GetBuildArtifactsPath(kUnittestPrivateKeyPath)}, &signature_length));
performer_.metadata_signature_size_ = signature_length;
performer_.set_public_key_path(GetBuildArtifactsPath(kUnittestPublicKeyPath));
EXPECT_EQ(ErrorCode::kSuccess,
performer_.ValidateMetadataSignature(payload_data));
}
TEST_F(DeltaPerformerTest, BadDeltaMagicTest) {
EXPECT_TRUE(performer_.Write("junk", 4));
EXPECT_FALSE(performer_.Write("morejunk", 8));
EXPECT_LT(performer_.Close(), 0);
}
TEST_F(DeltaPerformerTest, MissingMandatoryMetadataSizeTest) {
DoMetadataSizeTest(0, 75456, true);
}
TEST_F(DeltaPerformerTest, MissingNonMandatoryMetadataSizeTest) {
DoMetadataSizeTest(0, 123456, false);
}
TEST_F(DeltaPerformerTest, InvalidMandatoryMetadataSizeTest) {
DoMetadataSizeTest(13000, 140000, true);
}
TEST_F(DeltaPerformerTest, InvalidNonMandatoryMetadataSizeTest) {
DoMetadataSizeTest(40000, 50000, false);
}
TEST_F(DeltaPerformerTest, ValidMandatoryMetadataSizeTest) {
DoMetadataSizeTest(85376, 85376, true);
}
TEST_F(DeltaPerformerTest, MandatoryEmptyMetadataSignatureTest) {
DoMetadataSignatureTest(kEmptyMetadataSignature, true, true);
}
TEST_F(DeltaPerformerTest, NonMandatoryEmptyMetadataSignatureTest) {
DoMetadataSignatureTest(kEmptyMetadataSignature, true, false);
}
TEST_F(DeltaPerformerTest, MandatoryInvalidMetadataSignatureTest) {
DoMetadataSignatureTest(kInvalidMetadataSignature, true, true);
}
TEST_F(DeltaPerformerTest, NonMandatoryInvalidMetadataSignatureTest) {
DoMetadataSignatureTest(kInvalidMetadataSignature, true, false);
}
TEST_F(DeltaPerformerTest, MandatoryValidMetadataSignature1Test) {
DoMetadataSignatureTest(kValidMetadataSignature, false, true);
}
TEST_F(DeltaPerformerTest, MandatoryValidMetadataSignature2Test) {
DoMetadataSignatureTest(kValidMetadataSignature, true, true);
}
TEST_F(DeltaPerformerTest, NonMandatoryValidMetadataSignatureTest) {
DoMetadataSignatureTest(kValidMetadataSignature, true, false);
}
TEST_F(DeltaPerformerTest, UsePublicKeyFromResponse) {
base::FilePath key_path;
// The result of the GetPublicKeyResponse() method is based on three things
//
// 1. Whether it's an official build; and
// 2. Whether the Public RSA key to be used is in the root filesystem; and
// 3. Whether the response has a public key
//
// We test all eight combinations to ensure that we only use the
// public key in the response if
//
// a. it's not an official build; and
// b. there is no key in the root filesystem.
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
string non_existing_file = temp_dir.path().Append("non-existing").value();
string existing_file = temp_dir.path().Append("existing").value();
EXPECT_EQ(0, System(base::StringPrintf("touch %s", existing_file.c_str())));
// Non-official build, non-existing public-key, key in response -> true
fake_hardware_.SetIsOfficialBuild(false);
performer_.public_key_path_ = non_existing_file;
install_plan_.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
EXPECT_TRUE(performer_.GetPublicKeyFromResponse(&key_path));
EXPECT_FALSE(key_path.empty());
EXPECT_EQ(unlink(key_path.value().c_str()), 0);
// Same with official build -> false
fake_hardware_.SetIsOfficialBuild(true);
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Non-official build, existing public-key, key in response -> false
fake_hardware_.SetIsOfficialBuild(false);
performer_.public_key_path_ = existing_file;
install_plan_.public_key_rsa = "VGVzdAo="; // result of 'echo "Test" | base64'
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Same with official build -> false
fake_hardware_.SetIsOfficialBuild(true);
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Non-official build, non-existing public-key, no key in response -> false
fake_hardware_.SetIsOfficialBuild(false);
performer_.public_key_path_ = non_existing_file;
install_plan_.public_key_rsa = "";
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Same with official build -> false
fake_hardware_.SetIsOfficialBuild(true);
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Non-official build, existing public-key, no key in response -> false
fake_hardware_.SetIsOfficialBuild(false);
performer_.public_key_path_ = existing_file;
install_plan_.public_key_rsa = "";
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Same with official build -> false
fake_hardware_.SetIsOfficialBuild(true);
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Non-official build, non-existing public-key, key in response
// but invalid base64 -> false
fake_hardware_.SetIsOfficialBuild(false);
performer_.public_key_path_ = non_existing_file;
install_plan_.public_key_rsa = "not-valid-base64";
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
}
TEST_F(DeltaPerformerTest, ConfVersionsMatch) {
// Test that the versions in update_engine.conf that is installed to the
// image match the supported delta versions in the update engine.
uint32_t minor_version;
brillo::KeyValueStore store;
EXPECT_TRUE(store.Load(GetBuildArtifactsPath().Append("update_engine.conf")));
EXPECT_TRUE(utils::GetMinorVersion(store, &minor_version));
EXPECT_EQ(DeltaPerformer::kSupportedMinorPayloadVersion, minor_version);
string major_version_str;
uint64_t major_version;
EXPECT_TRUE(store.GetString("PAYLOAD_MAJOR_VERSION", &major_version_str));
EXPECT_TRUE(base::StringToUint64(major_version_str, &major_version));
EXPECT_EQ(DeltaPerformer::kSupportedMajorPayloadVersion, major_version);
}
} // namespace chromeos_update_engine