xref: /illumos-gate/usr/src/cmd/cmd-crypto/digest/digest.c (revision cdd3e9a818787b4def17c9f707f435885ce0ed31)
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 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * digest.c
28  *
29  * Implements digest(1) and mac(1) commands
30  * If command name is mac, performs mac operation
31  * else perform digest operation
32  *
33  * See the man pages for digest and mac for details on
34  * how these commands work.
35  */
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <ctype.h>
42 #include <strings.h>
43 #include <libintl.h>
44 #include <libgen.h>
45 #include <locale.h>
46 #include <errno.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <security/cryptoki.h>
50 #include <limits.h>
51 #include <cryptoutil.h>
52 #include <kmfapi.h>
53 
54 /*
55  * Buffer size for reading file. This is given a rather high value
56  * to get better performance when a hardware provider is present.
57  */
58 #define	BUFFERSIZE	(1024 * 64)
59 
60 /*
61  * RESULTLEN - large enough size in bytes to hold result for
62  * digest and mac results for all mechanisms
63  */
64 #define	RESULTLEN	(512)
65 
66 /*
67  * Exit Status codes
68  */
69 #ifndef	EXIT_SUCCESS
70 #define	EXIT_SUCCESS	0	/* No errors */
71 #define	EXIT_FAILURE	1	/* All errors except usage */
72 #endif /* EXIT_SUCCESS */
73 
74 #define	EXIT_USAGE	2	/* usage/syntax error */
75 
76 #define	MAC_NAME	"mac"		/* name of mac command */
77 #define	MAC_OPTIONS	"lva:k:T:K:"	/* for getopt */
78 #define	DIGEST_NAME	"digest"	/* name of digest command */
79 #define	DIGEST_OPTIONS	"lva:"		/* for getopt */
80 
81 /* Saved command line options */
82 static boolean_t vflag = B_FALSE;	/* -v (verbose) flag, optional */
83 static boolean_t aflag = B_FALSE;	/* -a <algorithm> flag, required */
84 static boolean_t lflag = B_FALSE;	/* -l flag, for mac and digest */
85 static boolean_t kflag = B_FALSE;	/* -k keyfile */
86 static boolean_t Tflag = B_FALSE;	/* -T token_spec */
87 static boolean_t Kflag = B_FALSE;	/* -K key_label */
88 
89 static char *keyfile = NULL;	 /* name of file containing key value */
90 static char *token_label = NULL; /* tokensSpec: tokenName[:manufId[:serial]] */
91 static char *key_label = NULL;	 /* PKCS#11 symmetric token key label */
92 
93 static CK_BYTE buf[BUFFERSIZE];
94 
95 struct mech_alias {
96 	CK_MECHANISM_TYPE type;
97 	char *alias;
98 	CK_ULONG keysize_min;
99 	CK_ULONG keysize_max;
100 	int keysize_unit;
101 	boolean_t available;
102 };
103 
104 #define	MECH_ALIASES_COUNT 11
105 
106 static struct mech_alias mech_aliases[] = {
107 	{ CKM_SHA_1, "sha1", ULONG_MAX, 0L, 8, B_FALSE },
108 	{ CKM_MD5, "md5", ULONG_MAX, 0L, 8, B_FALSE },
109 	{ CKM_DES_MAC, "des_mac", ULONG_MAX, 0L, 8, B_FALSE },
110 	{ CKM_SHA_1_HMAC, "sha1_hmac", ULONG_MAX, 0L, 8, B_FALSE },
111 	{ CKM_MD5_HMAC, "md5_hmac", ULONG_MAX, 0L, 8, B_FALSE },
112 	{ CKM_SHA256, "sha256", ULONG_MAX, 0L, 8, B_FALSE },
113 	{ CKM_SHA384, "sha384", ULONG_MAX, 0L, 8, B_FALSE },
114 	{ CKM_SHA512, "sha512", ULONG_MAX, 0L, 8, B_FALSE },
115 	{ CKM_SHA256_HMAC, "sha256_hmac", ULONG_MAX, 0L, 8, B_FALSE },
116 	{ CKM_SHA384_HMAC, "sha384_hmac", ULONG_MAX, 0L, 8, B_FALSE },
117 	{ CKM_SHA512_HMAC, "sha512_hmac", ULONG_MAX, 0L, 8, B_FALSE }
118 };
119 
120 static CK_BBOOL true = TRUE;
121 
122 static void usage(boolean_t mac_cmd);
123 static int execute_cmd(char *algo_str, int filecount,
124 	char **filelist, boolean_t mac_cmd);
125 static CK_RV do_mac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pmech,
126 	int fd, CK_OBJECT_HANDLE key, CK_BYTE_PTR *psignature,
127 	CK_ULONG_PTR psignaturelen);
128 static CK_RV do_digest(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pmech,
129 	int fd, CK_BYTE_PTR *pdigest, CK_ULONG_PTR pdigestlen);
130 
131 int
132 main(int argc, char **argv)
133 {
134 	extern char *optarg;
135 	extern int optind;
136 	int errflag = 0;	/* We had an optstr parse error */
137 	int c;			/* current getopts flag */
138 	char *algo_str;		/* mechanism/algorithm string */
139 	int filecount;
140 	boolean_t mac_cmd;	/* if TRUE, do mac, else do digest */
141 	char *optstr;
142 	char **filelist;	/* list of files */
143 	char *cmdname = NULL;	/* name of command */
144 
145 	(void) setlocale(LC_ALL, "");
146 #if !defined(TEXT_DOMAIN)	/* Should be defiend by cc -D */
147 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
148 #endif
149 	(void) textdomain(TEXT_DOMAIN);
150 
151 	/*
152 	 * Based on command name, determine
153 	 * type of command. mac is mac
154 	 * everything else is digest.
155 	 */
156 	cmdname = basename(argv[0]);
157 
158 	cryptodebug_init(cmdname);
159 
160 	if (strcmp(cmdname, MAC_NAME) == 0)
161 		mac_cmd = B_TRUE;
162 	else if (strcmp(cmdname, DIGEST_NAME) == 0)
163 		mac_cmd = B_FALSE;
164 	else {
165 		cryptoerror(LOG_STDERR, gettext(
166 		    "command name must be either digest or mac\n"));
167 		exit(EXIT_USAGE);
168 	}
169 
170 	if (mac_cmd) {
171 		optstr = MAC_OPTIONS;
172 	} else {
173 		optstr = DIGEST_OPTIONS;
174 	}
175 
176 	/* Parse command line arguments */
177 	while (!errflag && (c = getopt(argc, argv, optstr)) != -1) {
178 
179 		switch (c) {
180 		case 'v':
181 			vflag = B_TRUE;
182 			break;
183 		case 'a':
184 			aflag = B_TRUE;
185 			algo_str = optarg;
186 			break;
187 		case 'k':
188 			kflag = B_TRUE;
189 			keyfile = optarg;
190 			break;
191 		case 'l':
192 			lflag = B_TRUE;
193 			break;
194 		case 'T':
195 			Tflag = B_TRUE;
196 			token_label = optarg;
197 			break;
198 		case 'K':
199 			Kflag = B_TRUE;
200 			key_label = optarg;
201 			break;
202 		default:
203 			errflag++;
204 		}
205 	}
206 
207 	filecount = argc - optind;
208 	if (errflag || (!aflag && !lflag) || (lflag && argc > 2) ||
209 	    (kflag && Kflag) || (Tflag && !Kflag) || filecount < 0) {
210 		usage(mac_cmd);
211 		exit(EXIT_USAGE);
212 	}
213 
214 	if (filecount == 0) {
215 		filelist = NULL;
216 	} else {
217 		filelist = &argv[optind];
218 	}
219 
220 	return (execute_cmd(algo_str, filecount, filelist, mac_cmd));
221 }
222 
223 /*
224  * usage message for digest/mac
225  */
226 static void
227 usage(boolean_t mac_cmd)
228 {
229 	(void) fprintf(stderr, gettext("Usage:\n"));
230 	if (mac_cmd) {
231 		(void) fprintf(stderr, gettext("  mac -l\n"));
232 		(void) fprintf(stderr, gettext("  mac [-v] -a <algorithm> "
233 		    "[-k <keyfile> | -K <keylabel> [-T <tokenspec>]] "
234 		    "[file...]\n"));
235 	} else {
236 		(void) fprintf(stderr, gettext("  digest -l | [-v] "
237 		    "-a <algorithm> [file...]\n"));
238 	}
239 }
240 
241 /*
242  * Print out list of available algorithms.
243  */
244 static void
245 algorithm_list(boolean_t mac_cmd)
246 {
247 	int mech;
248 
249 	if (mac_cmd)
250 		(void) printf(gettext("Algorithm       Keysize:  Min   "
251 		    "Max (bits)\n"
252 		    "------------------------------------------\n"));
253 
254 	for (mech = 0; mech < MECH_ALIASES_COUNT; mech++) {
255 
256 		if (mech_aliases[mech].available == B_FALSE)
257 			continue;
258 
259 		if (mac_cmd) {
260 			(void) printf("%-15s", mech_aliases[mech].alias);
261 
262 			if (mech_aliases[mech].keysize_min != ULONG_MAX &&
263 			    mech_aliases[mech].keysize_max != 0)
264 				(void) printf("         %5lu %5lu\n",
265 				    (mech_aliases[mech].keysize_min *
266 				    mech_aliases[mech].keysize_unit),
267 				    (mech_aliases[mech].keysize_max *
268 				    mech_aliases[mech].keysize_unit));
269 			else
270 				(void) printf("\n");
271 
272 		} else
273 			(void) printf("%s\n", mech_aliases[mech].alias);
274 
275 	}
276 }
277 
278 static int
279 get_token_key(CK_SESSION_HANDLE hSession, CK_KEY_TYPE keytype,
280     char *keylabel, CK_BYTE *password, int password_len,
281     CK_OBJECT_HANDLE *keyobj)
282 {
283 	CK_RV rv;
284 	CK_ATTRIBUTE pTmpl[10];
285 	CK_OBJECT_CLASS class = CKO_SECRET_KEY;
286 	CK_BBOOL true = 1;
287 	CK_BBOOL is_token = 1;
288 	CK_ULONG key_obj_count = 1;
289 	int i;
290 	CK_KEY_TYPE ckKeyType = keytype;
291 
292 
293 	rv = C_Login(hSession, CKU_USER, (CK_UTF8CHAR_PTR)password,
294 	    password_len);
295 	if (rv != CKR_OK) {
296 		(void) fprintf(stderr, "Cannot login to the token."
297 		    " error = %s\n", pkcs11_strerror(rv));
298 		return (-1);
299 	}
300 
301 	i = 0;
302 	pTmpl[i].type = CKA_TOKEN;
303 	pTmpl[i].pValue = &is_token;
304 	pTmpl[i].ulValueLen = sizeof (CK_BBOOL);
305 	i++;
306 
307 	pTmpl[i].type = CKA_CLASS;
308 	pTmpl[i].pValue = &class;
309 	pTmpl[i].ulValueLen = sizeof (class);
310 	i++;
311 
312 	pTmpl[i].type = CKA_LABEL;
313 	pTmpl[i].pValue = keylabel;
314 	pTmpl[i].ulValueLen = strlen(keylabel);
315 	i++;
316 
317 	pTmpl[i].type = CKA_KEY_TYPE;
318 	pTmpl[i].pValue = &ckKeyType;
319 	pTmpl[i].ulValueLen = sizeof (ckKeyType);
320 	i++;
321 
322 	pTmpl[i].type = CKA_PRIVATE;
323 	pTmpl[i].pValue = &true;
324 	pTmpl[i].ulValueLen = sizeof (true);
325 	i++;
326 
327 	rv = C_FindObjectsInit(hSession, pTmpl, i);
328 	if (rv != CKR_OK) {
329 		goto out;
330 	}
331 
332 	rv = C_FindObjects(hSession, keyobj, 1, &key_obj_count);
333 	(void) C_FindObjectsFinal(hSession);
334 
335 out:
336 	if (rv != CKR_OK) {
337 		(void) fprintf(stderr,
338 		    "Cannot retrieve key object. error = %s\n",
339 		    pkcs11_strerror(rv));
340 		return (-1);
341 	}
342 
343 	if (key_obj_count == 0) {
344 		(void) fprintf(stderr, "Cannot find the key object.\n");
345 		return (-1);
346 	}
347 
348 	return (0);
349 }
350 
351 
352 /*
353  * Execute the command.
354  *   algo_str - name of algorithm
355  *   filecount - no. of files to process, if 0, use stdin
356  *   filelist - list of files
357  *   mac_cmd - if true do mac else do digest
358  */
359 static int
360 execute_cmd(char *algo_str, int filecount, char **filelist, boolean_t mac_cmd)
361 {
362 	int fd;
363 	char *filename = NULL;
364 	CK_RV rv;
365 	CK_ULONG slotcount;
366 	CK_SLOT_ID slotID;
367 	CK_SLOT_ID_PTR pSlotList = NULL;
368 	CK_MECHANISM_TYPE mech_type;
369 	CK_MECHANISM_INFO info;
370 	CK_MECHANISM mech;
371 	CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
372 	CK_BYTE_PTR resultbuf = NULL;
373 	CK_ULONG resultlen;
374 	CK_BYTE_PTR	pkeydata = NULL;
375 	CK_OBJECT_HANDLE key = (CK_OBJECT_HANDLE) 0;
376 	size_t keylen = 0;		/* key length */
377 	char *resultstr = NULL;	/* result in hex string */
378 	int resultstrlen;	/* result string length */
379 	int i;
380 	int exitcode = EXIT_SUCCESS;		/* return code */
381 	int slot, mek;			/* index variables */
382 	int mech_match = 0;
383 	CK_BYTE		salt[CK_PKCS5_PBKD2_SALT_SIZE];
384 	CK_ULONG	keysize;
385 	CK_ULONG	iterations = CK_PKCS5_PBKD2_ITERATIONS;
386 	CK_KEY_TYPE keytype;
387 	KMF_RETURN kmfrv;
388 	CK_SLOT_ID token_slot_id;
389 
390 	if (aflag) {
391 		/*
392 		 * Determine if algorithm/mechanism is valid
393 		 */
394 		for (mech_match = 0; mech_match < MECH_ALIASES_COUNT;
395 		    mech_match++) {
396 			if (strcmp(algo_str,
397 			    mech_aliases[mech_match].alias) == 0) {
398 				mech_type = mech_aliases[mech_match].type;
399 				break;
400 			}
401 
402 		}
403 
404 		if (mech_match == MECH_ALIASES_COUNT) {
405 			cryptoerror(LOG_STDERR,
406 			    gettext("unknown algorithm -- %s"), algo_str);
407 			return (EXIT_FAILURE);
408 		}
409 
410 		/* Get key to do a MAC operation */
411 		if (mac_cmd) {
412 			int status;
413 
414 			if (Kflag) {
415 				/* get the pin of the token */
416 				if (token_label == NULL ||
417 				    !strlen(token_label)) {
418 					token_label = pkcs11_default_token();
419 				}
420 
421 				status = pkcs11_get_pass(token_label,
422 				    (char **)&pkeydata, &keylen,
423 				    0, B_FALSE);
424 			} else if (keyfile != NULL) {
425 				/* get the key file */
426 				status = pkcs11_read_data(keyfile,
427 				    (void **)&pkeydata, &keylen);
428 			} else {
429 				/* get the key from input */
430 				status = pkcs11_get_pass(NULL,
431 				    (char **)&pkeydata, &keylen,
432 				    0, B_FALSE);
433 			}
434 
435 			if (status != 0 || keylen == 0 || pkeydata == NULL) {
436 				cryptoerror(LOG_STDERR,
437 				    (Kflag || (keyfile == NULL)) ?
438 				    gettext("invalid passphrase.") :
439 				    gettext("invalid key."));
440 				return (EXIT_FAILURE);
441 			}
442 		}
443 	}
444 
445 	/* Initialize, and get list of slots */
446 	rv = C_Initialize(NULL);
447 	if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
448 		cryptoerror(LOG_STDERR,
449 		    gettext("failed to initialize PKCS #11 framework: %s"),
450 		    pkcs11_strerror(rv));
451 		return (EXIT_FAILURE);
452 	}
453 
454 	/* Get slot count */
455 	rv = C_GetSlotList(0, NULL_PTR, &slotcount);
456 	if (rv != CKR_OK || slotcount == 0) {
457 		cryptoerror(LOG_STDERR, gettext(
458 		    "failed to find any cryptographic provider; "
459 		    "please check with your system administrator: %s"),
460 		    pkcs11_strerror(rv));
461 		exitcode = EXIT_FAILURE;
462 		goto cleanup;
463 	}
464 
465 	/* Found at least one slot, allocate memory for slot list */
466 	pSlotList = malloc(slotcount * sizeof (CK_SLOT_ID));
467 	if (pSlotList == NULL_PTR) {
468 		int err = errno;
469 		cryptoerror(LOG_STDERR, gettext("malloc: %s\n"),
470 		    strerror(err));
471 		exitcode = EXIT_FAILURE;
472 		goto cleanup;
473 	}
474 
475 	/* Get the list of slots */
476 	if ((rv = C_GetSlotList(0, pSlotList, &slotcount)) != CKR_OK) {
477 		cryptoerror(LOG_STDERR, gettext(
478 		    "failed to find any cryptographic provider; "
479 		    "please check with your system administrator: %s"),
480 		    pkcs11_strerror(rv));
481 		exitcode = EXIT_FAILURE;
482 		goto cleanup;
483 	}
484 
485 	/*
486 	 * Obtain list of algorithms if -l option was given
487 	 */
488 	if (lflag) {
489 
490 		for (slot = 0; slot < slotcount; slot++) {
491 
492 			/* Iterate through each mechanism */
493 			for (mek = 0; mek < MECH_ALIASES_COUNT; mek++) {
494 				rv = C_GetMechanismInfo(pSlotList[slot],
495 				    mech_aliases[mek].type, &info);
496 
497 				/* Only check algorithms that can be used */
498 				if ((rv != CKR_OK) ||
499 				    (!mac_cmd && (info.flags & CKF_SIGN)) ||
500 				    (mac_cmd && (info.flags & CKF_DIGEST)))
501 					continue;
502 
503 				/*
504 				 * Set to minimum/maximum key sizes assuming
505 				 * the values available are not 0.
506 				 */
507 				if (info.ulMinKeySize && (info.ulMinKeySize <
508 				    mech_aliases[mek].keysize_min))
509 					mech_aliases[mek].keysize_min =
510 					    info.ulMinKeySize;
511 
512 				if (info.ulMaxKeySize && (info.ulMaxKeySize >
513 				    mech_aliases[mek].keysize_max))
514 					mech_aliases[mek].keysize_max =
515 					    info.ulMaxKeySize;
516 
517 				mech_aliases[mek].available = B_TRUE;
518 			}
519 
520 		}
521 
522 		algorithm_list(mac_cmd);
523 
524 		goto cleanup;
525 	}
526 
527 	/*
528 	 * Find a slot with matching mechanism
529 	 *
530 	 * If -K is specified, we find the slot id for the token first, then
531 	 * check if the slot supports the algorithm.
532 	 */
533 	i = 0;
534 	if (Kflag) {
535 		kmfrv = kmf_pk11_token_lookup(NULL, token_label,
536 		    &token_slot_id);
537 		if (kmfrv != KMF_OK) {
538 			cryptoerror(LOG_STDERR,
539 			    gettext("no matching PKCS#11 token"));
540 			exitcode = EXIT_FAILURE;
541 			goto cleanup;
542 		}
543 		rv = C_GetMechanismInfo(token_slot_id, mech_type, &info);
544 		if (rv == CKR_OK && (info.flags & CKF_SIGN))
545 			slotID = token_slot_id;
546 		else
547 			i = slotcount;
548 
549 	} else {
550 		for (i = 0; i < slotcount; i++) {
551 			slotID = pSlotList[i];
552 			rv = C_GetMechanismInfo(slotID, mech_type, &info);
553 			if (rv != CKR_OK) {
554 				continue; /* to the next slot */
555 			} else {
556 				if (mac_cmd) {
557 					/*
558 					 * Make sure the slot supports
559 					 * PKCS5 key generation if we
560 					 * will be using it later.
561 					 * We use it whenever the key
562 					 * is entered at command line.
563 					 */
564 					if ((info.flags & CKF_SIGN) &&
565 					    (keyfile == NULL)) {
566 						CK_MECHANISM_INFO kg_info;
567 						rv = C_GetMechanismInfo(slotID,
568 						    CKM_PKCS5_PBKD2, &kg_info);
569 						if (rv == CKR_OK)
570 							break;
571 					} else if (info.flags & CKF_SIGN) {
572 						break;
573 					}
574 				} else {
575 					if (info.flags & CKF_DIGEST)
576 						break;
577 				}
578 			}
579 		}
580 	}
581 
582 	/* Show error if no matching mechanism found */
583 	if (i == slotcount) {
584 		cryptoerror(LOG_STDERR,
585 		    gettext("no cryptographic provider was "
586 		    "found for this algorithm -- %s"), algo_str);
587 		exitcode = EXIT_FAILURE;
588 		goto cleanup;
589 	}
590 
591 	/* Mechanism is supported. Go ahead & open a session */
592 	rv = C_OpenSession(slotID, CKF_SERIAL_SESSION,
593 	    NULL_PTR, NULL, &hSession);
594 
595 	if (rv != CKR_OK) {
596 		cryptoerror(LOG_STDERR,
597 		    gettext("can not open PKCS#11 session: %s"),
598 		    pkcs11_strerror(rv));
599 		exitcode = EXIT_FAILURE;
600 		goto cleanup;
601 	}
602 
603 	/* Create a key object for mac operation */
604 	if (mac_cmd) {
605 		/*
606 		 * If we read keybytes from a file,
607 		 * do NOT process them with C_GenerateKey,
608 		 * treat them as raw keydata bytes and
609 		 * create a key object for them.
610 		 */
611 		if (keyfile) {
612 			/* XXX : why wasn't SUNW_C_KeyToObject used here? */
613 			CK_OBJECT_CLASS class = CKO_SECRET_KEY;
614 			CK_KEY_TYPE tmpl_keytype = CKK_GENERIC_SECRET;
615 			CK_BBOOL false = FALSE;
616 			int nattr = 0;
617 			CK_ATTRIBUTE template[5];
618 
619 			if (mech_type == CKM_DES_MAC) {
620 				tmpl_keytype = CKK_DES;
621 			}
622 			template[nattr].type = CKA_CLASS;
623 			template[nattr].pValue = &class;
624 			template[nattr].ulValueLen = sizeof (class);
625 			nattr++;
626 
627 			template[nattr].type = CKA_KEY_TYPE;
628 			template[nattr].pValue = &tmpl_keytype;
629 			template[nattr].ulValueLen = sizeof (tmpl_keytype);
630 			nattr++;
631 
632 			template[nattr].type = CKA_SIGN;
633 			template[nattr].pValue = &true;
634 			template[nattr].ulValueLen = sizeof (true);
635 			nattr++;
636 
637 			template[nattr].type = CKA_TOKEN;
638 			template[nattr].pValue = &false;
639 			template[nattr].ulValueLen = sizeof (false);
640 			nattr++;
641 
642 			template[nattr].type = CKA_VALUE;
643 			template[nattr].pValue = pkeydata;
644 			template[nattr].ulValueLen = keylen;
645 			nattr++;
646 
647 			rv = C_CreateObject(hSession, template, nattr, &key);
648 
649 		} else if (Kflag) {
650 
651 			if (mech_type == CKM_DES_MAC) {
652 				keytype = CKK_DES;
653 			} else {
654 				keytype = CKK_GENERIC_SECRET;
655 			}
656 
657 			rv = get_token_key(hSession, keytype, key_label,
658 			    pkeydata, keylen, &key);
659 			if (rv != CKR_OK) {
660 				exitcode = EXIT_FAILURE;
661 				goto cleanup;
662 			}
663 		} else {
664 			CK_KEY_TYPE keytype;
665 			if (mech_type == CKM_DES_MAC) {
666 				keytype = CKK_DES;
667 				keysize = 0;
668 			} else {
669 				keytype = CKK_GENERIC_SECRET;
670 				keysize = 16; /* 128 bits */
671 			}
672 			/*
673 			 * We use a fixed salt (0x0a, 0x0a, 0x0a ...)
674 			 * for creating the key so that the end user
675 			 * will be able to generate the same 'mac'
676 			 * using the same passphrase.
677 			 */
678 			(void) memset(salt, 0x0a, sizeof (salt));
679 			rv = pkcs11_PasswdToPBKD2Object(hSession,
680 			    (char *)pkeydata, (size_t)keylen, (void *)salt,
681 			    sizeof (salt), iterations, keytype, keysize,
682 			    CKF_SIGN, &key);
683 		}
684 
685 		if (rv != CKR_OK) {
686 			cryptoerror(LOG_STDERR,
687 			    gettext("unable to create key for crypto "
688 			    "operation: %s"), pkcs11_strerror(rv));
689 			exitcode = EXIT_FAILURE;
690 			goto cleanup;
691 		}
692 	}
693 
694 	/* Allocate a buffer to store result. */
695 	resultlen = RESULTLEN;
696 	if ((resultbuf = malloc(resultlen)) == NULL) {
697 		int err = errno;
698 		cryptoerror(LOG_STDERR, gettext("malloc: %s\n"),
699 		    strerror(err));
700 		exitcode = EXIT_FAILURE;
701 		goto cleanup;
702 	}
703 
704 	/* Allocate a buffer to store result string */
705 	resultstrlen = RESULTLEN;
706 	if ((resultstr = malloc(resultstrlen)) == NULL) {
707 		int err = errno;
708 		cryptoerror(LOG_STDERR, gettext("malloc: %s\n"),
709 		    strerror(err));
710 		exitcode = EXIT_FAILURE;
711 		goto cleanup;
712 	}
713 
714 	mech.mechanism = mech_type;
715 	mech.pParameter = NULL_PTR;
716 	mech.ulParameterLen = 0;
717 	exitcode = EXIT_SUCCESS;
718 	i = 0;
719 
720 	do {
721 		if (filecount > 0 && filelist != NULL) {
722 			filename = filelist[i];
723 			if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) ==
724 			    -1) {
725 				cryptoerror(LOG_STDERR, gettext(
726 				    "can not open input file %s\n"), filename);
727 				exitcode = EXIT_USAGE;
728 				continue;
729 			}
730 		} else {
731 			fd = 0; /* use stdin */
732 		}
733 
734 		/*
735 		 * Perform the operation
736 		 */
737 		if (mac_cmd) {
738 			rv = do_mac(hSession, &mech, fd, key, &resultbuf,
739 			    &resultlen);
740 		} else {
741 			rv = do_digest(hSession, &mech, fd, &resultbuf,
742 			    &resultlen);
743 		}
744 
745 		if (rv != CKR_OK) {
746 			cryptoerror(LOG_STDERR,
747 			    gettext("crypto operation failed for "
748 			    "file %s: %s\n"),
749 			    filename ? filename : "STDIN",
750 			    pkcs11_strerror(rv));
751 			exitcode = EXIT_FAILURE;
752 			continue;
753 		}
754 
755 		/* if result size has changed, allocate a bigger resulstr buf */
756 		if (resultlen != RESULTLEN) {
757 			resultstrlen = 2 * resultlen + 1;
758 			resultstr = realloc(resultstr, resultstrlen);
759 
760 			if (resultstr == NULL) {
761 				int err = errno;
762 				cryptoerror(LOG_STDERR,
763 				    gettext("realloc: %s\n"), strerror(err));
764 				exitcode =  EXIT_FAILURE;
765 				goto cleanup;
766 			}
767 		}
768 
769 		/* Output the result */
770 		tohexstr(resultbuf, resultlen, resultstr, resultstrlen);
771 
772 		/* Include mechanism name for verbose */
773 		if (vflag)
774 			(void) fprintf(stdout, "%s ", algo_str);
775 
776 		/* Include file name for multiple files, or if verbose */
777 		if (filecount > 1 || (vflag && filecount > 0)) {
778 			(void) fprintf(stdout, "(%s) = ", filename);
779 		}
780 
781 		(void) fprintf(stdout, "%s\n", resultstr);
782 		(void) close(fd);
783 
784 
785 	} while (++i < filecount);
786 
787 
788 	/* clear and free the key */
789 	if (mac_cmd) {
790 		(void) memset(pkeydata, 0, keylen);
791 		free(pkeydata);
792 		pkeydata = NULL;
793 	}
794 
795 cleanup:
796 	if (resultbuf != NULL) {
797 		free(resultbuf);
798 	}
799 
800 	if (resultstr != NULL) {
801 		free(resultstr);
802 	}
803 
804 	if (pSlotList != NULL) {
805 		free(pSlotList);
806 	}
807 
808 	if (!Kflag && key != (CK_OBJECT_HANDLE) 0) {
809 		(void) C_DestroyObject(hSession, key);
810 	}
811 
812 	if (hSession != CK_INVALID_HANDLE)
813 		(void) C_CloseSession(hSession);
814 
815 	(void) C_Finalize(NULL_PTR);
816 
817 	return (exitcode);
818 }
819 
820 /*
821  * do_digest - Compute digest of a file
822  *
823  *  hSession - session
824  *  pmech - ptr to mechanism to be used for digest
825  *  fd  - file descriptor
826  *  pdigest - buffer  where digest result is returned
827  *  pdigestlen - length of digest buffer on input,
828  *               length of result on output
829  */
830 static CK_RV
831 do_digest(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pmech,
832 	int fd, CK_BYTE_PTR *pdigest, CK_ULONG_PTR pdigestlen)
833 {
834 	CK_RV rv;
835 	ssize_t nread;
836 	int saved_errno;
837 
838 	if ((rv = C_DigestInit(hSession, pmech)) != CKR_OK) {
839 		return (rv);
840 	}
841 
842 	while ((nread = read(fd, buf, sizeof (buf))) > 0) {
843 		/* Get the digest */
844 		rv = C_DigestUpdate(hSession, buf, (CK_ULONG)nread);
845 		if (rv != CKR_OK)
846 			return (rv);
847 	}
848 
849 	saved_errno = errno; /* for later use */
850 
851 	/*
852 	 * Perform the C_DigestFinal, even if there is a read error.
853 	 * Otherwise C_DigestInit will return CKR_OPERATION_ACTIVE
854 	 * next time it is called (for another file)
855 	 */
856 
857 	rv = C_DigestFinal(hSession, *pdigest, pdigestlen);
858 
859 	/* result too big to fit? Allocate a bigger buffer */
860 	if (rv == CKR_BUFFER_TOO_SMALL) {
861 		*pdigest = realloc(*pdigest, *pdigestlen);
862 
863 		if (*pdigest == NULL_PTR) {
864 			int err = errno;
865 			cryptoerror(LOG_STDERR,
866 			    gettext("realloc: %s\n"), strerror(err));
867 			return (CKR_HOST_MEMORY);
868 		}
869 
870 		rv = C_DigestFinal(hSession, *pdigest, pdigestlen);
871 	}
872 
873 
874 	/* There was a read error */
875 	if (nread == -1) {
876 		cryptoerror(LOG_STDERR, gettext(
877 		    "error reading file: %s"), strerror(saved_errno));
878 		return (CKR_GENERAL_ERROR);
879 	} else {
880 		return (rv);
881 	}
882 }
883 
884 /*
885  * do_mac - Compute mac of a file
886  *
887  *  hSession - session
888  *  pmech - ptr to mechanism to be used
889  *  fd  - file descriptor
890  *  key - key to be used
891  *  psignature - ptr buffer  where mac result is returned
892  *		returns new buf if current buf is small
893  *  psignaturelen - length of mac buffer on input,
894  *               length of result on output
895  */
896 static CK_RV
897 do_mac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pmech,
898 	int fd, CK_OBJECT_HANDLE key, CK_BYTE_PTR *psignature,
899 	CK_ULONG_PTR psignaturelen)
900 {
901 	CK_RV rv;
902 	ssize_t nread;
903 	int saved_errno;
904 
905 	if ((rv = C_SignInit(hSession, pmech, key)) != CKR_OK) {
906 		return (rv);
907 	}
908 
909 	while ((nread = read(fd, buf, sizeof (buf))) > 0) {
910 		/* Get the MAC */
911 		rv = C_SignUpdate(hSession, buf, (CK_ULONG)nread);
912 		if (rv != CKR_OK)
913 			return (rv);
914 	}
915 
916 	saved_errno = errno; /* for later use */
917 
918 	/*
919 	 * Perform the C_SignFinal, even if there is a read error.
920 	 * Otherwise C_SignInit will return CKR_OPERATION_ACTIVE
921 	 * next time it is called (for another file)
922 	 */
923 
924 	rv = C_SignFinal(hSession, *psignature, psignaturelen);
925 
926 	/* result too big to fit? Allocate a bigger buffer */
927 	if (rv == CKR_BUFFER_TOO_SMALL) {
928 		*psignature = realloc(*psignature, *psignaturelen);
929 
930 		if (*psignature == NULL_PTR) {
931 			int err = errno;
932 			cryptoerror(LOG_STDERR,
933 			    gettext("realloc: %s\n"), strerror(err));
934 			return (CKR_HOST_MEMORY);
935 		}
936 
937 		rv = C_SignFinal(hSession, *psignature, psignaturelen);
938 	}
939 
940 	/* There was a read error */
941 	if (nread == -1) {
942 		cryptoerror(LOG_STDERR, gettext("error reading file: %s"),
943 		    strerror(saved_errno));
944 		return (CKR_GENERAL_ERROR);
945 	} else {
946 		return (rv);
947 	}
948 }
949