blob: 729e085cf6b3807101fef9ae606b400e09676ebf [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "common.h"
18#include "verifier.h"
19
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080020#include "mincrypt/rsa.h"
21#include "mincrypt/sha.h"
22
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080023#include <string.h>
Doug Zongker54e2e862009-08-17 13:21:04 -070024#include <stdio.h>
25#include <errno.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080026
Doug Zongker54e2e862009-08-17 13:21:04 -070027// Look for an RSA signature embedded in the .ZIP file comment given
28// the path to the zip. Verify it matches one of the given public
29// keys.
30//
31// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
32// or no key matches the signature).
33
34int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) {
35 ui_set_progress(0.0);
36
37 FILE* f = fopen(path, "rb");
38 if (f == NULL) {
39 LOGE("failed to open %s (%s)\n", path, strerror(errno));
40 return VERIFY_FAILURE;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080041 }
42
Doug Zongker54e2e862009-08-17 13:21:04 -070043 // An archive with a whole-file signature will end in six bytes:
44 //
Doug Zongker73ae31c2009-12-09 17:01:45 -080045 // (2-byte signature start) $ff $ff (2-byte comment size)
Doug Zongker54e2e862009-08-17 13:21:04 -070046 //
47 // (As far as the ZIP format is concerned, these are part of the
48 // archive comment.) We start by reading this footer, this tells
49 // us how far back from the end we have to start reading to find
50 // the whole comment.
51
52#define FOOTER_SIZE 6
53
54 if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) {
55 LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
56 fclose(f);
57 return VERIFY_FAILURE;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080058 }
59
Doug Zongker54e2e862009-08-17 13:21:04 -070060 unsigned char footer[FOOTER_SIZE];
61 if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) {
62 LOGE("failed to read footer from %s (%s)\n", path, strerror(errno));
63 fclose(f);
64 return VERIFY_FAILURE;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080065 }
66
Doug Zongker54e2e862009-08-17 13:21:04 -070067 if (footer[2] != 0xff || footer[3] != 0xff) {
68 fclose(f);
69 return VERIFY_FAILURE;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080070 }
71
Doug Zongker54e2e862009-08-17 13:21:04 -070072 int comment_size = footer[4] + (footer[5] << 8);
73 int signature_start = footer[0] + (footer[1] << 8);
74 LOGI("comment is %d bytes; signature %d bytes from end\n",
75 comment_size, signature_start);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080076
Doug Zongker54e2e862009-08-17 13:21:04 -070077 if (signature_start - FOOTER_SIZE < RSANUMBYTES) {
78 // "signature" block isn't big enough to contain an RSA block.
79 LOGE("signature is too short\n");
80 fclose(f);
81 return VERIFY_FAILURE;
82 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080083
Doug Zongker54e2e862009-08-17 13:21:04 -070084#define EOCD_HEADER_SIZE 22
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080085
Doug Zongker54e2e862009-08-17 13:21:04 -070086 // The end-of-central-directory record is 22 bytes plus any
87 // comment length.
88 size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080089
Doug Zongker54e2e862009-08-17 13:21:04 -070090 if (fseek(f, -eocd_size, SEEK_END) != 0) {
91 LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
92 fclose(f);
93 return VERIFY_FAILURE;
94 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080095
Doug Zongker54e2e862009-08-17 13:21:04 -070096 // Determine how much of the file is covered by the signature.
97 // This is everything except the signature data and length, which
98 // includes all of the EOCD except for the comment length field (2
99 // bytes) and the comment data.
100 size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800101
Doug Zongker54e2e862009-08-17 13:21:04 -0700102 unsigned char* eocd = malloc(eocd_size);
103 if (eocd == NULL) {
104 LOGE("malloc for EOCD record failed\n");
105 fclose(f);
106 return VERIFY_FAILURE;
107 }
108 if (fread(eocd, 1, eocd_size, f) != eocd_size) {
109 LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno));
110 fclose(f);
111 return VERIFY_FAILURE;
112 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800113
Doug Zongker54e2e862009-08-17 13:21:04 -0700114 // If this is really is the EOCD record, it will begin with the
115 // magic number $50 $4b $05 $06.
116 if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
117 eocd[2] != 0x05 || eocd[3] != 0x06) {
118 LOGE("signature length doesn't match EOCD marker\n");
119 fclose(f);
120 return VERIFY_FAILURE;
121 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800122
Doug Zongker54e2e862009-08-17 13:21:04 -0700123 int i;
124 for (i = 4; i < eocd_size-3; ++i) {
125 if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b &&
Doug Zongkerc652e412009-12-08 15:30:09 -0800126 eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
Doug Zongker54e2e862009-08-17 13:21:04 -0700127 // if the sequence $50 $4b $05 $06 appears anywhere after
128 // the real one, minzip will find the later (wrong) one,
129 // which could be exploitable. Fail verification if
130 // this sequence occurs anywhere after the real one.
131 LOGE("EOCD marker occurs after start of EOCD\n");
132 fclose(f);
133 return VERIFY_FAILURE;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800134 }
135 }
136
Doug Zongker54e2e862009-08-17 13:21:04 -0700137#define BUFFER_SIZE 4096
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800138
Doug Zongker54e2e862009-08-17 13:21:04 -0700139 SHA_CTX ctx;
140 SHA_init(&ctx);
141 unsigned char* buffer = malloc(BUFFER_SIZE);
142 if (buffer == NULL) {
143 LOGE("failed to alloc memory for sha1 buffer\n");
144 fclose(f);
145 return VERIFY_FAILURE;
146 }
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800147
Doug Zongker54e2e862009-08-17 13:21:04 -0700148 double frac = -1.0;
149 size_t so_far = 0;
150 fseek(f, 0, SEEK_SET);
151 while (so_far < signed_len) {
152 int size = BUFFER_SIZE;
153 if (signed_len - so_far < size) size = signed_len - so_far;
154 if (fread(buffer, 1, size, f) != size) {
155 LOGE("failed to read data from %s (%s)\n", path, strerror(errno));
156 fclose(f);
157 return VERIFY_FAILURE;
158 }
159 SHA_update(&ctx, buffer, size);
160 so_far += size;
161 double f = so_far / (double)signed_len;
162 if (f > frac + 0.02 || size == so_far) {
163 ui_set_progress(f);
164 frac = f;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800165 }
166 }
Doug Zongker54e2e862009-08-17 13:21:04 -0700167 fclose(f);
168 free(buffer);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800169
Doug Zongker54e2e862009-08-17 13:21:04 -0700170 const uint8_t* sha1 = SHA_final(&ctx);
171 for (i = 0; i < numKeys; ++i) {
Doug Zongker73ae31c2009-12-09 17:01:45 -0800172 // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that
Doug Zongker54e2e862009-08-17 13:21:04 -0700173 // the signing tool appends after the signature itself.
174 if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES,
175 RSANUMBYTES, sha1)) {
Doug Zongker47626332011-03-15 12:11:08 -0700176 LOGI("whole-file signature verified against key %d\n", i);
Doug Zongker54e2e862009-08-17 13:21:04 -0700177 free(eocd);
178 return VERIFY_SUCCESS;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800179 }
180 }
Doug Zongker54e2e862009-08-17 13:21:04 -0700181 free(eocd);
182 LOGE("failed to verify whole-file signature\n");
183 return VERIFY_FAILURE;
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800184}