verifier: update to support certificates using SHA-256

(cherry picked from commit bac7fba02763ae5e78e8e4ba0bea727330ad953e)

Change-Id: I01c38d7fea088622a8b0bbf2c833fa2d969417af
diff --git a/verifier.cpp b/verifier.cpp
index 5f4c981..782a838 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -20,6 +20,7 @@
 
 #include "mincrypt/rsa.h"
 #include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
 
 #include <string.h>
 #include <stdio.h>
@@ -34,7 +35,7 @@
 // Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
 // or no key matches the signature).
 
-int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) {
+int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys) {
     ui->SetProgress(0.0);
 
     FILE* f = fopen(path, "rb");
@@ -68,6 +69,7 @@
     }
 
     if (footer[2] != 0xff || footer[3] != 0xff) {
+        LOGE("footer is wrong\n");
         fclose(f);
         return VERIFY_FAILURE;
     }
@@ -139,8 +141,19 @@
 
 #define BUFFER_SIZE 4096
 
-    SHA_CTX ctx;
-    SHA_init(&ctx);
+    bool need_sha1 = false;
+    bool need_sha256 = false;
+    for (i = 0; i < numKeys; ++i) {
+        switch (pKeys[i].hash_len) {
+            case SHA_DIGEST_SIZE: need_sha1 = true; break;
+            case SHA256_DIGEST_SIZE: need_sha256 = true; break;
+        }
+    }
+
+    SHA_CTX sha1_ctx;
+    SHA256_CTX sha256_ctx;
+    SHA_init(&sha1_ctx);
+    SHA256_init(&sha256_ctx);
     unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE);
     if (buffer == NULL) {
         LOGE("failed to alloc memory for sha1 buffer\n");
@@ -159,7 +172,8 @@
             fclose(f);
             return VERIFY_FAILURE;
         }
-        SHA_update(&ctx, buffer, size);
+        if (need_sha1) SHA_update(&sha1_ctx, buffer, size);
+        if (need_sha256) SHA256_update(&sha256_ctx, buffer, size);
         so_far += size;
         double f = so_far / (double)signed_len;
         if (f > frac + 0.02 || size == so_far) {
@@ -170,12 +184,21 @@
     fclose(f);
     free(buffer);
 
-    const uint8_t* sha1 = SHA_final(&ctx);
+    const uint8_t* sha1 = SHA_final(&sha1_ctx);
+    const uint8_t* sha256 = SHA256_final(&sha256_ctx);
+
     for (i = 0; i < numKeys; ++i) {
+        const uint8_t* hash;
+        switch (pKeys[i].hash_len) {
+            case SHA_DIGEST_SIZE: hash = sha1; break;
+            case SHA256_DIGEST_SIZE: hash = sha256; break;
+            default: continue;
+        }
+
         // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that
         // the signing tool appends after the signature itself.
-        if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES,
-                       RSANUMBYTES, sha1)) {
+        if (RSA_verify(pKeys[i].public_key, eocd + eocd_size - 6 - RSANUMBYTES,
+                       RSANUMBYTES, hash, pKeys[i].hash_len)) {
             LOGI("whole-file signature verified against key %d\n", i);
             free(eocd);
             return VERIFY_SUCCESS;
@@ -207,10 +230,19 @@
 // The file may contain multiple keys in this format, separated by
 // commas.  The last key must not be followed by a comma.
 //
+// A Certificate is a pair of an RSAPublicKey and a particular hash
+// (we support SHA-1 and SHA-256; we store the hash length to signify
+// which is being used).  The hash used is implied by the version number.
+//
+//       1: 2048-bit RSA key with e=3 and SHA-1 hash
+//       2: 2048-bit RSA key with e=65537 and SHA-1 hash
+//       3: 2048-bit RSA key with e=3 and SHA-256 hash
+//       4: 2048-bit RSA key with e=65537 and SHA-256 hash
+//
 // Returns NULL if the file failed to parse, or if it contain zero keys.
-RSAPublicKey*
+Certificate*
 load_keys(const char* filename, int* numKeys) {
-    RSAPublicKey* out = NULL;
+    Certificate* out = NULL;
     *numKeys = 0;
 
     FILE* f = fopen(filename, "r");
@@ -224,24 +256,38 @@
         bool done = false;
         while (!done) {
             ++*numKeys;
-            out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey));
-            RSAPublicKey* key = out + (*numKeys - 1);
+            out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate));
+            Certificate* cert = out + (*numKeys - 1);
+            cert->public_key = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
 
             char start_char;
             if (fscanf(f, " %c", &start_char) != 1) goto exit;
             if (start_char == '{') {
                 // a version 1 key has no version specifier.
-                key->exponent = 3;
+                cert->public_key->exponent = 3;
+                cert->hash_len = SHA_DIGEST_SIZE;
             } else if (start_char == 'v') {
                 int version;
                 if (fscanf(f, "%d {", &version) != 1) goto exit;
-                if (version == 2) {
-                    key->exponent = 65537;
-                } else {
-                    goto exit;
+                switch (version) {
+                    case 2:
+                        cert->public_key->exponent = 65537;
+                        cert->hash_len = SHA_DIGEST_SIZE;
+                        break;
+                    case 3:
+                        cert->public_key->exponent = 3;
+                        cert->hash_len = SHA256_DIGEST_SIZE;
+                        break;
+                    case 4:
+                        cert->public_key->exponent = 65537;
+                        cert->hash_len = SHA256_DIGEST_SIZE;
+                        break;
+                    default:
+                        goto exit;
                 }
             }
 
+            RSAPublicKey* key = cert->public_key;
             if (fscanf(f, " %i , 0x%x , { %u",
                        &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
                 goto exit;
@@ -274,7 +320,7 @@
                 goto exit;
             }
 
-            LOGI("read key e=%d\n", key->exponent);
+            LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len);
         }
     }