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