|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #ifdef XP_WIN |
|
6 #ifndef WIN32_LEAN_AND_MEAN |
|
7 #define WIN32_LEAN_AND_MEAN |
|
8 #endif |
|
9 #endif |
|
10 |
|
11 #include <sys/types.h> |
|
12 #include <sys/stat.h> |
|
13 #include <fcntl.h> |
|
14 #include <stdlib.h> |
|
15 #include <string.h> |
|
16 #include "mar_private.h" |
|
17 #include "mar.h" |
|
18 #include "cryptox.h" |
|
19 |
|
20 int mar_extract_and_verify_signatures_fp(FILE *fp, |
|
21 CryptoX_ProviderHandle provider, |
|
22 CryptoX_PublicKey *keys, |
|
23 uint32_t keyCount); |
|
24 int mar_verify_signatures_for_fp(FILE *fp, |
|
25 CryptoX_ProviderHandle provider, |
|
26 CryptoX_PublicKey *keys, |
|
27 const uint8_t * const *extractedSignatures, |
|
28 uint32_t keyCount, |
|
29 uint32_t *numVerified); |
|
30 |
|
31 /** |
|
32 * Reads the specified number of bytes from the file pointer and |
|
33 * stores them in the passed buffer. |
|
34 * |
|
35 * @param fp The file pointer to read from. |
|
36 * @param buffer The buffer to store the read results. |
|
37 * @param size The number of bytes to read, buffer must be |
|
38 * at least of this size. |
|
39 * @param ctxs Pointer to the first element in an array of verify context. |
|
40 * @param count The number of elements in ctxs |
|
41 * @param err The name of what is being written to in case of error. |
|
42 * @return 0 on success |
|
43 * -1 on read error |
|
44 * -2 on verify update error |
|
45 */ |
|
46 int |
|
47 ReadAndUpdateVerifyContext(FILE *fp, |
|
48 void *buffer, |
|
49 uint32_t size, |
|
50 CryptoX_SignatureHandle *ctxs, |
|
51 uint32_t count, |
|
52 const char *err) |
|
53 { |
|
54 uint32_t k; |
|
55 if (!fp || !buffer || !ctxs || count == 0 || !err) { |
|
56 fprintf(stderr, "ERROR: Invalid parameter specified.\n"); |
|
57 return CryptoX_Error; |
|
58 } |
|
59 |
|
60 if (!size) { |
|
61 return CryptoX_Success; |
|
62 } |
|
63 |
|
64 if (fread(buffer, size, 1, fp) != 1) { |
|
65 fprintf(stderr, "ERROR: Could not read %s\n", err); |
|
66 return CryptoX_Error; |
|
67 } |
|
68 |
|
69 for (k = 0; k < count; k++) { |
|
70 if (CryptoX_Failed(CryptoX_VerifyUpdate(&ctxs[k], buffer, size))) { |
|
71 fprintf(stderr, "ERROR: Could not update verify context for %s\n", err); |
|
72 return -2; |
|
73 } |
|
74 } |
|
75 return CryptoX_Success; |
|
76 } |
|
77 |
|
78 /** |
|
79 * Verifies a MAR file by verifying each signature with the corresponding |
|
80 * certificate. That is, the first signature will be verified using the first |
|
81 * certificate given, the second signature will be verified using the second |
|
82 * certificate given, etc. The signature count must exactly match the number of |
|
83 * certificates given, and all signature verifications must succeed. |
|
84 * This is only used by the signmar program when used with arguments to verify |
|
85 * a MAR. This should not be used to verify a MAR that will be extracted in the |
|
86 * same operation by updater code. This function prints the error message if |
|
87 * verification fails. |
|
88 * |
|
89 * @param pathToMARFile The path of the MAR file to verify. |
|
90 * @param certData Pointer to the first element in an array of certificate |
|
91 * file data. |
|
92 * @param certDataSizes Pointer to the first element in an array for size of the |
|
93 * cert data. |
|
94 * @param certNames Pointer to the first element in an array of certificate names. |
|
95 * Used only if compiled as NSS, specifies the certificate names |
|
96 * @param certCount The number of elements in certData, certDataSizes, and certNames |
|
97 * @return 0 on success |
|
98 * a negative number if there was an error |
|
99 * a positive number if the signature does not verify |
|
100 */ |
|
101 int |
|
102 mar_verify_signatures(const char *pathToMARFile, |
|
103 const uint8_t * const *certData, |
|
104 const uint32_t *certDataSizes, |
|
105 const char * const *certNames, |
|
106 uint32_t certCount) { |
|
107 int rv; |
|
108 CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue; |
|
109 CryptoX_Certificate certs[MAX_SIGNATURES]; |
|
110 CryptoX_PublicKey keys[MAX_SIGNATURES]; |
|
111 FILE *fp; |
|
112 uint32_t k; |
|
113 |
|
114 memset(certs, 0, sizeof(certs)); |
|
115 memset(keys, 0, sizeof(keys)); |
|
116 |
|
117 if (!pathToMARFile || certCount == 0) { |
|
118 fprintf(stderr, "ERROR: Invalid parameter specified.\n"); |
|
119 return CryptoX_Error; |
|
120 } |
|
121 |
|
122 fp = fopen(pathToMARFile, "rb"); |
|
123 if (!fp) { |
|
124 fprintf(stderr, "ERROR: Could not open MAR file.\n"); |
|
125 return CryptoX_Error; |
|
126 } |
|
127 |
|
128 if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) { |
|
129 fclose(fp); |
|
130 fprintf(stderr, "ERROR: Could not init crytpo library.\n"); |
|
131 return CryptoX_Error; |
|
132 } |
|
133 |
|
134 /* Load the certs and keys */ |
|
135 for (k = 0; k < certCount; k++) { |
|
136 if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData[k], certDataSizes[k], |
|
137 &keys[k], certNames[k], &certs[k]))) { |
|
138 fclose(fp); |
|
139 fprintf(stderr, "ERROR: Could not load public key.\n"); |
|
140 return CryptoX_Error; |
|
141 } |
|
142 } |
|
143 |
|
144 rv = mar_extract_and_verify_signatures_fp(fp, provider, keys, certCount); |
|
145 fclose(fp); |
|
146 |
|
147 /* Cleanup the allocated keys and certs */ |
|
148 for (k = 0; k < certCount; k++) { |
|
149 if (keys[k]) { |
|
150 CryptoX_FreePublicKey(&keys[k]); |
|
151 } |
|
152 |
|
153 if (certs[k]) { |
|
154 CryptoX_FreeCertificate(&certs[k]); |
|
155 } |
|
156 } |
|
157 return rv; |
|
158 } |
|
159 |
|
160 #ifdef XP_WIN |
|
161 /** |
|
162 * Verifies a MAR file by verifying each signature with the corresponding |
|
163 * certificate. That is, the first signature will be verified using the first |
|
164 * certificate given, the second signature will be verified using the second |
|
165 * certificate given, etc. The signature count must exactly match the number of |
|
166 * certificates given, and all signature verifications must succeed. |
|
167 * |
|
168 * @param pathToMARFile The path of the MAR file who's signature |
|
169 * should be calculated |
|
170 * @param certData Pointer to the first element in an array of |
|
171 * certificate data |
|
172 * @param certDataSizes Pointer to the first element in an array for size of |
|
173 * the data stored |
|
174 * @param certCount The number of elements in certData and certDataSizes |
|
175 * @return 0 on success |
|
176 */ |
|
177 int |
|
178 mar_verify_signaturesW(MarFile *mar, |
|
179 const uint8_t * const *certData, |
|
180 const uint32_t *certDataSizes, |
|
181 uint32_t certCount) { |
|
182 int rv = -1; |
|
183 CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue; |
|
184 CryptoX_Certificate certs[MAX_SIGNATURES]; |
|
185 CryptoX_PublicKey keys[MAX_SIGNATURES]; |
|
186 uint32_t k; |
|
187 |
|
188 memset(certs, 0, sizeof(certs)); |
|
189 memset(keys, 0, sizeof(keys)); |
|
190 |
|
191 if (!mar || !certData || !certDataSizes || certCount == 0) { |
|
192 fprintf(stderr, "ERROR: Invalid parameter specified.\n"); |
|
193 goto failure; |
|
194 } |
|
195 |
|
196 if (!mar->fp) { |
|
197 fprintf(stderr, "ERROR: MAR file is not open.\n"); |
|
198 goto failure; |
|
199 } |
|
200 |
|
201 if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) { |
|
202 fprintf(stderr, "ERROR: Could not init crytpo library.\n"); |
|
203 goto failure; |
|
204 } |
|
205 |
|
206 for (k = 0; k < certCount; ++k) { |
|
207 if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData[k], certDataSizes[k], |
|
208 &keys[k], "", &certs[k]))) { |
|
209 fprintf(stderr, "ERROR: Could not load public key.\n"); |
|
210 goto failure; |
|
211 } |
|
212 } |
|
213 |
|
214 rv = mar_extract_and_verify_signatures_fp(mar->fp, provider, keys, certCount); |
|
215 |
|
216 failure: |
|
217 |
|
218 for (k = 0; k < certCount; ++k) { |
|
219 if (keys[k]) { |
|
220 CryptoX_FreePublicKey(&keys[k]); |
|
221 } |
|
222 |
|
223 if (certs[k]) { |
|
224 CryptoX_FreeCertificate(&certs[k]); |
|
225 } |
|
226 } |
|
227 |
|
228 return rv; |
|
229 } |
|
230 #endif |
|
231 |
|
232 /** |
|
233 * Extracts each signature from the specified MAR file, |
|
234 * then calls mar_verify_signatures_for_fp to verify each signature. |
|
235 * |
|
236 * @param fp An opened MAR file handle |
|
237 * @param provider A library provider |
|
238 * @param keys The public keys to use to verify the MAR |
|
239 * @param keyCount The number of keys pointed to by keys |
|
240 * @return 0 on success |
|
241 */ |
|
242 int |
|
243 mar_extract_and_verify_signatures_fp(FILE *fp, |
|
244 CryptoX_ProviderHandle provider, |
|
245 CryptoX_PublicKey *keys, |
|
246 uint32_t keyCount) { |
|
247 char buf[5] = {0}; |
|
248 uint32_t signatureCount, signatureLen, numVerified = 0; |
|
249 uint32_t signatureAlgorithmIDs[MAX_SIGNATURES]; |
|
250 int rv = -1; |
|
251 int64_t curPos; |
|
252 uint8_t *extractedSignatures[MAX_SIGNATURES]; |
|
253 uint32_t i; |
|
254 |
|
255 memset(signatureAlgorithmIDs, 0, sizeof(signatureAlgorithmIDs)); |
|
256 memset(extractedSignatures, 0, sizeof(extractedSignatures)); |
|
257 |
|
258 if (!fp) { |
|
259 fprintf(stderr, "ERROR: Invalid file pointer passed.\n"); |
|
260 return CryptoX_Error; |
|
261 } |
|
262 |
|
263 /* To protect against invalid MAR files, we assumes that the MAR file |
|
264 size is less than or equal to MAX_SIZE_OF_MAR_FILE. */ |
|
265 if (fseeko(fp, 0, SEEK_END)) { |
|
266 fprintf(stderr, "ERROR: Could not seek to the end of the MAR file.\n"); |
|
267 return CryptoX_Error; |
|
268 } |
|
269 if (ftello(fp) > MAX_SIZE_OF_MAR_FILE) { |
|
270 fprintf(stderr, "ERROR: MAR file is too large to be verified.\n"); |
|
271 return CryptoX_Error; |
|
272 } |
|
273 |
|
274 /* Skip to the start of the signature block */ |
|
275 if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) { |
|
276 fprintf(stderr, "ERROR: Could not seek to the signature block.\n"); |
|
277 return CryptoX_Error; |
|
278 } |
|
279 |
|
280 /* Get the number of signatures */ |
|
281 if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) { |
|
282 fprintf(stderr, "ERROR: Could not read number of signatures.\n"); |
|
283 return CryptoX_Error; |
|
284 } |
|
285 signatureCount = ntohl(signatureCount); |
|
286 |
|
287 /* Check that we have less than the max amount of signatures so we don't |
|
288 waste too much of either updater's or signmar's time. */ |
|
289 if (signatureCount > MAX_SIGNATURES) { |
|
290 fprintf(stderr, "ERROR: At most %d signatures can be specified.\n", |
|
291 MAX_SIGNATURES); |
|
292 return CryptoX_Error; |
|
293 } |
|
294 |
|
295 for (i = 0; i < signatureCount; i++) { |
|
296 /* Get the signature algorithm ID */ |
|
297 if (fread(&signatureAlgorithmIDs[i], sizeof(uint32_t), 1, fp) != 1) { |
|
298 fprintf(stderr, "ERROR: Could not read signatures algorithm ID.\n"); |
|
299 return CryptoX_Error; |
|
300 } |
|
301 signatureAlgorithmIDs[i] = ntohl(signatureAlgorithmIDs[i]); |
|
302 |
|
303 if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) { |
|
304 fprintf(stderr, "ERROR: Could not read signatures length.\n"); |
|
305 return CryptoX_Error; |
|
306 } |
|
307 signatureLen = ntohl(signatureLen); |
|
308 |
|
309 /* To protected against invalid input make sure the signature length |
|
310 isn't too big. */ |
|
311 if (signatureLen > MAX_SIGNATURE_LENGTH) { |
|
312 fprintf(stderr, "ERROR: Signature length is too large to verify.\n"); |
|
313 return CryptoX_Error; |
|
314 } |
|
315 |
|
316 extractedSignatures[i] = malloc(signatureLen); |
|
317 if (!extractedSignatures[i]) { |
|
318 fprintf(stderr, "ERROR: Could allocate buffer for signature.\n"); |
|
319 return CryptoX_Error; |
|
320 } |
|
321 if (fread(extractedSignatures[i], signatureLen, 1, fp) != 1) { |
|
322 fprintf(stderr, "ERROR: Could not read extracted signature.\n"); |
|
323 for (i = 0; i < signatureCount; ++i) { |
|
324 free(extractedSignatures[i]); |
|
325 } |
|
326 return CryptoX_Error; |
|
327 } |
|
328 |
|
329 /* We don't try to verify signatures we don't know about */ |
|
330 if (signatureAlgorithmIDs[i] != 1) { |
|
331 fprintf(stderr, "ERROR: Unknown signature algorithm ID.\n"); |
|
332 for (i = 0; i < signatureCount; ++i) { |
|
333 free(extractedSignatures[i]); |
|
334 } |
|
335 return CryptoX_Error; |
|
336 } |
|
337 } |
|
338 |
|
339 curPos = ftello(fp); |
|
340 rv = mar_verify_signatures_for_fp(fp, |
|
341 provider, |
|
342 keys, |
|
343 (const uint8_t * const *)extractedSignatures, |
|
344 signatureCount, |
|
345 &numVerified); |
|
346 for (i = 0; i < signatureCount; ++i) { |
|
347 free(extractedSignatures[i]); |
|
348 } |
|
349 |
|
350 /* If we reached here and we verified every |
|
351 signature, return success. */ |
|
352 if (numVerified == signatureCount && keyCount == numVerified) { |
|
353 return CryptoX_Success; |
|
354 } |
|
355 |
|
356 if (numVerified == 0) { |
|
357 fprintf(stderr, "ERROR: Not all signatures were verified.\n"); |
|
358 } else { |
|
359 fprintf(stderr, "ERROR: Only %d of %d signatures were verified.\n", |
|
360 numVerified, signatureCount); |
|
361 } |
|
362 return CryptoX_Error; |
|
363 } |
|
364 |
|
365 /** |
|
366 * Verifies a MAR file by verifying each signature with the corresponding |
|
367 * certificate. That is, the first signature will be verified using the first |
|
368 * certificate given, the second signature will be verified using the second |
|
369 * certificate given, etc. The signature count must exactly match the number of |
|
370 * certificates given, and all signature verifications must succeed. |
|
371 * |
|
372 * @param fp An opened MAR file handle |
|
373 * @param provider A library provider |
|
374 * @param keys A pointer to the first element in an |
|
375 * array of keys. |
|
376 * @param extractedSignatures Pointer to the first element in an array |
|
377 * of extracted signatures. |
|
378 * @param signatureCount The number of signatures in the MAR file |
|
379 * @param numVerified Out parameter which will be filled with |
|
380 * the number of verified signatures. |
|
381 * This information can be useful for printing |
|
382 * error messages. |
|
383 * @return 0 on success, *numVerified == signatureCount. |
|
384 */ |
|
385 int |
|
386 mar_verify_signatures_for_fp(FILE *fp, |
|
387 CryptoX_ProviderHandle provider, |
|
388 CryptoX_PublicKey *keys, |
|
389 const uint8_t * const *extractedSignatures, |
|
390 uint32_t signatureCount, |
|
391 uint32_t *numVerified) |
|
392 { |
|
393 CryptoX_SignatureHandle signatureHandles[MAX_SIGNATURES]; |
|
394 char buf[BLOCKSIZE]; |
|
395 uint32_t signatureLengths[MAX_SIGNATURES]; |
|
396 uint32_t i; |
|
397 int rv = CryptoX_Error; |
|
398 |
|
399 memset(signatureHandles, 0, sizeof(signatureHandles)); |
|
400 memset(signatureLengths, 0, sizeof(signatureLengths)); |
|
401 |
|
402 if (!extractedSignatures || !numVerified) { |
|
403 fprintf(stderr, "ERROR: Invalid parameter specified.\n"); |
|
404 goto failure; |
|
405 } |
|
406 |
|
407 *numVerified = 0; |
|
408 |
|
409 /* This function is only called when we have at least one signature, |
|
410 but to protected against future people who call this function we |
|
411 make sure a non zero value is passed in. |
|
412 */ |
|
413 if (!signatureCount) { |
|
414 fprintf(stderr, "ERROR: There must be at least one signature.\n"); |
|
415 goto failure; |
|
416 } |
|
417 |
|
418 for (i = 0; i < signatureCount; i++) { |
|
419 if (CryptoX_Failed(CryptoX_VerifyBegin(provider, |
|
420 &signatureHandles[i], &keys[i]))) { |
|
421 fprintf(stderr, "ERROR: Could not initialize signature handle.\n"); |
|
422 goto failure; |
|
423 } |
|
424 } |
|
425 |
|
426 /* Skip to the start of the file */ |
|
427 if (fseeko(fp, 0, SEEK_SET)) { |
|
428 fprintf(stderr, "ERROR: Could not seek to start of the file\n"); |
|
429 goto failure; |
|
430 } |
|
431 |
|
432 /* Bytes 0-3: MAR1 |
|
433 Bytes 4-7: index offset |
|
434 Bytes 8-15: size of entire MAR |
|
435 */ |
|
436 if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, buf, |
|
437 SIGNATURE_BLOCK_OFFSET + |
|
438 sizeof(uint32_t), |
|
439 signatureHandles, |
|
440 signatureCount, |
|
441 "signature block"))) { |
|
442 goto failure; |
|
443 } |
|
444 |
|
445 /* Read the signature block */ |
|
446 for (i = 0; i < signatureCount; i++) { |
|
447 /* Get the signature algorithm ID */ |
|
448 if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, |
|
449 &buf, |
|
450 sizeof(uint32_t), |
|
451 signatureHandles, |
|
452 signatureCount, |
|
453 "signature algorithm ID"))) { |
|
454 goto failure; |
|
455 } |
|
456 |
|
457 if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, |
|
458 &signatureLengths[i], |
|
459 sizeof(uint32_t), |
|
460 signatureHandles, |
|
461 signatureCount, |
|
462 "signature length"))) { |
|
463 goto failure; |
|
464 } |
|
465 signatureLengths[i] = ntohl(signatureLengths[i]); |
|
466 if (signatureLengths[i] > MAX_SIGNATURE_LENGTH) { |
|
467 fprintf(stderr, "ERROR: Embedded signature length is too large.\n"); |
|
468 goto failure; |
|
469 } |
|
470 |
|
471 /* Skip past the signature itself as those are not included */ |
|
472 if (fseeko(fp, signatureLengths[i], SEEK_CUR)) { |
|
473 fprintf(stderr, "ERROR: Could not seek past signature.\n"); |
|
474 goto failure; |
|
475 } |
|
476 } |
|
477 |
|
478 /* Read the rest of the file after the signature block */ |
|
479 while (!feof(fp)) { |
|
480 int numRead = fread(buf, 1, BLOCKSIZE , fp); |
|
481 if (ferror(fp)) { |
|
482 fprintf(stderr, "ERROR: Error reading data block.\n"); |
|
483 goto failure; |
|
484 } |
|
485 |
|
486 for (i = 0; i < signatureCount; i++) { |
|
487 if (CryptoX_Failed(CryptoX_VerifyUpdate(&signatureHandles[i], |
|
488 buf, numRead))) { |
|
489 fprintf(stderr, "ERROR: Error updating verify context with" |
|
490 " data block.\n"); |
|
491 goto failure; |
|
492 } |
|
493 } |
|
494 } |
|
495 |
|
496 /* Verify the signatures */ |
|
497 for (i = 0; i < signatureCount; i++) { |
|
498 if (CryptoX_Failed(CryptoX_VerifySignature(&signatureHandles[i], |
|
499 &keys[i], |
|
500 extractedSignatures[i], |
|
501 signatureLengths[i]))) { |
|
502 fprintf(stderr, "ERROR: Error verifying signature.\n"); |
|
503 goto failure; |
|
504 } |
|
505 ++*numVerified; |
|
506 } |
|
507 |
|
508 rv = CryptoX_Success; |
|
509 failure: |
|
510 for (i = 0; i < signatureCount; i++) { |
|
511 CryptoX_FreeSignatureHandle(&signatureHandles[i]); |
|
512 } |
|
513 |
|
514 return rv; |
|
515 } |