aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTao Bao2017-03-18 09:33:26 -0500
committerTao Bao2017-03-18 09:33:26 -0500
commit553c7bd307ef27537bf8cb05162ba7e9da76ae95 (patch)
treef65b0d13a054395dd09201dcff46c5bd655bd997 /verifier.cpp
parent9accc96dd12c673d64521c2e8dfbdf4916e5174e (diff)
parent90d3f20c993671ec7fa90119ce5d214cc370f83d (diff)
downloadplatform-bootable-recovery-553c7bd307ef27537bf8cb05162ba7e9da76ae95.tar.gz
platform-bootable-recovery-553c7bd307ef27537bf8cb05162ba7e9da76ae95.tar.xz
platform-bootable-recovery-553c7bd307ef27537bf8cb05162ba7e9da76ae95.zip
resolve merge conflicts of 90d3f20c to stage-aosp-master
Test: I solemnly swear I tested this conflict resolution. Change-Id: I9c1806eceb56712c4b3d1c67d54f4b21bd3fe50a
Diffstat (limited to 'verifier.cpp')
-rw-r--r--verifier.cpp339
1 files changed, 166 insertions, 173 deletions
diff --git a/verifier.cpp b/verifier.cpp
index 82454867..582c498f 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -21,6 +21,7 @@
21#include <stdlib.h> 21#include <stdlib.h>
22#include <string.h> 22#include <string.h>
23 23
24#include <functional>
24#include <algorithm> 25#include <algorithm>
25#include <memory> 26#include <memory>
26 27
@@ -108,207 +109,199 @@ static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_d
108 return *sig_der != NULL; 109 return *sig_der != NULL;
109} 110}
110 111
111// Look for an RSA signature embedded in the .ZIP file comment given 112/*
112// the path to the zip. Verify it matches one of the given public 113 * Looks for an RSA signature embedded in the .ZIP file comment given the path to the zip. Verifies
113// keys. 114 * that it matches one of the given public keys. A callback function can be optionally provided for
114// 115 * posting the progress.
115// Return VERIFY_SUCCESS, VERIFY_FAILURE (if any error is encountered 116 *
116// or no key matches the signature). 117 * Returns VERIFY_SUCCESS or VERIFY_FAILURE (if any error is encountered or no key matches the
117 118 * signature).
118int verify_file(unsigned char* addr, size_t length, 119 */
119 const std::vector<Certificate>& keys) { 120int verify_file(unsigned char* addr, size_t length, const std::vector<Certificate>& keys,
120 ui->SetProgress(0.0); 121 const std::function<void(float)>& set_progress) {
121 122 if (set_progress) {
122 // An archive with a whole-file signature will end in six bytes: 123 set_progress(0.0);
123 // 124 }
124 // (2-byte signature start) $ff $ff (2-byte comment size) 125
125 // 126 // An archive with a whole-file signature will end in six bytes:
126 // (As far as the ZIP format is concerned, these are part of the 127 //
127 // archive comment.) We start by reading this footer, this tells 128 // (2-byte signature start) $ff $ff (2-byte comment size)
128 // us how far back from the end we have to start reading to find 129 //
129 // the whole comment. 130 // (As far as the ZIP format is concerned, these are part of the archive comment.) We start by
131 // reading this footer, this tells us how far back from the end we have to start reading to find
132 // the whole comment.
130 133
131#define FOOTER_SIZE 6 134#define FOOTER_SIZE 6
132 135
133 if (length < FOOTER_SIZE) { 136 if (length < FOOTER_SIZE) {
134 LOG(ERROR) << "not big enough to contain footer"; 137 LOG(ERROR) << "not big enough to contain footer";
135 return VERIFY_FAILURE; 138 return VERIFY_FAILURE;
136 } 139 }
137 140
138 unsigned char* footer = addr + length - FOOTER_SIZE; 141 unsigned char* footer = addr + length - FOOTER_SIZE;
139 142
140 if (footer[2] != 0xff || footer[3] != 0xff) { 143 if (footer[2] != 0xff || footer[3] != 0xff) {
141 LOG(ERROR) << "footer is wrong"; 144 LOG(ERROR) << "footer is wrong";
142 return VERIFY_FAILURE; 145 return VERIFY_FAILURE;
143 } 146 }
144 147
145 size_t comment_size = footer[4] + (footer[5] << 8); 148 size_t comment_size = footer[4] + (footer[5] << 8);
146 size_t signature_start = footer[0] + (footer[1] << 8); 149 size_t signature_start = footer[0] + (footer[1] << 8);
147 LOG(INFO) << "comment is " << comment_size << " bytes; signature is " << signature_start 150 LOG(INFO) << "comment is " << comment_size << " bytes; signature is " << signature_start
148 << " bytes from end"; 151 << " bytes from end";
149 152
150 if (signature_start > comment_size) { 153 if (signature_start > comment_size) {
151 LOG(ERROR) << "signature start: " << signature_start << " is larger than comment size: " 154 LOG(ERROR) << "signature start: " << signature_start << " is larger than comment size: "
152 << comment_size; 155 << comment_size;
153 return VERIFY_FAILURE; 156 return VERIFY_FAILURE;
154 } 157 }
155 158
156 if (signature_start <= FOOTER_SIZE) { 159 if (signature_start <= FOOTER_SIZE) {
157 LOG(ERROR) << "Signature start is in the footer"; 160 LOG(ERROR) << "Signature start is in the footer";
158 return VERIFY_FAILURE; 161 return VERIFY_FAILURE;
159 } 162 }
160 163
161#define EOCD_HEADER_SIZE 22 164#define EOCD_HEADER_SIZE 22
162 165
163 // The end-of-central-directory record is 22 bytes plus any 166 // The end-of-central-directory record is 22 bytes plus any comment length.
164 // comment length. 167 size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
165 size_t eocd_size = comment_size + EOCD_HEADER_SIZE;
166 168
167 if (length < eocd_size) { 169 if (length < eocd_size) {
168 LOG(ERROR) << "not big enough to contain EOCD"; 170 LOG(ERROR) << "not big enough to contain EOCD";
169 return VERIFY_FAILURE; 171 return VERIFY_FAILURE;
170 } 172 }
171 173
172 // Determine how much of the file is covered by the signature. 174 // Determine how much of the file is covered by the signature. This is everything except the
173 // This is everything except the signature data and length, which 175 // signature data and length, which includes all of the EOCD except for the comment length field
174 // includes all of the EOCD except for the comment length field (2 176 // (2 bytes) and the comment data.
175 // bytes) and the comment data. 177 size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
176 size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
177 178
178 unsigned char* eocd = addr + length - eocd_size; 179 unsigned char* eocd = addr + length - eocd_size;
179 180
180 // If this is really is the EOCD record, it will begin with the 181 // If this is really is the EOCD record, it will begin with the magic number $50 $4b $05 $06.
181 // magic number $50 $4b $05 $06. 182 if (eocd[0] != 0x50 || eocd[1] != 0x4b || eocd[2] != 0x05 || eocd[3] != 0x06) {
182 if (eocd[0] != 0x50 || eocd[1] != 0x4b || 183 LOG(ERROR) << "signature length doesn't match EOCD marker";
183 eocd[2] != 0x05 || eocd[3] != 0x06) { 184 return VERIFY_FAILURE;
184 LOG(ERROR) << "signature length doesn't match EOCD marker"; 185 }
185 return VERIFY_FAILURE;
186 }
187 186
188 for (size_t i = 4; i < eocd_size-3; ++i) { 187 for (size_t i = 4; i < eocd_size-3; ++i) {
189 if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b && 188 if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b && eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
190 eocd[i+2] == 0x05 && eocd[i+3] == 0x06) { 189 // If the sequence $50 $4b $05 $06 appears anywhere after the real one, libziparchive will
191 // if the sequence $50 $4b $05 $06 appears anywhere after 190 // find the later (wrong) one, which could be exploitable. Fail the verification if this
192 // the real one, libziparchive will find the later (wrong) one, 191 // sequence occurs anywhere after the real one.
193 // which could be exploitable. Fail verification if 192 LOG(ERROR) << "EOCD marker occurs after start of EOCD";
194 // this sequence occurs anywhere after the real one. 193 return VERIFY_FAILURE;
195 LOG(ERROR) << "EOCD marker occurs after start of EOCD";
196 return VERIFY_FAILURE;
197 }
198 } 194 }
195 }
199 196
200 bool need_sha1 = false; 197 bool need_sha1 = false;
201 bool need_sha256 = false; 198 bool need_sha256 = false;
202 for (const auto& key : keys) { 199 for (const auto& key : keys) {
203 switch (key.hash_len) { 200 switch (key.hash_len) {
204 case SHA_DIGEST_LENGTH: need_sha1 = true; break; 201 case SHA_DIGEST_LENGTH: need_sha1 = true; break;
205 case SHA256_DIGEST_LENGTH: need_sha256 = true; break; 202 case SHA256_DIGEST_LENGTH: need_sha256 = true; break;
206 }
207 } 203 }
204 }
208 205
209 SHA_CTX sha1_ctx; 206 SHA_CTX sha1_ctx;
210 SHA256_CTX sha256_ctx; 207 SHA256_CTX sha256_ctx;
211 SHA1_Init(&sha1_ctx); 208 SHA1_Init(&sha1_ctx);
212 SHA256_Init(&sha256_ctx); 209 SHA256_Init(&sha256_ctx);
213 210
214 double frac = -1.0; 211 double frac = -1.0;
215 size_t so_far = 0; 212 size_t so_far = 0;
216 while (so_far < signed_len) { 213 while (so_far < signed_len) {
217 // On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a 214 // On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a
218 // 1196MiB full OTA and 60% for an 89MiB incremental OTA. 215 // 1196MiB full OTA and 60% for an 89MiB incremental OTA.
219 // http://b/28135231. 216 // http://b/28135231.
220 size_t size = std::min(signed_len - so_far, 16 * MiB); 217 size_t size = std::min(signed_len - so_far, 16 * MiB);
221 218
222 if (need_sha1) SHA1_Update(&sha1_ctx, addr + so_far, size); 219 if (need_sha1) SHA1_Update(&sha1_ctx, addr + so_far, size);
223 if (need_sha256) SHA256_Update(&sha256_ctx, addr + so_far, size); 220 if (need_sha256) SHA256_Update(&sha256_ctx, addr + so_far, size);
224 so_far += size; 221 so_far += size;
225 222
226 double f = so_far / (double)signed_len; 223 if (set_progress) {
227 if (f > frac + 0.02 || size == so_far) { 224 double f = so_far / (double)signed_len;
228 ui->SetProgress(f); 225 if (f > frac + 0.02 || size == so_far) {
229 frac = f; 226 set_progress(f);
230 } 227 frac = f;
228 }
231 } 229 }
230 }
232 231
233 uint8_t sha1[SHA_DIGEST_LENGTH]; 232 uint8_t sha1[SHA_DIGEST_LENGTH];
234 SHA1_Final(sha1, &sha1_ctx); 233 SHA1_Final(sha1, &sha1_ctx);
235 uint8_t sha256[SHA256_DIGEST_LENGTH]; 234 uint8_t sha256[SHA256_DIGEST_LENGTH];
236 SHA256_Final(sha256, &sha256_ctx); 235 SHA256_Final(sha256, &sha256_ctx);
237
238 uint8_t* sig_der = nullptr;
239 size_t sig_der_length = 0;
240
241 uint8_t* signature = eocd + eocd_size - signature_start;
242 size_t signature_size = signature_start - FOOTER_SIZE;
243
244 LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start) << ", length: "
245 << signature_size << "): " << print_hex(signature, signature_size);
246 236
247 if (!read_pkcs7(signature, signature_size, &sig_der, &sig_der_length)) { 237 uint8_t* sig_der = nullptr;
248 LOG(ERROR) << "Could not find signature DER block"; 238 size_t sig_der_length = 0;
249 return VERIFY_FAILURE;
250 }
251 239
252 /* 240 uint8_t* signature = eocd + eocd_size - signature_start;
253 * Check to make sure at least one of the keys matches the signature. Since 241 size_t signature_size = signature_start - FOOTER_SIZE;
254 * any key can match, we need to try each before determining a verification
255 * failure has happened.
256 */
257 size_t i = 0;
258 for (const auto& key : keys) {
259 const uint8_t* hash;
260 int hash_nid;
261 switch (key.hash_len) {
262 case SHA_DIGEST_LENGTH:
263 hash = sha1;
264 hash_nid = NID_sha1;
265 break;
266 case SHA256_DIGEST_LENGTH:
267 hash = sha256;
268 hash_nid = NID_sha256;
269 break;
270 default:
271 continue;
272 }
273 242
274 // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that 243 LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start) << ", length: "
275 // the signing tool appends after the signature itself. 244 << signature_size << "): " << print_hex(signature, signature_size);
276 if (key.key_type == Certificate::KEY_TYPE_RSA) {
277 if (!RSA_verify(hash_nid, hash, key.hash_len, sig_der,
278 sig_der_length, key.rsa.get())) {
279 LOG(INFO) << "failed to verify against RSA key " << i;
280 continue;
281 }
282 245
283 LOG(INFO) << "whole-file signature verified against RSA key " << i; 246 if (!read_pkcs7(signature, signature_size, &sig_der, &sig_der_length)) {
284 free(sig_der); 247 LOG(ERROR) << "Could not find signature DER block";
285 return VERIFY_SUCCESS; 248 return VERIFY_FAILURE;
286 } else if (key.key_type == Certificate::KEY_TYPE_EC 249 }
287 && key.hash_len == SHA256_DIGEST_LENGTH) {
288 if (!ECDSA_verify(0, hash, key.hash_len, sig_der,
289 sig_der_length, key.ec.get())) {
290 LOG(INFO) << "failed to verify against EC key " << i;
291 continue;
292 }
293 250
294 LOG(INFO) << "whole-file signature verified against EC key " << i; 251 // Check to make sure at least one of the keys matches the signature. Since any key can match,
295 free(sig_der); 252 // we need to try each before determining a verification failure has happened.
296 return VERIFY_SUCCESS; 253 size_t i = 0;
297 } else { 254 for (const auto& key : keys) {
298 LOG(INFO) << "Unknown key type " << key.key_type; 255 const uint8_t* hash;
299 } 256 int hash_nid;
300 i++; 257 switch (key.hash_len) {
301 } 258 case SHA_DIGEST_LENGTH:
259 hash = sha1;
260 hash_nid = NID_sha1;
261 break;
262 case SHA256_DIGEST_LENGTH:
263 hash = sha256;
264 hash_nid = NID_sha256;
265 break;
266 default:
267 continue;
268 }
269
270 // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that the signing tool appends
271 // after the signature itself.
272 if (key.key_type == Certificate::KEY_TYPE_RSA) {
273 if (!RSA_verify(hash_nid, hash, key.hash_len, sig_der, sig_der_length, key.rsa.get())) {
274 LOG(INFO) << "failed to verify against RSA key " << i;
275 continue;
276 }
277
278 LOG(INFO) << "whole-file signature verified against RSA key " << i;
279 free(sig_der);
280 return VERIFY_SUCCESS;
281 } else if (key.key_type == Certificate::KEY_TYPE_EC && key.hash_len == SHA256_DIGEST_LENGTH) {
282 if (!ECDSA_verify(0, hash, key.hash_len, sig_der, sig_der_length, key.ec.get())) {
283 LOG(INFO) << "failed to verify against EC key " << i;
284 continue;
285 }
286
287 LOG(INFO) << "whole-file signature verified against EC key " << i;
288 free(sig_der);
289 return VERIFY_SUCCESS;
290 } else {
291 LOG(INFO) << "Unknown key type " << key.key_type;
292 }
293 i++;
294 }
302 295
303 if (need_sha1) { 296 if (need_sha1) {
304 LOG(INFO) << "SHA-1 digest: " << print_hex(sha1, SHA_DIGEST_LENGTH); 297 LOG(INFO) << "SHA-1 digest: " << print_hex(sha1, SHA_DIGEST_LENGTH);
305 } 298 }
306 if (need_sha256) { 299 if (need_sha256) {
307 LOG(INFO) << "SHA-256 digest: " << print_hex(sha256, SHA256_DIGEST_LENGTH); 300 LOG(INFO) << "SHA-256 digest: " << print_hex(sha256, SHA256_DIGEST_LENGTH);
308 } 301 }
309 free(sig_der); 302 free(sig_der);
310 LOG(ERROR) << "failed to verify whole-file signature"; 303 LOG(ERROR) << "failed to verify whole-file signature";
311 return VERIFY_FAILURE; 304 return VERIFY_FAILURE;
312} 305}
313 306
314std::unique_ptr<RSA, RSADeleter> parse_rsa_key(FILE* file, uint32_t exponent) { 307std::unique_ptr<RSA, RSADeleter> parse_rsa_key(FILE* file, uint32_t exponent) {