tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

signver.c (10168B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "secutil.h"
      6 #include "secmod.h"
      7 #include "cert.h"
      8 #include "secoid.h"
      9 #include "nss.h"
     10 
     11 /* NSPR 2.0 header files */
     12 #include "prinit.h"
     13 #include "prprf.h"
     14 #include "prsystem.h"
     15 #include "prmem.h"
     16 /* Portable layer header files */
     17 #include "plstr.h"
     18 #include "sechash.h" /* for HASH_GetHashObject() */
     19 
     20 static PRBool debugInfo;
     21 static PRBool verbose;
     22 static PRBool doVerify;
     23 static PRBool displayAll;
     24 
     25 static const char *const usageInfo[] = {
     26    "signver - verify a detached PKCS7 signature - Version " NSS_VERSION,
     27    "Commands:",
     28    " -A                    display all information from pkcs #7",
     29    " -V                    verify the signed object and display result",
     30    "Options:",
     31    " -a                    signature file is ASCII",
     32    " -d certdir            directory containing cert database",
     33    " -i dataFileName       input file containing signed data (default stdin)",
     34    " -o outputFileName     output file name, default stdout",
     35    " -s signatureFileName  input file for signature (default stdin)",
     36    " -v                    display verbose reason for failure"
     37 };
     38 static int nUsageInfo = sizeof(usageInfo) / sizeof(char *);
     39 
     40 extern int SV_PrintPKCS7ContentInfo(FILE *, SECItem *);
     41 
     42 static void
     43 Usage(char *progName, FILE *outFile)
     44 {
     45    int i;
     46    fprintf(outFile, "Usage:  %s [ commands ] options\n", progName);
     47    for (i = 0; i < nUsageInfo; i++)
     48        fprintf(outFile, "%s\n", usageInfo[i]);
     49    exit(-1);
     50 }
     51 
     52 static HASH_HashType
     53 AlgorithmToHashType(SECAlgorithmID *digestAlgorithms)
     54 {
     55    SECOidTag tag = SECOID_GetAlgorithmTag(digestAlgorithms);
     56    HASH_HashType hash = HASH_GetHashTypeByOidTag(tag);
     57    return hash;
     58 }
     59 
     60 static SECStatus
     61 DigestContent(SECItem *digest, SECItem *content, HASH_HashType hashType)
     62 {
     63    unsigned int maxLen = digest->len;
     64    unsigned int len = HASH_ResultLen(hashType);
     65    SECStatus rv;
     66 
     67    if (len > maxLen) {
     68        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
     69        return SECFailure;
     70    }
     71 
     72    rv = HASH_HashBuf(hashType, digest->data, content->data, content->len);
     73    if (rv == SECSuccess)
     74        digest->len = len;
     75    return rv;
     76 }
     77 
     78 enum {
     79    cmd_DisplayAllPCKS7Info = 0,
     80    cmd_VerifySignedObj
     81 };
     82 
     83 enum {
     84    opt_ASCII,
     85    opt_CertDir,
     86    opt_InputDataFile,
     87    opt_OutputFile,
     88    opt_InputSigFile,
     89    opt_PrintWhyFailure,
     90    opt_DebugInfo
     91 };
     92 
     93 static secuCommandFlag signver_commands[] = {
     94    { /* cmd_DisplayAllPCKS7Info*/ 'A', PR_FALSE, 0, PR_FALSE },
     95    { /* cmd_VerifySignedObj  */ 'V', PR_FALSE, 0, PR_FALSE }
     96 };
     97 
     98 static secuCommandFlag signver_options[] = {
     99    { /* opt_ASCII            */ 'a', PR_FALSE, 0, PR_FALSE },
    100    { /* opt_CertDir          */ 'd', PR_TRUE, 0, PR_FALSE },
    101    { /* opt_InputDataFile    */ 'i', PR_TRUE, 0, PR_FALSE },
    102    { /* opt_OutputFile       */ 'o', PR_TRUE, 0, PR_FALSE },
    103    { /* opt_InputSigFile     */ 's', PR_TRUE, 0, PR_FALSE },
    104    { /* opt_PrintWhyFailure  */ 'v', PR_FALSE, 0, PR_FALSE },
    105    { /* opt_DebugInfo                */ 0, PR_FALSE, 0, PR_FALSE, "debug" }
    106 };
    107 
    108 int
    109 main(int argc, char **argv)
    110 {
    111    PRFileDesc *contentFile = NULL;
    112    PRFileDesc *signFile = PR_STDIN;
    113    FILE *outFile = stdout;
    114    char *progName;
    115    SECStatus rv;
    116    int result = 1;
    117    SECItem pkcs7der, content;
    118    secuCommand signver;
    119 
    120    pkcs7der.data = NULL;
    121    content.data = NULL;
    122 
    123    signver.numCommands = sizeof(signver_commands) / sizeof(secuCommandFlag);
    124    signver.numOptions = sizeof(signver_options) / sizeof(secuCommandFlag);
    125    signver.commands = signver_commands;
    126    signver.options = signver_options;
    127 
    128 #ifdef XP_PC
    129    progName = strrchr(argv[0], '\\');
    130 #else
    131    progName = strrchr(argv[0], '/');
    132 #endif
    133    progName = progName ? progName + 1 : argv[0];
    134 
    135    rv = SECU_ParseCommandLine(argc, argv, progName, &signver);
    136    if (SECSuccess != rv) {
    137        Usage(progName, outFile);
    138    }
    139    debugInfo = signver.options[opt_DebugInfo].activated;
    140    verbose = signver.options[opt_PrintWhyFailure].activated;
    141    doVerify = signver.commands[cmd_VerifySignedObj].activated;
    142    displayAll = signver.commands[cmd_DisplayAllPCKS7Info].activated;
    143    if (!doVerify && !displayAll)
    144        doVerify = PR_TRUE;
    145 
    146    /*  Set the certdb directory (default is ~/.netscape) */
    147    rv = NSS_Init(SECU_ConfigDirectory(signver.options[opt_CertDir].arg));
    148    if (rv != SECSuccess) {
    149        SECU_PrintPRandOSError(progName);
    150        return result;
    151    }
    152    /* below here, goto cleanup */
    153    SECU_RegisterDynamicOids();
    154 
    155    /*  Open the input content file. */
    156    if (signver.options[opt_InputDataFile].activated &&
    157        signver.options[opt_InputDataFile].arg) {
    158        if (PL_strcmp("-", signver.options[opt_InputDataFile].arg)) {
    159            contentFile = PR_Open(signver.options[opt_InputDataFile].arg,
    160                                  PR_RDONLY, 0);
    161            if (!contentFile) {
    162                PR_fprintf(PR_STDERR,
    163                           "%s: unable to open \"%s\" for reading.\n",
    164                           progName, signver.options[opt_InputDataFile].arg);
    165                goto cleanup;
    166            }
    167        } else
    168            contentFile = PR_STDIN;
    169    }
    170 
    171    /*  Open the input signature file.  */
    172    if (signver.options[opt_InputSigFile].activated &&
    173        signver.options[opt_InputSigFile].arg) {
    174        if (PL_strcmp("-", signver.options[opt_InputSigFile].arg)) {
    175            signFile = PR_Open(signver.options[opt_InputSigFile].arg,
    176                               PR_RDONLY, 0);
    177            if (!signFile) {
    178                PR_fprintf(PR_STDERR,
    179                           "%s: unable to open \"%s\" for reading.\n",
    180                           progName, signver.options[opt_InputSigFile].arg);
    181                goto cleanup;
    182            }
    183        }
    184    }
    185 
    186    if (contentFile == PR_STDIN && signFile == PR_STDIN && doVerify) {
    187        PR_fprintf(PR_STDERR,
    188                   "%s: cannot read both content and signature from standard input\n",
    189                   progName);
    190        goto cleanup;
    191    }
    192 
    193    /*  Open|Create the output file.  */
    194    if (signver.options[opt_OutputFile].activated) {
    195        outFile = fopen(signver.options[opt_OutputFile].arg, "w");
    196        if (!outFile) {
    197            PR_fprintf(PR_STDERR, "%s: unable to open \"%s\" for writing.\n",
    198                       progName, signver.options[opt_OutputFile].arg);
    199            goto cleanup;
    200        }
    201    }
    202 
    203    /* read in the input files' contents */
    204    rv = SECU_ReadDERFromFile(&pkcs7der, signFile,
    205                              signver.options[opt_ASCII].activated, PR_FALSE);
    206    if (signFile != PR_STDIN)
    207        PR_Close(signFile);
    208    if (rv != SECSuccess) {
    209        SECU_PrintError(progName, "problem reading PKCS7 input");
    210        goto cleanup;
    211    }
    212    if (contentFile) {
    213        rv = SECU_FileToItem(&content, contentFile);
    214        if (contentFile != PR_STDIN)
    215            PR_Close(contentFile);
    216        if (rv != SECSuccess)
    217            content.data = NULL;
    218    }
    219 
    220    /* Signature Verification */
    221    if (doVerify) {
    222        SEC_PKCS7ContentInfo *cinfo;
    223        SEC_PKCS7SignedData *signedData;
    224        HASH_HashType digestType;
    225        PRBool contentIsSigned;
    226 
    227        cinfo = SEC_PKCS7DecodeItem(&pkcs7der, NULL, NULL, NULL, NULL,
    228                                    NULL, NULL, NULL);
    229        if (cinfo == NULL) {
    230            PR_fprintf(PR_STDERR, "Unable to decode PKCS7 data\n");
    231            goto cleanup;
    232        }
    233        /* below here, goto done */
    234 
    235        contentIsSigned = SEC_PKCS7ContentIsSigned(cinfo);
    236        if (debugInfo) {
    237            PR_fprintf(PR_STDERR, "Content is%s encrypted.\n",
    238                       SEC_PKCS7ContentIsEncrypted(cinfo) ? "" : " not");
    239        }
    240        if (debugInfo || !contentIsSigned) {
    241            PR_fprintf(PR_STDERR, "Content is%s signed.\n",
    242                       contentIsSigned ? "" : " not");
    243        }
    244 
    245        if (!contentIsSigned)
    246            goto done;
    247 
    248        signedData = cinfo->content.signedData;
    249 
    250        /* assume that there is only one digest algorithm for now */
    251        digestType = AlgorithmToHashType(signedData->digestAlgorithms[0]);
    252        if (digestType == HASH_AlgNULL) {
    253            PR_fprintf(PR_STDERR, "Invalid hash algorithmID\n");
    254            goto done;
    255        }
    256        if (content.data) {
    257            SECCertUsage usage = certUsageEmailSigner;
    258            SECItem digest;
    259            unsigned char digestBuffer[HASH_LENGTH_MAX];
    260 
    261            if (debugInfo)
    262                PR_fprintf(PR_STDERR, "contentToVerify=%s\n", content.data);
    263 
    264            digest.data = digestBuffer;
    265            digest.len = sizeof digestBuffer;
    266 
    267            if (DigestContent(&digest, &content, digestType)) {
    268                SECU_PrintError(progName, "Message digest computation failure");
    269                goto done;
    270            }
    271 
    272            if (debugInfo) {
    273                unsigned int i;
    274                PR_fprintf(PR_STDERR, "Data Digest=:");
    275                for (i = 0; i < digest.len; i++)
    276                    PR_fprintf(PR_STDERR, "%02x:", digest.data[i]);
    277                PR_fprintf(PR_STDERR, "\n");
    278            }
    279 
    280            fprintf(outFile, "signatureValid=");
    281            PORT_SetError(0);
    282            if (SEC_PKCS7VerifyDetachedSignature(cinfo, usage,
    283                                                 &digest, digestType, PR_FALSE)) {
    284                fprintf(outFile, "yes");
    285            } else {
    286                fprintf(outFile, "no");
    287                if (verbose) {
    288                    fprintf(outFile, ":%s",
    289                            SECU_Strerror(PORT_GetError()));
    290                }
    291            }
    292            fprintf(outFile, "\n");
    293            result = 0;
    294        }
    295    done:
    296        SEC_PKCS7DestroyContentInfo(cinfo);
    297    }
    298 
    299    if (displayAll) {
    300        if (SV_PrintPKCS7ContentInfo(outFile, &pkcs7der))
    301            result = 1;
    302    }
    303 
    304 cleanup:
    305    SECITEM_FreeItem(&pkcs7der, PR_FALSE);
    306    SECITEM_FreeItem(&content, PR_FALSE);
    307 
    308    if (NSS_Shutdown() != SECSuccess) {
    309        result = 1;
    310    }
    311 
    312    return result;
    313 }