Allow update engine read public keys from otacerts.zip am: 7a78d630e1 am: de863bc026
am: 5527f8ea8d

Change-Id: I230b4e3184d05d1d95088d0de5b0a462f06af973
diff --git a/Android.bp b/Android.bp
index e5e592c..a691e7e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -128,6 +128,7 @@
         "libverity_tree",
     ],
     shared_libs: [
+        "libziparchive",
         "libbase",
         "libcrypto",
         "libfec",
@@ -164,6 +165,7 @@
         "common/utils.cc",
         "payload_consumer/bzip_extent_writer.cc",
         "payload_consumer/cached_file_descriptor.cc",
+        "payload_consumer/certificate_parser_android.cc",
         "payload_consumer/delta_performer.cc",
         "payload_consumer/download_action.cc",
         "payload_consumer/extent_reader.cc",
@@ -659,6 +661,7 @@
         ":ue_unittest_delta_generator",
         ":ue_unittest_disk_imgs",
         ":ue_unittest_keys",
+        "otacerts.zip",
         "unittest_key.pem",
         "unittest_key2.pem",
         "unittest_key_RSA4096.pem",
@@ -693,6 +696,7 @@
         "dynamic_partition_control_android_unittest.cc",
         "payload_consumer/bzip_extent_writer_unittest.cc",
         "payload_consumer/cached_file_descriptor_unittest.cc",
+        "payload_consumer/certificate_parser_android_unittest.cc",
         "payload_consumer/delta_performer_integration_test.cc",
         "payload_consumer/delta_performer_unittest.cc",
         "payload_consumer/extent_reader_unittest.cc",
diff --git a/common/platform_constants.h b/common/platform_constants.h
index 6eaa940..243af69 100644
--- a/common/platform_constants.h
+++ b/common/platform_constants.h
@@ -38,6 +38,10 @@
 // whole payload.
 extern const char kUpdatePayloadPublicKeyPath[];
 
+// Path to the location of the zip archive file that contains PEM encoded X509
+// certificates. e.g. 'system/etc/security/otacerts.zip'.
+extern const char kUpdateCertificatesPath[];
+
 // Path to the directory containing all the SSL certificates accepted by
 // update_engine when sending requests to Omaha and the download server (if
 // HTTPS is used for that as well).
diff --git a/common/platform_constants_android.cc b/common/platform_constants_android.cc
index 9d8d30e..f468c3b 100644
--- a/common/platform_constants_android.cc
+++ b/common/platform_constants_android.cc
@@ -25,8 +25,8 @@
     "https://clients2.google.com/service/update2/brillo";
 const char kOmahaUpdaterID[] = "Brillo";
 const char kOmahaPlatformName[] = "Brillo";
-const char kUpdatePayloadPublicKeyPath[] =
-    "/etc/update_engine/update-payload-key.pub.pem";
+const char kUpdatePayloadPublicKeyPath[] = "";
+const char kUpdateCertificatesPath[] = "/system/etc/security/otacerts.zip";
 const char kCACertificatesPath[] = "/system/etc/security/cacerts_google";
 // No deadline file API support on Android.
 const char kOmahaResponseDeadlineFile[] = "";
diff --git a/common/platform_constants_chromeos.cc b/common/platform_constants_chromeos.cc
index f1ac490..fe94a45 100644
--- a/common/platform_constants_chromeos.cc
+++ b/common/platform_constants_chromeos.cc
@@ -27,6 +27,7 @@
 const char kOmahaPlatformName[] = "Chrome OS";
 const char kUpdatePayloadPublicKeyPath[] =
     "/usr/share/update_engine/update-payload-key.pub.pem";
+const char kUpdateCertificatesPath[] = "";
 const char kCACertificatesPath[] = "/usr/share/chromeos-ca-certificates";
 const char kOmahaResponseDeadlineFile[] = "/tmp/update-check-response-deadline";
 // This directory is wiped during powerwash.
diff --git a/otacerts.zip b/otacerts.zip
new file mode 100644
index 0000000..00a5a51
--- /dev/null
+++ b/otacerts.zip
Binary files differ
diff --git a/payload_consumer/certificate_parser_android.cc b/payload_consumer/certificate_parser_android.cc
new file mode 100644
index 0000000..4a20547
--- /dev/null
+++ b/payload_consumer/certificate_parser_android.cc
@@ -0,0 +1,121 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_consumer/certificate_parser_android.h"
+
+#include <memory>
+#include <utility>
+
+#include <base/logging.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <ziparchive/zip_archive.h>
+
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
+
+namespace {
+bool IterateZipEntriesAndSearchForKeys(
+    const ZipArchiveHandle& handle, std::vector<std::vector<uint8_t>>* result) {
+  void* cookie;
+  int32_t iter_status = StartIteration(handle, &cookie, "", "x509.pem");
+  if (iter_status != 0) {
+    LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: "
+               << ErrorCodeString(iter_status);
+    return false;
+  }
+  std::unique_ptr<void, decltype(&EndIteration)> guard(cookie, EndIteration);
+
+  std::vector<std::vector<uint8_t>> pem_keys;
+  std::string_view name;
+  ZipEntry entry;
+  while ((iter_status = Next(cookie, &entry, &name)) == 0) {
+    std::vector<uint8_t> pem_content(entry.uncompressed_length);
+    if (int32_t extract_status = ExtractToMemory(
+            handle, &entry, pem_content.data(), pem_content.size());
+        extract_status != 0) {
+      LOG(ERROR) << "Failed to extract " << name << ": "
+                 << ErrorCodeString(extract_status);
+      return false;
+    }
+    pem_keys.push_back(pem_content);
+  }
+
+  if (iter_status != -1) {
+    LOG(ERROR) << "Error while iterating over zip entries: "
+               << ErrorCodeString(iter_status);
+    return false;
+  }
+
+  *result = std::move(pem_keys);
+  return true;
+}
+
+}  // namespace
+
+namespace chromeos_update_engine {
+bool CertificateParserAndroid::ReadPublicKeysFromCertificates(
+    const std::string& path,
+    std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+        out_public_keys) {
+  out_public_keys->clear();
+
+  ZipArchiveHandle handle;
+  if (int32_t open_status = OpenArchive(path.c_str(), &handle);
+      open_status != 0) {
+    LOG(ERROR) << "Failed to open " << path << ": "
+               << ErrorCodeString(open_status);
+    return false;
+  }
+
+  std::vector<std::vector<uint8_t>> pem_certs;
+  if (!IterateZipEntriesAndSearchForKeys(handle, &pem_certs)) {
+    CloseArchive(handle);
+    return false;
+  }
+  CloseArchive(handle);
+
+  // Convert the certificates into public keys. Stop and return false if we
+  // encounter an error.
+  std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> result;
+  for (const auto& cert : pem_certs) {
+    std::unique_ptr<BIO, decltype(&BIO_free)> input(
+        BIO_new_mem_buf(cert.data(), cert.size()), BIO_free);
+
+    std::unique_ptr<X509, decltype(&X509_free)> x509(
+        PEM_read_bio_X509(input.get(), nullptr, nullptr, nullptr), X509_free);
+    if (!x509) {
+      LOG(ERROR) << "Failed to read x509 certificate";
+      return false;
+    }
+
+    std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> public_key(
+        X509_get_pubkey(x509.get()), EVP_PKEY_free);
+    if (!public_key) {
+      LOG(ERROR) << "Failed to extract the public key from x509 certificate";
+      return false;
+    }
+    result.push_back(std::move(public_key));
+  }
+
+  *out_public_keys = std::move(result);
+  return true;
+}
+
+std::unique_ptr<CertificateParserInterface> CreateCertificateParser() {
+  return std::make_unique<CertificateParserAndroid>();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/certificate_parser_android.h b/payload_consumer/certificate_parser_android.h
new file mode 100644
index 0000000..ccb9293
--- /dev/null
+++ b/payload_consumer/certificate_parser_android.h
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_CERTIFICATE_PARSER_ANDROID_H_
+#define UPDATE_ENGINE_CERTIFICATE_PARSER_ANDROID_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "payload_consumer/certificate_parser_interface.h"
+
+namespace chromeos_update_engine {
+// This class parses the certificates from a zip file. Because the Android
+// build system stores the certs in otacerts.zip.
+class CertificateParserAndroid : public CertificateParserInterface {
+ public:
+  CertificateParserAndroid() = default;
+
+  bool ReadPublicKeysFromCertificates(
+      const std::string& path,
+      std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+          out_public_keys) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CertificateParserAndroid);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/certificate_parser_android_unittest.cc b/payload_consumer/certificate_parser_android_unittest.cc
new file mode 100644
index 0000000..e300414
--- /dev/null
+++ b/payload_consumer/certificate_parser_android_unittest.cc
@@ -0,0 +1,61 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
+
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/common/hash_calculator.h"
+#include "update_engine/common/test_utils.h"
+#include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
+#include "update_engine/payload_generator/payload_signer.h"
+
+namespace chromeos_update_engine {
+
+extern const char* kUnittestPrivateKeyPath;
+const char* kUnittestOtacertsPath = "otacerts.zip";
+
+TEST(CertificateParserAndroidTest, ParseZipArchive) {
+  std::string ota_cert =
+      test_utils::GetBuildArtifactsPath(kUnittestOtacertsPath);
+  ASSERT_TRUE(utils::FileExists(ota_cert.c_str()));
+
+  std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> keys;
+  auto parser = CreateCertificateParser();
+  ASSERT_TRUE(parser->ReadPublicKeysFromCertificates(ota_cert, &keys));
+  ASSERT_EQ(1u, keys.size());
+}
+
+TEST(CertificateParserAndroidTest, VerifySignature) {
+  brillo::Blob hash_blob;
+  ASSERT_TRUE(HashCalculator::RawHashOfData({'x'}, &hash_blob));
+  brillo::Blob sig_blob;
+  ASSERT_TRUE(PayloadSigner::SignHash(
+      hash_blob,
+      test_utils::GetBuildArtifactsPath(kUnittestPrivateKeyPath),
+      &sig_blob));
+
+  auto verifier = PayloadVerifier::CreateInstanceFromZipPath(
+      test_utils::GetBuildArtifactsPath(kUnittestOtacertsPath));
+  ASSERT_TRUE(verifier != nullptr);
+  ASSERT_TRUE(verifier->VerifyRawSignature(sig_blob, hash_blob, nullptr));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/certificate_parser_interface.h b/payload_consumer/certificate_parser_interface.h
new file mode 100644
index 0000000..dad23d2
--- /dev/null
+++ b/payload_consumer/certificate_parser_interface.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_CERTIFICATE_PARSER_INTERFACE_H_
+#define UPDATE_ENGINE_CERTIFICATE_PARSER_INTERFACE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <openssl/pem.h>
+
+namespace chromeos_update_engine {
+
+// This class parses the PEM encoded X509 certificates from |path|; and
+// passes the parsed public keys to the caller.
+class CertificateParserInterface {
+ public:
+  virtual ~CertificateParserInterface() = default;
+
+  virtual bool ReadPublicKeysFromCertificates(
+      const std::string& path,
+      std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+          out_public_keys) = 0;
+};
+
+std::unique_ptr<CertificateParserInterface> CreateCertificateParser();
+
+}  // namespace chromeos_update_engine
+
+#endif
diff --git a/payload_consumer/certificate_parser_stub.cc b/payload_consumer/certificate_parser_stub.cc
new file mode 100644
index 0000000..95fd6e8
--- /dev/null
+++ b/payload_consumer/certificate_parser_stub.cc
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <payload_consumer/certificate_parser_stub.h>
+
+namespace chromeos_update_engine {
+bool CertificateParserStub::ReadPublicKeysFromCertificates(
+    const std::string& path,
+    std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+        out_public_keys) {
+  return true;
+}
+
+std::unique_ptr<CertificateParserInterface> CreateCertificateParser() {
+  return std::make_unique<CertificateParserStub>();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_consumer/certificate_parser_stub.h b/payload_consumer/certificate_parser_stub.h
new file mode 100644
index 0000000..f4f8825
--- /dev/null
+++ b/payload_consumer/certificate_parser_stub.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef UPDATE_ENGINE_CERTIFICATE_PARSER_STUB_H_
+#define UPDATE_ENGINE_CERTIFICATE_PARSER_STUB_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "payload_consumer/certificate_parser_interface.h"
+
+namespace chromeos_update_engine {
+class CertificateParserStub : public CertificateParserInterface {
+ public:
+  CertificateParserStub() = default;
+
+  bool ReadPublicKeysFromCertificates(
+      const std::string& path,
+      std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>*
+          out_public_keys) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CertificateParserStub);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_CERTIFICATE_PARSER_STUB_H_
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 8049af7..4b80ae6 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -46,6 +46,7 @@
 #include "update_engine/common/terminator.h"
 #include "update_engine/payload_consumer/bzip_extent_writer.h"
 #include "update_engine/payload_consumer/cached_file_descriptor.h"
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
 #include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/extent_reader.h"
 #include "update_engine/payload_consumer/extent_writer.h"
@@ -526,9 +527,10 @@
                  << "Trusting metadata size in payload = " << metadata_size_;
   }
 
-  string public_key;
-  if (!GetPublicKey(&public_key)) {
-    LOG(ERROR) << "Failed to get public key.";
+  // Perform the verification unconditionally.
+  auto [payload_verifier, perform_verification] = CreatePayloadVerifier();
+  if (!payload_verifier) {
+    LOG(ERROR) << "Failed to create payload verifier.";
     *error = ErrorCode::kDownloadMetadataSignatureVerificationError;
     return MetadataParseResult::kError;
   }
@@ -536,7 +538,7 @@
   // We have the full metadata in |payload|. Verify its integrity
   // and authenticity based on the information we have in Omaha response.
   *error = payload_metadata_.ValidateMetadataSignature(
-      payload, payload_->metadata_signature, public_key);
+      payload, payload_->metadata_signature, *payload_verifier);
   if (*error != ErrorCode::kSuccess) {
     if (install_plan_->hash_checks_mandatory) {
       // The autoupdate_CatchBadSignatures test checks for this string
@@ -1596,10 +1598,32 @@
     return brillo::data_encoding::Base64Decode(install_plan_->public_key_rsa,
                                                out_public_key);
   }
-
+  LOG(INFO) << "No public keys found for verification.";
   return true;
 }
 
+std::pair<std::unique_ptr<PayloadVerifier>, bool>
+DeltaPerformer::CreatePayloadVerifier() {
+  if (utils::FileExists(update_certificates_path_.c_str())) {
+    LOG(INFO) << "Verifying using certificates: " << update_certificates_path_;
+    return {
+        PayloadVerifier::CreateInstanceFromZipPath(update_certificates_path_),
+        true};
+  }
+
+  string public_key;
+  if (!GetPublicKey(&public_key)) {
+    LOG(ERROR) << "Failed to read public key";
+    return {nullptr, true};
+  }
+
+  // Skips the verification if the public key is empty.
+  if (public_key.empty()) {
+    return {nullptr, false};
+  }
+  return {PayloadVerifier::CreateInstance(public_key), true};
+}
+
 ErrorCode DeltaPerformer::ValidateManifest() {
   // Perform assorted checks to sanity check the manifest, make sure it
   // matches data from other sources, and that it is a supported version.
@@ -1760,12 +1784,6 @@
 ErrorCode DeltaPerformer::VerifyPayload(
     const brillo::Blob& update_check_response_hash,
     const uint64_t update_check_response_size) {
-  string public_key;
-  if (!GetPublicKey(&public_key)) {
-    LOG(ERROR) << "Failed to get public key.";
-    return ErrorCode::kDownloadPayloadPubKeyVerificationError;
-  }
-
   // Verifies the download size.
   if (update_check_response_size !=
       metadata_size_ + metadata_signature_size_ + buffer_offset_) {
@@ -1783,20 +1801,19 @@
       ErrorCode::kPayloadHashMismatchError,
       payload_hash_calculator_.raw_hash() == update_check_response_hash);
 
-  // Verifies the signed payload hash.
-  if (public_key.empty()) {
-    LOG(WARNING) << "Not verifying signed delta payload -- missing public key.";
-    return ErrorCode::kSuccess;
-  }
   TEST_AND_RETURN_VAL(ErrorCode::kSignedDeltaPayloadExpectedError,
                       !signatures_message_data_.empty());
   brillo::Blob hash_data = signed_hash_calculator_.raw_hash();
   TEST_AND_RETURN_VAL(ErrorCode::kDownloadPayloadPubKeyVerificationError,
                       hash_data.size() == kSHA256Size);
 
-  auto payload_verifier = PayloadVerifier::CreateInstance(public_key);
+  auto [payload_verifier, perform_verification] = CreatePayloadVerifier();
+  if (!perform_verification) {
+    LOG(WARNING) << "Not verifying signed delta payload -- missing public key.";
+    return ErrorCode::kSuccess;
+  }
   if (!payload_verifier) {
-    LOG(ERROR) << "Failed to create the payload verifier from " << public_key;
+    LOG(ERROR) << "Failed to create the payload verifier.";
     return ErrorCode::kDownloadPayloadPubKeyVerificationError;
   }
   if (!payload_verifier->VerifySignature(signatures_message_data_, hash_data)) {
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 25c348c..4c64dfa 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -20,7 +20,9 @@
 #include <inttypes.h>
 
 #include <limits>
+#include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <base/time/time.h>
@@ -34,6 +36,7 @@
 #include "update_engine/payload_consumer/file_writer.h"
 #include "update_engine/payload_consumer/install_plan.h"
 #include "update_engine/payload_consumer/payload_metadata.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
 #include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
@@ -156,6 +159,11 @@
     public_key_path_ = public_key_path;
   }
 
+  void set_update_certificates_path(
+      const std::string& update_certificates_path) {
+    update_certificates_path_ = update_certificates_path;
+  }
+
   // Return true if header parsing is finished and no errors occurred.
   bool IsHeaderParsed() const;
 
@@ -273,6 +281,12 @@
   // |out_public_key|. Returns false on failures.
   bool GetPublicKey(std::string* out_public_key);
 
+  // Creates a PayloadVerifier from the zip file containing certificates. If the
+  // path to the zip file doesn't exist, falls back to use the public key.
+  // Returns a tuple with the created PayloadVerifier and if we should perform
+  // the verification.
+  std::pair<std::unique_ptr<PayloadVerifier>, bool> CreatePayloadVerifier();
+
   // After install_plan_ is filled with partition names and sizes, initialize
   // metadata of partitions and map necessary devices before opening devices.
   bool PreparePartitionsForUpdate();
@@ -383,6 +397,9 @@
   // override with test keys.
   std::string public_key_path_{constants::kUpdatePayloadPublicKeyPath};
 
+  // The path to the zip file with X509 certificates.
+  std::string update_certificates_path_{constants::kUpdateCertificatesPath};
+
   // The number of bytes received so far, used for progress tracking.
   size_t total_bytes_received_{0};
 
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index 28c11b6..a2ad77b 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -777,6 +777,7 @@
                                : GetBuildArtifactsPath(kUnittestPublicKeyPath);
   EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
   (*performer)->set_public_key_path(public_key_path);
+  (*performer)->set_update_certificates_path("");
 
   EXPECT_EQ(static_cast<off_t>(state->image_size),
             HashCalculator::RawHashOfFile(
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index b7a38cc..e9022ba 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -159,6 +159,11 @@
     install_plan_.target_slot = 1;
     EXPECT_CALL(mock_delegate_, ShouldCancel(_))
         .WillRepeatedly(testing::Return(false));
+    performer_.set_update_certificates_path("");
+    // Set the public key corresponding to the unittest private key.
+    string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
+    EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
+    performer_.set_public_key_path(public_key_path);
   }
 
   // Test helper placed where it can easily be friended from DeltaPerformer.
@@ -388,12 +393,6 @@
       expected_error = ErrorCode::kSuccess;
     }
 
-    // Use the public key corresponding to the private key used above to
-    // sign the metadata.
-    string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
-    EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
-    performer_.set_public_key_path(public_key_path);
-
     // Init actual_error with an invalid value so that we make sure
     // ParsePayloadMetadata properly populates it in all cases.
     actual_error = ErrorCode::kUmaReportedMax;
@@ -920,7 +919,6 @@
   brillo::Blob payload_data = GeneratePayload(
       {}, {}, true, kBrilloMajorPayloadVersion, kSourceMinorPayloadVersion);
   install_plan_.hash_checks_mandatory = true;
-  performer_.set_public_key_path(GetBuildArtifactsPath(kUnittestPublicKeyPath));
   ErrorCode error;
   EXPECT_EQ(MetadataParseResult::kSuccess,
             performer_.ParsePayloadMetadata(payload_data, &error));
diff --git a/payload_consumer/payload_metadata.cc b/payload_consumer/payload_metadata.cc
index c81d3a9..0952646 100644
--- a/payload_consumer/payload_metadata.cc
+++ b/payload_consumer/payload_metadata.cc
@@ -159,7 +159,7 @@
 ErrorCode PayloadMetadata::ValidateMetadataSignature(
     const brillo::Blob& payload,
     const string& metadata_signature,
-    const string& pem_public_key) const {
+    const PayloadVerifier& payload_verifier) const {
   if (payload.size() < metadata_size_ + metadata_signature_size_)
     return ErrorCode::kDownloadMetadataSignatureError;
 
@@ -201,16 +201,9 @@
     return ErrorCode::kDownloadMetadataSignatureVerificationError;
   }
 
-  auto payload_verifier = PayloadVerifier::CreateInstance(pem_public_key);
-  if (!payload_verifier) {
-    LOG(ERROR) << "Failed to create the payload verifier from "
-               << pem_public_key;
-    return ErrorCode::kDownloadMetadataSignatureVerificationError;
-  }
-
   if (!metadata_signature_blob.empty()) {
     brillo::Blob decrypted_signature;
-    if (!payload_verifier->VerifyRawSignature(
+    if (!payload_verifier.VerifyRawSignature(
             metadata_signature_blob, metadata_hash, &decrypted_signature)) {
       LOG(ERROR) << "Manifest hash verification failed. Decrypted hash = ";
       utils::HexDumpVector(decrypted_signature);
@@ -219,8 +212,8 @@
       return ErrorCode::kDownloadMetadataSignatureMismatch;
     }
   } else {
-    if (!payload_verifier->VerifySignature(metadata_signature_protobuf,
-                                           metadata_hash)) {
+    if (!payload_verifier.VerifySignature(metadata_signature_protobuf,
+                                          metadata_hash)) {
       LOG(ERROR) << "Manifest hash verification failed.";
       return ErrorCode::kDownloadMetadataSignatureMismatch;
     }
diff --git a/payload_consumer/payload_metadata.h b/payload_consumer/payload_metadata.h
index 1b4c5c8..75ef8f9 100644
--- a/payload_consumer/payload_metadata.h
+++ b/payload_consumer/payload_metadata.h
@@ -27,6 +27,7 @@
 
 #include "update_engine/common/error_code.h"
 #include "update_engine/common/platform_constants.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
 #include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
@@ -65,9 +66,10 @@
   // metadata is parsed so that a man-in-the-middle attack on the SSL connection
   // to the payload server doesn't exploit any vulnerability in the code that
   // parses the protocol buffer.
-  ErrorCode ValidateMetadataSignature(const brillo::Blob& payload,
-                                      const std::string& metadata_signature,
-                                      const std::string& pem_public_key) const;
+  ErrorCode ValidateMetadataSignature(
+      const brillo::Blob& payload,
+      const std::string& metadata_signature,
+      const PayloadVerifier& payload_verifier) const;
 
   // Returns the major payload version. If the version was not yet parsed,
   // returns zero.
diff --git a/payload_consumer/payload_verifier.cc b/payload_consumer/payload_verifier.cc
index 02eeb76..24e337e 100644
--- a/payload_consumer/payload_verifier.cc
+++ b/payload_consumer/payload_verifier.cc
@@ -25,6 +25,7 @@
 #include "update_engine/common/constants.h"
 #include "update_engine/common/hash_calculator.h"
 #include "update_engine/common/utils.h"
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
 #include "update_engine/update_metadata.pb.h"
 
 using std::string;
@@ -63,17 +64,39 @@
   auto pub_key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>(
       PEM_read_bio_PUBKEY(bp.get(), nullptr, nullptr, nullptr), EVP_PKEY_free);
   if (!pub_key) {
-    LOG(ERROR) << "Failed to parse the public key in " << pem_public_key;
+    LOG(ERROR) << "Failed to parse the public key in: " << pem_public_key;
+    return nullptr;
+  }
+
+  std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> keys;
+  keys.emplace_back(std::move(pub_key));
+  return std::unique_ptr<PayloadVerifier>(new PayloadVerifier(std::move(keys)));
+}
+
+std::unique_ptr<PayloadVerifier> PayloadVerifier::CreateInstanceFromZipPath(
+    const std::string& certificate_zip_path) {
+  auto parser = CreateCertificateParser();
+  if (!parser) {
+    LOG(ERROR) << "Failed to create certificate parser from "
+               << certificate_zip_path;
+    return nullptr;
+  }
+
+  std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> public_keys;
+  if (!parser->ReadPublicKeysFromCertificates(certificate_zip_path,
+                                              &public_keys) ||
+      public_keys.empty()) {
+    LOG(ERROR) << "Failed to parse public keys in: " << certificate_zip_path;
     return nullptr;
   }
 
   return std::unique_ptr<PayloadVerifier>(
-      new PayloadVerifier(std::move(pub_key)));
+      new PayloadVerifier(std::move(public_keys)));
 }
 
 bool PayloadVerifier::VerifySignature(
     const string& signature_proto, const brillo::Blob& sha256_hash_data) const {
-  TEST_AND_RETURN_FALSE(public_key_ != nullptr);
+  TEST_AND_RETURN_FALSE(!public_keys_.empty());
 
   Signatures signatures;
   LOG(INFO) << "signature blob size = " << signature_proto.size();
@@ -125,37 +148,50 @@
     const brillo::Blob& sig_data,
     const brillo::Blob& sha256_hash_data,
     brillo::Blob* decrypted_sig_data) const {
-  TEST_AND_RETURN_FALSE(public_key_ != nullptr);
+  TEST_AND_RETURN_FALSE(!public_keys_.empty());
 
-  int key_type = EVP_PKEY_id(public_key_.get());
-  if (key_type == EVP_PKEY_RSA) {
-    brillo::Blob sig_hash_data;
-    TEST_AND_RETURN_FALSE(
-        GetRawHashFromSignature(sig_data, public_key_.get(), &sig_hash_data));
+  for (const auto& public_key : public_keys_) {
+    int key_type = EVP_PKEY_id(public_key.get());
+    if (key_type == EVP_PKEY_RSA) {
+      brillo::Blob sig_hash_data;
+      if (!GetRawHashFromSignature(
+              sig_data, public_key.get(), &sig_hash_data)) {
+        LOG(WARNING)
+            << "Failed to get the raw hash with RSA key. Trying other keys.";
+        continue;
+      }
 
-    if (decrypted_sig_data != nullptr) {
-      *decrypted_sig_data = sig_hash_data;
+      if (decrypted_sig_data != nullptr) {
+        *decrypted_sig_data = sig_hash_data;
+      }
+
+      brillo::Blob padded_hash_data = sha256_hash_data;
+      TEST_AND_RETURN_FALSE(
+          PadRSASHA256Hash(&padded_hash_data, sig_hash_data.size()));
+
+      if (padded_hash_data == sig_hash_data) {
+        return true;
+      }
     }
 
-    brillo::Blob padded_hash_data = sha256_hash_data;
-    TEST_AND_RETURN_FALSE(
-        PadRSASHA256Hash(&padded_hash_data, sig_hash_data.size()));
+    if (key_type == EVP_PKEY_EC) {
+      EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(public_key.get());
+      TEST_AND_RETURN_FALSE(ec_key != nullptr);
+      if (ECDSA_verify(0,
+                       sha256_hash_data.data(),
+                       sha256_hash_data.size(),
+                       sig_data.data(),
+                       sig_data.size(),
+                       ec_key) == 1) {
+        return true;
+      }
+    }
 
-    return padded_hash_data == sig_hash_data;
+    LOG(ERROR) << "Unsupported key type " << key_type;
+    return false;
   }
-
-  if (key_type == EVP_PKEY_EC) {
-    EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(public_key_.get());
-    TEST_AND_RETURN_FALSE(ec_key != nullptr);
-    return ECDSA_verify(0,
-                        sha256_hash_data.data(),
-                        sha256_hash_data.size(),
-                        sig_data.data(),
-                        sig_data.size(),
-                        ec_key) == 1;
-  }
-
-  LOG(ERROR) << "Unsupported key type " << key_type;
+  LOG(INFO) << "Failed to verify the signature with " << public_keys_.size()
+            << " keys.";
   return false;
 }
 
diff --git a/payload_consumer/payload_verifier.h b/payload_consumer/payload_verifier.h
index b5d5457..bc5231f 100644
--- a/payload_consumer/payload_verifier.h
+++ b/payload_consumer/payload_verifier.h
@@ -20,13 +20,14 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include <brillo/secure_blob.h>
 #include <openssl/evp.h>
 
 #include "update_engine/update_metadata.pb.h"
 
-// This class holds the public key and implements methods used for payload
+// This class holds the public keys and implements methods used for payload
 // signature verification. See payload_generator/payload_signer.h for payload
 // signing.
 
@@ -47,6 +48,11 @@
   static std::unique_ptr<PayloadVerifier> CreateInstance(
       const std::string& pem_public_key);
 
+  // Extracts the public keys from the certificates contained in the input
+  // zip file. And creates a PayloadVerifier with these public keys.
+  static std::unique_ptr<PayloadVerifier> CreateInstanceFromZipPath(
+      const std::string& certificate_zip_path);
+
   // Interprets |signature_proto| as a protocol buffer containing the
   // |Signatures| message and decrypts each signature data using the stored
   // public key. Pads the 32 bytes |sha256_hash_data| to 256 or 512 bytes
@@ -65,8 +71,9 @@
 
  private:
   explicit PayloadVerifier(
-      std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>&& public_key)
-      : public_key_(std::move(public_key)) {}
+      std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>>&&
+          public_keys)
+      : public_keys_(std::move(public_keys)) {}
 
   // Decrypts |sig_data| with the given |public_key| and populates
   // |out_hash_data| with the decoded raw hash. Returns true if successful,
@@ -75,8 +82,7 @@
                                const EVP_PKEY* public_key,
                                brillo::Blob* out_hash_data) const;
 
-  std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> public_key_{nullptr,
-                                                                  nullptr};
+  std::vector<std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>> public_keys_;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/update_attempter_android.cc b/update_attempter_android.cc
index 08f6c20..5bffc42 100644
--- a/update_attempter_android.cc
+++ b/update_attempter_android.cc
@@ -39,6 +39,7 @@
 #include "update_engine/metrics_reporter_interface.h"
 #include "update_engine/metrics_utils.h"
 #include "update_engine/network_selector.h"
+#include "update_engine/payload_consumer/certificate_parser_interface.h"
 #include "update_engine/payload_consumer/delta_performer.h"
 #include "update_engine/payload_consumer/download_action.h"
 #include "update_engine/payload_consumer/file_descriptor.h"
@@ -46,6 +47,7 @@
 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 #include "update_engine/payload_consumer/payload_metadata.h"
+#include "update_engine/payload_consumer/payload_verifier.h"
 #include "update_engine/payload_consumer/postinstall_runner_action.h"
 #include "update_engine/update_boot_flags_action.h"
 #include "update_engine/update_status_utils.h"
@@ -410,12 +412,16 @@
   }
   fd->Close();
 
-  string public_key;
-  if (!utils::ReadFile(constants::kUpdatePayloadPublicKeyPath, &public_key)) {
-    return LogAndSetError(error, FROM_HERE, "Failed to read public key.");
+  auto payload_verifier = PayloadVerifier::CreateInstanceFromZipPath(
+      constants::kUpdateCertificatesPath);
+  if (!payload_verifier) {
+    return LogAndSetError(error,
+                          FROM_HERE,
+                          "Failed to create the payload verifier from " +
+                              std::string(constants::kUpdateCertificatesPath));
   }
-  errorcode =
-      payload_metadata.ValidateMetadataSignature(metadata, "", public_key);
+  errorcode = payload_metadata.ValidateMetadataSignature(
+      metadata, "", *payload_verifier);
   if (errorcode != ErrorCode::kSuccess) {
     return LogAndSetError(error,
                           FROM_HERE,