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_cmdline.h" michael@0: #include "mar.h" michael@0: #include "cryptox.h" michael@0: #ifndef XP_WIN michael@0: #include michael@0: #endif michael@0: michael@0: #include "nss_secutil.h" michael@0: #include "base64.h" michael@0: michael@0: /** michael@0: * Initializes the NSS context. michael@0: * michael@0: * @param NSSConfigDir The config dir containing the private key to use michael@0: * @return 0 on success michael@0: * -1 on error michael@0: */ michael@0: int michael@0: NSSInitCryptoContext(const char *NSSConfigDir) michael@0: { michael@0: SECStatus status = NSS_Initialize(NSSConfigDir, michael@0: "", "", SECMOD_DB, NSS_INIT_READONLY); michael@0: if (SECSuccess != status) { michael@0: fprintf(stderr, "ERROR: Could not initialize NSS\n"); michael@0: return -1; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Obtains a signing context. michael@0: * michael@0: * @param ctx A pointer to the signing context to fill michael@0: * @return 0 on success michael@0: * -1 on error michael@0: */ michael@0: int michael@0: NSSSignBegin(const char *certName, michael@0: SGNContext **ctx, michael@0: SECKEYPrivateKey **privKey, michael@0: CERTCertificate **cert, michael@0: uint32_t *signatureLength) michael@0: { michael@0: secuPWData pwdata = { PW_NONE, 0 }; michael@0: if (!certName || !ctx || !privKey || !cert || !signatureLength) { michael@0: fprintf(stderr, "ERROR: Invalid parameter passed to NSSSignBegin\n"); michael@0: return -1; michael@0: } michael@0: michael@0: /* Get the cert and embedded public key out of the database */ michael@0: *cert = PK11_FindCertFromNickname(certName, &pwdata); michael@0: if (!*cert) { michael@0: fprintf(stderr, "ERROR: Could not find cert from nickname\n"); michael@0: return -1; michael@0: } michael@0: michael@0: /* Get the private key out of the database */ michael@0: *privKey = PK11_FindKeyByAnyCert(*cert, &pwdata); michael@0: if (!*privKey) { michael@0: fprintf(stderr, "ERROR: Could not find private key\n"); michael@0: return -1; michael@0: } michael@0: michael@0: *signatureLength = PK11_SignatureLen(*privKey); michael@0: michael@0: if (*signatureLength > BLOCKSIZE) { michael@0: fprintf(stderr, michael@0: "ERROR: Program must be compiled with a larger block size" michael@0: " to support signing with signatures this large: %u.\n", michael@0: *signatureLength); michael@0: return -1; michael@0: } michael@0: michael@0: /* Check that the key length is large enough for our requirements */ michael@0: if (*signatureLength < XP_MIN_SIGNATURE_LEN_IN_BYTES) { michael@0: fprintf(stderr, "ERROR: Key length must be >= %d bytes\n", michael@0: XP_MIN_SIGNATURE_LEN_IN_BYTES); michael@0: return -1; michael@0: } michael@0: michael@0: *ctx = SGN_NewContext (SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE, *privKey); michael@0: if (!*ctx) { michael@0: fprintf(stderr, "ERROR: Could not create signature context\n"); michael@0: return -1; michael@0: } michael@0: michael@0: if (SGN_Begin(*ctx) != SECSuccess) { michael@0: fprintf(stderr, "ERROR: Could not begin signature\n"); michael@0: return -1; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Writes the passed buffer to the file fp and updates the signature contexts. michael@0: * michael@0: * @param fpDest The file pointer to write to. michael@0: * @param buffer The buffer to write. michael@0: * @param size The size of the buffer to write. michael@0: * @param ctxs Pointer to the first element in an array of signature michael@0: * contexts to update. michael@0: * @param ctxCount The number of signature contexts pointed to by 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: * -2 on write error michael@0: * -3 on signature update error michael@0: */ michael@0: int michael@0: WriteAndUpdateSignatures(FILE *fpDest, void *buffer, michael@0: uint32_t size, SGNContext **ctxs, michael@0: uint32_t ctxCount, michael@0: const char *err) michael@0: { michael@0: uint32_t k; michael@0: if (!size) { michael@0: return 0; michael@0: } michael@0: michael@0: if (fwrite(buffer, size, 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write %s\n", err); michael@0: return -2; michael@0: } michael@0: michael@0: for (k = 0; k < ctxCount; ++k) { michael@0: if (SGN_Update(ctxs[k], buffer, size) != SECSuccess) { michael@0: fprintf(stderr, "ERROR: Could not update signature context for %s\n", err); michael@0: return -3; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Adjusts each entry's content offset in the the passed in index by the michael@0: * specified amount. michael@0: * michael@0: * @param indexBuf A buffer containing the MAR index michael@0: * @param indexLength The length of the MAR index michael@0: * @param offsetAmount The amount to adjust each index entry by michael@0: */ michael@0: void michael@0: AdjustIndexContentOffsets(char *indexBuf, uint32_t indexLength, uint32_t offsetAmount) michael@0: { michael@0: uint32_t *offsetToContent; michael@0: char *indexBufLoc = indexBuf; michael@0: michael@0: /* Consume the index and adjust each index by the specified amount */ michael@0: while (indexBufLoc != (indexBuf + indexLength)) { michael@0: /* Adjust the offset */ michael@0: offsetToContent = (uint32_t *)indexBufLoc; michael@0: *offsetToContent = ntohl(*offsetToContent); michael@0: *offsetToContent += offsetAmount; michael@0: *offsetToContent = htonl(*offsetToContent); michael@0: /* Skip past the offset, length, and flags */ michael@0: indexBufLoc += 3 * sizeof(uint32_t); michael@0: indexBufLoc += strlen(indexBufLoc) + 1; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Reads from fpSrc, writes it to fpDest, and updates the signature contexts. michael@0: * michael@0: * @param fpSrc The file pointer to read from. michael@0: * @param fpDest The file pointer to write to. michael@0: * @param buffer The buffer to write. michael@0: * @param size The size of the buffer to write. michael@0: * @param ctxs Pointer to the first element in an array of signature michael@0: * contexts to update. michael@0: * @param ctxCount The number of signature contexts pointed to by 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 write error michael@0: * -3 on signature update error michael@0: */ michael@0: int michael@0: ReadWriteAndUpdateSignatures(FILE *fpSrc, FILE *fpDest, void *buffer, michael@0: uint32_t size, SGNContext **ctxs, michael@0: uint32_t ctxCount, michael@0: const char *err) michael@0: { michael@0: if (!size) { michael@0: return 0; michael@0: } michael@0: michael@0: if (fread(buffer, size, 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could not read %s\n", err); michael@0: return -1; michael@0: } michael@0: michael@0: return WriteAndUpdateSignatures(fpDest, buffer, size, ctxs, ctxCount, err); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * Reads from fpSrc, writes it to fpDest. michael@0: * michael@0: * @param fpSrc The file pointer to read from. michael@0: * @param fpDest The file pointer to write to. michael@0: * @param buffer The buffer to write. michael@0: * @param size The size of the buffer to write. 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 write error michael@0: */ michael@0: int michael@0: ReadAndWrite(FILE *fpSrc, FILE *fpDest, void *buffer, michael@0: uint32_t size, const char *err) michael@0: { michael@0: if (!size) { michael@0: return 0; michael@0: } michael@0: michael@0: if (fread(buffer, size, 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could not read %s\n", err); michael@0: return -1; michael@0: } michael@0: michael@0: if (fwrite(buffer, size, 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write %s\n", err); michael@0: return -2; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /** michael@0: * Writes out a copy of the MAR at src but with the signature block stripped. michael@0: * michael@0: * @param src The path of the source MAR file michael@0: * @param dest The path of the MAR file to write out that michael@0: has no signature block michael@0: * @return 0 on success michael@0: * -1 on error michael@0: */ michael@0: int michael@0: strip_signature_block(const char *src, const char * dest) michael@0: { michael@0: uint32_t offsetToIndex, dstOffsetToIndex, indexLength, michael@0: numSignatures = 0, leftOver; michael@0: int32_t stripAmount = 0; michael@0: int64_t oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR, numBytesToCopy, michael@0: numChunks, i; michael@0: FILE *fpSrc = NULL, *fpDest = NULL; michael@0: int rv = -1, hasSignatureBlock; michael@0: char buf[BLOCKSIZE]; michael@0: char *indexBuf = NULL, *indexBufLoc; michael@0: michael@0: if (!src || !dest) { michael@0: fprintf(stderr, "ERROR: Invalid parameter passed in.\n"); michael@0: return -1; michael@0: } michael@0: michael@0: fpSrc = fopen(src, "rb"); michael@0: if (!fpSrc) { michael@0: fprintf(stderr, "ERROR: could not open source file: %s\n", src); michael@0: goto failure; michael@0: } michael@0: michael@0: fpDest = fopen(dest, "wb"); michael@0: if (!fpDest) { michael@0: fprintf(stderr, "ERROR: could not create target file: %s\n", dest); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Determine if the source MAR file has the new fields for signing or not */ michael@0: if (get_mar_file_info(src, &hasSignatureBlock, NULL, NULL, NULL, NULL)) { michael@0: fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* MAR ID */ michael@0: if (ReadAndWrite(fpSrc, fpDest, buf, MAR_ID_SIZE, "MAR ID")) { michael@0: goto failure; michael@0: } michael@0: michael@0: /* Offset to index */ michael@0: if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could not read offset\n"); michael@0: goto failure; michael@0: } michael@0: offsetToIndex = ntohl(offsetToIndex); michael@0: michael@0: /* Get the real size of the MAR */ michael@0: oldPos = ftello(fpSrc); michael@0: if (fseeko(fpSrc, 0, SEEK_END)) { michael@0: fprintf(stderr, "ERROR: Could not seek to end of file.\n"); michael@0: goto failure; michael@0: } michael@0: realSizeOfSrcMAR = ftello(fpSrc); michael@0: if (fseeko(fpSrc, oldPos, SEEK_SET)) { michael@0: fprintf(stderr, "ERROR: Could not seek back to current location.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: if (hasSignatureBlock) { michael@0: /* Get the MAR length and adjust its size */ michael@0: if (fread(&sizeOfEntireMAR, michael@0: sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could read mar size\n"); michael@0: goto failure; michael@0: } michael@0: sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); michael@0: if (sizeOfEntireMAR != realSizeOfSrcMAR) { michael@0: fprintf(stderr, "ERROR: Source MAR is not of the right size\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Get the num signatures in the source file so we know what to strip */ michael@0: if (fread(&numSignatures, sizeof(numSignatures), 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could read num signatures\n"); michael@0: goto failure; michael@0: } michael@0: numSignatures = ntohl(numSignatures); michael@0: michael@0: for (i = 0; i < numSignatures; i++) { michael@0: uint32_t signatureLen; michael@0: michael@0: /* Skip past the signature algorithm ID */ michael@0: if (fseeko(fpSrc, sizeof(uint32_t), SEEK_CUR)) { michael@0: fprintf(stderr, "ERROR: Could not skip past signature algorithm ID\n"); michael@0: } michael@0: michael@0: /* Read in the length of the signature so we know how far to skip */ michael@0: if (fread(&signatureLen, sizeof(uint32_t), 1, fpSrc) != 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: /* Skip past the signature */ michael@0: if (fseeko(fpSrc, signatureLen, SEEK_CUR)) { michael@0: fprintf(stderr, "ERROR: Could not skip past signature algorithm ID\n"); michael@0: } michael@0: michael@0: stripAmount += sizeof(uint32_t) + sizeof(uint32_t) + signatureLen; michael@0: } michael@0: michael@0: } else { michael@0: sizeOfEntireMAR = realSizeOfSrcMAR; michael@0: numSignatures = 0; michael@0: } michael@0: michael@0: if (((int64_t)offsetToIndex) > sizeOfEntireMAR) { michael@0: fprintf(stderr, "ERROR: Offset to index is larger than the file size.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: dstOffsetToIndex = offsetToIndex; michael@0: if (!hasSignatureBlock) { michael@0: dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); michael@0: } michael@0: dstOffsetToIndex -= stripAmount; michael@0: michael@0: /* Write out the index offset */ michael@0: dstOffsetToIndex = htonl(dstOffsetToIndex); michael@0: if (fwrite(&dstOffsetToIndex, sizeof(dstOffsetToIndex), 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write offset to index\n"); michael@0: goto failure; michael@0: } michael@0: dstOffsetToIndex = ntohl(dstOffsetToIndex); michael@0: michael@0: /* Write out the new MAR file size */ michael@0: if (!hasSignatureBlock) { michael@0: sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); michael@0: } michael@0: sizeOfEntireMAR -= stripAmount; michael@0: michael@0: /* Write out the MAR size */ michael@0: sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR); michael@0: if (fwrite(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write size of MAR\n"); michael@0: goto failure; michael@0: } michael@0: sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); michael@0: michael@0: /* Write out the number of signatures, which is 0 */ michael@0: numSignatures = 0; michael@0: if (fwrite(&numSignatures, sizeof(numSignatures), 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write out num signatures\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Write out the rest of the MAR excluding the index header and index michael@0: offsetToIndex unfortunately has to remain 32-bit because for backwards michael@0: compatibility with the old MAR file format. */ michael@0: if (ftello(fpSrc) > ((int64_t)offsetToIndex)) { michael@0: fprintf(stderr, "ERROR: Index offset is too small.\n"); michael@0: goto failure; michael@0: } michael@0: numBytesToCopy = ((int64_t)offsetToIndex) - ftello(fpSrc); michael@0: numChunks = numBytesToCopy / BLOCKSIZE; michael@0: leftOver = numBytesToCopy % BLOCKSIZE; michael@0: michael@0: /* Read each file and write it to the MAR file */ michael@0: for (i = 0; i < numChunks; ++i) { michael@0: if (ReadAndWrite(fpSrc, fpDest, buf, BLOCKSIZE, "content block")) { michael@0: goto failure; michael@0: } michael@0: } michael@0: michael@0: /* Write out the left over */ michael@0: if (ReadAndWrite(fpSrc, fpDest, buf, michael@0: leftOver, "left over content block")) { michael@0: goto failure; michael@0: } michael@0: michael@0: /* Length of the index */ michael@0: if (ReadAndWrite(fpSrc, fpDest, &indexLength, michael@0: sizeof(indexLength), "index length")) { michael@0: goto failure; michael@0: } michael@0: indexLength = ntohl(indexLength); michael@0: michael@0: /* Consume the index and adjust each index by the difference */ michael@0: indexBuf = malloc(indexLength); michael@0: indexBufLoc = indexBuf; michael@0: if (fread(indexBuf, indexLength, 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could not read index\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Adjust each entry in the index */ michael@0: if (hasSignatureBlock) { michael@0: AdjustIndexContentOffsets(indexBuf, indexLength, -stripAmount); michael@0: } else { michael@0: AdjustIndexContentOffsets(indexBuf, indexLength, michael@0: sizeof(sizeOfEntireMAR) + michael@0: sizeof(numSignatures) - michael@0: stripAmount); michael@0: } michael@0: michael@0: if (fwrite(indexBuf, indexLength, 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write index\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: rv = 0; michael@0: failure: michael@0: if (fpSrc) { michael@0: fclose(fpSrc); michael@0: } michael@0: michael@0: if (fpDest) { michael@0: fclose(fpDest); michael@0: } michael@0: michael@0: if (rv) { michael@0: remove(dest); michael@0: } michael@0: michael@0: if (indexBuf) { michael@0: free(indexBuf); michael@0: } michael@0: michael@0: if (rv) { michael@0: remove(dest); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * Extracts a signature from a MAR file, base64 encodes it, and writes it out michael@0: * michael@0: * @param src The path of the source MAR file michael@0: * @param sigIndex The index of the signature to extract michael@0: * @param dest The path of file to write the signature to michael@0: * @return 0 on success michael@0: * -1 on error michael@0: */ michael@0: int michael@0: extract_signature(const char *src, uint32_t sigIndex, const char * dest) michael@0: { michael@0: FILE *fpSrc = NULL, *fpDest = NULL; michael@0: uint32_t i; michael@0: uint32_t signatureCount; michael@0: uint32_t signatureLen; michael@0: uint8_t *extractedSignature = NULL; michael@0: char *base64Encoded = NULL; michael@0: int rv = -1; michael@0: if (!src || !dest) { michael@0: fprintf(stderr, "ERROR: Invalid parameter passed in.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: fpSrc = fopen(src, "rb"); michael@0: if (!fpSrc) { michael@0: fprintf(stderr, "ERROR: could not open source file: %s\n", src); michael@0: goto failure; michael@0: } michael@0: michael@0: fpDest = fopen(dest, "wb"); michael@0: if (!fpDest) { michael@0: fprintf(stderr, "ERROR: could not create target file: %s\n", dest); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Skip to the start of the signature block */ michael@0: if (fseeko(fpSrc, SIGNATURE_BLOCK_OFFSET, SEEK_SET)) { michael@0: fprintf(stderr, "ERROR: could not seek to signature block\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Get the number of signatures */ michael@0: if (fread(&signatureCount, sizeof(signatureCount), 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: could not read signature count\n"); michael@0: goto failure; michael@0: } michael@0: signatureCount = ntohl(signatureCount); michael@0: if (sigIndex >= signatureCount) { michael@0: fprintf(stderr, "ERROR: Signature index was out of range\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Skip to the correct signature */ michael@0: for (i = 0; i <= sigIndex; i++) { michael@0: /* skip past the signature algorithm ID */ michael@0: if (fseeko(fpSrc, sizeof(uint32_t), SEEK_CUR)) { michael@0: fprintf(stderr, "ERROR: Could not seek past sig algorithm ID.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Get the signature length */ michael@0: if (fread(&signatureLen, sizeof(signatureLen), 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: could not read signature length\n"); michael@0: goto failure; michael@0: } michael@0: signatureLen = ntohl(signatureLen); michael@0: michael@0: /* Get the signature */ michael@0: extractedSignature = malloc(signatureLen); michael@0: if (fread(extractedSignature, signatureLen, 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: could not read signature\n"); michael@0: goto failure; michael@0: } michael@0: } michael@0: michael@0: base64Encoded = BTOA_DataToAscii(extractedSignature, signatureLen); michael@0: if (!base64Encoded) { michael@0: fprintf(stderr, "ERROR: could not obtain base64 encoded data\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: if (fwrite(base64Encoded, strlen(base64Encoded), 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write base64 encoded string\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: rv = 0; michael@0: failure: michael@0: if (base64Encoded) { michael@0: PORT_Free(base64Encoded); michael@0: } michael@0: michael@0: if (extractedSignature) { michael@0: free(extractedSignature); michael@0: } michael@0: michael@0: if (fpSrc) { michael@0: fclose(fpSrc); michael@0: } michael@0: michael@0: if (fpDest) { michael@0: fclose(fpDest); michael@0: } michael@0: michael@0: if (rv) { michael@0: remove(dest); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * Imports a base64 encoded signature into a MAR file michael@0: * michael@0: * @param src The path of the source MAR file michael@0: * @param sigIndex The index of the signature to import michael@0: * @param base64SigFile A file which contains the signature to import michael@0: * @param dest The path of the destination MAR file with replaced signature michael@0: * @return 0 on success michael@0: * -1 on error michael@0: */ michael@0: int michael@0: import_signature(const char *src, uint32_t sigIndex, michael@0: const char *base64SigFile, const char *dest) michael@0: { michael@0: int rv = -1; michael@0: FILE *fpSrc = NULL; michael@0: FILE *fpDest = NULL; michael@0: FILE *fpSigFile = NULL; michael@0: uint32_t i; michael@0: uint32_t signatureCount, signatureLen, signatureAlgorithmID, michael@0: numChunks, leftOver; michael@0: char buf[BLOCKSIZE]; michael@0: uint64_t sizeOfSrcMAR, sizeOfBase64EncodedFile; michael@0: char *passedInSignatureB64 = NULL; michael@0: uint8_t *passedInSignatureRaw = NULL; michael@0: uint8_t *extractedMARSignature = NULL; michael@0: unsigned int passedInSignatureLenRaw; michael@0: michael@0: if (!src || !dest) { michael@0: fprintf(stderr, "ERROR: Invalid parameter passed in.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: fpSrc = fopen(src, "rb"); michael@0: if (!fpSrc) { michael@0: fprintf(stderr, "ERROR: could not open source file: %s\n", src); michael@0: goto failure; michael@0: } michael@0: michael@0: fpDest = fopen(dest, "wb"); michael@0: if (!fpDest) { michael@0: fprintf(stderr, "ERROR: could not open dest file: %s\n", dest); michael@0: goto failure; michael@0: } michael@0: michael@0: fpSigFile = fopen(base64SigFile , "rb"); michael@0: if (!fpSigFile) { michael@0: fprintf(stderr, "ERROR: could not open sig file: %s\n", base64SigFile); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Get the src file size */ michael@0: if (fseeko(fpSrc, 0, SEEK_END)) { michael@0: fprintf(stderr, "ERROR: Could not seek to end of src file.\n"); michael@0: goto failure; michael@0: } michael@0: sizeOfSrcMAR = ftello(fpSrc); michael@0: if (fseeko(fpSrc, 0, SEEK_SET)) { michael@0: fprintf(stderr, "ERROR: Could not seek to start of src file.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Get the sig file size */ michael@0: if (fseeko(fpSigFile, 0, SEEK_END)) { michael@0: fprintf(stderr, "ERROR: Could not seek to end of sig file.\n"); michael@0: goto failure; michael@0: } michael@0: sizeOfBase64EncodedFile= ftello(fpSigFile); michael@0: if (fseeko(fpSigFile, 0, SEEK_SET)) { michael@0: fprintf(stderr, "ERROR: Could not seek to start of sig file.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Read in the base64 encoded signature to import */ michael@0: passedInSignatureB64 = malloc(sizeOfBase64EncodedFile + 1); michael@0: passedInSignatureB64[sizeOfBase64EncodedFile] = '\0'; michael@0: if (fread(passedInSignatureB64, sizeOfBase64EncodedFile, 1, fpSigFile) != 1) { michael@0: fprintf(stderr, "ERROR: Could read b64 sig file.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Decode the base64 encoded data */ michael@0: passedInSignatureRaw = ATOB_AsciiToData(passedInSignatureB64, &passedInSignatureLenRaw); michael@0: if (!passedInSignatureRaw) { michael@0: fprintf(stderr, "ERROR: could not obtain base64 decoded data\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Read everything up until the signature block offset and write it out */ michael@0: if (ReadAndWrite(fpSrc, fpDest, buf, michael@0: SIGNATURE_BLOCK_OFFSET, "signature block offset")) { michael@0: goto failure; michael@0: } michael@0: michael@0: /* Get the number of signatures */ michael@0: if (ReadAndWrite(fpSrc, fpDest, &signatureCount, michael@0: sizeof(signatureCount), "signature count")) { michael@0: goto failure; michael@0: } michael@0: signatureCount = ntohl(signatureCount); michael@0: if (signatureCount > MAX_SIGNATURES) { michael@0: fprintf(stderr, "ERROR: Signature count was out of range\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: if (sigIndex >= signatureCount) { michael@0: fprintf(stderr, "ERROR: Signature index was out of range\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Read and write the whole signature block, but if we reach the michael@0: signature offset, then we should replace it with the specified michael@0: base64 decoded signature */ michael@0: for (i = 0; i < signatureCount; i++) { michael@0: /* Read/Write the signature algorithm ID */ michael@0: if (ReadAndWrite(fpSrc, fpDest, michael@0: &signatureAlgorithmID, michael@0: sizeof(signatureAlgorithmID), "sig algorithm ID")) { michael@0: goto failure; michael@0: } michael@0: michael@0: /* Read/Write the signature length */ michael@0: if (ReadAndWrite(fpSrc, fpDest, michael@0: &signatureLen, sizeof(signatureLen), "sig length")) { michael@0: goto failure; michael@0: } michael@0: signatureLen = ntohl(signatureLen); michael@0: michael@0: /* Get the signature */ michael@0: if (extractedMARSignature) { michael@0: free(extractedMARSignature); michael@0: } michael@0: extractedMARSignature = malloc(signatureLen); michael@0: michael@0: if (sigIndex == i) { michael@0: if (passedInSignatureLenRaw != signatureLen) { michael@0: fprintf(stderr, "ERROR: Signature length must be the same\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: if (fread(extractedMARSignature, signatureLen, 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could not read signature\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: if (fwrite(passedInSignatureRaw, passedInSignatureLenRaw, michael@0: 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write signature\n"); michael@0: goto failure; michael@0: } michael@0: } else { michael@0: if (ReadAndWrite(fpSrc, fpDest, michael@0: extractedMARSignature, signatureLen, "signature")) { michael@0: goto failure; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* We replaced the signature so let's just skip past the rest o the michael@0: file. */ michael@0: numChunks = (sizeOfSrcMAR - ftello(fpSrc)) / BLOCKSIZE; michael@0: leftOver = (sizeOfSrcMAR - ftello(fpSrc)) % BLOCKSIZE; michael@0: michael@0: /* Read each file and write it to the MAR file */ michael@0: for (i = 0; i < numChunks; ++i) { michael@0: if (ReadAndWrite(fpSrc, fpDest, buf, BLOCKSIZE, "content block")) { michael@0: goto failure; michael@0: } michael@0: } michael@0: michael@0: if (ReadAndWrite(fpSrc, fpDest, buf, leftOver, "left over content block")) { michael@0: goto failure; michael@0: } michael@0: michael@0: rv = 0; michael@0: michael@0: failure: michael@0: michael@0: if (fpSrc) { michael@0: fclose(fpSrc); michael@0: } michael@0: michael@0: if (fpDest) { michael@0: fclose(fpDest); michael@0: } michael@0: michael@0: if (fpSigFile) { michael@0: fclose(fpSigFile); michael@0: } michael@0: michael@0: if (rv) { michael@0: remove(dest); michael@0: } michael@0: michael@0: if (extractedMARSignature) { michael@0: free(extractedMARSignature); michael@0: } michael@0: michael@0: if (passedInSignatureB64) { michael@0: free(passedInSignatureB64); michael@0: } michael@0: michael@0: if (passedInSignatureRaw) { michael@0: PORT_Free(passedInSignatureRaw); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * Writes out a copy of the MAR at src but with embedded signatures. michael@0: * The passed in MAR file must not already be signed or an error will michael@0: * be returned. michael@0: * michael@0: * @param NSSConfigDir The NSS directory containing the private key for signing michael@0: * @param certNames The nicknames of the certificate to use for signing michael@0: * @param certCount The number of certificate names contained in certNames. michael@0: * One signature will be produced for each certificate. michael@0: * @param src The path of the source MAR file to sign michael@0: * @param dest The path of the MAR file to write out that is signed michael@0: * @return 0 on success michael@0: * -1 on error michael@0: */ michael@0: int michael@0: mar_repackage_and_sign(const char *NSSConfigDir, michael@0: const char * const *certNames, michael@0: uint32_t certCount, michael@0: const char *src, michael@0: const char *dest) michael@0: { michael@0: uint32_t offsetToIndex, dstOffsetToIndex, indexLength, michael@0: numSignatures = 0, leftOver, michael@0: signatureAlgorithmID, signatureSectionLength = 0; michael@0: uint32_t signatureLengths[MAX_SIGNATURES]; michael@0: int64_t oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR, michael@0: signaturePlaceholderOffset, numBytesToCopy, michael@0: numChunks, i; michael@0: FILE *fpSrc = NULL, *fpDest = NULL; michael@0: int rv = -1, hasSignatureBlock; michael@0: SGNContext *ctxs[MAX_SIGNATURES]; michael@0: SECItem secItems[MAX_SIGNATURES]; michael@0: char buf[BLOCKSIZE]; michael@0: SECKEYPrivateKey *privKeys[MAX_SIGNATURES]; michael@0: CERTCertificate *certs[MAX_SIGNATURES]; michael@0: char *indexBuf = NULL, *indexBufLoc; michael@0: uint32_t k; michael@0: michael@0: memset(signatureLengths, 0, sizeof(signatureLengths)); michael@0: memset(ctxs, 0, sizeof(ctxs)); michael@0: memset(secItems, 0, sizeof(secItems)); michael@0: memset(privKeys, 0, sizeof(privKeys)); michael@0: memset(certs, 0, sizeof(certs)); michael@0: michael@0: if (!NSSConfigDir || !certNames || certCount == 0 || !src || !dest) { michael@0: fprintf(stderr, "ERROR: Invalid parameter passed in.\n"); michael@0: return -1; michael@0: } michael@0: michael@0: if (NSSInitCryptoContext(NSSConfigDir)) { michael@0: fprintf(stderr, "ERROR: Could not init config dir: %s\n", NSSConfigDir); michael@0: goto failure; michael@0: } michael@0: michael@0: PK11_SetPasswordFunc(SECU_GetModulePassword); michael@0: michael@0: fpSrc = fopen(src, "rb"); michael@0: if (!fpSrc) { michael@0: fprintf(stderr, "ERROR: could not open source file: %s\n", src); michael@0: goto failure; michael@0: } michael@0: michael@0: fpDest = fopen(dest, "wb"); michael@0: if (!fpDest) { michael@0: fprintf(stderr, "ERROR: could not create target file: %s\n", dest); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Determine if the source MAR file has the new fields for signing or not */ michael@0: if (get_mar_file_info(src, &hasSignatureBlock, NULL, NULL, NULL, NULL)) { michael@0: fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: for (k = 0; k < certCount; k++) { michael@0: if (NSSSignBegin(certNames[k], &ctxs[k], &privKeys[k], michael@0: &certs[k], &signatureLengths[k])) { michael@0: fprintf(stderr, "ERROR: NSSSignBegin failed\n"); michael@0: goto failure; michael@0: } michael@0: } michael@0: michael@0: /* MAR ID */ michael@0: if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, michael@0: buf, MAR_ID_SIZE, michael@0: ctxs, certCount, "MAR ID")) { michael@0: goto failure; michael@0: } michael@0: michael@0: /* Offset to index */ michael@0: if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could not read offset\n"); michael@0: goto failure; michael@0: } michael@0: offsetToIndex = ntohl(offsetToIndex); michael@0: michael@0: /* Get the real size of the MAR */ michael@0: oldPos = ftello(fpSrc); michael@0: if (fseeko(fpSrc, 0, SEEK_END)) { michael@0: fprintf(stderr, "ERROR: Could not seek to end of file.\n"); michael@0: goto failure; michael@0: } michael@0: realSizeOfSrcMAR = ftello(fpSrc); michael@0: if (fseeko(fpSrc, oldPos, SEEK_SET)) { michael@0: fprintf(stderr, "ERROR: Could not seek back to current location.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: if (hasSignatureBlock) { michael@0: /* Get the MAR length and adjust its size */ michael@0: if (fread(&sizeOfEntireMAR, michael@0: sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could read mar size\n"); michael@0: goto failure; michael@0: } michael@0: sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); michael@0: if (sizeOfEntireMAR != realSizeOfSrcMAR) { michael@0: fprintf(stderr, "ERROR: Source MAR is not of the right size\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Get the num signatures in the source file */ michael@0: if (fread(&numSignatures, sizeof(numSignatures), 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could read num signatures\n"); michael@0: goto failure; michael@0: } michael@0: numSignatures = ntohl(numSignatures); michael@0: michael@0: /* We do not support resigning, if you have multiple signatures, michael@0: you must add them all at the same time. */ michael@0: if (numSignatures) { michael@0: fprintf(stderr, "ERROR: MAR is already signed\n"); michael@0: goto failure; michael@0: } michael@0: } else { michael@0: sizeOfEntireMAR = realSizeOfSrcMAR; michael@0: } michael@0: michael@0: if (((int64_t)offsetToIndex) > sizeOfEntireMAR) { michael@0: fprintf(stderr, "ERROR: Offset to index is larger than the file size.\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Calculate the total signature block length */ michael@0: for (k = 0; k < certCount; k++) { michael@0: signatureSectionLength += sizeof(signatureAlgorithmID) + michael@0: sizeof(signatureLengths[k]) + michael@0: signatureLengths[k]; michael@0: } michael@0: dstOffsetToIndex = offsetToIndex; michael@0: if (!hasSignatureBlock) { michael@0: dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); michael@0: } michael@0: dstOffsetToIndex += signatureSectionLength; michael@0: michael@0: /* Write out the index offset */ michael@0: dstOffsetToIndex = htonl(dstOffsetToIndex); michael@0: if (WriteAndUpdateSignatures(fpDest, &dstOffsetToIndex, michael@0: sizeof(dstOffsetToIndex), ctxs, certCount, michael@0: "index offset")) { michael@0: goto failure; michael@0: } michael@0: dstOffsetToIndex = ntohl(dstOffsetToIndex); michael@0: michael@0: /* Write out the new MAR file size */ michael@0: sizeOfEntireMAR += signatureSectionLength; michael@0: if (!hasSignatureBlock) { michael@0: sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); michael@0: } michael@0: michael@0: /* Write out the MAR size */ michael@0: sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR); michael@0: if (WriteAndUpdateSignatures(fpDest, &sizeOfEntireMAR, michael@0: sizeof(sizeOfEntireMAR), ctxs, certCount, michael@0: "size of MAR")) { michael@0: goto failure; michael@0: } michael@0: sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); michael@0: michael@0: /* Write out the number of signatures */ michael@0: numSignatures = certCount; michael@0: numSignatures = htonl(numSignatures); michael@0: if (WriteAndUpdateSignatures(fpDest, &numSignatures, michael@0: sizeof(numSignatures), ctxs, certCount, michael@0: "num signatures")) { michael@0: goto failure; michael@0: } michael@0: numSignatures = ntohl(numSignatures); michael@0: michael@0: signaturePlaceholderOffset = ftello(fpDest); michael@0: michael@0: for (k = 0; k < certCount; k++) { michael@0: /* Write out the signature algorithm ID, Only an ID of 1 is supported */ michael@0: signatureAlgorithmID = htonl(1); michael@0: if (WriteAndUpdateSignatures(fpDest, &signatureAlgorithmID, michael@0: sizeof(signatureAlgorithmID), michael@0: ctxs, certCount, "num signatures")) { michael@0: goto failure; michael@0: } michael@0: signatureAlgorithmID = ntohl(signatureAlgorithmID); michael@0: michael@0: /* Write out the signature length */ michael@0: signatureLengths[k] = htonl(signatureLengths[k]); michael@0: if (WriteAndUpdateSignatures(fpDest, &signatureLengths[k], michael@0: sizeof(signatureLengths[k]), michael@0: ctxs, certCount, "signature length")) { michael@0: goto failure; michael@0: } michael@0: signatureLengths[k] = ntohl(signatureLengths[k]); michael@0: michael@0: /* Write out a placeholder for the signature, we'll come back to this later michael@0: *** THIS IS NOT SIGNED because it is a placeholder that will be replaced michael@0: below, plus it is going to be the signature itself. *** */ michael@0: memset(buf, 0, sizeof(buf)); michael@0: if (fwrite(buf, signatureLengths[k], 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write signature length\n"); michael@0: goto failure; michael@0: } michael@0: } michael@0: michael@0: /* Write out the rest of the MAR excluding the index header and index michael@0: offsetToIndex unfortunately has to remain 32-bit because for backwards michael@0: compatibility with the old MAR file format. */ michael@0: if (ftello(fpSrc) > ((int64_t)offsetToIndex)) { michael@0: fprintf(stderr, "ERROR: Index offset is too small.\n"); michael@0: goto failure; michael@0: } michael@0: numBytesToCopy = ((int64_t)offsetToIndex) - ftello(fpSrc); michael@0: numChunks = numBytesToCopy / BLOCKSIZE; michael@0: leftOver = numBytesToCopy % BLOCKSIZE; michael@0: michael@0: /* Read each file and write it to the MAR file */ michael@0: for (i = 0; i < numChunks; ++i) { michael@0: if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, buf, michael@0: BLOCKSIZE, ctxs, certCount, michael@0: "content block")) { michael@0: goto failure; michael@0: } michael@0: } michael@0: michael@0: /* Write out the left over */ michael@0: if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, buf, michael@0: leftOver, ctxs, certCount, michael@0: "left over content block")) { michael@0: goto failure; michael@0: } michael@0: michael@0: /* Length of the index */ michael@0: if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, &indexLength, michael@0: sizeof(indexLength), ctxs, certCount, michael@0: "index length")) { michael@0: goto failure; michael@0: } michael@0: indexLength = ntohl(indexLength); michael@0: michael@0: /* Consume the index and adjust each index by signatureSectionLength */ michael@0: indexBuf = malloc(indexLength); michael@0: indexBufLoc = indexBuf; michael@0: if (fread(indexBuf, indexLength, 1, fpSrc) != 1) { michael@0: fprintf(stderr, "ERROR: Could not read index\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Adjust each entry in the index */ michael@0: if (hasSignatureBlock) { michael@0: AdjustIndexContentOffsets(indexBuf, indexLength, signatureSectionLength); michael@0: } else { michael@0: AdjustIndexContentOffsets(indexBuf, indexLength, michael@0: sizeof(sizeOfEntireMAR) + michael@0: sizeof(numSignatures) + michael@0: signatureSectionLength); michael@0: } michael@0: michael@0: if (WriteAndUpdateSignatures(fpDest, indexBuf, michael@0: indexLength, ctxs, certCount, "index")) { michael@0: goto failure; michael@0: } michael@0: michael@0: /* Ensure that we don't sign a file that is too large to be accepted by michael@0: the verification function. */ michael@0: if (ftello(fpDest) > MAX_SIZE_OF_MAR_FILE) { michael@0: goto failure; michael@0: } michael@0: michael@0: for (k = 0; k < certCount; k++) { michael@0: /* Get the signature */ michael@0: if (SGN_End(ctxs[k], &secItems[k]) != SECSuccess) { michael@0: fprintf(stderr, "ERROR: Could not end signature context\n"); michael@0: goto failure; michael@0: } michael@0: if (signatureLengths[k] != secItems[k].len) { michael@0: fprintf(stderr, "ERROR: Signature is not the expected length\n"); michael@0: goto failure; michael@0: } michael@0: } michael@0: michael@0: /* Get back to the location of the signature placeholder */ michael@0: if (fseeko(fpDest, signaturePlaceholderOffset, SEEK_SET)) { michael@0: fprintf(stderr, "ERROR: Could not seek to signature offset\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: for (k = 0; k < certCount; k++) { michael@0: /* Skip to the position of the next signature */ michael@0: if (fseeko(fpDest, sizeof(signatureAlgorithmID) + michael@0: sizeof(signatureLengths[k]), SEEK_CUR)) { michael@0: fprintf(stderr, "ERROR: Could not seek to signature offset\n"); michael@0: goto failure; michael@0: } michael@0: michael@0: /* Write out the calculated signature. michael@0: *** THIS IS NOT SIGNED because it is the signature itself. *** */ michael@0: if (fwrite(secItems[k].data, secItems[k].len, 1, fpDest) != 1) { michael@0: fprintf(stderr, "ERROR: Could not write signature\n"); michael@0: goto failure; michael@0: } michael@0: } michael@0: michael@0: rv = 0; michael@0: failure: michael@0: if (fpSrc) { michael@0: fclose(fpSrc); michael@0: } michael@0: michael@0: if (fpDest) { michael@0: fclose(fpDest); michael@0: } michael@0: michael@0: if (rv) { michael@0: remove(dest); michael@0: } michael@0: michael@0: if (indexBuf) { michael@0: free(indexBuf); michael@0: } michael@0: michael@0: /* Cleanup */ michael@0: for (k = 0; k < certCount; k++) { michael@0: if (ctxs[k]) { michael@0: SGN_DestroyContext(ctxs[k], PR_TRUE); michael@0: } michael@0: michael@0: if (certs[k]) { michael@0: CERT_DestroyCertificate(certs[k]); michael@0: } michael@0: michael@0: if (privKeys[k]) { michael@0: SECKEY_DestroyPrivateKey(privKeys[k]); michael@0: } michael@0: michael@0: SECITEM_FreeItem(&secItems[k], PR_FALSE); michael@0: } michael@0: michael@0: if (rv) { michael@0: remove(dest); michael@0: } michael@0: michael@0: return rv; michael@0: }