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