summaryrefslogtreecommitdiffstats
path: root/onlineupdate/source/libmar/verify/mar_verify.c
diff options
context:
space:
mode:
Diffstat (limited to 'onlineupdate/source/libmar/verify/mar_verify.c')
-rw-r--r--onlineupdate/source/libmar/verify/mar_verify.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/onlineupdate/source/libmar/verify/mar_verify.c b/onlineupdate/source/libmar/verify/mar_verify.c
new file mode 100644
index 000000000..9f33f8bad
--- /dev/null
+++ b/onlineupdate/source/libmar/verify/mar_verify.c
@@ -0,0 +1,466 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <onlineupdate/mar_private.h>
+#include <onlineupdate/mar.h>
+#include "cryptox.h"
+
+int
+mar_read_entire_file(const char * filePath, uint32_t maxSize,
+ /*out*/ const uint8_t * *data,
+ /*out*/ uint32_t *size)
+{
+ int result;
+ FILE * f;
+
+ if (!filePath || !data || !size) {
+ return -1;
+ }
+
+ f = fopen(filePath, "rb");
+ if (!f) {
+ return -1;
+ }
+
+ result = -1;
+ if (!fseeko(f, 0, SEEK_END)) {
+ int64_t fileSize = ftello(f);
+ if (fileSize > 0 && fileSize <= maxSize && !fseeko(f, 0, SEEK_SET)) {
+ unsigned char * fileData;
+
+ *size = (unsigned int) fileSize;
+ fileData = malloc(*size);
+ if (fileData) {
+ if (fread(fileData, *size, 1, f) == 1) {
+ *data = fileData;
+ result = 0;
+ } else {
+ free(fileData);
+ }
+ }
+ }
+ }
+
+ fclose(f);
+
+ return result;
+}
+
+int mar_extract_and_verify_signatures_fp(FILE *fp,
+ CryptoX_ProviderHandle provider,
+ CryptoX_PublicKey *keys,
+ uint32_t keyCount);
+int mar_verify_signatures_for_fp(FILE *fp,
+ CryptoX_ProviderHandle provider,
+ CryptoX_PublicKey *keys,
+ const uint8_t * const *extractedSignatures,
+ uint32_t keyCount,
+ uint32_t *numVerified);
+
+/**
+ * Reads the specified number of bytes from the file pointer and
+ * stores them in the passed buffer.
+ *
+ * @param fp The file pointer to read from.
+ * @param buffer The buffer to store the read results.
+ * @param size The number of bytes to read, buffer must be
+ * at least of this size.
+ * @param ctxs Pointer to the first element in an array of verify context.
+ * @param count The number of elements in ctxs
+ * @param err The name of what is being written to in case of error.
+ * @return 0 on success
+ * -1 on read error
+ * -2 on verify update error
+*/
+int
+ReadAndUpdateVerifyContext(FILE *fp,
+ void *buffer,
+ uint32_t size,
+ CryptoX_SignatureHandle *ctxs,
+ uint32_t count,
+ const char *err)
+{
+ uint32_t k;
+ if (!fp || !buffer || !ctxs || count == 0 || !err) {
+ fprintf(stderr, "ERROR: Invalid parameter specified.\n");
+ return CryptoX_Error;
+ }
+
+ if (!size) {
+ return CryptoX_Success;
+ }
+
+ if (fread(buffer, size, 1, fp) != 1) {
+ fprintf(stderr, "ERROR: Could not read %s\n", err);
+ return CryptoX_Error;
+ }
+
+ for (k = 0; k < count; k++) {
+ if (CryptoX_Failed(CryptoX_VerifyUpdate(&ctxs[k], buffer, size))) {
+ fprintf(stderr, "ERROR: Could not update verify context for %s\n", err);
+ return -2;
+ }
+ }
+ return CryptoX_Success;
+}
+
+/**
+ * Verifies a MAR file by verifying each signature with the corresponding
+ * certificate. That is, the first signature will be verified using the first
+ * certificate given, the second signature will be verified using the second
+ * certificate given, etc. The signature count must exactly match the number of
+ * certificates given, and all signature verifications must succeed.
+ *
+ * @param mar The file who's signature should be calculated
+ * @param certData Pointer to the first element in an array of
+ * certificate data
+ * @param certDataSizes Pointer to the first element in an array for size of
+ * the data stored
+ * @param certCount The number of elements in certData and certDataSizes
+ * @return 0 on success
+*/
+int
+mar_verify_signatures(MarFile *mar,
+ const uint8_t * const *certData,
+ const uint32_t *certDataSizes,
+ uint32_t certCount) {
+ int rv = -1;
+ CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue;
+ CryptoX_PublicKey keys[MAX_SIGNATURES];
+ uint32_t k;
+
+ memset(keys, 0, sizeof(keys));
+
+ if (!mar || !certData || !certDataSizes || certCount == 0) {
+ fprintf(stderr, "ERROR: Invalid parameter specified.\n");
+ goto failure;
+ }
+
+ if (!mar->fp) {
+ fprintf(stderr, "ERROR: MAR file is not open.\n");
+ goto failure;
+ }
+
+ if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) {
+ fprintf(stderr, "ERROR: Could not init crypto library.\n");
+ goto failure;
+ }
+
+ for (k = 0; k < certCount; ++k) {
+ if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData[k], certDataSizes[k],
+ &keys[k]))) {
+ fprintf(stderr, "ERROR: Could not load public key.\n");
+ goto failure;
+ }
+ }
+
+ rv = mar_extract_and_verify_signatures_fp(mar->fp, provider, keys, certCount);
+
+failure:
+
+ for (k = 0; k < certCount; ++k) {
+ if (keys[k]) {
+ CryptoX_FreePublicKey(&keys[k]);
+ }
+ }
+
+ return rv;
+}
+
+/**
+ * Extracts each signature from the specified MAR file,
+ * then calls mar_verify_signatures_for_fp to verify each signature.
+ *
+ * @param fp An opened MAR file handle
+ * @param provider A library provider
+ * @param keys The public keys to use to verify the MAR
+ * @param keyCount The number of keys pointed to by keys
+ * @return 0 on success
+*/
+int
+mar_extract_and_verify_signatures_fp(FILE *fp,
+ CryptoX_ProviderHandle provider,
+ CryptoX_PublicKey *keys,
+ uint32_t keyCount) {
+ uint32_t signatureCount, signatureLen, numVerified = 0;
+ uint32_t signatureAlgorithmIDs[MAX_SIGNATURES];
+ int rv = -1;
+ uint8_t *extractedSignatures[MAX_SIGNATURES];
+ uint32_t i;
+
+ memset(signatureAlgorithmIDs, 0, sizeof(signatureAlgorithmIDs));
+ memset(extractedSignatures, 0, sizeof(extractedSignatures));
+
+ if (!fp) {
+ fprintf(stderr, "ERROR: Invalid file pointer passed.\n");
+ return CryptoX_Error;
+ }
+
+ /* To protect against invalid MAR files, we assume that the MAR file
+ size is less than or equal to MAX_SIZE_OF_MAR_FILE. */
+ if (fseeko(fp, 0, SEEK_END)) {
+ fprintf(stderr, "ERROR: Could not seek to the end of the MAR file.\n");
+ return CryptoX_Error;
+ }
+ if (ftello(fp) > MAX_SIZE_OF_MAR_FILE) {
+ fprintf(stderr, "ERROR: MAR file is too large to be verified.\n");
+ return CryptoX_Error;
+ }
+
+ /* Skip to the start of the signature block */
+ if (fseeko(fp, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) {
+ fprintf(stderr, "ERROR: Could not seek to the signature block.\n");
+ return CryptoX_Error;
+ }
+
+ /* Get the number of signatures */
+ if (fread(&signatureCount, sizeof(signatureCount), 1, fp) != 1) {
+ fprintf(stderr, "ERROR: Could not read number of signatures.\n");
+ return CryptoX_Error;
+ }
+ signatureCount = ntohl(signatureCount);
+
+ /* Check that we have less than the max amount of signatures so we don't
+ waste too much of either updater's or signmar's time. */
+ if (signatureCount > MAX_SIGNATURES) {
+ fprintf(stderr, "ERROR: At most %d signatures can be specified.\n",
+ MAX_SIGNATURES);
+ return CryptoX_Error;
+ }
+
+ for (i = 0; i < signatureCount; i++) {
+ /* Get the signature algorithm ID */
+ if (fread(&signatureAlgorithmIDs[i], sizeof(uint32_t), 1, fp) != 1) {
+ fprintf(stderr, "ERROR: Could not read signatures algorithm ID.\n");
+ return CryptoX_Error;
+ }
+ signatureAlgorithmIDs[i] = ntohl(signatureAlgorithmIDs[i]);
+
+ if (fread(&signatureLen, sizeof(uint32_t), 1, fp) != 1) {
+ fprintf(stderr, "ERROR: Could not read signatures length.\n");
+ return CryptoX_Error;
+ }
+ signatureLen = ntohl(signatureLen);
+
+ /* To protected against invalid input make sure the signature length
+ isn't too big. */
+ if (signatureLen > MAX_SIGNATURE_LENGTH) {
+ fprintf(stderr, "ERROR: Signature length is too large to verify.\n");
+ return CryptoX_Error;
+ }
+
+ extractedSignatures[i] = malloc(signatureLen);
+ if (!extractedSignatures[i]) {
+ fprintf(stderr, "ERROR: Could allocate buffer for signature.\n");
+ return CryptoX_Error;
+ }
+ if (fread(extractedSignatures[i], signatureLen, 1, fp) != 1) {
+ fprintf(stderr, "ERROR: Could not read extracted signature.\n");
+ for (i = 0; i < signatureCount; ++i) {
+ free(extractedSignatures[i]);
+ }
+ return CryptoX_Error;
+ }
+
+ /* We don't try to verify signatures we don't know about */
+ if (signatureAlgorithmIDs[i] != 1) {
+ fprintf(stderr, "ERROR: Unknown signature algorithm ID.\n");
+ for (i = 0; i < signatureCount; ++i) {
+ free(extractedSignatures[i]);
+ }
+ return CryptoX_Error;
+ }
+ }
+
+ rv = mar_verify_signatures_for_fp(fp,
+ provider,
+ keys,
+ (const uint8_t * const *)extractedSignatures,
+ signatureCount,
+ &numVerified);
+ for (i = 0; i < signatureCount; ++i) {
+ free(extractedSignatures[i]);
+ }
+
+ /* If we reached here and we verified every
+ signature, return success. */
+ if (numVerified == signatureCount && keyCount == numVerified) {
+ assert(rv == 0); (void) rv;
+ return CryptoX_Success;
+ }
+
+ if (numVerified == 0) {
+ fprintf(stderr, "ERROR: Not all signatures were verified.\n");
+ } else {
+ fprintf(stderr, "ERROR: Only %d of %d signatures were verified.\n",
+ numVerified, signatureCount);
+ }
+ return CryptoX_Error;
+}
+
+/**
+ * Verifies a MAR file by verifying each signature with the corresponding
+ * certificate. That is, the first signature will be verified using the first
+ * certificate given, the second signature will be verified using the second
+ * certificate given, etc. The signature count must exactly match the number of
+ * certificates given, and all signature verifications must succeed.
+ *
+ * @param fp An opened MAR file handle
+ * @param provider A library provider
+ * @param keys A pointer to the first element in an
+ * array of keys.
+ * @param extractedSignatures Pointer to the first element in an array
+ * of extracted signatures.
+ * @param signatureCount The number of signatures in the MAR file
+ * @param numVerified Out parameter which will be filled with
+ * the number of verified signatures.
+ * This information can be useful for printing
+ * error messages.
+ * @return 0 on success, *numVerified == signatureCount.
+*/
+int
+mar_verify_signatures_for_fp(FILE *fp,
+ CryptoX_ProviderHandle provider,
+ CryptoX_PublicKey *keys,
+ const uint8_t * const *extractedSignatures,
+ uint32_t signatureCount,
+ uint32_t *numVerified)
+{
+ CryptoX_SignatureHandle signatureHandles[MAX_SIGNATURES];
+ char buf[BLOCKSIZE];
+ uint32_t signatureLengths[MAX_SIGNATURES];
+ uint32_t i;
+ int rv = CryptoX_Error;
+
+ (void) provider; (void) keys; // avoid warnings
+
+ memset(signatureHandles, 0, sizeof(signatureHandles));
+ memset(signatureLengths, 0, sizeof(signatureLengths));
+
+ if (!extractedSignatures || !numVerified) {
+ fprintf(stderr, "ERROR: Invalid parameter specified.\n");
+ goto failure;
+ }
+
+ *numVerified = 0;
+
+ /* This function is only called when we have at least one signature,
+ but to protected against future people who call this function we
+ make sure a non zero value is passed in.
+ */
+ if (!signatureCount) {
+ fprintf(stderr, "ERROR: There must be at least one signature.\n");
+ goto failure;
+ }
+
+ for (i = 0; i < signatureCount; i++) {
+ if (CryptoX_Failed(CryptoX_VerifyBegin(provider,
+ &signatureHandles[i], &keys[i]))) {
+ fprintf(stderr, "ERROR: Could not initialize signature handle.\n");
+ goto failure;
+ }
+ }
+
+ /* Skip to the start of the file */
+ if (fseeko(fp, 0, SEEK_SET)) {
+ fprintf(stderr, "ERROR: Could not seek to start of the file\n");
+ goto failure;
+ }
+
+ /* Bytes 0-3: MAR1
+ Bytes 4-7: index offset
+ Bytes 8-15: size of entire MAR
+ */
+ if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, buf,
+ SIGNATURE_BLOCK_OFFSET +
+ sizeof(uint32_t),
+ signatureHandles,
+ signatureCount,
+ "signature block"))) {
+ goto failure;
+ }
+
+ /* Read the signature block */
+ for (i = 0; i < signatureCount; i++) {
+ /* Get the signature algorithm ID */
+ if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp,
+ &buf,
+ sizeof(uint32_t),
+ signatureHandles,
+ signatureCount,
+ "signature algorithm ID"))) {
+ goto failure;
+ }
+
+ if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp,
+ &signatureLengths[i],
+ sizeof(uint32_t),
+ signatureHandles,
+ signatureCount,
+ "signature length"))) {
+ goto failure;
+ }
+ signatureLengths[i] = ntohl(signatureLengths[i]);
+ if (signatureLengths[i] > MAX_SIGNATURE_LENGTH) {
+ fprintf(stderr, "ERROR: Embedded signature length is too large.\n");
+ goto failure;
+ }
+
+ /* Skip past the signature itself as those are not included */
+ if (fseeko(fp, signatureLengths[i], SEEK_CUR)) {
+ fprintf(stderr, "ERROR: Could not seek past signature.\n");
+ goto failure;
+ }
+ }
+
+ /* Read the rest of the file after the signature block */
+ while (!feof(fp)) {
+ int numRead = fread(buf, 1, BLOCKSIZE , fp);
+ if (ferror(fp)) {
+ fprintf(stderr, "ERROR: Error reading data block.\n");
+ goto failure;
+ }
+
+ for (i = 0; i < signatureCount; i++) {
+ if (CryptoX_Failed(CryptoX_VerifyUpdate(&signatureHandles[i],
+ buf, numRead))) {
+ fprintf(stderr, "ERROR: Error updating verify context with"
+ " data block.\n");
+ goto failure;
+ }
+ }
+ }
+
+ /* Verify the signatures */
+ for (i = 0; i < signatureCount; i++) {
+ if (CryptoX_Failed(CryptoX_VerifySignature(&signatureHandles[i],
+ &keys[i],
+ extractedSignatures[i],
+ signatureLengths[i]))) {
+ fprintf(stderr, "ERROR: Error verifying signature.\n");
+ goto failure;
+ }
+ ++*numVerified;
+ }
+
+ rv = CryptoX_Success;
+failure:
+ for (i = 0; i < signatureCount; i++) {
+ CryptoX_FreeSignatureHandle(&signatureHandles[i]);
+ }
+
+ return rv;
+}