Enable brotli_bsdiff in update package generation am: de1685fd76 am: 8679c142bb
am: 1c14a6cf2d
Change-Id: I3cf6b81d24f146b3b73562e8f31cfbd83966e4e7
diff --git a/Android.mk b/Android.mk
index d2a83bc..8f2c8fa 100644
--- a/Android.mk
+++ b/Android.mk
@@ -143,6 +143,7 @@
payload_consumer/install_plan.cc \
payload_consumer/mount_history.cc \
payload_consumer/payload_constants.cc \
+ payload_consumer/payload_metadata.cc \
payload_consumer/payload_verifier.cc \
payload_consumer/postinstall_runner_action.cc \
payload_consumer/xz_extent_writer.cc
diff --git a/binder_bindings/android/os/IUpdateEngine.aidl b/binder_bindings/android/os/IUpdateEngine.aidl
index 7e26752..c0e29f5 100644
--- a/binder_bindings/android/os/IUpdateEngine.aidl
+++ b/binder_bindings/android/os/IUpdateEngine.aidl
@@ -37,4 +37,6 @@
void cancel();
/** @hide */
void resetStatus();
+ /** @hide */
+ boolean verifyPayloadApplicable(in String metadataFilename);
}
diff --git a/binder_service_android.cc b/binder_service_android.cc
index 0305727..1702ead 100644
--- a/binder_service_android.cc
+++ b/binder_service_android.cc
@@ -139,6 +139,20 @@
return Status::ok();
}
+Status BinderUpdateEngineAndroidService::verifyPayloadApplicable(
+ const android::String16& metadata_filename, bool* return_value) {
+ const std::string payload_metadata{
+ android::String8{metadata_filename}.string()};
+ LOG(INFO) << "Received a request of verifying payload metadata in "
+ << payload_metadata << ".";
+ brillo::ErrorPtr error;
+ *return_value =
+ service_delegate_->VerifyPayloadApplicable(payload_metadata, &error);
+ if (error != nullptr)
+ return ErrorPtrToStatus(error);
+ return Status::ok();
+}
+
bool BinderUpdateEngineAndroidService::UnbindCallback(const IBinder* callback) {
auto it = std::find_if(
callbacks_.begin(),
diff --git a/binder_service_android.h b/binder_service_android.h
index eb36e4c..694b80a 100644
--- a/binder_service_android.h
+++ b/binder_service_android.h
@@ -65,6 +65,8 @@
android::binder::Status resume() override;
android::binder::Status cancel() override;
android::binder::Status resetStatus() override;
+ android::binder::Status verifyPayloadApplicable(
+ const android::String16& metadata_filename, bool* return_value) override;
private:
// Remove the passed |callback| from the list of registered callbacks. Called
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 440243e..c2c43db 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -16,7 +16,6 @@
#include "update_engine/payload_consumer/delta_performer.h"
-#include <endian.h>
#include <errno.h>
#include <linux/fs.h>
@@ -65,13 +64,6 @@
namespace chromeos_update_engine {
-const uint64_t DeltaPerformer::kDeltaVersionOffset = sizeof(kDeltaMagic);
-const uint64_t DeltaPerformer::kDeltaVersionSize = 8;
-const uint64_t DeltaPerformer::kDeltaManifestSizeOffset =
- kDeltaVersionOffset + kDeltaVersionSize;
-const uint64_t DeltaPerformer::kDeltaManifestSizeSize = 8;
-const uint64_t DeltaPerformer::kDeltaMetadataSignatureSizeSize = 4;
-const uint64_t DeltaPerformer::kMaxPayloadHeaderSize = 24;
const uint64_t DeltaPerformer::kSupportedMajorPayloadVersion = 2;
const uint32_t DeltaPerformer::kSupportedMinorPayloadVersion = 4;
@@ -422,39 +414,6 @@
} // namespace
-bool DeltaPerformer::GetMetadataSignatureSizeOffset(
- uint64_t* out_offset) const {
- if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
- *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
- return true;
- }
- return false;
-}
-
-bool DeltaPerformer::GetManifestOffset(uint64_t* out_offset) const {
- // Actual manifest begins right after the manifest size field or
- // metadata signature size field if major version >= 2.
- if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
- *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
- return true;
- }
- if (major_payload_version_ == kBrilloMajorPayloadVersion) {
- *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
- kDeltaMetadataSignatureSizeSize;
- return true;
- }
- LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
- return false;
-}
-
-uint64_t DeltaPerformer::GetMetadataSize() const {
- return metadata_size_;
-}
-
-uint64_t DeltaPerformer::GetMajorVersion() const {
- return major_payload_version_;
-}
-
uint32_t DeltaPerformer::GetMinorVersion() const {
if (manifest_.has_minor_version()) {
return manifest_.minor_version();
@@ -465,96 +424,35 @@
}
}
-bool DeltaPerformer::GetManifest(DeltaArchiveManifest* out_manifest_p) const {
- if (!manifest_parsed_)
- return false;
- *out_manifest_p = manifest_;
- return true;
-}
-
bool DeltaPerformer::IsHeaderParsed() const {
return metadata_size_ != 0;
}
-DeltaPerformer::MetadataParseResult DeltaPerformer::ParsePayloadMetadata(
+MetadataParseResult DeltaPerformer::ParsePayloadMetadata(
const brillo::Blob& payload, ErrorCode* error) {
*error = ErrorCode::kSuccess;
- uint64_t manifest_offset;
if (!IsHeaderParsed()) {
- // Ensure we have data to cover the major payload version.
- if (payload.size() < kDeltaManifestSizeOffset)
- return kMetadataParseInsufficientData;
+ MetadataParseResult result = payload_metadata_.ParsePayloadHeader(
+ payload, supported_major_version_, error);
+ if (result != MetadataParseResult::kSuccess)
+ return result;
- // Validate the magic string.
- if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
- LOG(ERROR) << "Bad payload format -- invalid delta magic.";
- *error = ErrorCode::kDownloadInvalidMetadataMagicString;
- return kMetadataParseError;
- }
-
- // Extract the payload version from the metadata.
- static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
- "Major payload version size mismatch");
- memcpy(&major_payload_version_,
- &payload[kDeltaVersionOffset],
- kDeltaVersionSize);
- // switch big endian to host
- major_payload_version_ = be64toh(major_payload_version_);
-
- if (major_payload_version_ != supported_major_version_ &&
- major_payload_version_ != kChromeOSMajorPayloadVersion) {
- LOG(ERROR) << "Bad payload format -- unsupported payload version: "
- << major_payload_version_;
- *error = ErrorCode::kUnsupportedMajorPayloadVersion;
- return kMetadataParseError;
- }
-
- // Get the manifest offset now that we have payload version.
- if (!GetManifestOffset(&manifest_offset)) {
- *error = ErrorCode::kUnsupportedMajorPayloadVersion;
- return kMetadataParseError;
- }
- // Check again with the manifest offset.
- if (payload.size() < manifest_offset)
- return kMetadataParseInsufficientData;
-
- // Next, parse the manifest size.
- static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
- "manifest_size size mismatch");
- memcpy(&manifest_size_,
- &payload[kDeltaManifestSizeOffset],
- kDeltaManifestSizeSize);
- manifest_size_ = be64toh(manifest_size_); // switch big endian to host
-
- if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
- // Parse the metadata signature size.
- static_assert(sizeof(metadata_signature_size_) ==
- kDeltaMetadataSignatureSizeSize,
- "metadata_signature_size size mismatch");
- uint64_t metadata_signature_size_offset;
- if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
- *error = ErrorCode::kError;
- return kMetadataParseError;
- }
- memcpy(&metadata_signature_size_,
- &payload[metadata_signature_size_offset],
- kDeltaMetadataSignatureSizeSize);
- metadata_signature_size_ = be32toh(metadata_signature_size_);
- }
+ metadata_size_ = payload_metadata_.GetMetadataSize();
+ metadata_signature_size_ = payload_metadata_.GetMetadataSignatureSize();
+ major_payload_version_ = payload_metadata_.GetMajorVersion();
// If the metadata size is present in install plan, check for it immediately
// even before waiting for that many number of bytes to be downloaded in the
// payload. This will prevent any attack which relies on us downloading data
// beyond the expected metadata size.
- metadata_size_ = manifest_offset + manifest_size_;
if (install_plan_->hash_checks_mandatory) {
if (payload_->metadata_size != metadata_size_) {
LOG(ERROR) << "Mandatory metadata size in Omaha response ("
<< payload_->metadata_size
<< ") is missing/incorrect, actual = " << metadata_size_;
*error = ErrorCode::kDownloadInvalidMetadataSize;
- return kMetadataParseError;
+ return MetadataParseResult::kError;
}
}
}
@@ -562,7 +460,7 @@
// Now that we have validated the metadata size, we should wait for the full
// metadata and its signature (if exist) to be read in before we can parse it.
if (payload.size() < metadata_size_ + metadata_signature_size_)
- return kMetadataParseInsufficientData;
+ return MetadataParseResult::kInsufficientData;
// Log whether we validated the size or simply trusting what's in the payload
// here. This is logged here (after we received the full metadata data) so
@@ -579,15 +477,25 @@
<< "Trusting metadata size in payload = " << metadata_size_;
}
+ // See if we should use the public RSA key in the Omaha response.
+ base::FilePath path_to_public_key(public_key_path_);
+ base::FilePath tmp_key;
+ if (GetPublicKeyFromResponse(&tmp_key))
+ path_to_public_key = tmp_key;
+ ScopedPathUnlinker tmp_key_remover(tmp_key.value());
+ if (tmp_key.empty())
+ tmp_key_remover.set_should_remove(false);
+
// We have the full metadata in |payload|. Verify its integrity
// and authenticity based on the information we have in Omaha response.
- *error = ValidateMetadataSignature(payload);
+ *error = payload_metadata_.ValidateMetadataSignature(
+ payload, payload_->metadata_signature, path_to_public_key);
if (*error != ErrorCode::kSuccess) {
if (install_plan_->hash_checks_mandatory) {
// The autoupdate_CatchBadSignatures test checks for this string
// in log-files. Keep in sync.
LOG(ERROR) << "Mandatory metadata signature validation failed";
- return kMetadataParseError;
+ return MetadataParseResult::kError;
}
// For non-mandatory cases, just send a UMA stat.
@@ -595,19 +503,15 @@
*error = ErrorCode::kSuccess;
}
- if (!GetManifestOffset(&manifest_offset)) {
- *error = ErrorCode::kUnsupportedMajorPayloadVersion;
- return kMetadataParseError;
- }
// The payload metadata is deemed valid, it's safe to parse the protobuf.
- if (!manifest_.ParseFromArray(&payload[manifest_offset], manifest_size_)) {
+ if (!payload_metadata_.GetManifest(payload, &manifest_)) {
LOG(ERROR) << "Unable to parse manifest in update file.";
*error = ErrorCode::kDownloadManifestParseError;
- return kMetadataParseError;
+ return MetadataParseResult::kError;
}
manifest_parsed_ = true;
- return kMetadataParseSuccess;
+ return MetadataParseResult::kSuccess;
}
#define OP_DURATION_HISTOGRAM(_op_name, _start_time) \
@@ -639,9 +543,9 @@
metadata_size_ + metadata_signature_size_));
MetadataParseResult result = ParsePayloadMetadata(buffer_, error);
- if (result == kMetadataParseError)
+ if (result == MetadataParseResult::kError)
return false;
- if (result == kMetadataParseInsufficientData) {
+ if (result == MetadataParseResult::kInsufficientData) {
// If we just processed the header, make an attempt on the manifest.
if (do_read_header && IsHeaderParsed())
continue;
@@ -1076,15 +980,10 @@
return true;
}
-namespace {
-
-// Compare |calculated_hash| with source hash in |operation|, return false and
-// dump hash and set |error| if don't match.
-// |source_fd| is the file descriptor of the source partition.
-bool ValidateSourceHash(const brillo::Blob& calculated_hash,
- const InstallOperation& operation,
- const FileDescriptorPtr source_fd,
- ErrorCode* error) {
+bool DeltaPerformer::ValidateSourceHash(const brillo::Blob& calculated_hash,
+ const InstallOperation& operation,
+ const FileDescriptorPtr source_fd,
+ ErrorCode* error) {
brillo::Blob expected_source_hash(operation.src_sha256_hash().begin(),
operation.src_sha256_hash().end());
if (calculated_hash != expected_source_hash) {
@@ -1119,8 +1018,6 @@
return true;
}
-} // namespace
-
bool DeltaPerformer::PerformSourceCopyOperation(
const InstallOperation& operation, ErrorCode* error) {
if (operation.has_src_length())
@@ -1489,93 +1386,6 @@
return true;
}
-ErrorCode DeltaPerformer::ValidateMetadataSignature(
- const brillo::Blob& payload) {
- if (payload.size() < metadata_size_ + metadata_signature_size_)
- return ErrorCode::kDownloadMetadataSignatureError;
-
- brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
- if (!payload_->metadata_signature.empty()) {
- // Convert base64-encoded signature to raw bytes.
- if (!brillo::data_encoding::Base64Decode(payload_->metadata_signature,
- &metadata_signature_blob)) {
- LOG(ERROR) << "Unable to decode base64 metadata signature: "
- << payload_->metadata_signature;
- return ErrorCode::kDownloadMetadataSignatureError;
- }
- } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
- metadata_signature_protobuf_blob.assign(
- payload.begin() + metadata_size_,
- payload.begin() + metadata_size_ + metadata_signature_size_);
- }
-
- if (metadata_signature_blob.empty() &&
- metadata_signature_protobuf_blob.empty()) {
- if (install_plan_->hash_checks_mandatory) {
- LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
- << "response and payload.";
- return ErrorCode::kDownloadMetadataSignatureMissingError;
- }
-
- LOG(WARNING) << "Cannot validate metadata as the signature is empty";
- return ErrorCode::kSuccess;
- }
-
- // See if we should use the public RSA key in the Omaha response.
- base::FilePath path_to_public_key(public_key_path_);
- base::FilePath tmp_key;
- if (GetPublicKeyFromResponse(&tmp_key))
- path_to_public_key = tmp_key;
- ScopedPathUnlinker tmp_key_remover(tmp_key.value());
- if (tmp_key.empty())
- tmp_key_remover.set_should_remove(false);
-
- LOG(INFO) << "Verifying metadata hash signature using public key: "
- << path_to_public_key.value();
-
- brillo::Blob calculated_metadata_hash;
- if (!HashCalculator::RawHashOfBytes(
- payload.data(), metadata_size_, &calculated_metadata_hash)) {
- LOG(ERROR) << "Unable to compute actual hash of manifest";
- return ErrorCode::kDownloadMetadataSignatureVerificationError;
- }
-
- PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
- if (calculated_metadata_hash.empty()) {
- LOG(ERROR) << "Computed actual hash of metadata is empty.";
- return ErrorCode::kDownloadMetadataSignatureVerificationError;
- }
-
- if (!metadata_signature_blob.empty()) {
- brillo::Blob expected_metadata_hash;
- if (!PayloadVerifier::GetRawHashFromSignature(metadata_signature_blob,
- path_to_public_key.value(),
- &expected_metadata_hash)) {
- LOG(ERROR) << "Unable to compute expected hash from metadata signature";
- return ErrorCode::kDownloadMetadataSignatureError;
- }
- if (calculated_metadata_hash != expected_metadata_hash) {
- LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
- utils::HexDumpVector(expected_metadata_hash);
- LOG(ERROR) << "Calculated hash = ";
- utils::HexDumpVector(calculated_metadata_hash);
- return ErrorCode::kDownloadMetadataSignatureMismatch;
- }
- } else {
- if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
- path_to_public_key.value(),
- calculated_metadata_hash)) {
- LOG(ERROR) << "Manifest hash verification failed.";
- return ErrorCode::kDownloadMetadataSignatureMismatch;
- }
- }
-
- // The autoupdate_CatchBadSignatures test checks for this string in
- // log-files. Keep in sync.
- LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
- return ErrorCode::kSuccess;
-}
-
ErrorCode DeltaPerformer::ValidateManifest() {
// Perform assorted checks to sanity check the manifest, make sure it
// matches data from other sources, and that it is a supported version.
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index d5ca799..ac9ca80 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -33,6 +33,7 @@
#include "update_engine/payload_consumer/file_descriptor.h"
#include "update_engine/payload_consumer/file_writer.h"
#include "update_engine/payload_consumer/install_plan.h"
+#include "update_engine/payload_consumer/payload_metadata.h"
#include "update_engine/update_metadata.pb.h"
namespace chromeos_update_engine {
@@ -47,18 +48,6 @@
class DeltaPerformer : public FileWriter {
public:
- enum MetadataParseResult {
- kMetadataParseSuccess,
- kMetadataParseError,
- kMetadataParseInsufficientData,
- };
-
- static const uint64_t kDeltaVersionOffset;
- static const uint64_t kDeltaVersionSize;
- static const uint64_t kDeltaManifestSizeOffset;
- static const uint64_t kDeltaManifestSizeSize;
- static const uint64_t kDeltaMetadataSignatureSizeSize;
- static const uint64_t kMaxPayloadHeaderSize;
static const uint64_t kSupportedMajorPayloadVersion;
static const uint32_t kSupportedMinorPayloadVersion;
@@ -165,39 +154,26 @@
public_key_path_ = public_key_path;
}
- // Set |*out_offset| to the byte offset where the size of the metadata
- // signature is stored in a payload. Return true on success, if this field is
- // not present in the payload, return false.
- bool GetMetadataSignatureSizeOffset(uint64_t* out_offset) const;
-
- // Set |*out_offset| to the byte offset at which the manifest protobuf begins
- // in a payload. Return true on success, false if the offset is unknown.
- bool GetManifestOffset(uint64_t* out_offset) const;
-
- // Returns the size of the payload metadata, which includes the payload header
- // and the manifest. If the header was not yet parsed, returns zero.
- uint64_t GetMetadataSize() const;
-
- // If the manifest was successfully parsed, copies it to |*out_manifest_p|.
- // Returns true on success.
- bool GetManifest(DeltaArchiveManifest* out_manifest_p) const;
-
// Return true if header parsing is finished and no errors occurred.
bool IsHeaderParsed() const;
- // Returns the major payload version. If the version was not yet parsed,
- // returns zero.
- uint64_t GetMajorVersion() const;
-
// Returns the delta minor version. If this value is defined in the manifest,
// it returns that value, otherwise it returns the default value.
uint32_t GetMinorVersion() const;
+ // Compare |calculated_hash| with source hash in |operation|, return false and
+ // dump hash and set |error| if don't match.
+ // |source_fd| is the file descriptor of the source partition.
+ static bool ValidateSourceHash(const brillo::Blob& calculated_hash,
+ const InstallOperation& operation,
+ const FileDescriptorPtr source_fd,
+ ErrorCode* error);
+
private:
friend class DeltaPerformerTest;
friend class DeltaPerformerIntegrationTest;
FRIEND_TEST(DeltaPerformerTest, BrilloMetadataSignatureSizeTest);
- FRIEND_TEST(DeltaPerformerTest, BrilloVerifyMetadataSignatureTest);
+ FRIEND_TEST(DeltaPerformerTest, BrilloParsePayloadMetadataTest);
FRIEND_TEST(DeltaPerformerTest, UsePublicKeyFromResponse);
// Parse and move the update instructions of all partitions into our local
@@ -235,16 +211,6 @@
// Returns ErrorCode::kSuccess on match or a suitable error code otherwise.
ErrorCode ValidateOperationHash(const InstallOperation& operation);
- // Given the |payload|, verifies that the signed hash of its metadata matches
- // what's specified in the install plan from Omaha (if present) or the
- // metadata signature in payload itself (if present). Returns
- // ErrorCode::kSuccess on match or a suitable error code otherwise. This
- // method must be called before any part of the metadata is parsed so that a
- // man-in-the-middle attack on the SSL connection to the payload server
- // doesn't exploit any vulnerability in the code that parses the protocol
- // buffer.
- ErrorCode ValidateMetadataSignature(const brillo::Blob& payload);
-
// Returns true on success.
bool PerformInstallOperation(const InstallOperation& operation);
@@ -325,13 +291,14 @@
std::string source_path_;
std::string target_path_;
+ PayloadMetadata payload_metadata_;
+
// Parsed manifest. Set after enough bytes to parse the manifest were
// downloaded.
DeltaArchiveManifest manifest_;
bool manifest_parsed_{false};
bool manifest_valid_{false};
uint64_t metadata_size_{0};
- uint64_t manifest_size_{0};
uint32_t metadata_signature_size_{0};
uint64_t major_payload_version_{0};
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index 420efd2..88df98a 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -317,20 +317,20 @@
install_plan_.hash_checks_mandatory = hash_checks_mandatory;
- DeltaPerformer::MetadataParseResult expected_result, actual_result;
+ 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:
payload_.metadata_signature.clear();
- expected_result = DeltaPerformer::kMetadataParseError;
+ expected_result = MetadataParseResult::kError;
expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
break;
case kInvalidMetadataSignature:
payload_.metadata_signature = kBogusMetadataSignature1;
- expected_result = DeltaPerformer::kMetadataParseError;
+ expected_result = MetadataParseResult::kError;
expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
break;
@@ -345,14 +345,14 @@
GetBuildArtifactsPath(kUnittestPrivateKeyPath),
&payload_.metadata_signature));
EXPECT_FALSE(payload_.metadata_signature.empty());
- expected_result = DeltaPerformer::kMetadataParseSuccess;
+ expected_result = MetadataParseResult::kSuccess;
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_result = MetadataParseResult::kSuccess;
expected_error = ErrorCode::kSuccess;
}
@@ -372,7 +372,7 @@
// Check that the parsed metadata size is what's expected. This test
// implicitly confirms that the metadata signature is valid, if required.
- EXPECT_EQ(payload_.metadata_size, performer_.GetMetadataSize());
+ EXPECT_EQ(payload_.metadata_size, performer_.metadata_size_);
}
void SetSupportedMajorVersion(uint64_t major_version) {
@@ -741,40 +741,32 @@
uint64_t major_version = htobe64(kBrilloMajorPayloadVersion);
EXPECT_TRUE(performer_.Write(&major_version, 8));
- uint64_t manifest_size = rand() % 256;
+ uint64_t manifest_size = 222;
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 = 111;
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(kBrilloMajorPayloadVersion, performer_.major_payload_version_);
+ EXPECT_EQ(24 + manifest_size, performer_.metadata_size_); // 4 + 8 + 8 + 4
EXPECT_EQ(metadata_signature_size, performer_.metadata_signature_size_);
}
-TEST_F(DeltaPerformerTest, BrilloVerifyMetadataSignatureTest) {
+TEST_F(DeltaPerformerTest, BrilloParsePayloadMetadataTest) {
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_ = payload_.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));
+ ErrorCode error;
+ EXPECT_EQ(MetadataParseResult::kSuccess,
+ performer_.ParsePayloadMetadata(payload_data, &error));
+ EXPECT_EQ(ErrorCode::kSuccess, error);
}
TEST_F(DeltaPerformerTest, BadDeltaMagicTest) {
@@ -855,7 +847,8 @@
// 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'
+ // result of 'echo "Test" | base64'
+ install_plan_.public_key_rsa = "VGVzdAo=";
EXPECT_TRUE(performer_.GetPublicKeyFromResponse(&key_path));
EXPECT_FALSE(key_path.empty());
EXPECT_EQ(unlink(key_path.value().c_str()), 0);
@@ -866,7 +859,8 @@
// 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'
+ // result of 'echo "Test" | base64'
+ install_plan_.public_key_rsa = "VGVzdAo=";
EXPECT_FALSE(performer_.GetPublicKeyFromResponse(&key_path));
// Same with official build -> false
fake_hardware_.SetIsOfficialBuild(true);
diff --git a/payload_consumer/payload_constants.cc b/payload_consumer/payload_constants.cc
index ad193a0..9338d29 100644
--- a/payload_consumer/payload_constants.cc
+++ b/payload_consumer/payload_constants.cc
@@ -27,6 +27,8 @@
const uint32_t kOpSrcHashMinorPayloadVersion = 3;
const uint32_t kPuffdiffMinorPayloadVersion = 4;
+const uint64_t kMaxPayloadHeaderSize = 24;
+
const char kLegacyPartitionNameKernel[] = "boot";
const char kLegacyPartitionNameRoot[] = "system";
diff --git a/payload_consumer/payload_constants.h b/payload_consumer/payload_constants.h
index 1e2e810..b3bd5e7 100644
--- a/payload_consumer/payload_constants.h
+++ b/payload_consumer/payload_constants.h
@@ -46,6 +46,9 @@
// The minor version that allows PUFFDIFF operation.
extern const uint32_t kPuffdiffMinorPayloadVersion;
+// The maximum size of the payload header (anything before the protobuf).
+extern const uint64_t kMaxPayloadHeaderSize;
+
// The kernel and rootfs partition names used by the BootControlInterface when
// handling update payloads with a major version 1. The names of the updated
// partitions are include in the payload itself for major version 2.
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc
new file mode 100644
index 0000000..fe2df0a
--- /dev/null
+++ b/payload_consumer/payload_metadata.cc
@@ -0,0 +1,216 @@
+//
+// 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_consumer/payload_metadata.h"
+
+#include <endian.h>
+
+#include <brillo/data_encoding.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
+
+namespace chromeos_update_engine {
+
+const uint64_t PayloadMetadata::kDeltaVersionOffset = sizeof(kDeltaMagic);
+const uint64_t PayloadMetadata::kDeltaVersionSize = 8;
+const uint64_t PayloadMetadata::kDeltaManifestSizeOffset =
+ kDeltaVersionOffset + kDeltaVersionSize;
+const uint64_t PayloadMetadata::kDeltaManifestSizeSize = 8;
+const uint64_t PayloadMetadata::kDeltaMetadataSignatureSizeSize = 4;
+
+bool PayloadMetadata::GetMetadataSignatureSizeOffset(
+ uint64_t* out_offset) const {
+ if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+ *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+ return true;
+ }
+ return false;
+}
+
+bool PayloadMetadata::GetManifestOffset(uint64_t* out_offset) const {
+ // Actual manifest begins right after the manifest size field or
+ // metadata signature size field if major version >= 2.
+ if (major_payload_version_ == kChromeOSMajorPayloadVersion) {
+ *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize;
+ return true;
+ }
+ if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+ *out_offset = kDeltaManifestSizeOffset + kDeltaManifestSizeSize +
+ kDeltaMetadataSignatureSizeSize;
+ return true;
+ }
+ LOG(ERROR) << "Unknown major payload version: " << major_payload_version_;
+ return false;
+}
+
+MetadataParseResult PayloadMetadata::ParsePayloadHeader(
+ const brillo::Blob& payload,
+ uint64_t supported_major_version,
+ ErrorCode* error) {
+ uint64_t manifest_offset;
+ // Ensure we have data to cover the major payload version.
+ if (payload.size() < kDeltaManifestSizeOffset)
+ return MetadataParseResult::kInsufficientData;
+
+ // Validate the magic string.
+ if (memcmp(payload.data(), kDeltaMagic, sizeof(kDeltaMagic)) != 0) {
+ LOG(ERROR) << "Bad payload format -- invalid delta magic.";
+ *error = ErrorCode::kDownloadInvalidMetadataMagicString;
+ return MetadataParseResult::kError;
+ }
+
+ // Extract the payload version from the metadata.
+ static_assert(sizeof(major_payload_version_) == kDeltaVersionSize,
+ "Major payload version size mismatch");
+ memcpy(&major_payload_version_,
+ &payload[kDeltaVersionOffset],
+ kDeltaVersionSize);
+ // Switch big endian to host.
+ major_payload_version_ = be64toh(major_payload_version_);
+
+ if (major_payload_version_ != supported_major_version &&
+ major_payload_version_ != kChromeOSMajorPayloadVersion) {
+ LOG(ERROR) << "Bad payload format -- unsupported payload version: "
+ << major_payload_version_;
+ *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+ return MetadataParseResult::kError;
+ }
+
+ // Get the manifest offset now that we have payload version.
+ if (!GetManifestOffset(&manifest_offset)) {
+ *error = ErrorCode::kUnsupportedMajorPayloadVersion;
+ return MetadataParseResult::kError;
+ }
+ // Check again with the manifest offset.
+ if (payload.size() < manifest_offset)
+ return MetadataParseResult::kInsufficientData;
+
+ // Next, parse the manifest size.
+ static_assert(sizeof(manifest_size_) == kDeltaManifestSizeSize,
+ "manifest_size size mismatch");
+ memcpy(&manifest_size_,
+ &payload[kDeltaManifestSizeOffset],
+ kDeltaManifestSizeSize);
+ manifest_size_ = be64toh(manifest_size_); // switch big endian to host
+
+ if (GetMajorVersion() == kBrilloMajorPayloadVersion) {
+ // Parse the metadata signature size.
+ static_assert(
+ sizeof(metadata_signature_size_) == kDeltaMetadataSignatureSizeSize,
+ "metadata_signature_size size mismatch");
+ uint64_t metadata_signature_size_offset;
+ if (!GetMetadataSignatureSizeOffset(&metadata_signature_size_offset)) {
+ *error = ErrorCode::kError;
+ return MetadataParseResult::kError;
+ }
+ memcpy(&metadata_signature_size_,
+ &payload[metadata_signature_size_offset],
+ kDeltaMetadataSignatureSizeSize);
+ metadata_signature_size_ = be32toh(metadata_signature_size_);
+ }
+ metadata_size_ = manifest_offset + manifest_size_;
+ return MetadataParseResult::kSuccess;
+}
+
+bool PayloadMetadata::GetManifest(const brillo::Blob& payload,
+ DeltaArchiveManifest* out_manifest) const {
+ uint64_t manifest_offset;
+ if (!GetManifestOffset(&manifest_offset))
+ return false;
+ CHECK_GE(payload.size(), manifest_offset + manifest_size_);
+ return out_manifest->ParseFromArray(&payload[manifest_offset],
+ manifest_size_);
+}
+
+ErrorCode PayloadMetadata::ValidateMetadataSignature(
+ const brillo::Blob& payload,
+ std::string metadata_signature,
+ base::FilePath path_to_public_key) const {
+ if (payload.size() < metadata_size_ + metadata_signature_size_)
+ return ErrorCode::kDownloadMetadataSignatureError;
+
+ brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
+ if (!metadata_signature.empty()) {
+ // Convert base64-encoded signature to raw bytes.
+ if (!brillo::data_encoding::Base64Decode(metadata_signature,
+ &metadata_signature_blob)) {
+ LOG(ERROR) << "Unable to decode base64 metadata signature: "
+ << metadata_signature;
+ return ErrorCode::kDownloadMetadataSignatureError;
+ }
+ } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
+ metadata_signature_protobuf_blob.assign(
+ payload.begin() + metadata_size_,
+ payload.begin() + metadata_size_ + metadata_signature_size_);
+ }
+
+ if (metadata_signature_blob.empty() &&
+ metadata_signature_protobuf_blob.empty()) {
+ LOG(ERROR) << "Missing mandatory metadata signature in both Omaha "
+ << "response and payload.";
+ return ErrorCode::kDownloadMetadataSignatureMissingError;
+ }
+
+ LOG(INFO) << "Verifying metadata hash signature using public key: "
+ << path_to_public_key.value();
+
+ brillo::Blob calculated_metadata_hash;
+ if (!HashCalculator::RawHashOfBytes(
+ payload.data(), metadata_size_, &calculated_metadata_hash)) {
+ LOG(ERROR) << "Unable to compute actual hash of manifest";
+ return ErrorCode::kDownloadMetadataSignatureVerificationError;
+ }
+
+ PayloadVerifier::PadRSA2048SHA256Hash(&calculated_metadata_hash);
+ if (calculated_metadata_hash.empty()) {
+ LOG(ERROR) << "Computed actual hash of metadata is empty.";
+ return ErrorCode::kDownloadMetadataSignatureVerificationError;
+ }
+
+ if (!metadata_signature_blob.empty()) {
+ brillo::Blob expected_metadata_hash;
+ if (!PayloadVerifier::GetRawHashFromSignature(metadata_signature_blob,
+ path_to_public_key.value(),
+ &expected_metadata_hash)) {
+ LOG(ERROR) << "Unable to compute expected hash from metadata signature";
+ return ErrorCode::kDownloadMetadataSignatureError;
+ }
+ if (calculated_metadata_hash != expected_metadata_hash) {
+ LOG(ERROR) << "Manifest hash verification failed. Expected hash = ";
+ utils::HexDumpVector(expected_metadata_hash);
+ LOG(ERROR) << "Calculated hash = ";
+ utils::HexDumpVector(calculated_metadata_hash);
+ return ErrorCode::kDownloadMetadataSignatureMismatch;
+ }
+ } else {
+ if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf_blob,
+ path_to_public_key.value(),
+ calculated_metadata_hash)) {
+ LOG(ERROR) << "Manifest hash verification failed.";
+ return ErrorCode::kDownloadMetadataSignatureMismatch;
+ }
+ }
+
+ // The autoupdate_CatchBadSignatures test checks for this string in
+ // log-files. Keep in sync.
+ LOG(INFO) << "Metadata hash signature matches value in Omaha response.";
+ return ErrorCode::kSuccess;
+}
+
+} // namespace chromeos_update_engine
diff --git a/payload_consumer/payload_metadata.h b/payload_consumer/payload_metadata.h
new file mode 100644
index 0000000..e00b5c1
--- /dev/null
+++ b/payload_consumer/payload_metadata.h
@@ -0,0 +1,108 @@
+//
+// 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.
+//
+
+#ifndef UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_METADATA_H_
+#define UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_METADATA_H_
+
+#include <inttypes.h>
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <brillo/secure_blob.h>
+
+#include "update_engine/common/error_code.h"
+#include "update_engine/common/platform_constants.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+enum class MetadataParseResult {
+ kSuccess,
+ kError,
+ kInsufficientData,
+};
+
+// This class parses payload metadata and validate its signature.
+class PayloadMetadata {
+ public:
+ static const uint64_t kDeltaVersionOffset;
+ static const uint64_t kDeltaVersionSize;
+ static const uint64_t kDeltaManifestSizeOffset;
+ static const uint64_t kDeltaManifestSizeSize;
+ static const uint64_t kDeltaMetadataSignatureSizeSize;
+
+ PayloadMetadata() = default;
+
+ // Attempts to parse the update payload header starting from the beginning of
+ // |payload|. On success, returns kMetadataParseSuccess. Returns
+ // kMetadataParseInsufficientData if more data is needed to parse the complete
+ // metadata. Returns kMetadataParseError if the metadata can't be parsed given
+ // the payload.
+ MetadataParseResult ParsePayloadHeader(const brillo::Blob& payload,
+ uint64_t supported_major_version,
+ ErrorCode* error);
+
+ // Given the |payload|, verifies that the signed hash of its metadata matches
+ // |metadata_signature| (if present) or the metadata signature in payload
+ // itself (if present). Returns ErrorCode::kSuccess on match or a suitable
+ // error code otherwise. This method must be called before any part of the
+ // metadata is parsed so that a man-in-the-middle attack on the SSL connection
+ // to the payload server doesn't exploit any vulnerability in the code that
+ // parses the protocol buffer.
+ ErrorCode ValidateMetadataSignature(const brillo::Blob& payload,
+ std::string metadata_signature,
+ base::FilePath path_to_public_key) const;
+
+ // Returns the major payload version. If the version was not yet parsed,
+ // returns zero.
+ uint64_t GetMajorVersion() const { return major_payload_version_; }
+
+ // Returns the size of the payload metadata, which includes the payload header
+ // and the manifest. If the header was not yet parsed, returns zero.
+ uint64_t GetMetadataSize() const { return metadata_size_; }
+
+ // Returns the size of the payload metadata signature. If the header was not
+ // yet parsed, returns zero.
+ uint32_t GetMetadataSignatureSize() const { return metadata_signature_size_; }
+
+ // Set |*out_manifest| to the manifest in |payload|.
+ // Returns true on success.
+ bool GetManifest(const brillo::Blob& payload,
+ DeltaArchiveManifest* out_manifest) const;
+
+ private:
+ // Set |*out_offset| to the byte offset at which the manifest protobuf begins
+ // in a payload. Return true on success, false if the offset is unknown.
+ bool GetManifestOffset(uint64_t* out_offset) const;
+
+ // Set |*out_offset| to the byte offset where the size of the metadata
+ // signature is stored in a payload. Return true on success, if this field is
+ // not present in the payload, return false.
+ bool GetMetadataSignatureSizeOffset(uint64_t* out_offset) const;
+
+ uint64_t metadata_size_{0};
+ uint64_t manifest_size_{0};
+ uint32_t metadata_signature_size_{0};
+ uint64_t major_payload_version_{0};
+
+ DISALLOW_COPY_AND_ASSIGN(PayloadMetadata);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // UPDATE_ENGINE_PAYLOAD_CONSUMER_PAYLOAD_METADATA_H_
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
index 824195d..0b47dd4 100644
--- a/payload_generator/payload_signer.cc
+++ b/payload_generator/payload_signer.cc
@@ -18,6 +18,8 @@
#include <endian.h>
+#include <utility>
+
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
@@ -248,7 +250,7 @@
TEST_AND_RETURN_FALSE(payload_file);
brillo::Blob payload_metadata;
- payload_metadata.resize(DeltaPerformer::kMaxPayloadHeaderSize);
+ payload_metadata.resize(kMaxPayloadHeaderSize);
TEST_AND_RETURN_FALSE(payload_file->ReadAllBlocking(
payload_metadata.data(), payload_metadata.size(), nullptr));
diff --git a/service_delegate_android_interface.h b/service_delegate_android_interface.h
index 7dae40f..5267bb0 100644
--- a/service_delegate_android_interface.h
+++ b/service_delegate_android_interface.h
@@ -70,6 +70,12 @@
// of error, returns false and sets |error| accordingly.
virtual bool ResetStatus(brillo::ErrorPtr* error) = 0;
+ // Verifies whether a payload (delegated by the payload metadata) can be
+ // applied to the current device. Returns whether the payload is applicable.
+ // In case of error, returns false and sets |error| accordingly.
+ virtual bool VerifyPayloadApplicable(const std::string& metadata_filename,
+ brillo::ErrorPtr* error) = 0;
+
protected:
ServiceDelegateAndroidInterface() = default;
};
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index aacb06b..04ccb18 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -32,14 +32,20 @@
#include <log/log_safetynet.h>
#include "update_engine/common/constants.h"
+#include "update_engine/common/error_code_utils.h"
#include "update_engine/common/file_fetcher.h"
#include "update_engine/common/utils.h"
#include "update_engine/daemon_state_interface.h"
#include "update_engine/metrics_reporter_interface.h"
#include "update_engine/metrics_utils.h"
#include "update_engine/network_selector.h"
+#include "update_engine/payload_consumer/delta_performer.h"
#include "update_engine/payload_consumer/download_action.h"
+#include "update_engine/payload_consumer/file_descriptor.h"
+#include "update_engine/payload_consumer/file_descriptor_utils.h"
#include "update_engine/payload_consumer/filesystem_verifier_action.h"
+#include "update_engine/payload_consumer/payload_constants.h"
+#include "update_engine/payload_consumer/payload_metadata.h"
#include "update_engine/payload_consumer/postinstall_runner_action.h"
#include "update_engine/update_status_utils.h"
@@ -327,6 +333,96 @@
}
}
+bool UpdateAttempterAndroid::VerifyPayloadApplicable(
+ const std::string& metadata_filename, brillo::ErrorPtr* error) {
+ FileDescriptorPtr fd(new EintrSafeFileDescriptor);
+ if (!fd->Open(metadata_filename.c_str(), O_RDONLY)) {
+ return LogAndSetError(
+ error, FROM_HERE, "Failed to open " + metadata_filename);
+ }
+ brillo::Blob metadata(kMaxPayloadHeaderSize);
+ if (!fd->Read(metadata.data(), metadata.size())) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Failed to read payload header from " + metadata_filename);
+ }
+ ErrorCode errorcode;
+ PayloadMetadata payload_metadata;
+ if (payload_metadata.ParsePayloadHeader(
+ metadata, kBrilloMajorPayloadVersion, &errorcode) !=
+ MetadataParseResult::kSuccess) {
+ return LogAndSetError(error,
+ FROM_HERE,
+ "Failed to parse payload header: " +
+ utils::ErrorCodeToString(errorcode));
+ }
+ metadata.resize(payload_metadata.GetMetadataSize() +
+ payload_metadata.GetMetadataSignatureSize());
+ if (metadata.size() < kMaxPayloadHeaderSize) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Metadata size too small: " + std::to_string(metadata.size()));
+ }
+ if (!fd->Read(metadata.data() + kMaxPayloadHeaderSize,
+ metadata.size() - kMaxPayloadHeaderSize)) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Failed to read metadata and signature from " + metadata_filename);
+ }
+ fd->Close();
+ errorcode = payload_metadata.ValidateMetadataSignature(
+ metadata, "", base::FilePath(constants::kUpdatePayloadPublicKeyPath));
+ if (errorcode != ErrorCode::kSuccess) {
+ return LogAndSetError(error,
+ FROM_HERE,
+ "Failed to validate metadata signature: " +
+ utils::ErrorCodeToString(errorcode));
+ }
+ DeltaArchiveManifest manifest;
+ if (!payload_metadata.GetManifest(metadata, &manifest)) {
+ return LogAndSetError(error, FROM_HERE, "Failed to parse manifest.");
+ }
+
+ BootControlInterface::Slot current_slot = boot_control_->GetCurrentSlot();
+ for (const PartitionUpdate& partition : manifest.partitions()) {
+ if (!partition.has_old_partition_info())
+ continue;
+ string partition_path;
+ if (!boot_control_->GetPartitionDevice(
+ partition.partition_name(), current_slot, &partition_path)) {
+ return LogAndSetError(
+ error,
+ FROM_HERE,
+ "Failed to get partition device for " + partition.partition_name());
+ }
+ if (!fd->Open(partition_path.c_str(), O_RDONLY)) {
+ return LogAndSetError(
+ error, FROM_HERE, "Failed to open " + partition_path);
+ }
+ for (const InstallOperation& operation : partition.operations()) {
+ if (!operation.has_src_sha256_hash())
+ continue;
+ brillo::Blob source_hash;
+ if (!fd_utils::ReadAndHashExtents(fd,
+ operation.src_extents(),
+ manifest.block_size(),
+ &source_hash)) {
+ return LogAndSetError(
+ error, FROM_HERE, "Failed to hash " + partition_path);
+ }
+ if (!DeltaPerformer::ValidateSourceHash(
+ source_hash, operation, fd, &errorcode)) {
+ return false;
+ }
+ }
+ fd->Close();
+ }
+ return true;
+}
+
void UpdateAttempterAndroid::ProcessingDone(const ActionProcessor* processor,
ErrorCode code) {
LOG(INFO) << "Processing Done.";
diff --git a/update_attempter_android.h b/update_attempter_android.h
index 28bf90a..f00692e 100644
--- a/update_attempter_android.h
+++ b/update_attempter_android.h
@@ -69,6 +69,8 @@
bool ResumeUpdate(brillo::ErrorPtr* error) override;
bool CancelUpdate(brillo::ErrorPtr* error) override;
bool ResetStatus(brillo::ErrorPtr* error) override;
+ bool VerifyPayloadApplicable(const std::string& metadata_filename,
+ brillo::ErrorPtr* error) override;
// ActionProcessorDelegate methods:
void ProcessingDone(const ActionProcessor* processor,
diff --git a/update_engine.gyp b/update_engine.gyp
index 8111100..1ff4d7f 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -183,6 +183,7 @@
'payload_consumer/install_plan.cc',
'payload_consumer/mount_history.cc',
'payload_consumer/payload_constants.cc',
+ 'payload_consumer/payload_metadata.cc',
'payload_consumer/payload_verifier.cc',
'payload_consumer/postinstall_runner_action.cc',
'payload_consumer/xz_extent_writer.cc',