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: /* michael@0: * p7verify -- A command to do a verification of a *detached* pkcs7 signature. michael@0: */ michael@0: michael@0: #include "nspr.h" michael@0: #include "secutil.h" michael@0: #include "plgetopt.h" michael@0: #include "secpkcs7.h" michael@0: #include "cert.h" michael@0: #include "certdb.h" michael@0: #include "secoid.h" michael@0: #include "sechash.h" /* for HASH_GetHashObject() */ michael@0: #include "nss.h" michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #if (defined(XP_WIN) && !defined(WIN32)) || (defined(__sun) && !defined(SVR4)) michael@0: extern int fread(char *, size_t, size_t, FILE*); michael@0: extern int fprintf(FILE *, char *, ...); michael@0: #endif michael@0: michael@0: michael@0: static HASH_HashType michael@0: AlgorithmToHashType(SECAlgorithmID *digestAlgorithms) michael@0: { michael@0: michael@0: SECOidTag tag; michael@0: michael@0: tag = SECOID_GetAlgorithmTag(digestAlgorithms); michael@0: michael@0: switch (tag) { michael@0: case SEC_OID_MD2: michael@0: return HASH_AlgMD2; michael@0: case SEC_OID_MD5: michael@0: return HASH_AlgMD5; michael@0: case SEC_OID_SHA1: michael@0: return HASH_AlgSHA1; michael@0: default: michael@0: fprintf(stderr, "should never get here\n"); michael@0: return HASH_AlgNULL; michael@0: } michael@0: } michael@0: michael@0: static int michael@0: DigestFile(unsigned char *digest, unsigned int *len, unsigned int maxLen, michael@0: FILE *inFile, HASH_HashType hashType) michael@0: { michael@0: int nb; michael@0: unsigned char ibuf[4096]; michael@0: const SECHashObject *hashObj; michael@0: void *hashcx; michael@0: michael@0: hashObj = HASH_GetHashObject(hashType); michael@0: michael@0: hashcx = (* hashObj->create)(); michael@0: if (hashcx == NULL) michael@0: return -1; michael@0: michael@0: (* hashObj->begin)(hashcx); michael@0: michael@0: for (;;) { michael@0: if (feof(inFile)) break; michael@0: nb = fread(ibuf, 1, sizeof(ibuf), inFile); michael@0: if (nb != sizeof(ibuf)) { michael@0: if (nb == 0) { michael@0: if (ferror(inFile)) { michael@0: PORT_SetError(SEC_ERROR_IO); michael@0: (* hashObj->destroy)(hashcx, PR_TRUE); michael@0: return -1; michael@0: } michael@0: /* eof */ michael@0: break; michael@0: } michael@0: } michael@0: (* hashObj->update)(hashcx, ibuf, nb); michael@0: } michael@0: michael@0: (* hashObj->end)(hashcx, digest, len, maxLen); michael@0: (* hashObj->destroy)(hashcx, PR_TRUE); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: static void michael@0: Usage(char *progName) michael@0: { michael@0: fprintf(stderr, michael@0: "Usage: %s -c content -s signature [-d dbdir] [-u certusage]\n", michael@0: progName); michael@0: fprintf(stderr, "%-20s content file that was signed\n", michael@0: "-c content"); michael@0: fprintf(stderr, "%-20s file containing signature for that content\n", michael@0: "-s signature"); michael@0: fprintf(stderr, michael@0: "%-20s Key/Cert database directory (default is ~/.netscape)\n", michael@0: "-d dbdir"); michael@0: fprintf(stderr, "%-20s Define the type of certificate usage (default is certUsageEmailSigner)\n", michael@0: "-u certusage"); michael@0: fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " "); michael@0: fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " "); michael@0: fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " "); michael@0: fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " "); michael@0: fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " "); michael@0: fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " "); michael@0: fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " "); michael@0: fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " "); michael@0: fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " "); michael@0: fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " "); michael@0: fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " "); michael@0: fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " "); michael@0: michael@0: exit(-1); michael@0: } michael@0: michael@0: static int michael@0: HashDecodeAndVerify(FILE *out, FILE *content, PRFileDesc *signature, michael@0: SECCertUsage usage, char *progName) michael@0: { michael@0: SECItem derdata; michael@0: SEC_PKCS7ContentInfo *cinfo; michael@0: SEC_PKCS7SignedData *signedData; michael@0: HASH_HashType digestType; michael@0: SECItem digest; michael@0: unsigned char buffer[32]; michael@0: michael@0: if (SECU_ReadDERFromFile(&derdata, signature, PR_FALSE, michael@0: PR_FALSE) != SECSuccess) { michael@0: SECU_PrintError(progName, "error reading signature file"); michael@0: return -1; michael@0: } michael@0: michael@0: cinfo = SEC_PKCS7DecodeItem(&derdata, NULL, NULL, NULL, NULL, michael@0: NULL, NULL, NULL); michael@0: if (cinfo == NULL) michael@0: return -1; michael@0: michael@0: if (! SEC_PKCS7ContentIsSigned(cinfo)) { michael@0: fprintf (out, "Signature file is pkcs7 data, but not signed.\n"); michael@0: return -1; michael@0: } 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: fprintf (out, "Invalid hash algorithmID\n"); michael@0: return -1; michael@0: } michael@0: michael@0: digest.data = buffer; michael@0: if (DigestFile (digest.data, &digest.len, 32, content, digestType)) { michael@0: SECU_PrintError (progName, "problem computing message digest"); michael@0: return -1; michael@0: } michael@0: michael@0: fprintf(out, "Signature is "); michael@0: if (SEC_PKCS7VerifyDetachedSignature (cinfo, usage, &digest, digestType, michael@0: PR_FALSE)) michael@0: fprintf(out, "valid.\n"); michael@0: else michael@0: fprintf(out, "invalid (Reason: %s).\n", michael@0: SECU_Strerror(PORT_GetError())); michael@0: michael@0: SEC_PKCS7DestroyContentInfo(cinfo); michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: char *progName; michael@0: FILE *contentFile, *outFile; michael@0: PRFileDesc *signatureFile; michael@0: SECCertUsage certUsage = certUsageEmailSigner; michael@0: PLOptState *optstate; michael@0: PLOptStatus status; michael@0: SECStatus rv; michael@0: michael@0: progName = strrchr(argv[0], '/'); michael@0: progName = progName ? progName+1 : argv[0]; michael@0: michael@0: contentFile = NULL; michael@0: signatureFile = NULL; michael@0: outFile = NULL; michael@0: michael@0: /* michael@0: * Parse command line arguments michael@0: */ michael@0: optstate = PL_CreateOptState(argc, argv, "c:d:o:s:u:"); michael@0: while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { michael@0: switch (optstate->option) { michael@0: case '?': michael@0: Usage(progName); michael@0: break; michael@0: michael@0: case 'c': michael@0: contentFile = fopen(optstate->value, "r"); michael@0: if (!contentFile) { michael@0: fprintf(stderr, "%s: unable to open \"%s\" for reading\n", michael@0: progName, optstate->value); michael@0: return -1; michael@0: } michael@0: break; michael@0: michael@0: case 'd': michael@0: SECU_ConfigDirectory(optstate->value); michael@0: break; michael@0: michael@0: case 'o': michael@0: outFile = fopen(optstate->value, "w"); michael@0: if (!outFile) { michael@0: fprintf(stderr, "%s: unable to open \"%s\" for writing\n", michael@0: progName, optstate->value); michael@0: return -1; michael@0: } michael@0: break; michael@0: michael@0: case 's': michael@0: signatureFile = PR_Open(optstate->value, PR_RDONLY, 0); michael@0: if (!signatureFile) { michael@0: fprintf(stderr, "%s: unable to open \"%s\" for reading\n", michael@0: progName, optstate->value); michael@0: return -1; michael@0: } michael@0: break; michael@0: michael@0: case 'u': { michael@0: int usageType; michael@0: michael@0: usageType = atoi (strdup(optstate->value)); michael@0: if (usageType < certUsageSSLClient || usageType > certUsageAnyCA) michael@0: return -1; michael@0: certUsage = (SECCertUsage)usageType; michael@0: break; michael@0: } michael@0: michael@0: } michael@0: } michael@0: michael@0: if (!contentFile) Usage (progName); michael@0: if (!signatureFile) Usage (progName); michael@0: if (!outFile) outFile = stdout; michael@0: michael@0: /* Call the NSS initialization routines */ michael@0: PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); michael@0: rv = NSS_Init(SECU_ConfigDirectory(NULL)); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintPRandOSError(progName); michael@0: return -1; michael@0: } michael@0: michael@0: if (HashDecodeAndVerify(outFile, contentFile, signatureFile, michael@0: certUsage, progName)) { michael@0: SECU_PrintError(progName, "problem decoding/verifying signature"); michael@0: return -1; michael@0: } michael@0: michael@0: if (NSS_Shutdown() != SECSuccess) { michael@0: exit(1); michael@0: } michael@0: michael@0: return 0; michael@0: }