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 // Set up i18n
441 initIntlSys( );
442
443 // Parse the command
444 if ( parseCmd( a_iArgc, a_szArgv ) == -1 )
445 goto out;
446
447 // Open the PKCS#11 TPM Token
448 rv = openToken( g_pszToken );
449 if ( rv != CKR_OK )
450 goto out;
451
452 // Make sure the token is initialized
453 if ( !isTokenInitialized( ) ) {
454 logMsg( TOKEN_NOT_INIT_ERROR );
455 goto out;
456 }
457
458 // Open a session
459 rv = openTokenSession( CKF_RW_SESSION, &hSession );
460 if ( rv != CKR_OK )
461 goto out;
462
463 pszPin = getPlainPasswd( TOKEN_USER_PIN_PROMPT, FALSE );
464 if ( !pszPin )
465 goto out;
466
467 // Login to the token
468 rv = loginToken( hSession, CKU_USER, pszPin );
469 if ( rv != CKR_OK )
470 goto out;
471
472 // Obtain the key
473 if ( getKey( hSession, &hObject ) == -1 )
474 goto out;
475
476 // Perform the operation
477 if ( g_bEncrypt )
478 rv = encryptData( hSession, hObject, &tMechanism,
479 readData, writeData );
480 else
481 rv = decryptData( hSession, hObject, &tMechanism,
482 readData, writeData );
483
484 if ( rv != CKR_OK )
485 goto out;
486
487 rc = 0;
488
489 out:
490 shredPasswd( pszPin );
491
492 if ( hSession )
493 closeTokenSession( hSession );
494
495 closeToken( );
496
497 free( g_pszInFile );
498 free( g_pszOutFile );
499 free( g_pbInData );
500 free( g_pbOutData );
501
502 if ( rc == 0 )
503 logInfo( TOKEN_CMD_SUCCESS, a_szArgv[ 0 ] );
504 else
505 logInfo( TOKEN_CMD_FAILED, a_szArgv[ 0 ] );
506
507 return rc;
508 }
509