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  */
38 int       g_bEncrypt     = TRUE;	// Encrypt/Decrypt operation specifier
39 
40 char     *g_pszInFile    = NULL;	// Input file name
41 FILE     *g_pInFile      = NULL;	// Input file stream
42 CK_BYTE  *g_pbInData     = NULL;	// Input file buffer
43 
44 char     *g_pszOutFile   = NULL;	// Output file name
45 FILE     *g_pOutFile     = NULL;	// Output file stream
46 CK_ULONG  g_ulOutBuffLen = 0;		// Output file buffer length
47 CK_BYTE  *g_pbOutData    = NULL;	// Output file buffer
48 CK_ULONG  g_ulOutDataLen = 0;		// Length of data contained in output buffer
49 
50 char     *g_pszToken     = NULL;	// Token label to be used
51 
52 /*
53  * parseCallback
54  *   Process the command specific options.
55  */
56 int
parseCallback(int a_iOpt,const char * a_pszOptArg)57 parseCallback( 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  */
99 void
usageCallback(const char * a_szCmd)100 usageCallback( 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  */
119 int
parseCmd(int a_iArgc,char ** a_szArgv)120 parseCmd( 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  */
166 int
makeKey(CK_SESSION_HANDLE a_hSession)167 makeKey( 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;
205 out:
206 	return rc;
207 }
208 
209 /*
210  * getKey
211  *   Get the symmetric key used for encryption or decryption.
212  */
213 int
getKey(CK_SESSION_HANDLE a_hSession,CK_OBJECT_HANDLE * a_phObject)214 getKey( 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 
261 out:
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  */
271 int
readData(CK_BYTE ** a_pbData,CK_ULONG * a_pulDataLen,CK_BBOOL * a_pbMoreData,CK_BBOOL a_bEncrypt)272 readData( 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  */
343 int
writeData(CK_BYTE * a_pbData,CK_ULONG a_ulDataLen,CK_BBOOL a_bMoreData,CK_BBOOL a_bEncrypt)344 writeData( 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 ( tWriteCount != g_ulOutDataLen ) {
387 			logError(TOKEN_FILE_WRITE_ERROR, g_pOutFile, "short write");
388 			return -1;
389 		}
390 	}
391 
392 	if ( a_bMoreData ) {
393 		// Allocate a (new) buffer if necessary
394 		if ( a_ulDataLen > g_ulOutBuffLen ) {
395 			free( g_pbOutData );
396 
397 			g_ulOutBuffLen = a_ulDataLen;
398 			g_pbOutData = malloc( g_ulOutBuffLen );
399 			if ( !g_pbOutData ) {
400 				logError( TOKEN_MEMORY_ERROR );
401 				return -1;
402 			}
403 		}
404 
405 		// Copy the current data to the holding buffer
406 		if ( a_ulDataLen > 0 )
407 			memcpy( g_pbOutData, a_pbData, a_ulDataLen );
408 		g_ulOutDataLen = a_ulDataLen;
409 	}
410 	else {
411 		// No more data so write the last piece of data
412 		if ( a_ulDataLen > 0 ) {
413 			tWriteCount = fwrite( a_pbData, 1, a_ulDataLen, g_pOutFile );
414 
415 			if ( tWriteCount != a_ulDataLen ) {
416 				logError(TOKEN_FILE_WRITE_ERROR, g_pOutFile, "short write");
417 				return -1;
418 			}
419 		}
420 
421 		fclose( g_pOutFile );
422 	}
423 
424 	return 0;
425 }
426 
427 int
main(int a_iArgc,char ** a_szArgv)428 main( int    a_iArgc,
429       char **a_szArgv ) {
430 
431 	int  rc = 1;
432 
433 	char *pszPin = NULL;
434 
435 	CK_RV              rv;
436 	CK_SESSION_HANDLE  hSession;
437 	CK_OBJECT_HANDLE   hObject;
438 	CK_MECHANISM       tMechanism = { CKM_AES_ECB, NULL, 0 };
439 
440 	memset(&hSession, 0, sizeof(hSession));
441 	// Set up i18n
442 	initIntlSys( );
443 
444 	// Parse the command
445 	if ( parseCmd( a_iArgc, a_szArgv ) == -1 )
446 		goto out;
447 
448 	// Open the PKCS#11 TPM Token
449 	rv = openToken( g_pszToken );
450 	if ( rv != CKR_OK )
451 		goto out;
452 
453 	// Make sure the token is initialized
454 	if ( !isTokenInitialized( ) ) {
455 		logMsg( TOKEN_NOT_INIT_ERROR );
456 		goto out;
457 	}
458 
459 	// Open a session
460 	rv = openTokenSession( CKF_RW_SESSION, &hSession );
461 	if ( rv != CKR_OK )
462 		goto out;
463 
464 	pszPin = getPlainPasswd( TOKEN_USER_PIN_PROMPT, FALSE );
465 	if ( !pszPin )
466 		goto out;
467 
468 	// Login to the token
469 	rv = loginToken( hSession, CKU_USER, pszPin );
470 	if ( rv != CKR_OK )
471 		goto out;
472 
473 	// Obtain the key
474 	if ( getKey( hSession, &hObject ) == -1 )
475 		goto out;
476 
477 	// Perform the operation
478 	if ( g_bEncrypt )
479 		rv = encryptData( hSession, hObject, &tMechanism,
480 			readData, writeData );
481 	else
482 		rv = decryptData( hSession, hObject, &tMechanism,
483 			readData, writeData );
484 
485 	if ( rv != CKR_OK )
486 		goto out;
487 
488 	rc = 0;
489 
490 out:
491 	shredPasswd( pszPin );
492 
493 	if ( hSession )
494 		closeTokenSession( hSession );
495 
496 	closeToken( );
497 
498 	free( g_pszInFile );
499 	free( g_pszOutFile );
500 	free( g_pbInData );
501 	free( g_pbOutData );
502 
503 	if ( rc == 0 )
504 		logInfo( TOKEN_CMD_SUCCESS, a_szArgv[ 0 ] );
505 	else
506 		logInfo( TOKEN_CMD_FAILED, a_szArgv[ 0 ] );
507 
508 	return rc;
509 }
510