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