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