merge in nyc-release history after reset to master
diff --git a/Android.mk b/Android.mk
index 13fc04b..4da34ee 100644
--- a/Android.mk
+++ b/Android.mk
@@ -104,28 +104,10 @@
 LOCAL_MODULE := libverifier
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := \
-    asn1_decoder.cpp
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_MODULE := verifier_test
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += -Wno-unused-parameter
-LOCAL_SRC_FILES := \
-    verifier_test.cpp \
     asn1_decoder.cpp \
     verifier.cpp \
     ui.cpp
-LOCAL_STATIC_LIBRARIES := \
-    libmincrypt \
-    libminui \
-    libminzip \
-    libcutils \
-    libc
-include $(BUILD_EXECUTABLE)
-
+include $(BUILD_STATIC_LIBRARY)
 
 include $(LOCAL_PATH)/minui/Android.mk \
     $(LOCAL_PATH)/minzip/Android.mk \
diff --git a/README.md b/README.md
index bab7e87..01fab94 100644
--- a/README.md
+++ b/README.md
@@ -10,3 +10,20 @@
     # without flashing the recovery partition:
     adb reboot bootloader
     fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
+
+Running the tests
+-----------------
+    # After setting up environment and lunch.
+    mmma -j bootable/recovery
+
+    # Running the tests on device.
+    adb root
+    adb sync data
+
+    # 32-bit device
+    adb shell /data/nativetest/recovery_unit_test/recovery_unit_test
+    adb shell /data/nativetest/recovery_component_test/recovery_component_test
+
+    # Or 64-bit device
+    adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
+    adb shell /data/nativetest64/recovery_component_test/recovery_component_test
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index bc2e69e..887a570 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_MODULE := libapplypatch
 LOCAL_MODULE_TAGS := eng
 LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libmincrypt libbz libz
+LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libcrypto_static libbz libz
 
 include $(BUILD_STATIC_LIBRARY)
 
@@ -32,7 +32,7 @@
 LOCAL_MODULE := libimgpatch
 LOCAL_C_INCLUDES += bootable/recovery
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES += libmincrypt libbz libz
+LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz
 
 include $(BUILD_STATIC_LIBRARY)
 
@@ -44,7 +44,7 @@
 LOCAL_MODULE := libimgpatch
 LOCAL_C_INCLUDES += bootable/recovery
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES += libmincrypt libbz libz
+LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz
 
 include $(BUILD_HOST_STATIC_LIBRARY)
 endif  # HOST_OS == linux
@@ -55,7 +55,9 @@
 LOCAL_SRC_FILES := main.cpp
 LOCAL_MODULE := applypatch
 LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypto_static libbz \
+                          libedify \
+
 LOCAL_SHARED_LIBRARIES += libz libcutils libc
 
 include $(BUILD_EXECUTABLE)
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index 93e6b25..9f5e2f2 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -25,9 +25,12 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
+#include <string>
+
 #include <android-base/strings.h>
 
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "applypatch.h"
 #include "mtdutils/mtdutils.h"
 #include "edify/expr.h"
@@ -42,7 +45,7 @@
                           const Value* copy_patch_value,
                           const char* source_filename,
                           const char* target_filename,
-                          const uint8_t target_sha1[SHA_DIGEST_SIZE],
+                          const uint8_t target_sha1[SHA_DIGEST_LENGTH],
                           size_t target_size,
                           const Value* bonus_data);
 
@@ -68,26 +71,30 @@
     }
 
     file->size = file->st.st_size;
-    file->data = reinterpret_cast<unsigned char*>(malloc(file->size));
+    file->data = nullptr;
+
+    std::unique_ptr<unsigned char, decltype(&free)> data(
+            static_cast<unsigned char*>(malloc(file->size)), free);
+    if (data == nullptr) {
+        printf("failed to allocate memory: %s\n", strerror(errno));
+        return -1;
+    }
 
     FILE* f = ota_fopen(filename, "rb");
     if (f == NULL) {
         printf("failed to open \"%s\": %s\n", filename, strerror(errno));
-        free(file->data);
-        file->data = NULL;
         return -1;
     }
 
-    size_t bytes_read = ota_fread(file->data, 1, file->size, f);
+    size_t bytes_read = ota_fread(data.get(), 1, file->size, f);
     if (bytes_read != static_cast<size_t>(file->size)) {
         printf("short read of \"%s\" (%zu bytes of %zd)\n", filename, bytes_read, file->size);
-        free(file->data);
-        file->data = NULL;
+        fclose(f);
         return -1;
     }
     ota_fclose(f);
-
-    SHA_hash(file->data, file->size, file->sha1);
+    file->data = data.release();
+    SHA1(file->data, file->size, file->sha1);
     return 0;
 }
 
@@ -182,11 +189,11 @@
     }
 
     SHA_CTX sha_ctx;
-    SHA_init(&sha_ctx);
-    uint8_t parsed_sha[SHA_DIGEST_SIZE];
+    SHA1_Init(&sha_ctx);
+    uint8_t parsed_sha[SHA_DIGEST_LENGTH];
 
     // Allocate enough memory to hold the largest size.
-    file->data = reinterpret_cast<unsigned char*>(malloc(size[index[pairs-1]]));
+    file->data = static_cast<unsigned char*>(malloc(size[index[pairs-1]]));
     char* p = (char*)file->data;
     file->size = 0;                // # bytes read so far
     bool found = false;
@@ -213,7 +220,7 @@
                 file->data = NULL;
                 return -1;
             }
-            SHA_update(&sha_ctx, p, read);
+            SHA1_Update(&sha_ctx, p, read);
             file->size += read;
         }
 
@@ -221,7 +228,8 @@
         // check it against this pair's expected hash.
         SHA_CTX temp_ctx;
         memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
-        const uint8_t* sha_so_far = SHA_final(&temp_ctx);
+        uint8_t sha_so_far[SHA_DIGEST_LENGTH];
+        SHA1_Final(sha_so_far, &temp_ctx);
 
         if (ParseSha1(sha1sum[index[i]].c_str(), parsed_sha) != 0) {
             printf("failed to parse sha1 %s in %s\n", sha1sum[index[i]].c_str(), filename);
@@ -230,7 +238,7 @@
             return -1;
         }
 
-        if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
+        if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) {
             // we have a match.  stop reading the partition; we'll return
             // the data we've read so far.
             printf("partition read matched size %zu sha %s\n",
@@ -261,10 +269,7 @@
         return -1;
     }
 
-    const uint8_t* sha_final = SHA_final(&sha_ctx);
-    for (size_t i = 0; i < SHA_DIGEST_SIZE; ++i) {
-        file->sha1[i] = sha_final[i];
-    }
+    SHA1_Final(file->sha1, &sha_ctx);
 
     // Fake some stat() info.
     file->st.st_mode = 0644;
@@ -316,7 +321,7 @@
 // "MTD:<partition>[:...]" or "EMMC:<partition_device>[:...]". The target name
 // might contain multiple colons, but WriteToPartition() only uses the first
 // two and ignores the rest. Return 0 on success.
-int WriteToPartition(unsigned char* data, size_t len, const char* target) {
+int WriteToPartition(const unsigned char* data, size_t len, const char* target) {
     std::string copy(target);
     std::vector<std::string> pieces = android::base::Split(copy, ":");
 
@@ -355,7 +360,7 @@
                 return -1;
             }
 
-            size_t written = mtd_write_data(ctx, reinterpret_cast<char*>(data), len);
+            size_t written = mtd_write_data(ctx, reinterpret_cast<const char*>(data), len);
             if (written != len) {
                 printf("only wrote %zu of %zu bytes to MTD %s\n", written, len, partition);
                 mtd_write_close(ctx);
@@ -495,7 +500,7 @@
 int ParseSha1(const char* str, uint8_t* digest) {
     const char* ps = str;
     uint8_t* pd = digest;
-    for (int i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) {
+    for (int i = 0; i < SHA_DIGEST_LENGTH * 2; ++i, ++ps) {
         int digit;
         if (*ps >= '0' && *ps <= '9') {
             digit = *ps - '0';
@@ -522,10 +527,10 @@
 // found.
 int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
                       int num_patches) {
-    uint8_t patch_sha1[SHA_DIGEST_SIZE];
+    uint8_t patch_sha1[SHA_DIGEST_LENGTH];
     for (int i = 0; i < num_patches; ++i) {
         if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 &&
-            memcmp(patch_sha1, sha1, SHA_DIGEST_SIZE) == 0) {
+            memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
             return i;
         }
     }
@@ -581,7 +586,7 @@
 }
 
 ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) {
-    int fd = *reinterpret_cast<int *>(token);
+    int fd = *static_cast<int*>(token);
     ssize_t done = 0;
     ssize_t wrote;
     while (done < len) {
@@ -595,19 +600,9 @@
     return done;
 }
 
-typedef struct {
-    unsigned char* buffer;
-    ssize_t size;
-    ssize_t pos;
-} MemorySinkInfo;
-
 ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) {
-    MemorySinkInfo* msi = reinterpret_cast<MemorySinkInfo*>(token);
-    if (msi->size - msi->pos < len) {
-        return -1;
-    }
-    memcpy(msi->buffer + msi->pos, data, len);
-    msi->pos += len;
+    std::string* s = static_cast<std::string*>(token);
+    s->append(reinterpret_cast<const char*>(data), len);
     return len;
 }
 
@@ -671,7 +666,7 @@
         target_filename = source_filename;
     }
 
-    uint8_t target_sha1[SHA_DIGEST_SIZE];
+    uint8_t target_sha1[SHA_DIGEST_LENGTH];
     if (ParseSha1(target_sha1_str, target_sha1) != 0) {
         printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
         return 1;
@@ -686,7 +681,7 @@
 
     // We try to load the target file into the source_file object.
     if (LoadFileContents(target_filename, &source_file) == 0) {
-        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
+        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
             // The early-exit case:  the patch was already applied, this file
             // has the desired hash, nothing for us to do.
             printf("already %s\n", short_sha1(target_sha1).c_str());
@@ -757,7 +752,7 @@
                      const char* target_sha1_str, size_t target_size) {
     printf("flash %s: ", target_filename);
 
-    uint8_t target_sha1[SHA_DIGEST_SIZE];
+    uint8_t target_sha1[SHA_DIGEST_LENGTH];
     if (ParseSha1(target_sha1_str, target_sha1) != 0) {
         printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
         return 1;
@@ -778,7 +773,7 @@
     pieces.push_back(target_sha1_str);
     std::string fullname = android::base::Join(pieces, ':');
     if (LoadPartitionContents(fullname.c_str(), &source_file) == 0 &&
-        memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
+        memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
         // The early-exit case: the image was already applied, this partition
         // has the desired hash, nothing for us to do.
         printf("already %s\n", short_sha1(target_sha1).c_str());
@@ -787,7 +782,7 @@
     }
 
     if (LoadFileContents(source_filename, &source_file) == 0) {
-        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
+        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
             // The source doesn't have desired checksum.
             printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
             printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
@@ -813,36 +808,57 @@
                           const Value* copy_patch_value,
                           const char* source_filename,
                           const char* target_filename,
-                          const uint8_t target_sha1[SHA_DIGEST_SIZE],
+                          const uint8_t target_sha1[SHA_DIGEST_LENGTH],
                           size_t target_size,
                           const Value* bonus_data) {
     int retry = 1;
     SHA_CTX ctx;
-    int output;
-    MemorySinkInfo msi;
+    std::string memory_sink_str;
     FileContents* source_to_use;
-    char* outname;
     int made_copy = 0;
 
+    bool target_is_partition = (strncmp(target_filename, "MTD:", 4) == 0 ||
+                                strncmp(target_filename, "EMMC:", 5) == 0);
+    const std::string tmp_target_filename = std::string(target_filename) + ".patch";
+
     // assume that target_filename (eg "/system/app/Foo.apk") is located
     // on the same filesystem as its top-level directory ("/system").
     // We need something that exists for calling statfs().
-    char target_fs[strlen(target_filename)+1];
-    char* slash = strchr(target_filename+1, '/');
-    if (slash != NULL) {
-        int count = slash - target_filename;
-        strncpy(target_fs, target_filename, count);
-        target_fs[count] = '\0';
+    std::string target_fs = target_filename;
+    auto slash_pos = target_fs.find('/', 1);
+    if (slash_pos != std::string::npos) {
+        target_fs.resize(slash_pos);
+    }
+
+    const Value* patch;
+    if (source_patch_value != NULL) {
+        source_to_use = source_file;
+        patch = source_patch_value;
     } else {
-        strcpy(target_fs, target_filename);
+        source_to_use = copy_file;
+        patch = copy_patch_value;
+    }
+    if (patch->type != VAL_BLOB) {
+        printf("patch is not a blob\n");
+        return 1;
+    }
+    char* header = patch->data;
+    ssize_t header_bytes_read = patch->size;
+    bool use_bsdiff = false;
+    if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
+        use_bsdiff = true;
+    } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
+        use_bsdiff = false;
+    } else {
+        printf("Unknown patch file format\n");
+        return 1;
     }
 
     do {
         // Is there enough room in the target filesystem to hold the patched
         // file?
 
-        if (strncmp(target_filename, "MTD:", 4) == 0 ||
-            strncmp(target_filename, "EMMC:", 5) == 0) {
+        if (target_is_partition) {
             // If the target is a partition, we're actually going to
             // write the output to /tmp and then copy it to the
             // partition.  statfs() always returns 0 blocks free for
@@ -864,7 +880,7 @@
         } else {
             int enough_space = 0;
             if (retry > 0) {
-                size_t free_space = FreeSpaceForFile(target_fs);
+                size_t free_space = FreeSpaceForFile(target_fs.c_str());
                 enough_space =
                     (free_space > (256 << 10)) &&          // 256k (two-block) minimum
                     (free_space > (target_size * 3 / 2));  // 50% margin of error
@@ -904,84 +920,53 @@
                 made_copy = 1;
                 unlink(source_filename);
 
-                size_t free_space = FreeSpaceForFile(target_fs);
+                size_t free_space = FreeSpaceForFile(target_fs.c_str());
                 printf("(now %zu bytes free for target) ", free_space);
             }
         }
 
-        const Value* patch;
-        if (source_patch_value != NULL) {
-            source_to_use = source_file;
-            patch = source_patch_value;
-        } else {
-            source_to_use = copy_file;
-            patch = copy_patch_value;
-        }
-
-        if (patch->type != VAL_BLOB) {
-            printf("patch is not a blob\n");
-            return 1;
-        }
 
         SinkFn sink = NULL;
         void* token = NULL;
-        output = -1;
-        outname = NULL;
-        if (strncmp(target_filename, "MTD:", 4) == 0 ||
-            strncmp(target_filename, "EMMC:", 5) == 0) {
+        int output_fd = -1;
+        if (target_is_partition) {
             // We store the decoded output in memory.
-            msi.buffer = reinterpret_cast<unsigned char*>(malloc(target_size));
-            if (msi.buffer == NULL) {
-                printf("failed to alloc %zu bytes for output\n", target_size);
-                return 1;
-            }
-            msi.pos = 0;
-            msi.size = target_size;
             sink = MemorySink;
-            token = &msi;
+            token = &memory_sink_str;
         } else {
             // We write the decoded output to "<tgt-file>.patch".
-            outname = reinterpret_cast<char*>(malloc(strlen(target_filename) + 10));
-            strcpy(outname, target_filename);
-            strcat(outname, ".patch");
-
-            output = ota_open(outname, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
-            if (output < 0) {
-                printf("failed to open output file %s: %s\n",
-                       outname, strerror(errno));
+            output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
+                          S_IRUSR | S_IWUSR);
+            if (output_fd < 0) {
+                printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(),
+                       strerror(errno));
                 return 1;
             }
             sink = FileSink;
-            token = &output;
+            token = &output_fd;
         }
 
-        char* header = patch->data;
-        ssize_t header_bytes_read = patch->size;
 
-        SHA_init(&ctx);
+        SHA1_Init(&ctx);
 
         int result;
-
-        if (header_bytes_read >= 8 &&
-            memcmp(header, "BSDIFF40", 8) == 0) {
+        if (use_bsdiff) {
             result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
                                       patch, 0, sink, token, &ctx);
-        } else if (header_bytes_read >= 8 &&
-                   memcmp(header, "IMGDIFF2", 8) == 0) {
+        } else {
             result = ApplyImagePatch(source_to_use->data, source_to_use->size,
                                      patch, sink, token, &ctx, bonus_data);
-        } else {
-            printf("Unknown patch file format\n");
-            return 1;
         }
 
-        if (output >= 0) {
-            if (ota_fsync(output) != 0) {
-                printf("failed to fsync file \"%s\" (%s)\n", outname, strerror(errno));
+        if (!target_is_partition) {
+            if (ota_fsync(output_fd) != 0) {
+                printf("failed to fsync file \"%s\" (%s)\n", tmp_target_filename.c_str(),
+                       strerror(errno));
                 result = 1;
             }
-            if (ota_close(output) != 0) {
-                printf("failed to close file \"%s\" (%s)\n", outname, strerror(errno));
+            if (ota_close(output_fd) != 0) {
+                printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(),
+                       strerror(errno));
                 result = 1;
             }
         }
@@ -993,8 +978,8 @@
             } else {
                 printf("applying patch failed; retrying\n");
             }
-            if (outname != NULL) {
-                unlink(outname);
+            if (!target_is_partition) {
+                unlink(tmp_target_filename.c_str());
             }
         } else {
             // succeeded; no need to retry
@@ -1002,35 +987,36 @@
         }
     } while (retry-- > 0);
 
-    const uint8_t* current_target_sha1 = SHA_final(&ctx);
-    if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
+    uint8_t current_target_sha1[SHA_DIGEST_LENGTH];
+    SHA1_Final(current_target_sha1, &ctx);
+    if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
         printf("patch did not produce expected sha1\n");
         return 1;
     } else {
         printf("now %s\n", short_sha1(target_sha1).c_str());
     }
 
-    if (output < 0) {
+    if (target_is_partition) {
         // Copy the temp file to the partition.
-        if (WriteToPartition(msi.buffer, msi.pos, target_filename) != 0) {
+        if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()),
+                             memory_sink_str.size(), target_filename) != 0) {
             printf("write of patched data to %s failed\n", target_filename);
             return 1;
         }
-        free(msi.buffer);
     } else {
         // Give the .patch file the same owner, group, and mode of the
         // original source file.
-        if (chmod(outname, source_to_use->st.st_mode) != 0) {
-            printf("chmod of \"%s\" failed: %s\n", outname, strerror(errno));
+        if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) {
+            printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
             return 1;
         }
-        if (chown(outname, source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
-            printf("chown of \"%s\" failed: %s\n", outname, strerror(errno));
+        if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
+            printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
             return 1;
         }
 
         // Finally, rename the .patch file to replace the target file.
-        if (rename(outname, target_filename) != 0) {
+        if (rename(tmp_target_filename.c_str(), target_filename) != 0) {
             printf("rename of .patch to \"%s\" failed: %s\n", target_filename, strerror(errno));
             return 1;
         }
diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h
index 415bc1b..14fb490 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/applypatch.h
@@ -18,16 +18,19 @@
 #define _APPLYPATCH_H
 
 #include <sys/stat.h>
-#include "mincrypt/sha.h"
+
+#include <vector>
+
+#include "openssl/sha.h"
 #include "edify/expr.h"
 
 typedef struct _Patch {
-  uint8_t sha1[SHA_DIGEST_SIZE];
+  uint8_t sha1[SHA_DIGEST_LENGTH];
   const char* patch_filename;
 } Patch;
 
 typedef struct _FileContents {
-  uint8_t sha1[SHA_DIGEST_SIZE];
+  uint8_t sha1[SHA_DIGEST_LENGTH];
   unsigned char* data;
   ssize_t size;
   struct stat st;
@@ -68,22 +71,22 @@
 int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
                       int num_patches);
 
-// bsdiff.c
+// bsdiff.cpp
 void ShowBSDiffLicense();
 int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
                      const Value* patch, ssize_t patch_offset,
                      SinkFn sink, void* token, SHA_CTX* ctx);
 int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
                         const Value* patch, ssize_t patch_offset,
-                        unsigned char** new_data, ssize_t* new_size);
+                        std::vector<unsigned char>* new_data);
 
-// imgpatch.c
+// imgpatch.cpp
 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
                     const Value* patch,
                     SinkFn sink, void* token, SHA_CTX* ctx,
                     const Value* bonus_data);
 
-// freecache.c
+// freecache.cpp
 int MakeFreeSpaceOnCache(size_t bytes_needed);
 
 #endif
diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp
index 75975ad..ebb55f1 100644
--- a/applypatch/bspatch.cpp
+++ b/applypatch/bspatch.cpp
@@ -24,13 +24,12 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <errno.h>
-#include <malloc.h>
 #include <unistd.h>
 #include <string.h>
 
 #include <bzlib.h>
 
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "applypatch.h"
 
 void ShowBSDiffLicense() {
@@ -103,26 +102,22 @@
                      const Value* patch, ssize_t patch_offset,
                      SinkFn sink, void* token, SHA_CTX* ctx) {
 
-    unsigned char* new_data;
-    ssize_t new_size;
-    if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset,
-                            &new_data, &new_size) != 0) {
+    std::vector<unsigned char> new_data;
+    if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, &new_data) != 0) {
         return -1;
     }
 
-    if (sink(new_data, new_size, token) < new_size) {
+    if (sink(new_data.data(), new_data.size(), token) < static_cast<ssize_t>(new_data.size())) {
         printf("short write of output: %d (%s)\n", errno, strerror(errno));
         return 1;
     }
-    if (ctx) SHA_update(ctx, new_data, new_size);
-    free(new_data);
-
+    if (ctx) SHA1_Update(ctx, new_data.data(), new_data.size());
     return 0;
 }
 
 int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
                         const Value* patch, ssize_t patch_offset,
-                        unsigned char** new_data, ssize_t* new_size) {
+                        std::vector<unsigned char>* new_data) {
     // Patch data format:
     //   0       8       "BSDIFF40"
     //   8       8       X
@@ -141,12 +136,12 @@
         return 1;
     }
 
-    ssize_t ctrl_len, data_len;
+    ssize_t ctrl_len, data_len, new_size;
     ctrl_len = offtin(header+8);
     data_len = offtin(header+16);
-    *new_size = offtin(header+24);
+    new_size = offtin(header+24);
 
-    if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
+    if (ctrl_len < 0 || data_len < 0 || new_size < 0) {
         printf("corrupt patch file header (data lengths)\n");
         return 1;
     }
@@ -183,18 +178,14 @@
         printf("failed to bzinit extra stream (%d)\n", bzerr);
     }
 
-    *new_data = reinterpret_cast<unsigned char*>(malloc(*new_size));
-    if (*new_data == NULL) {
-        printf("failed to allocate %zd bytes of memory for output file\n", *new_size);
-        return 1;
-    }
+    new_data->resize(new_size);
 
     off_t oldpos = 0, newpos = 0;
     off_t ctrl[3];
     off_t len_read;
     int i;
     unsigned char buf[24];
-    while (newpos < *new_size) {
+    while (newpos < new_size) {
         // Read control data
         if (FillBuffer(buf, 24, &cstream) != 0) {
             printf("error while reading control stream\n");
@@ -210,13 +201,13 @@
         }
 
         // Sanity check
-        if (newpos + ctrl[0] > *new_size) {
+        if (newpos + ctrl[0] > new_size) {
             printf("corrupt patch (new file overrun)\n");
             return 1;
         }
 
         // Read diff string
-        if (FillBuffer(*new_data + newpos, ctrl[0], &dstream) != 0) {
+        if (FillBuffer(new_data->data() + newpos, ctrl[0], &dstream) != 0) {
             printf("error while reading diff stream\n");
             return 1;
         }
@@ -233,13 +224,13 @@
         oldpos += ctrl[0];
 
         // Sanity check
-        if (newpos + ctrl[1] > *new_size) {
+        if (newpos + ctrl[1] > new_size) {
             printf("corrupt patch (new file overrun)\n");
             return 1;
         }
 
         // Read extra string
-        if (FillBuffer(*new_data + newpos, ctrl[1], &estream) != 0) {
+        if (FillBuffer(new_data->data() + newpos, ctrl[1], &estream) != 0) {
             printf("error while reading extra stream\n");
             return 1;
         }
diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp
index 2eb2f55..c84f427 100644
--- a/applypatch/freecache.cpp
+++ b/applypatch/freecache.cpp
@@ -25,119 +25,90 @@
 #include <dirent.h>
 #include <ctype.h>
 
+#include <memory>
+#include <set>
+#include <string>
+
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+
 #include "applypatch.h"
 
-static int EliminateOpenFiles(char** files, int file_count) {
-  DIR* d;
-  struct dirent* de;
-  d = opendir("/proc");
-  if (d == NULL) {
+static int EliminateOpenFiles(std::set<std::string>* files) {
+  std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir);
+  if (!d) {
     printf("error opening /proc: %s\n", strerror(errno));
     return -1;
   }
-  while ((de = readdir(d)) != 0) {
-    int i;
-    for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i);
-    if (de->d_name[i]) continue;
+  struct dirent* de;
+  while ((de = readdir(d.get())) != 0) {
+    unsigned int pid;
+    if (!android::base::ParseUint(de->d_name, &pid)) {
+        continue;
+    }
+    std::string path = android::base::StringPrintf("/proc/%s/fd/", de->d_name);
 
-    // de->d_name[i] is numeric
-
-    char path[FILENAME_MAX];
-    strcpy(path, "/proc/");
-    strcat(path, de->d_name);
-    strcat(path, "/fd/");
-
-    DIR* fdd;
     struct dirent* fdde;
-    fdd = opendir(path);
-    if (fdd == NULL) {
-      printf("error opening %s: %s\n", path, strerror(errno));
+    std::unique_ptr<DIR, decltype(&closedir)> fdd(opendir(path.c_str()), closedir);
+    if (!fdd) {
+      printf("error opening %s: %s\n", path.c_str(), strerror(errno));
       continue;
     }
-    while ((fdde = readdir(fdd)) != 0) {
-      char fd_path[FILENAME_MAX];
+    while ((fdde = readdir(fdd.get())) != 0) {
+      std::string fd_path = path + fdde->d_name;
       char link[FILENAME_MAX];
-      strcpy(fd_path, path);
-      strcat(fd_path, fdde->d_name);
 
-      int count;
-      count = readlink(fd_path, link, sizeof(link)-1);
+      int count = readlink(fd_path.c_str(), link, sizeof(link)-1);
       if (count >= 0) {
         link[count] = '\0';
-
-        // This is inefficient, but it should only matter if there are
-        // lots of files in /cache, and lots of them are open (neither
-        // of which should be true, especially in recovery).
         if (strncmp(link, "/cache/", 7) == 0) {
-          int j;
-          for (j = 0; j < file_count; ++j) {
-            if (files[j] && strcmp(files[j], link) == 0) {
-              printf("%s is open by %s\n", link, de->d_name);
-              free(files[j]);
-              files[j] = NULL;
-            }
+          if (files->erase(link) > 0) {
+            printf("%s is open by %s\n", link, de->d_name);
           }
         }
       }
     }
-    closedir(fdd);
   }
-  closedir(d);
-
   return 0;
 }
 
-int FindExpendableFiles(char*** names, int* entries) {
-  DIR* d;
-  struct dirent* de;
-  int size = 32;
-  *entries = 0;
-  *names = reinterpret_cast<char**>(malloc(size * sizeof(char*)));
-
-  char path[FILENAME_MAX];
-
+static std::set<std::string> FindExpendableFiles() {
+  std::set<std::string> files;
   // We're allowed to delete unopened regular files in any of these
   // directories.
   const char* dirs[2] = {"/cache", "/cache/recovery/otatest"};
 
   for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) {
-    d = opendir(dirs[i]);
-    if (d == NULL) {
+    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirs[i]), closedir);
+    if (!d) {
       printf("error opening %s: %s\n", dirs[i], strerror(errno));
       continue;
     }
 
     // Look for regular files in the directory (not in any subdirectories).
-    while ((de = readdir(d)) != 0) {
-      strcpy(path, dirs[i]);
-      strcat(path, "/");
-      strcat(path, de->d_name);
+    struct dirent* de;
+    while ((de = readdir(d.get())) != 0) {
+      std::string path = std::string(dirs[i]) + "/" + de->d_name;
 
       // We can't delete CACHE_TEMP_SOURCE; if it's there we might have
       // restarted during installation and could be depending on it to
       // be there.
-      if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue;
+      if (path == CACHE_TEMP_SOURCE) {
+        continue;
+      }
 
       struct stat st;
-      if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
-        if (*entries >= size) {
-          size *= 2;
-          *names = reinterpret_cast<char**>(realloc(*names, size * sizeof(char*)));
-        }
-        (*names)[(*entries)++] = strdup(path);
+      if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+        files.insert(path);
       }
     }
-
-    closedir(d);
   }
 
-  printf("%d regular files in deletable directories\n", *entries);
-
-  if (EliminateOpenFiles(*names, *entries) < 0) {
-    return -1;
+  printf("%zu regular files in deletable directories\n", files.size());
+  if (EliminateOpenFiles(&files) < 0) {
+    return std::set<std::string>();
   }
-
-  return 0;
+  return files;
 }
 
 int MakeFreeSpaceOnCache(size_t bytes_needed) {
@@ -147,15 +118,8 @@
   if (free_now >= bytes_needed) {
     return 0;
   }
-
-  char** names;
-  int entries;
-
-  if (FindExpendableFiles(&names, &entries) < 0) {
-    return -1;
-  }
-
-  if (entries == 0) {
+  std::set<std::string> files = FindExpendableFiles();
+  if (files.empty()) {
     // nothing we can delete to free up space!
     printf("no files can be deleted to free space on /cache\n");
     return -1;
@@ -167,20 +131,13 @@
   //
   // Instead, we'll be dumb.
 
-  int i;
-  for (i = 0; i < entries && free_now < bytes_needed; ++i) {
-    if (names[i]) {
-      unlink(names[i]);
-      free_now = FreeSpaceForFile("/cache");
-      printf("deleted %s; now %zu bytes free\n", names[i], free_now);
-      free(names[i]);
+  for (const auto& file : files) {
+    unlink(file.c_str());
+    free_now = FreeSpaceForFile("/cache");
+    printf("deleted %s; now %zu bytes free\n", file.c_str(), free_now);
+    if (free_now < bytes_needed) {
+        break;
     }
   }
-
-  for (; i < entries; ++i) {
-    free(names[i]);
-  }
-  free(names);
-
   return (free_now >= bytes_needed) ? 0 : -1;
 }
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index 3e72b2c..8824038 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -21,12 +21,13 @@
 #include <sys/cdefs.h>
 #include <sys/stat.h>
 #include <errno.h>
-#include <malloc.h>
 #include <unistd.h>
 #include <string.h>
 
+#include <vector>
+
 #include "zlib.h"
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "applypatch.h"
 #include "imgdiff.h"
 #include "utils.h"
@@ -109,7 +110,7 @@
                 printf("failed to read chunk %d raw data\n", i);
                 return -1;
             }
-            if (ctx) SHA_update(ctx, patch->data + pos, data_len);
+            if (ctx) SHA1_Update(ctx, patch->data + pos, data_len);
             if (sink((unsigned char*)patch->data + pos,
                      data_len, token) != data_len) {
                 printf("failed to write chunk %d raw data\n", i);
@@ -129,7 +130,6 @@
             size_t src_len = Read8(deflate_header+8);
             size_t patch_offset = Read8(deflate_header+16);
             size_t expanded_len = Read8(deflate_header+24);
-            size_t target_len = Read8(deflate_header+32);
             int level = Read4(deflate_header+40);
             int method = Read4(deflate_header+44);
             int windowBits = Read4(deflate_header+48);
@@ -150,13 +150,7 @@
             // must be appended from the bonus_data value.
             size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0;
 
-            unsigned char* expanded_source = reinterpret_cast<unsigned char*>(malloc(expanded_len));
-            if (expanded_source == NULL) {
-                printf("failed to allocate %zu bytes for expanded_source\n",
-                       expanded_len);
-                return -1;
-            }
-
+            std::vector<unsigned char> expanded_source(expanded_len);
             z_stream strm;
             strm.zalloc = Z_NULL;
             strm.zfree = Z_NULL;
@@ -164,7 +158,7 @@
             strm.avail_in = src_len;
             strm.next_in = (unsigned char*)(old_data + src_start);
             strm.avail_out = expanded_len;
-            strm.next_out = expanded_source;
+            strm.next_out = expanded_source.data();
 
             int ret;
             ret = inflateInit2(&strm, -15);
@@ -189,18 +183,16 @@
             inflateEnd(&strm);
 
             if (bonus_size) {
-                memcpy(expanded_source + (expanded_len - bonus_size),
+                memcpy(expanded_source.data() + (expanded_len - bonus_size),
                        bonus_data->data, bonus_size);
             }
 
             // Next, apply the bsdiff patch (in memory) to the uncompressed
             // data.
-            unsigned char* uncompressed_target_data;
-            ssize_t uncompressed_target_size;
-            if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
+            std::vector<unsigned char> uncompressed_target_data;
+            if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len,
                                     patch, patch_offset,
-                                    &uncompressed_target_data,
-                                    &uncompressed_target_size) != 0) {
+                                    &uncompressed_target_data) != 0) {
                 return -1;
             }
 
@@ -208,40 +200,36 @@
 
             // we're done with the expanded_source data buffer, so we'll
             // reuse that memory to receive the output of deflate.
-            unsigned char* temp_data = expanded_source;
-            ssize_t temp_size = expanded_len;
-            if (temp_size < 32768) {
-                // ... unless the buffer is too small, in which case we'll
-                // allocate a fresh one.
-                free(temp_data);
-                temp_data = reinterpret_cast<unsigned char*>(malloc(32768));
-                temp_size = 32768;
+            if (expanded_source.size() < 32768U) {
+                expanded_source.resize(32768U);
             }
+            std::vector<unsigned char>& temp_data = expanded_source;
 
             // now the deflate stream
             strm.zalloc = Z_NULL;
             strm.zfree = Z_NULL;
             strm.opaque = Z_NULL;
-            strm.avail_in = uncompressed_target_size;
-            strm.next_in = uncompressed_target_data;
+            strm.avail_in = uncompressed_target_data.size();
+            strm.next_in = uncompressed_target_data.data();
             ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
+            if (ret != Z_OK) {
+                printf("failed to init uncompressed data deflation: %d\n", ret);
+                return -1;
+            }
             do {
-                strm.avail_out = temp_size;
-                strm.next_out = temp_data;
+                strm.avail_out = temp_data.size();
+                strm.next_out = temp_data.data();
                 ret = deflate(&strm, Z_FINISH);
-                ssize_t have = temp_size - strm.avail_out;
+                ssize_t have = temp_data.size() - strm.avail_out;
 
-                if (sink(temp_data, have, token) != have) {
+                if (sink(temp_data.data(), have, token) != have) {
                     printf("failed to write %ld compressed bytes to output\n",
                            (long)have);
                     return -1;
                 }
-                if (ctx) SHA_update(ctx, temp_data, have);
+                if (ctx) SHA1_Update(ctx, temp_data.data(), have);
             } while (ret != Z_STREAM_END);
             deflateEnd(&strm);
-
-            free(temp_data);
-            free(uncompressed_target_data);
         } else {
             printf("patch chunk %d is unknown type %d\n", i, type);
             return -1;
diff --git a/applypatch/main.cpp b/applypatch/main.cpp
index 966d8b9..7606d5d 100644
--- a/applypatch/main.cpp
+++ b/applypatch/main.cpp
@@ -19,9 +19,12 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <memory>
+#include <vector>
+
 #include "applypatch.h"
 #include "edify/expr.h"
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 
 static int CheckMode(int argc, char** argv) {
     if (argc < 3) {
@@ -47,16 +50,11 @@
 // "<sha1>:<filename>" into the new parallel arrays *sha1s and
 // *patches (loading file contents into the patches).  Returns true on
 // success.
-static bool ParsePatchArgs(int argc, char** argv, char*** sha1s,
-                           Value*** patches, int* num_patches) {
-    *num_patches = argc;
-    *sha1s = reinterpret_cast<char**>(malloc(*num_patches * sizeof(char*)));
-    *patches = reinterpret_cast<Value**>(malloc(*num_patches * sizeof(Value*)));
-    memset(*patches, 0, *num_patches * sizeof(Value*));
+static bool ParsePatchArgs(int argc, char** argv, std::vector<char*>* sha1s,
+                           std::vector<std::unique_ptr<Value, decltype(&FreeValue)>>* patches) {
+    uint8_t digest[SHA_DIGEST_LENGTH];
 
-    uint8_t digest[SHA_DIGEST_SIZE];
-
-    for (int i = 0; i < *num_patches; ++i) {
+    for (int i = 0; i < argc; ++i) {
         char* colon = strchr(argv[i], ':');
         if (colon != NULL) {
             *colon = '\0';
@@ -68,34 +66,22 @@
             return false;
         }
 
-        (*sha1s)[i] = argv[i];
+        sha1s->push_back(argv[i]);
         if (colon == NULL) {
-            (*patches)[i] = NULL;
+            patches->emplace_back(nullptr, FreeValue);
         } else {
             FileContents fc;
             if (LoadFileContents(colon, &fc) != 0) {
-                goto abort;
+                return false;
             }
-            (*patches)[i] = reinterpret_cast<Value*>(malloc(sizeof(Value)));
-            (*patches)[i]->type = VAL_BLOB;
-            (*patches)[i]->size = fc.size;
-            (*patches)[i]->data = reinterpret_cast<char*>(fc.data);
+            std::unique_ptr<Value, decltype(&FreeValue)> value(new Value, FreeValue);
+            value->type = VAL_BLOB;
+            value->size = fc.size;
+            value->data = reinterpret_cast<char*>(fc.data);
+            patches->push_back(std::move(value));
         }
     }
-
     return true;
-
-  abort:
-    for (int i = 0; i < *num_patches; ++i) {
-        Value* p = (*patches)[i];
-        if (p != NULL) {
-            free(p->data);
-            free(p);
-        }
-    }
-    free(*sha1s);
-    free(*patches);
-    return false;
 }
 
 static int FlashMode(const char* src_filename, const char* tgt_filename,
@@ -104,17 +90,17 @@
 }
 
 static int PatchMode(int argc, char** argv) {
-    Value* bonus = NULL;
+    std::unique_ptr<Value, decltype(&FreeValue)> bonus(nullptr, FreeValue);
     if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
         FileContents fc;
         if (LoadFileContents(argv[2], &fc) != 0) {
             printf("failed to load bonus file %s\n", argv[2]);
             return 1;
         }
-        bonus = reinterpret_cast<Value*>(malloc(sizeof(Value)));
+        bonus.reset(new Value);
         bonus->type = VAL_BLOB;
         bonus->size = fc.size;
-        bonus->data = (char*)fc.data;
+        bonus->data = reinterpret_cast<char*>(fc.data);
         argc -= 2;
         argv += 2;
     }
@@ -140,33 +126,20 @@
     }
 
 
-    char** sha1s;
-    Value** patches;
-    int num_patches;
-    if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &patches, &num_patches)) {
+    std::vector<char*> sha1s;
+    std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patches;
+    if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &patches)) {
         printf("failed to parse patch args\n");
         return 1;
     }
 
-    int result = applypatch(argv[1], argv[2], argv[3], target_size,
-                            num_patches, sha1s, patches, bonus);
-
-    int i;
-    for (i = 0; i < num_patches; ++i) {
-        Value* p = patches[i];
-        if (p != NULL) {
-            free(p->data);
-            free(p);
-        }
+    std::vector<Value*> patch_ptrs;
+    for (const auto& p : patches) {
+        patch_ptrs.push_back(p.get());
     }
-    if (bonus) {
-        free(bonus->data);
-        free(bonus);
-    }
-    free(sha1s);
-    free(patches);
-
-    return result;
+    return applypatch(argv[1], argv[2], argv[3], target_size,
+                      patch_ptrs.size(), sha1s.data(),
+                      patch_ptrs.data(), bonus.get());
 }
 
 // This program applies binary patches to files in a way that is safe
diff --git a/print_sha1.h b/print_sha1.h
index 9e37c5f..fa3d7e0 100644
--- a/print_sha1.h
+++ b/print_sha1.h
@@ -20,9 +20,9 @@
 #include <stdint.h>
 #include <string>
 
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 
-static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_SIZE], size_t len) {
+static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH], size_t len) {
     const char* hex = "0123456789abcdef";
     std::string result = "";
     for (size_t i = 0; i < len; ++i) {
@@ -32,11 +32,11 @@
     return result;
 }
 
-static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) {
-    return print_sha1(sha1, SHA_DIGEST_SIZE);
+static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) {
+    return print_sha1(sha1, SHA_DIGEST_LENGTH);
 }
 
-static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) {
+static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) {
     return print_sha1(sha1, 4);
 }
 
diff --git a/tests/Android.mk b/tests/Android.mk
index 4ce00b4..3f3c433 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -16,11 +16,40 @@
 
 LOCAL_PATH := $(call my-dir)
 
+# Unit tests
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_MODULE := recovery_unit_test
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_STATIC_LIBRARIES := libverifier
+LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp
+LOCAL_C_INCLUDES := bootable/recovery
+include $(BUILD_NATIVE_TEST)
+
+# Component tests
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_STATIC_LIBRARIES := libverifier
-LOCAL_SRC_FILES := asn1_decoder_test.cpp
-LOCAL_MODULE := asn1_decoder_test
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
+LOCAL_MODULE := recovery_component_test
+LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_SRC_FILES := component/verifier_test.cpp
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_STATIC_LIBRARIES := \
+    libbase \
+    libverifier \
+    libmincrypt \
+    libminui \
+    libminzip \
+    libcutils \
+    libc
+
+testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+testdata_files := $(call find-subdir-files, testdata/*)
+
+GEN := $(addprefix $(testdata_out_path)/, $(testdata_files))
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/%
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
 include $(BUILD_NATIVE_TEST)
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
new file mode 100644
index 0000000..7f7b1b4
--- /dev/null
+++ b/tests/component/verifier_test.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2009 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 agree 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 <errno.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "common.h"
+#include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
+#include "minzip/SysUtil.h"
+#include "ui.h"
+#include "verifier.h"
+
+#if defined(__LP64__)
+#define NATIVE_TEST_PATH "/nativetest64"
+#else
+#define NATIVE_TEST_PATH "/nativetest"
+#endif
+
+static const char* DATA_PATH = getenv("ANDROID_DATA");
+static const char* TESTDATA_PATH = "/recovery_component_test/testdata/";
+
+// This is build/target/product/security/testkey.x509.pem after being
+// dumped out by dumpkey.jar.
+RSAPublicKey test_key =
+    { 64, 0xc926ad21,
+      { 0x6afee91fu, 0x7fa31d5bu, 0x38a0b217u, 0x99df9baeu,
+        0xfe72991du, 0x727d3c04u, 0x20943f99u, 0xd08e7826u,
+        0x69e7c8a2u, 0xdeeccc8eu, 0x6b9af76fu, 0x553311c4u,
+        0x07b9e247u, 0x54c8bbcau, 0x6a540d81u, 0x48dbf567u,
+        0x98c92877u, 0x134fbfdeu, 0x01b32564u, 0x24581948u,
+        0x6cddc3b8u, 0x0cd444dau, 0xfe0381ccu, 0xf15818dfu,
+        0xc06e6d42u, 0x2e2f6412u, 0x093a6737u, 0x94d83b31u,
+        0xa466c87au, 0xb3f284a0u, 0xa694ec2cu, 0x053359e6u,
+        0x9717ee6au, 0x0732e080u, 0x220d5008u, 0xdc4af350u,
+        0x93d0a7c3u, 0xe330c9eau, 0xcac3da1eu, 0x8ebecf8fu,
+        0xc2be387fu, 0x38a14e89u, 0x211586f0u, 0x18b846f5u,
+        0x43be4c72u, 0xb578c204u, 0x1bbfb230u, 0xf1e267a8u,
+        0xa2d3e656u, 0x64b8e4feu, 0xe7e83d4bu, 0x3e77a943u,
+        0x3559ffd9u, 0x0ebb0f99u, 0x0aa76ce6u, 0xd3786ea7u,
+        0xbca8cd6bu, 0x068ca8e8u, 0xeb1de2ffu, 0x3e3ecd6cu,
+        0xe0d9d825u, 0xb1edc762u, 0xdec60b24u, 0xd6931904u},
+      { 0xccdcb989u, 0xe19281f9u, 0xa6e80accu, 0xb7f40560u,
+        0x0efb0bccu, 0x7f12b0bbu, 0x1e90531au, 0x136d95d0u,
+        0x9e660665u, 0x7d54918fu, 0xe3b93ea2u, 0x2f415d10u,
+        0x3d2df6e6u, 0x7a627ecfu, 0xa6f22d70u, 0xb995907au,
+        0x09de16b2u, 0xfeb8bd61u, 0xf24ec294u, 0x716a427fu,
+        0x2e12046fu, 0xeaf3d56au, 0xd9b873adu, 0x0ced340bu,
+        0xbc9cec09u, 0x73c65903u, 0xee39ce9bu, 0x3eede25au,
+        0x397633b7u, 0x2583c165u, 0x8514f97du, 0xe9166510u,
+        0x0b6fae99u, 0xa47139fdu, 0xdb8352f0u, 0xb2ad7f2cu,
+        0xa11552e2u, 0xd4d490a7u, 0xe11e8568u, 0xe9e484dau,
+        0xd3ef8449u, 0xa47055dau, 0x4edd9557u, 0x03a78ba1u,
+        0x770e130du, 0x16762facu, 0x0cbdfcc4u, 0xf3070540u,
+        0x008b6515u, 0x60e7e1b7u, 0xa72cf7f9u, 0xaff86e39u,
+        0x4296faadu, 0xfc90430eu, 0x6cc8f377u, 0xb398fd43u,
+        0x423c5997u, 0x991d59c4u, 0x6464bf73u, 0x96431575u,
+        0x15e3d207u, 0x30532a7au, 0x8c4be618u, 0x460a4d76u },
+      3
+    };
+
+RSAPublicKey test_f4_key =
+    { 64, 0xc9bd1f21,
+      { 0x1178db1fu, 0xbf5d0e55u, 0x3393a165u, 0x0ef4c287u,
+        0xbc472a4au, 0x383fc5a1u, 0x4a13b7d2u, 0xb1ff2ac3u,
+        0xaf66b4d9u, 0x9280acefu, 0xa2165bdbu, 0x6a4d6e5cu,
+        0x08ea676bu, 0xb7ac70c7u, 0xcd158139u, 0xa635ccfeu,
+        0xa46ab8a8u, 0x445a3e8bu, 0xdc81d9bbu, 0x91ce1a20u,
+        0x68021cdeu, 0x4516eda9u, 0x8d43c30cu, 0xed1eff14u,
+        0xca387e4cu, 0x58adc233u, 0x4657ab27u, 0xa95b521eu,
+        0xdfc0e30cu, 0x394d64a1u, 0xc6b321a1u, 0x2ca22cb8u,
+        0xb1892d5cu, 0x5d605f3eu, 0x6025483cu, 0x9afd5181u,
+        0x6e1a7105u, 0x03010593u, 0x70acd304u, 0xab957cbfu,
+        0x8844abbbu, 0x53846837u, 0x24e98a43u, 0x2ba060c1u,
+        0x8b88b88eu, 0x44eea405u, 0xb259fc41u, 0x0907ad9cu,
+        0x13003adau, 0xcf79634eu, 0x7d314ec9u, 0xfbbe4c2bu,
+        0xd84d0823u, 0xfd30fd88u, 0x68d8a909u, 0xfb4572d9u,
+        0xa21301c2u, 0xd00a4785u, 0x6862b50cu, 0xcfe49796u,
+        0xdaacbd83u, 0xfb620906u, 0xdf71e0ccu, 0xbbc5b030u },
+      { 0x69a82189u, 0x1a8b22f4u, 0xcf49207bu, 0x68cc056au,
+        0xb206b7d2u, 0x1d449bbdu, 0xe9d342f2u, 0x29daea58u,
+        0xb19d011au, 0xc62f15e4u, 0x9452697au, 0xb62bb87eu,
+        0x60f95cc2u, 0x279ebb2du, 0x17c1efd8u, 0xec47558bu,
+        0xc81334d1u, 0x88fe7601u, 0x79992eb1u, 0xb4555615u,
+        0x2022ac8cu, 0xc79a4b8cu, 0xb288b034u, 0xd6b942f0u,
+        0x0caa32fbu, 0xa065ba51u, 0x4de9f154u, 0x29f64f6cu,
+        0x7910af5eu, 0x3ed4636au, 0xe4c81911u, 0x9183f37du,
+        0x5811e1c4u, 0x29c7a58cu, 0x9715d4d3u, 0xc7e2dce3u,
+        0x140972ebu, 0xf4c8a69eu, 0xa104d424u, 0x5dabbdfbu,
+        0x41cb4c6bu, 0xd7f44717u, 0x61785ff7u, 0x5e0bc273u,
+        0x36426c70u, 0x2aa6f08eu, 0x083badbfu, 0x3cab941bu,
+        0x8871da23u, 0x1ab3dbaeu, 0x7115a21du, 0xf5aa0965u,
+        0xf766f562u, 0x7f110225u, 0x86d96a04u, 0xc50a120eu,
+        0x3a751ca3u, 0xc21aa186u, 0xba7359d0u, 0x3ff2b257u,
+        0xd116e8bbu, 0xfc1318c0u, 0x070e5b1du, 0x83b759a6u },
+      65537
+    };
+
+ECPublicKey test_ec_key =
+    {
+       {
+         {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu,
+          0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u}
+      },
+      {
+        {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au,
+         0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u}
+      }
+    };
+
+RecoveryUI* ui = NULL;
+
+class MockUI : public RecoveryUI {
+    void Init() { }
+    void SetStage(int, int) { }
+    void SetLocale(const char*) { }
+    void SetBackground(Icon icon) { }
+
+    void SetProgressType(ProgressType determinate) { }
+    void ShowProgress(float portion, float seconds) { }
+    void SetProgress(float fraction) { }
+
+    void ShowText(bool visible) { }
+    bool IsTextVisible() { return false; }
+    bool WasTextEverVisible() { return false; }
+    void Print(const char* fmt, ...) {
+        va_list ap;
+        va_start(ap, fmt);
+        vfprintf(stderr, fmt, ap);
+        va_end(ap);
+    }
+    void PrintOnScreenOnly(const char* fmt, ...) {
+        va_list ap;
+        va_start(ap, fmt);
+        vfprintf(stderr, fmt, ap);
+        va_end(ap);
+    }
+    void ShowFile(const char*) { }
+
+    void StartMenu(const char* const * headers, const char* const * items,
+                           int initial_selection) { }
+    int SelectMenu(int sel) { return 0; }
+    void EndMenu() { }
+};
+
+void
+ui_print(const char* format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    vfprintf(stdout, format, ap);
+    va_end(ap);
+}
+
+class VerifierTest : public testing::TestWithParam<std::vector<std::string>> {
+  public:
+    MemMapping memmap;
+    std::vector<Certificate> certs;
+
+    virtual void SetUp() {
+        std::vector<std::string> args = GetParam();
+        std::string package = android::base::StringPrintf("%s%s%s%s", DATA_PATH, NATIVE_TEST_PATH,
+                TESTDATA_PATH, args[0].c_str());
+        for (auto it = ++(args.cbegin()); it != args.cend(); ++it) {
+            if (it->substr(it->length() - 3, it->length()) == "256") {
+                if (certs.empty()) {
+                    FAIL() << "May only specify -sha256 after key type\n";
+                }
+                certs.back().hash_len = SHA256_DIGEST_SIZE;
+            } else if (*it == "ec") {
+                certs.emplace_back(SHA_DIGEST_SIZE, Certificate::EC,
+                        nullptr, std::unique_ptr<ECPublicKey>(new ECPublicKey(test_ec_key)));
+            } else if (*it == "e3") {
+                certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
+                        std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_key)), nullptr);
+            } else if (*it == "f4") {
+                certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
+                        std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_f4_key)), nullptr);
+            }
+        }
+        if (certs.empty()) {
+            certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
+                    std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_key)), nullptr);
+        }
+        if (sysMapFile(package.c_str(), &memmap) != 0) {
+            FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
+        }
+    }
+
+    static void SetUpTestCase() {
+        ui = new MockUI();
+    }
+};
+
+class VerifierSuccessTest : public VerifierTest {
+};
+
+class VerifierFailureTest : public VerifierTest {
+};
+
+TEST_P(VerifierSuccessTest, VerifySucceed) {
+    ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_SUCCESS);
+}
+
+TEST_P(VerifierFailureTest, VerifyFailure) {
+    ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_FAILURE);
+}
+
+INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest,
+        ::testing::Values(
+            std::vector<std::string>({"otasigned.zip", "e3"}),
+            std::vector<std::string>({"otasigned_f4.zip", "f4"}),
+            std::vector<std::string>({"otasigned_sha256.zip", "e3", "sha256"}),
+            std::vector<std::string>({"otasigned_f4_sha256.zip", "f4", "sha256"}),
+            std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "ec", "sha256"})));
+
+INSTANTIATE_TEST_CASE_P(MultiKeySuccess, VerifierSuccessTest,
+        ::testing::Values(
+            std::vector<std::string>({"otasigned.zip", "f4", "e3"}),
+            std::vector<std::string>({"otasigned_f4.zip", "ec", "f4"}),
+            std::vector<std::string>({"otasigned_sha256.zip", "ec", "e3", "e3", "sha256"}),
+            std::vector<std::string>({"otasigned_f4_sha256.zip", "ec", "sha256", "e3", "f4", "sha256"}),
+            std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "f4", "sha256", "e3", "ec", "sha256"})));
+
+INSTANTIATE_TEST_CASE_P(WrongKey, VerifierFailureTest,
+        ::testing::Values(
+            std::vector<std::string>({"otasigned.zip", "f4"}),
+            std::vector<std::string>({"otasigned_f4.zip", "e3"}),
+            std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "e3", "sha256"})));
+
+INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest,
+        ::testing::Values(
+            std::vector<std::string>({"otasigned.zip", "e3", "sha256"}),
+            std::vector<std::string>({"otasigned_f4.zip", "f4", "sha256"}),
+            std::vector<std::string>({"otasigned_sha256.zip"}),
+            std::vector<std::string>({"otasigned_f4_sha256.zip", "f4"}),
+            std::vector<std::string>({"otasigned_ecdsa_sha256.zip"})));
+
+INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest,
+        ::testing::Values(
+            std::vector<std::string>({"random.zip"}),
+            std::vector<std::string>({"fake-eocd.zip"}),
+            std::vector<std::string>({"alter-metadata.zip"}),
+            std::vector<std::string>({"alter-footer.zip"})));
diff --git a/testdata/alter-footer.zip b/tests/testdata/alter-footer.zip
similarity index 100%
rename from testdata/alter-footer.zip
rename to tests/testdata/alter-footer.zip
Binary files differ
diff --git a/testdata/alter-metadata.zip b/tests/testdata/alter-metadata.zip
similarity index 100%
rename from testdata/alter-metadata.zip
rename to tests/testdata/alter-metadata.zip
Binary files differ
diff --git a/testdata/fake-eocd.zip b/tests/testdata/fake-eocd.zip
similarity index 100%
rename from testdata/fake-eocd.zip
rename to tests/testdata/fake-eocd.zip
Binary files differ
diff --git a/testdata/jarsigned.zip b/tests/testdata/jarsigned.zip
similarity index 100%
rename from testdata/jarsigned.zip
rename to tests/testdata/jarsigned.zip
Binary files differ
diff --git a/testdata/otasigned.zip b/tests/testdata/otasigned.zip
similarity index 100%
rename from testdata/otasigned.zip
rename to tests/testdata/otasigned.zip
Binary files differ
diff --git a/testdata/otasigned_ecdsa_sha256.zip b/tests/testdata/otasigned_ecdsa_sha256.zip
similarity index 100%
rename from testdata/otasigned_ecdsa_sha256.zip
rename to tests/testdata/otasigned_ecdsa_sha256.zip
Binary files differ
diff --git a/testdata/otasigned_f4.zip b/tests/testdata/otasigned_f4.zip
similarity index 100%
rename from testdata/otasigned_f4.zip
rename to tests/testdata/otasigned_f4.zip
Binary files differ
diff --git a/testdata/otasigned_f4_sha256.zip b/tests/testdata/otasigned_f4_sha256.zip
similarity index 100%
rename from testdata/otasigned_f4_sha256.zip
rename to tests/testdata/otasigned_f4_sha256.zip
Binary files differ
diff --git a/testdata/otasigned_sha256.zip b/tests/testdata/otasigned_sha256.zip
similarity index 100%
rename from testdata/otasigned_sha256.zip
rename to tests/testdata/otasigned_sha256.zip
Binary files differ
diff --git a/testdata/random.zip b/tests/testdata/random.zip
similarity index 100%
rename from testdata/random.zip
rename to tests/testdata/random.zip
Binary files differ
diff --git a/testdata/test_f4.pk8 b/tests/testdata/test_f4.pk8
similarity index 100%
rename from testdata/test_f4.pk8
rename to tests/testdata/test_f4.pk8
Binary files differ
diff --git a/testdata/test_f4.x509.pem b/tests/testdata/test_f4.x509.pem
similarity index 100%
rename from testdata/test_f4.x509.pem
rename to tests/testdata/test_f4.x509.pem
diff --git a/testdata/test_f4_sha256.x509.pem b/tests/testdata/test_f4_sha256.x509.pem
similarity index 100%
rename from testdata/test_f4_sha256.x509.pem
rename to tests/testdata/test_f4_sha256.x509.pem
diff --git a/testdata/testkey.pk8 b/tests/testdata/testkey.pk8
similarity index 100%
rename from testdata/testkey.pk8
rename to tests/testdata/testkey.pk8
Binary files differ
diff --git a/testdata/testkey.x509.pem b/tests/testdata/testkey.x509.pem
similarity index 100%
rename from testdata/testkey.x509.pem
rename to tests/testdata/testkey.x509.pem
diff --git a/testdata/testkey_ecdsa.pk8 b/tests/testdata/testkey_ecdsa.pk8
similarity index 100%
rename from testdata/testkey_ecdsa.pk8
rename to tests/testdata/testkey_ecdsa.pk8
Binary files differ
diff --git a/testdata/testkey_ecdsa.x509.pem b/tests/testdata/testkey_ecdsa.x509.pem
similarity index 100%
rename from testdata/testkey_ecdsa.x509.pem
rename to tests/testdata/testkey_ecdsa.x509.pem
diff --git a/testdata/testkey_sha256.x509.pem b/tests/testdata/testkey_sha256.x509.pem
similarity index 100%
rename from testdata/testkey_sha256.x509.pem
rename to tests/testdata/testkey_sha256.x509.pem
diff --git a/testdata/unsigned.zip b/tests/testdata/unsigned.zip
similarity index 100%
rename from testdata/unsigned.zip
rename to tests/testdata/unsigned.zip
Binary files differ
diff --git a/tests/asn1_decoder_test.cpp b/tests/unit/asn1_decoder_test.cpp
similarity index 100%
rename from tests/asn1_decoder_test.cpp
rename to tests/unit/asn1_decoder_test.cpp
diff --git a/updater/Android.mk b/updater/Android.mk
index d74dffc..d7aa613 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -46,7 +46,7 @@
 
 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
 LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libedify libmtdutils libminzip libz
-LOCAL_STATIC_LIBRARIES += libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libbz
 LOCAL_STATIC_LIBRARIES += libcutils liblog libc
 LOCAL_STATIC_LIBRARIES += libselinux
 tune2fs_static_libraries := \
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index c898ac6..e9c8ddb 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -43,7 +43,7 @@
 #include "applypatch/applypatch.h"
 #include "edify/expr.h"
 #include "install.h"
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "minzip/Hash.h"
 #include "otafault/ota_io.h"
 #include "print_sha1.h"
@@ -408,10 +408,10 @@
 
 static int VerifyBlocks(const std::string& expected, const std::vector<uint8_t>& buffer,
         const size_t blocks, bool printerror) {
-    uint8_t digest[SHA_DIGEST_SIZE];
+    uint8_t digest[SHA_DIGEST_LENGTH];
     const uint8_t* data = buffer.data();
 
-    SHA_hash(data, blocks * BLOCKSIZE, digest);
+    SHA1(data, blocks * BLOCKSIZE, digest);
 
     std::string hexdigest = print_sha1(digest);
 
@@ -663,10 +663,8 @@
     // Stash directory should be different for each partition to avoid conflicts
     // when updating multiple partitions at the same time, so we use the hash of
     // the block device name as the base directory
-    SHA_CTX ctx;
-    SHA_init(&ctx);
-    SHA_update(&ctx, blockdev, strlen(blockdev));
-    const uint8_t* digest = SHA_final(&ctx);
+    uint8_t digest[SHA_DIGEST_LENGTH];
+    SHA1(reinterpret_cast<const uint8_t*>(blockdev), strlen(blockdev), digest);
     base = print_sha1(digest);
 
     std::string dirname = GetStashFileName(base, "", "");
@@ -1628,7 +1626,7 @@
     parse_range(ranges->data, rs);
 
     SHA_CTX ctx;
-    SHA_init(&ctx);
+    SHA1_Init(&ctx);
 
     std::vector<uint8_t> buffer(BLOCKSIZE);
     for (size_t i = 0; i < rs.count; ++i) {
@@ -1644,10 +1642,11 @@
                 return StringValue(strdup(""));
             }
 
-            SHA_update(&ctx, buffer.data(), BLOCKSIZE);
+            SHA1_Update(&ctx, buffer.data(), BLOCKSIZE);
         }
     }
-    const uint8_t* digest = SHA_final(&ctx);
+    uint8_t digest[SHA_DIGEST_LENGTH];
+    SHA1_Final(digest, &ctx);
 
     return StringValue(strdup(print_sha1(digest).c_str()));
 }
diff --git a/updater/install.cpp b/updater/install.cpp
index be97355..a2efc0b 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -34,6 +34,9 @@
 #include <linux/xattr.h>
 #include <inttypes.h>
 
+#include <memory>
+#include <vector>
+
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
@@ -44,7 +47,7 @@
 #include "cutils/misc.h"
 #include "cutils/properties.h"
 #include "edify/expr.h"
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "minzip/DirUtil.h"
 #include "mtdutils/mounts.h"
 #include "mtdutils/mtdutils.h"
@@ -92,10 +95,10 @@
 
 // Take a sha-1 digest and return it as a newly-allocated hex string.
 char* PrintSha1(const uint8_t* digest) {
-    char* buffer = reinterpret_cast<char*>(malloc(SHA_DIGEST_SIZE*2 + 1));
+    char* buffer = reinterpret_cast<char*>(malloc(SHA_DIGEST_LENGTH*2 + 1));
     const char* alphabet = "0123456789abcdef";
     size_t i;
-    for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+    for (i = 0; i < SHA_DIGEST_LENGTH; ++i) {
         buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
         buffer[i*2+1] = alphabet[digest[i] & 0xf];
     }
@@ -440,8 +443,7 @@
     for (int i = 0; i < argc; ++i) {
         paths[i] = Evaluate(state, argv[i]);
         if (paths[i] == NULL) {
-            int j;
-            for (j = 0; j < i; ++i) {
+            for (int j = 0; j < i; ++j) {
                 free(paths[j]);
             }
             free(paths);
@@ -582,13 +584,13 @@
         // as the result.
 
         char* zip_path;
+        if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL;
+
         Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value)));
         v->type = VAL_BLOB;
         v->size = -1;
         v->data = NULL;
 
-        if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL;
-
         ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
         const ZipEntry* entry = mzFindZipEntry(za, zip_path);
         if (entry == NULL) {
@@ -1194,44 +1196,40 @@
     }
 
     int patchcount = (argc-4) / 2;
-    Value** patches = ReadValueVarArgs(state, argc-4, argv+4);
+    std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc-4, argv+4),
+                                                        free);
+    if (!arg_values) {
+        return nullptr;
+    }
+    std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patch_shas;
+    std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patches;
+    // Protect values by unique_ptrs first to get rid of memory leak.
+    for (int i = 0; i < patchcount * 2; i += 2) {
+        patch_shas.emplace_back(arg_values.get()[i], FreeValue);
+        patches.emplace_back(arg_values.get()[i+1], FreeValue);
+    }
 
-    int i;
-    for (i = 0; i < patchcount; ++i) {
-        if (patches[i*2]->type != VAL_STRING) {
+    for (int i = 0; i < patchcount; ++i) {
+        if (patch_shas[i]->type != VAL_STRING) {
             ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i);
-            break;
+            return nullptr;
         }
-        if (patches[i*2+1]->type != VAL_BLOB) {
+        if (patches[i]->type != VAL_BLOB) {
             ErrorAbort(state, "%s(): patch #%d is not blob", name, i);
-            break;
+            return nullptr;
         }
     }
-    if (i != patchcount) {
-        for (i = 0; i < patchcount*2; ++i) {
-            FreeValue(patches[i]);
-        }
-        free(patches);
-        return NULL;
-    }
 
-    char** patch_sha_str = reinterpret_cast<char**>(malloc(patchcount * sizeof(char*)));
-    for (i = 0; i < patchcount; ++i) {
-        patch_sha_str[i] = patches[i*2]->data;
-        patches[i*2]->data = NULL;
-        FreeValue(patches[i*2]);
-        patches[i] = patches[i*2+1];
+    std::vector<char*> patch_sha_str;
+    std::vector<Value*> patch_ptrs;
+    for (int i = 0; i < patchcount; ++i) {
+        patch_sha_str.push_back(patch_shas[i]->data);
+        patch_ptrs.push_back(patches[i].get());
     }
 
     int result = applypatch(source_filename, target_filename,
                             target_sha1, target_size,
-                            patchcount, patch_sha_str, patches, NULL);
-
-    for (i = 0; i < patchcount; ++i) {
-        FreeValue(patches[i]);
-    }
-    free(patch_sha_str);
-    free(patches);
+                            patchcount, patch_sha_str.data(), patch_ptrs.data(), NULL);
 
     return StringValue(strdup(result == 0 ? "t" : ""));
 }
@@ -1350,24 +1348,27 @@
         return ErrorAbort(state, "%s() expects at least 1 arg", name);
     }
 
-    Value** args = ReadValueVarArgs(state, argc, argv);
-    if (args == NULL) {
-        return NULL;
+    std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc, argv), free);
+    if (arg_values == nullptr) {
+        return nullptr;
+    }
+    std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> args;
+    for (int i = 0; i < argc; ++i) {
+        args.emplace_back(arg_values.get()[i], FreeValue);
     }
 
     if (args[0]->size < 0) {
         return StringValue(strdup(""));
     }
-    uint8_t digest[SHA_DIGEST_SIZE];
-    SHA_hash(args[0]->data, args[0]->size, digest);
-    FreeValue(args[0]);
+    uint8_t digest[SHA_DIGEST_LENGTH];
+    SHA1(reinterpret_cast<uint8_t*>(args[0]->data), args[0]->size, digest);
 
     if (argc == 1) {
         return StringValue(PrintSha1(digest));
     }
 
     int i;
-    uint8_t* arg_digest = reinterpret_cast<uint8_t*>(malloc(SHA_DIGEST_SIZE));
+    uint8_t arg_digest[SHA_DIGEST_LENGTH];
     for (i = 1; i < argc; ++i) {
         if (args[i]->type != VAL_STRING) {
             printf("%s(): arg %d is not a string; skipping",
@@ -1376,22 +1377,16 @@
             // Warn about bad args and skip them.
             printf("%s(): error parsing \"%s\" as sha-1; skipping",
                    name, args[i]->data);
-        } else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) {
+        } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) {
             break;
         }
-        FreeValue(args[i]);
     }
     if (i >= argc) {
         // Didn't match any of the hex strings; return false.
         return StringValue(strdup(""));
     }
-    // Found a match; free all the remaining arguments and return the
-    // matched one.
-    int j;
-    for (j = i+1; j < argc; ++j) {
-        FreeValue(args[j]);
-    }
-    return args[i];
+    // Found a match.
+    return args[i].release();
 }
 
 // Read a local file and return its contents (the Value* returned
diff --git a/verifier_test.cpp b/verifier_test.cpp
deleted file mode 100644
index 2367e00..0000000
--- a/verifier_test.cpp
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Copyright (C) 2009 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 <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <memory>
-#include <vector>
-
-#include "common.h"
-#include "verifier.h"
-#include "ui.h"
-#include "mincrypt/sha.h"
-#include "mincrypt/sha256.h"
-#include "minzip/SysUtil.h"
-
-// This is build/target/product/security/testkey.x509.pem after being
-// dumped out by dumpkey.jar.
-RSAPublicKey test_key =
-    { 64, 0xc926ad21,
-      { 0x6afee91fu, 0x7fa31d5bu, 0x38a0b217u, 0x99df9baeu,
-        0xfe72991du, 0x727d3c04u, 0x20943f99u, 0xd08e7826u,
-        0x69e7c8a2u, 0xdeeccc8eu, 0x6b9af76fu, 0x553311c4u,
-        0x07b9e247u, 0x54c8bbcau, 0x6a540d81u, 0x48dbf567u,
-        0x98c92877u, 0x134fbfdeu, 0x01b32564u, 0x24581948u,
-        0x6cddc3b8u, 0x0cd444dau, 0xfe0381ccu, 0xf15818dfu,
-        0xc06e6d42u, 0x2e2f6412u, 0x093a6737u, 0x94d83b31u,
-        0xa466c87au, 0xb3f284a0u, 0xa694ec2cu, 0x053359e6u,
-        0x9717ee6au, 0x0732e080u, 0x220d5008u, 0xdc4af350u,
-        0x93d0a7c3u, 0xe330c9eau, 0xcac3da1eu, 0x8ebecf8fu,
-        0xc2be387fu, 0x38a14e89u, 0x211586f0u, 0x18b846f5u,
-        0x43be4c72u, 0xb578c204u, 0x1bbfb230u, 0xf1e267a8u,
-        0xa2d3e656u, 0x64b8e4feu, 0xe7e83d4bu, 0x3e77a943u,
-        0x3559ffd9u, 0x0ebb0f99u, 0x0aa76ce6u, 0xd3786ea7u,
-        0xbca8cd6bu, 0x068ca8e8u, 0xeb1de2ffu, 0x3e3ecd6cu,
-        0xe0d9d825u, 0xb1edc762u, 0xdec60b24u, 0xd6931904u},
-      { 0xccdcb989u, 0xe19281f9u, 0xa6e80accu, 0xb7f40560u,
-        0x0efb0bccu, 0x7f12b0bbu, 0x1e90531au, 0x136d95d0u,
-        0x9e660665u, 0x7d54918fu, 0xe3b93ea2u, 0x2f415d10u,
-        0x3d2df6e6u, 0x7a627ecfu, 0xa6f22d70u, 0xb995907au,
-        0x09de16b2u, 0xfeb8bd61u, 0xf24ec294u, 0x716a427fu,
-        0x2e12046fu, 0xeaf3d56au, 0xd9b873adu, 0x0ced340bu,
-        0xbc9cec09u, 0x73c65903u, 0xee39ce9bu, 0x3eede25au,
-        0x397633b7u, 0x2583c165u, 0x8514f97du, 0xe9166510u,
-        0x0b6fae99u, 0xa47139fdu, 0xdb8352f0u, 0xb2ad7f2cu,
-        0xa11552e2u, 0xd4d490a7u, 0xe11e8568u, 0xe9e484dau,
-        0xd3ef8449u, 0xa47055dau, 0x4edd9557u, 0x03a78ba1u,
-        0x770e130du, 0x16762facu, 0x0cbdfcc4u, 0xf3070540u,
-        0x008b6515u, 0x60e7e1b7u, 0xa72cf7f9u, 0xaff86e39u,
-        0x4296faadu, 0xfc90430eu, 0x6cc8f377u, 0xb398fd43u,
-        0x423c5997u, 0x991d59c4u, 0x6464bf73u, 0x96431575u,
-        0x15e3d207u, 0x30532a7au, 0x8c4be618u, 0x460a4d76u },
-      3
-    };
-
-RSAPublicKey test_f4_key =
-    { 64, 0xc9bd1f21,
-      { 0x1178db1fu, 0xbf5d0e55u, 0x3393a165u, 0x0ef4c287u,
-        0xbc472a4au, 0x383fc5a1u, 0x4a13b7d2u, 0xb1ff2ac3u,
-        0xaf66b4d9u, 0x9280acefu, 0xa2165bdbu, 0x6a4d6e5cu,
-        0x08ea676bu, 0xb7ac70c7u, 0xcd158139u, 0xa635ccfeu,
-        0xa46ab8a8u, 0x445a3e8bu, 0xdc81d9bbu, 0x91ce1a20u,
-        0x68021cdeu, 0x4516eda9u, 0x8d43c30cu, 0xed1eff14u,
-        0xca387e4cu, 0x58adc233u, 0x4657ab27u, 0xa95b521eu,
-        0xdfc0e30cu, 0x394d64a1u, 0xc6b321a1u, 0x2ca22cb8u,
-        0xb1892d5cu, 0x5d605f3eu, 0x6025483cu, 0x9afd5181u,
-        0x6e1a7105u, 0x03010593u, 0x70acd304u, 0xab957cbfu,
-        0x8844abbbu, 0x53846837u, 0x24e98a43u, 0x2ba060c1u,
-        0x8b88b88eu, 0x44eea405u, 0xb259fc41u, 0x0907ad9cu,
-        0x13003adau, 0xcf79634eu, 0x7d314ec9u, 0xfbbe4c2bu,
-        0xd84d0823u, 0xfd30fd88u, 0x68d8a909u, 0xfb4572d9u,
-        0xa21301c2u, 0xd00a4785u, 0x6862b50cu, 0xcfe49796u,
-        0xdaacbd83u, 0xfb620906u, 0xdf71e0ccu, 0xbbc5b030u },
-      { 0x69a82189u, 0x1a8b22f4u, 0xcf49207bu, 0x68cc056au,
-        0xb206b7d2u, 0x1d449bbdu, 0xe9d342f2u, 0x29daea58u,
-        0xb19d011au, 0xc62f15e4u, 0x9452697au, 0xb62bb87eu,
-        0x60f95cc2u, 0x279ebb2du, 0x17c1efd8u, 0xec47558bu,
-        0xc81334d1u, 0x88fe7601u, 0x79992eb1u, 0xb4555615u,
-        0x2022ac8cu, 0xc79a4b8cu, 0xb288b034u, 0xd6b942f0u,
-        0x0caa32fbu, 0xa065ba51u, 0x4de9f154u, 0x29f64f6cu,
-        0x7910af5eu, 0x3ed4636au, 0xe4c81911u, 0x9183f37du,
-        0x5811e1c4u, 0x29c7a58cu, 0x9715d4d3u, 0xc7e2dce3u,
-        0x140972ebu, 0xf4c8a69eu, 0xa104d424u, 0x5dabbdfbu,
-        0x41cb4c6bu, 0xd7f44717u, 0x61785ff7u, 0x5e0bc273u,
-        0x36426c70u, 0x2aa6f08eu, 0x083badbfu, 0x3cab941bu,
-        0x8871da23u, 0x1ab3dbaeu, 0x7115a21du, 0xf5aa0965u,
-        0xf766f562u, 0x7f110225u, 0x86d96a04u, 0xc50a120eu,
-        0x3a751ca3u, 0xc21aa186u, 0xba7359d0u, 0x3ff2b257u,
-        0xd116e8bbu, 0xfc1318c0u, 0x070e5b1du, 0x83b759a6u },
-      65537
-    };
-
-ECPublicKey test_ec_key =
-    {
-       {
-         {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu,
-          0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u}
-      },
-      {
-        {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au,
-         0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u}
-      }
-    };
-
-RecoveryUI* ui = NULL;
-
-// verifier expects to find a UI object; we provide one that does
-// nothing but print.
-class FakeUI : public RecoveryUI {
-    void Init() { }
-    void SetStage(int, int) { }
-    void SetLocale(const char*) { }
-    void SetBackground(Icon icon) { }
-
-    void SetProgressType(ProgressType determinate) { }
-    void ShowProgress(float portion, float seconds) { }
-    void SetProgress(float fraction) { }
-
-    void ShowText(bool visible) { }
-    bool IsTextVisible() { return false; }
-    bool WasTextEverVisible() { return false; }
-    void Print(const char* fmt, ...) {
-        va_list ap;
-        va_start(ap, fmt);
-        vfprintf(stderr, fmt, ap);
-        va_end(ap);
-    }
-    void PrintOnScreenOnly(const char* fmt, ...) {
-        va_list ap;
-        va_start(ap, fmt);
-        vfprintf(stderr, fmt, ap);
-        va_end(ap);
-    }
-    void ShowFile(const char*) { }
-
-    void StartMenu(const char* const * headers, const char* const * items,
-                           int initial_selection) { }
-    int SelectMenu(int sel) { return 0; }
-    void EndMenu() { }
-};
-
-void
-ui_print(const char* format, ...) {
-    va_list ap;
-    va_start(ap, format);
-    vfprintf(stdout, format, ap);
-    va_end(ap);
-}
-
-int main(int argc, char** argv) {
-    if (argc < 2) {
-        fprintf(stderr, "Usage: %s [-sha256] [-ec | -f4 | -file <keys>] <package>\n", argv[0]);
-        return 2;
-    }
-
-    std::vector<Certificate> certs;
-    int argn = 1;
-    while (argn < argc) {
-        if (strcmp(argv[argn], "-sha256") == 0) {
-            if (certs.empty()) {
-                fprintf(stderr, "May only specify -sha256 after key type\n");
-                return 2;
-            }
-            ++argn;
-            certs.back().hash_len = SHA256_DIGEST_SIZE;
-        } else if (strcmp(argv[argn], "-ec") == 0) {
-            ++argn;
-            certs.emplace_back(SHA_DIGEST_SIZE, Certificate::EC,
-                    nullptr, std::unique_ptr<ECPublicKey>(new ECPublicKey(test_ec_key)));
-        } else if (strcmp(argv[argn], "-e3") == 0) {
-            ++argn;
-            certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
-                    std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_key)), nullptr);
-        } else if (strcmp(argv[argn], "-f4") == 0) {
-            ++argn;
-            certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
-                    std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_f4_key)), nullptr);
-        } else if (strcmp(argv[argn], "-file") == 0) {
-            if (!certs.empty()) {
-                fprintf(stderr, "Cannot specify -file with other certs specified\n");
-                return 2;
-            }
-            ++argn;
-            if (!load_keys(argv[argn], certs)) {
-                fprintf(stderr, "Cannot load keys from %s\n", argv[argn]);
-            }
-            ++argn;
-        } else if (argv[argn][0] == '-') {
-            fprintf(stderr, "Unknown argument %s\n", argv[argn]);
-            return 2;
-        } else {
-            break;
-        }
-    }
-
-    if (argn == argc) {
-        fprintf(stderr, "Must specify package to verify\n");
-        return 2;
-    }
-
-    if (certs.empty()) {
-        certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
-                std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_key)), nullptr);
-    }
-
-    ui = new FakeUI();
-
-    MemMapping map;
-    if (sysMapFile(argv[argn], &map) != 0) {
-        fprintf(stderr, "failed to mmap %s: %s\n", argv[argn], strerror(errno));
-        return 4;
-    }
-
-    int result = verify_file(map.addr, map.length, certs);
-    if (result == VERIFY_SUCCESS) {
-        printf("VERIFIED\n");
-        return 0;
-    } else if (result == VERIFY_FAILURE) {
-        printf("NOT VERIFIED\n");
-        return 1;
-    } else {
-        printf("bad return value\n");
-        return 3;
-    }
-}
diff --git a/verifier_test.sh b/verifier_test.sh
deleted file mode 100755
index 4761cef..0000000
--- a/verifier_test.sh
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/bin/bash
-#
-# A test suite for recovery's package signature verifier.  Run in a
-# client where you have done envsetup, lunch, etc.
-#
-# TODO: find some way to get this run regularly along with the rest of
-# the tests.
-
-EMULATOR_PORT=5580
-DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/testdata
-
-WORK_DIR=/data/local/tmp
-
-# set to 0 to use a device instead
-USE_EMULATOR=0
-
-# ------------------------
-
-if [ "$USE_EMULATOR" == 1 ]; then
-  emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
-  pid_emulator=$!
-  ADB="adb -s emulator-$EMULATOR_PORT "
-else
-  ADB="adb -d "
-fi
-
-echo "waiting to connect to device"
-$ADB wait-for-device
-
-# run a command on the device; exit with the exit status of the device
-# command.
-run_command() {
-  $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
-}
-
-testname() {
-  echo
-  echo "::: testing $1 :::"
-  testname="$1"
-}
-
-fail() {
-  echo
-  echo FAIL: $testname
-  echo
-  [ "$open_pid" == "" ] || kill $open_pid
-  [ "$pid_emulator" == "" ] || kill $pid_emulator
-  exit 1
-}
-
-
-cleanup() {
-  # not necessary if we're about to kill the emulator, but nice for
-  # running on real devices or already-running emulators.
-  run_command rm $WORK_DIR/verifier_test
-  run_command rm $WORK_DIR/package.zip
-
-  [ "$pid_emulator" == "" ] || kill $pid_emulator
-}
-
-$ADB push $ANDROID_PRODUCT_OUT/system/bin/verifier_test \
-          $WORK_DIR/verifier_test
-
-expect_succeed() {
-  testname "$1 (should succeed)"
-  $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip
-  shift
-  run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip || fail
-}
-
-expect_fail() {
-  testname "$1 (should fail)"
-  $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip
-  shift
-  run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip && fail
-}
-
-# not signed at all
-expect_fail unsigned.zip
-# signed in the pre-donut way
-expect_fail jarsigned.zip
-
-# success cases
-expect_succeed otasigned.zip -e3
-expect_succeed otasigned_f4.zip -f4
-expect_succeed otasigned_sha256.zip -e3 -sha256
-expect_succeed otasigned_f4_sha256.zip -f4 -sha256
-expect_succeed otasigned_ecdsa_sha256.zip -ec -sha256
-
-# success with multiple keys
-expect_succeed otasigned.zip -f4 -e3
-expect_succeed otasigned_f4.zip -ec -f4
-expect_succeed otasigned_sha256.zip -ec -e3 -e3 -sha256
-expect_succeed otasigned_f4_sha256.zip -ec -sha256 -e3 -f4 -sha256
-expect_succeed otasigned_ecdsa_sha256.zip -f4 -sha256 -e3 -ec -sha256
-
-# verified against different key
-expect_fail otasigned.zip -f4
-expect_fail otasigned_f4.zip -e3
-expect_fail otasigned_ecdsa_sha256.zip -e3 -sha256
-
-# verified against right key but wrong hash algorithm
-expect_fail otasigned.zip -e3 -sha256
-expect_fail otasigned_f4.zip -f4 -sha256
-expect_fail otasigned_sha256.zip
-expect_fail otasigned_f4_sha256.zip -f4
-expect_fail otasigned_ecdsa_sha256.zip
-
-# various other cases
-expect_fail random.zip
-expect_fail fake-eocd.zip
-expect_fail alter-metadata.zip
-expect_fail alter-footer.zip
-
-# --------------- cleanup ----------------------
-
-cleanup
-
-echo
-echo PASS
-echo