1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /* Portions Copyright 2005 Richard Lowe */
  22 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "@(#)decrypt.c  1.14    08/04/30 SMI"
  28 
  29 /*
  30  * decrypt.c
  31  *
  32  * Implements encrypt(1) and decrypt(1) commands
  33  *
  34  * One binary performs both encrypt/decrypt operation.
  35  *
  36  * Usage:
  37  *  -a algorithm mechanism name without CKM_ prefix. Case
  38  *               does not matter
  39  *  -k keyfile   file containing key data. If not specified user is
  40  *               prompted to enter key. key length > 0 is required
  41  *  -i infile    input file to encrypt/decrypt. If omitted, stdin used.
  42  *  -o outfile   output file to encrypt/decrypt. If omitted, stdout used.
  43  *               if infile & outfile are same, a temp file is used for
  44  *               output and infile is replaced with this file after
  45  *               operation is complete
  46  *  -l           Display the list of  algorithms
  47  *  -v           Display verbose information
  48  *  -T tokenspec Specify a PKCS#11 token (optionally used with -K)
  49  *  -K keylabel  Specify the symmetric PKCS#11 token key label
  50  *
  51  * Implementation notes:
  52  *   IV data - It is generated by random bytes equal to one block size.
  53  *
  54  *   Encrypted output format -
  55  *   - Output format version number - 4 bytes in network byte order.
  56  *   - Iterations used in key gen function, 4 bytes in  network byte order.
  57  *   - IV ('ivlen' bytes).  Length is algorithm-dependent (see mech_aliases)
  58  *   - Salt data used in key gen (16 bytes)
  59  *   - Cipher text data (remainder of the file)
  60  */
  61 
  62 #include <stdio.h>
  63 #include <stdlib.h>
  64 #include <unistd.h>
  65 #include <errno.h>
  66 #include <fcntl.h>
  67 #include <ctype.h>
  68 #include <strings.h>
  69 #include <libintl.h>
  70 #include <libgen.h>
  71 #include <locale.h>
  72 #include <limits.h>
  73 #include <sys/types.h>
  74 #include <sys/stat.h>
  75 #include <netinet/in.h>
  76 #include <security/cryptoki.h>
  77 #include <cryptoutil.h>
  78 #include <kmfapi.h>
  79 
  80 #define BUFFERSIZE      (2048)          /* Buffer size for reading file */
  81 #define BLOCKSIZE       (128)           /* Largest guess for block size */
  82 #define PROGRESSSIZE    (BUFFERSIZE*20) /* stdin progress indicator size */
  83 
  84 #define SUNW_ENCRYPT_FILE_VERSION 1
  85 
  86 /*
  87  * Exit Status codes
  88  */
  89 #ifndef EXIT_SUCCESS
  90 #define EXIT_SUCCESS    0       /* No errors */
  91 #define EXIT_FAILURE    1       /* All errors except usage */
  92 #endif /* EXIT_SUCCESS */
  93 
  94 #define EXIT_USAGE      2       /* usage/syntax error */
  95 
  96 #define ENCRYPT_NAME    "encrypt"       /* name of encrypt command */
  97 #define ENCRYPT_OPTIONS "a:T:K:k:i:o:lv"        /* options for encrypt */
  98 #define DECRYPT_NAME    "decrypt"       /* name of decrypt command */
  99 #define DECRYPT_OPTIONS "a:T:K:k:i:o:lv"        /* options for decrypt */
 100 
 101 /*
 102  * Structure containing info for encrypt/decrypt
 103  * command
 104  */
 105 struct CommandInfo {
 106         char            *name;          /* name of the command */
 107         char            *options;       /* command line options */
 108         CK_FLAGS        flags;
 109         CK_ATTRIBUTE_TYPE type;         /* type of command */
 110 
 111         /* function pointers for various operations */
 112         CK_RV   (*Init)(CK_SESSION_HANDLE, CK_MECHANISM_PTR, CK_OBJECT_HANDLE);
 113         CK_RV   (*Update)(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
 114                 CK_ULONG_PTR);
 115         CK_RV   (*Crypt)(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG, CK_BYTE_PTR,
 116                 CK_ULONG_PTR);
 117         CK_RV   (*Final)(CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR);
 118 };
 119 
 120 static struct CommandInfo encrypt_cmd = {
 121         ENCRYPT_NAME,
 122         ENCRYPT_OPTIONS,
 123         CKF_ENCRYPT,
 124         CKA_ENCRYPT,
 125         C_EncryptInit,
 126         C_EncryptUpdate,
 127         C_Encrypt,
 128         C_EncryptFinal
 129 };
 130 
 131 static struct CommandInfo decrypt_cmd = {
 132         DECRYPT_NAME,
 133         DECRYPT_OPTIONS,
 134         CKF_DECRYPT,
 135         CKA_DECRYPT,
 136         C_DecryptInit,
 137         C_DecryptUpdate,
 138         C_Decrypt,
 139         C_DecryptFinal
 140 };
 141 
 142 struct mech_alias {
 143         CK_MECHANISM_TYPE type;
 144         char *alias;
 145         CK_ULONG keysize_min;
 146         CK_ULONG keysize_max;
 147         int keysize_unit;
 148         int ivlen;
 149         boolean_t available;
 150 };
 151 
 152 #define MECH_ALIASES_COUNT 4
 153 
 154 static struct mech_alias mech_aliases[] = {
 155         { CKM_AES_CBC_PAD, "aes", ULONG_MAX, 0L, 8, 16, B_FALSE },
 156         { CKM_RC4, "arcfour", ULONG_MAX, 0L, 1, 0, B_FALSE },
 157         { CKM_DES_CBC_PAD, "des", 8, 8, 8, 8, B_FALSE },
 158         { CKM_DES3_CBC_PAD, "3des", 24, 24, 8, 8, B_FALSE },
 159 };
 160 
 161 static CK_BBOOL truevalue = TRUE;
 162 static CK_BBOOL falsevalue = FALSE;
 163 
 164 static boolean_t aflag = B_FALSE; /* -a <algorithm> flag, required */
 165 static boolean_t kflag = B_FALSE; /* -k <keyfile> flag */
 166 static boolean_t iflag = B_FALSE; /* -i <infile> flag, use stdin if absent */
 167 static boolean_t oflag = B_FALSE; /* -o <outfile> flag, use stdout if absent */
 168 static boolean_t lflag = B_FALSE; /* -l flag (list) */
 169 static boolean_t vflag = B_FALSE; /* -v flag (verbose) */
 170 static boolean_t Tflag = B_FALSE; /* -T flag (tokenspec) */
 171 static boolean_t Kflag = B_FALSE; /* -K flag (keylabel) */
 172 
 173 static char *keyfile = NULL;     /* name of keyfile */
 174 static char *inputfile = NULL;   /* name of input file */
 175 static char *outputfile = NULL;  /* name of output file */
 176 static char *token_label = NULL; /* name of PKCS#11 token */
 177 static char *key_label = NULL;   /* name of PKCS#11 token key label */
 178 
 179 static int status_pos = 0; /* current position of progress bar element */
 180 
 181 /*
 182  * function prototypes
 183  */
 184 static void usage(struct CommandInfo *cmd);
 185 static int execute_cmd(struct CommandInfo *cmd, char *algo_str);
 186 static int crypt_multipart(struct CommandInfo *cmd, CK_SESSION_HANDLE hSession,
 187         int infd, int outfd, off_t insize);
 188 
 189 int
 190 main(int argc, char **argv)
 191 {
 192 
 193         extern char *optarg;
 194         extern int optind;
 195         char *optstr;
 196         char c;                 /* current getopts flag */
 197         char *algo_str = NULL;  /* algorithm string */
 198         struct CommandInfo *cmd;
 199         char *cmdname;          /* name of command */
 200         boolean_t errflag = B_FALSE;
 201 
 202         (void) setlocale(LC_ALL, "");
 203 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 204 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 205 #endif
 206         (void) textdomain(TEXT_DOMAIN);
 207 
 208         /*
 209          * Based on command name, determine
 210          * type of command.
 211          */
 212         cmdname = basename(argv[0]);
 213 
 214         cryptodebug_init(cmdname);
 215 
 216         if (strcmp(cmdname, encrypt_cmd.name) == 0) {
 217                 cmd = &encrypt_cmd;
 218         } else if (strcmp(cmdname, decrypt_cmd.name) == 0) {
 219                 cmd = &decrypt_cmd;
 220         } else {
 221                 cryptoerror(LOG_STDERR, gettext(
 222                     "command name must be either encrypt or decrypt"));
 223                 exit(EXIT_USAGE);
 224         }
 225 
 226         optstr = cmd->options;
 227 
 228         /* Parse command line arguments */
 229         while (!errflag && (c = getopt(argc, argv, optstr)) != -1) {
 230 
 231                 switch (c) {
 232                 case 'a':
 233                         aflag = B_TRUE;
 234                         algo_str = optarg;
 235                         break;
 236                 case 'k':
 237                         kflag = B_TRUE;
 238                         keyfile = optarg;
 239                         break;
 240                 case 'T':
 241                         Tflag = B_TRUE;
 242                         token_label = optarg;
 243                         break;
 244                 case 'K':
 245                         Kflag = B_TRUE;
 246                         key_label = optarg;
 247                         break;
 248                 case 'i':
 249                         iflag = B_TRUE;
 250                         inputfile = optarg;
 251                         break;
 252                 case 'o':
 253                         oflag = B_TRUE;
 254                         outputfile = optarg;
 255                         break;
 256                 case 'l':
 257                         lflag = B_TRUE;
 258                         break;
 259                 case 'v':
 260                         vflag = B_TRUE;
 261                         break;
 262                 default:
 263                         errflag = B_TRUE;
 264                 }
 265         }
 266 
 267         if (errflag || (!aflag && !lflag) || (lflag && argc > 2) ||
 268             (kflag && Kflag) || (Tflag && !Kflag) ||
 269             (optind < argc)) {
 270                 usage(cmd);
 271                 exit(EXIT_USAGE);
 272         }
 273 
 274         return (execute_cmd(cmd, algo_str));
 275 }
 276 
 277 /*
 278  * usage message
 279  */
 280 static void
 281 usage(struct CommandInfo *cmd)
 282 {
 283         (void) fprintf(stderr, gettext("Usage:\n"));
 284         if (cmd->type == CKA_ENCRYPT) {
 285                 (void) fprintf(stderr, gettext("  encrypt -l\n"));
 286                 (void) fprintf(stderr, gettext("  encrypt -a <algorithm> "
 287                     "[-v] [-k <keyfile> | -K <keylabel> [-T <tokenspec>]] "
 288                     "[-i <infile>] [-o <outfile>]\n"));
 289 
 290         } else {
 291                 (void) fprintf(stderr, gettext("  decrypt -l\n"));
 292                 (void) fprintf(stderr, gettext("  decrypt -a <algorithm> "
 293                     "[-v] [-k <keyfile> | -K <keylabel> [-T <tokenspec>]] "
 294                     "[-i <infile>] [-o <outfile>]\n"));
 295         }
 296 }
 297 
 298 /*
 299  * Print out list of algorithms in default and verbose mode
 300  */
 301 static void
 302 algorithm_list()
 303 {
 304         int mech;
 305 
 306         (void) printf(gettext("Algorithm       Keysize:  Min   Max (bits)\n"
 307             "------------------------------------------\n"));
 308 
 309         for (mech = 0; mech < MECH_ALIASES_COUNT; mech++) {
 310 
 311                 if (mech_aliases[mech].available == B_FALSE)
 312                         continue;
 313 
 314                 (void) printf("%-15s", mech_aliases[mech].alias);
 315 
 316                 if (mech_aliases[mech].keysize_min != UINT_MAX &&
 317                     mech_aliases[mech].keysize_max != 0)
 318                         (void) printf("         %5lu %5lu\n",
 319                             (mech_aliases[mech].keysize_min *
 320                             mech_aliases[mech].keysize_unit),
 321                             (mech_aliases[mech].keysize_max *
 322                             mech_aliases[mech].keysize_unit));
 323                 else
 324                         (void) printf("\n");
 325 
 326         }
 327 }
 328 
 329 /*
 330  * This function will login into the token with the provided password and
 331  * find the token key object with the specified keytype and keylabel.
 332  */
 333 static int
 334 get_token_key(CK_SESSION_HANDLE hSession, CK_KEY_TYPE keytype,
 335     char *keylabel, CK_BYTE *password, int password_len,
 336     CK_OBJECT_HANDLE *keyobj)
 337 {
 338         CK_RV   rv;
 339         CK_ATTRIBUTE pTmpl[10];
 340         CK_OBJECT_CLASS class = CKO_SECRET_KEY;
 341         CK_BBOOL true = 1;
 342         CK_BBOOL is_token = 1;
 343         CK_ULONG key_obj_count = 1;
 344         int i;
 345         CK_KEY_TYPE ckKeyType = keytype;
 346 
 347 
 348         rv = C_Login(hSession, CKU_USER, (CK_UTF8CHAR_PTR)password,
 349             (CK_ULONG)password_len);
 350         if (rv != CKR_OK) {
 351                 (void) fprintf(stderr, "Cannot login to the token."
 352                     " error = %s\n", pkcs11_strerror(rv));
 353                 return (-1);
 354         }
 355 
 356         i = 0;
 357         pTmpl[i].type = CKA_TOKEN;
 358         pTmpl[i].pValue = &is_token;
 359         pTmpl[i].ulValueLen = sizeof (CK_BBOOL);
 360         i++;
 361 
 362         pTmpl[i].type = CKA_CLASS;
 363         pTmpl[i].pValue = &class;
 364         pTmpl[i].ulValueLen = sizeof (class);
 365         i++;
 366 
 367         pTmpl[i].type = CKA_LABEL;
 368         pTmpl[i].pValue = keylabel;
 369         pTmpl[i].ulValueLen = strlen(keylabel);
 370         i++;
 371 
 372         pTmpl[i].type = CKA_KEY_TYPE;
 373         pTmpl[i].pValue = &ckKeyType;
 374         pTmpl[i].ulValueLen = sizeof (ckKeyType);
 375         i++;
 376 
 377         pTmpl[i].type = CKA_PRIVATE;
 378         pTmpl[i].pValue = &true;
 379         pTmpl[i].ulValueLen = sizeof (true);
 380         i++;
 381 
 382         rv = C_FindObjectsInit(hSession, pTmpl, i);
 383         if (rv != CKR_OK) {
 384                 goto out;
 385         }
 386 
 387         rv = C_FindObjects(hSession, keyobj, 1, &key_obj_count);
 388 
 389         (void) C_FindObjectsFinal(hSession);
 390 
 391 out:
 392         if (rv != CKR_OK) {
 393                 (void) fprintf(stderr,
 394                     "Cannot retrieve key object. error = %s\n",
 395                     pkcs11_strerror(rv));
 396                 return (-1);
 397         }
 398 
 399         if (key_obj_count == 0) {
 400                 (void) fprintf(stderr, "Cannot find the key object.\n");
 401                 return (-1);
 402         }
 403 
 404         return (0);
 405 }
 406 
 407 
 408 /*
 409  * Execute the command.
 410  *   cmd - command pointing to type of operation.
 411  *   algo_str - alias of the algorithm passed.
 412  */
 413 static int
 414 execute_cmd(struct CommandInfo *cmd, char *algo_str)
 415 {
 416         CK_RV rv;
 417         CK_ULONG slotcount;
 418         CK_SLOT_ID slotID;
 419         CK_SLOT_ID_PTR pSlotList = NULL;
 420         CK_MECHANISM_TYPE mech_type = 0;
 421         CK_MECHANISM_INFO info, kg_info;
 422         CK_MECHANISM mech;
 423         CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
 424         CK_BYTE_PTR     pkeydata = NULL;
 425         CK_BYTE         salt[CK_PKCS5_PBKD2_SALT_SIZE];
 426         CK_ULONG        keysize = 0;
 427         int i, slot, mek;               /* index variables */
 428         int status;
 429         struct stat     insbuf;         /* stat buf for infile */
 430         struct stat     outsbuf;        /* stat buf for outfile */
 431         char    tmpnam[PATH_MAX];       /* tmp file name */
 432         CK_OBJECT_HANDLE key = (CK_OBJECT_HANDLE) 0;
 433         int infd = 0;                   /* input file, stdin default */
 434         int outfd = 1;                  /* output file, stdout default */
 435         char *outfilename = NULL;
 436         boolean_t errflag = B_TRUE;
 437         boolean_t inoutsame = B_FALSE;  /* if both input & output are same */
 438         CK_BYTE_PTR     pivbuf = NULL_PTR;
 439         CK_ULONG        ivlen = 0L;
 440         int             mech_match = 0;
 441         uint32_t        iterations = CK_PKCS5_PBKD2_ITERATIONS;
 442         CK_ULONG        keylen;
 443         uint32_t        version = SUNW_ENCRYPT_FILE_VERSION;
 444         CK_KEY_TYPE keytype;
 445         KMF_RETURN kmfrv;
 446         CK_SLOT_ID token_slot_id;
 447 
 448         if (aflag) {
 449                 /* Determine if algorithm is valid */
 450                 for (mech_match = 0; mech_match < MECH_ALIASES_COUNT;
 451                     mech_match++) {
 452                         if (strcmp(algo_str,
 453                             mech_aliases[mech_match].alias) == 0) {
 454                                 mech_type = mech_aliases[mech_match].type;
 455                                 break;
 456                         }
 457                 }
 458 
 459                 if (mech_match == MECH_ALIASES_COUNT) {
 460                         cryptoerror(LOG_STDERR,
 461                             gettext("unknown algorithm -- %s"), algo_str);
 462                         return (EXIT_FAILURE);
 463                 }
 464 
 465                 /*
 466                  * Process keyfile or get the token pin if -K is specified.
 467                  *
 468                  * If a keyfile is provided, get the key data from
 469                  * the file. Otherwise, prompt for a passphrase. The
 470                  * passphrase is used as the key data.
 471                  */
 472                 if (Kflag) {
 473                         /* get the pin of the token */
 474                         if (token_label == NULL || !strlen(token_label)) {
 475                                 token_label = pkcs11_default_token();
 476                         }
 477 
 478                         status = pkcs11_get_pass(token_label,
 479                             (char **)&pkeydata, (size_t *)&keysize, 0, B_FALSE);
 480                 } else if (kflag) {
 481                         /* get the key file */
 482                         status = pkcs11_read_data(keyfile, (void **)&pkeydata,
 483                             (size_t *)&keysize);
 484                 } else {
 485                         /* get the key from input */
 486                         status = pkcs11_get_pass(NULL, (char **)&pkeydata,
 487                             (size_t *)&keysize, 0, B_FALSE);
 488                 }
 489 
 490                 if (status == -1 || keysize == 0L) {
 491                         cryptoerror(LOG_STDERR,
 492                             Kflag ? gettext("invalid password.") :
 493                             gettext("invalid key."));
 494                         return (EXIT_FAILURE);
 495                 }
 496         }
 497 
 498         bzero(salt, sizeof (salt));
 499         /* Initialize pkcs */
 500         rv = C_Initialize(NULL);
 501         if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
 502                 cryptoerror(LOG_STDERR, gettext("failed to initialize "
 503                     "PKCS #11 framework: %s"), pkcs11_strerror(rv));
 504                 goto cleanup;
 505         }
 506 
 507         /* Get slot count */
 508         rv = C_GetSlotList(0, NULL_PTR, &slotcount);
 509         if (rv != CKR_OK || slotcount == 0) {
 510                 cryptoerror(LOG_STDERR, gettext(
 511                     "failed to find any cryptographic provider,"
 512                     "please check with your system administrator: %s"),
 513                     pkcs11_strerror(rv));
 514                 goto cleanup;
 515         }
 516 
 517         /* Found at least one slot, allocate memory for slot list */
 518         pSlotList = malloc(slotcount * sizeof (CK_SLOT_ID));
 519         if (pSlotList == NULL_PTR) {
 520                 int err = errno;
 521                 cryptoerror(LOG_STDERR, gettext("malloc: %s"), strerror(err));
 522                 goto cleanup;
 523         }
 524 
 525         /* Get the list of slots */
 526         if ((rv = C_GetSlotList(0, pSlotList, &slotcount)) != CKR_OK) {
 527                 cryptoerror(LOG_STDERR, gettext(
 528                     "failed to find any cryptographic provider,"
 529                     "please check with your system administrator: %s"),
 530                     pkcs11_strerror(rv));
 531                 goto cleanup;
 532         }
 533 
 534         if (lflag) {
 535 
 536                 /* Iterate through slots */
 537                 for (slot = 0; slot < slotcount; slot++) {
 538 
 539                         /* Iterate through each mechanism */
 540                         for (mek = 0; mek < MECH_ALIASES_COUNT; mek++) {
 541                                 rv = C_GetMechanismInfo(pSlotList[slot],
 542                                     mech_aliases[mek].type, &info);
 543 
 544                                 if (rv != CKR_OK)
 545                                         continue;
 546 
 547                                 /*
 548                                  * Set to minimum/maximum key sizes assuming
 549                                  * the values available are not 0.
 550                                  */
 551                                 if (info.ulMinKeySize && (info.ulMinKeySize <
 552                                     mech_aliases[mek].keysize_min))
 553                                         mech_aliases[mek].keysize_min =
 554                                             info.ulMinKeySize;
 555 
 556                                 if (info.ulMaxKeySize && (info.ulMaxKeySize >
 557                                     mech_aliases[mek].keysize_max))
 558                                         mech_aliases[mek].keysize_max =
 559                                             info.ulMaxKeySize;
 560 
 561                                 mech_aliases[mek].available = B_TRUE;
 562                         }
 563 
 564                 }
 565 
 566                 algorithm_list();
 567 
 568                 errflag = B_FALSE;
 569                 goto cleanup;
 570         }
 571 
 572 
 573         /*
 574          * Find a slot with matching mechanism
 575          *
 576          * If -K is specified, we find the slot id for the token first, then
 577          * check if the slot supports the algorithm.
 578          */
 579         i = 0;
 580         if (Kflag) {
 581                 kmfrv = kmf_pk11_token_lookup(NULL, token_label,
 582                     &token_slot_id);
 583                 if (kmfrv != KMF_OK) {
 584                         cryptoerror(LOG_STDERR,
 585                             gettext("no matching PKCS#11 token"));
 586                         errflag = B_TRUE;
 587                         goto cleanup;
 588                 }
 589                 rv = C_GetMechanismInfo(token_slot_id, mech_type, &info);
 590                 if (rv == CKR_OK && (info.flags & cmd->flags))
 591                         slotID = token_slot_id;
 592                 else
 593                         i = slotcount;
 594         } else {
 595                 for (i = 0; i < slotcount; i++) {
 596                         slotID = pSlotList[i];
 597                         rv = C_GetMechanismInfo(slotID, mech_type, &info);
 598                         if (rv != CKR_OK) {
 599                                 continue; /* to the next slot */
 600                         } else {
 601                                 /*
 602                                  * If the slot support the crypto, also
 603                                  * make sure it supports the correct
 604                                  * key generation mech if needed.
 605                                  *
 606                                  * We need PKCS5 when RC4 is used or
 607                                  * when the key is entered on cmd line.
 608                                  */
 609                                 if ((info.flags & cmd->flags) &&
 610                                     (mech_type == CKM_RC4) ||
 611                                     (keyfile == NULL)) {
 612                                         rv = C_GetMechanismInfo(slotID,
 613                                             CKM_PKCS5_PBKD2, &kg_info);
 614                                         if (rv == CKR_OK)
 615                                                 break;
 616                                 } else if (info.flags & cmd->flags) {
 617                                         break;
 618                                 }
 619                         }
 620                 }
 621         }
 622 
 623         /* Show error if no matching mechanism found */
 624         if (i == slotcount) {
 625                 cryptoerror(LOG_STDERR,
 626                     gettext("no cryptographic provider was "
 627                     "found for this algorithm -- %s"), algo_str);
 628                 goto cleanup;
 629         }
 630 
 631         /* Open a session */
 632         rv = C_OpenSession(slotID, CKF_SERIAL_SESSION,
 633             NULL_PTR, NULL, &hSession);
 634 
 635         if (rv != CKR_OK) {
 636                 cryptoerror(LOG_STDERR,
 637                     gettext("can not open PKCS #11 session: %s"),
 638                     pkcs11_strerror(rv));
 639                 goto cleanup;
 640         }
 641 
 642         /*
 643          * Generate IV data for encrypt.
 644          */
 645         ivlen = mech_aliases[mech_match].ivlen;
 646         if ((pivbuf = malloc((size_t)ivlen)) == NULL) {
 647                 int err = errno;
 648                 cryptoerror(LOG_STDERR, gettext("malloc: %s"),
 649                     strerror(err));
 650                 goto cleanup;
 651         }
 652 
 653         if (cmd->type == CKA_ENCRYPT) {
 654                 if ((pkcs11_random_data((void *)pivbuf,
 655                     mech_aliases[mech_match].ivlen)) != 0) {
 656                         cryptoerror(LOG_STDERR, gettext(
 657                             "Unable to generate random "
 658                             "data for initialization vector."));
 659                         goto cleanup;
 660                 }
 661         }
 662 
 663         /*
 664          * Create the key object
 665          */
 666         rv = pkcs11_mech2keytype(mech_type, &keytype);
 667         if (rv != CKR_OK) {
 668                 cryptoerror(LOG_STDERR,
 669                     gettext("unable to find key type for algorithm."));
 670                 goto cleanup;
 671         }
 672 
 673         /* Open input file */
 674         if (iflag) {
 675                 if ((infd = open(inputfile, O_RDONLY | O_NONBLOCK)) == -1) {
 676                         cryptoerror(LOG_STDERR, gettext(
 677                             "can not open input file %s"), inputfile);
 678                         goto cleanup;
 679                 }
 680 
 681                 /* Get info on input file */
 682                 if (fstat(infd, &insbuf) == -1) {
 683                         cryptoerror(LOG_STDERR, gettext(
 684                             "can not stat input file %s"), inputfile);
 685                         goto cleanup;
 686                 }
 687         }
 688 
 689         /*
 690          * Prepare output file
 691          * If the input & output file are same,
 692          * the output is written to a temp
 693          * file first, then renamed to the original file
 694          * after the crypt operation
 695          */
 696         inoutsame = B_FALSE;
 697         if (oflag) {
 698                 outfilename = outputfile;
 699                 if ((stat(outputfile, &outsbuf) != -1) &&
 700                     (insbuf.st_ino == outsbuf.st_ino)) {
 701                         char *dir;
 702 
 703                         /* create temp file on same dir */
 704                         dir = dirname(outputfile);
 705                         (void) snprintf(tmpnam, sizeof (tmpnam),
 706                             "%s/encrXXXXXX", dir);
 707                         outfilename = tmpnam;
 708                         if ((outfd = mkstemp(tmpnam)) == -1) {
 709                                 cryptoerror(LOG_STDERR, gettext(
 710                                     "cannot create temp file"));
 711                                 goto cleanup;
 712                         }
 713                         inoutsame = B_TRUE;
 714                 } else {
 715                         /* Create file for output */
 716                         if ((outfd = open(outfilename,
 717                             O_CREAT|O_WRONLY|O_TRUNC, 0644)) == -1) {
 718                                 cryptoerror(LOG_STDERR, gettext(
 719                                     "cannot open output file %s"),
 720                                     outfilename);
 721                                 goto cleanup;
 722                         }
 723                 }
 724         }
 725 
 726         /*
 727          * Read the version number from the head of the file
 728          * to know how to interpret the data that follows.
 729          */
 730         if (cmd->type == CKA_DECRYPT) {
 731                 if (read(infd, &version, sizeof (version)) !=
 732                     sizeof (version)) {
 733                         cryptoerror(LOG_STDERR, gettext(
 734                             "failed to get format version from "
 735                             "input file."));
 736                         goto cleanup;
 737                 }
 738                 /* convert to host byte order */
 739                 version = ntohl(version);
 740 
 741                 switch (version) {
 742                 case 1:
 743                 /*
 744                  * Version 1 output format:
 745                  *  - Output format version 1 (4 bytes)
 746                  *  - Iterations used in key gen function (4 bytes)
 747                  *  - IV ('ivlen' bytes). The length algorithm-dependent
 748                  *  - Salt data used in key gen (16 bytes)
 749                  *  - Cipher text data (remainder of the file)
 750                  *
 751                  * An encrypted file has IV as first block (0 or
 752                  * more bytes depending on mechanism) followed
 753                  * by cipher text.  Get the IV from the encrypted
 754                  * file.
 755                  */
 756                         /*
 757                          * Read iteration count and salt data.
 758                          */
 759                         if (read(infd, &iterations,
 760                             sizeof (iterations)) != sizeof (iterations)) {
 761                                 cryptoerror(LOG_STDERR, gettext(
 762                                     "failed to get iterations from "
 763                                     "input file."));
 764                                 goto cleanup;
 765                         }
 766                         /* convert to host byte order */
 767                         iterations = ntohl(iterations);
 768                         if (ivlen > 0 &&
 769                             read(infd, pivbuf, ivlen) != ivlen) {
 770                                 cryptoerror(LOG_STDERR, gettext(
 771                                     "failed to get initialization "
 772                                     "vector from input file."));
 773                                 goto cleanup;
 774                         }
 775                         if (read(infd, salt, sizeof (salt))
 776                             != sizeof (salt)) {
 777                                 cryptoerror(LOG_STDERR, gettext(
 778                                     "failed to get salt data from "
 779                                     "input file."));
 780                                 goto cleanup;
 781                         }
 782                         break;
 783                 default:
 784                         cryptoerror(LOG_STDERR, gettext(
 785                             "Unrecognized format version read from "
 786                             "input file - expected %d, got %d."),
 787                             SUNW_ENCRYPT_FILE_VERSION, version);
 788                         goto cleanup;
 789                         break;
 790                 }
 791         }
 792 
 793         /*
 794          * If Kflag is set, let's find the token key now.
 795          *
 796          * If Kflag is not set and if encrypting, we need some random
 797          * salt data to create the key.  If decrypting,
 798          * the salt should come from head of the file
 799          * to be decrypted.
 800          */
 801         if (Kflag) {
 802                 rv = get_token_key(hSession, keytype, key_label, pkeydata,
 803                     keysize, &key);
 804                 if (rv != CKR_OK) {
 805                         cryptoerror(LOG_STDERR, gettext(
 806                             "Can not find the token key"));
 807                         goto cleanup;
 808                 } else {
 809                         goto do_crypto;
 810                 }
 811         } else if (cmd->type == CKA_ENCRYPT) {
 812                 rv = pkcs11_random_data((void *)salt, sizeof (salt));
 813                 if (rv != 0) {
 814                         cryptoerror(LOG_STDERR,
 815                         gettext("unable to generate random "
 816                             "data for key salt."));
 817                         goto cleanup;
 818                 }
 819         }
 820 
 821 
 822         /*
 823          * If key input is read from  a file, treat it as
 824          * raw key data, unless it is to be used with RC4,
 825          * in which case it must be used to generate a pkcs5
 826          * key to address security concerns with RC4 keys.
 827          */
 828         if (kflag && keyfile != NULL && keytype != CKK_RC4) {
 829                 /* XXX : why wasn't SUNW_C_KeyToObject used here? */
 830                 CK_OBJECT_CLASS objclass = CKO_SECRET_KEY;
 831                 CK_ATTRIBUTE template[5];
 832                 int nattr = 0;
 833 
 834                 template[nattr].type = CKA_CLASS;
 835                 template[nattr].pValue = &objclass;
 836                 template[nattr].ulValueLen = sizeof (objclass);
 837                 nattr++;
 838 
 839                 template[nattr].type = CKA_KEY_TYPE;
 840                 template[nattr].pValue = &keytype;
 841                 template[nattr].ulValueLen = sizeof (keytype);
 842                 nattr++;
 843 
 844                 template[nattr].type = cmd->type;
 845                 template[nattr].pValue = &truevalue;
 846                 template[nattr].ulValueLen = sizeof (truevalue);
 847                 nattr++;
 848 
 849                 template[nattr].type = CKA_TOKEN;
 850                 template[nattr].pValue = &falsevalue;
 851                 template[nattr].ulValueLen = sizeof (falsevalue);
 852                 nattr++;
 853 
 854                 template[nattr].type = CKA_VALUE;
 855                 template[nattr].pValue = pkeydata;
 856                 template[nattr].ulValueLen = keysize;
 857                 nattr++;
 858 
 859                 rv = C_CreateObject(hSession, template, nattr, &key);
 860         } else {
 861                 /*
 862                  * If the encryption type has a fixed key length,
 863                  * then its not necessary to set the key length
 864                  * parameter when generating the key.
 865                  */
 866                 if (keytype == CKK_DES || keytype == CKK_DES3)
 867                         keylen = 0;
 868                 else
 869                         keylen = 16;
 870 
 871                 /*
 872                  * Generate a cryptographically secure key using
 873                  * the key read from the file given (-k keyfile) or
 874                  * the passphrase entered by the user.
 875                  */
 876                 rv = pkcs11_PasswdToPBKD2Object(hSession, (char *)pkeydata,
 877                     (size_t)keysize, (void *)salt, sizeof (salt), iterations,
 878                     keytype, keylen, cmd->flags, &key);
 879         }
 880 
 881         if (rv != CKR_OK) {
 882                 cryptoerror(LOG_STDERR, gettext(
 883                     "failed to generate a key: %s"),
 884                     pkcs11_strerror(rv));
 885                 goto cleanup;
 886         }
 887 
 888 
 889 do_crypto:
 890         /* Setup up mechanism */
 891         mech.mechanism = mech_type;
 892         mech.pParameter = (CK_VOID_PTR)pivbuf;
 893         mech.ulParameterLen = ivlen;
 894 
 895         if ((rv = cmd->Init(hSession, &mech, key)) != CKR_OK) {
 896                 cryptoerror(LOG_STDERR, gettext(
 897                     "failed to initialize crypto operation: %s"),
 898                     pkcs11_strerror(rv));
 899                 goto cleanup;
 900         }
 901 
 902         /* Write the version header encrypt command */
 903         if (cmd->type == CKA_ENCRYPT) {
 904                 /* convert to network order for storage */
 905                 uint32_t        netversion = htonl(version);
 906                 uint32_t        netiter;
 907 
 908                 if (write(outfd, &netversion, sizeof (netversion))
 909                     != sizeof (netversion)) {
 910                         cryptoerror(LOG_STDERR, gettext(
 911                             "failed to write version number "
 912                             "to output file."));
 913                         goto cleanup;
 914                 }
 915                 /*
 916                  * Write the iteration and salt data, even if they
 917                  * were not used to generate a key.
 918                  */
 919                 netiter = htonl(iterations);
 920                 if (write(outfd, &netiter,
 921                     sizeof (netiter)) != sizeof (netiter)) {
 922                         cryptoerror(LOG_STDERR, gettext(
 923                             "failed to write iterations to output"));
 924                         goto cleanup;
 925                 }
 926                 if (ivlen > 0 && write(outfd, pivbuf, ivlen) != ivlen) {
 927                         cryptoerror(LOG_STDERR, gettext(
 928                             "failed to write initialization vector "
 929                             "to output"));
 930                         goto cleanup;
 931                 }
 932                 if (write(outfd, salt, sizeof (salt)) != sizeof (salt)) {
 933                         cryptoerror(LOG_STDERR, gettext(
 934                             "failed to write salt data to output"));
 935                         goto cleanup;
 936                 }
 937         }
 938 
 939         if (crypt_multipart(cmd, hSession, infd, outfd, insbuf.st_size) == -1) {
 940                 goto cleanup;
 941         }
 942 
 943         errflag = B_FALSE;
 944 
 945         /*
 946          * Clean up
 947          */
 948 cleanup:
 949         /* Clear the key data, so others cannot snoop */
 950         if (pkeydata != NULL) {
 951                 bzero(pkeydata, keysize);
 952                 free(pkeydata);
 953                 pkeydata = NULL;
 954         }
 955 
 956         /* Destroy key object */
 957         if (Kflag != B_FALSE && key != (CK_OBJECT_HANDLE) 0) {
 958                 (void) C_DestroyObject(hSession, key);
 959         }
 960 
 961         /* free allocated memory */
 962         if (pSlotList != NULL)
 963                 free(pSlotList);
 964         if (pivbuf != NULL)
 965                 free(pivbuf);
 966 
 967         /* close all the files */
 968         if (iflag && (infd != -1))
 969                 (void) close(infd);
 970         if (oflag && (outfd != -1))
 971                 (void) close(outfd);
 972 
 973         /* rename tmp output to input file */
 974         if (inoutsame) {
 975                 if (rename(outfilename, inputfile) == -1) {
 976                         (void) unlink(outfilename);
 977                         cryptoerror(LOG_STDERR, gettext("rename failed."));
 978                 }
 979         }
 980 
 981         /* If error occurred, remove the output file */
 982         if (errflag && outfilename != NULL) {
 983                 (void) unlink(outfilename);
 984         }
 985 
 986         /* close pkcs11 session */
 987         if (hSession != CK_INVALID_HANDLE)
 988                 (void) C_CloseSession(hSession);
 989 
 990         (void) C_Finalize(NULL);
 991 
 992         return (errflag);
 993 }
 994 
 995 /*
 996  * Function for printing progress bar when the verbose flag
 997  * is set.
 998  *
 999  * The vertical bar is printed at 25, 50, and 75% complete.
1000  *
1001  * The function is passed the number of positions on the screen it needs to
1002  * advance and loops.
1003  */
1004 
1005 static void
1006 print_status(int pos_to_advance)
1007 {
1008 
1009         while (pos_to_advance > 0) {
1010                 switch (status_pos) {
1011                 case 0:
1012                         (void) fprintf(stderr, gettext("["));
1013                         break;
1014                 case 19:
1015                 case 39:
1016                 case 59:
1017                         (void) fprintf(stderr, gettext("|"));
1018                         break;
1019                 default:
1020                         (void) fprintf(stderr, gettext("."));
1021                 }
1022                 pos_to_advance--;
1023                 status_pos++;
1024         }
1025 }
1026 
1027 /*
1028  * Encrypt/Decrypt in multi part.
1029  *
1030  * This function reads the input file (infd) and writes the
1031  * encrypted/decrypted output to file (outfd).
1032  *
1033  * cmd - pointing  to commandinfo
1034  * hSession - pkcs session
1035  * infd - input file descriptor
1036  * outfd - output file descriptor
1037  *
1038  */
1039 
1040 static int
1041 crypt_multipart(struct CommandInfo *cmd, CK_SESSION_HANDLE hSession,
1042         int infd, int outfd, off_t insize)
1043 {
1044         CK_RV           rv;
1045         CK_ULONG        resultlen;
1046         CK_ULONG        resultbuflen;
1047         CK_BYTE_PTR     resultbuf;
1048         CK_ULONG        datalen;
1049         CK_BYTE         databuf[BUFFERSIZE];
1050         CK_BYTE         outbuf[BUFFERSIZE+BLOCKSIZE];
1051         CK_ULONG        status_index = 0; /* current total file size read */
1052         float           status_last = 0.0; /* file size of last element used */
1053         float           status_incr = 0.0; /* file size element increments */
1054         int             pos; /* # of progress bar elements to be print */
1055         ssize_t         nread;
1056         boolean_t       errflag = B_FALSE;
1057 
1058         datalen = sizeof (databuf);
1059         resultbuflen = sizeof (outbuf);
1060         resultbuf = outbuf;
1061 
1062         /* Divide into 79 increments for progress bar element spacing */
1063         if (vflag && iflag)
1064                 status_incr = (insize / 79.0);
1065 
1066         while ((nread = read(infd, databuf, datalen)) > 0) {
1067 
1068                 /* Start with the initial buffer */
1069                 resultlen = resultbuflen;
1070                 rv = cmd->Update(hSession, databuf, (CK_ULONG)nread,
1071                     resultbuf, &resultlen);
1072 
1073                 /* Need a bigger buffer? */
1074                 if (rv == CKR_BUFFER_TOO_SMALL) {
1075 
1076                         /* free the old buffer */
1077                         if (resultbuf != NULL && resultbuf != outbuf) {
1078                                 bzero(resultbuf, resultbuflen);
1079                                 free(resultbuf);
1080                         }
1081 
1082                         /* allocate a new big buffer */
1083                         if ((resultbuf = malloc((size_t)resultlen)) == NULL) {
1084                                 int err = errno;
1085                                 cryptoerror(LOG_STDERR, gettext("malloc: %s"),
1086                                     strerror(err));
1087                                 return (-1);
1088                         }
1089                         resultbuflen = resultlen;
1090 
1091                         /* Try again with bigger buffer */
1092                         rv = cmd->Update(hSession, databuf, (CK_ULONG)nread,
1093                             resultbuf, &resultlen);
1094                 }
1095 
1096                 if (rv != CKR_OK) {
1097                         errflag = B_TRUE;
1098                         cryptoerror(LOG_STDERR, gettext(
1099                             "crypto operation failed: %s"),
1100                             pkcs11_strerror(rv));
1101                         break;
1102                 }
1103 
1104                 /* write the output */
1105                 if (write(outfd, resultbuf, resultlen) != resultlen) {
1106                         cryptoerror(LOG_STDERR, gettext(
1107                             "failed to write result to output file."));
1108                         errflag = B_TRUE;
1109                         break;
1110                 }
1111 
1112                 if (vflag) {
1113                         status_index += resultlen;
1114 
1115                         /*
1116                          * If input is from stdin, do a our own progress bar
1117                          * by printing periods at a pre-defined increment
1118                          * until the file is done.
1119                          */
1120                         if (!iflag) {
1121 
1122                                 /*
1123                                  * Print at least 1 element in case the file
1124                                  * is small, it looks better than nothing.
1125                                  */
1126                                 if (status_pos == 0) {
1127                                         (void) fprintf(stderr, gettext("."));
1128                                         status_pos = 1;
1129                                 }
1130 
1131                                 if ((status_index - status_last) >
1132                                     (PROGRESSSIZE)) {
1133                                         (void) fprintf(stderr, gettext("."));
1134                                         status_last = status_index;
1135                                 }
1136                                 continue;
1137                         }
1138 
1139                         /* Calculate the number of elements need to be print */
1140                         if (insize <= BUFFERSIZE)
1141                                 pos = 78;
1142                         else
1143                                 pos = (int)((status_index - status_last) /
1144                                     status_incr);
1145 
1146                         /* Add progress bar elements, if needed */
1147                         if (pos > 0) {
1148                                 print_status(pos);
1149                                 status_last += (status_incr * pos);
1150                         }
1151                 }
1152         }
1153 
1154         /* Print verbose completion */
1155         if (vflag) {
1156                 if (iflag)
1157                         (void) fprintf(stderr, "]");
1158 
1159                 (void) fprintf(stderr, "\n%s\n", gettext("Done."));
1160         }
1161 
1162         /* Error in reading */
1163         if (nread == -1) {
1164                 cryptoerror(LOG_STDERR, gettext(
1165                     "error reading from input file"));
1166                 errflag = B_TRUE;
1167         }
1168 
1169         if (!errflag) {
1170 
1171                 /* Do the final part */
1172 
1173                 rv = cmd->Final(hSession, resultbuf, &resultlen);
1174 
1175                 if (rv == CKR_OK) {
1176                         /* write the output */
1177                         if (write(outfd, resultbuf, resultlen) != resultlen) {
1178                                 cryptoerror(LOG_STDERR, gettext(
1179                                     "failed to write result to output file."));
1180                                 errflag = B_TRUE;
1181                         }
1182                 } else {
1183                         cryptoerror(LOG_STDERR, gettext(
1184                             "crypto operation failed: %s"),
1185                             pkcs11_strerror(rv));
1186                         errflag = B_TRUE;
1187                 }
1188 
1189         }
1190 
1191         if (resultbuf != NULL && resultbuf != outbuf) {
1192                 bzero(resultbuf, resultbuflen);
1193                 free(resultbuf);
1194         }
1195 
1196         if (errflag) {
1197                 return (-1);
1198         } else {
1199                 return (0);
1200         }
1201 }