Generates encryption/mac keys and uses session objects.
/* 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 #include #include #include #include #include #include /* NSS headers */ #include #include /* our samples utilities */ #include "util.h" #define BUFFERSIZE 80 #define DIGESTSIZE 16 #define PTEXT_MAC_BUFFER_SIZE 96 #define CIPHERSIZE 96 #define BLOCKSIZE 32 #define CIPHER_HEADER "-----BEGIN CIPHER-----" #define CIPHER_TRAILER "-----END CIPHER-----" #define ENCKEY_HEADER "-----BEGIN AESKEY CKAID-----" #define ENCKEY_TRAILER "-----END AESKEY CKAID-----" #define MACKEY_HEADER "-----BEGIN MACKEY CKAID-----" #define MACKEY_TRAILER "-----END MACKEY CKAID-----" #define IV_HEADER "-----BEGIN IV-----" #define IV_TRAILER "-----END IV-----" #define MAC_HEADER "-----BEGIN MAC-----" #define MAC_TRAILER "-----END MAC-----" #define PAD_HEADER "-----BEGIN PAD-----" #define PAD_TRAILER "-----END PAD-----" typedef enum { ENCRYPT, DECRYPT, UNKNOWN } CommandType; typedef enum { SYMKEY = 0, MACKEY = 1, IV = 2, MAC = 3, PAD = 4 } HeaderType; /* * Print usage message and exit */ static void Usage(const char *progName) { fprintf(stderr, "\nUsage: %s -c -d [-z ] " "[-p | -f ] -i -o \n\n", progName); fprintf(stderr, "%-20s Specify 'a' for encrypt operation\n\n", "-c "); fprintf(stderr, "%-20s Specify 'b' for decrypt operation\n\n", " "); fprintf(stderr, "%-20s Specify db directory path\n\n", "-d "); fprintf(stderr, "%-20s Specify db password [optional]\n\n", "-p "); fprintf(stderr, "%-20s Specify db password file [optional]\n\n", "-f "); fprintf(stderr, "%-20s Specify noise file name [optional]\n\n", "-z "); fprintf(stderr, "%-21s Specify an input file name\n\n", "-i "); fprintf(stderr, "%-21s Specify an output file name\n\n", "-o "); fprintf(stderr, "%-7s For encrypt, it takes as an input file and produces\n", "Note :"); fprintf(stderr, "%-7s .enc and .header as intermediate output files.\n\n", ""); fprintf(stderr, "%-7s For decrypt, it takes .enc and .header\n", ""); fprintf(stderr, "%-7s as input files and produces as a final output file.\n\n", ""); exit(-1); } /* * Gather a CKA_ID */ SECStatus GatherCKA_ID(PK11SymKey* key, SECItem* buf) { SECStatus rv = PK11_ReadRawAttribute(PK11_TypeSymKey, key, CKA_ID, buf); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "PK11_ReadRawAttribute returned (%d)\n", rv); PR_fprintf(PR_STDERR, "Could not read SymKey CKA_ID attribute\n"); return rv; } return rv; } /* * Generate a Symmetric Key */ PK11SymKey * GenerateSYMKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism, int keySize, SECItem *keyID, secuPWData *pwdata) { SECStatus rv; PK11SymKey *key; if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); return NULL; } } /* Generate the symmetric key */ key = PK11_TokenKeyGen(slot, mechanism, NULL, keySize, keyID, PR_TRUE, pwdata); if (!key) { PR_fprintf(PR_STDERR, "Symmetric Key Generation Failed \n"); } return key; } /* * MacInit */ SECStatus MacInit(PK11Context *ctx) { SECStatus rv = PK11_DigestBegin(ctx); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestBegin()\n"); } return rv; } /* * MacUpdate */ SECStatus MacUpdate(PK11Context *ctx, unsigned char *msg, unsigned int msgLen) { SECStatus rv = PK11_DigestOp(ctx, msg, msgLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Compute MAC Failed : DigestOp()\n"); } return rv; } /* * Finalize MACing */ SECStatus MacFinal(PK11Context *ctx, unsigned char *mac, unsigned int *macLen, unsigned int maxLen) { SECStatus rv = PK11_DigestFinal(ctx, mac, macLen, maxLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestFinal()\n"); } return SECSuccess; } /* * Compute Mac */ SECStatus ComputeMac(PK11Context *ctxmac, unsigned char *ptext, unsigned int ptextLen, unsigned char *mac, unsigned int *macLen, unsigned int maxLen) { SECStatus rv = MacInit(ctxmac); if (rv != SECSuccess) return rv; rv = MacUpdate(ctxmac, ptext, ptextLen); if (rv != SECSuccess) return rv; rv = MacFinal(ctxmac, mac, macLen, maxLen); return rv; } /* * WriteToHeaderFile */ SECStatus WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, PRFileDesc *outFile) { SECStatus rv; char header[40]; char trailer[40]; char *outString = NULL; switch (type) { case SYMKEY: strcpy(header, ENCKEY_HEADER); strcpy(trailer, ENCKEY_TRAILER); break; case MACKEY: strcpy(header, MACKEY_HEADER); strcpy(trailer, MACKEY_TRAILER); break; case IV: strcpy(header, IV_HEADER); strcpy(trailer, IV_TRAILER); break; case MAC: strcpy(header, MAC_HEADER); strcpy(trailer, MAC_TRAILER); break; case PAD: strcpy(header, PAD_HEADER); strcpy(trailer, PAD_TRAILER); break; } PR_fprintf(outFile, "%s\n", header); PrintAsHex(outFile, buf, len); PR_fprintf(outFile, "%s\n\n", trailer); return SECSuccess; } /* * Initialize for encryption or decryption - common code */ PK11Context * CryptInit(PK11SymKey *key, unsigned char *iv, unsigned int ivLen, CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation) { SECItem ivItem = { siBuffer, iv, ivLen }; PK11Context *ctx = NULL; SECItem *secParam = PK11_ParamFromIV(CKM_AES_CBC, &ivItem); if (secParam == NULL) { PR_fprintf(PR_STDERR, "Crypt Failed : secParam NULL\n"); return NULL; } ctx = PK11_CreateContextBySymKey(CKM_AES_CBC, operation, key, secParam); if (ctx == NULL) { PR_fprintf(PR_STDERR, "Crypt Failed : can't create a context\n"); goto cleanup; } cleanup: if (secParam) { SECITEM_FreeItem(secParam, PR_TRUE); } return ctx; } /* * Common encryption and decryption code */ SECStatus Crypt(PK11Context *ctx, unsigned char *out, unsigned int *outLen, unsigned int maxOut, unsigned char *in, unsigned int inLen) { SECStatus rv; rv = PK11_CipherOp(ctx, out, outLen, maxOut, in, inLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Crypt Failed : PK11_CipherOp returned %d\n", rv); goto cleanup; } cleanup: if (rv != SECSuccess) { return rv; } return SECSuccess; } /* * Decrypt */ SECStatus Decrypt(PK11Context *ctx, unsigned char *out, unsigned int *outLen, unsigned int maxout, unsigned char *in, unsigned int inLen) { return Crypt(ctx, out, outLen, maxout, in, inLen); } /* * Encrypt */ SECStatus Encrypt(PK11Context* ctx, unsigned char *out, unsigned int *outLen, unsigned int maxout, unsigned char *in, unsigned int inLen) { return Crypt(ctx, out, outLen, maxout, in, inLen); } /* * EncryptInit */ PK11Context * EncryptInit(PK11SymKey *ek, unsigned char *iv, unsigned int ivLen, CK_MECHANISM_TYPE type) { return CryptInit(ek, iv, ivLen, type, CKA_ENCRYPT); } /* * DecryptInit */ PK11Context * DecryptInit(PK11SymKey *dk, unsigned char *iv, unsigned int ivLen, CK_MECHANISM_TYPE type) { return CryptInit(dk, iv, ivLen, type, CKA_DECRYPT); } /* * Read cryptographic parameters from the header file */ SECStatus ReadFromHeaderFile(const char *fileName, HeaderType type, SECItem *item, PRBool isHexData) { SECStatus rv; PRFileDesc* file; SECItem filedata; SECItem outbuf; unsigned char *nonbody; unsigned char *body; char header[40]; char trailer[40]; outbuf.type = siBuffer; file = PR_Open(fileName, PR_RDONLY, 0); if (!file) { PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); return SECFailure; } switch (type) { case SYMKEY: strcpy(header, ENCKEY_HEADER); strcpy(trailer, ENCKEY_TRAILER); break; case MACKEY: strcpy(header, MACKEY_HEADER); strcpy(trailer, MACKEY_TRAILER); break; case IV: strcpy(header, IV_HEADER); strcpy(trailer, IV_TRAILER); break; case MAC: strcpy(header, MAC_HEADER); strcpy(trailer, MAC_TRAILER); break; case PAD: strcpy(header, PAD_HEADER); strcpy(trailer, PAD_TRAILER); break; } rv = FileToItem(&filedata, file); nonbody = (char *)filedata.data; if (!nonbody) { 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(nonbody, header)) != NULL) { char *trail = NULL; nonbody = body; body = PORT_Strchr(body, '\n'); if (!body) body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */ if (body) trail = strstr(++body, trailer); if (trail != NULL) { *trail = '\0'; } else { PR_fprintf(PR_STDERR, "input has header but no trailer\n"); PORT_Free(filedata.data); return SECFailure; } } else { body = nonbody; } cleanup: PR_Close(file); HexToBuf(body, item, isHexData); return SECSuccess; } /* * EncryptAndMac */ SECStatus EncryptAndMac(PRFileDesc *inFile, PRFileDesc *headerFile, PRFileDesc *encFile, PK11SymKey *ek, PK11SymKey *mk, unsigned char *iv, unsigned int ivLen, PRBool ascii) { SECStatus rv; unsigned char ptext[BLOCKSIZE]; unsigned int ptextLen; unsigned char mac[DIGESTSIZE]; unsigned int macLen; unsigned int nwritten; unsigned char encbuf[BLOCKSIZE]; unsigned int encbufLen; SECItem noParams = { siBuffer, NULL, 0 }; PK11Context *ctxmac = NULL; PK11Context *ctxenc = NULL; unsigned int pad[1]; SECItem padItem; unsigned int paddingLength; static unsigned int firstTime = 1; int j; ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); if (ctxmac == NULL) { PR_fprintf(PR_STDERR, "Can't create MAC context\n"); rv = SECFailure; goto cleanup; } rv = MacInit(ctxmac); if (rv != SECSuccess) { goto cleanup; } ctxenc = EncryptInit(ek, iv, ivLen, CKM_AES_CBC); /* read a buffer of plaintext from input file */ while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { /* Encrypt using it using CBC, using previously created IV */ if (ptextLen != BLOCKSIZE) { paddingLength = BLOCKSIZE - ptextLen; for ( j=0; j < paddingLength; j++) { ptext[ptextLen+j] = (unsigned char)paddingLength; } ptextLen = BLOCKSIZE; } rv = Encrypt(ctxenc, encbuf, &encbufLen, sizeof(encbuf), ptext, ptextLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Encrypt Failure\n"); goto cleanup; } /* save the last block of ciphertext as the next IV */ iv = encbuf; ivLen = encbufLen; /* write the cipher text to intermediate file */ nwritten = PR_Write(encFile, encbuf, encbufLen); /*PR_Assert(nwritten == encbufLen);*/ rv = MacUpdate(ctxmac, ptext, ptextLen); } rv = MacFinal(ctxmac, mac, &macLen, DIGESTSIZE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "MacFinal Failure\n"); goto cleanup; } if (macLen == 0) { PR_fprintf(PR_STDERR, "Bad MAC length\n"); rv = SECFailure; goto cleanup; } WriteToHeaderFile(mac, macLen, MAC, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Write MAC Failure\n"); goto cleanup; } pad[0] = paddingLength; padItem.type = siBuffer; padItem.data = (unsigned char *)pad; padItem.len = sizeof(pad[0]); WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Write PAD Failure\n"); goto cleanup; } rv = SECSuccess; cleanup: if (ctxmac != NULL) { PK11_DestroyContext(ctxmac, PR_TRUE); } if (ctxenc != NULL) { PK11_DestroyContext(ctxenc, PR_TRUE); } return rv; } /* * Find the Key for the given mechanism */ PK11SymKey* FindKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism, SECItem *keyBuf, secuPWData *pwdata) { SECStatus rv; PK11SymKey *key; if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); if (slot) { PK11_FreeSlot(slot); } return NULL; } } key = PK11_FindFixedKey(slot, mechanism, keyBuf, 0); if (!key) { PR_fprintf(PR_STDERR, "PK11_FindFixedKey failed (err %d)\n", PR_GetError()); PK11_FreeSlot(slot); return NULL; } return key; } /* * Decrypt and Verify MAC */ SECStatus DecryptAndVerifyMac(const char* outFileName, char *encryptedFileName, SECItem *cItem, SECItem *macItem, PK11SymKey* ek, PK11SymKey* mk, SECItem *ivItem, SECItem *padItem) { SECStatus rv; PRFileDesc* inFile; PRFileDesc* outFile; unsigned char decbuf[64]; unsigned int decbufLen; unsigned char ptext[BLOCKSIZE]; unsigned int ptextLen = 0; unsigned char ctext[64]; unsigned int ctextLen; unsigned char newmac[DIGESTSIZE]; unsigned int newmacLen = 0; unsigned int newptextLen = 0; unsigned int count = 0; unsigned int temp = 0; unsigned int blockNumber = 0; SECItem noParams = { siBuffer, NULL, 0 }; PK11Context *ctxmac = NULL; PK11Context *ctxenc = NULL; unsigned char iv[BLOCKSIZE]; unsigned int ivLen = ivItem->len; unsigned int fileLength; unsigned int paddingLength; int j; memcpy(iv, ivItem->data, ivItem->len); paddingLength = (unsigned int)padItem->data[0]; ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); if (ctxmac == NULL) { PR_fprintf(PR_STDERR, "Can't create MAC context\n"); rv = SECFailure; goto cleanup; } /* Open the input file. */ inFile = PR_Open(encryptedFileName, PR_RDONLY , 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", encryptedFileName); return SECFailure; } /* Open the output file. */ outFile = PR_Open(outFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR , 00660); if (!outFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", outFileName); return SECFailure; } rv = MacInit(ctxmac); if (rv != SECSuccess) goto cleanup; ctxenc = DecryptInit(ek, iv, ivLen, CKM_AES_CBC); fileLength = FileSize(encryptedFileName); while ((ctextLen = PR_Read(inFile, ctext, sizeof(ctext))) > 0) { count += ctextLen; /* decrypt cipher text buffer using CBC and IV */ rv = Decrypt(ctxenc, decbuf, &decbufLen, sizeof(decbuf), ctext, ctextLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Decrypt Failure\n"); goto cleanup; } if (decbufLen == 0) break; rv = MacUpdate(ctxmac, decbuf, decbufLen); if (rv != SECSuccess) { goto cleanup; } if (count == fileLength) { decbufLen = decbufLen-paddingLength; } /* write the plain text to out file */ temp = PR_Write(outFile, decbuf, decbufLen); if (temp != decbufLen) { PR_fprintf(PR_STDERR, "write error\n"); rv = SECFailure; break; } /* save last block of ciphertext */ memcpy(iv, decbuf, decbufLen); ivLen = decbufLen; blockNumber++; } if (rv != SECSuccess) { goto cleanup; } rv = MacFinal(ctxmac, newmac, &newmacLen, sizeof(newmac)); if (rv != SECSuccess) { goto cleanup; } if (PORT_Memcmp(macItem->data, newmac, newmacLen) == 0) { rv = SECSuccess; } else { PR_fprintf(PR_STDERR, "Check MAC : Failure\n"); PR_fprintf(PR_STDERR, "Extracted : "); PrintAsHex(PR_STDERR, macItem->data, macItem->len); PR_fprintf(PR_STDERR, "Computed : "); PrintAsHex(PR_STDERR, newmac, newmacLen); rv = SECFailure; } cleanup: if (ctxmac) { PK11_DestroyContext(ctxmac, PR_TRUE); } if (ctxenc) { PK11_DestroyContext(ctxenc, PR_TRUE); } if (outFile) { PR_Close(outFile); } return rv; } /* * Gets IV and CKAIDS From Header File */ SECStatus GetIVandCKAIDSFromHeader(const char *cipherFileName, SECItem *ivItem, SECItem *encKeyItem, SECItem *macKeyItem) { SECStatus rv; /* open intermediate file, read in header, get IV and CKA_IDs of two keys * from it */ rv = ReadFromHeaderFile(cipherFileName, IV, ivItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve IV from cipher file\n"); goto cleanup; } rv = ReadFromHeaderFile(cipherFileName, SYMKEY, encKeyItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve AES CKA_ID from cipher file\n"); goto cleanup; } rv = ReadFromHeaderFile(cipherFileName, MACKEY, macKeyItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve MAC CKA_ID from cipher file\n"); goto cleanup; } cleanup: return rv; } /* * DecryptFile */ SECStatus DecryptFile(PK11SlotInfo *slot, const char *dbdir, const char *outFileName, const char *headerFileName, char *encryptedFileName, secuPWData *pwdata, PRBool ascii) { /* * The DB is open read only and we have authenticated to it * open input file, read in header, get IV and CKA_IDs of two keys from it * find those keys in the DB token * Open output file * loop until EOF(input): * read a buffer of ciphertext from input file, * Save last block of ciphertext * decrypt ciphertext buffer using CBC and IV, * compute and check MAC, then remove MAC from plaintext * replace IV with saved last block of ciphertext * write the plain text to output file * close files * report success */ SECStatus rv; SECItem ivItem; SECItem encKeyItem; SECItem macKeyItem; SECItem cipherItem; SECItem macItem; SECItem padItem; PK11SymKey *encKey = NULL; PK11SymKey *macKey = NULL; /* open intermediate file, read in header, get IV and CKA_IDs of two keys * from it */ rv = GetIVandCKAIDSFromHeader(headerFileName, &ivItem, &encKeyItem, &macKeyItem); if (rv != SECSuccess) { goto cleanup; } /* find those keys in the DB token */ encKey = FindKey(slot, CKM_AES_CBC, &encKeyItem, pwdata); if (encKey == NULL) { PR_fprintf(PR_STDERR, "Can't find the encryption key\n"); rv = SECFailure; goto cleanup; } /* CKM_MD5_HMAC or CKM_EXTRACT_KEY_FROM_KEY */ macKey = FindKey(slot, CKM_MD5_HMAC, &macKeyItem, pwdata); if (macKey == NULL) { rv = SECFailure; goto cleanup; } /* Read in the Mac into item from the intermediate file */ rv = ReadFromHeaderFile(headerFileName, MAC, &macItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve MAC from cipher file\n"); goto cleanup; } if (macItem.data == NULL) { PR_fprintf(PR_STDERR, "MAC has NULL data\n"); rv = SECFailure; goto cleanup; } if (macItem.len == 0) { PR_fprintf(PR_STDERR, "MAC has data has 0 length\n"); /*rv = SECFailure; goto cleanup;*/ } rv = ReadFromHeaderFile(headerFileName, PAD, &padItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve PAD detail from header file\n"); goto cleanup; } if (rv == SECSuccess) { /* Decrypt and Remove Mac */ rv = DecryptAndVerifyMac(outFileName, encryptedFileName, &cipherItem, &macItem, encKey, macKey, &ivItem, &padItem); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed while decrypting and removing MAC\n"); } } cleanup: if (slot) { PK11_FreeSlot(slot); } if (encKey) { PK11_FreeSymKey(encKey); } if (macKey) { PK11_FreeSymKey(macKey); } return rv; } /* * EncryptFile */ SECStatus EncryptFile(PK11SlotInfo *slot, const char *dbdir, const char *inFileName, const char *headerFileName, const char *encryptedFileName, const char *noiseFileName, secuPWData *pwdata, PRBool ascii) { /* * The DB is open for read/write and we have authenticated to it. * generate a symmetric AES key as a token object. * generate a second key to use for MACing, also a token object. * get their CKA_IDs * generate a random value to use as IV for AES CBC * open an input file and an output file, * write a header to the output that identifies the two keys by * their CKA_IDs, May include original file name and length. * loop until EOF(input) * read a buffer of plaintext from input file, * MAC it, append the MAC to the plaintext * encrypt it using CBC, using previously created IV, * store the last block of ciphertext as the new IV, * write the cipher text to intermediate file * close files * report success */ SECStatus rv; PRFileDesc *inFile; PRFileDesc *headerFile; PRFileDesc *encFile; unsigned char *encKeyId = (unsigned char *) "Encrypt Key"; unsigned char *macKeyId = (unsigned char *) "MAC Key"; SECItem encKeyID = { siAsciiString, encKeyId, PL_strlen(encKeyId) }; SECItem macKeyID = { siAsciiString, macKeyId, PL_strlen(macKeyId) }; SECItem encCKAID; SECItem macCKAID; unsigned char iv[BLOCKSIZE]; SECItem ivItem; PK11SymKey *encKey = NULL; PK11SymKey *macKey = NULL; SECItem temp; unsigned char c; /* generate a symmetric AES key as a token object. */ encKey = GenerateSYMKey(slot, CKM_AES_KEY_GEN, 128/8, &encKeyID, pwdata); if (encKey == NULL) { PR_fprintf(PR_STDERR, "GenerateSYMKey for AES returned NULL.\n"); rv = SECFailure; goto cleanup; } /* generate a second key to use for MACing, also a token object. */ macKey = GenerateSYMKey(slot, CKM_GENERIC_SECRET_KEY_GEN, 160/8, &macKeyID, pwdata); if (macKey == NULL) { PR_fprintf(PR_STDERR, "GenerateSYMKey for MACing returned NULL.\n"); rv = SECFailure; goto cleanup; } /* get the encrypt key CKA_ID */ rv = GatherCKA_ID(encKey, &encCKAID); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Error while wrapping encrypt key\n"); goto cleanup; } /* get the MAC key CKA_ID */ rv = GatherCKA_ID(macKey, &macCKAID); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Can't get the MAC key CKA_ID.\n"); goto cleanup; } if (noiseFileName) { rv = SeedFromNoiseFile(noiseFileName); if (rv != SECSuccess) { PORT_SetError(PR_END_OF_FILE_ERROR); return SECFailure; } rv = PK11_GenerateRandom(iv, BLOCKSIZE); if (rv != SECSuccess) { goto cleanup; } } else { /* generate a random value to use as IV for AES CBC */ GenerateRandom(iv, BLOCKSIZE); } headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); if (!headerFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", headerFileName); return SECFailure; } encFile = PR_Open(encryptedFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); if (!encFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", encryptedFileName); return SECFailure; } /* write to a header file the IV and the CKA_IDs * identifying the two keys */ ivItem.type = siBuffer; ivItem.data = iv; ivItem.len = BLOCKSIZE; rv = WriteToHeaderFile(iv, BLOCKSIZE, IV, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Error writing IV to cipher file - %s\n", headerFileName); goto cleanup; } rv = WriteToHeaderFile(encCKAID.data, encCKAID.len, SYMKEY, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Error writing AES CKA_ID to cipher file - %s\n", encryptedFileName); goto cleanup; } rv = WriteToHeaderFile(macCKAID.data, macCKAID.len, MACKEY, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Error writing MAC CKA_ID to cipher file - %s\n", headerFileName); goto cleanup; } /* Open the input file. */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); return SECFailure; } /* Macing and Encryption */ if (rv == SECSuccess) { rv = EncryptAndMac(inFile, headerFile, encFile, encKey, macKey, ivItem.data, ivItem.len, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed : Macing and Encryption\n"); goto cleanup; } } cleanup: if (inFile) { PR_Close(inFile); } if (headerFile) { PR_Close(headerFile); } if (encFile) { PR_Close(encFile); } if (slot) { PK11_FreeSlot(slot); } if (encKey) { PK11_FreeSymKey(encKey); } if (macKey) { PK11_FreeSymKey(macKey); } return rv; } /* * This example illustrates basic encryption/decryption and MACing * Generates the encryption/mac keys and uses token for storing. * Encrypts the input file and appends MAC before storing in intermediate * header file. * Writes the CKA_IDs of the encryption keys into intermediate header file. * Reads the intermediate headerfile for CKA_IDs and encrypted * contents and decrypts into output file. */ int main(int argc, char **argv) { SECStatus rv; SECStatus rvShutdown; PK11SlotInfo *slot = NULL; PLOptState *optstate; PLOptStatus status; char headerFileName[50]; char encryptedFileName[50]; PRFileDesc *inFile; PRFileDesc *outFile; PRBool ascii = PR_FALSE; CommandType cmd = UNKNOWN; const char *command = NULL; const char *dbdir = NULL; const char *inFileName = NULL; const char *outFileName = NULL; const char *noiseFileName = NULL; secuPWData pwdata = { PW_NONE, 0 }; char * progName = strrchr(argv[0], '/'); progName = progName ? progName + 1 : argv[0]; /* Parse command line arguments */ optstate = PL_CreateOptState(argc, argv, "c:d:i:o:f:p:z:a"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case 'a': ascii = PR_TRUE; break; case 'c': command = strdup(optstate->value); break; case 'd': dbdir = strdup(optstate->value); break; case 'f': pwdata.source = PW_FROMFILE; pwdata.data = strdup(optstate->value); break; case 'p': pwdata.source = PW_PLAINTEXT; pwdata.data = strdup(optstate->value); break; case 'i': inFileName = strdup(optstate->value); break; case 'o': outFileName = strdup(optstate->value); break; case 'z': noiseFileName = strdup(optstate->value); break; default: Usage(progName); break; } } PL_DestroyOptState(optstate); if (!command || !dbdir || !inFileName || !outFileName) Usage(progName); if (PL_strlen(command)==0) Usage(progName); cmd = command[0] == 'a' ? ENCRYPT : command[0] == 'b' ? DECRYPT : UNKNOWN; /* Open the input file. */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); return SECFailure; } PR_Close(inFile); /* For intermediate header file, choose filename as inputfile name with extension ".header" */ strcpy(headerFileName, inFileName); strcat(headerFileName, ".header"); /* For intermediate encrypted file, choose filename as inputfile name with extension ".enc" */ strcpy(encryptedFileName, inFileName); strcat(encryptedFileName, ".enc"); PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); switch (cmd) { case ENCRYPT: /* If the intermediate header file already exists, delete it */ if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(headerFileName); } /* If the intermediate encrypted already exists, delete it */ if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(encryptedFileName); } /* Open DB for read/write and authenticate to it. */ rv = NSS_InitReadWrite(dbdir); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); goto cleanup; } PK11_SetPasswordFunc(GetModulePassword); slot = PK11_GetInternalKeySlot(); if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); goto cleanup; } } rv = EncryptFile(slot, dbdir, inFileName, headerFileName, encryptedFileName, noiseFileName, &pwdata, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "EncryptFile : Failed\n"); return SECFailure; } break; case DECRYPT: /* Open DB read only, authenticate to it */ PK11_SetPasswordFunc(GetModulePassword); rv = NSS_Init(dbdir); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "NSS_Init Failed\n"); return SECFailure; } slot = PK11_GetInternalKeySlot(); if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); goto cleanup; } } rv = DecryptFile(slot, dbdir, outFileName, headerFileName, encryptedFileName, &pwdata, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "DecryptFile : Failed\n"); return SECFailure; } break; } cleanup: rvShutdown = NSS_Shutdown(); if (rvShutdown != SECSuccess) { PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n"); rv = SECFailure; } PR_Cleanup(); return rv; }