Check if zlib is compatible.

Add a --zlib_fingerprint flag in delta_generator to check if the zlib of
source image is compatible, and only enable IMGDIFF operation if it is.

Test: Added unittest to check /etc/zlib_fingerprint
Bug: 27156099

Change-Id: Ida346a99430c95cdc8a43e1055d6efa08f07ca25
diff --git a/Android.mk b/Android.mk
index c67ca22..fdfcba7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -745,6 +745,15 @@
 $(call ue-unittest-sample-image,disk_ext2_4k_empty.img)
 $(call ue-unittest-sample-image,disk_ext2_unittest.img)
 
+# Zlib Fingerprint
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := zlib_fingerprint
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_NATIVE_TESTS)/update_engine_unittests
+LOCAL_PREBUILT_MODULE_FILE := $(TARGET_OUT_COMMON_GEN)/zlib_fingerprint
+include $(BUILD_PREBUILT)
+
 # test_http_server (type: executable)
 # ========================================================
 # Test HTTP Server.
@@ -783,7 +792,8 @@
     ue_unittest_key.pem \
     ue_unittest_key.pub.pem \
     ue_unittest_key2.pem \
-    ue_unittest_key2.pub.pem
+    ue_unittest_key2.pub.pem \
+    zlib_fingerprint
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_CLANG := true
diff --git a/common/utils.cc b/common/utils.cc
index 912bc96..2b09560 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -61,6 +61,7 @@
 #include "update_engine/common/subprocess.h"
 #include "update_engine/payload_consumer/file_descriptor.h"
 #include "update_engine/payload_consumer/file_writer.h"
+#include "update_engine/payload_consumer/payload_constants.h"
 
 using base::Time;
 using base::TimeDelta;
@@ -1133,6 +1134,19 @@
   return false;
 }
 
+bool IsZlibCompatible(const string& fingerprint) {
+  if (fingerprint.size() != sizeof(kCompatibleZlibFingerprint[0]) - 1) {
+    LOG(ERROR) << "Invalid fingerprint: " << fingerprint;
+    return false;
+  }
+  for (auto& f : kCompatibleZlibFingerprint) {
+    if (base::CompareCaseInsensitiveASCII(fingerprint, f) == 0) {
+      return true;
+    }
+  }
+  return false;
+}
+
 bool ReadExtents(const string& path, const vector<Extent>& extents,
                  brillo::Blob* out_data, ssize_t out_data_size,
                  size_t block_size) {
diff --git a/common/utils.h b/common/utils.h
index df06ef1..55d8a80 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -343,6 +343,9 @@
 bool GetMinorVersion(const brillo::KeyValueStore& store,
                      uint32_t* minor_version);
 
+// Returns whether zlib |fingerprint| is compatible with zlib we are using.
+bool IsZlibCompatible(const std::string& fingerprint);
+
 // This function reads the specified data in |extents| into |out_data|. The
 // extents are read from the file at |path|. |out_data_size| is the size of
 // |out_data|. Returns false if the number of bytes to read given in
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index d637a7e..89182fe 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -56,7 +56,9 @@
 extern const char* kUnittestPrivateKeyPath;
 extern const char* kUnittestPublicKeyPath;
 
-static const char* kBogusMetadataSignature1 =
+namespace {
+
+const char kBogusMetadataSignature1[] =
     "awSFIUdUZz2VWFiR+ku0Pj00V7bPQPQFYQSXjEXr3vaw3TE4xHV5CraY3/YrZpBv"
     "J5z4dSBskoeuaO1TNC/S6E05t+yt36tE4Fh79tMnJ/z9fogBDXWgXLEUyG78IEQr"
     "YH6/eBsQGT2RJtBgXIXbZ9W+5G9KmGDoPOoiaeNsDuqHiBc/58OFsrxskH8E6vMS"
@@ -64,7 +66,13 @@
     "fjoTeLYZpt+WN65Vu7jJ0cQN8e1y+2yka5112wpRf/LLtPgiAjEZnsoYpLUd7CoV"
     "pLRtClp97kN2+tXGNBQqkA==";
 
-namespace {
+#ifdef __ANDROID__
+const char kZlibFingerprintPath[] =
+    "/data/nativetest/update_engine_unittests/zlib_fingerprint";
+#else
+const char kZlibFingerprintPath[] = "/etc/zlib_fingerprint";
+#endif  // __ANDROID__
+
 // Different options that determine what we should fill into the
 // install_plan.metadata_signature to simulate the contents received in the
 // Omaha response.
@@ -893,4 +901,14 @@
   EXPECT_EQ(DeltaPerformer::kSupportedMajorPayloadVersion, major_version);
 }
 
+// Test that we recognize our own zlib compressor implementation as supported.
+// All other equivalent implementations should be added to
+// kCompatibleZlibFingerprint.
+TEST_F(DeltaPerformerTest, ZlibFingerprintMatch) {
+  string fingerprint;
+  EXPECT_TRUE(base::ReadFileToString(base::FilePath(kZlibFingerprintPath),
+                                     &fingerprint));
+  EXPECT_TRUE(utils::IsZlibCompatible(fingerprint));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/payload_constants.cc b/payload_consumer/payload_constants.cc
index 3d78e0a..6078a74 100644
--- a/payload_consumer/payload_constants.cc
+++ b/payload_consumer/payload_constants.cc
@@ -33,6 +33,15 @@
 const char kDeltaMagic[4] = {'C', 'r', 'A', 'U'};
 const char kBspatchPath[] = "bspatch";
 
+// The zlib in Android and Chrome OS are currently compatible with each other,
+// so they are sharing the same array, but if in the future they are no longer
+// compatible with each other, we coule make the same change on the other one to
+// make them compatible again or use ifdef here.
+const char kCompatibleZlibFingerprint[][65] = {
+    "ea973605ccbbdb24f59f449c5f65861a1a9bc7a4353377aaaa06cb3e0f1cfbd7",
+    "3747fa404cceb00a5ec3606fc779510aaa784d5864ab1d5c28b9e267c40aad5c",
+};
+
 const char* InstallOperationTypeName(InstallOperation_Type op_type) {
   switch (op_type) {
     case InstallOperation::BSDIFF:
diff --git a/payload_consumer/payload_constants.h b/payload_consumer/payload_constants.h
index 76d740f..2dbc5fa 100644
--- a/payload_consumer/payload_constants.h
+++ b/payload_consumer/payload_constants.h
@@ -56,6 +56,15 @@
 extern const char kBspatchPath[];
 extern const char kDeltaMagic[4];
 
+// The list of compatible SHA256 hashes of zlib source code.
+// This is used to check if the source image have a compatible zlib (produce
+// same compressed result given the same input).
+// When a new fingerprint is found, please examine the changes in zlib source
+// carefully and determine if it's still compatible with previous version, if
+// yes then add the new fingerprint to this array, otherwise remove all previous
+// fingerprints in the array first, and only include the new fingerprint.
+extern const char kCompatibleZlibFingerprint[2][65];
+
 // A block number denoting a hole on a sparse file. Used on Extents to refer to
 // section of blocks not present on disk on a sparse file.
 const uint64_t kSparseHole = std::numeric_limits<uint64_t>::max();
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 70df2a0..f4ea884 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -309,6 +309,9 @@
   DEFINE_string(properties_file, "",
                 "If passed, dumps the payload properties of the payload passed "
                 "in --in_file and exits.");
+  DEFINE_string(zlib_fingerprint, "",
+                "The fingerprint of zlib in the source image in hash string "
+                "format, used to check imgdiff compatibility.");
 
   DEFINE_string(old_channel, "",
                 "The channel for the old image. 'dev-channel', 'npo-channel', "
@@ -535,9 +538,13 @@
     LOG(INFO) << "Using provided minor_version=" << FLAGS_minor_version;
   }
 
-  if (payload_config.minor_version >= kImgdiffMinorPayloadVersion) {
-    // TODO(senj): Check if the zlib version in source and target are the same.
-    payload_config.imgdiff_allowed = true;
+  if (payload_config.minor_version >= kImgdiffMinorPayloadVersion &&
+      !FLAGS_zlib_fingerprint.empty()) {
+    if (utils::IsZlibCompatible(FLAGS_zlib_fingerprint)) {
+      payload_config.imgdiff_allowed = true;
+    } else {
+      LOG(INFO) << "IMGDIFF operation disabled due to fingerprint mismatch.";
+    }
   }
 
   if (payload_config.is_delta) {
diff --git a/scripts/brillo_update_payload b/scripts/brillo_update_payload
index 75b089d..a3eec4b 100755
--- a/scripts/brillo_update_payload
+++ b/scripts/brillo_update_payload
@@ -200,6 +200,9 @@
 # Path to the postinstall config file in target image if exists.
 POSTINSTALL_CONFIG_FILE=""
 
+# The fingerprint of zlib in the source image.
+ZLIB_FINGERPRINT=""
+
 # read_option_int <file.txt> <option_key> [default_value]
 #
 # Reads the unsigned integer value associated with |option_key| in a key=value
@@ -307,6 +310,11 @@
   # updater supports a newer major version.
   FORCE_MAJOR_VERSION="1"
 
+  if [[ "${partitions_array}" == "SRC_PARTITIONS" ]]; then
+    # Copy from zlib_fingerprint in source image to stdout.
+    ZLIB_FINGERPRINT=$(e2cp "${root}":/etc/zlib_fingerprint -)
+  fi
+
   # When generating legacy Chrome OS images, we need to use "boot" and "system"
   # for the partition names to be compatible with updating Brillo devices with
   # Chrome OS images.
@@ -380,6 +388,10 @@
 Disabling deltas for this source version."
       exit ${EX_UNSUPPORTED_DELTA}
     fi
+
+    if [[ "${FORCE_MINOR_VERSION}" -ge 4 ]]; then
+      ZLIB_FINGERPRINT=$(unzip -p "${image}" "META/zlib_fingerprint.txt")
+    fi
   else
     # Target image
     local postinstall_config=$(create_tempfile "postinstall_config.XXXXXX")
@@ -475,6 +487,9 @@
     if [[ -n "${FORCE_MINOR_VERSION}" ]]; then
       GENERATOR_ARGS+=( --minor_version="${FORCE_MINOR_VERSION}" )
     fi
+    if [[ -n "${ZLIB_FINGERPRINT}" ]]; then
+      GENERATOR_ARGS+=( --zlib_fingerprint="${ZLIB_FINGERPRINT}" )
+    fi
   fi
 
   if [[ -n "${FORCE_MAJOR_VERSION}" ]]; then