1/*
2 * The Initial Developer of the Original Code is International
3 * Business Machines Corporation. Portions created by IBM
4 * Corporation are Copyright (C) 2005, 2006 International Business
5 * Machines Corporation. All Rights Reserved.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the Common Public License as published by
9 * IBM Corporation; either version 1 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * Common Public License for more details.
16 *
17 * You should have received a copy of the Common Public License
18 * along with this program; if not, a copy can be viewed at
19 * http://www.opensource.org/licenses/cpl1.0.php.
20 */
21
22#include "data_protect.h"
23#include "data_common.h"
24
25#include <tpm_pkcs11.h>
26#include <tpm_utils.h>
27
28#include <stdlib.h>
29#include <unistd.h>
30#define _GNU_SOURCE
31#include <getopt.h>
32#include <errno.h>
33
34
35/*
36 * Global variables
37 */
38int       g_bEncrypt     = TRUE;	// Encrypt/Decrypt operation specifier
39
40char     *g_pszInFile    = NULL;	// Input file name
41FILE     *g_pInFile      = NULL;	// Input file stream
42CK_BYTE  *g_pbInData     = NULL;	// Input file buffer
43
44char     *g_pszOutFile   = NULL;	// Output file name
45FILE     *g_pOutFile     = NULL;	// Output file stream
46CK_ULONG  g_ulOutBuffLen = 0;		// Output file buffer length
47CK_BYTE  *g_pbOutData    = NULL;	// Output file buffer
48CK_ULONG  g_ulOutDataLen = 0;		// Length of data contained in output buffer
49
50char     *g_pszToken     = NULL;	// Token label to be used
51
52/*
53 * parseCallback
54 *   Process the command specific options.
55 */
56int
57parseCallback( int         a_iOpt,
58               const char *a_pszOptArg ) {
59
60	switch ( a_iOpt ) {
61		case 'd':
62			g_bEncrypt = FALSE;
63			break;
64
65		case 'e':
66			g_bEncrypt = TRUE;
67			break;
68
69		case 'i':
70			if ( !a_pszOptArg )
71				return -1;
72
73			g_pszInFile = strdup( a_pszOptArg );
74			break;
75
76		// Use the specified token label when finding the token
77		case 'k':
78			if ( !a_pszOptArg )
79				return -1;
80
81			g_pszToken = strdup( a_pszOptArg );
82			break;
83
84		case 'o':
85			if ( !a_pszOptArg )
86				return -1;
87
88			g_pszOutFile = strdup( a_pszOptArg );
89			break;
90	}
91
92	return 0;
93}
94
95/*
96 * usageCallback
97 *   Display command usage information.
98 */
99void
100usageCallback( const char *a_szCmd ) {
101
102	logCmdHelp( a_szCmd );
103	logCmdOption( "-d, --decrypt",
104			_("Decrypt the input data") );
105	logCmdOption( "-e, --encrypt",
106			_("Encrypt the input data (default)") );
107	logCmdOption( "-i, --infile FILE",
108			_("Use FILE as the input to the specified operation") );
109	logCmdOption( "-k, --token STRING",
110			_("Use STRING to identify the label of the PKCS#11 token to be used") );
111	logCmdOption( "-o, --outfile FILE",
112			_("Use FILE as the output of the specified operation") );
113}
114
115/*
116 * parseCmd
117 *   Parse the command line options.
118 */
119int
120parseCmd( int    a_iArgc,
121          char **a_szArgv ) {
122
123	int  rc;
124
125	char          *szShortOpts = "dei:k:o:";
126	struct option  stLongOpts[]  = {
127			{ "decrypt", no_argument, NULL, 'd' },
128			{ "encrypt", no_argument, NULL, 'e' },
129			{ "infile", required_argument, NULL, 'i' },
130			{ "token", required_argument, NULL, 'k' },
131			{ "outfile", required_argument, NULL, 'o' },
132		};
133	int  iNumLongOpts = sizeof( stLongOpts ) / sizeof( struct option );
134
135	rc = genericOptHandler( a_iArgc, a_szArgv,
136		szShortOpts, stLongOpts, iNumLongOpts,
137		parseCallback, usageCallback );
138	if ( rc == -1 )
139		return -1;
140
141	// Make sure "-i" is specified until stdin support is added
142	if ( !g_pszInFile ) {
143		logMsg( TOKEN_INPUT_FILE_ERROR );
144		rc = -1;
145	}
146
147	// Make sure "-o" is specified until stdout support is added
148	if ( !g_pszOutFile ) {
149		logMsg( TOKEN_OUTPUT_FILE_ERROR );
150		rc = -1;
151	}
152
153	if ( rc == -1 ) {
154		usageCallback( a_szArgv[ 0 ] );
155		return -1;
156	}
157
158	return 0;
159}
160
161/*
162 * makeKey
163 *   Make the 256-bit AES symmetric key used to encrypt
164 *   or decrypt the input data.
165 */
166int
167makeKey( CK_SESSION_HANDLE  a_hSession ) {
168
169	int  rc = -1;
170
171	// Generate a 256-bit AES key
172	CK_RV             rv;
173	CK_BBOOL          bTrue       = TRUE;
174	CK_BBOOL          bFalse      = FALSE;
175	CK_OBJECT_CLASS   tKeyClass   = CKO_SECRET_KEY;
176	CK_KEY_TYPE       tKeyType    = CKK_AES;
177	CK_ULONG          ulKeyLen    = 32;
178	CK_MECHANISM      tMechanism  = { CKM_AES_KEY_GEN, NULL, 0 };
179	CK_ATTRIBUTE      tAttr[]     = {
180			{ CKA_CLASS, &tKeyClass, sizeof( tKeyClass ) },
181			{ CKA_TOKEN, &bTrue, sizeof( bTrue ) },
182			{ CKA_PRIVATE, &bTrue, sizeof( bTrue ) },
183			{ CKA_MODIFIABLE, &bFalse, sizeof( bFalse ) },
184			{ CKA_LABEL, TOKEN_PROTECT_KEY_LABEL, strlen( TOKEN_PROTECT_KEY_LABEL ) },
185			{ CKA_KEY_TYPE, &tKeyType, sizeof( tKeyType ) },
186			{ CKA_SENSITIVE, &bTrue, sizeof( bTrue ) },
187			{ CKA_ENCRYPT, &bTrue, sizeof( bTrue ) },
188			{ CKA_DECRYPT, &bTrue, sizeof( bTrue ) },
189			{ CKA_SIGN, &bFalse, sizeof( bFalse ) },
190			{ CKA_VERIFY, &bFalse, sizeof( bFalse ) },
191			{ CKA_WRAP, &bTrue, sizeof( bTrue ) },
192			{ CKA_UNWRAP, &bTrue, sizeof( bTrue ) },
193			{ CKA_EXTRACTABLE, &bFalse, sizeof( bFalse ) },
194			{ CKA_VALUE_LEN, &ulKeyLen, sizeof( ulKeyLen ) },
195		};
196	CK_ULONG          ulAttrCount = sizeof( tAttr ) / sizeof( CK_ATTRIBUTE );
197	CK_OBJECT_HANDLE  hObject;
198
199	// Generate the key on the token
200	rv = generateKey( a_hSession, &tMechanism, tAttr, ulAttrCount, &hObject );
201	if ( rv != CKR_OK )
202		goto out;
203
204	rc = 0;
205out:
206	return rc;
207}
208
209/*
210 * getKey
211 *   Get the symmetric key used for encryption or decryption.
212 */
213int
214getKey( CK_SESSION_HANDLE  a_hSession,
215        CK_OBJECT_HANDLE  *a_phObject ) {
216
217	int  rc = -1;
218
219	CK_RV             rv;
220	CK_BBOOL          bTrue       = TRUE;
221	CK_OBJECT_CLASS   tKeyClass   = CKO_SECRET_KEY;
222	CK_ATTRIBUTE      tAttr[]     = {
223			{ CKA_CLASS, &tKeyClass, sizeof( tKeyClass ) },
224			{ CKA_TOKEN, &bTrue, sizeof( bTrue ) },
225			{ CKA_LABEL, TOKEN_PROTECT_KEY_LABEL, strlen( TOKEN_PROTECT_KEY_LABEL ) },
226		};
227	CK_ULONG          ulAttrCount = sizeof( tAttr ) / sizeof( CK_ATTRIBUTE );
228	CK_OBJECT_HANDLE *phObjList   = NULL;
229	CK_ULONG          ulObjCount  = 0;
230
231	*a_phObject = 0;
232
233	// Search for the protection key
234	rv = findObjects( a_hSession, tAttr, ulAttrCount, &phObjList, &ulObjCount );
235	if ( rv != CKR_OK )
236		goto out;
237
238	if ( ulObjCount == 0 ) {
239		// Key doesn't exist, create it
240		if ( makeKey( a_hSession ) == -1 )
241			goto out;
242
243		// Search for the protection key again
244		rv = findObjects( a_hSession, tAttr, ulAttrCount, &phObjList, &ulObjCount );
245		if ( rv != CKR_OK )
246			goto out;
247
248	}
249
250	// Make sure we found it
251	if ( ulObjCount == 0 ) {
252		logError( TOKEN_NO_KEY_ERROR );
253		goto out;
254	}
255
256	// Return the handle to the key
257	*a_phObject = phObjList[ 0 ];
258
259	rc = 0;
260
261out:
262	return rc;
263}
264
265/*
266 * readData
267 *   Callback routine that reads the input data for the encryption
268 *   or decryption operation.  The operation (encryption or decryption)
269 *   determines some of the logic in this routine.
270 */
271int
272readData( CK_BYTE  **a_pbData,
273          CK_ULONG  *a_pulDataLen,
274          CK_BBOOL  *a_pbMoreData,
275          CK_BBOOL   a_bEncrypt ) {
276
277	CK_ULONG  iBytes;
278	CK_BBOOL  bMoreData = TRUE;
279
280	if ( !g_pInFile ) {
281		// Open the input file
282		errno = 0;
283		g_pInFile = fopen( g_pszInFile, "r" );
284		if ( !g_pInFile ) {
285			logError( TOKEN_FILE_OPEN_ERROR, g_pszInFile, strerror( errno ) );
286			return -1;
287		}
288
289		// Allocate an input buffer
290		g_pbInData = malloc( TOKEN_BUFFER_SIZE );
291		if ( !g_pbInData ) {
292			logError( TOKEN_MEMORY_ERROR );
293			return -1;
294		}
295	}
296
297	// Read the data
298	iBytes = fread( g_pbInData, 1, TOKEN_BUFFER_SIZE, g_pInFile );
299	if ( feof( g_pInFile ) ) {
300		fclose( g_pInFile );
301
302		// End of file encountered, indicate that there is
303		// no more data
304		bMoreData = FALSE;
305	}
306	else {
307		// Not end of file so make sure the buffer was filled
308		if ( iBytes != TOKEN_BUFFER_SIZE ) {
309			// Error encountered, terminate
310			fclose( g_pInFile );
311			return -1;
312		}
313	}
314
315	if ( !bMoreData && a_bEncrypt ) {
316		// No more data, so if we are encrypting then we MUST add padding
317		int  iCount   = iBytes - 1;
318		int  iPadding = TOKEN_AES_BLOCKSIZE - ( iBytes % TOKEN_AES_BLOCKSIZE );
319
320		iBytes += iPadding;
321
322		g_pbInData[iCount + iPadding] = iPadding;
323		iPadding--;
324		while ( iPadding > 0 ) {
325			g_pbInData[iCount + iPadding] = 0;
326			iPadding--;
327		}
328	}
329
330	*a_pbData = g_pbInData;
331	*a_pulDataLen = iBytes;
332	*a_pbMoreData = bMoreData;
333
334	return 0;
335}
336
337/*
338 * writeData
339 *   Callback routine that writes the output data for the encryption
340 *   or decryption operation.  The operation (encryption or decryption)
341 *   determines some of the logic in this routine.
342 */
343int
344writeData( CK_BYTE  *a_pbData,
345           CK_ULONG  a_ulDataLen,
346           CK_BBOOL  a_bMoreData,
347           CK_BBOOL  a_bEncrypt ) {
348
349	size_t tWriteCount;
350
351	if ( !g_pOutFile ) {
352		// Open the output file
353		errno = 0;
354		g_pOutFile = fopen( g_pszOutFile, "w" );
355		if ( !g_pOutFile ) {
356			logError( TOKEN_FILE_OPEN_ERROR, g_pszOutFile, strerror( errno ) );
357			return -1;
358		}
359	}
360
361	if ( !a_bMoreData ) {
362		// No more data so remove padding if we are decrypting
363		if ( !a_bEncrypt ) {
364			int      iPadding;
365
366			if ( a_ulDataLen == 0 ) {
367				// Remove padding from previous block is current
368				// block is zero length
369				if ( g_pbOutData ) {
370					iPadding = g_pbOutData[g_ulOutDataLen - 1];
371					g_ulOutDataLen -= iPadding;
372				}
373			}
374			else {
375				// Remove padding from current block
376				iPadding = a_pbData[a_ulDataLen - 1];
377				a_ulDataLen -= iPadding;
378			}
379		}
380	}
381
382	// Write the previous buffer if there is one
383	if ( g_pbOutData && ( g_ulOutDataLen > 0 ) )
384		tWriteCount = fwrite( g_pbOutData, 1, g_ulOutDataLen, g_pOutFile );
385
386	if ( a_bMoreData ) {
387		// Allocate a (new) buffer if necessary
388		if ( a_ulDataLen > g_ulOutBuffLen ) {
389			free( g_pbOutData );
390
391			g_ulOutBuffLen = a_ulDataLen;
392			g_pbOutData = malloc( g_ulOutBuffLen );
393			if ( !g_pbOutData ) {
394				logError( TOKEN_MEMORY_ERROR );
395				return -1;
396			}
397		}
398
399		// Copy the current data to the holding buffer
400		if ( a_ulDataLen > 0 )
401			memcpy( g_pbOutData, a_pbData, a_ulDataLen );
402		g_ulOutDataLen = a_ulDataLen;
403	}
404	else {
405		// No more data so write the last piece of data
406		if ( a_ulDataLen > 0 )
407			tWriteCount = fwrite( a_pbData, 1, a_ulDataLen, g_pOutFile );
408
409		fclose( g_pOutFile );
410	}
411
412	return 0;
413}
414
415int
416main( int    a_iArgc,
417      char **a_szArgv ) {
418
419	int  rc = 1;
420
421	char *pszPin = NULL;
422
423	CK_RV              rv;
424	CK_SESSION_HANDLE  hSession;
425	CK_OBJECT_HANDLE   hObject;
426	CK_MECHANISM       tMechanism = { CKM_AES_ECB, NULL, 0 };
427
428	// Set up i18n
429	initIntlSys( );
430
431	// Parse the command
432	if ( parseCmd( a_iArgc, a_szArgv ) == -1 )
433		goto out;
434
435	// Open the PKCS#11 TPM Token
436	rv = openToken( g_pszToken );
437	if ( rv != CKR_OK )
438		goto out;
439
440	// Make sure the token is initialized
441	if ( !isTokenInitialized( ) ) {
442		logMsg( TOKEN_NOT_INIT_ERROR );
443		goto out;
444	}
445
446	// Open a session
447	rv = openTokenSession( CKF_RW_SESSION, &hSession );
448	if ( rv != CKR_OK )
449		goto out;
450
451	pszPin = getPlainPasswd( TOKEN_USER_PIN_PROMPT, FALSE );
452	if ( !pszPin )
453		goto out;
454
455	// Login to the token
456	rv = loginToken( hSession, CKU_USER, pszPin );
457	if ( rv != CKR_OK )
458		goto out;
459
460	// Obtain the key
461	if ( getKey( hSession, &hObject ) == -1 )
462		goto out;
463
464	// Perform the operation
465	if ( g_bEncrypt )
466		rv = encryptData( hSession, hObject, &tMechanism,
467			readData, writeData );
468	else
469		rv = decryptData( hSession, hObject, &tMechanism,
470			readData, writeData );
471
472	if ( rv != CKR_OK )
473		goto out;
474
475	rc = 0;
476
477out:
478	shredPasswd( pszPin );
479
480	if ( hSession )
481		closeTokenSession( hSession );
482
483	closeToken( );
484
485	free( g_pszInFile );
486	free( g_pszOutFile );
487	free( g_pbInData );
488	free( g_pbOutData );
489
490	if ( rc == 0 )
491		logInfo( TOKEN_CMD_SUCCESS, a_szArgv[ 0 ] );
492	else
493		logInfo( TOKEN_CMD_FAILED, a_szArgv[ 0 ] );
494
495	return rc;
496}
497