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