verifier.cpp 16.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (C) 2008 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.
 */

Kenny Root's avatar
Kenny Root committed
17
#include "asn1_decoder.h"
18
#include "common.h"
Doug Zongker's avatar
Doug Zongker committed
19
#include "ui.h"
Kenny Root's avatar
Kenny Root committed
20
#include "verifier.h"
21

Kenny Root's avatar
Kenny Root committed
22 23 24
#include "mincrypt/dsa_sig.h"
#include "mincrypt/p256.h"
#include "mincrypt/p256_ecdsa.h"
25 26
#include "mincrypt/rsa.h"
#include "mincrypt/sha.h"
27
#include "mincrypt/sha256.h"
28 29

#include <string.h>
30 31 32
#include <stdio.h>
#include <errno.h>

33 34
extern RecoveryUI* ui;

Kenny Root's avatar
Kenny Root committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
/*
 * Simple version of PKCS#7 SignedData extraction. This extracts the
 * signature OCTET STRING to be used for signature verification.
 *
 * For full details, see http://www.ietf.org/rfc/rfc3852.txt
 *
 * The PKCS#7 structure looks like:
 *
 *   SEQUENCE (ContentInfo)
 *     OID (ContentType)
 *     [0] (content)
 *       SEQUENCE (SignedData)
 *         INTEGER (version CMSVersion)
 *         SET (DigestAlgorithmIdentifiers)
 *         SEQUENCE (EncapsulatedContentInfo)
 *         [0] (CertificateSet OPTIONAL)
 *         [1] (RevocationInfoChoices OPTIONAL)
 *         SET (SignerInfos)
 *           SEQUENCE (SignerInfo)
 *             INTEGER (CMSVersion)
 *             SEQUENCE (SignerIdentifier)
 *             SEQUENCE (DigestAlgorithmIdentifier)
 *             SEQUENCE (SignatureAlgorithmIdentifier)
 *             OCTET STRING (SignatureValue)
 */
static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_der,
        size_t* sig_der_length) {
    asn1_context_t* ctx = asn1_context_new(pkcs7_der, pkcs7_der_len);
    if (ctx == NULL) {
        return false;
    }

    asn1_context_t* pkcs7_seq = asn1_sequence_get(ctx);
    if (pkcs7_seq != NULL && asn1_sequence_next(pkcs7_seq)) {
        asn1_context_t *signed_data_app = asn1_constructed_get(pkcs7_seq);
        if (signed_data_app != NULL) {
            asn1_context_t* signed_data_seq = asn1_sequence_get(signed_data_app);
            if (signed_data_seq != NULL
                    && asn1_sequence_next(signed_data_seq)
                    && asn1_sequence_next(signed_data_seq)
                    && asn1_sequence_next(signed_data_seq)
                    && asn1_constructed_skip_all(signed_data_seq)) {
                asn1_context_t *sig_set = asn1_set_get(signed_data_seq);
                if (sig_set != NULL) {
                    asn1_context_t* sig_seq = asn1_sequence_get(sig_set);
                    if (sig_seq != NULL
                            && asn1_sequence_next(sig_seq)
                            && asn1_sequence_next(sig_seq)
                            && asn1_sequence_next(sig_seq)
                            && asn1_sequence_next(sig_seq)) {
                        uint8_t* sig_der_ptr;
                        if (asn1_octet_string_get(sig_seq, &sig_der_ptr, sig_der_length)) {
                            *sig_der = (uint8_t*) malloc(*sig_der_length);
                            if (*sig_der != NULL) {
                                memcpy(*sig_der, sig_der_ptr, *sig_der_length);
                            }
                        }
                        asn1_context_free(sig_seq);
                    }
                    asn1_context_free(sig_set);
                }
                asn1_context_free(signed_data_seq);
            }
            asn1_context_free(signed_data_app);
        }
        asn1_context_free(pkcs7_seq);
    }
    asn1_context_free(ctx);

    return *sig_der != NULL;
}

107 108 109 110 111 112 113
// Look for an RSA signature embedded in the .ZIP file comment given
// the path to the zip.  Verify it matches one of the given public
// keys.
//
// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
// or no key matches the signature).

114 115
int verify_file(unsigned char* addr, size_t length,
                const Certificate* pKeys, unsigned int numKeys) {
116
    ui->SetProgress(0.0);
117 118 119

    // An archive with a whole-file signature will end in six bytes:
    //
120
    //   (2-byte signature start) $ff $ff (2-byte comment size)
121 122 123 124 125 126 127 128
    //
    // (As far as the ZIP format is concerned, these are part of the
    // archive comment.)  We start by reading this footer, this tells
    // us how far back from the end we have to start reading to find
    // the whole comment.

#define FOOTER_SIZE 6

129 130
    if (length < FOOTER_SIZE) {
        LOGE("not big enough to contain footer\n");
131
        return VERIFY_FAILURE;
132 133
    }

134
    unsigned char* footer = addr + length - FOOTER_SIZE;
135

136
    if (footer[2] != 0xff || footer[3] != 0xff) {
137
        LOGE("footer is wrong\n");
138
        return VERIFY_FAILURE;
139 140
    }

Doug Zongker's avatar
Doug Zongker committed
141 142
    size_t comment_size = footer[4] + (footer[5] << 8);
    size_t signature_start = footer[0] + (footer[1] << 8);
Mark Salyzyn's avatar
Mark Salyzyn committed
143
    LOGI("comment is %zu bytes; signature %zu bytes from end\n",
144
         comment_size, signature_start);
145

Kenny Root's avatar
Kenny Root committed
146 147
    if (signature_start <= FOOTER_SIZE) {
        LOGE("Signature start is in the footer");
148
        return VERIFY_FAILURE;
149 150
    }

151
#define EOCD_HEADER_SIZE 22
152

153 154 155
    // The end-of-central-directory record is 22 bytes plus any
    // comment length.
    size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
156

157 158
    if (length < eocd_size) {
        LOGE("not big enough to contain EOCD\n");
159
        return VERIFY_FAILURE;
160 161
    }

162 163 164 165
    // Determine how much of the file is covered by the signature.
    // This is everything except the signature data and length, which
    // includes all of the EOCD except for the comment length field (2
    // bytes) and the comment data.
166
    size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
167

168
    unsigned char* eocd = addr + length - eocd_size;
169

170 171 172 173 174 175
    // If this is really is the EOCD record, it will begin with the
    // magic number $50 $4b $05 $06.
    if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
        eocd[2] != 0x05 || eocd[3] != 0x06) {
        LOGE("signature length doesn't match EOCD marker\n");
        return VERIFY_FAILURE;
176 177
    }

Doug Zongker's avatar
Doug Zongker committed
178
    size_t i;
179 180
    for (i = 4; i < eocd_size-3; ++i) {
        if (eocd[i  ] == 0x50 && eocd[i+1] == 0x4b &&
181
            eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
182 183 184 185 186 187 188
            // if the sequence $50 $4b $05 $06 appears anywhere after
            // the real one, minzip will find the later (wrong) one,
            // which could be exploitable.  Fail verification if
            // this sequence occurs anywhere after the real one.
            LOGE("EOCD marker occurs after start of EOCD\n");
            return VERIFY_FAILURE;
        }
189 190
    }

191
#define BUFFER_SIZE 4096
192

193 194 195 196 197 198 199 200 201 202 203 204 205
    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);
206

207 208 209
    double frac = -1.0;
    size_t so_far = 0;
    while (so_far < signed_len) {
210 211 212 213 214
        size_t size = signed_len - so_far;
        if (size > BUFFER_SIZE) size = BUFFER_SIZE;

        if (need_sha1) SHA_update(&sha1_ctx, addr + so_far, size);
        if (need_sha256) SHA256_update(&sha256_ctx, addr + so_far, size);
215
        so_far += size;
216

217 218
        double f = so_far / (double)signed_len;
        if (f > frac + 0.02 || size == so_far) {
219
            ui->SetProgress(f);
220
            frac = f;
221 222
        }
    }
223

224 225 226
    const uint8_t* sha1 = SHA_final(&sha1_ctx);
    const uint8_t* sha256 = SHA256_final(&sha256_ctx);

Kenny Root's avatar
Kenny Root committed
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
    uint8_t* sig_der = NULL;
    size_t sig_der_length = 0;

    size_t signature_size = signature_start - FOOTER_SIZE;
    if (!read_pkcs7(eocd + eocd_size - signature_start, signature_size, &sig_der,
            &sig_der_length)) {
        LOGE("Could not find signature DER block\n");
        return VERIFY_FAILURE;
    }

    /*
     * Check to make sure at least one of the keys matches the signature. Since
     * any key can match, we need to try each before determining a verification
     * failure has happened.
     */
242
    for (i = 0; i < numKeys; ++i) {
243 244 245 246 247 248 249
        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;
        }

250
        // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that
251
        // the signing tool appends after the signature itself.
Kenny Root's avatar
Kenny Root committed
252 253 254
        if (pKeys[i].key_type == Certificate::RSA) {
            if (sig_der_length < RSANUMBYTES) {
                // "signature" block isn't big enough to contain an RSA block.
Mark Salyzyn's avatar
Mark Salyzyn committed
255
                LOGI("signature is too short for RSA key %zu\n", i);
Kenny Root's avatar
Kenny Root committed
256 257 258 259 260
                continue;
            }

            if (!RSA_verify(pKeys[i].rsa, sig_der, RSANUMBYTES,
                            hash, pKeys[i].hash_len)) {
Mark Salyzyn's avatar
Mark Salyzyn committed
261
                LOGI("failed to verify against RSA key %zu\n", i);
Kenny Root's avatar
Kenny Root committed
262 263 264
                continue;
            }

Mark Salyzyn's avatar
Mark Salyzyn committed
265
            LOGI("whole-file signature verified against RSA key %zu\n", i);
Kenny Root's avatar
Kenny Root committed
266 267 268 269 270 271
            free(sig_der);
            return VERIFY_SUCCESS;
        } else if (pKeys[i].key_type == Certificate::EC
                && pKeys[i].hash_len == SHA256_DIGEST_SIZE) {
            p256_int r, s;
            if (!dsa_sig_unpack(sig_der, sig_der_length, &r, &s)) {
Mark Salyzyn's avatar
Mark Salyzyn committed
272
                LOGI("Not a DSA signature block for EC key %zu\n", i);
Kenny Root's avatar
Kenny Root committed
273 274 275 276 277 278 279
                continue;
            }

            p256_int p256_hash;
            p256_from_bin(hash, &p256_hash);
            if (!p256_ecdsa_verify(&(pKeys[i].ec->x), &(pKeys[i].ec->y),
                                   &p256_hash, &r, &s)) {
Mark Salyzyn's avatar
Mark Salyzyn committed
280
                LOGI("failed to verify against EC key %zu\n", i);
Kenny Root's avatar
Kenny Root committed
281 282 283
                continue;
            }

Mark Salyzyn's avatar
Mark Salyzyn committed
284
            LOGI("whole-file signature verified against EC key %zu\n", i);
Kenny Root's avatar
Kenny Root committed
285
            free(sig_der);
286
            return VERIFY_SUCCESS;
287
        } else {
Kenny Root's avatar
Kenny Root committed
288
            LOGI("Unknown key type %d\n", pKeys[i].key_type);
289
        }
290
    }
Kenny Root's avatar
Kenny Root committed
291
    free(sig_der);
292 293
    LOGE("failed to verify whole-file signature\n");
    return VERIFY_FAILURE;
294
}
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

// Reads a file containing one or more public keys as produced by
// DumpPublicKey:  this is an RSAPublicKey struct as it would appear
// as a C source literal, eg:
//
//  "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
//
// For key versions newer than the original 2048-bit e=3 keys
// supported by Android, the string is preceded by a version
// identifier, eg:
//
//  "v2 {64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}"
//
// (Note that the braces and commas in this example are actual
// characters the parser expects to find in the file; the ellipses
// indicate more numbers omitted from this example.)
//
// The file may contain multiple keys in this format, separated by
// commas.  The last key must not be followed by a comma.
//
315 316 317 318 319 320 321 322
// 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
Kenny Root's avatar
Kenny Root committed
323
//       5: 256-bit EC key using the NIST P-256 curve parameters and SHA-256 hash
324
//
325
// Returns NULL if the file failed to parse, or if it contain zero keys.
326
Certificate*
327
load_keys(const char* filename, int* numKeys) {
328
    Certificate* out = NULL;
329 330 331 332 333 334 335 336 337 338 339 340 341
    *numKeys = 0;

    FILE* f = fopen(filename, "r");
    if (f == NULL) {
        LOGE("opening %s: %s\n", filename, strerror(errno));
        goto exit;
    }

    {
        int i;
        bool done = false;
        while (!done) {
            ++*numKeys;
342 343
            out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate));
            Certificate* cert = out + (*numKeys - 1);
Kenny Root's avatar
Kenny Root committed
344
            memset(cert, '\0', sizeof(Certificate));
345 346 347 348 349

            char start_char;
            if (fscanf(f, " %c", &start_char) != 1) goto exit;
            if (start_char == '{') {
                // a version 1 key has no version specifier.
Kenny Root's avatar
Kenny Root committed
350 351 352
                cert->key_type = Certificate::RSA;
                cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
                cert->rsa->exponent = 3;
353
                cert->hash_len = SHA_DIGEST_SIZE;
354 355 356
            } else if (start_char == 'v') {
                int version;
                if (fscanf(f, "%d {", &version) != 1) goto exit;
357 358
                switch (version) {
                    case 2:
Kenny Root's avatar
Kenny Root committed
359 360 361
                        cert->key_type = Certificate::RSA;
                        cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
                        cert->rsa->exponent = 65537;
362 363 364
                        cert->hash_len = SHA_DIGEST_SIZE;
                        break;
                    case 3:
Kenny Root's avatar
Kenny Root committed
365 366 367
                        cert->key_type = Certificate::RSA;
                        cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
                        cert->rsa->exponent = 3;
368 369 370
                        cert->hash_len = SHA256_DIGEST_SIZE;
                        break;
                    case 4:
Kenny Root's avatar
Kenny Root committed
371 372 373 374 375 376 377 378
                        cert->key_type = Certificate::RSA;
                        cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
                        cert->rsa->exponent = 65537;
                        cert->hash_len = SHA256_DIGEST_SIZE;
                        break;
                    case 5:
                        cert->key_type = Certificate::EC;
                        cert->ec = (ECPublicKey*)calloc(1, sizeof(ECPublicKey));
379 380 381 382
                        cert->hash_len = SHA256_DIGEST_SIZE;
                        break;
                    default:
                        goto exit;
383 384 385
                }
            }

Kenny Root's avatar
Kenny Root committed
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
            if (cert->key_type == Certificate::RSA) {
                RSAPublicKey* key = cert->rsa;
                if (fscanf(f, " %i , 0x%x , { %u",
                           &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
                    goto exit;
                }
                if (key->len != RSANUMWORDS) {
                    LOGE("key length (%d) does not match expected size\n", key->len);
                    goto exit;
                }
                for (i = 1; i < key->len; ++i) {
                    if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
                }
                if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
                for (i = 1; i < key->len; ++i) {
                    if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
                }
                fscanf(f, " } } ");

                LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len);
            } else if (cert->key_type == Certificate::EC) {
                ECPublicKey* key = cert->ec;
                int key_len;
                unsigned int byte;
                uint8_t x_bytes[P256_NBYTES];
                uint8_t y_bytes[P256_NBYTES];
                if (fscanf(f, " %i , { %u", &key_len, &byte) != 2) goto exit;
                if (key_len != P256_NBYTES) {
                    LOGE("Key length (%d) does not match expected size %d\n", key_len, P256_NBYTES);
                    goto exit;
                }
                x_bytes[P256_NBYTES - 1] = byte;
                for (i = P256_NBYTES - 2; i >= 0; --i) {
                    if (fscanf(f, " , %u", &byte) != 1) goto exit;
                    x_bytes[i] = byte;
                }
                if (fscanf(f, " } , { %u", &byte) != 1) goto exit;
                y_bytes[P256_NBYTES - 1] = byte;
                for (i = P256_NBYTES - 2; i >= 0; --i) {
                    if (fscanf(f, " , %u", &byte) != 1) goto exit;
                    y_bytes[i] = byte;
                }
                fscanf(f, " } } ");
                p256_from_bin(x_bytes, &key->x);
                p256_from_bin(y_bytes, &key->y);
            } else {
                LOGE("Unknown key type %d\n", cert->key_type);
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
                goto exit;
            }

            // if the line ends in a comma, this file has more keys.
            switch (fgetc(f)) {
            case ',':
                // more keys to come.
                break;

            case EOF:
                done = true;
                break;

            default:
                LOGE("unexpected character between keys\n");
                goto exit;
            }
        }
    }

    fclose(f);
    return out;

exit:
    if (f) fclose(f);
    free(out);
    *numKeys = 0;
    return NULL;
}