am 06083e44: read back and verify radio image from cache before installing

Merge commit '06083e44b28162761d718da52dc1662cb6e5ee35' into gingerbread-plus-aosp

* commit '06083e44b28162761d718da52dc1662cb6e5ee35':
  read back and verify radio image from cache before installing
diff --git a/releasetools.py b/releasetools.py
index d8c3361..01b39e6 100644
--- a/releasetools.py
+++ b/releasetools.py
@@ -17,7 +17,7 @@
 
 import common
 import re
-
+import sha
 
 def FullOTA_Assertions(info):
   AddBootloaderAssertion(info, info.input_zip)
@@ -46,12 +46,15 @@
     error_img = input_zip.read("RADIO/firmware_error.565")
     width, height, bpp = bitmap_txt.split()
 
+    radio_sha = sha.sha(radio_img).hexdigest()
+
     info.script.UnmountAll()
     info.script.AppendExtra(('''
 assert(htc.install_radio(package_extract_file("radio.img"),
                          %(width)s, %(height)s, %(bpp)s,
                          package_extract_file("install.565"),
-                         package_extract_file("error.565")));
+                         package_extract_file("error.565"),
+                         %(radio_sha)s));
 ''' % locals()).lstrip())
 
     common.ZipWriteStr(info.output_zip, "install.565", install_img)
diff --git a/updater/firmware.c b/updater/firmware.c
index 76b9344..4672e18 100644
--- a/updater/firmware.c
+++ b/updater/firmware.c
@@ -18,6 +18,7 @@
 #include "common.h"
 #include "firmware.h"
 #include "mtdutils/mtdutils.h"
+#include "mincrypt/sha.h"
 
 #include <errno.h>
 #include <string.h>
@@ -58,13 +59,98 @@
 #undef LOGE
 #define LOGE(...) fprintf(stderr, "E:" __VA_ARGS__)
 
+
+int verify_image(const uint8_t* expected_sha1) {
+    MtdPartition* part = mtd_find_partition_by_name(CACHE_NAME);
+    if (part == NULL) {
+        printf("verify image: failed to find cache partition\n");
+        return -1;
+    }
+
+    size_t block_size;
+    if (mtd_partition_info(part, NULL, &block_size, NULL) != 0) {
+        printf("verify image: failed to get cache partition block size\n");
+        return -1;
+    }
+    printf("block size is 0x%x\n", block_size);
+
+    char* buffer = malloc(block_size);
+    if (buffer == NULL) {
+        printf("verify image: failed to allocate memory\n");
+        return -1;
+    }
+
+    MtdReadContext* ctx = mtd_read_partition(part);
+    if (ctx == NULL) {
+        printf("verify image: failed to init read context\n");
+        return -1;
+    }
+
+    size_t pos = 0;
+    if (mtd_read_data(ctx, buffer, block_size) != block_size) {
+        printf("verify image: failed to read header\n");
+        return -1;
+    }
+    pos += block_size;
+
+    if (strncmp(buffer, "MSM-RADIO-UPDATE", 16) != 0) {
+        printf("verify image: header missing magic\n");
+        return -1;
+    }
+
+    unsigned image_offset = *(unsigned*)(buffer+24);
+    unsigned image_length = *(unsigned*)(buffer+28);
+    printf("image offset 0x%x length 0x%x\n", image_offset, image_length);
+
+    while (pos < image_offset) {
+        size_t to_read = image_offset - pos;
+        if (to_read > block_size) to_read = block_size;
+        ssize_t read = mtd_read_data(ctx, buffer, to_read);
+        if (read < 0) {
+            printf("verify image: failed to skip to image start\n");
+            return -1;
+        }
+        pos += read;
+    }
+
+    SHA_CTX sha_ctx;
+    SHA_init(&sha_ctx);
+
+    size_t total = 0;
+    while (total < image_length) {
+        size_t to_read = image_length - total;
+        if (to_read > block_size) to_read = block_size;
+        ssize_t read = mtd_read_data(ctx, buffer, to_read);
+        if (read < 0) {
+            printf("verify image: failed reading image (read 0x%x so far)\n",
+                   total);
+            return -1;
+        }
+        SHA_update(&sha_ctx, buffer, read);
+        total += read;
+    }
+
+    free(buffer);
+
+    const uint8_t* sha1 = SHA_final(&sha_ctx);
+    if (memcmp(sha1, expected_sha1, SHA_DIGEST_SIZE) != 0) {
+        printf("verify image: sha1 doesn't match\n");
+        return -1;
+    }
+
+    printf("verify image: verification succeeded\n");
+
+    return 0;
+}
+
 int install_firmware_update(const char *update_type,
                             const char *update_data,
                             size_t update_length,
                             int width, int height, int bpp,
                             const char* busy_image,
                             const char* fail_image,
-                            const char *log_filename) {
+                            const char *log_filename,
+                            const uint8_t* expected_sha1) {
     if (update_data == NULL || update_length == 0) return 0;
 
     mtd_scan_partitions();
@@ -87,18 +173,21 @@
         return -1;
     }
 
-    /* The update image is fully written, so now we can instruct the bootloader
-     * to install it.  (After doing so, it will come back here, and we will
-     * wipe the cache and reboot into the system.)
-     */
-    snprintf(boot.command, sizeof(boot.command), "update-%s", update_type);
-    if (set_bootloader_message(&boot)) {
-        return -1;
+    if (verify_image(expected_sha1) == 0) {
+        /* The update image is fully written, so now we can instruct
+         * the bootloader to install it.  (After doing so, it will
+         * come back here, and we will wipe the cache and reboot into
+         * the system.)
+         */
+        snprintf(boot.command, sizeof(boot.command), "update-%s", update_type);
+        if (set_bootloader_message(&boot)) {
+            return -1;
+        }
+
+        reboot(RB_AUTOBOOT);
+
+        // Can't reboot?  WTF?
+        LOGE("Can't reboot\n");
     }
-
-    reboot(RB_AUTOBOOT);
-
-    // Can't reboot?  WTF?
-    LOGE("Can't reboot\n");
     return -1;
 }
diff --git a/updater/firmware.h b/updater/firmware.h
index 7297ecf..e463b6c 100644
--- a/updater/firmware.h
+++ b/updater/firmware.h
@@ -27,6 +27,7 @@
                             int width, int height, int bpp,
                             const char* busy_image,
                             const char* fail_image,
-                            const char *log_filename);
+                            const char *log_filename,
+                            const uint8_t* expected_sha1);
 
 #endif
diff --git a/updater/recovery_updater.c b/updater/recovery_updater.c
index 64b35a5..d382f96 100644
--- a/updater/recovery_updater.c
+++ b/updater/recovery_updater.c
@@ -21,12 +21,13 @@
 #include <string.h>
 #include <sys/stat.h>
 
+#include "mincrypt/sha.h"
 #include "edify/expr.h"
 #include "firmware.h"
 
 Value* UpdateFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 6) {
-        return ErrorAbort(state, "%s() expects 6 args, got %d", name, argc);
+    if (argc != 7) {
+        return ErrorAbort(state, "%s() expects 7 args, got %d", name, argc);
     }
 
     char* type = strrchr(name, '_');
@@ -42,9 +43,10 @@
     Value* bpp_string;
     Value* busy;
     Value* fail;
-    if (ReadValueArgs(state, argv, 6, &image,
-                 &width_string, &height_string, &bpp_string,
-                 &busy, &fail) < 0) {
+    Value* expected_sha1_string;
+    if (ReadValueArgs(state, argv, 7, &image,
+                      &width_string, &height_string, &bpp_string,
+                      &busy, &fail, &expected_sha1_string) < 0) {
         return NULL;
     }
 
@@ -68,6 +70,24 @@
         goto done;
     }
 
+    uint8_t expected_sha1[SHA_DIGEST_SIZE];
+    char* data = expected_sha1_string->data;
+    if (expected_sha1_string->type != VAL_STRING ||
+        strlen(data) != SHA_DIGEST_SIZE*2) {
+        printf("%s(): bad expected_sha1 argument", name);
+        goto done;
+    }
+    printf("expected sha1 is: ");
+    int i;
+    for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+        char temp = data[i*2+2];
+        data[i*2+2] = '\0';
+        expected_sha1[i] = strtol(data+i*2, NULL, 16);
+        data[i*2+2] = temp;
+        printf("%02x", expected_sha1[i]);
+    }
+    printf("\n");
+
     install_firmware_update(
         type,
         image->data,
@@ -75,7 +95,8 @@
         width, height, bpp,
         busy->size > 0 ? busy->data : NULL,
         fail->size > 0 ? fail->data : NULL,
-        "/tmp/recovery.log");
+        "/tmp/recovery.log",
+        expected_sha1);
     printf("%s: install_firmware_update returned!\n", name);
 
   done: