Support for signing with multiple signature files, key sizes.
If we do a key-rotation in the future, we'll want to sign updates with
two keys. This CL changes the delta generator in a
backwards-compatible way to take multiple key lengths and signature
files: On a command line where one could be given before, now multiple
may be given by colon-delimiting them.
Also, adds two unittests to show that old and new clients can
successfully verify a payload when it's signed by old and new keys.
BUG=chromium-os:19873
TEST=unittests; tested on device
Change-Id: I2063095773a5c71c32704c30b12d6eab2a5f3b80
Reviewed-on: http://gerrit.chromium.org/gerrit/6999
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
Tested-by: Andrew de los Reyes <adlr@chromium.org>
diff --git a/SConstruct b/SConstruct
index d91b365..5fada88 100644
--- a/SConstruct
+++ b/SConstruct
@@ -227,6 +227,7 @@
'dbus-1 dbus-glib-1 gio-2.0 gio-unix-2.0 glib-2.0')
env.ProtocolBuffer('update_metadata.pb.cc', 'update_metadata.proto')
env.PublicKey('unittest_key.pub.pem', 'unittest_key.pem')
+env.PublicKey('unittest_key2.pub.pem', 'unittest_key2.pem')
env.DbusBindings('update_engine.dbusclient.h', 'update_engine.xml')
diff --git a/delta_diff_generator.cc b/delta_diff_generator.cc
index 5c5b2ef..1c962f1 100644
--- a/delta_diff_generator.cc
+++ b/delta_diff_generator.cc
@@ -1460,7 +1460,7 @@
if (!private_key_path.empty()) {
uint64_t signature_blob_length = 0;
TEST_AND_RETURN_FALSE(
- PayloadSigner::SignatureBlobLength(private_key_path,
+ PayloadSigner::SignatureBlobLength(vector<string>(1, private_key_path),
&signature_blob_length));
AddSignatureOp(next_blob_offset, signature_blob_length, &manifest);
}
@@ -1523,9 +1523,10 @@
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(PayloadSigner::SignPayload(
+ output_path,
+ vector<string>(1, private_key_path),
+ &signature_blob));
TEST_AND_RETURN_FALSE(writer.Write(&signature_blob[0],
signature_blob.size()) ==
static_cast<ssize_t>(signature_blob.size()));
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 6318806..0983cd3 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -36,6 +36,8 @@
extern const char* kUnittestPrivateKeyPath;
extern const char* kUnittestPublicKeyPath;
+extern const char* kUnittestPrivateKey2Path;
+extern const char* kUnittestPublicKey2Path;
namespace {
const size_t kBlockSize = 4096;
@@ -101,6 +103,8 @@
kSignatureGenerated, // Sign the payload after it's generated.
kSignatureGeneratedShell, // Sign the generated payload through shell cmds.
kSignatureGeneratedShellBadKey, // Sign with a bad key through shell cmds.
+ kSignatureGeneratedShellRotateCl1, // Rotate key, test client v1
+ kSignatureGeneratedShellRotateCl2, // Rotate key, test client v2
};
size_t GetSignatureSize(const string& private_key_path) {
@@ -117,18 +121,22 @@
void SignGeneratedPayload(const string& payload_path) {
int signature_size = GetSignatureSize(kUnittestPrivateKeyPath);
vector<char> hash;
- ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(payload_path,
- signature_size,
- &hash));
+ ASSERT_TRUE(PayloadSigner::HashPayloadForSigning(
+ payload_path,
+ vector<int>(1, signature_size),
+ &hash));
vector<char> signature;
ASSERT_TRUE(PayloadSigner::SignHash(hash,
kUnittestPrivateKeyPath,
&signature));
- ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(payload_path,
- signature,
- payload_path));
- EXPECT_TRUE(PayloadSigner::VerifySignedPayload(payload_path,
- kUnittestPublicKeyPath));
+ ASSERT_TRUE(PayloadSigner::AddSignatureToPayload(
+ payload_path,
+ vector<vector<char> >(1, signature),
+ payload_path));
+ EXPECT_TRUE(PayloadSigner::VerifySignedPayload(
+ payload_path,
+ kUnittestPublicKeyPath,
+ kSignatureMessageOriginalVersion));
}
void SignGeneratedShellPayload(SignatureTest signature_test,
@@ -139,7 +147,9 @@
&private_key_path,
NULL));
} else {
- ASSERT_EQ(kSignatureGeneratedShell, signature_test);
+ ASSERT_TRUE(signature_test == kSignatureGeneratedShell ||
+ signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2);
}
ScopedPathUnlinker key_unlinker(private_key_path);
key_unlinker.set_should_remove(signature_test ==
@@ -156,12 +166,19 @@
string hash_file;
ASSERT_TRUE(utils::MakeTempFile("/tmp/hash.XXXXXX", &hash_file, NULL));
ScopedPathUnlinker hash_unlinker(hash_file);
+ string signature_size_string;
+ if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2)
+ signature_size_string = StringPrintf("%d:%d",
+ signature_size, signature_size);
+ else
+ signature_size_string = StringPrintf("%d", signature_size);
ASSERT_EQ(0,
System(StringPrintf(
- "./delta_generator -in_file %s -signature_size %d "
+ "./delta_generator -in_file %s -signature_size %s "
"-out_hash_file %s",
payload_path.c_str(),
- signature_size,
+ signature_size_string.c_str(),
hash_file.c_str())));
// Pad the hash
@@ -179,6 +196,21 @@
private_key_path.c_str(),
hash_file.c_str(),
sig_file.c_str())));
+ string sig_file2;
+ ASSERT_TRUE(utils::MakeTempFile("/tmp/signature.XXXXXX", &sig_file2, NULL));
+ ScopedPathUnlinker sig2_unlinker(sig_file2);
+ if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2) {
+ ASSERT_EQ(0,
+ System(StringPrintf(
+ "/usr/bin/openssl rsautl -raw -sign -inkey %s -in %s -out %s",
+ kUnittestPrivateKey2Path,
+ hash_file.c_str(),
+ sig_file2.c_str())));
+ // Append second sig file to first path
+ sig_file += ":" + sig_file2;
+ }
+
ASSERT_EQ(0,
System(StringPrintf(
"./delta_generator -in_file %s -signature_file %s "
@@ -187,9 +219,12 @@
sig_file.c_str(),
payload_path.c_str())));
int verify_result =
- System(StringPrintf("./delta_generator -in_file %s -public_key %s",
- payload_path.c_str(),
- kUnittestPublicKeyPath));
+ System(StringPrintf(
+ "./delta_generator -in_file %s -public_key %s -public_key_version %d",
+ payload_path.c_str(),
+ signature_test == kSignatureGeneratedShellRotateCl2 ?
+ kUnittestPublicKey2Path : kUnittestPublicKeyPath,
+ signature_test == kSignatureGeneratedShellRotateCl2 ? 2 : 1));
if (signature_test == kSignatureGeneratedShellBadKey) {
ASSERT_NE(0, verify_result);
} else {
@@ -327,7 +362,9 @@
if (signature_test == kSignatureGenerated) {
SignGeneratedPayload(delta_path);
} else if (signature_test == kSignatureGeneratedShell ||
- signature_test == kSignatureGeneratedShellBadKey) {
+ signature_test == kSignatureGeneratedShellBadKey ||
+ signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2) {
SignGeneratedShellPayload(signature_test, delta_path);
}
@@ -361,13 +398,23 @@
EXPECT_TRUE(sigs_message.ParseFromArray(
&delta[manifest_metadata_size + manifest.signatures_offset()],
manifest.signatures_size()));
- EXPECT_EQ(1, sigs_message.signatures_size());
+ if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2)
+ EXPECT_EQ(2, sigs_message.signatures_size());
+ else
+ 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;
+ vector<string> key_paths (1, kUnittestPrivateKeyPath);
+ if (signature_test == kSignatureGeneratedShellRotateCl1 ||
+ signature_test == kSignatureGeneratedShellRotateCl2) {
+ key_paths.push_back(kUnittestPrivateKey2Path);
+ }
EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
- kUnittestPrivateKeyPath, &expected_sig_data_length));
+ key_paths,
+ &expected_sig_data_length));
EXPECT_EQ(expected_sig_data_length, manifest.signatures_size());
EXPECT_FALSE(signature.data().empty());
}
@@ -492,7 +539,7 @@
&expected_new_rootfs_hash));
EXPECT_TRUE(expected_new_rootfs_hash == new_rootfs_hash);
}
-}
+} // namespace {}
TEST(DeltaPerformerTest, RunAsRootSmallImageTest) {
DoSmallImageTest(false, false, false, kSignatureGenerator);
@@ -526,6 +573,14 @@
DoSmallImageTest(false, false, false, kSignatureGeneratedShellBadKey);
}
+TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl1Test) {
+ DoSmallImageTest(false, false, false, kSignatureGeneratedShellRotateCl1);
+}
+
+TEST(DeltaPerformerTest, RunAsRootSmallImageSignGeneratedShellRotateCl2Test) {
+ DoSmallImageTest(false, false, false, kSignatureGeneratedShellRotateCl2);
+}
+
TEST(DeltaPerformerTest, BadDeltaMagicTest) {
PrefsMock prefs;
DeltaPerformer performer(&prefs);
diff --git a/generate_delta_main.cc b/generate_delta_main.cc
index 1e4d856..5a2dda0 100644
--- a/generate_delta_main.cc
+++ b/generate_delta_main.cc
@@ -14,6 +14,8 @@
#include <base/command_line.h>
#include <base/logging.h>
+#include <base/string_number_conversions.h>
+#include <base/string_split.h>
#include <gflags/gflags.h>
#include <glib.h>
@@ -41,10 +43,22 @@
DEFINE_string(out_hash_file, "", "Path to output hash file");
DEFINE_string(private_key, "", "Path to private key in .pem format");
DEFINE_string(public_key, "", "Path to public key in .pem format");
+DEFINE_int32(public_key_version,
+ chromeos_update_engine::kSignatureMessageCurrentVersion,
+ "Key-check version # of client");
DEFINE_string(prefs_dir, "/tmp/update_engine_prefs",
"Preferences directory, used with apply_delta");
-DEFINE_int32(signature_size, 0, "Raw signature size used for hash calculation");
-DEFINE_string(signature_file, "", "Raw signature file to sign payload with");
+DEFINE_string(signature_size, "",
+ "Raw signature size used for hash calculation. "
+ "You may pass in multiple sizes by colon separating them. E.g. "
+ "2048:2048:4096 will assume 3 signatures, the first two with "
+ "2048 size and the last 4096.");
+DEFINE_string(signature_file, "",
+ "Raw signature file to sign payload with. To pass multiple "
+ "signatures, use a single argument with a colon between paths, "
+ "e.g. /path/to/sig:/path/to/next:/path/to/last_sig . Each "
+ "signature will be assigned a client version, starting from "
+ "kSignatureOriginalVersion.");
// This file contains a simple program that takes an old path, a new path,
// and an output file as arguments and the path to an output file and
@@ -70,11 +84,21 @@
<< "Must pass --in_file to calculate hash for signing.";
LOG_IF(FATAL, FLAGS_out_hash_file.empty())
<< "Must pass --out_hash_file to calculate hash for signing.";
- LOG_IF(FATAL, FLAGS_signature_size <= 0)
+ LOG_IF(FATAL, FLAGS_signature_size.empty())
<< "Must pass --signature_size to calculate hash for signing.";
+ vector<int> sizes;
+ vector<string> strsizes;
+ base::SplitString(FLAGS_signature_size, ':', &strsizes);
+ for (vector<string>::iterator it = strsizes.begin(), e = strsizes.end();
+ it != e; ++it) {
+ int size = 0;
+ LOG_IF(FATAL, !base::StringToInt(*it, &size))
+ << "Not an integer: " << *it;
+ sizes.push_back(size);
+ }
vector<char> hash;
CHECK(PayloadSigner::HashPayloadForSigning(
- FLAGS_in_file, FLAGS_signature_size, &hash));
+ FLAGS_in_file, sizes, &hash));
CHECK(utils::WriteFile(
FLAGS_out_hash_file.c_str(), hash.data(), hash.size()));
LOG(INFO) << "Done calculating payload hash for signing.";
@@ -88,10 +112,17 @@
<< "Must pass --out_file to sign payload.";
LOG_IF(FATAL, FLAGS_signature_file.empty())
<< "Must pass --signature_file to sign payload.";
- vector<char> signature;
- CHECK(utils::ReadFile(FLAGS_signature_file, &signature));
+ vector<vector<char> > signatures;
+ vector<string> signature_files;
+ base::SplitString(FLAGS_signature_file, ':', &signature_files);
+ for (vector<string>::iterator it = signature_files.begin(),
+ e = signature_files.end(); it != e; ++it) {
+ vector<char> signature;
+ CHECK(utils::ReadFile(*it, &signature));
+ signatures.push_back(signature);
+ }
CHECK(PayloadSigner::AddSignatureToPayload(
- FLAGS_in_file, signature, FLAGS_out_file));
+ FLAGS_in_file, signatures, FLAGS_out_file));
LOG(INFO) << "Done signing payload.";
}
@@ -101,7 +132,8 @@
<< "Must pass --in_file to verify signed payload.";
LOG_IF(FATAL, FLAGS_public_key.empty())
<< "Must pass --public_key to verify signed payload.";
- CHECK(PayloadSigner::VerifySignedPayload(FLAGS_in_file, FLAGS_public_key));
+ CHECK(PayloadSigner::VerifySignedPayload(FLAGS_in_file, FLAGS_public_key,
+ FLAGS_public_key_version));
LOG(INFO) << "Done verifying signed payload.";
}
@@ -158,7 +190,7 @@
logging::DONT_LOCK_LOG_FILE,
logging::APPEND_TO_OLD_LOG_FILE,
logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
- if (FLAGS_signature_size > 0 || !FLAGS_out_hash_file.empty()) {
+ if (!FLAGS_signature_size.empty() || !FLAGS_out_hash_file.empty()) {
CalculatePayloadHashForSigning();
return 0;
}
diff --git a/payload_signer.cc b/payload_signer.cc
index c27e3bc..d8993d9 100644
--- a/payload_signer.cc
+++ b/payload_signer.cc
@@ -21,7 +21,8 @@
namespace chromeos_update_engine {
-const uint32_t kSignatureMessageVersion = 1;
+const uint32_t kSignatureMessageOriginalVersion = 1;
+const uint32_t kSignatureMessageCurrentVersion = 1;
namespace {
@@ -46,15 +47,26 @@
0x00, 0x04, 0x20
};
-// Given a raw |signature|, packs it into a protobuf and serializes it into a
+// Given raw |signatures|, packs them into a protobuf and serializes it into a
// binary blob. Returns true on success, false otherwise.
-bool ConvertSignatureToProtobufBlob(const vector<char> signature,
+bool ConvertSignatureToProtobufBlob(const vector<vector<char> >& signatures,
vector<char>* out_signature_blob) {
// Pack it into a protobuf
Signatures out_message;
- Signatures_Signature* sig_message = out_message.add_signatures();
- sig_message->set_version(kSignatureMessageVersion);
- sig_message->set_data(signature.data(), signature.size());
+ uint32_t version = kSignatureMessageOriginalVersion;
+ LOG_IF(WARNING, kSignatureMessageCurrentVersion -
+ kSignatureMessageOriginalVersion + 1 < signatures.size())
+ << "You may want to support clients in the rage ["
+ << kSignatureMessageOriginalVersion << ", "
+ << kSignatureMessageCurrentVersion << "] inclusive, but you only "
+ << "provided " << signatures.size() << " signatures.";
+ for (vector<vector<char> >::const_iterator it = signatures.begin(),
+ e = signatures.end(); it != e; ++it) {
+ const vector<char>& signature = *it;
+ Signatures_Signature* sig_message = out_message.add_signatures();
+ sig_message->set_version(version++);
+ sig_message->set_data(signature.data(), signature.size());
+ }
// Serialize protobuf
string serialized;
@@ -165,23 +177,27 @@
}
bool PayloadSigner::SignPayload(const string& unsigned_payload_path,
- const string& private_key_path,
+ const vector<string>& private_key_paths,
vector<char>* out_signature_blob) {
vector<char> hash_data;
TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfFile(
unsigned_payload_path, -1, &hash_data) ==
utils::FileSize(unsigned_payload_path));
- vector<char> signature;
- TEST_AND_RETURN_FALSE(SignHash(hash_data, private_key_path, &signature));
- TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signature,
+ vector<vector<char> > signatures;
+ for (vector<string>::const_iterator it = private_key_paths.begin(),
+ e = private_key_paths.end(); it != e; ++it) {
+ vector<char> signature;
+ TEST_AND_RETURN_FALSE(SignHash(hash_data, *it, &signature));
+ signatures.push_back(signature);
+ }
+ TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures,
out_signature_blob));
return true;
}
-bool PayloadSigner::SignatureBlobLength(
- const string& private_key_path,
- uint64_t* out_length) {
+bool PayloadSigner::SignatureBlobLength(const vector<string>& private_key_paths,
+ uint64_t* out_length) {
DCHECK(out_length);
string x_path;
@@ -192,7 +208,7 @@
vector<char> sig_blob;
TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(x_path,
- private_key_path,
+ private_key_paths,
&sig_blob));
*out_length = sig_blob.size();
return true;
@@ -201,6 +217,15 @@
bool PayloadSigner::VerifySignature(const std::vector<char>& signature_blob,
const std::string& public_key_path,
std::vector<char>* out_hash_data) {
+ return VerifySignatureVersion(signature_blob, public_key_path,
+ kSignatureMessageCurrentVersion, out_hash_data);
+}
+
+bool PayloadSigner::VerifySignatureVersion(
+ const std::vector<char>& signature_blob,
+ const std::string& public_key_path,
+ uint32_t client_version,
+ std::vector<char>* out_hash_data) {
TEST_AND_RETURN_FALSE(!public_key_path.empty());
Signatures signatures;
@@ -212,7 +237,7 @@
for (; sig_index < signatures.signatures_size(); sig_index++) {
const Signatures_Signature& signature = signatures.signatures(sig_index);
if (signature.has_version() &&
- signature.version() == kSignatureMessageVersion) {
+ signature.version() == client_version) {
break;
}
}
@@ -257,7 +282,8 @@
}
bool PayloadSigner::VerifySignedPayload(const std::string& payload_path,
- const std::string& public_key_path) {
+ const std::string& public_key_path,
+ uint32_t client_key_check_version) {
vector<char> payload;
DeltaArchiveManifest manifest;
uint64_t metadata_size;
@@ -272,8 +298,8 @@
payload.begin() + metadata_size + manifest.signatures_offset(),
payload.end());
vector<char> signed_hash;
- TEST_AND_RETURN_FALSE(VerifySignature(
- signature_blob, public_key_path, &signed_hash));
+ TEST_AND_RETURN_FALSE(VerifySignatureVersion(
+ signature_blob, public_key_path, client_key_check_version, &signed_hash));
TEST_AND_RETURN_FALSE(!signed_hash.empty());
vector<char> hash;
TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(
@@ -284,14 +310,19 @@
}
bool PayloadSigner::HashPayloadForSigning(const std::string& payload_path,
- int signature_size,
+ const vector<int>& signature_sizes,
vector<char>* out_hash_data) {
// TODO(petkov): Reduce memory usage -- the payload is manipulated in memory.
// Loads the payload and adds the signature op to it.
- vector<char> signature(signature_size, 0);
+ vector<vector<char> > signatures;
+ for (vector<int>::const_iterator it = signature_sizes.begin(),
+ e = signature_sizes.end(); it != e; ++it) {
+ vector<char> signature(*it, 0);
+ signatures.push_back(signature);
+ }
vector<char> signature_blob;
- TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signature,
+ TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures,
&signature_blob));
vector<char> payload;
TEST_AND_RETURN_FALSE(AddSignatureOpToPayload(payload_path,
@@ -304,14 +335,15 @@
return true;
}
-bool PayloadSigner::AddSignatureToPayload(const string& payload_path,
- const vector<char>& signature,
- const string& signed_payload_path) {
+bool PayloadSigner::AddSignatureToPayload(
+ const string& payload_path,
+ const vector<vector<char> >& signatures,
+ const string& signed_payload_path) {
// TODO(petkov): Reduce memory usage -- the payload is manipulated in memory.
// Loads the payload and adds the signature op to it.
vector<char> signature_blob;
- TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signature,
+ TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures,
&signature_blob));
vector<char> payload;
TEST_AND_RETURN_FALSE(AddSignatureOpToPayload(payload_path,
diff --git a/payload_signer.h b/payload_signer.h
index ee1bee9..1e0db35 100644
--- a/payload_signer.h
+++ b/payload_signer.h
@@ -15,7 +15,8 @@
namespace chromeos_update_engine {
-extern const uint32_t kSignatureMessageVersion;
+extern const uint32_t kSignatureMessageOriginalVersion;
+extern const uint32_t kSignatureMessageCurrentVersion;
class PayloadSigner {
public:
@@ -25,35 +26,37 @@
const std::string& private_key_path,
std::vector<char>* out_signature);
- // Given an unsigned payload in |unsigned_payload_path| and a private key in
+ // Given an unsigned payload in |unsigned_payload_path| and private keys in
// |private_key_path|, calculates the signature blob into
// |out_signature_blob|. Note that the payload must already have an updated
// manifest that includes the dummy signature op. Returns true on success,
// false otherwise.
static bool SignPayload(const std::string& unsigned_payload_path,
- const std::string& private_key_path,
+ const std::vector<std::string>& private_key_paths,
std::vector<char>* out_signature_blob);
// Returns the length of out_signature_blob that will result in a call
- // to SignPayload with a given private key. Returns true on success.
- static bool SignatureBlobLength(const std::string& private_key_path,
- uint64_t* out_length);
+ // to SignPayload with the given private keys. Returns true on success.
+ static bool SignatureBlobLength(
+ const std::vector<std::string>& private_key_paths,
+ uint64_t* out_length);
// Given an unsigned payload in |payload_path| (with no dummy signature op)
- // and the raw |signature_size| calculates the raw hash that needs to be
+ // and the raw |signature_sizes| calculates the raw hash that needs to be
// signed in |out_hash_data|. Returns true on success, false otherwise.
static bool HashPayloadForSigning(const std::string& payload_path,
- int signature_size,
+ const std::vector<int>& signature_sizes,
std::vector<char>* out_hash_data);
// Given an unsigned payload in |payload_path| (with no dummy signature op)
- // and the raw |signature| updates the payload to include the signature thus
+ // and the raw |signatures| updates the payload to include the signature thus
// turning it into a signed payload. The new payload is stored in
// |signed_payload_path|. |payload_path| and |signed_payload_path| can point
// to the same file. Returns true on success, false otherwise.
- static bool AddSignatureToPayload(const std::string& payload_path,
- const std::vector<char>& signature,
- const std::string& signed_payload_path);
+ static bool AddSignatureToPayload(
+ const std::string& payload_path,
+ const std::vector<std::vector<char> >& signatures,
+ const std::string& signed_payload_path);
// Returns false if the payload signature can't be verified. Returns true
// otherwise and sets |out_hash| to the signed payload hash.
@@ -61,11 +64,18 @@
const std::string& public_key_path,
std::vector<char>* out_hash_data);
+ // Same as previous function, but the client version can be set.
+ static bool VerifySignatureVersion(const std::vector<char>& signature_blob,
+ const std::string& public_key_path,
+ uint32_t client_version,
+ std::vector<char>* out_hash_data);
+
// Returns true if the payload in |payload_path| is signed and its hash can be
- // verified using the public key in |public_key_path|. Returns false
- // otherwise.
+ // verified using the public key in |public_key_path| with the signature
+ // of a given version in the signature blob. Returns false otherwise.
static bool VerifySignedPayload(const std::string& payload_path,
- const std::string& public_key_path);
+ const std::string& public_key_path,
+ uint32_t client_key_check_version);
// Pads a SHA256 hash so that it may be encrypted/signed with RSA2048
// using the PKCS#1 v1.5 scheme.
diff --git a/payload_signer_unittest.cc b/payload_signer_unittest.cc
index f7c59a6..0a5a883 100644
--- a/payload_signer_unittest.cc
+++ b/payload_signer_unittest.cc
@@ -15,11 +15,14 @@
// Note: the test key was generated with the following command:
// openssl genrsa -out unittest_key.pem 2048
+// The public-key version is created by the build system.
namespace chromeos_update_engine {
const char* kUnittestPrivateKeyPath = "unittest_key.pem";
const char* kUnittestPublicKeyPath = "unittest_key.pub.pem";
+const char* kUnittestPrivateKey2Path = "unittest_key2.pem";
+const char* kUnittestPublicKey2Path = "unittest_key2.pub.pem";
// Some data and its corresponding hash and signature:
const char kDataToSign[] = "This is some data to sign.";
@@ -86,12 +89,14 @@
kDataToSign,
strlen(kDataToSign)));
uint64_t length = 0;
- EXPECT_TRUE(PayloadSigner::SignatureBlobLength(kUnittestPrivateKeyPath,
- &length));
+ EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
+ vector<string> (1, kUnittestPrivateKeyPath),
+ &length));
EXPECT_GT(length, 0);
- EXPECT_TRUE(PayloadSigner::SignPayload(data_path,
- kUnittestPrivateKeyPath,
- out_signature_blob));
+ EXPECT_TRUE(PayloadSigner::SignPayload(
+ data_path,
+ vector<string>(1, kUnittestPrivateKeyPath),
+ out_signature_blob));
EXPECT_EQ(length, out_signature_blob->size());
}
}
@@ -106,7 +111,7 @@
signature_blob.size()));
EXPECT_EQ(1, signatures.signatures_size());
const Signatures_Signature& signature = signatures.signatures(0);
- EXPECT_EQ(kSignatureMessageVersion, signature.version());
+ EXPECT_EQ(kSignatureMessageOriginalVersion, signature.version());
const string sig_data = signature.data();
ASSERT_EQ(arraysize(kDataSignature), sig_data.size());
for (size_t i = 0; i < arraysize(kDataSignature); i++) {
diff --git a/unittest_key2.pem b/unittest_key2.pem
new file mode 100644
index 0000000..d1f9a78
--- /dev/null
+++ b/unittest_key2.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAqdM7gnd43cWfQzyrt16QDHE+T0yxg18OZO+9sc8jWpjC1zFt
+oMFokESiu9TLUp+KUC274f+DxIgAfSCO6U5DJFRPXUzve1vaXYfqljUBxc+JYcGd
+d6PCUl4Ap9f02DhkrGnf/rkHOCEhFNpm53dovZ9A6Q5KBfcg1HeQ088opVceQGtU
+mL+bONq5tLvNFLUU0Ru0Am4ADJ8Xmw4rpvqvmXJaFmVuf/Gx7+TOnaJvZTeOO9AJ
+K6q6V8E7+ZZiMIc5F4EOs5AX/lZfNO3RVgG2O7Dhzza+nOzJ3ZJGK5R4WwZduhn9
+QRYogYPB0c62878qXpf2Bn3NpUPi8Cf/RLMMmQIDAQABAoIBACyLUWKpP7S770hN
+k6TnUtVQps1aCn2w4y+qipEnCdjrlL+pIV43HNwqhJzL9gDYBAl/1XYz9TYJjkdD
+0Ph1JLtUufR5B5/NufsqeWeow6xFAX34sPr+oyvDqFxeEsTcFdv7cVt44OHiHrE/
+kBpKgdiq+vWmX9gsuBnCuuQzxC+Juo6nupwZXcpa/ow9lC4QsgKqcjaUGrXXy2t9
+Er+9aSl8NdTjK76BXQsDgNkDyJZwNN14xrdS8eFsS4twskaOEYI4hEM0g62NOjgd
+Po8Ap/MnPpGSGcAd3d3Fq8KgT1lpyMKedLFU+k0H+/Y4RBl7grz1XXvSTzGi3Qy6
+38F4eVkCgYEA4mo4iiXSfrov9QffsVM17O0t9hUsOJHnUsEScxWLDm4IzaObyTtv
+tWW33iQSeFu4Wsol0nzjqWo3BaqiRidRUd42yZ07LJvfUDxUX9xPaUPFRs25iwhZ
+6tKAVqGk7/CFrN+R44sIwbsSvbExMAyW6gnj93EWUmMWWYp02hLbN0sCgYEAwAQI
+awVoc56OCtRpfYtlAPD/VOP1mbNzRmVl/UyZ4XYmz6f/hEz63Bk5PhYSZftlmK/r
+nj4qnl7HZ8jrJgZn2e97rPNpk7KDVU1+csCgLWZBTOXl/o9tOTyjh9LoRAjKtBB7
+x6CkWyiyd94xIq5VbnXhvL3a4d4o6OxMWdG5aSsCgYEAo44z1afIzP7WkdzkPIZt
+l/8linR1A1BymBccqsHPN9dIyLP9X3puEc2u6uuH5CXtoLgSZmENXF577L38h0zz
+s34gebf4/RqEUMOj97OAMfxgz+rgs4yO19DEINCYAzPufJjsHEFdTAVFXn5Xl+wg
+QGRwp1ir1Uv64yffjYC9ls0CgYEAjvIxpiKniPNvwUYypmDgt5uyKetvCpaaabzQ
++YpOQJep+wuRYFfCpZotkDf0SHGoR8wnd23GYpIilvPvgyZfp9HuW2n2nhrWROnl
+Cd63IDUwxeOcni7+XA71mwb7HLMC3Jws2geQc8DPZAdIww3P0eT2QYGBcobmI8jO
+akuEYXMCgYAm79Kb/r+3Hew5oAS1Whw70DskVlOutSgNsDPfW9MtDcnETBcGep7A
+1jCL5jjdUYRonimVMFjh1K+UFzV/DQHkgNzjxz9Inbh02y67vL2X836dS9esOcbx
+uZhf+8rL+GnSNqYDqCEuP7qCIloDhguJq9NKyTB4yc59qIkY2zPAzQ==
+-----END RSA PRIVATE KEY-----