.. _mozilla_projects_nss_nss_sample_code_sample1_-_hashing:

Hashing - sample 1
==================

.. _nss_sample_code_1_hashing.:

`NSS sample code 1: hashing. <#nss_sample_code_1_hashing.>`__
-------------------------------------------------------------

.. container::

   The NSS same code below computes the hash of a file and saves it to another file, this
   illustrates the use of NSS message APIs.

   .. code:: c

      /* 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 https://mozilla.org/MPL/2.0/. */

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

      /* NSS headers */
      #include <secoid.h>
      #include <secmodt.h>
      #include <sechash.h>
      #include <nss.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 if 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;
          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;
      }