• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #include "ui.h"
20 
21 #include "mincrypt/rsa.h"
22 #include "mincrypt/sha.h"
23 
24 #include <string.h>
25 #include <stdio.h>
26 #include <errno.h>
27 
28 extern RecoveryUI* ui;
29 
30 // Look for an RSA signature embedded in the .ZIP file comment given
31 // the path to the zip.  Verify it matches one of the given public
32 // keys.
33 //
34 // Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered
35 // or no key matches the signature).
36 
verify_file(const char * path,const RSAPublicKey * pKeys,unsigned int numKeys)37 int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) {
38     ui->SetProgress(0.0);
39 
40     FILE* f = fopen(path, "rb");
41     if (f == NULL) {
42         LOGE("failed to open %s (%s)\n", path, strerror(errno));
43         return VERIFY_FAILURE;
44     }
45 
46     // An archive with a whole-file signature will end in six bytes:
47     //
48     //   (2-byte signature start) $ff $ff (2-byte comment size)
49     //
50     // (As far as the ZIP format is concerned, these are part of the
51     // archive comment.)  We start by reading this footer, this tells
52     // us how far back from the end we have to start reading to find
53     // the whole comment.
54 
55 #define FOOTER_SIZE 6
56 
57     if (fseek(f, -FOOTER_SIZE, SEEK_END) != 0) {
58         LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
59         fclose(f);
60         return VERIFY_FAILURE;
61     }
62 
63     unsigned char footer[FOOTER_SIZE];
64     if (fread(footer, 1, FOOTER_SIZE, f) != FOOTER_SIZE) {
65         LOGE("failed to read footer from %s (%s)\n", path, strerror(errno));
66         fclose(f);
67         return VERIFY_FAILURE;
68     }
69 
70     if (footer[2] != 0xff || footer[3] != 0xff) {
71         fclose(f);
72         return VERIFY_FAILURE;
73     }
74 
75     size_t comment_size = footer[4] + (footer[5] << 8);
76     size_t signature_start = footer[0] + (footer[1] << 8);
77     LOGI("comment is %d bytes; signature %d bytes from end\n",
78          comment_size, signature_start);
79 
80     if (signature_start - FOOTER_SIZE < RSANUMBYTES) {
81         // "signature" block isn't big enough to contain an RSA block.
82         LOGE("signature is too short\n");
83         fclose(f);
84         return VERIFY_FAILURE;
85     }
86 
87 #define EOCD_HEADER_SIZE 22
88 
89     // The end-of-central-directory record is 22 bytes plus any
90     // comment length.
91     size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
92 
93     if (fseek(f, -eocd_size, SEEK_END) != 0) {
94         LOGE("failed to seek in %s (%s)\n", path, strerror(errno));
95         fclose(f);
96         return VERIFY_FAILURE;
97     }
98 
99     // Determine how much of the file is covered by the signature.
100     // This is everything except the signature data and length, which
101     // includes all of the EOCD except for the comment length field (2
102     // bytes) and the comment data.
103     size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2;
104 
105     unsigned char* eocd = (unsigned char*)malloc(eocd_size);
106     if (eocd == NULL) {
107         LOGE("malloc for EOCD record failed\n");
108         fclose(f);
109         return VERIFY_FAILURE;
110     }
111     if (fread(eocd, 1, eocd_size, f) != eocd_size) {
112         LOGE("failed to read eocd from %s (%s)\n", path, strerror(errno));
113         fclose(f);
114         return VERIFY_FAILURE;
115     }
116 
117     // If this is really is the EOCD record, it will begin with the
118     // magic number $50 $4b $05 $06.
119     if (eocd[0] != 0x50 || eocd[1] != 0x4b ||
120         eocd[2] != 0x05 || eocd[3] != 0x06) {
121         LOGE("signature length doesn't match EOCD marker\n");
122         fclose(f);
123         return VERIFY_FAILURE;
124     }
125 
126     size_t i;
127     for (i = 4; i < eocd_size-3; ++i) {
128         if (eocd[i  ] == 0x50 && eocd[i+1] == 0x4b &&
129             eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
130             // if the sequence $50 $4b $05 $06 appears anywhere after
131             // the real one, minzip will find the later (wrong) one,
132             // which could be exploitable.  Fail verification if
133             // this sequence occurs anywhere after the real one.
134             LOGE("EOCD marker occurs after start of EOCD\n");
135             fclose(f);
136             return VERIFY_FAILURE;
137         }
138     }
139 
140 #define BUFFER_SIZE 4096
141 
142     SHA_CTX ctx;
143     SHA_init(&ctx);
144     unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE);
145     if (buffer == NULL) {
146         LOGE("failed to alloc memory for sha1 buffer\n");
147         fclose(f);
148         return VERIFY_FAILURE;
149     }
150 
151     double frac = -1.0;
152     size_t so_far = 0;
153     fseek(f, 0, SEEK_SET);
154     while (so_far < signed_len) {
155         size_t size = BUFFER_SIZE;
156         if (signed_len - so_far < size) size = signed_len - so_far;
157         if (fread(buffer, 1, size, f) != size) {
158             LOGE("failed to read data from %s (%s)\n", path, strerror(errno));
159             fclose(f);
160             return VERIFY_FAILURE;
161         }
162         SHA_update(&ctx, buffer, size);
163         so_far += size;
164         double f = so_far / (double)signed_len;
165         if (f > frac + 0.02 || size == so_far) {
166             ui->SetProgress(f);
167             frac = f;
168         }
169     }
170     fclose(f);
171     free(buffer);
172 
173     const uint8_t* sha1 = SHA_final(&ctx);
174     for (i = 0; i < numKeys; ++i) {
175         // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that
176         // the signing tool appends after the signature itself.
177         if (RSA_verify(pKeys+i, eocd + eocd_size - 6 - RSANUMBYTES,
178                        RSANUMBYTES, sha1)) {
179             LOGI("whole-file signature verified against key %d\n", i);
180             free(eocd);
181             return VERIFY_SUCCESS;
182         }
183     }
184     free(eocd);
185     LOGE("failed to verify whole-file signature\n");
186     return VERIFY_FAILURE;
187 }
188