Verify wipe package when wiping A/B device in recovery.

To increase the security of wiping A/B devices, let uncrypt write
wipe package in misc partition. Then recovery verifies the wipe
package before wiping the device.

Bug: 29159185

Change-Id: I186691bab1928d3dc036bc5542abd64a81bc2168
diff --git a/recovery.cpp b/recovery.cpp
index 65e1a7b..6df9b6f 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -61,6 +61,7 @@
 #include "install.h"
 #include "minui/minui.h"
 #include "minzip/DirUtil.h"
+#include "minzip/Zip.h"
 #include "roots.h"
 #include "ui.h"
 #include "unique_fd.h"
@@ -84,6 +85,7 @@
   { "reason", required_argument, NULL, 'r' },
   { "security", no_argument, NULL, 'e'},
   { "wipe_ab", no_argument, NULL, 0 },
+  { "wipe_package_size", required_argument, NULL, 0 },
   { NULL, 0, NULL, 0 },
 };
 
@@ -904,12 +906,75 @@
     return true;
 }
 
+// Check if the wipe package matches expectation:
+// 1. verify the package.
+// 2. check metadata (ota-type, pre-device and serial number if having one).
+static bool check_wipe_package(size_t wipe_package_size) {
+    if (wipe_package_size == 0) {
+        LOGE("wipe_package_size is zero.\n");
+        return false;
+    }
+    std::string wipe_package;
+    if (!read_wipe_package(wipe_package_size, &wipe_package)) {
+        LOGE("Failed to read wipe package.\n");
+        return false;
+    }
+    if (!verify_package(reinterpret_cast<const unsigned char*>(wipe_package.data()),
+                        wipe_package.size())) {
+        LOGE("Failed to verify package.\n");
+        return false;
+    }
+
+    // Extract metadata
+    ZipArchive zip;
+    int err = mzOpenZipArchive(reinterpret_cast<unsigned char*>(&wipe_package[0]),
+                               wipe_package.size(), &zip);
+    if (err != 0) {
+        LOGE("Can't open wipe package: %s\n", err != -1 ? strerror(err) : "bad");
+        return false;
+    }
+    std::string metadata;
+    if (!read_metadata_from_package(&zip, &metadata)) {
+        mzCloseZipArchive(&zip);
+        return false;
+    }
+    mzCloseZipArchive(&zip);
+
+    // Check metadata
+    std::vector<std::string> lines = android::base::Split(metadata, "\n");
+    bool ota_type_matched = false;
+    bool device_type_matched = false;
+    bool has_serial_number = false;
+    bool serial_number_matched = false;
+    for (const auto& line : lines) {
+        if (line == "ota-type=BRICK") {
+            ota_type_matched = true;
+        } else if (android::base::StartsWith(line, "pre-device=")) {
+            std::string device_type = line.substr(strlen("pre-device="));
+            char real_device_type[PROPERTY_VALUE_MAX];
+            property_get("ro.build.product", real_device_type, "");
+            device_type_matched = (device_type == real_device_type);
+        } else if (android::base::StartsWith(line, "serialno=")) {
+            std::string serial_no = line.substr(strlen("serialno="));
+            char real_serial_no[PROPERTY_VALUE_MAX];
+            property_get("ro.serialno", real_serial_no, "");
+            has_serial_number = true;
+            serial_number_matched = (serial_no == real_serial_no);
+        }
+    }
+    return ota_type_matched && device_type_matched && (!has_serial_number || serial_number_matched);
+}
+
 // Wipe the current A/B device, with a secure wipe of all the partitions in
 // RECOVERY_WIPE.
-static bool wipe_ab_device() {
+static bool wipe_ab_device(size_t wipe_package_size) {
     ui->SetBackground(RecoveryUI::ERASING);
     ui->SetProgressType(RecoveryUI::INDETERMINATE);
 
+    if (!check_wipe_package(wipe_package_size)) {
+        LOGE("Failed to verify wipe package\n");
+        return false;
+    }
     std::string partition_list;
     if (!android::base::ReadFileToString(RECOVERY_WIPE, &partition_list)) {
         LOGE("failed to read \"%s\".\n", RECOVERY_WIPE);
@@ -1418,6 +1483,7 @@
     bool should_wipe_data = false;
     bool should_wipe_cache = false;
     bool should_wipe_ab = false;
+    size_t wipe_package_size = 0;
     bool show_text = false;
     bool sideload = false;
     bool sideload_auto_reboot = false;
@@ -1455,6 +1521,9 @@
             if (strcmp(OPTIONS[option_index].name, "wipe_ab") == 0) {
                 should_wipe_ab = true;
                 break;
+            } else if (strcmp(OPTIONS[option_index].name, "wipe_package_size") == 0) {
+                android::base::ParseUint(optarg, &wipe_package_size);
+                break;
             }
             break;
         }
@@ -1597,7 +1666,7 @@
             status = INSTALL_ERROR;
         }
     } else if (should_wipe_ab) {
-        if (!wipe_ab_device()) {
+        if (!wipe_ab_device(wipe_package_size)) {
             status = INSTALL_ERROR;
         }
     } else if (sideload) {