.. _mozilla_projects_nss_nss_sample_code_sample1:

sample1
=======

.. container::

   1. A program to compute the hash of a file and save it to another file.

   .. code:: c

       /* NSPR Headers */
       #include <prprf.h>
       #include <prtypes.h>
       #include <plgetopt.h>
       #include <prio.h>
       #include <prprf.h>

       /* NSS headers */
       #include <secoid.h>
       #include <secmodt.h>
       #include <sechash.h>

       typedef struct {
           const char *hashName;
           SECOidTag oid;
       } NameTagPair;

       /* The hash algorithms supported */
       static const NameTagPair HASH_NAMES[] = {
           { "MD2", SEC_OID_MD2 },
           { "MD5", SEC_OID_MD5 },
           { "SHA1", SEC_OID_SHA1 },
           { "SHA256", SEC_OID_SHA256 },
           { "SHA384", SEC_OID_SHA384 },
           { "SHA512", SEC_OID_SHA512 }
       };

       /* Maps a hash name to a SECOidTag.
        * Returns NULL if the name is not a supported algorithm
        */
       static SECOidTag HashNameToOIDTag(const char *hashName)
       {
           int i, nhashes = sizeof(HASH_NAMES);
           SECOidTag hashtag = SEC_OID_UNKNOWN;

           for (i = 0; i < nhashes; i++) {
               if (PORT_Strcasecmp(hashName, HASH_NAMES[i].hashName) == 0) {
                   hashtag = HASH_NAMES[i].oid;
                   break;
               }
           }
           return hashtag;
       }

       /*  Newline  */
       static void Newline(PRFileDesc* out)
       {
           PR_fprintf(out, "\n");
       }

       /*  PrintAsHex  */
       void PrintAsHex(PRFileDesc* out, unsigned char *data, unsigned int len)
       {
           unsigned i;
           int column;
           unsigned int limit = 15;
           unsigned int level = 1;

           column = level;
           if (!len) {
               PR_fprintf(out, "(empty)\n");
               return;
           }

           for (i = 0; i < len; i++) {
               if (i != len - 1) {
                   PR_fprintf(out, "%02x:", data[i]);
                   column += 3;
               } else {
                   PR_fprintf(out, "%02x", data[i]);
                   column += 2;
                   break;
               }
               if (column > 76 || (i % 16 == limit)) {
                   Newline(out);
                   column = level;
                   limit = i % 16;
               }
           }
           if (column != level) {
               Newline(out);
           }
       }


       /*  Prints a usage message and exits  */
       static void Usage(const char *progName)
       {
           int htype;
           int HASH_AlgTOTAL = sizeof(HASH_NAMES) / sizeof(HASH_NAMES[0]);

           fprintf(stderr, "Usage:  %s -t type [ < input ] [ > output ]\n", progName);
           fprintf(stderr, "%-20s Specify the digest method (must be one of\n",
                   "-t type");
           fprintf(stderr, "%-20s ", "");
           for (htype = 0; htype < HASH_AlgTOTAL; htype++) {
               fprintf(stderr, HASH_NAMES[htype].hashName);
               if (htype == (HASH_AlgTOTAL - 2))
                   fprintf(stderr, " or ");
               else if (htype != (HASH_AlgTOTAL - 1))
                   fprintf(stderr, ", ");
           }
           fprintf(stderr, " (case ignored))\n");
           fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n",
                   "< input");
           fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n",
                   "> output");
           exit(-1);
       }

       /*  Check for the missing arguments  */
       static void
       PrintMsgAndExit(const char *progName, char opt)
       {
           fprintf(stderr, "%s: option -%c requires an argument\n", progName, opt);
           Usage(progName);
       }

       #define REQUIRE_ARG(opt,value) if (!(value)) PrintMsgAndExit(progName, opt)

       /* Digests a file according to the specified algorithm.
        * It writes out the digest as a hexadecimal string.
        */
       static int
       DigestFile(PRFileDesc *outFile, PRFileDesc *inFile, SECOidTag hashOIDTag)
       {
           unsigned int  nb;
           unsigned char ibuf[4096];
           unsigned char digest[64];
           unsigned int  len;
           unsigned int  digestLen;
           HASH_HashType hashType;
           HASHContext   *hashContext = NULL;

           hashType    = HASH_GetHashTypeByOidTag(hashOIDTag);
           hashContext = HASH_Create(hashType);
           if (hashContext == NULL) {
               return SECFailure;
           }

           do {
               HASH_Begin(hashContext);

               /* Incrementally hash the file contents */
               while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) {
                   HASH_Update(hashContext, ibuf, nb);
               }

               HASH_End(hashContext, digest, &len, 64);

               /*  Normally we would write it out in binary with
                *  nb = PR_Write(outFile, digest, len);
                *  but for illustration let's print it in hex.
                */
               PrintAsHex(outFile, digest, len);

           } while (0);

           /* cleanup */
           if (hashContext != NULL)
               HASH_Destroy(hashContext);

           return SECSuccess;
       }

       /*
        * This sample computes the hash of a file and saves it to another file. It illustrates the use of NSS message APIs.
        */
       int main(int argc, char **argv)
       {
           SECOidTag     hashOIDTag;
           PLOptState    *optstate;
           PLOptStatus   status;
           SECStatus  rv;
           char *hashName  = NULL;
           char  *progName = strrchr(argv[0], '/');

           progName = progName ? progName + 1 : argv[0];

           rv = NSS_NoDB_Init("/tmp");
           if (rv != SECSuccess) {
               fprintf(stderr, "%s: NSS_Init failed in directory %s\n", progName, "/tmp");
               return -1;
           }

           /* Parse command line arguments */
           optstate = PL_CreateOptState(argc, argv, "t:");
           while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
               switch (optstate->option) {
               case 't':
                   REQUIRE_ARG(optstate->option, optstate->value);
                   hashName = strdup(optstate->value);
                   break;
               }
           }

           if (!hashName)
               Usage(progName);

           /* convert and validate */
           hashOIDTag = HashNameToOIDTag(hashName);
           if (hashOIDTag == SEC_OID_UNKNOWN) {
               fprintf(stderr, "%s: invalid digest type - %s\n", progName, hashName);
               Usage(progName);
           }

           /* Digest it and print the result */
           rv = DigestFile(PR_STDOUT, PR_STDIN, hashOIDTag);
           if (rv != SECSuccess) {
               fprintf(stderr, "%s: problem digesting data (%d)\n", progName, PORT_GetError());
           }

           rv = NSS_Shutdown();
           if (rv != SECSuccess) {
               exit(-1);
           }

           return 0;
       }