cryptTool.c   [plain text]


/*
	File:		 cryptTool.c
	
	Description: simple encrypt/decrypt utility to demonstrate CDSA API
                 used for symmetric encryption

	Author:		dmitch

	Copyright: 	Copyright (c) 2001,2003,2005-2006 Apple Computer, Inc. All Rights Reserved.
	
	Disclaimer:	IMPORTANT:  This Apple software is supplied to you by Apple 
	            Computer, Inc. ("Apple") in consideration of your agreement to 
				the following terms, and your use, installation, modification 
				or redistribution of this Apple software constitutes acceptance 
				of these terms.  If you do not agree with these terms, please 
				do not use, install, modify or redistribute this Apple software.

				In consideration of your agreement to abide by the following 
				terms, and subject to these terms, Apple grants you a personal, 
				non-exclusive license, under Apple's copyrights in this 
				original Apple software (the "Apple Software"), to use, 
				reproduce, modify and redistribute the Apple Software, with 
				or without modifications, in source and/or binary forms; 
				provided that if you redistribute the Apple Software in 
				its entirety and without modifications, you must retain
				this notice and the following text and disclaimers in all 
				such redistributions of the Apple Software.  Neither the 
				name, trademarks, service marks or logos of Apple Computer, 
				Inc. may be used to endorse or promote products derived from the
				Apple Software without specific prior written permission from 
				Apple.  Except as expressly stated in this notice, no other 
				rights or licenses, express or implied, are granted by Apple 
				herein, including but not limited to any patent rights that
				may be infringed by your derivative works or by other works 
				in which the Apple Software may be incorporated.

				The Apple Software is provided by Apple on an "AS IS" basis.  
				APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING 
				WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
				MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, 
				REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE 
				OR IN COMBINATION WITH YOUR PRODUCTS.

				IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, 
				INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
				LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
				LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
				ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION 
				AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 
				AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING 
				NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE 
				HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "common.h"
#include <security_cdsa_utils/cuFileIo.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static void usage(char **argv) 
{
	printf("usage:\n");
	printf("  %s op password keySize inFile outFile [a=algorithm]\n", argv[0]);
	printf("  op:\n");
	printf("     e  encrypt\n");
	printf("     d  decrypt\n");
	printf("  algorithm:\n");
	printf("     4  RC4 (default if no algorithm specified)\n");
	printf("     c  ASC/ComCryption\n");
	printf("     d  DES\n");
	printf("     a  AES\n");
	exit(1);
}

/*
 * Derive symmetric key.
 */
static CSSM_RETURN ctDeriveKey(CSSM_CSP_HANDLE cspHand,
		uint32				keyAlg,			// CSSM_ALGID_RC5, etc.
		const char 			*keyLabel,
		unsigned 			keyLabelLen,
		uint32 				keyUsage,		// CSSM_KEYUSE_ENCRYPT, etc.
		uint32 				keySizeInBits,
		CSSM_DATA_PTR		password,		// in PKCS-5 lingo
		CSSM_DATA_PTR		salt,			// ditto
		uint32				iterationCnt,	// ditto
		CSSM_KEY_PTR		key)
{
	CSSM_RETURN					crtn;
	CSSM_CC_HANDLE 				ccHand;
	uint32						keyAttr;
	CSSM_DATA					dummyLabel;
	CSSM_PKCS5_PBKDF2_PARAMS 	pbeParams;
	CSSM_DATA					pbeData;
	CSSM_ACCESS_CREDENTIALS		creds;
	
	memset(key, 0, sizeof(CSSM_KEY));
	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
	crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
		CSSM_ALGID_PKCS5_PBKDF2,
		keyAlg,
		keySizeInBits,
		&creds,
		NULL,			// BaseKey
		iterationCnt,
		salt,
		NULL,			// seed
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateDeriveKeyContext", crtn);
		return crtn;
	}
	keyAttr = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF | 
			  CSSM_KEYATTR_SENSITIVE;
	dummyLabel.Length = keyLabelLen;
	dummyLabel.Data = (uint8 *)keyLabel;
	
	/* passing in password is pretty strange....*/
	pbeParams.Passphrase = *password;
	pbeParams.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
	pbeData.Data = (uint8 *)&pbeParams;
	pbeData.Length = sizeof(pbeParams);
	crtn = CSSM_DeriveKey(ccHand,
		&pbeData,
		keyUsage,
		keyAttr,
		&dummyLabel,
		NULL,			// cred and acl
		key);
	if(crtn) {
		printError("CSSM_DeriveKey", crtn);
		return crtn;
	}
	crtn = CSSM_DeleteContext(ccHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
	}
	return crtn;
}


int main(int argc, char **argv)
{
	int						rtn;
	uint32					keySizeInBytes;		// from cmd line
	char					*password;			// ASCII password from cmd line
	char					*inFileName;		// from cmd line
	unsigned char 			*inFile;			// raw infile data
	unsigned				inFileSize;			// in bytes
	char					*outFileName;		// from cmd line
	CSSM_CSP_HANDLE			cspHand;
	CSSM_RETURN				crtn;
	int						doEncrypt = 0;
	CSSM_DATA				passwordData;
	CSSM_DATA				saltData = {8, (uint8 *)"someSalt"};
	CSSM_DATA				inData;				// data to encrypt/decrypt, from inFile
	CSSM_DATA				outData = {0, NULL};// result data, written to outFile
	CSSM_CC_HANDLE			ccHand;				// crypto context
	CSSM_DATA				remData = {0, NULL};
	CSSM_SIZE				bytesProcessed;
	CSSM_KEY				symKey;
	char					algSpec = '4';
	CSSM_ALGORITHMS			keyAlg = 0;
	CSSM_ALGORITHMS			encrAlg = 0;
	CSSM_ENCRYPT_MODE		encrMode = 0;
	CSSM_PADDING			padding = 0;
	/* max of 16 bytes of init vector for the algs we use */
	CSSM_DATA				initVect = {16, (uint8 *)"someStrangeInitVector"};
	CSSM_DATA_PTR			initVectPtr = NULL;
	
	if(argc < 6) {
		usage(argv);
	}
	
	/* gather up cmd line args */
	switch(argv[1][0]) {
		case 'e':
			doEncrypt = 1;
			break;
		case 'd':
			doEncrypt = 0;
			break;
		default:
			usage(argv);
	}
	password = argv[2];
	passwordData.Data = (uint8 *)password;
	passwordData.Length = strlen(password);
	keySizeInBytes = atoi(argv[3]);
	if(keySizeInBytes == 0) {
		printf("keySize of 0 illegal\n");
		exit(1);
	}
	inFileName = argv[4];
	outFileName = argv[5];

	/* optional algorithm specifier */
	if(argc == 7) {
		if(argv[6][0] != 'a') {
			usage(argv);
		}
		algSpec = argv[6][2];
	}
	
	/* algorithm-specific parameters */
	switch(algSpec) {
		case '4':
			/* RC4 stream cipher - no padding, no IV, variable key size */
			keyAlg   = CSSM_ALGID_RC4;
			encrAlg  = CSSM_ALGID_RC4;
			encrMode = CSSM_ALGMODE_NONE;
			padding  = CSSM_PADDING_NONE;
			break;
		case 'c':
			/* ComCryption stream cipher - no padding, no IV, variable key size */
			keyAlg   = CSSM_ALGID_ASC;
			encrAlg  = CSSM_ALGID_ASC;
			encrMode = CSSM_ALGMODE_NONE;
			padding  = CSSM_PADDING_NONE;
			break;
		case 'd':
			/* DES block cipher, block size = 8 bytes, fixed key size */
			if(keySizeInBytes != 8) {
				printf("***DES must have key size of 8 bytes\n");
				exit(1);
			}
			keyAlg   = CSSM_ALGID_DES;
			encrAlg  = CSSM_ALGID_DES;
			encrMode = CSSM_ALGMODE_CBCPadIV8;
			padding  = CSSM_PADDING_PKCS7;
			initVect.Length = 8;
			initVectPtr = &initVect;
			break;
		case 'a':
			/* AES block cipher, block size = 16 bytes, fixed key size */
			if(keySizeInBytes != 16) {
				printf("***AES must have key size of 8 bytes\n");
				exit(1);
			}
			keyAlg   = CSSM_ALGID_AES;
			encrAlg  = CSSM_ALGID_AES;
			encrMode = CSSM_ALGMODE_CBCPadIV8;
			padding  = CSSM_PADDING_PKCS7;
			initVect.Length = 16;
			initVectPtr = &initVect;
			break;
		default:
			usage(argv);
	}

	/* read inFile from disk */
	rtn = readFile(inFileName, &inFile, &inFileSize);
	if(rtn) {
		printf("Error reading %s: %s\n", inFileName, strerror(rtn));
		exit(1);
	}
	inData.Data = inFile;
	inData.Length = inFileSize;
	
	/* attach to CSP */
	cspHand = cspStartup();
	if(cspHand == 0) {
		exit(1);
	}

	/*
	 * Derive an actual encryption/decryption key from the password ASCII text. 
	 * We could use the ASCII text directly as key material but using the DeriveKey
	 * function is much more secure (besides being an industry-standard way to 
	 * convert an ASCII password into binary key material). 
	 */
	crtn = ctDeriveKey(cspHand,
		keyAlg,
		"someLabel",		// keyLabel, not important
		9,					// keyLabelLen
		doEncrypt ? CSSM_KEYUSE_ENCRYPT : CSSM_KEYUSE_DECRYPT,
		keySizeInBytes * 8,	// keySizeInBits,
		&passwordData,
		&saltData,
		1000,				// iterCount, 1000 is the minimum
		&symKey);
	if(crtn) {
		exit(1);
	}
	
	/*
	 * Cook up a symmetric encrypt/decrypt context using the key we just derived
	 */
	crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
		encrAlg,				// encryption algorithm
		encrMode,				// mode
		NULL,					// access cred
		&symKey,
		initVectPtr,			// InitVector
		padding,				// Padding
		NULL,					// Params
		&ccHand);
	if(crtn) {
		printError("CSSM_CSP_CreateSymmetricContext", crtn);
		exit(1);
	}

	/* 
	 * Do the encrypt/decrypt.
	 * We do this with the init/update/final sequence only to demonstrate its
	 * usage.
	 */
	if(doEncrypt) {
		crtn = CSSM_EncryptDataInit(ccHand);
		if(crtn) {
			printError("CSSM_EncryptDataInit", crtn);
			exit(1);
		}
		
		/* this step can be performed an arbitrary number of times, with
		 * the appropriate housekeeping of inData and outData */
		crtn = CSSM_EncryptDataUpdate(ccHand,
			&inData,
			1,
			&outData,
			1,
			&bytesProcessed);
		if(crtn) {
			printError("CSSM_EncryptDataUpdate", crtn);
			exit(1);
		}
		outData.Length = bytesProcessed;
		
		/* one call more to clean up */
		crtn = CSSM_EncryptDataFinal(ccHand, &remData);
		if(crtn) {
			printError("CSSM_EncryptDataFinal", crtn);
			exit(1);
		}
		if(remData.Length != 0) {
			/* append remaining data to outData */
			uint32 newLen = outData.Length + remData.Length;
			outData.Data = (uint8 *)appRealloc(outData.Data,
				newLen,
				NULL);
			memmove(outData.Data + outData.Length, remData.Data, remData.Length);
			outData.Length = newLen;
			appFree(remData.Data, NULL);
		}
	}
	else {
		crtn = CSSM_DecryptDataInit(ccHand);
		if(crtn) {
			printError("CSSM_DecryptDataInit", crtn);
			exit(1);
		}
		
		/* this step can be performed an arbitrary number of times, with
		 * the appropriate housekeeping of inData and outData */
		crtn = CSSM_DecryptDataUpdate(ccHand,
			&inData,
			1,
			&outData,
			1,
			&bytesProcessed);
		if(crtn) {
			printError("CSSM_DecryptDataUpdate", crtn);
			exit(1);
		}
		outData.Length = bytesProcessed;
		
		/* one call more to clean up */
		crtn = CSSM_DecryptDataFinal(ccHand, &remData);
		if(crtn) {
			printError("CSSM_DecryptDataFinal", crtn);
			exit(1);
		}
		if(remData.Length != 0) {
			/* append remaining data to outData */
			uint32 newLen = outData.Length + remData.Length;
			outData.Data = (uint8 *)appRealloc(outData.Data,
				newLen,
				NULL);
			memmove(outData.Data + outData.Length, remData.Data, remData.Length);
			outData.Length = newLen;
			appFree(remData.Data, NULL);
		}
	}
	if(crtn == CSSM_OK) {
		rtn = writeFile(outFileName, outData.Data, outData.Length);
		if(rtn) {
			printf("Error writing %s: %s\n", outFileName, strerror(rtn));
			exit(1);
		}
		else {
			printf("SUCCESS: inFile length %u bytes, outFile length %u bytes\n",
				inFileSize, (unsigned)outData.Length);
		}
	}
	/* free resources */
	crtn = CSSM_DeleteContext(ccHand);
	if(crtn) {
		printError("CSSM_DeleteContext", crtn);
	}
	crtn = CSSM_FreeKey(cspHand, 
		NULL,			// access cred
		&symKey,	
		CSSM_FALSE);	// don't delete since it wasn't permanent
	if(crtn) {
		printError("CSSM_FreeKey", crtn);
	}
	free(inFile);		// mallocd by readFile() 
	
	/* this was mallocd by CSP */
	appFree(outData.Data, NULL);
	CSSM_ModuleDetach(cspHand);
	return rtn;
}