AU: Sign delta payloads
- Change .proto to have explicit offset/length of signature. I was
hoping the length could be kept out of the proto, but it needs to go
in. The way we cheat and keep the signature in the file is to have a
dummer install operation at the end that will cause old clients to
write the signature data to nowhere.
- Change delta generator to take an optional private key, which if
present will cause the payload to be signed
- Cleanup Omaha hash calculator, which should be renamed to SHA1 hash
calculator, and allow export of the non-base64 encoded SHA1 result.
- Note: signatures are not yet checked. That will come in a future CL.
BUG=5662
TEST=unittests
Review URL: http://codereview.chromium.org/3132033
diff --git a/delta_diff_generator.cc b/delta_diff_generator.cc
index 48730a8..10f6846 100644
--- a/delta_diff_generator.cc
+++ b/delta_diff_generator.cc
@@ -21,6 +21,7 @@
#include "update_engine/filesystem_iterator.h"
#include "update_engine/graph_types.h"
#include "update_engine/graph_utils.h"
+#include "update_engine/payload_signer.h"
#include "update_engine/subprocess.h"
#include "update_engine/topological_sort.h"
#include "update_engine/update_metadata.pb.h"
@@ -92,7 +93,7 @@
<< graph[vertex].file_name;
// See if this is already present.
TEST_AND_RETURN_FALSE(operation.dst_extents_size() > 0);
-
+
enum BlockField { READER = 0, WRITER, BLOCK_FIELD_COUNT };
for (int field = READER; field < BLOCK_FIELD_COUNT; field++) {
const int extents_size =
@@ -158,13 +159,13 @@
TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, &data[0], data.size()));
*data_file_size += data.size();
-
+
// Now, insert into graph and blocks vector
graph->resize(graph->size() + 1);
graph->back().op = operation;
CHECK(graph->back().op.has_type());
graph->back().file_name = path;
-
+
TEST_AND_RETURN_FALSE(AddInstallOpToBlocksVector(graph->back().op,
blocks,
*graph,
@@ -196,7 +197,7 @@
continue;
LOG(INFO) << "Encoding file " << fs_iter.GetPartialPath();
-
+
TEST_AND_RETURN_FALSE(DeltaReadFile(graph,
blocks,
old_root,
@@ -298,17 +299,17 @@
FILE* file = fopen(temp_file_path.c_str(), "w");
TEST_AND_RETURN_FALSE(file);
int err = BZ_OK;
-
+
BZFILE* bz_file = BZ2_bzWriteOpen(&err,
file,
9, // max compression
0, // verbosity
0); // default work factor
TEST_AND_RETURN_FALSE(err == BZ_OK);
-
+
vector<Extent> extents;
vector<Block>::size_type block_count = 0;
-
+
LOG(INFO) << "Appending left over blocks to extents";
for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
if (blocks[i].writer != Vertex::kInvalidIndex)
@@ -356,12 +357,12 @@
bz_file = NULL;
TEST_AND_RETURN_FALSE_ERRNO(0 == fclose(file));
file = NULL;
-
+
vector<char> compressed_data;
LOG(INFO) << "Reading compressed data off disk";
TEST_AND_RETURN_FALSE(utils::ReadFile(temp_file_path, &compressed_data));
TEST_AND_RETURN_FALSE(unlink(temp_file_path.c_str()) == 0);
-
+
// Add node to graph to write these blocks
out_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
out_op->set_data_offset(*blobs_length);
@@ -369,7 +370,7 @@
*blobs_length += compressed_data.size();
out_op->set_dst_length(kBlockSize * block_count);
DeltaDiffGenerator::StoreExtents(extents, out_op->mutable_dst_extents());
-
+
TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd,
&compressed_data[0],
compressed_data.size()));
@@ -414,7 +415,7 @@
}
// Delta compresses a kernel partition new_kernel_part with knowledge of
-// the old kernel partition old_kernel_part.
+// the old kernel partition old_kernel_part.
bool DeltaCompressKernelPartition(
const string& old_kernel_part,
const string& new_kernel_part,
@@ -438,12 +439,12 @@
TEST_AND_RETURN_FALSE(BsdiffFiles(old_kernel_part, new_kernel_part, &data));
TEST_AND_RETURN_FALSE(utils::WriteAll(blobs_fd, &data[0], data.size()));
*blobs_length += data.size();
-
+
off_t old_part_size = utils::FileSize(old_kernel_part);
TEST_AND_RETURN_FALSE(old_part_size >= 0);
off_t new_part_size = utils::FileSize(new_kernel_part);
TEST_AND_RETURN_FALSE(new_part_size >= 0);
-
+
op->set_data_length(data.size());
op->set_src_length(old_part_size);
@@ -457,7 +458,7 @@
Extent* dst_extent = op->add_dst_extents();
dst_extent->set_start_block(0);
dst_extent->set_num_blocks((new_part_size + kBlockSize - 1) / kBlockSize);
-
+
LOG(INFO) << "Done delta compressing kernel partition.";
return true;
}
@@ -472,9 +473,9 @@
// Read new data in
vector<char> new_data;
TEST_AND_RETURN_FALSE(utils::ReadFile(new_filename, &new_data));
-
+
TEST_AND_RETURN_FALSE(!new_data.empty());
-
+
vector<char> new_data_bz;
TEST_AND_RETURN_FALSE(BzipCompress(new_data, &new_data_bz));
CHECK(!new_data_bz.empty());
@@ -516,15 +517,15 @@
if (bsdiff_delta.size() < current_best_size) {
operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
current_best_size = bsdiff_delta.size();
-
+
data = bsdiff_delta;
}
}
}
-
+
// Set parameters of the operations
CHECK_EQ(data.size(), current_best_size);
-
+
if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE ||
operation.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) {
TEST_AND_RETURN_FALSE(
@@ -535,10 +536,10 @@
TEST_AND_RETURN_FALSE(
GatherExtents(new_filename, operation.mutable_dst_extents()));
operation.set_dst_length(new_data.size());
-
+
out_data->swap(data);
*out_op = operation;
-
+
return true;
}
@@ -613,7 +614,7 @@
TEST_AND_RETURN_FALSE(
FindScratchSpace(blocks, blocks_required, &scratch_extents));
LinearExtentAllocator scratch_allocator(scratch_extents);
-
+
uint64_t scratch_blocks_used = 0;
for (set<Edge>::const_iterator it = edges.begin();
it != edges.end(); ++it) {
@@ -628,7 +629,7 @@
scratch_allocator.Allocate(graph_utils::EdgeWeight(*graph, *it));
// create vertex to copy original->scratch
graph->resize(graph->size() + 1);
-
+
// make node depend on the copy operation
(*graph)[it->first].out_edges.insert(make_pair(graph->size() - 1,
EdgeProperties()));
@@ -743,9 +744,10 @@
const string& old_image,
const string& new_root,
const string& new_image,
- const std::string& old_kernel_part,
- const std::string& new_kernel_part,
- const string& output_path) {
+ const string& old_kernel_part,
+ const string& new_kernel_part,
+ const string& output_path,
+ const string& private_key_path) {
struct stat old_image_stbuf;
TEST_AND_RETURN_FALSE_ERRNO(stat(old_image.c_str(), &old_image_stbuf) == 0);
struct stat new_image_stbuf;
@@ -771,7 +773,7 @@
}
Graph graph;
CheckGraph(graph);
-
+
const string kTempFileTemplate("/tmp/CrAU_temp_data.XXXXXX");
string temp_file_path;
off_t data_file_size = 0;
@@ -787,7 +789,7 @@
utils::MakeTempFile(kTempFileTemplate, &temp_file_path, &fd));
TEST_AND_RETURN_FALSE(fd >= 0);
ScopedFdCloser fd_closer(&fd);
-
+
TEST_AND_RETURN_FALSE(DeltaReadFiles(&graph,
&blocks,
old_root,
@@ -795,12 +797,12 @@
fd,
&data_file_size));
CheckGraph(graph);
-
+
TEST_AND_RETURN_FALSE(ReadUnwrittenBlocks(blocks,
fd,
&data_file_size,
new_image,
- &final_op));
+ &final_op));
// Read kernel partition
TEST_AND_RETURN_FALSE(DeltaCompressKernelPartition(old_kernel_part,
@@ -810,11 +812,11 @@
&data_file_size));
}
CheckGraph(graph);
-
+
LOG(INFO) << "Creating edges...";
CreateEdges(&graph, blocks);
CheckGraph(graph);
-
+
CycleBreaker cycle_breaker;
LOG(INFO) << "Finding cycles...";
set<Edge> cut_edges;
@@ -831,7 +833,7 @@
LOG(INFO) << "Ordering...";
TopologicalSort(graph, &final_order);
CheckGraph(graph);
-
+
// Convert to protobuf Manifest object
DeltaArchiveManifest manifest;
CheckGraph(graph);
@@ -844,7 +846,7 @@
CHECK(op->has_type());
LOG(INFO) << "final op length: " << op->data_length();
}
-
+
CheckGraph(graph);
manifest.set_block_size(kBlockSize);
@@ -859,29 +861,29 @@
ordered_blobs_path));
// Check that install op blobs are in order and that all blocks are written.
+ uint64_t next_blob_offset = 0;
{
vector<uint32_t> written_count(blocks.size(), 0);
- uint64_t next_blob_offset = 0;
for (int i = 0; i < (manifest.install_operations_size() +
manifest.kernel_install_operations_size()); i++) {
- const DeltaArchiveManifest_InstallOperation& op =
+ DeltaArchiveManifest_InstallOperation* op =
i < manifest.install_operations_size() ?
- manifest.install_operations(i) :
- manifest.kernel_install_operations(
+ manifest.mutable_install_operations(i) :
+ manifest.mutable_kernel_install_operations(
i - manifest.install_operations_size());
- for (int j = 0; j < op.dst_extents_size(); j++) {
- const Extent& extent = op.dst_extents(j);
+ for (int j = 0; j < op->dst_extents_size(); j++) {
+ const Extent& extent = op->dst_extents(j);
for (uint64_t block = extent.start_block();
block < (extent.start_block() + extent.num_blocks()); block++) {
written_count[block]++;
}
}
- if (op.has_data_offset()) {
- if (op.data_offset() != next_blob_offset) {
- LOG(FATAL) << "bad blob offset! " << op.data_offset() << " != "
+ if (op->has_data_offset()) {
+ if (op->data_offset() != next_blob_offset) {
+ LOG(FATAL) << "bad blob offset! " << op->data_offset() << " != "
<< next_blob_offset;
}
- next_blob_offset += op.data_length();
+ next_blob_offset += op->data_length();
}
}
// check all blocks written to
@@ -892,9 +894,34 @@
}
}
+ // Signatures appear at the end of the blobs. Note the offset in the
+ // manifest
+ if (!private_key_path.empty()) {
+ LOG(INFO) << "Making room for signature in file";
+ manifest.set_signatures_offset(next_blob_offset);
+ LOG(INFO) << "set? " << manifest.has_signatures_offset();
+ // Add a dummy op at the end to appease older clients
+ DeltaArchiveManifest_InstallOperation* dummy_op =
+ manifest.add_kernel_install_operations();
+ dummy_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+ dummy_op->set_data_offset(next_blob_offset);
+ manifest.set_signatures_offset(next_blob_offset);
+ uint64_t signature_blob_length = 0;
+ TEST_AND_RETURN_FALSE(
+ PayloadSigner::SignatureBlobLength(private_key_path,
+ &signature_blob_length));
+ dummy_op->set_data_length(signature_blob_length);
+ manifest.set_signatures_size(signature_blob_length);
+ Extent* dummy_extent = dummy_op->add_dst_extents();
+ // Tell the dummy op to write this data to a big sparse hole
+ dummy_extent->set_start_block(kSparseHole);
+ dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) /
+ kBlockSize);
+ }
+
// Serialize protobuf
string serialized_manifest;
-
+
CheckGraph(graph);
TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest));
CheckGraph(graph);
@@ -905,25 +932,25 @@
O_WRONLY | O_CREAT | O_TRUNC,
0644) == 0);
ScopedFileWriterCloser writer_closer(&writer);
-
+
// Write header
TEST_AND_RETURN_FALSE(writer.Write(kDeltaMagic, strlen(kDeltaMagic)) ==
static_cast<ssize_t>(strlen(kDeltaMagic)));
-
+
// Write version number
TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer, kVersionNumber));
-
+
// Write protobuf length
TEST_AND_RETURN_FALSE(WriteUint64AsBigEndian(&writer,
serialized_manifest.size()));
-
+
// Write protobuf
LOG(INFO) << "Writing final delta file protobuf... "
<< serialized_manifest.size();
TEST_AND_RETURN_FALSE(writer.Write(serialized_manifest.data(),
serialized_manifest.size()) ==
static_cast<ssize_t>(serialized_manifest.size()));
-
+
// Append the data blobs
LOG(INFO) << "Writing final delta file data blobs...";
int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
@@ -939,7 +966,19 @@
TEST_AND_RETURN_FALSE_ERRNO(rc > 0);
TEST_AND_RETURN_FALSE(writer.Write(buf, rc) == rc);
}
-
+
+ // Write signature blob.
+ if (!private_key_path.empty()) {
+ LOG(INFO) << "Signing the update...";
+ vector<char> signature_blob;
+ TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(output_path,
+ private_key_path,
+ &signature_blob));
+ TEST_AND_RETURN_FALSE(writer.Write(&signature_blob[0],
+ signature_blob.size()) ==
+ static_cast<ssize_t>(signature_blob.size()));
+ }
+
LOG(INFO) << "All done. Successfully created delta file.";
return true;
}
diff --git a/delta_diff_generator.h b/delta_diff_generator.h
index efc4747..31f9e64 100644
--- a/delta_diff_generator.h
+++ b/delta_diff_generator.h
@@ -42,6 +42,8 @@
// mounted read-only at paths old_root and new_root respectively.
// {old,new}_kernel_part are paths to the old and new kernel partition
// images, respectively.
+ // private_key_path points to a private key used to sign the update.
+ // Pass empty string to not sign the update.
// output_path is the filename where the delta update should be written.
// Returns true on success.
static bool GenerateDeltaUpdateFile(const std::string& old_root,
@@ -50,7 +52,8 @@
const std::string& new_image,
const std::string& old_kernel_part,
const std::string& new_kernel_part,
- const std::string& output_path);
+ const std::string& output_path,
+ const std::string& private_key_path);
// These functions are public so that the unit tests can access them:
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index fe12b9b..09a4c7d 100755
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -17,6 +17,7 @@
#include "update_engine/delta_diff_generator.h"
#include "update_engine/delta_performer.h"
#include "update_engine/graph_types.h"
+#include "update_engine/payload_signer.h"
#include "update_engine/test_utils.h"
#include "update_engine/update_metadata.pb.h"
#include "update_engine/utils.h"
@@ -27,6 +28,8 @@
using std::string;
using std::vector;
+extern const char* kUnittestPrivateKeyPath;
+
class DeltaPerformerTest : public ::testing::Test { };
TEST(DeltaPerformerTest, ExtentsToByteStringTest) {
@@ -166,7 +169,7 @@
vector<char> new_kernel_data(old_kernel_data.size());
FillWithData(&old_kernel_data);
FillWithData(&new_kernel_data);
-
+
// change the new kernel data
const char* new_data_string = "This is new data.";
strcpy(&new_kernel_data[0], new_data_string);
@@ -179,25 +182,57 @@
string delta_path;
EXPECT_TRUE(utils::MakeTempFile("/tmp/delta.XXXXXX", &delta_path, NULL));
+ LOG(INFO) << "delta path: " << delta_path;
ScopedPathUnlinker delta_path_unlinker(delta_path);
{
string a_mnt, b_mnt;
ScopedLoopMounter a_mounter(a_img, &a_mnt, MS_RDONLY);
ScopedLoopMounter b_mounter(b_img, &b_mnt, MS_RDONLY);
- EXPECT_TRUE(DeltaDiffGenerator::GenerateDeltaUpdateFile(a_mnt,
- a_img,
- b_mnt,
- b_img,
- old_kernel,
- new_kernel,
- delta_path));
+ EXPECT_TRUE(
+ DeltaDiffGenerator::GenerateDeltaUpdateFile(a_mnt,
+ a_img,
+ b_mnt,
+ b_img,
+ old_kernel,
+ new_kernel,
+ delta_path,
+ kUnittestPrivateKeyPath));
}
// Read delta into memory.
vector<char> delta;
EXPECT_TRUE(utils::ReadFile(delta_path, &delta));
+ // Check that the null signature blob exists
+ {
+ LOG(INFO) << "delta size: " << delta.size();
+ DeltaArchiveManifest manifest;
+ const int kManifestSizeOffset = 12;
+ const int kManifestOffset = 20;
+ uint64_t manifest_size = 0;
+ memcpy(&manifest_size, &delta[kManifestSizeOffset], sizeof(manifest_size));
+ manifest_size = be64toh(manifest_size);
+ LOG(INFO) << "manifest size: " << manifest_size;
+ EXPECT_TRUE(manifest.ParseFromArray(&delta[kManifestOffset],
+ manifest_size));
+ EXPECT_TRUE(manifest.has_signatures_offset());
+
+ Signatures sigs_message;
+ EXPECT_TRUE(sigs_message.ParseFromArray(
+ &delta[kManifestOffset + manifest_size + manifest.signatures_offset()],
+ manifest.signatures_size()));
+ EXPECT_EQ(1, sigs_message.signatures_size());
+ const Signatures_Signature& signature = sigs_message.signatures(0);
+ EXPECT_EQ(1, signature.version());
+
+ uint64_t expected_sig_data_length = 0;
+ EXPECT_TRUE(PayloadSigner::SignatureBlobLength(kUnittestPrivateKeyPath,
+ &expected_sig_data_length));
+ EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
+ EXPECT_FALSE(signature.data().empty());
+ }
+
// Update the A image in place.
DeltaPerformer performer;
@@ -215,7 +250,7 @@
EXPECT_EQ(0, performer.Close());
CompareFilesByBlock(old_kernel, new_kernel);
-
+
vector<char> updated_kernel_partition;
EXPECT_TRUE(utils::ReadFile(old_kernel, &updated_kernel_partition));
EXPECT_EQ(0, strncmp(&updated_kernel_partition[0], new_data_string,
diff --git a/generate_delta_main.cc b/generate_delta_main.cc
index 08ee8df..81227e4 100644
--- a/generate_delta_main.cc
+++ b/generate_delta_main.cc
@@ -29,6 +29,7 @@
DEFINE_string(out_file, "", "Path to output file");
DEFINE_string(old_kernel, "", "Path to the old kernel partition image");
DEFINE_string(new_kernel, "", "Path to the new kernel partition image");
+DEFINE_string(private_key, "", "Path to private key in .pem format");
DEFINE_string(apply_delta, "",
"If set, apply delta over old_image. (For debugging.)");
@@ -98,7 +99,8 @@
FLAGS_new_image,
FLAGS_old_kernel,
FLAGS_new_kernel,
- FLAGS_out_file);
+ FLAGS_out_file,
+ FLAGS_private_key);
return 0;
}
diff --git a/omaha_hash_calculator.cc b/omaha_hash_calculator.cc
index 7f62622..fee72a9 100644
--- a/omaha_hash_calculator.cc
+++ b/omaha_hash_calculator.cc
@@ -2,51 +2,81 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "update_engine/omaha_hash_calculator.h"
+
#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include "base/logging.h"
-#include "update_engine/omaha_hash_calculator.h"
+#include "update_engine/utils.h"
+
+using std::string;
+using std::vector;
namespace chromeos_update_engine {
-OmahaHashCalculator::OmahaHashCalculator() {
- CHECK_EQ(SHA1_Init(&ctx_), 1);
+OmahaHashCalculator::OmahaHashCalculator() : valid_(false) {
+ valid_ = (SHA1_Init(&ctx_) == 1);
+ LOG_IF(ERROR, !valid_) << "SHA1_Init failed";
}
// Update is called with all of the data that should be hashed in order.
// Mostly just passes the data through to OpenSSL's SHA1_Update()
-void OmahaHashCalculator::Update(const char* data, size_t length) {
- CHECK(hash_.empty()) << "Can't Update after hash is finalized";
+bool OmahaHashCalculator::Update(const char* data, size_t length) {
+ TEST_AND_RETURN_FALSE(valid_);
+ TEST_AND_RETURN_FALSE(hash_.empty());
COMPILE_ASSERT(sizeof(size_t) <= sizeof(unsigned long),
length_param_may_be_truncated_in_SHA1_Update);
- CHECK_EQ(SHA1_Update(&ctx_, data, length), 1);
+ TEST_AND_RETURN_FALSE(SHA1_Update(&ctx_, data, length) == 1);
+ return true;
}
// Call Finalize() when all data has been passed in. This mostly just
// calls OpenSSL's SHA1_Final() and then base64 encodes the hash.
-void OmahaHashCalculator::Finalize() {
- CHECK(hash_.empty()) << "Don't call Finalize() twice";
+bool OmahaHashCalculator::Finalize() {
+ bool success = true;
+ TEST_AND_RETURN_FALSE(hash_.empty());
unsigned char md[SHA_DIGEST_LENGTH];
- CHECK_EQ(SHA1_Final(md, &ctx_), 1);
+ TEST_AND_RETURN_FALSE(SHA1_Final(md, &ctx_) == 1);
// Convert md to base64 encoding and store it in hash_
BIO *b64 = BIO_new(BIO_f_base64());
- CHECK(b64);
+ if (!b64)
+ LOG(INFO) << "BIO_new(BIO_f_base64()) failed";
BIO *bmem = BIO_new(BIO_s_mem());
- CHECK(bmem);
- b64 = BIO_push(b64, bmem);
- CHECK_EQ(BIO_write(b64, md, sizeof(md)), sizeof(md));
- CHECK_EQ(BIO_flush(b64), 1);
+ if (!bmem)
+ LOG(INFO) << "BIO_new(BIO_s_mem()) failed";
+ if (b64 && bmem) {
+ b64 = BIO_push(b64, bmem);
+ success = (BIO_write(b64, md, sizeof(md)) == sizeof(md));
+ if (success)
+ success = (BIO_flush(b64) == 1);
- BUF_MEM *bptr = NULL;
- BIO_get_mem_ptr(b64, &bptr);
- hash_.assign(bptr->data, bptr->length - 1);
-
- BIO_free_all(b64);
+ BUF_MEM *bptr = NULL;
+ BIO_get_mem_ptr(b64, &bptr);
+ hash_.assign(bptr->data, bptr->length - 1);
+ }
+ if (b64) {
+ BIO_free_all(b64);
+ b64 = NULL;
+ }
+ return success;
}
-std::string OmahaHashCalculator::OmahaHashOfBytes(
+bool OmahaHashCalculator::RawHashOfData(const vector<char>& data,
+ vector<char>* out_hash) {
+ OmahaHashCalculator calc;
+ calc.Update(&data[0], data.size());
+
+ out_hash->resize(out_hash->size() + SHA_DIGEST_LENGTH);
+ TEST_AND_RETURN_FALSE(
+ SHA1_Final(reinterpret_cast<unsigned char*>(&(*(out_hash->end() -
+ SHA_DIGEST_LENGTH))),
+ &calc.ctx_) == 1);
+ return true;
+}
+
+string OmahaHashCalculator::OmahaHashOfBytes(
const void* data, size_t length) {
OmahaHashCalculator calc;
calc.Update(reinterpret_cast<const char*>(data), length);
@@ -54,13 +84,11 @@
return calc.hash();
}
-std::string OmahaHashCalculator::OmahaHashOfString(
- const std::string& str) {
+string OmahaHashCalculator::OmahaHashOfString(const string& str) {
return OmahaHashOfBytes(str.data(), str.size());
}
-std::string OmahaHashCalculator::OmahaHashOfData(
- const std::vector<char>& data) {
+string OmahaHashCalculator::OmahaHashOfData(const vector<char>& data) {
return OmahaHashOfBytes(&data[0], data.size());
}
diff --git a/omaha_hash_calculator.h b/omaha_hash_calculator.h
index 900da80..5a666b6 100644
--- a/omaha_hash_calculator.h
+++ b/omaha_hash_calculator.h
@@ -9,6 +9,7 @@
#include <vector>
#include <openssl/sha.h>
#include "base/basictypes.h"
+#include "base/logging.h"
// Omaha uses base64 encoded SHA-1 as the hash. This class provides a simple
// wrapper around OpenSSL providing such a formatted hash of data passed in.
@@ -20,23 +21,28 @@
class OmahaHashCalculator {
public:
- OmahaHashCalculator();
+ OmahaHashCalculator();
// Update is called with all of the data that should be hashed in order.
- // Update will read |length| bytes of |data|
- void Update(const char* data, size_t length);
+ // Update will read |length| bytes of |data|.
+ // Returns true on success.
+ bool Update(const char* data, size_t length);
// Call Finalize() when all data has been passed in. This method tells
// OpenSSl that no more data will come in and base64 encodes the resulting
// hash.
- void Finalize();
+ // Returns true on success.
+ bool Finalize();
// Gets the hash. Finalize() must have been called.
const std::string& hash() const {
- CHECK(!hash_.empty()) << "Call Finalize() first";
+ DCHECK(!hash_.empty()) << "Call Finalize() first";
return hash_;
}
+ static bool RawHashOfData(const std::vector<char>& data,
+ std::vector<char>* out_hash);
+
// Used by tests
static std::string OmahaHashOfBytes(const void* data, size_t length);
static std::string OmahaHashOfString(const std::string& str);
@@ -47,6 +53,9 @@
// non-empty when Finalize is called.
std::string hash_;
+ // Init success
+ bool valid_;
+
// The hash state used by OpenSSL
SHA_CTX ctx_;
DISALLOW_COPY_AND_ASSIGN(OmahaHashCalculator);
@@ -54,4 +63,4 @@
} // namespace chromeos_update_engine
-#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
\ No newline at end of file
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
diff --git a/payload_signer.cc b/payload_signer.cc
index 03ff391..2fa9616 100644
--- a/payload_signer.cc
+++ b/payload_signer.cc
@@ -6,6 +6,7 @@
#include "base/logging.h"
#include "base/string_util.h"
+#include "update_engine/omaha_hash_calculator.h"
#include "update_engine/subprocess.h"
#include "update_engine/update_metadata.pb.h"
#include "update_engine/utils.h"
@@ -24,6 +25,23 @@
TEST_AND_RETURN_FALSE(
utils::MakeTempFile("/tmp/signature.XXXXXX", &sig_path, NULL));
ScopedPathUnlinker sig_path_unlinker(sig_path);
+
+ string hash_path;
+ TEST_AND_RETURN_FALSE(
+ utils::MakeTempFile("/tmp/hash.XXXXXX", &hash_path, NULL));
+ ScopedPathUnlinker hash_path_unlinker(hash_path);
+
+ vector<char> hash_data;
+ {
+ vector<char> payload;
+ // TODO(adlr): Read file in chunks. Not urgent as this runs on the server.
+ TEST_AND_RETURN_FALSE(utils::ReadFile(unsigned_payload_path, &payload));
+ TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfData(payload,
+ &hash_data));
+ }
+ TEST_AND_RETURN_FALSE(utils::WriteFile(hash_path.c_str(),
+ &hash_data[0],
+ hash_data.size()));
// This runs on the server, so it's okay to cop out and call openssl
// executable rather than properly use the library
@@ -32,7 +50,7 @@
' ',
&cmd);
cmd[cmd.size() - 5] = private_key_path;
- cmd[cmd.size() - 3] = unsigned_payload_path;
+ cmd[cmd.size() - 3] = hash_path;
cmd[cmd.size() - 1] = sig_path;
int return_code = 0;
diff --git a/payload_signer_unittest.cc b/payload_signer_unittest.cc
index 4388f3c..2ee58f5 100644
--- a/payload_signer_unittest.cc
+++ b/payload_signer_unittest.cc
@@ -16,32 +16,32 @@
// Note: the test key was generated with the following command:
// openssl genrsa -out unittest_key.pem 1024
-const char kUnittestPrivateKeyPath[] = "unittest_key.pem";
-
namespace chromeos_update_engine {
+const char* kUnittestPrivateKeyPath = "unittest_key.pem";
+
//class PayloadSignerTest : public ::testing::Test {};
TEST(PayloadSignerTest, SimpleTest) {
// Some data and its corresponding signature:
const string kDataToSign = "This is some data to sign.";
const char kExpectedSignature[] = {
- 0x74, 0xd9, 0xea, 0x45, 0xf4, 0xd8, 0x64, 0x16,
- 0x88, 0x1b, 0x7f, 0x8b, 0x5d, 0xcb, 0x22, 0x2c,
- 0xb1, 0xce, 0x6d, 0x6d, 0x7c, 0x8f, 0x76, 0xf0,
- 0xb7, 0xa9, 0x80, 0xb3, 0x5e, 0x0b, 0xdd, 0x99,
- 0xfd, 0x88, 0x1f, 0x64, 0xd6, 0xac, 0x0c, 0x1b,
- 0xb1, 0x3c, 0x28, 0x11, 0x97, 0x15, 0x97, 0xec,
- 0x90, 0x25, 0xa0, 0x64, 0x90, 0x36, 0x5a, 0x96,
- 0x21, 0xdf, 0x16, 0x42, 0x6d, 0x7c, 0xb1, 0xf2,
- 0xf6, 0xe3, 0xb2, 0xa9, 0xea, 0xc8, 0xec, 0x6b,
- 0xa1, 0x99, 0x8a, 0xf0, 0x25, 0x0d, 0xcd, 0x41,
- 0x85, 0x76, 0x7c, 0xe1, 0xd6, 0x70, 0x71, 0xda,
- 0x02, 0x9f, 0xa2, 0x40, 0xb2, 0xfe, 0xfd, 0x84,
- 0x5c, 0xcf, 0x08, 0xa8, 0x50, 0x16, 0x46, 0xc1,
- 0x37, 0xe1, 0x16, 0xd2, 0xf5, 0x49, 0xe3, 0xcb,
- 0x58, 0x57, 0x11, 0x97, 0x49, 0x8f, 0x14, 0x1d,
- 0x4d, 0xa6, 0xfc, 0x75, 0x63, 0x64, 0xa3, 0xd5
+ 0x00, 0x8d, 0x20, 0x22, 0x87, 0xd3, 0xd0, 0xeb,
+ 0x85, 0x80, 0xde, 0x76, 0xa4, 0x5a, 0xac, 0xdc,
+ 0xa8, 0xe0, 0x6e, 0x10, 0x98, 0xc3, 0xa4, 0x55,
+ 0x48, 0xbf, 0x15, 0x98, 0x32, 0xda, 0xbe, 0x21,
+ 0x3d, 0xa8, 0x1a, 0xb6, 0xf9, 0x93, 0x03, 0x70,
+ 0x44, 0x1b, 0xec, 0x39, 0xe3, 0xd4, 0xfd, 0x6b,
+ 0xff, 0x84, 0xee, 0x60, 0xbe, 0xed, 0x9e, 0x5b,
+ 0xac, 0xd5, 0xd6, 0x1a, 0xf9, 0x4e, 0xdb, 0x6d,
+ 0x11, 0x9e, 0x01, 0xb1, 0xcb, 0x55, 0x05, 0x52,
+ 0x8c, 0xad, 0xb6, 0x8e, 0x9f, 0xf7, 0xc2, 0x1a,
+ 0x26, 0xb3, 0x96, 0xd2, 0x4a, 0xfd, 0x7c, 0x96,
+ 0x53, 0x38, 0x3a, 0xcf, 0xab, 0x95, 0x83, 0xbd,
+ 0x8e, 0xe1, 0xbd, 0x07, 0x12, 0xa2, 0x80, 0x18,
+ 0xca, 0x64, 0x91, 0xee, 0x9d, 0x9d, 0xe3, 0x69,
+ 0xc0, 0xab, 0x1b, 0x75, 0x9f, 0xf0, 0x64, 0x74,
+ 0x01, 0xb3, 0x49, 0xea, 0x87, 0x63, 0x04, 0x29
};
string data_path;
diff --git a/update_metadata.proto b/update_metadata.proto
index 20c5d9f..213e740 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -120,9 +120,10 @@
optional uint32 block_size = 3 [default = 4096];
// If signatures are present, the offset into the blobs, generally
- // tacked onto the end of the file. We use an offset rather than
- // a bool to allow for more flexibility in future file formats.
- // If this is absent, it means signatures aren't supported in this
+ // tacked onto the end of the file, and the length. We use an offset
+ // rather than a bool to allow for more flexibility in future file formats.
+ // If either is absent, it means signatures aren't supported in this
// file.
optional uint64 signatures_offset = 4;
+ optional uint64 signatures_size = 5;
}