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