Support signing payload with 4096 bits RSA keys am: cda3c034bc
am: 5bb6cf142b

Change-Id: Icbef166e2fa4d96784f605e803cf53d52ee18a26
diff --git a/Android.bp b/Android.bp
index dac1acd..8b1730b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -582,14 +582,17 @@
 genrule {
     name: "ue_unittest_keys",
     cmd: "openssl rsa -in $(location unittest_key.pem) -pubout -out $(location unittest_key.pub.pem) &&" +
-        "openssl rsa -in $(location unittest_key2.pem) -pubout -out $(location unittest_key2.pub.pem)",
+        "openssl rsa -in $(location unittest_key2.pem) -pubout -out $(location unittest_key2.pub.pem) &&" +
+        "openssl rsa -in $(location unittest_key_RSA4096.pem) -pubout -out $(location unittest_key_RSA4096.pub.pem)",
     srcs: [
         "unittest_key.pem",
         "unittest_key2.pem",
+        "unittest_key_RSA4096.pem",
     ],
     out: [
         "unittest_key.pub.pem",
         "unittest_key2.pub.pem",
+        "unittest_key_RSA4096.pub.pem",
     ],
 }
 
@@ -641,6 +644,7 @@
         ":ue_unittest_keys",
         "unittest_key.pem",
         "unittest_key2.pem",
+        "unittest_key_RSA4096.pem",
         "update_engine.conf",
     ],
 
diff --git a/common/constants.h b/common/constants.h
index d5a8ae3..af1c0ab 100644
--- a/common/constants.h
+++ b/common/constants.h
@@ -197,6 +197,9 @@
 const int kDownloadConnectTimeoutSeconds = 30;
 const int kDownloadP2PConnectTimeoutSeconds = 5;
 
+// Size in bytes of SHA256 hash.
+const int kSHA256Size = 32;
+
 }  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_COMMON_CONSTANTS_H_
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index ae73d03..d76a959 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -1816,9 +1816,7 @@
                       !signatures_message_data_.empty());
   brillo::Blob hash_data = signed_hash_calculator_.raw_hash();
   TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
-                      PayloadVerifier::PadRSA2048SHA256Hash(&hash_data));
-  TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
-                      !hash_data.empty());
+                      hash_data.size() == kSHA256Size);
 
   if (!PayloadVerifier::VerifySignature(
           signatures_message_data_, public_key, hash_data)) {
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc
index 8b3eb4e..3739767 100644
--- a/payload_consumer/payload_metadata.cc
+++ b/payload_consumer/payload_metadata.cc
@@ -20,6 +20,7 @@
 
 #include <brillo/data_encoding.h>
 
+#include "update_engine/common/constants.h"
 #include "update_engine/common/hash_calculator.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/payload_consumer/payload_constants.h"
@@ -187,16 +188,16 @@
     return ErrorCode::kDownloadMetadataSignatureMissingError;
   }
 
-  brillo::Blob calculated_metadata_hash;
+  brillo::Blob metadata_hash;
   if (!HashCalculator::RawHashOfBytes(
-          payload.data(), metadata_size_, &calculated_metadata_hash)) {
+          payload.data(), metadata_size_, &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.";
+  if (metadata_hash.size() != kSHA256Size) {
+    LOG(ERROR) << "Computed actual hash of metadata has incorrect size: "
+               << metadata_hash.size();
     return ErrorCode::kDownloadMetadataSignatureVerificationError;
   }
 
@@ -207,17 +208,25 @@
       LOG(ERROR) << "Unable to compute expected hash from metadata signature";
       return ErrorCode::kDownloadMetadataSignatureError;
     }
-    if (calculated_metadata_hash != expected_metadata_hash) {
+
+    brillo::Blob padded_metadata_hash = metadata_hash;
+    if (!PayloadVerifier::PadRSASHA256Hash(&padded_metadata_hash,
+                                           expected_metadata_hash.size())) {
+      LOG(ERROR) << "Failed to pad the SHA256 hash to "
+                 << expected_metadata_hash.size() << " bytes.";
+      return ErrorCode::kDownloadMetadataSignatureVerificationError;
+    }
+
+    if (padded_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);
+      utils::HexDumpVector(padded_metadata_hash);
       return ErrorCode::kDownloadMetadataSignatureMismatch;
     }
   } else {
-    if (!PayloadVerifier::VerifySignature(metadata_signature_protobuf,
-                                          pem_public_key,
-                                          calculated_metadata_hash)) {
+    if (!PayloadVerifier::VerifySignature(
+            metadata_signature_protobuf, pem_public_key, metadata_hash)) {
       LOG(ERROR) << "Manifest hash verification failed.";
       return ErrorCode::kDownloadMetadataSignatureMismatch;
     }
diff --git a/payload_consumer/payload_verifier.cc b/payload_consumer/payload_verifier.cc
index 3eb1da8..3a3ccbf 100644
--- a/payload_consumer/payload_verifier.cc
+++ b/payload_consumer/payload_verifier.cc
@@ -16,11 +16,13 @@
 
 #include "update_engine/payload_consumer/payload_verifier.h"
 
+#include <utility>
 #include <vector>
 
 #include <base/logging.h>
 #include <openssl/pem.h>
 
+#include "update_engine/common/constants.h"
 #include "update_engine/common/hash_calculator.h"
 #include "update_engine/common/utils.h"
 #include "update_engine/update_metadata.pb.h"
@@ -31,57 +33,27 @@
 
 namespace {
 
-// The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
-// defined in RFC3447. It is prepended to the actual signature (32 bytes) to
-// form a sequence of 256 bytes (2048 bits) that is amenable to RSA signing. The
-// padded hash will look as follows:
+// The ASN.1 DigestInfo prefix for encoding SHA256 digest. The complete 51-byte
+// DigestInfo consists of 19-byte SHA256_DIGEST_INFO_PREFIX and 32-byte SHA256
+// digest.
 //
-//    0x00 0x01 0xff ... 0xff 0x00  ASN1HEADER  SHA256HASH
-//   |--------------205-----------||----19----||----32----|
-//
-// where ASN1HEADER is the ASN.1 description of the signed data. The complete 51
-// bytes of actual data (i.e. the ASN.1 header complete with the hash) are
-// packed as follows:
-//
-//  SEQUENCE(2+49) {
+// SEQUENCE(2+49) {
 //   SEQUENCE(2+13) {
-//    OBJECT(2+9) id-sha256
-//    NULL(2+0)
+//     OBJECT(2+9) id-sha256
+//     NULL(2+0)
 //   }
 //   OCTET STRING(2+32) <actual signature bytes...>
-//  }
-// clang-format off
-const uint8_t kRSA2048SHA256Padding[] = {
-    // PKCS1-v1_5 padding
-    0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0x00,
-    // ASN.1 header
-    0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
-    0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
+// }
+const uint8_t kSHA256DigestInfoPrefix[] = {
+    0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+    0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20,
 };
-// clang-format on
 
 }  // namespace
 
 bool PayloadVerifier::VerifySignature(const string& signature_proto,
                                       const string& pem_public_key,
-                                      const brillo::Blob& hash_data) {
+                                      const brillo::Blob& sha256_hash_data) {
   Signatures signatures;
   LOG(INFO) << "signature blob size = " << signature_proto.size();
   TEST_AND_RETURN_FALSE(signatures.ParseFromString(signature_proto));
@@ -100,7 +72,9 @@
     if (!GetRawHashFromSignature(sig_data, pem_public_key, &sig_hash_data))
       continue;
 
-    if (hash_data == sig_hash_data) {
+    brillo::Blob padded_hash_data = sha256_hash_data;
+    if (PadRSASHA256Hash(&padded_hash_data, sig_hash_data.size()) &&
+        padded_hash_data == sig_hash_data) {
       LOG(INFO) << "Verified correct signature " << i + 1 << " out of "
                 << signatures.signatures_size() << " signatures.";
       return true;
@@ -108,8 +82,8 @@
     tested_hashes.push_back(sig_hash_data);
   }
   LOG(ERROR) << "None of the " << signatures.signatures_size()
-             << " signatures is correct. Expected:";
-  utils::HexDumpVector(hash_data);
+             << " signatures is correct. Expected hash before padding:";
+  utils::HexDumpVector(sha256_hash_data);
   LOG(ERROR) << "But found decrypted hashes:";
   for (const auto& sig_hash_data : tested_hashes) {
     utils::HexDumpVector(sig_hash_data);
@@ -150,13 +124,30 @@
   return true;
 }
 
-bool PayloadVerifier::PadRSA2048SHA256Hash(brillo::Blob* hash) {
-  TEST_AND_RETURN_FALSE(hash->size() == 32);
-  hash->insert(hash->begin(),
-               reinterpret_cast<const char*>(kRSA2048SHA256Padding),
-               reinterpret_cast<const char*>(kRSA2048SHA256Padding +
-                                             sizeof(kRSA2048SHA256Padding)));
-  TEST_AND_RETURN_FALSE(hash->size() == 256);
+bool PayloadVerifier::PadRSASHA256Hash(brillo::Blob* hash, size_t rsa_size) {
+  TEST_AND_RETURN_FALSE(hash->size() == kSHA256Size);
+  TEST_AND_RETURN_FALSE(rsa_size == 256 || rsa_size == 512);
+
+  // The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
+  // defined in RFC3447 section 9.2. It is prepended to the actual signature
+  // (32 bytes) to form a sequence of 256|512 bytes (2048|4096 bits) that is
+  // amenable to RSA signing. The padded hash will look as follows:
+  //
+  //    0x00 0x01 0xff ... 0xff 0x00  ASN1HEADER  SHA256HASH
+  //   |-----------205|461----------||----19----||----32----|
+  size_t padding_string_size =
+      rsa_size - hash->size() - sizeof(kSHA256DigestInfoPrefix) - 3;
+  brillo::Blob padded_result = brillo::CombineBlobs({
+      {0x00, 0x01},
+      brillo::Blob(padding_string_size, 0xff),
+      {0x00},
+      brillo::Blob(kSHA256DigestInfoPrefix,
+                   kSHA256DigestInfoPrefix + sizeof(kSHA256DigestInfoPrefix)),
+      *hash,
+  });
+
+  *hash = std::move(padded_result);
+  TEST_AND_RETURN_FALSE(hash->size() == rsa_size);
   return true;
 }
 
diff --git a/payload_consumer/payload_verifier.h b/payload_consumer/payload_verifier.h
index 09bdbf9..af8e05f 100644
--- a/payload_consumer/payload_verifier.h
+++ b/payload_consumer/payload_verifier.h
@@ -34,12 +34,13 @@
   // Interprets |signature_proto| as a protocol buffer containing the Signatures
   // message and decrypts each signature data using the |pem_public_key|.
   // |pem_public_key| should be a PEM format RSA public key data.
-  // Returns whether *any* of the decrypted hashes matches the |hash_data|.
-  // In case of any error parsing the signatures or the public key, returns
-  // false.
+  // Pads the 32 bytes |sha256_hash_data| to 256 or 512 bytes according to the
+  // PKCS#1 v1.5 standard; and returns whether *any* of the decrypted hashes
+  // matches the padded hash data. In case of any error parsing the signatures
+  // or the public key, returns false.
   static bool VerifySignature(const std::string& signature_proto,
                               const std::string& pem_public_key,
-                              const brillo::Blob& hash_data);
+                              const brillo::Blob& sha256_hash_data);
 
   // Decrypts |sig_data| with the given |pem_public_key| and populates
   // |out_hash_data| with the decoded raw hash. |pem_public_key| should be a PEM
@@ -48,12 +49,13 @@
                                       const std::string& pem_public_key,
                                       brillo::Blob* out_hash_data);
 
-  // Pads a SHA256 hash so that it may be encrypted/signed with RSA2048
-  // using the PKCS#1 v1.5 scheme.
-  // hash should be a pointer to vector of exactly 256 bits. The vector
-  // will be modified in place and will result in having a length of
-  // 2048 bits. Returns true on success, false otherwise.
-  static bool PadRSA2048SHA256Hash(brillo::Blob* hash);
+  // Pads a SHA256 hash so that it may be encrypted/signed with RSA2048 or
+  // RSA4096 using the PKCS#1 v1.5 scheme.
+  // hash should be a pointer to vector of exactly 256 bits. |rsa_size| must be
+  // one of 256 or 512 bytes. The vector will be modified in place and will
+  // result in having a length of 2048 or 4096 bits, depending on the rsa size.
+  // Returns true on success, false otherwise.
+  static bool PadRSASHA256Hash(brillo::Blob* hash, size_t rsa_size);
 
  private:
   // This should never be constructed
diff --git a/payload_generator/payload_signer.cc b/payload_generator/payload_signer.cc
index cbca7fe..92313dc 100644
--- a/payload_generator/payload_signer.cc
+++ b/payload_generator/payload_signer.cc
@@ -28,6 +28,7 @@
 #include <openssl/err.h>
 #include <openssl/pem.h>
 
+#include "update_engine/common/constants.h"
 #include "update_engine/common/hash_calculator.h"
 #include "update_engine/common/subprocess.h"
 #include "update_engine/common/utils.h"
@@ -253,14 +254,13 @@
   string signature(payload.begin() + signatures_offset, payload.end());
   string public_key;
   TEST_AND_RETURN_FALSE(utils::ReadFile(public_key_path, &public_key));
-  TEST_AND_RETURN_FALSE(PayloadVerifier::PadRSA2048SHA256Hash(&payload_hash));
+  TEST_AND_RETURN_FALSE(payload_hash.size() == kSHA256Size);
   TEST_AND_RETURN_FALSE(
       PayloadVerifier::VerifySignature(signature, public_key, payload_hash));
   if (metadata_signature_size) {
     signature.assign(payload.begin() + metadata_size,
                      payload.begin() + metadata_size + metadata_signature_size);
-    TEST_AND_RETURN_FALSE(
-        PayloadVerifier::PadRSA2048SHA256Hash(&metadata_hash));
+    TEST_AND_RETURN_FALSE(metadata_hash.size() == kSHA256Size);
     TEST_AND_RETURN_FALSE(
         PayloadVerifier::VerifySignature(signature, public_key, metadata_hash));
   }
@@ -272,10 +272,7 @@
                              brillo::Blob* out_signature) {
   LOG(INFO) << "Signing hash with private key: " << private_key_path;
   // We expect unpadded SHA256 hash coming in
-  TEST_AND_RETURN_FALSE(hash.size() == 32);
-  brillo::Blob padded_hash(hash);
-  PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash);
-
+  TEST_AND_RETURN_FALSE(hash.size() == kSHA256Size);
   // The code below executes the equivalent of:
   //
   // openssl rsautl -raw -sign -inkey |private_key_path|
@@ -286,6 +283,10 @@
   RSA* rsa = PEM_read_RSAPrivateKey(fprikey, nullptr, nullptr, nullptr);
   fclose(fprikey);
   TEST_AND_RETURN_FALSE(rsa != nullptr);
+
+  brillo::Blob padded_hash = hash;
+  PayloadVerifier::PadRSASHA256Hash(&padded_hash, RSA_size(rsa));
+
   brillo::Blob signature(RSA_size(rsa));
   ssize_t signature_size = RSA_private_encrypt(padded_hash.size(),
                                                padded_hash.data(),
diff --git a/payload_generator/payload_signer_unittest.cc b/payload_generator/payload_signer_unittest.cc
index 0b863b1..75fc694 100644
--- a/payload_generator/payload_signer_unittest.cc
+++ b/payload_generator/payload_signer_unittest.cc
@@ -44,6 +44,8 @@
 const char* kUnittestPublicKeyPath = "unittest_key.pub.pem";
 const char* kUnittestPrivateKey2Path = "unittest_key2.pem";
 const char* kUnittestPublicKey2Path = "unittest_key2.pub.pem";
+const char* kUnittestPrivateKeyRSA4096Path = "unittest_key_RSA4096.pem";
+const char* kUnittestPublicKeyRSA4096Path = "unittest_key_RSA4096.pub.pem";
 
 // Some data and its corresponding hash and signature:
 const char kDataToSign[] = "This is some data to sign.";
@@ -101,11 +103,7 @@
 
 class PayloadSignerTest : public ::testing::Test {
  protected:
-  void SetUp() override {
-    PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash_data_);
-  }
-
-  brillo::Blob padded_hash_data_{std::begin(kDataHash), std::end(kDataHash)};
+  brillo::Blob hash_data_{std::begin(kDataHash), std::end(kDataHash)};
 };
 
 TEST_F(PayloadSignerTest, SignSimpleTextTest) {
@@ -129,18 +127,23 @@
   string signature;
   SignSampleData(&signature,
                  {GetBuildArtifactsPath(kUnittestPrivateKeyPath),
-                  GetBuildArtifactsPath(kUnittestPrivateKey2Path)});
+                  GetBuildArtifactsPath(kUnittestPrivateKey2Path),
+                  GetBuildArtifactsPath(kUnittestPrivateKeyRSA4096Path)});
 
   // Either public key should pass the verification.
   string public_key;
   EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKeyPath),
                               &public_key));
-  EXPECT_TRUE(PayloadVerifier::VerifySignature(
-      signature, public_key, padded_hash_data_));
+  EXPECT_TRUE(
+      PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
   EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKey2Path),
                               &public_key));
-  EXPECT_TRUE(PayloadVerifier::VerifySignature(
-      signature, public_key, padded_hash_data_));
+  EXPECT_TRUE(
+      PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
+  EXPECT_TRUE(utils::ReadFile(
+      GetBuildArtifactsPath(kUnittestPublicKeyRSA4096Path), &public_key));
+  EXPECT_TRUE(
+      PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
 }
 
 TEST_F(PayloadSignerTest, VerifySignatureTest) {
@@ -150,13 +153,13 @@
   string public_key;
   EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKeyPath),
                               &public_key));
-  EXPECT_TRUE(PayloadVerifier::VerifySignature(
-      signature, public_key, padded_hash_data_));
+  EXPECT_TRUE(
+      PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
   // Passing the invalid key should fail the verification.
   EXPECT_TRUE(utils::ReadFile(GetBuildArtifactsPath(kUnittestPublicKey2Path),
                               &public_key));
-  EXPECT_TRUE(PayloadVerifier::VerifySignature(
-      signature, public_key, padded_hash_data_));
+  EXPECT_TRUE(
+      PayloadVerifier::VerifySignature(signature, public_key, hash_data_));
 }
 
 TEST_F(PayloadSignerTest, SkipMetadataSignatureTest) {
diff --git a/unittest_key_RSA4096.pem b/unittest_key_RSA4096.pem
new file mode 100644
index 0000000..5613910
--- /dev/null
+++ b/unittest_key_RSA4096.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/update_engine.gyp b/update_engine.gyp
index 754b314..c2c0c62 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -491,6 +491,7 @@
           'sources': [
             'unittest_key.pem',
             'unittest_key2.pem',
+            'unittest_key_RSA4096.pem',
           ],
           'includes': ['../../../platform2/common-mk/openssl_pem.gypi'],
         },