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