index.rst (40973B)
1 .. _mozilla_projects_nss_encrypt_decrypt_mac_using_token: 2 3 Encrypt and decrypt MAC using token 4 =================================== 5 6 .. _nss_sample_code_3_encryptiondecryption_and_mac_using_token_object.: 7 8 `NSS sample code 3: encryption/decryption and MAC using token object. <#nss_sample_code_3_encryptiondecryption_and_mac_using_token_object.>`__ 9 ---------------------------------------------------------------------------------------------------------------------------------------------- 10 11 .. container:: 12 13 Generates encryption/mac keys and uses token for storing. 14 15 .. code:: c 16 17 /* This Source Code Form is subject to the terms of the Mozilla Public 18 * License, v. 2.0. If a copy of the MPL was not distributed with this 19 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 20 21 /* NSPR Headers */ 22 #include 23 #include 24 #include 25 #include 26 #include 27 #include 28 #include 29 30 /* NSS headers */ 31 #include 32 #include 33 34 /* our samples utilities */ 35 #include "util.h" 36 37 #define BUFFERSIZE 80 38 #define DIGESTSIZE 16 39 #define PTEXT_MAC_BUFFER_SIZE 96 40 #define CIPHERSIZE 96 41 #define BLOCKSIZE 32 42 43 #define CIPHER_HEADER "-----BEGIN CIPHER-----" 44 #define CIPHER_TRAILER "-----END CIPHER-----" 45 #define ENCKEY_HEADER "-----BEGIN AESKEY CKAID-----" 46 #define ENCKEY_TRAILER "-----END AESKEY CKAID-----" 47 #define MACKEY_HEADER "-----BEGIN MACKEY CKAID-----" 48 #define MACKEY_TRAILER "-----END MACKEY CKAID-----" 49 #define IV_HEADER "-----BEGIN IV-----" 50 #define IV_TRAILER "-----END IV-----" 51 #define MAC_HEADER "-----BEGIN MAC-----" 52 #define MAC_TRAILER "-----END MAC-----" 53 #define PAD_HEADER "-----BEGIN PAD-----" 54 #define PAD_TRAILER "-----END PAD-----" 55 56 typedef enum { 57 ENCRYPT, 58 DECRYPT, 59 UNKNOWN 60 } CommandType; 61 62 typedef enum { 63 SYMKEY = 0, 64 MACKEY = 1, 65 IV = 2, 66 MAC = 3, 67 PAD = 4 68 } HeaderType; 69 70 71 /* 72 * Print usage message and exit 73 */ 74 static void 75 Usage(const char *progName) 76 { 77 fprintf(stderr, "\nUsage: %s -c -d [-z ] " 78 "[-p | -f ] -i -o \n\n", 79 progName); 80 fprintf(stderr, "%-20s Specify 'a' for encrypt operation\n\n", 81 "-c "); 82 fprintf(stderr, "%-20s Specify 'b' for decrypt operation\n\n", 83 " "); 84 fprintf(stderr, "%-20s Specify db directory path\n\n", 85 "-d "); 86 fprintf(stderr, "%-20s Specify db password [optional]\n\n", 87 "-p "); 88 fprintf(stderr, "%-20s Specify db password file [optional]\n\n", 89 "-f "); 90 fprintf(stderr, "%-20s Specify noise file name [optional]\n\n", 91 "-z "); 92 fprintf(stderr, "%-21s Specify an input file name\n\n", 93 "-i "); 94 fprintf(stderr, "%-21s Specify an output file name\n\n", 95 "-o "); 96 fprintf(stderr, "%-7s For encrypt, it takes as an input file and produces\n", 97 "Note :"); 98 fprintf(stderr, "%-7s .enc and .header as intermediate output files.\n\n", 99 ""); 100 fprintf(stderr, "%-7s For decrypt, it takes .enc and .header\n", 101 ""); 102 fprintf(stderr, "%-7s as input files and produces as a final output file.\n\n", 103 ""); 104 exit(-1); 105 } 106 107 /* 108 * Gather a CKA_ID 109 */ 110 SECStatus 111 GatherCKA_ID(PK11SymKey* key, SECItem* buf) 112 { 113 SECStatus rv = PK11_ReadRawAttribute(PK11_TypeSymKey, key, CKA_ID, buf); 114 if (rv != SECSuccess) { 115 PR_fprintf(PR_STDERR, "PK11_ReadRawAttribute returned (%d)\n", rv); 116 PR_fprintf(PR_STDERR, "Could not read SymKey CKA_ID attribute\n"); 117 return rv; 118 } 119 return rv; 120 } 121 122 /* 123 * Generate a Symmetric Key 124 */ 125 PK11SymKey * 126 GenerateSYMKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism, 127 int keySize, SECItem *keyID, secuPWData *pwdata) 128 { 129 SECStatus rv; 130 PK11SymKey *key; 131 132 if (PK11_NeedLogin(slot)) { 133 rv = PK11_Authenticate(slot, PR_TRUE, pwdata); 134 if (rv != SECSuccess) { 135 PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", 136 PK11_GetTokenName(slot)); 137 return NULL; 138 } 139 } 140 141 /* Generate the symmetric key */ 142 key = PK11_TokenKeyGen(slot, mechanism, 143 NULL, keySize, keyID, PR_TRUE, pwdata); 144 145 if (!key) { 146 PR_fprintf(PR_STDERR, "Symmetric Key Generation Failed \n"); 147 } 148 149 return key; 150 } 151 152 /* 153 * MacInit 154 */ 155 SECStatus 156 MacInit(PK11Context *ctx) 157 { 158 SECStatus rv = PK11_DigestBegin(ctx); 159 if (rv != SECSuccess) { 160 PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestBegin()\n"); 161 } 162 return rv; 163 } 164 165 /* 166 * MacUpdate 167 */ 168 SECStatus 169 MacUpdate(PK11Context *ctx, 170 unsigned char *msg, unsigned int msgLen) 171 { 172 SECStatus rv = PK11_DigestOp(ctx, msg, msgLen); 173 if (rv != SECSuccess) { 174 PR_fprintf(PR_STDERR, "Compute MAC Failed : DigestOp()\n"); 175 } 176 return rv; 177 } 178 179 /* 180 * Finalize MACing 181 */ 182 SECStatus 183 MacFinal(PK11Context *ctx, 184 unsigned char *mac, unsigned int *macLen, unsigned int maxLen) 185 { 186 SECStatus rv = PK11_DigestFinal(ctx, mac, macLen, maxLen); 187 if (rv != SECSuccess) { 188 PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestFinal()\n"); 189 } 190 return SECSuccess; 191 } 192 193 /* 194 * Compute Mac 195 */ 196 SECStatus 197 ComputeMac(PK11Context *ctxmac, 198 unsigned char *ptext, unsigned int ptextLen, 199 unsigned char *mac, unsigned int *macLen, 200 unsigned int maxLen) 201 { 202 SECStatus rv = MacInit(ctxmac); 203 if (rv != SECSuccess) return rv; 204 rv = MacUpdate(ctxmac, ptext, ptextLen); 205 if (rv != SECSuccess) return rv; 206 rv = MacFinal(ctxmac, mac, macLen, maxLen); 207 return rv; 208 } 209 210 /* 211 * WriteToHeaderFile 212 */ 213 SECStatus 214 WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, 215 PRFileDesc *outFile) 216 { 217 SECStatus rv; 218 char header[40]; 219 char trailer[40]; 220 char *outString = NULL; 221 222 switch (type) { 223 case SYMKEY: 224 strcpy(header, ENCKEY_HEADER); 225 strcpy(trailer, ENCKEY_TRAILER); 226 break; 227 case MACKEY: 228 strcpy(header, MACKEY_HEADER); 229 strcpy(trailer, MACKEY_TRAILER); 230 break; 231 case IV: 232 strcpy(header, IV_HEADER); 233 strcpy(trailer, IV_TRAILER); 234 break; 235 case MAC: 236 strcpy(header, MAC_HEADER); 237 strcpy(trailer, MAC_TRAILER); 238 break; 239 case PAD: 240 strcpy(header, PAD_HEADER); 241 strcpy(trailer, PAD_TRAILER); 242 break; 243 } 244 245 PR_fprintf(outFile, "%s\n", header); 246 PrintAsHex(outFile, buf, len); 247 PR_fprintf(outFile, "%s\n\n", trailer); 248 return SECSuccess; 249 } 250 251 /* 252 * Initialize for encryption or decryption - common code 253 */ 254 PK11Context * 255 CryptInit(PK11SymKey *key, 256 unsigned char *iv, unsigned int ivLen, 257 CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation) 258 { 259 SECItem ivItem = { siBuffer, iv, ivLen }; 260 PK11Context *ctx = NULL; 261 262 SECItem *secParam = PK11_ParamFromIV(CKM_AES_CBC, &ivItem); 263 if (secParam == NULL) { 264 PR_fprintf(PR_STDERR, "Crypt Failed : secParam NULL\n"); 265 return NULL; 266 } 267 ctx = PK11_CreateContextBySymKey(CKM_AES_CBC, operation, key, secParam); 268 if (ctx == NULL) { 269 PR_fprintf(PR_STDERR, "Crypt Failed : can't create a context\n"); 270 goto cleanup; 271 272 } 273 cleanup: 274 if (secParam) { 275 SECITEM_FreeItem(secParam, PR_TRUE); 276 } 277 return ctx; 278 } 279 280 /* 281 * Common encryption and decryption code 282 */ 283 SECStatus 284 Crypt(PK11Context *ctx, 285 unsigned char *out, unsigned int *outLen, unsigned int maxOut, 286 unsigned char *in, unsigned int inLen) 287 { 288 SECStatus rv; 289 290 rv = PK11_CipherOp(ctx, out, outLen, maxOut, in, inLen); 291 if (rv != SECSuccess) { 292 PR_fprintf(PR_STDERR, "Crypt Failed : PK11_CipherOp returned %d\n", rv); 293 goto cleanup; 294 } 295 296 cleanup: 297 if (rv != SECSuccess) { 298 return rv; 299 } 300 return SECSuccess; 301 } 302 303 /* 304 * Decrypt 305 */ 306 SECStatus 307 Decrypt(PK11Context *ctx, 308 unsigned char *out, unsigned int *outLen, unsigned int maxout, 309 unsigned char *in, unsigned int inLen) 310 { 311 return Crypt(ctx, out, outLen, maxout, in, inLen); 312 } 313 314 /* 315 * Encrypt 316 */ 317 SECStatus 318 Encrypt(PK11Context* ctx, 319 unsigned char *out, unsigned int *outLen, unsigned int maxout, 320 unsigned char *in, unsigned int inLen) 321 { 322 return Crypt(ctx, out, outLen, maxout, in, inLen); 323 } 324 325 /* 326 * EncryptInit 327 */ 328 PK11Context * 329 EncryptInit(PK11SymKey *ek, unsigned char *iv, unsigned int ivLen, 330 CK_MECHANISM_TYPE type) 331 { 332 return CryptInit(ek, iv, ivLen, type, CKA_ENCRYPT); 333 } 334 335 /* 336 * DecryptInit 337 */ 338 PK11Context * 339 DecryptInit(PK11SymKey *dk, unsigned char *iv, unsigned int ivLen, 340 CK_MECHANISM_TYPE type) 341 { 342 return CryptInit(dk, iv, ivLen, type, CKA_DECRYPT); 343 } 344 345 /* 346 * Read cryptographic parameters from the header file 347 */ 348 SECStatus 349 ReadFromHeaderFile(const char *fileName, HeaderType type, 350 SECItem *item, PRBool isHexData) 351 { 352 SECStatus rv; 353 PRFileDesc* file; 354 SECItem filedata; 355 SECItem outbuf; 356 unsigned char *nonbody; 357 unsigned char *body; 358 char header[40]; 359 char trailer[40]; 360 361 outbuf.type = siBuffer; 362 file = PR_Open(fileName, PR_RDONLY, 0); 363 if (!file) { 364 PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); 365 return SECFailure; 366 } 367 switch (type) { 368 case SYMKEY: 369 strcpy(header, ENCKEY_HEADER); 370 strcpy(trailer, ENCKEY_TRAILER); 371 break; 372 case MACKEY: 373 strcpy(header, MACKEY_HEADER); 374 strcpy(trailer, MACKEY_TRAILER); 375 break; 376 case IV: 377 strcpy(header, IV_HEADER); 378 strcpy(trailer, IV_TRAILER); 379 break; 380 case MAC: 381 strcpy(header, MAC_HEADER); 382 strcpy(trailer, MAC_TRAILER); 383 break; 384 case PAD: 385 strcpy(header, PAD_HEADER); 386 strcpy(trailer, PAD_TRAILER); 387 break; 388 } 389 390 rv = FileToItem(&filedata, file); 391 nonbody = (char *)filedata.data; 392 if (!nonbody) { 393 PR_fprintf(PR_STDERR, "unable to read data from input file\n"); 394 rv = SECFailure; 395 goto cleanup; 396 } 397 398 /* check for headers and trailers and remove them */ 399 if ((body = strstr(nonbody, header)) != NULL) { 400 char *trail = NULL; 401 nonbody = body; 402 body = PORT_Strchr(body, '\n'); 403 if (!body) 404 body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */ 405 if (body) 406 trail = strstr(++body, trailer); 407 if (trail != NULL) { 408 *trail = '\0'; 409 } else { 410 PR_fprintf(PR_STDERR, "input has header but no trailer\n"); 411 PORT_Free(filedata.data); 412 return SECFailure; 413 } 414 } else { 415 body = nonbody; 416 } 417 418 cleanup: 419 PR_Close(file); 420 HexToBuf(body, item, isHexData); 421 return SECSuccess; 422 } 423 424 /* 425 * EncryptAndMac 426 */ 427 SECStatus 428 EncryptAndMac(PRFileDesc *inFile, 429 PRFileDesc *headerFile, 430 PRFileDesc *encFile, 431 PK11SymKey *ek, 432 PK11SymKey *mk, 433 unsigned char *iv, unsigned int ivLen, 434 PRBool ascii) 435 { 436 SECStatus rv; 437 unsigned char ptext[BLOCKSIZE]; 438 unsigned int ptextLen; 439 unsigned char mac[DIGESTSIZE]; 440 unsigned int macLen; 441 unsigned int nwritten; 442 unsigned char encbuf[BLOCKSIZE]; 443 unsigned int encbufLen; 444 SECItem noParams = { siBuffer, NULL, 0 }; 445 PK11Context *ctxmac = NULL; 446 PK11Context *ctxenc = NULL; 447 unsigned int pad[1]; 448 SECItem padItem; 449 unsigned int paddingLength; 450 451 static unsigned int firstTime = 1; 452 int j; 453 454 ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); 455 if (ctxmac == NULL) { 456 PR_fprintf(PR_STDERR, "Can't create MAC context\n"); 457 rv = SECFailure; 458 goto cleanup; 459 } 460 rv = MacInit(ctxmac); 461 if (rv != SECSuccess) { 462 goto cleanup; 463 } 464 465 ctxenc = EncryptInit(ek, iv, ivLen, CKM_AES_CBC); 466 467 /* read a buffer of plaintext from input file */ 468 while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { 469 470 /* Encrypt using it using CBC, using previously created IV */ 471 if (ptextLen != BLOCKSIZE) { 472 paddingLength = BLOCKSIZE - ptextLen; 473 for ( j=0; j < paddingLength; j++) { 474 ptext[ptextLen+j] = (unsigned char)paddingLength; 475 } 476 ptextLen = BLOCKSIZE; 477 } 478 rv = Encrypt(ctxenc, 479 encbuf, &encbufLen, sizeof(encbuf), 480 ptext, ptextLen); 481 if (rv != SECSuccess) { 482 PR_fprintf(PR_STDERR, "Encrypt Failure\n"); 483 goto cleanup; 484 } 485 486 /* save the last block of ciphertext as the next IV */ 487 iv = encbuf; 488 ivLen = encbufLen; 489 490 /* write the cipher text to intermediate file */ 491 nwritten = PR_Write(encFile, encbuf, encbufLen); 492 /*PR_Assert(nwritten == encbufLen);*/ 493 494 rv = MacUpdate(ctxmac, ptext, ptextLen); 495 } 496 497 rv = MacFinal(ctxmac, mac, &macLen, DIGESTSIZE); 498 if (rv != SECSuccess) { 499 PR_fprintf(PR_STDERR, "MacFinal Failure\n"); 500 goto cleanup; 501 } 502 if (macLen == 0) { 503 PR_fprintf(PR_STDERR, "Bad MAC length\n"); 504 rv = SECFailure; 505 goto cleanup; 506 } 507 WriteToHeaderFile(mac, macLen, MAC, headerFile); 508 if (rv != SECSuccess) { 509 PR_fprintf(PR_STDERR, "Write MAC Failure\n"); 510 goto cleanup; 511 } 512 513 pad[0] = paddingLength; 514 padItem.type = siBuffer; 515 padItem.data = (unsigned char *)pad; 516 padItem.len = sizeof(pad[0]); 517 518 WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); 519 if (rv != SECSuccess) { 520 PR_fprintf(PR_STDERR, "Write PAD Failure\n"); 521 goto cleanup; 522 } 523 524 rv = SECSuccess; 525 526 cleanup: 527 if (ctxmac != NULL) { 528 PK11_DestroyContext(ctxmac, PR_TRUE); 529 } 530 if (ctxenc != NULL) { 531 PK11_DestroyContext(ctxenc, PR_TRUE); 532 } 533 534 return rv; 535 } 536 537 /* 538 * Find the Key for the given mechanism 539 */ 540 PK11SymKey* 541 FindKey(PK11SlotInfo *slot, 542 CK_MECHANISM_TYPE mechanism, 543 SECItem *keyBuf, secuPWData *pwdata) 544 { 545 SECStatus rv; 546 PK11SymKey *key; 547 548 if (PK11_NeedLogin(slot)) { 549 rv = PK11_Authenticate(slot, PR_TRUE, pwdata); 550 if (rv != SECSuccess) { 551 PR_fprintf(PR_STDERR, 552 "Could not authenticate to token %s.\n", 553 PK11_GetTokenName(slot)); 554 if (slot) { 555 PK11_FreeSlot(slot); 556 } 557 return NULL; 558 } 559 } 560 561 key = PK11_FindFixedKey(slot, mechanism, keyBuf, 0); 562 if (!key) { 563 PR_fprintf(PR_STDERR, 564 "PK11_FindFixedKey failed (err %d)\n", 565 PR_GetError()); 566 PK11_FreeSlot(slot); 567 return NULL; 568 } 569 return key; 570 } 571 572 /* 573 * Decrypt and Verify MAC 574 */ 575 SECStatus 576 DecryptAndVerifyMac(const char* outFileName, 577 char *encryptedFileName, 578 SECItem *cItem, SECItem *macItem, 579 PK11SymKey* ek, PK11SymKey* mk, SECItem *ivItem, SECItem *padItem) 580 { 581 SECStatus rv; 582 PRFileDesc* inFile; 583 PRFileDesc* outFile; 584 585 unsigned char decbuf[64]; 586 unsigned int decbufLen; 587 588 unsigned char ptext[BLOCKSIZE]; 589 unsigned int ptextLen = 0; 590 unsigned char ctext[64]; 591 unsigned int ctextLen; 592 unsigned char newmac[DIGESTSIZE]; 593 unsigned int newmacLen = 0; 594 unsigned int newptextLen = 0; 595 unsigned int count = 0; 596 unsigned int temp = 0; 597 unsigned int blockNumber = 0; 598 SECItem noParams = { siBuffer, NULL, 0 }; 599 PK11Context *ctxmac = NULL; 600 PK11Context *ctxenc = NULL; 601 602 unsigned char iv[BLOCKSIZE]; 603 unsigned int ivLen = ivItem->len; 604 unsigned int fileLength; 605 unsigned int paddingLength; 606 int j; 607 608 memcpy(iv, ivItem->data, ivItem->len); 609 paddingLength = (unsigned int)padItem->data[0]; 610 611 ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); 612 if (ctxmac == NULL) { 613 PR_fprintf(PR_STDERR, "Can't create MAC context\n"); 614 rv = SECFailure; 615 goto cleanup; 616 } 617 618 /* Open the input file. */ 619 inFile = PR_Open(encryptedFileName, PR_RDONLY , 0); 620 if (!inFile) { 621 PR_fprintf(PR_STDERR, 622 "Unable to open \"%s\" for writing.\n", 623 encryptedFileName); 624 return SECFailure; 625 } 626 /* Open the output file. */ 627 outFile = PR_Open(outFileName, 628 PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR , 00660); 629 if (!outFile) { 630 PR_fprintf(PR_STDERR, 631 "Unable to open \"%s\" for writing.\n", 632 outFileName); 633 return SECFailure; 634 } 635 636 rv = MacInit(ctxmac); 637 if (rv != SECSuccess) goto cleanup; 638 639 ctxenc = DecryptInit(ek, iv, ivLen, CKM_AES_CBC); 640 fileLength = FileSize(encryptedFileName); 641 642 while ((ctextLen = PR_Read(inFile, ctext, sizeof(ctext))) > 0) { 643 644 count += ctextLen; 645 646 /* decrypt cipher text buffer using CBC and IV */ 647 648 rv = Decrypt(ctxenc, decbuf, &decbufLen, sizeof(decbuf), 649 ctext, ctextLen); 650 651 if (rv != SECSuccess) { 652 PR_fprintf(PR_STDERR, "Decrypt Failure\n"); 653 goto cleanup; 654 } 655 656 if (decbufLen == 0) break; 657 658 rv = MacUpdate(ctxmac, decbuf, decbufLen); 659 if (rv != SECSuccess) { goto cleanup; } 660 if (count == fileLength) { 661 decbufLen = decbufLen-paddingLength; 662 } 663 664 /* write the plain text to out file */ 665 temp = PR_Write(outFile, decbuf, decbufLen); 666 if (temp != decbufLen) { 667 PR_fprintf(PR_STDERR, "write error\n"); 668 rv = SECFailure; 669 break; 670 } 671 672 /* save last block of ciphertext */ 673 memcpy(iv, decbuf, decbufLen); 674 ivLen = decbufLen; 675 blockNumber++; 676 } 677 678 if (rv != SECSuccess) { goto cleanup; } 679 680 rv = MacFinal(ctxmac, newmac, &newmacLen, sizeof(newmac)); 681 if (rv != SECSuccess) { goto cleanup; } 682 683 if (PORT_Memcmp(macItem->data, newmac, newmacLen) == 0) { 684 rv = SECSuccess; 685 } else { 686 PR_fprintf(PR_STDERR, "Check MAC : Failure\n"); 687 PR_fprintf(PR_STDERR, "Extracted : "); 688 PrintAsHex(PR_STDERR, macItem->data, macItem->len); 689 PR_fprintf(PR_STDERR, "Computed : "); 690 PrintAsHex(PR_STDERR, newmac, newmacLen); 691 rv = SECFailure; 692 } 693 cleanup: 694 if (ctxmac) { 695 PK11_DestroyContext(ctxmac, PR_TRUE); 696 } 697 if (ctxenc) { 698 PK11_DestroyContext(ctxenc, PR_TRUE); 699 } 700 if (outFile) { 701 PR_Close(outFile); 702 } 703 704 return rv; 705 } 706 707 /* 708 * Gets IV and CKAIDS From Header File 709 */ 710 SECStatus 711 GetIVandCKAIDSFromHeader(const char *cipherFileName, 712 SECItem *ivItem, SECItem *encKeyItem, SECItem *macKeyItem) 713 { 714 SECStatus rv; 715 716 /* open intermediate file, read in header, get IV and CKA_IDs of two keys 717 * from it 718 */ 719 rv = ReadFromHeaderFile(cipherFileName, IV, ivItem, PR_TRUE); 720 if (rv != SECSuccess) { 721 PR_fprintf(PR_STDERR, "Could not retrieve IV from cipher file\n"); 722 goto cleanup; 723 } 724 725 rv = ReadFromHeaderFile(cipherFileName, SYMKEY, encKeyItem, PR_TRUE); 726 if (rv != SECSuccess) { 727 PR_fprintf(PR_STDERR, 728 "Could not retrieve AES CKA_ID from cipher file\n"); 729 goto cleanup; 730 } 731 rv = ReadFromHeaderFile(cipherFileName, MACKEY, macKeyItem, PR_TRUE); 732 if (rv != SECSuccess) { 733 PR_fprintf(PR_STDERR, 734 "Could not retrieve MAC CKA_ID from cipher file\n"); 735 goto cleanup; 736 } 737 cleanup: 738 return rv; 739 } 740 741 /* 742 * DecryptFile 743 */ 744 SECStatus 745 DecryptFile(PK11SlotInfo *slot, 746 const char *dbdir, 747 const char *outFileName, 748 const char *headerFileName, 749 char *encryptedFileName, 750 secuPWData *pwdata, 751 PRBool ascii) 752 { 753 /* 754 * The DB is open read only and we have authenticated to it 755 * open input file, read in header, get IV and CKA_IDs of two keys from it 756 * find those keys in the DB token 757 * Open output file 758 * loop until EOF(input): 759 * read a buffer of ciphertext from input file, 760 * Save last block of ciphertext 761 * decrypt ciphertext buffer using CBC and IV, 762 * compute and check MAC, then remove MAC from plaintext 763 * replace IV with saved last block of ciphertext 764 * write the plain text to output file 765 * close files 766 * report success 767 */ 768 769 SECStatus rv; 770 SECItem ivItem; 771 SECItem encKeyItem; 772 SECItem macKeyItem; 773 SECItem cipherItem; 774 SECItem macItem; 775 SECItem padItem; 776 PK11SymKey *encKey = NULL; 777 PK11SymKey *macKey = NULL; 778 779 780 /* open intermediate file, read in header, get IV and CKA_IDs of two keys 781 * from it 782 */ 783 rv = GetIVandCKAIDSFromHeader(headerFileName, 784 &ivItem, &encKeyItem, &macKeyItem); 785 if (rv != SECSuccess) { 786 goto cleanup; 787 } 788 789 /* find those keys in the DB token */ 790 encKey = FindKey(slot, CKM_AES_CBC, &encKeyItem, pwdata); 791 if (encKey == NULL) { 792 PR_fprintf(PR_STDERR, "Can't find the encryption key\n"); 793 rv = SECFailure; 794 goto cleanup; 795 } 796 /* CKM_MD5_HMAC or CKM_EXTRACT_KEY_FROM_KEY */ 797 macKey = FindKey(slot, CKM_MD5_HMAC, &macKeyItem, pwdata); 798 if (macKey == NULL) { 799 rv = SECFailure; 800 goto cleanup; 801 } 802 803 /* Read in the Mac into item from the intermediate file */ 804 rv = ReadFromHeaderFile(headerFileName, MAC, &macItem, PR_TRUE); 805 if (rv != SECSuccess) { 806 PR_fprintf(PR_STDERR, 807 "Could not retrieve MAC from cipher file\n"); 808 goto cleanup; 809 } 810 if (macItem.data == NULL) { 811 PR_fprintf(PR_STDERR, "MAC has NULL data\n"); 812 rv = SECFailure; 813 goto cleanup; 814 } 815 if (macItem.len == 0) { 816 PR_fprintf(PR_STDERR, "MAC has data has 0 length\n"); 817 /*rv = SECFailure; 818 goto cleanup;*/ 819 } 820 821 rv = ReadFromHeaderFile(headerFileName, PAD, &padItem, PR_TRUE); 822 if (rv != SECSuccess) { 823 PR_fprintf(PR_STDERR, 824 "Could not retrieve PAD detail from header file\n"); 825 goto cleanup; 826 } 827 828 if (rv == SECSuccess) { 829 /* Decrypt and Remove Mac */ 830 rv = DecryptAndVerifyMac(outFileName, encryptedFileName, 831 &cipherItem, &macItem, encKey, macKey, &ivItem, &padItem); 832 if (rv != SECSuccess) { 833 PR_fprintf(PR_STDERR, "Failed while decrypting and removing MAC\n"); 834 } 835 } 836 837 cleanup: 838 if (slot) { 839 PK11_FreeSlot(slot); 840 } 841 if (encKey) { 842 PK11_FreeSymKey(encKey); 843 } 844 if (macKey) { 845 PK11_FreeSymKey(macKey); 846 } 847 848 return rv; 849 } 850 851 /* 852 * EncryptFile 853 */ 854 SECStatus 855 EncryptFile(PK11SlotInfo *slot, 856 const char *dbdir, 857 const char *inFileName, 858 const char *headerFileName, 859 const char *encryptedFileName, 860 const char *noiseFileName, 861 secuPWData *pwdata, 862 PRBool ascii) 863 { 864 /* 865 * The DB is open for read/write and we have authenticated to it. 866 * generate a symmetric AES key as a token object. 867 * generate a second key to use for MACing, also a token object. 868 * get their CKA_IDs 869 * generate a random value to use as IV for AES CBC 870 * open an input file and an output file, 871 * write a header to the output that identifies the two keys by 872 * their CKA_IDs, May include original file name and length. 873 * loop until EOF(input) 874 * read a buffer of plaintext from input file, 875 * MAC it, append the MAC to the plaintext 876 * encrypt it using CBC, using previously created IV, 877 * store the last block of ciphertext as the new IV, 878 * write the cipher text to intermediate file 879 * close files 880 * report success 881 */ 882 SECStatus rv; 883 PRFileDesc *inFile; 884 PRFileDesc *headerFile; 885 PRFileDesc *encFile; 886 887 unsigned char *encKeyId = (unsigned char *) "Encrypt Key"; 888 unsigned char *macKeyId = (unsigned char *) "MAC Key"; 889 SECItem encKeyID = { siAsciiString, encKeyId, PL_strlen(encKeyId) }; 890 SECItem macKeyID = { siAsciiString, macKeyId, PL_strlen(macKeyId) }; 891 892 SECItem encCKAID; 893 SECItem macCKAID; 894 unsigned char iv[BLOCKSIZE]; 895 SECItem ivItem; 896 PK11SymKey *encKey = NULL; 897 PK11SymKey *macKey = NULL; 898 SECItem temp; 899 unsigned char c; 900 901 /* generate a symmetric AES key as a token object. */ 902 encKey = GenerateSYMKey(slot, CKM_AES_KEY_GEN, 128/8, &encKeyID, pwdata); 903 if (encKey == NULL) { 904 PR_fprintf(PR_STDERR, "GenerateSYMKey for AES returned NULL.\n"); 905 rv = SECFailure; 906 goto cleanup; 907 } 908 909 /* generate a second key to use for MACing, also a token object. */ 910 macKey = GenerateSYMKey(slot, CKM_GENERIC_SECRET_KEY_GEN, 160/8, 911 &macKeyID, pwdata); 912 if (macKey == NULL) { 913 PR_fprintf(PR_STDERR, "GenerateSYMKey for MACing returned NULL.\n"); 914 rv = SECFailure; 915 goto cleanup; 916 } 917 918 /* get the encrypt key CKA_ID */ 919 rv = GatherCKA_ID(encKey, &encCKAID); 920 if (rv != SECSuccess) { 921 PR_fprintf(PR_STDERR, "Error while wrapping encrypt key\n"); 922 goto cleanup; 923 } 924 925 /* get the MAC key CKA_ID */ 926 rv = GatherCKA_ID(macKey, &macCKAID); 927 if (rv != SECSuccess) { 928 PR_fprintf(PR_STDERR, "Can't get the MAC key CKA_ID.\n"); 929 goto cleanup; 930 } 931 932 if (noiseFileName) { 933 rv = SeedFromNoiseFile(noiseFileName); 934 if (rv != SECSuccess) { 935 PORT_SetError(PR_END_OF_FILE_ERROR); 936 return SECFailure; 937 } 938 rv = PK11_GenerateRandom(iv, BLOCKSIZE); 939 if (rv != SECSuccess) { 940 goto cleanup; 941 } 942 943 } else { 944 /* generate a random value to use as IV for AES CBC */ 945 GenerateRandom(iv, BLOCKSIZE); 946 } 947 948 headerFile = PR_Open(headerFileName, 949 PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); 950 if (!headerFile) { 951 PR_fprintf(PR_STDERR, 952 "Unable to open \"%s\" for writing.\n", 953 headerFileName); 954 return SECFailure; 955 } 956 encFile = PR_Open(encryptedFileName, 957 PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); 958 if (!encFile) { 959 PR_fprintf(PR_STDERR, 960 "Unable to open \"%s\" for writing.\n", 961 encryptedFileName); 962 return SECFailure; 963 } 964 /* write to a header file the IV and the CKA_IDs 965 * identifying the two keys 966 */ 967 ivItem.type = siBuffer; 968 ivItem.data = iv; 969 ivItem.len = BLOCKSIZE; 970 971 rv = WriteToHeaderFile(iv, BLOCKSIZE, IV, headerFile); 972 if (rv != SECSuccess) { 973 PR_fprintf(PR_STDERR, "Error writing IV to cipher file - %s\n", 974 headerFileName); 975 goto cleanup; 976 } 977 978 rv = WriteToHeaderFile(encCKAID.data, encCKAID.len, SYMKEY, headerFile); 979 if (rv != SECSuccess) { 980 PR_fprintf(PR_STDERR, "Error writing AES CKA_ID to cipher file - %s\n", 981 encryptedFileName); 982 goto cleanup; 983 } 984 rv = WriteToHeaderFile(macCKAID.data, macCKAID.len, MACKEY, headerFile); 985 if (rv != SECSuccess) { 986 PR_fprintf(PR_STDERR, "Error writing MAC CKA_ID to cipher file - %s\n", 987 headerFileName); 988 goto cleanup; 989 } 990 991 /* Open the input file. */ 992 inFile = PR_Open(inFileName, PR_RDONLY, 0); 993 if (!inFile) { 994 PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", 995 inFileName); 996 return SECFailure; 997 } 998 999 /* Macing and Encryption */ 1000 if (rv == SECSuccess) { 1001 rv = EncryptAndMac(inFile, headerFile, encFile, 1002 encKey, macKey, ivItem.data, ivItem.len, ascii); 1003 if (rv != SECSuccess) { 1004 PR_fprintf(PR_STDERR, "Failed : Macing and Encryption\n"); 1005 goto cleanup; 1006 } 1007 } 1008 1009 cleanup: 1010 if (inFile) { 1011 PR_Close(inFile); 1012 } 1013 if (headerFile) { 1014 PR_Close(headerFile); 1015 } 1016 if (encFile) { 1017 PR_Close(encFile); 1018 } 1019 if (slot) { 1020 PK11_FreeSlot(slot); 1021 } 1022 if (encKey) { 1023 PK11_FreeSymKey(encKey); 1024 } 1025 if (macKey) { 1026 PK11_FreeSymKey(macKey); 1027 } 1028 1029 return rv; 1030 } 1031 1032 /* 1033 * This example illustrates basic encryption/decryption and MACing 1034 * Generates the encryption/mac keys and uses token for storing. 1035 * Encrypts the input file and appends MAC before storing in intermediate 1036 * header file. 1037 * Writes the CKA_IDs of the encryption keys into intermediate header file. 1038 * Reads the intermediate headerfile for CKA_IDs and encrypted 1039 * contents and decrypts into output file. 1040 */ 1041 int 1042 main(int argc, char **argv) 1043 { 1044 SECStatus rv; 1045 SECStatus rvShutdown; 1046 PK11SlotInfo *slot = NULL; 1047 PLOptState *optstate; 1048 PLOptStatus status; 1049 char headerFileName[50]; 1050 char encryptedFileName[50]; 1051 PRFileDesc *inFile; 1052 PRFileDesc *outFile; 1053 PRBool ascii = PR_FALSE; 1054 CommandType cmd = UNKNOWN; 1055 const char *command = NULL; 1056 const char *dbdir = NULL; 1057 const char *inFileName = NULL; 1058 const char *outFileName = NULL; 1059 const char *noiseFileName = NULL; 1060 secuPWData pwdata = { PW_NONE, 0 }; 1061 1062 char * progName = strrchr(argv[0], '/'); 1063 progName = progName ? progName + 1 : argv[0]; 1064 1065 /* Parse command line arguments */ 1066 optstate = PL_CreateOptState(argc, argv, "c:d:i:o:f:p:z:a"); 1067 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 1068 switch (optstate->option) { 1069 case 'a': 1070 ascii = PR_TRUE; 1071 break; 1072 case 'c': 1073 command = strdup(optstate->value); 1074 break; 1075 case 'd': 1076 dbdir = strdup(optstate->value); 1077 break; 1078 case 'f': 1079 pwdata.source = PW_FROMFILE; 1080 pwdata.data = strdup(optstate->value); 1081 break; 1082 case 'p': 1083 pwdata.source = PW_PLAINTEXT; 1084 pwdata.data = strdup(optstate->value); 1085 break; 1086 case 'i': 1087 inFileName = strdup(optstate->value); 1088 break; 1089 case 'o': 1090 outFileName = strdup(optstate->value); 1091 break; 1092 case 'z': 1093 noiseFileName = strdup(optstate->value); 1094 break; 1095 default: 1096 Usage(progName); 1097 break; 1098 } 1099 } 1100 PL_DestroyOptState(optstate); 1101 1102 if (!command || !dbdir || !inFileName || !outFileName) 1103 Usage(progName); 1104 if (PL_strlen(command)==0) 1105 Usage(progName); 1106 1107 cmd = command[0] == 'a' ? ENCRYPT : command[0] == 'b' ? DECRYPT : UNKNOWN; 1108 1109 /* Open the input file. */ 1110 inFile = PR_Open(inFileName, PR_RDONLY, 0); 1111 if (!inFile) { 1112 PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", 1113 inFileName); 1114 return SECFailure; 1115 } 1116 PR_Close(inFile); 1117 1118 /* For intermediate header file, choose filename as inputfile name 1119 with extension ".header" */ 1120 strcpy(headerFileName, inFileName); 1121 strcat(headerFileName, ".header"); 1122 1123 /* For intermediate encrypted file, choose filename as inputfile name 1124 with extension ".enc" */ 1125 strcpy(encryptedFileName, inFileName); 1126 strcat(encryptedFileName, ".enc"); 1127 1128 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 1129 1130 switch (cmd) { 1131 case ENCRYPT: 1132 /* If the intermediate header file already exists, delete it */ 1133 if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { 1134 PR_Delete(headerFileName); 1135 } 1136 /* If the intermediate encrypted already exists, delete it */ 1137 if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { 1138 PR_Delete(encryptedFileName); 1139 } 1140 1141 /* Open DB for read/write and authenticate to it. */ 1142 rv = NSS_InitReadWrite(dbdir); 1143 if (rv != SECSuccess) { 1144 PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); 1145 goto cleanup; 1146 } 1147 1148 PK11_SetPasswordFunc(GetModulePassword); 1149 slot = PK11_GetInternalKeySlot(); 1150 if (PK11_NeedLogin(slot)) { 1151 rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); 1152 if (rv != SECSuccess) { 1153 PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", 1154 PK11_GetTokenName(slot)); 1155 goto cleanup; 1156 } 1157 } 1158 rv = EncryptFile(slot, dbdir, 1159 inFileName, headerFileName, encryptedFileName, 1160 noiseFileName, &pwdata, ascii); 1161 if (rv != SECSuccess) { 1162 PR_fprintf(PR_STDERR, "EncryptFile : Failed\n"); 1163 return SECFailure; 1164 } 1165 break; 1166 case DECRYPT: 1167 /* Open DB read only, authenticate to it */ 1168 PK11_SetPasswordFunc(GetModulePassword); 1169 1170 rv = NSS_Init(dbdir); 1171 if (rv != SECSuccess) { 1172 PR_fprintf(PR_STDERR, "NSS_Init Failed\n"); 1173 return SECFailure; 1174 } 1175 1176 slot = PK11_GetInternalKeySlot(); 1177 if (PK11_NeedLogin(slot)) { 1178 rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); 1179 if (rv != SECSuccess) { 1180 PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", 1181 PK11_GetTokenName(slot)); 1182 goto cleanup; 1183 } 1184 } 1185 1186 rv = DecryptFile(slot, dbdir, 1187 outFileName, headerFileName, 1188 encryptedFileName, &pwdata, ascii); 1189 if (rv != SECSuccess) { 1190 PR_fprintf(PR_STDERR, "DecryptFile : Failed\n"); 1191 return SECFailure; 1192 } 1193 break; 1194 } 1195 1196 cleanup: 1197 rvShutdown = NSS_Shutdown(); 1198 if (rvShutdown != SECSuccess) { 1199 PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n"); 1200 rv = SECFailure; 1201 } 1202 1203 PR_Cleanup(); 1204 1205 return rv; 1206 }