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