/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Archive verify code. * * The Initial Developer of the Original Code is * Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Brian R. Bondy * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifdef XP_WIN #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #endif #include #include #include #include #include #include "mar_private.h" #include "mar.h" #include "cryptox.h" int mar_verify_signature_fp(FILE *fp, CryptoX_ProviderHandle provider, CryptoX_PublicKey key); int mar_verify_signature_for_fp(FILE *fp, CryptoX_ProviderHandle provider, CryptoX_PublicKey key, PRUint32 signatureCount, char *extractedSignature); /** * 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 ctx The verify context. * @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, PRUint32 size, CryptoX_SignatureHandle *ctx, const char *err) { if (!fp || !buffer || !ctx || !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; } if (CryptoX_Failed(CryptoX_VerifyUpdate(ctx, buffer, size))) { fprintf(stderr, "ERROR: Could not update verify context for %s\n", err); return -2; } return CryptoX_Success; } /** * Verifies the embedded signature of the specified file path. * This is only used by the signmar program when used with arguments to verify * a MAR. This should not be used to verify a MAR that will be extracted in the * same operation by updater code. This function prints the error message if * verification fails. * * @param pathToMAR The path of the MAR file who's signature should be checked * @param certData The certificate file data. * @param sizeOfCertData The size of the cert data. * @param certName Used only if compiled as NSS, specifies the certName * @return 0 on success * a negative number if there was an error * a positive number if the signature does not verify */ int mar_verify_signature(const char *pathToMARFile, const char *certData, PRUint32 sizeOfCertData, const char *certName) { int rv; CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue; CryptoX_Certificate cert; CryptoX_PublicKey key; FILE *fp; if (!pathToMARFile || (!certData && !certName)) { fprintf(stderr, "ERROR: Invalid parameter specified.\n"); return CryptoX_Error; } fp = fopen(pathToMARFile, "rb"); if (!fp) { fprintf(stderr, "ERROR: Could not open MAR file.\n"); return CryptoX_Error; } if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) { fclose(fp); fprintf(stderr, "ERROR: Could not init crytpo library.\n"); return CryptoX_Error; } if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData, sizeOfCertData, &key, certName, &cert))) { fclose(fp); fprintf(stderr, "ERROR: Could not load public key.\n"); return CryptoX_Error; } rv = mar_verify_signature_fp(fp, provider, key); fclose(fp); if (key) { CryptoX_FreePublicKey(&key); } if (cert) { CryptoX_FreeCertificate(&cert); } return rv; } #ifdef XP_WIN /** * Verifies a MAR file's signature by making sure at least one * signature verifies. * * @param pathToMARFile The path of the MAR file who's signature * should be calculated * @param certData The certificate data * @param sizeOfCertData The size of the data stored in certData * @return 0 on success */ int mar_verify_signatureW(MarFile *mar, const char *certData, PRUint32 sizeOfCertData) { int rv; CryptoX_ProviderHandle provider = CryptoX_InvalidHandleValue; CryptoX_Certificate cert; CryptoX_PublicKey key; if (!mar || !certData) { fprintf(stderr, "ERROR: Invalid parameter specified.\n"); return CryptoX_Error; } if (!mar->fp) { fprintf(stderr, "ERROR: MAR file is not open.\n"); return CryptoX_Error; } if (CryptoX_Failed(CryptoX_InitCryptoProvider(&provider))) { fprintf(stderr, "ERROR: Could not init crytpo library.\n"); return CryptoX_Error; } if (CryptoX_Failed(CryptoX_LoadPublicKey(provider, certData, sizeOfCertData, &key, "", &cert))) { fprintf(stderr, "ERROR: Could not load public key.\n"); return CryptoX_Error; } rv = mar_verify_signature_fp(mar->fp, provider, key); if (key) { CryptoX_FreePublicKey(&key); } if (cert) { CryptoX_FreeCertificate(&cert); } return rv; } #endif /** * Verifies a MAR file's signature by making sure at least one * signature verifies. * * @param fp An opened MAR file handle * @param provider A library provider * @param key The public key to use to verify the MAR * @return 0 on success */ int mar_verify_signature_fp(FILE *fp, CryptoX_ProviderHandle provider, CryptoX_PublicKey key) { char buf[5] = {0}; PRUint32 signatureAlgorithmID, signatureCount, signatureLen, numVerified = 0; int rv = -1; PRInt64 curPos; char *extractedSignature; PRUint32 i; if (!fp) { fprintf(stderr, "ERROR: Invalid file pointer passed.\n"); return CryptoX_Error; } /* To protect against invalid MAR files, we assumes 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 && numVerified == 0; i++) { /* Get the signature algorithm ID */ if (fread(&signatureAlgorithmID, sizeof(PRUint32), 1, fp) != 1) { fprintf(stderr, "ERROR: Could not read signatures algorithm ID.\n"); return CryptoX_Error; } signatureAlgorithmID = ntohl(signatureAlgorithmID); if (fread(&signatureLen, sizeof(PRUint32), 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; } extractedSignature = malloc(signatureLen); if (!extractedSignature) { fprintf(stderr, "ERROR: Could allocate buffer for signature.\n"); return CryptoX_Error; } if (fread(extractedSignature, signatureLen, 1, fp) != 1) { fprintf(stderr, "ERROR: Could not read extracted signature.\n"); free(extractedSignature); return CryptoX_Error; } /* We don't try to verify signatures we don't know about */ if (1 == signatureAlgorithmID) { curPos = ftello(fp); rv = mar_verify_signature_for_fp(fp, provider, key, signatureCount, extractedSignature); if (CryptoX_Succeeded(rv)) { numVerified++; } free(extractedSignature); if (fseeko(fp, curPos, SEEK_SET)) { fprintf(stderr, "ERROR: Could not seek back to last signature.\n"); return CryptoX_Error; } } else { free(extractedSignature); } } /* If we reached here and we verified at least one signature, return success. */ if (numVerified > 0) { return CryptoX_Success; } else { fprintf(stderr, "ERROR: No signatures were verified.\n"); return CryptoX_Error; } } /** * Verifies if a specific signature ID matches the extracted signature. * * @param fp An opened MAR file handle * @param provider A library provider * @param key The public key to use to verify the MAR * @param signatureCount The number of signatures in the MAR file * @param extractedSignature The signature that should be verified * @return 0 on success */ int mar_verify_signature_for_fp(FILE *fp, CryptoX_ProviderHandle provider, CryptoX_PublicKey key, PRUint32 signatureCount, char *extractedSignature) { CryptoX_SignatureHandle signatureHandle; char buf[BLOCKSIZE]; PRUint32 signatureLen; PRUint32 i; if (!extractedSignature) { fprintf(stderr, "ERROR: Invalid parameter specified.\n"); return CryptoX_Error; } /* 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"); return CryptoX_Error; } CryptoX_VerifyBegin(provider, &signatureHandle, &key); /* 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"); return CryptoX_Error; } /* 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(PRUint32), &signatureHandle, "signature block"))) { return CryptoX_Error; } for (i = 0; i < signatureCount; i++) { /* Get the signature algorithm ID */ if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, &buf, sizeof(PRUint32), &signatureHandle, "signature algorithm ID"))) { return CryptoX_Error; } if (CryptoX_Failed(ReadAndUpdateVerifyContext(fp, &signatureLen, sizeof(PRUint32), &signatureHandle, "signature length"))) { return CryptoX_Error; } signatureLen = ntohl(signatureLen); /* Skip past the signature itself as those are not included */ if (fseeko(fp, signatureLen, SEEK_CUR)) { fprintf(stderr, "ERROR: Could not seek past signature.\n"); return CryptoX_Error; } } while (!feof(fp)) { int numRead = fread(buf, 1, BLOCKSIZE , fp); if (ferror(fp)) { fprintf(stderr, "ERROR: Error reading data block.\n"); return CryptoX_Error; } if (CryptoX_Failed(CryptoX_VerifyUpdate(&signatureHandle, buf, numRead))) { fprintf(stderr, "ERROR: Error updating verify context with" " data block.\n"); return CryptoX_Error; } } if (CryptoX_Failed(CryptoX_VerifySignature(&signatureHandle, &key, extractedSignature, signatureLen))) { fprintf(stderr, "ERROR: Error verifying signature.\n"); return CryptoX_Error; } return CryptoX_Success; }