/* ** Sign a block of data returning in result a bunch of bytes that are the ** signature. Returns zero on success, an error code on failure. */ SECStatus SEC_SignData(SECItem *res, const unsigned char *buf, int len, SECKEYPrivateKey *pk, SECOidTag algid) { SECStatus rv; SGNContext *sgn; sgn = SGN_NewContext(algid, pk); if (sgn == NULL) return SECFailure; rv = SGN_Begin(sgn); if (rv != SECSuccess) goto loser; rv = SGN_Update(sgn, buf, len); if (rv != SECSuccess) goto loser; rv = SGN_End(sgn, res); loser: SGN_DestroyContext(sgn, PR_TRUE); return rv; }
/** * Writes out a copy of the MAR at src but with embedded signatures. * The passed in MAR file must not already be signed or an error will * be returned. * * @param NSSConfigDir The NSS directory containing the private key for signing * @param certNames The nicknames of the certificate to use for signing * @param certCount The number of certificate names contained in certNames. * One signature will be produced for each certificate. * @param src The path of the source MAR file to sign * @param dest The path of the MAR file to write out that is signed * @return 0 on success * -1 on error */ int mar_repackage_and_sign(const char *NSSConfigDir, const char * const *certNames, uint32_t certCount, const char *src, const char *dest) { uint32_t offsetToIndex, dstOffsetToIndex, indexLength, numSignatures = 0, leftOver, signatureAlgorithmID, signatureSectionLength = 0; uint32_t signatureLengths[MAX_SIGNATURES]; int64_t oldPos, sizeOfEntireMAR = 0, realSizeOfSrcMAR, signaturePlaceholderOffset, numBytesToCopy, numChunks, i; FILE *fpSrc = NULL, *fpDest = NULL; int rv = -1, hasSignatureBlock; SGNContext *ctxs[MAX_SIGNATURES]; SECItem secItems[MAX_SIGNATURES]; char buf[BLOCKSIZE]; SECKEYPrivateKey *privKeys[MAX_SIGNATURES]; CERTCertificate *certs[MAX_SIGNATURES]; char *indexBuf = NULL; uint32_t k; memset(signatureLengths, 0, sizeof(signatureLengths)); memset(ctxs, 0, sizeof(ctxs)); memset(secItems, 0, sizeof(secItems)); memset(privKeys, 0, sizeof(privKeys)); memset(certs, 0, sizeof(certs)); if (!NSSConfigDir || !certNames || certCount == 0 || !src || !dest) { fprintf(stderr, "ERROR: Invalid parameter passed in.\n"); return -1; } if (NSSInitCryptoContext(NSSConfigDir)) { fprintf(stderr, "ERROR: Could not init config dir: %s\n", NSSConfigDir); goto failure; } PK11_SetPasswordFunc(SECU_GetModulePassword); fpSrc = fopen(src, "rb"); if (!fpSrc) { fprintf(stderr, "ERROR: could not open source file: %s\n", src); goto failure; } fpDest = fopen(dest, "wb"); if (!fpDest) { fprintf(stderr, "ERROR: could not create target file: %s\n", dest); goto failure; } /* Determine if the source MAR file has the new fields for signing or not */ if (get_mar_file_info(src, &hasSignatureBlock, NULL, NULL, NULL, NULL)) { fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n"); goto failure; } for (k = 0; k < certCount; k++) { if (NSSSignBegin(certNames[k], &ctxs[k], &privKeys[k], &certs[k], &signatureLengths[k])) { fprintf(stderr, "ERROR: NSSSignBegin failed\n"); goto failure; } } /* MAR ID */ if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, buf, MAR_ID_SIZE, ctxs, certCount, "MAR ID")) { goto failure; } /* Offset to index */ if (fread(&offsetToIndex, sizeof(offsetToIndex), 1, fpSrc) != 1) { fprintf(stderr, "ERROR: Could not read offset\n"); goto failure; } offsetToIndex = ntohl(offsetToIndex); /* Get the real size of the MAR */ oldPos = ftello(fpSrc); if (fseeko(fpSrc, 0, SEEK_END)) { fprintf(stderr, "ERROR: Could not seek to end of file.\n"); goto failure; } realSizeOfSrcMAR = ftello(fpSrc); if (fseeko(fpSrc, oldPos, SEEK_SET)) { fprintf(stderr, "ERROR: Could not seek back to current location.\n"); goto failure; } if (hasSignatureBlock) { /* Get the MAR length and adjust its size */ if (fread(&sizeOfEntireMAR, sizeof(sizeOfEntireMAR), 1, fpSrc) != 1) { fprintf(stderr, "ERROR: Could read mar size\n"); goto failure; } sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); if (sizeOfEntireMAR != realSizeOfSrcMAR) { fprintf(stderr, "ERROR: Source MAR is not of the right size\n"); goto failure; } /* Get the num signatures in the source file */ if (fread(&numSignatures, sizeof(numSignatures), 1, fpSrc) != 1) { fprintf(stderr, "ERROR: Could read num signatures\n"); goto failure; } numSignatures = ntohl(numSignatures); /* We do not support resigning, if you have multiple signatures, you must add them all at the same time. */ if (numSignatures) { fprintf(stderr, "ERROR: MAR is already signed\n"); goto failure; } } else { sizeOfEntireMAR = realSizeOfSrcMAR; } if (((int64_t)offsetToIndex) > sizeOfEntireMAR) { fprintf(stderr, "ERROR: Offset to index is larger than the file size.\n"); goto failure; } /* Calculate the total signature block length */ for (k = 0; k < certCount; k++) { signatureSectionLength += sizeof(signatureAlgorithmID) + sizeof(signatureLengths[k]) + signatureLengths[k]; } dstOffsetToIndex = offsetToIndex; if (!hasSignatureBlock) { dstOffsetToIndex += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); } dstOffsetToIndex += signatureSectionLength; /* Write out the index offset */ dstOffsetToIndex = htonl(dstOffsetToIndex); if (WriteAndUpdateSignatures(fpDest, &dstOffsetToIndex, sizeof(dstOffsetToIndex), ctxs, certCount, "index offset")) { goto failure; } dstOffsetToIndex = ntohl(dstOffsetToIndex); /* Write out the new MAR file size */ sizeOfEntireMAR += signatureSectionLength; if (!hasSignatureBlock) { sizeOfEntireMAR += sizeof(sizeOfEntireMAR) + sizeof(numSignatures); } /* Write out the MAR size */ sizeOfEntireMAR = HOST_TO_NETWORK64(sizeOfEntireMAR); if (WriteAndUpdateSignatures(fpDest, &sizeOfEntireMAR, sizeof(sizeOfEntireMAR), ctxs, certCount, "size of MAR")) { goto failure; } sizeOfEntireMAR = NETWORK_TO_HOST64(sizeOfEntireMAR); /* Write out the number of signatures */ numSignatures = certCount; numSignatures = htonl(numSignatures); if (WriteAndUpdateSignatures(fpDest, &numSignatures, sizeof(numSignatures), ctxs, certCount, "num signatures")) { goto failure; } numSignatures = ntohl(numSignatures); signaturePlaceholderOffset = ftello(fpDest); for (k = 0; k < certCount; k++) { /* Write out the signature algorithm ID, Only an ID of 1 is supported */ signatureAlgorithmID = htonl(1); if (WriteAndUpdateSignatures(fpDest, &signatureAlgorithmID, sizeof(signatureAlgorithmID), ctxs, certCount, "num signatures")) { goto failure; } signatureAlgorithmID = ntohl(signatureAlgorithmID); /* Write out the signature length */ signatureLengths[k] = htonl(signatureLengths[k]); if (WriteAndUpdateSignatures(fpDest, &signatureLengths[k], sizeof(signatureLengths[k]), ctxs, certCount, "signature length")) { goto failure; } signatureLengths[k] = ntohl(signatureLengths[k]); /* Write out a placeholder for the signature, we'll come back to this later *** THIS IS NOT SIGNED because it is a placeholder that will be replaced below, plus it is going to be the signature itself. *** */ memset(buf, 0, sizeof(buf)); if (fwrite(buf, signatureLengths[k], 1, fpDest) != 1) { fprintf(stderr, "ERROR: Could not write signature length\n"); goto failure; } } /* Write out the rest of the MAR excluding the index header and index offsetToIndex unfortunately has to remain 32-bit because for backwards compatibility with the old MAR file format. */ if (ftello(fpSrc) > ((int64_t)offsetToIndex)) { fprintf(stderr, "ERROR: Index offset is too small.\n"); goto failure; } numBytesToCopy = ((int64_t)offsetToIndex) - ftello(fpSrc); numChunks = numBytesToCopy / BLOCKSIZE; leftOver = numBytesToCopy % BLOCKSIZE; /* Read each file and write it to the MAR file */ for (i = 0; i < numChunks; ++i) { if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, buf, BLOCKSIZE, ctxs, certCount, "content block")) { goto failure; } } /* Write out the left over */ if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, buf, leftOver, ctxs, certCount, "left over content block")) { goto failure; } /* Length of the index */ if (ReadWriteAndUpdateSignatures(fpSrc, fpDest, &indexLength, sizeof(indexLength), ctxs, certCount, "index length")) { goto failure; } indexLength = ntohl(indexLength); /* Consume the index and adjust each index by signatureSectionLength */ indexBuf = malloc(indexLength); if (fread(indexBuf, indexLength, 1, fpSrc) != 1) { fprintf(stderr, "ERROR: Could not read index\n"); goto failure; } /* Adjust each entry in the index */ if (hasSignatureBlock) { AdjustIndexContentOffsets(indexBuf, indexLength, signatureSectionLength); } else { AdjustIndexContentOffsets(indexBuf, indexLength, sizeof(sizeOfEntireMAR) + sizeof(numSignatures) + signatureSectionLength); } if (WriteAndUpdateSignatures(fpDest, indexBuf, indexLength, ctxs, certCount, "index")) { goto failure; } /* Ensure that we don't sign a file that is too large to be accepted by the verification function. */ if (ftello(fpDest) > MAX_SIZE_OF_MAR_FILE) { goto failure; } for (k = 0; k < certCount; k++) { /* Get the signature */ if (SGN_End(ctxs[k], &secItems[k]) != SECSuccess) { fprintf(stderr, "ERROR: Could not end signature context\n"); goto failure; } if (signatureLengths[k] != secItems[k].len) { fprintf(stderr, "ERROR: Signature is not the expected length\n"); goto failure; } } /* Get back to the location of the signature placeholder */ if (fseeko(fpDest, signaturePlaceholderOffset, SEEK_SET)) { fprintf(stderr, "ERROR: Could not seek to signature offset\n"); goto failure; } for (k = 0; k < certCount; k++) { /* Skip to the position of the next signature */ if (fseeko(fpDest, sizeof(signatureAlgorithmID) + sizeof(signatureLengths[k]), SEEK_CUR)) { fprintf(stderr, "ERROR: Could not seek to signature offset\n"); goto failure; } /* Write out the calculated signature. *** THIS IS NOT SIGNED because it is the signature itself. *** */ if (fwrite(secItems[k].data, secItems[k].len, 1, fpDest) != 1) { fprintf(stderr, "ERROR: Could not write signature\n"); goto failure; } } rv = 0; failure: if (fpSrc) { fclose(fpSrc); } if (fpDest) { fclose(fpDest); } if (rv) { remove(dest); } if (indexBuf) { free(indexBuf); } /* Cleanup */ for (k = 0; k < certCount; k++) { if (ctxs[k]) { SGN_DestroyContext(ctxs[k], PR_TRUE); } if (certs[k]) { CERT_DestroyCertificate(certs[k]); } if (privKeys[k]) { SECKEY_DestroyPrivateKey(privKeys[k]); } SECITEM_FreeItem(&secItems[k], PR_FALSE); } if (rv) { remove(dest); } return rv; }