Symmtric Key Generation in the NSS Database


Sample 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>
#include <nspr.h>
#include <prprf.h>
#include <prlog.h>

/* the key id can be any sequence of bytes. this example happens to use an
 * integer */
static 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
 */
static 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) {
    PR_fprintf(PR_STDERR, "NSS initialization failed (err %d)\n", rv);
      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);

  return 0;
}

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;

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

    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) {
      PR_fprintf(PR_STDERR, "PK11_TokenKeyGen failed (err %d)\n",
                 PR_GetError());
      break;
    }

    PR_fprintf(PR_STDOUT, "key length of generated key is %d\n",
                PK11_GetKeyLength(key));

    PR_fprintf(PR_STDOUT,
               "mechanism of key is %ldl (asked for %ld)\n",
               PK11_GetMechanism(key), cipherMech);

    PK11_FreeSymKey(key);

    key = PK11_FindFixedKey(slot, cipherMech, &keyIdItem, 0);
    if (!key) {
        PR_fprintf(PR_STDERR, "PK11_FindFixedKey failed (err %d)\n",
                   PR_GetError());
        PK11_FreeSlot(slot);
       return;
    }

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

  } while (0);

  if (key)
    PK11_FreeSymKey(key);

  if (slot)
    PK11_FreeSlot(slot);
}

char *passwdcb(PK11SlotInfo *info, PRBool retry, void *arg)
{
  return retry ? NULL : PL_strdup("testsample");
}