.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample6:

NSS Sample Code sample6
=======================

.. _nss_sample_code_6_persistent_symmetric_keys_in_nss_database:

`NSS Sample Code 6: Persistent Symmetric Keys in NSS database <#nss_sample_code_6_persistent_symmetric_keys_in_nss_database>`__
-------------------------------------------------------------------------------------------------------------------------------

.. container::

   .. code::

      /* Example code to illustrate generation of a secret symmetric key ring
       * that PERSISTS in the NSS database. The symmetric keys can then be used
       * without ever exposing them in the clear.
       *
       * To encrypt, you need the id of the key to use.
       * To decrypt, you need the ciphertext and the id of the key that was used
       * to encrypt
       *
       * Before running this example, create the NSS database
       *     certutil -N -d .
       * (enter "test" when prompted for password)
       */


      #include "nss.h"
      #include "pk11pub.h"

      /* the key id can be any sequence of bytes. this example happens to use an
       * integer */
      void genkey(int id);

      /* this callback is responsible for returning the password to the NSS
       * key database. for example purposes, this function hardcodes the password.
       * In a real app, this function should obtain the password using secure means
       * such as prompting an operator, or retrieving it over a secure communication
       * channel
       */
      char *passwdcb(PK11SlotInfo *info, PRBool retry, void *arg);


      int main(int argc, char **argv)
      {
        SECStatus rv;

        /* Initialize NSS */
        PK11_SetPasswordFunc(passwdcb);

        /* The NSS db must be initialized read-write since we'll be creating
         * keys in it. Once keys are generated, it can be opened without read-write
         * subsequently (NSS_Init).
         */
        rv = NSS_InitReadWrite(".");
        if (rv != SECSuccess)
        {
          fprintf(stderr, "NSS initialization failed (err %d)\n",
                  PR_GetError());
          exit(1);
        }

        /* generate a key with id 1. should succeed on first run on a fresh db,
         * should fail on successive runs because key with that id already exists */
        genkey(1);

        /* generate a key with id 2. should succeed on first run on a fresh db,
         * should fail on successive runs because key with that id already exists */
        genkey(2);

        /* generate a key with id 1 - this will fail because key with that id
         * already exists */
        genkey(1);
      }


      void genkey(int id)
      {
        PK11SlotInfo*  slot = NULL;
        PK11SymKey*    key = NULL;
        SECItem        keyiditem;
        int            keyid[1];
        CK_MECHANISM_TYPE cipherMech;

        /* using CKM_AES_CBC_PAD mechanism for example */
        cipherMech = CKM_AES_CBC_PAD;

         slot = PK11_GetInternalKeySlot();
        /* slot = PK11_GetBestSlot(cipherMech, NULL); didn't work.
         * Error code: token is read-only. ??
         */
        if (slot == NULL)
        {
          fprintf(stderr, "Unable to find security device (err %d)\n",
                  PR_GetError());
          return;
        }

        keyid[0] = id;
        keyiditem.type = siBuffer;
        keyiditem.data = (void *)keyid;
        keyiditem.len = sizeof(keyid[0]);

        /* Note: keysize must be 0 for fixed key-length algorithms like DES.
         *       Since we're using AES in this example, we're specifying
         *       one of the valid keysizes (16, 24, 32)
         */
        key = PK11_TokenKeyGen(slot, cipherMech, 0, 32 /*keysize*/,
                               &keyiditem, PR_TRUE, 0);
        if (key == NULL)
        {
          fprintf(stderr, "PK11_TokenKeyGen failed (err %d)\n",
                  PR_GetError());
          PK11_FreeSlot(slot);
          return;
        }

        fprintf(stderr, "key length of generated key is %d\n",
                PK11_GetKeyLength(key));
        fprintf(stderr, "mechanism of key is %d (asked for %d)\n",
                PK11_GetMechanism(key), cipherMech);

        PK11_FreeSymKey(key);


        key = PK11_FindFixedKey(slot, cipherMech, &keyiditem, 0);
        if (key == NULL)
        {
          fprintf(stderr, "PK11_FindFixedKey failed (err %d)\n",
                  PR_GetError());
          PK11_FreeSlot(slot);
          return;
        }

        fprintf(stderr, "Found key!\n");
        fprintf(stderr, "key length of generated key is %d\n",
                PK11_GetKeyLength(key));
        fprintf(stderr, "mechanism of key is %d (asked for %d)\n",
                PK11_GetMechanism(key), cipherMech);

        PK11_FreeSymKey(key);

        PK11_FreeSlot(slot);
      }

      char *passwdcb(PK11SlotInfo *info, PRBool retry, void *arg)
      {
        if (!retry)
          return PL_strdup("test");
        else
          return NULL;
      }