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/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);