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: #include "secutil.h" michael@0: #include "secmod.h" michael@0: #include "cert.h" michael@0: #include "secoid.h" michael@0: #include "nss.h" michael@0: michael@0: /* NSPR 2.0 header files */ michael@0: #include "prinit.h" michael@0: #include "prprf.h" michael@0: #include "prsystem.h" michael@0: #include "prmem.h" michael@0: /* Portable layer header files */ michael@0: #include "plstr.h" michael@0: #include "sechash.h" /* for HASH_GetHashObject() */ michael@0: michael@0: static PRBool debugInfo; michael@0: static PRBool verbose; michael@0: static PRBool doVerify; michael@0: static PRBool displayAll; michael@0: michael@0: static const char * const usageInfo[] = { michael@0: "signver - verify a detached PKCS7 signature - Version " NSS_VERSION, michael@0: "Commands:", michael@0: " -A display all information from pkcs #7", michael@0: " -V verify the signed object and display result", michael@0: "Options:", michael@0: " -a signature file is ASCII", michael@0: " -d certdir directory containing cert database", michael@0: " -i dataFileName input file containing signed data (default stdin)", michael@0: " -o outputFileName output file name, default stdout", michael@0: " -s signatureFileName input file for signature (default stdin)", michael@0: " -v display verbose reason for failure" michael@0: }; michael@0: static int nUsageInfo = sizeof(usageInfo)/sizeof(char *); michael@0: michael@0: extern int SV_PrintPKCS7ContentInfo(FILE *, SECItem *); michael@0: michael@0: static void Usage(char *progName, FILE *outFile) michael@0: { michael@0: int i; michael@0: fprintf(outFile, "Usage: %s [ commands ] options\n", progName); michael@0: for (i = 0; i < nUsageInfo; i++) michael@0: fprintf(outFile, "%s\n", usageInfo[i]); michael@0: exit(-1); michael@0: } michael@0: michael@0: static HASH_HashType michael@0: AlgorithmToHashType(SECAlgorithmID *digestAlgorithms) michael@0: { michael@0: SECOidTag tag = SECOID_GetAlgorithmTag(digestAlgorithms); michael@0: HASH_HashType hash = HASH_GetHashTypeByOidTag(tag); michael@0: return hash; michael@0: } michael@0: michael@0: michael@0: static SECStatus michael@0: DigestContent (SECItem * digest, SECItem * content, HASH_HashType hashType) michael@0: { michael@0: unsigned int maxLen = digest->len; michael@0: unsigned int len = HASH_ResultLen(hashType); michael@0: SECStatus rv; michael@0: michael@0: if (len > maxLen) { michael@0: PORT_SetError(SEC_ERROR_OUTPUT_LEN); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = HASH_HashBuf(hashType, digest->data, content->data, content->len); michael@0: if (rv == SECSuccess) michael@0: digest->len = len; michael@0: return rv; michael@0: } michael@0: michael@0: enum { michael@0: cmd_DisplayAllPCKS7Info = 0, michael@0: cmd_VerifySignedObj michael@0: }; michael@0: michael@0: enum { michael@0: opt_ASCII, michael@0: opt_CertDir, michael@0: opt_InputDataFile, michael@0: opt_ItemNumber, michael@0: opt_OutputFile, michael@0: opt_InputSigFile, michael@0: opt_PrintWhyFailure, michael@0: opt_DebugInfo michael@0: }; michael@0: michael@0: static secuCommandFlag signver_commands[] = michael@0: { michael@0: { /* cmd_DisplayAllPCKS7Info*/ 'A', PR_FALSE, 0, PR_FALSE }, michael@0: { /* cmd_VerifySignedObj */ 'V', PR_FALSE, 0, PR_FALSE } michael@0: }; michael@0: michael@0: static secuCommandFlag signver_options[] = michael@0: { michael@0: { /* opt_ASCII */ 'a', PR_FALSE, 0, PR_FALSE }, michael@0: { /* opt_CertDir */ 'd', PR_TRUE, 0, PR_FALSE }, michael@0: { /* opt_InputDataFile */ 'i', PR_TRUE, 0, PR_FALSE }, michael@0: { /* opt_OutputFile */ 'o', PR_TRUE, 0, PR_FALSE }, michael@0: { /* opt_InputSigFile */ 's', PR_TRUE, 0, PR_FALSE }, michael@0: { /* opt_PrintWhyFailure */ 'v', PR_FALSE, 0, PR_FALSE }, michael@0: { /* opt_DebugInfo */ 0, PR_FALSE, 0, PR_FALSE, "debug" } michael@0: }; michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: PRFileDesc *contentFile = NULL; michael@0: PRFileDesc *signFile = PR_STDIN; michael@0: FILE * outFile = stdout; michael@0: char * progName; michael@0: SECStatus rv; michael@0: int result = 1; michael@0: SECItem pkcs7der, content; michael@0: secuCommand signver; michael@0: michael@0: pkcs7der.data = NULL; michael@0: content.data = NULL; michael@0: michael@0: signver.numCommands = sizeof(signver_commands) /sizeof(secuCommandFlag); michael@0: signver.numOptions = sizeof(signver_options) / sizeof(secuCommandFlag); michael@0: signver.commands = signver_commands; michael@0: signver.options = signver_options; michael@0: michael@0: #ifdef XP_PC michael@0: progName = strrchr(argv[0], '\\'); michael@0: #else michael@0: progName = strrchr(argv[0], '/'); michael@0: #endif michael@0: progName = progName ? progName+1 : argv[0]; michael@0: michael@0: rv = SECU_ParseCommandLine(argc, argv, progName, &signver); michael@0: if (SECSuccess != rv) { michael@0: Usage(progName, outFile); michael@0: } michael@0: debugInfo = signver.options[opt_DebugInfo ].activated; michael@0: verbose = signver.options[opt_PrintWhyFailure ].activated; michael@0: doVerify = signver.commands[cmd_VerifySignedObj].activated; michael@0: displayAll= signver.commands[cmd_DisplayAllPCKS7Info].activated; michael@0: if (!doVerify && !displayAll) michael@0: doVerify = PR_TRUE; michael@0: michael@0: /* Set the certdb directory (default is ~/.netscape) */ michael@0: rv = NSS_Init(SECU_ConfigDirectory(signver.options[opt_CertDir].arg)); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintPRandOSError(progName); michael@0: return result; michael@0: } michael@0: /* below here, goto cleanup */ michael@0: SECU_RegisterDynamicOids(); michael@0: michael@0: /* Open the input content file. */ michael@0: if (signver.options[opt_InputDataFile].activated && michael@0: signver.options[opt_InputDataFile].arg) { michael@0: if (PL_strcmp("-", signver.options[opt_InputDataFile].arg)) { michael@0: contentFile = PR_Open(signver.options[opt_InputDataFile].arg, michael@0: PR_RDONLY, 0); michael@0: if (!contentFile) { michael@0: PR_fprintf(PR_STDERR, michael@0: "%s: unable to open \"%s\" for reading.\n", michael@0: progName, signver.options[opt_InputDataFile].arg); michael@0: goto cleanup; michael@0: } michael@0: } else michael@0: contentFile = PR_STDIN; michael@0: } michael@0: michael@0: /* Open the input signature file. */ michael@0: if (signver.options[opt_InputSigFile].activated && michael@0: signver.options[opt_InputSigFile].arg) { michael@0: if (PL_strcmp("-", signver.options[opt_InputSigFile].arg)) { michael@0: signFile = PR_Open(signver.options[opt_InputSigFile].arg, michael@0: PR_RDONLY, 0); michael@0: if (!signFile) { michael@0: PR_fprintf(PR_STDERR, michael@0: "%s: unable to open \"%s\" for reading.\n", michael@0: progName, signver.options[opt_InputSigFile].arg); michael@0: goto cleanup; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (contentFile == PR_STDIN && signFile == PR_STDIN && doVerify) { michael@0: PR_fprintf(PR_STDERR, michael@0: "%s: cannot read both content and signature from standard input\n", michael@0: progName); michael@0: goto cleanup; michael@0: } michael@0: michael@0: /* Open|Create the output file. */ michael@0: if (signver.options[opt_OutputFile].activated) { michael@0: outFile = fopen(signver.options[opt_OutputFile].arg, "w"); michael@0: if (!outFile) { michael@0: PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for writing.\n", michael@0: progName, signver.options[opt_OutputFile].arg); michael@0: goto cleanup; michael@0: } michael@0: } michael@0: michael@0: /* read in the input files' contents */ michael@0: rv = SECU_ReadDERFromFile(&pkcs7der, signFile, michael@0: signver.options[opt_ASCII].activated, PR_FALSE); michael@0: if (signFile != PR_STDIN) michael@0: PR_Close(signFile); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "problem reading PKCS7 input"); michael@0: goto cleanup; michael@0: } michael@0: if (contentFile) { michael@0: rv = SECU_FileToItem(&content, contentFile); michael@0: if (contentFile != PR_STDIN) michael@0: PR_Close(contentFile); michael@0: if (rv != SECSuccess) michael@0: content.data = NULL; michael@0: } michael@0: michael@0: /* Signature Verification */ michael@0: if (doVerify) { michael@0: SEC_PKCS7ContentInfo *cinfo; michael@0: SEC_PKCS7SignedData *signedData; michael@0: HASH_HashType digestType; michael@0: PRBool contentIsSigned; michael@0: michael@0: cinfo = SEC_PKCS7DecodeItem(&pkcs7der, NULL, NULL, NULL, NULL, michael@0: NULL, NULL, NULL); michael@0: if (cinfo == NULL) { michael@0: PR_fprintf(PR_STDERR, "Unable to decode PKCS7 data\n"); michael@0: goto cleanup; michael@0: } michael@0: /* below here, goto done */ michael@0: michael@0: contentIsSigned = SEC_PKCS7ContentIsSigned(cinfo); michael@0: if (debugInfo) { michael@0: PR_fprintf(PR_STDERR, "Content is%s encrypted.\n", michael@0: SEC_PKCS7ContentIsEncrypted(cinfo) ? "" : " not"); michael@0: } michael@0: if (debugInfo || !contentIsSigned) { michael@0: PR_fprintf(PR_STDERR, "Content is%s signed.\n", michael@0: contentIsSigned ? "" : " not"); michael@0: } michael@0: michael@0: if (!contentIsSigned) michael@0: goto done; michael@0: michael@0: signedData = cinfo->content.signedData; michael@0: michael@0: /* assume that there is only one digest algorithm for now */ michael@0: digestType = AlgorithmToHashType(signedData->digestAlgorithms[0]); michael@0: if (digestType == HASH_AlgNULL) { michael@0: PR_fprintf(PR_STDERR, "Invalid hash algorithmID\n"); michael@0: goto done; michael@0: } michael@0: if (content.data) { michael@0: SECCertUsage usage = certUsageEmailSigner; michael@0: SECItem digest; michael@0: unsigned char digestBuffer[HASH_LENGTH_MAX]; michael@0: michael@0: if (debugInfo) michael@0: PR_fprintf(PR_STDERR, "contentToVerify=%s\n", content.data); michael@0: michael@0: digest.data = digestBuffer; michael@0: digest.len = sizeof digestBuffer; michael@0: michael@0: if (DigestContent(&digest, &content, digestType)) { michael@0: SECU_PrintError(progName, "Message digest computation failure"); michael@0: goto done; michael@0: } michael@0: michael@0: if (debugInfo) { michael@0: unsigned int i; michael@0: PR_fprintf(PR_STDERR, "Data Digest=:"); michael@0: for (i = 0; i < digest.len; i++) michael@0: PR_fprintf(PR_STDERR, "%02x:", digest.data[i]); michael@0: PR_fprintf(PR_STDERR, "\n"); michael@0: } michael@0: michael@0: fprintf(outFile, "signatureValid="); michael@0: PORT_SetError(0); michael@0: if (SEC_PKCS7VerifyDetachedSignature (cinfo, usage, michael@0: &digest, digestType, PR_FALSE)) { michael@0: fprintf(outFile, "yes"); michael@0: } else { michael@0: fprintf(outFile, "no"); michael@0: if (verbose) { michael@0: fprintf(outFile, ":%s", michael@0: SECU_Strerror(PORT_GetError())); michael@0: } michael@0: } michael@0: fprintf(outFile, "\n"); michael@0: result = 0; michael@0: } michael@0: done: michael@0: SEC_PKCS7DestroyContentInfo(cinfo); michael@0: } michael@0: michael@0: if (displayAll) { michael@0: if (SV_PrintPKCS7ContentInfo(outFile, &pkcs7der)) michael@0: result = 1; michael@0: } michael@0: michael@0: cleanup: michael@0: SECITEM_FreeItem(&pkcs7der, PR_FALSE); michael@0: SECITEM_FreeItem(&content, PR_FALSE); michael@0: michael@0: if (NSS_Shutdown() != SECSuccess) { michael@0: result = 1; michael@0: } michael@0: michael@0: return result; michael@0: }