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