Index: delta_performer.cc |
diff --git a/delta_performer.cc b/delta_performer.cc |
index 04273f34e732037e543e29b64254da8485def354..2436797210fa895738ee43d28d8f9fd7d4180c25 100644 |
--- a/delta_performer.cc |
+++ b/delta_performer.cc |
@@ -3,21 +3,24 @@ |
// found in the LICENSE file. |
#include "update_engine/delta_performer.h" |
+ |
#include <endian.h> |
#include <errno.h> |
+ |
#include <algorithm> |
#include <cstring> |
#include <string> |
#include <vector> |
+#include <base/scoped_ptr.h> |
+#include <base/string_util.h> |
#include <google/protobuf/repeated_field.h> |
-#include "base/scoped_ptr.h" |
-#include "base/string_util.h" |
#include "update_engine/bzip_extent_writer.h" |
#include "update_engine/delta_diff_generator.h" |
#include "update_engine/extent_writer.h" |
#include "update_engine/graph_types.h" |
+#include "update_engine/payload_signer.h" |
#include "update_engine/subprocess.h" |
using std::min; |
@@ -31,11 +34,8 @@ namespace { |
const int kDeltaVersionLength = 8; |
const int kDeltaProtobufLengthLength = 8; |
- |
-// Remove count bytes from the beginning of *buffer. |
-void RemoveBufferHeadBytes(vector<char>* buffer, size_t count) { |
- buffer->erase(buffer->begin(), buffer->begin() + count); |
-} |
+const char kUpdatePayloadPublicKeyPath[] = |
+ "/usr/share/update_engine/update-payload-key.pub.pem"; |
// Converts extents to a human-readable string, for use by DumpUpdateProto(). |
string ExtentsToString(const RepeatedPtrField<Extent>& extents) { |
@@ -136,6 +136,7 @@ int DeltaPerformer::Close() { |
err = errno; |
PLOG(ERROR) << "Unable to close rootfs fd:"; |
} |
+ LOG_IF(ERROR, !hash_calculator_.Finalize()) << "Unable to finalize the hash."; |
fd_ = -2; // Set so that isn't not valid AND calls to Open() will fail. |
path_ = ""; |
return -err; |
@@ -176,10 +177,10 @@ ssize_t DeltaPerformer::Write(const void* bytes, size_t count) { |
} |
// Remove protobuf and header info from buffer_, so buffer_ contains |
// just data blobs |
- RemoveBufferHeadBytes(&buffer_, |
- strlen(kDeltaMagic) + |
- kDeltaVersionLength + |
- kDeltaProtobufLengthLength + protobuf_length); |
+ DiscardBufferHeadBytes(strlen(kDeltaMagic) + |
+ kDeltaVersionLength + |
+ kDeltaProtobufLengthLength + protobuf_length, |
+ true); // do_hash |
manifest_valid_ = true; |
block_size_ = manifest_.block_size(); |
} |
@@ -239,7 +240,7 @@ bool DeltaPerformer::CanPerformInstallOperation( |
LOG(ERROR) << "we threw away data it seems?"; |
return false; |
} |
- |
+ |
return (operation.data_offset() + operation.data_length()) <= |
(buffer_offset_ + buffer_.size()); |
} |
@@ -256,11 +257,14 @@ bool DeltaPerformer::PerformReplaceOperation( |
// the data we need should be exactly at the beginning of the buffer. |
CHECK_EQ(buffer_offset_, operation.data_offset()); |
CHECK_GE(buffer_.size(), operation.data_length()); |
- |
+ |
+ // Don't include the signature data blob in the hash. |
+ bool do_hash = !ExtractSignatureMessage(operation); |
+ |
DirectExtentWriter direct_writer; |
ZeroPadExtentWriter zero_pad_writer(&direct_writer); |
scoped_ptr<BzipExtentWriter> bzip_writer; |
- |
+ |
// Since bzip decompression is optional, we have a variable writer that will |
// point to one of the ExtentWriter objects above. |
ExtentWriter* writer = NULL; |
@@ -285,10 +289,10 @@ bool DeltaPerformer::PerformReplaceOperation( |
TEST_AND_RETURN_FALSE(writer->Init(fd, extents, block_size_)); |
TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length())); |
TEST_AND_RETURN_FALSE(writer->End()); |
- |
+ |
// Update buffer |
buffer_offset_ += operation.data_length(); |
- RemoveBufferHeadBytes(&buffer_, operation.data_length()); |
+ DiscardBufferHeadBytes(operation.data_length(), do_hash); |
return true; |
} |
@@ -431,8 +435,58 @@ bool DeltaPerformer::PerformBsdiffOperation( |
// Update buffer. |
buffer_offset_ += operation.data_length(); |
- RemoveBufferHeadBytes(&buffer_, operation.data_length()); |
+ DiscardBufferHeadBytes(operation.data_length(), |
+ true); // do_hash |
+ return true; |
+} |
+ |
+bool DeltaPerformer::ExtractSignatureMessage( |
+ const DeltaArchiveManifest_InstallOperation& operation) { |
+ if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_REPLACE || |
+ !manifest_.has_signatures_offset() || |
+ manifest_.signatures_offset() != operation.data_offset()) { |
+ return false; |
+ } |
+ TEST_AND_RETURN_FALSE(manifest_.has_signatures_size() && |
+ manifest_.signatures_size() == operation.data_length()); |
+ TEST_AND_RETURN_FALSE(signatures_message_data_.empty()); |
+ TEST_AND_RETURN_FALSE(buffer_offset_ == manifest_.signatures_offset()); |
+ TEST_AND_RETURN_FALSE(buffer_.size() >= manifest_.signatures_size()); |
+ signatures_message_data_.insert( |
+ signatures_message_data_.begin(), |
+ buffer_.begin(), |
+ buffer_.begin() + manifest_.signatures_size()); |
+ LOG(INFO) << "Extracted signature data of size " |
+ << manifest_.signatures_size() << " at " |
+ << manifest_.signatures_offset(); |
return true; |
} |
+bool DeltaPerformer::VerifyPayload(const string& public_key_path) { |
+ string key_path = public_key_path; |
+ if (key_path.empty()) { |
+ key_path = kUpdatePayloadPublicKeyPath; |
+ } |
+ LOG(INFO) << "Verifying delta payload. Public key path: " << key_path; |
+ if (!utils::FileExists(key_path.c_str())) { |
+ LOG(WARNING) << "Not verifying delta payload due to missing public key."; |
+ return true; |
+ } |
+ TEST_AND_RETURN_FALSE(!signatures_message_data_.empty()); |
+ vector<char> signed_hash_data; |
+ TEST_AND_RETURN_FALSE(PayloadSigner::VerifySignature(signatures_message_data_, |
+ key_path, |
+ &signed_hash_data)); |
+ const vector<char>& hash_data = hash_calculator_.raw_hash(); |
+ TEST_AND_RETURN_FALSE(!hash_data.empty()); |
+ return hash_data == signed_hash_data; |
+} |
+ |
+void DeltaPerformer::DiscardBufferHeadBytes(size_t count, bool do_hash) { |
+ if (do_hash) { |
+ hash_calculator_.Update(&buffer_[0], count); |
+ } |
+ buffer_.erase(buffer_.begin(), buffer_.begin() + count); |
+} |
+ |
} // namespace chromeos_update_engine |