Encryption and Decryption

NSS Sample Code: 2

Sample program that demonstrates how to encrypt and decrypt using NSS.

/* Example code to illustrate encryption/decryption using NSS.
 * The example skips the details of obtaining the Key & IV to use, and
 * just uses a hardcoded Key & IV.
 * Note: IV is only needed if Cipher Blocking Chaining (CBC) mode of encryption
 *       is used
 *
 * The recommended approach is to store and transport WRAPPED (encrypted)
 * symmteric keys (IVs can be in the clear). However, it is a common, and
 * dangerous, practice to use raw DES Keys. This example shows the use of
 * a RAW key.
 */

#include <nss.h>
#include <pk11pub.h>
#include <nspr.h>
#include <prprf.h>
#include <prlog.h>
#include <plstr.h>

/* example Key & IV */
unsigned char gKey[] = {0xe8, 0xa7, 0x7c, 0xe2, 0x05, 0x63, 0x6a, 0x31};
unsigned char gIV[] = {0xe4, 0xbb, 0x3b, 0xd3, 0xc3, 0x71, 0x2e, 0x58};

int main(int argc, char **argv)
{
  CK_MECHANISM_TYPE  cipherMech;
  PK11SlotInfo*      slot = NULL;
  PK11SymKey*        symKey = NULL;
  SECItem*           secParam = NULL;
  PK11Context*       ctx = NULL;
  SECItem            keyItem, ivItem;
  SECStatus          rv, rv1, rv2;
  unsigned char      data[1024], encrypted[1024], decrypted[1024];
  int                i, outLen1;
  unsigned int       resultLen, outLen2;
  PRBool             nssInitialized = PR_FALSE;

  do {
    /* Initialize NSS
     * If your application code has already initialized NSS, you can skip it
     * here.
     * This code uses the simplest of the Init functions, which does not
     * require a NSS database to exist
     */
    rv = NSS_NoDB_Init(".");
    if (rv != SECSuccess)
    {
      PR_fprintf(PR_STDERR, "NSS initialization failed (err %d)\n", rv);
      break;
    }

    nssInitialized = PR_TRUE;

    /* choose mechanism: CKM_DES_CBC_PAD, CKM_DES3_ECB, CKM_DES3_CBC.....
     * Note that some mechanisms (*_PAD) imply the padding is handled for you
     * by NSS. If you choose something else, then data padding is the
     * application's responsibility
     */
    cipherMech = CKM_DES_CBC_PAD;
    slot = PK11_GetBestSlot(cipherMech, NULL);
    /* slot = PK11_GetInternalKeySlot(); is a simpler alternative but in
     * theory, it *may not* return the optimal slot for the operation. For
     * DES ops, Internal slot is typically the best slot
     */
    if (!slot)
    {
      rv = PR_GetError();
      PR_fprintf(PR_STDERR, "Unable to find security device (err %d)\n", rv);
      break;
    }

    /* NSS passes blobs around as SECItems. These contain a pointer to
     * data and a length. Turn the raw key into a SECItem.
     */
    keyItem.type = siBuffer;
    keyItem.data = gKey;
    keyItem.len = sizeof(gKey);

    /* Turn the raw key into a key object. We use PK11_OriginUnwrap
     * to indicate the key was unwrapped - which is what should be done
     * normally anyway - using raw keys isn't a good idea
     */
    symKey = PK11_ImportSymKey(slot, cipherMech, PK11_OriginUnwrap,
                               CKA_ENCRYPT, &keyItem, NULL);
    if (!symKey)
    {
      rv = PR_GetError();
      PR_fprintf(PR_STDERR,
	    		"Failure to import key into NSS (err %d)\n", rv);
      break;
    }

    /* set up the PKCS11 encryption parameters.
     * when not using CBC mode, ivItem.data and ivItem.len can be 0, or you
     * can simply pass NULL for the iv parameter in PK11_ParamFromIV function
     */
    ivItem.type = siBuffer;
    ivItem.data = gIV;
    ivItem.len = sizeof(gIV);
    secParam = PK11_ParamFromIV(cipherMech, &ivItem);
    if (!secParam)
    {
      rv = PR_GetError();
      PR_fprintf(PR_STDERR,
                 "Failure to set up PKCS11 param (err %d)\n", rv);
      break;
    }

    /* sample data we'll encrypt and decrypt */
    PL_strcpy((char *)data, "Encrypt me!");
    PR_fprintf(PR_STDERR, "Clear Data: %s\n", data);

    /* ========================= START SECTION ============================= */
    /* If using the the same key and iv over and over, stuff before this */
    /* section and after this section needs to be done only ONCE */

    /* ENCRYPT data into cipherText.
     * cipherText length must be at least (data len + 8)
     */
    outLen1 = outLen2 = 0;

    /* Create cipher context */
    ctx = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT,
                                     symKey, secParam);

    rv1 = PK11_CipherOp(ctx, encrypted, &outLen1, sizeof(encrypted),
                        data, (int)PL_strlen((const char *)data)+1);

    rv2 = PK11_DigestFinal(ctx, encrypted+outLen1, &outLen2,
                           sizeof(encrypted) - outLen1);

    PK11_DestroyContext(ctx, PR_TRUE);
    resultLen = outLen1 + outLen2;
    if (rv1 != SECSuccess || rv2 != SECSuccess)
      break;

    PR_fprintf(PR_STDOUT, "Encrypted Data: ");
    for (i = 0; i < resultLen; i++)
      PR_fprintf(PR_STDOUT,"%02x ", encrypted[i]);
    PR_fprintf(PR_STDOUT,"\n");

    /* Release it before reusing ctx for a new one */
    PK11_DestroyContext(ctx, PR_TRUE);

    /* DECRYPT encrypted into decrypted. decrypted
     * length must be at least encrypted length
     */
    outLen1 = outLen2 = 0;
    
    /* Create cipher context */
    ctx = PK11_CreateContextBySymKey(cipherMech, CKA_DECRYPT,
                                     symKey, secParam);

    rv1 = PK11_CipherOp(ctx, decrypted, &outLen1, sizeof(decrypted),
                        encrypted, resultLen);
    rv2 = PK11_DigestFinal(ctx, decrypted+outLen1, &outLen2,
                           resultLen-outLen1);

    resultLen = outLen1 + outLen2;
    if (rv1 != SECSuccess || rv2 != SECSuccess)
      break;

    PR_ASSERT(resultLen == PLstrlen(data));
    PR_ASSERT(PL_strncmp(decrypted, data, resultLen)==0);

    PR_fprintf(PR_STDERR,"Decrypted Data: %s\n", decrypted);

    /* =========================== END SECTION ============================= */

  } while (0);

  /* Cleanup after ourselves */

  if (symKey)
    PK11_FreeSymKey(symKey);

  if (secParam)
	SECITEM_FreeItem(secParam, PR_TRUE);

  if (ctx)
	PK11_DestroyContext(ctx, PR_TRUE);

  if (nssInitialized)
    (void) NSS_Shutdown();

  return rv;
}