.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_utililies_1:

NSS Sample Code Utilities_1
===========================

.. _nss_sample_code_common_utilities:

`NSS Sample Code Common: Utilities <#nss_sample_code_common_utilities>`__
-------------------------------------------------------------------------

.. container::

   This is a library of utilities used by many of the samples.  This code shows the following:

   -  Extract seed from noise file
   -  Read DER encoding from a file
   -  Extract the password from a text file
   -  Get the module password
   -  Print as ascii or hexadecimal

.. _sample_code:

`Sample Code <#sample_code>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. container::

   .. code:: notranslate

      #include <prlog.h>
      #include <termios.h>
      #include <base64.h>
      #include <unistd.h>
      #include <sys/stat.h>
      #include <prprf.h>
      #include "util.h"


      /*
       * These utility functions are adapted from those found in
       * the sectool library used by the NSS security tools and
       * other NSS test applications.
       */

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

      /*
       * PrintAsAscii
       */
      void PrintAsAscii(PRFileDesc* out, const unsigned char *data, unsigned int len)
      {
          char *b64Data = NULL;

          b64Data = BTOA_DataToAscii(data, len);
          PR_fprintf(out, "%s", b64Data);
          PR_fprintf(out, "\n");
          if (b64Data) {
              PORT_Free(b64Data);
          }
      }

      /*
       * PrintAsHex
       */
      void PrintAsHex(PRFileDesc* out, const 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);
          }
      }

      /*
       * GetDigit
       */
      int GetDigit(char c)
      {
          if (c == 0) {
              return -1;
          }
          if (c <= '9' && c >= '0') {
              return c - '0';
          }
          if (c <= 'f' && c >= 'a') {
              return c - 'a' + 0xa;
          }
          if (c <= 'F' && c >= 'A') {
              return c - 'A' + 0xa;
          }
          return -1;
      }

      /*
       * HexToBuf
       */
      int HexToBuf(unsigned char *inString, SECItem *outbuf, PRBool isHexData)
      {
          int len = strlen(inString);
          int outLen = len+1/2;
          int trueLen = 0;
          int digit1, digit2;

          outbuf->data = isHexData
              ? PORT_Alloc(outLen)
              : PORT_Alloc(len);
          if (!outbuf->data) {
              return -1;
          }
          if (isHexData) {
              while (*inString) {
                   if ((*inString == '\n') || (*inString == ':')) {
                       inString++;
                       continue;
                   }
                   digit1 = GetDigit(*inString++);
                   digit2 = GetDigit(*inString++);
                   if ((digit1 == -1) || (digit2 == -1)) {
                       PORT_Free(outbuf->data);
                       outbuf->data = NULL;
                       return -1;
                   }
                   outbuf->data[trueLen++] = digit1 << 4 | digit2;
              }
          } else {
              while (*inString) {
                  if (*inString == '\n') {
                      inString++;
                      continue;
                  }
                  outbuf->data[trueLen++] = *inString++;
              }
              outbuf->data[trueLen] = '\0';
              trueLen = trueLen-1;
          }
          outbuf->len = trueLen;
          return 0;
      }

      /*
       * FileToItem
       */
      SECStatus FileToItem(SECItem *dst, PRFileDesc *src)
      {
          PRFileInfo info;
          PRInt32 numBytes;
          PRStatus prStatus;

          prStatus = PR_GetOpenFileInfo(src, &info);

          if (prStatus != PR_SUCCESS) {
              return SECFailure;
          }

          dst->data = 0;
          if (SECITEM_AllocItem(NULL, dst, info.size)) {
              numBytes = PR_Read(src, dst->data, info.size);
              if (numBytes == info.size) {
                  return SECSuccess;
              }
          }
          SECITEM_FreeItem(dst, PR_FALSE);
          dst->data = NULL;
          return SECFailure;
      }

      /*
       * echoOff
       */
      static void echoOff(int fd)
      {
         if (isatty(fd)) {
             struct termios tio;
             tcgetattr(fd, &tio);
             tio.c_lflag &= ~ECHO;
             tcsetattr(fd, TCSAFLUSH, &tio);
         }
      }

      /*
       * echoOn
       */
      static void echoOn(int fd)
      {
         if (isatty(fd)) {
             struct termios tio;
             tcgetattr(fd, &tio);
             tio.c_lflag |= ECHO;
             tcsetattr(fd, TCSAFLUSH, &tio);
         }
      }

      /*
       * CheckPassword
       */
      PRBool CheckPassword(char *cp)
      {
          int len;
          char *end;
          len = PORT_Strlen(cp);
          if (len < 8) {
              return PR_FALSE;
          }
          end = cp + len;
          while (cp < end) {
              unsigned char ch = *cp++;
              if (!((ch >= 'A') && (ch <= 'Z')) &&
                  !((ch >= 'a') && (ch <= 'z'))) {
                  return PR_TRUE;
              }
         }
         return PR_FALSE;
      }

      /*
       * GetPassword
       */
      char* GetPassword(FILE *input, FILE *output, char *prompt,
                        PRBool (*ok)(char *))
      {
          char phrase[200] = {'\0'};
          int infd         = fileno(input);
          int isTTY        = isatty(infd);

          for (;;) {
              /* Prompt for password */
              if (isTTY) {
                  fprintf(output, "%s", prompt);
                  fflush (output);
                  echoOff(infd);
              }
              fgets(phrase, sizeof(phrase), input);
              if (isTTY) {
                  fprintf(output, "\n");
                  echoOn(infd);
              }
              /* stomp on newline */
              phrase[PORT_Strlen(phrase)-1] = 0;
              /* Validate password */
              if (!(*ok)(phrase)) {
                  if (!isTTY) return 0;
                  fprintf(output, "Password must be at least 8 characters long with one or more\n");
                  fprintf(output, "non-alphabetic characters\n");
                  continue;
              }
              return (char*) PORT_Strdup(phrase);
          }
      }

      /*
       * FilePasswd extracts the password from a text file
       *
       * Storing passwords is often used with server environments
       * where prompting the user for a password or requiring it
       * to be entered in the command line is not a feasible option.
       *
       * This function supports password extraction from files with
       * multiple passwords, one for each token. In the single password
       * case a line would just have the password whereas in the multi-
       * password variant they could be of the form
       *
       * token_1_name:its_password
       * token_2_name:its_password
       *
       */
      char *FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg)
      {
          char* phrases, *phrase;
          PRFileDesc *fd;
          PRInt32 nb;
          char *pwFile = arg;
          int i;
          const long maxPwdFileSize = 4096;
          char* tokenName = NULL;
          int tokenLen = 0;

          if (!pwFile)
              return 0;

          if (retry) {
              return 0;  /* no good retrying - the file contents will be the same */
          }

          phrases = PORT_ZAlloc(maxPwdFileSize);

          if (!phrases) {
              return 0; /* out of memory */
          }

          fd = PR_Open(pwFile, PR_RDONLY, 0);
          if (!fd) {
              fprintf(stderr, "No password file \"%s\" exists.\n", pwFile);
              PORT_Free(phrases);
              return NULL;
          }

          nb = PR_Read(fd, phrases, maxPwdFileSize);

          PR_Close(fd);

          if (nb == 0) {
              fprintf(stderr,"password file contains no data\n");
              PORT_Free(phrases);
              return NULL;
          }

          if (slot) {
              tokenName = PK11_GetTokenName(slot);
              if (tokenName) {
                  tokenLen = PORT_Strlen(tokenName);
              }
          }
          i = 0;
          do {
              int startphrase = i;
              int phraseLen;

              /* handle the Windows EOL case */
              while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb) i++;

              /* terminate passphrase */
              phrases[i++] = '\0';
              /* clean up any EOL before the start of the next passphrase */
              while ( (i<nb) analyze="" char="" current="" getmodulepassword="" if="" int="" now="" passphrase="" phrase="&amp;phrases[startphrase];" phraselen="" pk11slotinfo="" pwdata="=" pwdata-="" retry="" return="" secupwdata="" the="" void="" while="">source != PW_NONE) {
              PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n");
              return NULL;
          }

          switch (pwdata->source) {
          case PW_NONE:
              sprintf(prompt, "Enter Password or Pin for \"%s\":",
                      PK11_GetTokenName(slot));
              return GetPassword(stdin, stdout, prompt, CheckPassword);
          case PW_FROMFILE:
              pw = FilePasswd(slot, retry, pwdata->data);
              pwdata->source = PW_PLAINTEXT;
              pwdata->data = PL_strdup(pw);
              return pw;
          case PW_PLAINTEXT:
              return PL_strdup(pwdata->data);
          default:
              break;
          }
          PR_fprintf(PR_STDERR, "Password check failed: No password found.\n");
          return NULL;
      }

      /*
       * GenerateRandom
       */
      SECStatus GenerateRandom(unsigned char *rbuf, int rsize)
      {
          char meter[] = {
                         "\r|                                |" };
          int            fd,  count;
          int            c;
          SECStatus      rv                  = SECSuccess;
          cc_t           orig_cc_min;
          cc_t           orig_cc_time;
          tcflag_t       orig_lflag;
          struct termios tio;

          fprintf(stderr, "To generate random numbers, "
                  "continue typing until the progress meter is full:\n\n");
          fprintf(stderr, "%s", meter);
          fprintf(stderr, "\r|");

          /* turn off echo on stdin & return on 1 char instead of NL */
          fd = fileno(stdin);

          tcgetattr(fd, &tio);
          orig_lflag = tio.c_lflag;
          orig_cc_min = tio.c_cc[VMIN];
          orig_cc_time = tio.c_cc[VTIME];
          tio.c_lflag &= ~ECHO;
          tio.c_lflag &= ~ICANON;
          tio.c_cc[VMIN] = 1;
          tio.c_cc[VTIME] = 0;
          tcsetattr(fd, TCSAFLUSH, &tio);
          /* Get random noise from keyboard strokes */
          count = 0;
          while (count < rsize) {
              c = getc(stdin);
              if (c == EOF) {
                  rv = SECFailure;
                  break;
              }
              *(rbuf + count) = c;
              if (count == 0 || c != *(rbuf + count -1)) {
                  count++;
                  fprintf(stderr, "*");
              }
          }
          rbuf[count] = '\0';

          fprintf(stderr, "\n\nFinished.  Press enter to continue: ");
          while ((c = getc(stdin)) != '\n' && c != EOF)
              ;
          if (c == EOF)
              rv = SECFailure;
          fprintf(stderr, "\n");

          /* set back termio the way it was */
          tio.c_lflag = orig_lflag;
          tio.c_cc[VMIN] = orig_cc_min;
          tio.c_cc[VTIME] = orig_cc_time;
          tcsetattr(fd, TCSAFLUSH, &tio);
          return rv;
      }

      /*
       * SeedFromNoiseFile
       */
      SECStatus SeedFromNoiseFile(const char *noiseFileName)
      {
          char buf[2048];
          PRFileDesc *fd;
          PRInt32 count;

          fd = PR_Open(noiseFileName, PR_RDONLY, 0);
          if (!fd) {
              fprintf(stderr, "failed to open noise file.");
              return SECFailure;
          }

          do {
              count = PR_Read(fd,buf,sizeof(buf));
              if (count > 0) {
                  PK11_RandomUpdate(buf,count);
              }
          } while (count > 0);

          PR_Close(fd);
          return SECSuccess;
      }

      /*
       * FileSize
       */
      long FileSize(const char* filename)
      {
          struct stat stbuf;
          stat(filename, &stbuf);
          return stbuf.st_size;
      }

      /*
       *  ReadDERFromFile
       */
      SECStatus ReadDERFromFile(SECItem *der, const char *inFileName, PRBool ascii)
      {
          SECStatus rv       = SECSuccess;
          PRFileDesc *inFile = NULL;

          inFile = PR_Open(inFileName, PR_RDONLY, 0);
          if (!inFile) {
              PR_fprintf(PR_STDERR, "Failed to open file \"%s\" (%ld, %ld).\n",
                         inFileName, PR_GetError(), PR_GetOSError());
              rv = SECFailure;
              goto cleanup;
          }

          if (ascii) {
              /* First convert ascii to binary */
              SECItem filedata;
              char *asc, *body;

              /* Read in ascii data */
              rv = FileToItem(&filedata, inFile);
              asc = (char *)filedata.data;
              if (!asc) {
                  PR_fprintf(PR_STDERR, "unable to read data from input file\n");
                  rv = SECFailure;
                  goto cleanup;
              }

              /* check for headers and trailers and remove them */
              if ((body = strstr(asc, "-----BEGIN")) != NULL) {
                  char *trailer = NULL;
                  asc = body;
                  body = PORT_Strchr(body, '\n');
                  if (!body)
                      body = PORT_Strchr(asc, '\r'); /* maybe this is a MAC file */
                  if (body)
                      trailer = strstr(++body, "-----END");
                  if (trailer != NULL) {
                      *trailer = '\0';
                  } else {
                      PR_fprintf(PR_STDERR,  "input has header but no trailer\n");
                      PORT_Free(filedata.data);
                      rv = SECFailure;
                      goto cleanup;
                  }
              } else {
                  body = asc;
              }

              /* Convert to binary */
              rv = ATOB_ConvertAsciiToItem(der, body);
              if (rv) {
                  PR_fprintf(PR_STDERR,  "error converting ascii to binary %s\n",
                             PORT_GetError());
                  PORT_Free(filedata.data);
                  rv = SECFailure;
                  goto cleanup;
              }

              PORT_Free(filedata.data);
          } else {
              /* Read in binary der */
              rv = FileToItem(der, inFile);
              if (rv) {
                  PR_fprintf(PR_STDERR, "error converting der \n");
                  rv = SECFailure;
              }
          }
      cleanup:
          if (inFile) {
              PR_Close(inFile);
          }
          return rv;
      }

      </nb)>