xref: /illumos-gate/usr/src/lib/pkcs11/libpkcs11/common/pkcs11SUNWExtensions.c (revision 89b2a9fbeabf42fa54594df0e5927bcc50a07cc9)
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 2008 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  * Solaris specific functions to reduce the initialization
30  * overhead of using PKCS #11
31  */
32 
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <security/cryptoki.h>
36 #include <assert.h>
37 #include <cryptoutil.h>
38 #include <pkcs11Global.h>
39 
40 static CK_OBJECT_CLASS objclass = CKO_SECRET_KEY;
41 static CK_BBOOL falsevalue = FALSE;
42 static CK_BBOOL truevalue = TRUE;
43 
44 #define	NUM_SECRETKEY_ATTRS	12
45 
46 typedef struct _ATTRTYPE_MECHINFO_MAPPING {
47 	CK_ATTRIBUTE_TYPE attr;
48 	CK_FLAGS	flag;
49 } ATTRTYPE_MECHINFO_MAPPING;
50 
51 /* possible attribute types for creating key */
52 ATTRTYPE_MECHINFO_MAPPING mapping[] = {
53 	{CKA_ENCRYPT, CKF_ENCRYPT},
54 	{CKA_DECRYPT, CKF_DECRYPT},
55 	{CKA_SIGN, CKF_SIGN},
56 	{CKA_VERIFY, CKF_VERIFY},
57 	{CKA_WRAP, CKF_WRAP},
58 	{CKA_UNWRAP, CKF_UNWRAP}
59 };
60 
61 
62 /*
63  * List of mechanisms that only supports asymmetric key operations
64  * in PKCS #11 V2.11
65  */
66 CK_MECHANISM_TYPE asymmetric_mechs[] = {
67 	CKM_RSA_PKCS_KEY_PAIR_GEN, CKM_RSA_PKCS, CKM_RSA_9796, CKM_RSA_X_509,
68 	CKM_RSA_PKCS_OAEP, CKM_RSA_X9_31_KEY_PAIR_GEN, CKM_RSA_X9_31,
69 	CKM_RSA_PKCS_PSS, CKM_DSA_KEY_PAIR_GEN, CKM_DSA, CKM_DSA_SHA1,
70 	CKM_DSA_PARAMETER_GEN, CKM_ECDSA_KEY_PAIR_GEN, CKM_EC_KEY_PAIR_GEN,
71 	CKM_ECDSA, CKM_ECDSA_SHA1, CKM_ECDH1_DERIVE,
72 	CKM_ECDH1_COFACTOR_DERIVE, CKM_ECMQV_DERIVE
73 };
74 
75 
76 typedef struct _KEY_TYPE_SIZE_MAPPING {
77 	CK_KEY_TYPE type;
78 	CK_ULONG len;
79 } KEY_TYPE_SIZE_MAPPING;
80 
81 /*
82  * List of secret key types that have fixed sizes and their sizes.
83  * These key types do not allow CKA_VALUE_LEN for key generation.
84  * The sizes are in bytes.
85  *
86  * Discrete-sized keys, such as AES and Twofish, and variable sized
87  * keys, such as Blowfish, are not in this list.
88  */
89 KEY_TYPE_SIZE_MAPPING fixed_size_secrets[] = {
90 	{CKK_DES, 8}, {CKK_DES2, 16}, {CKK_DES3, 24}, {CKK_IDEA, 16},
91 	{CKK_CDMF, 8}, {CKK_SKIPJACK, 12}, {CKK_BATON, 40}, {CKK_JUNIPER, 40}
92 };
93 
94 /*
95  * match_mech is an example of many possible criteria functions.
96  * It matches the given mech type (in args) with the slot's mech info.
97  * If no match is found, pkcs11_GetCriteriaSession is asked to return
98  * CKR_MECHANISM_INVALID.
99  */
100 boolean_t
101 match_mech(CK_SLOT_ID slot_id, void *args, CK_RV *rv)
102 {
103 	CK_MECHANISM_INFO mech_info;
104 	CK_MECHANISM_TYPE mech = (CK_MECHANISM_TYPE)args;
105 
106 	*rv = CKR_MECHANISM_INVALID;
107 	return (C_GetMechanismInfo(slot_id, mech, &mech_info) == CKR_OK);
108 }
109 
110 /*
111  * pkcs11_GetCriteriaSession will initialize the framework and do all
112  * the necessary work of calling C_GetSlotList(), C_GetMechanismInfo()
113  * C_OpenSession() to create a session that meets all the criteria in
114  * the given function pointer.
115  *
116  * The criteria function must return a boolean value of true or false.
117  * The arguments to the function are the current slot id, an opaque
118  * args value that is passed through to the function, and the error
119  * value pkcs11_GetCriteriaSession should return if no slot id meets the
120  * criteria.
121  *
122  * If the function is called multiple times, it will return a new session
123  * without reinitializing the framework.
124  */
125 CK_RV
126 pkcs11_GetCriteriaSession(
127     boolean_t (*criteria)(CK_SLOT_ID slot_id, void *args, CK_RV *rv),
128     void *args, CK_SESSION_HANDLE_PTR hSession)
129 {
130 	CK_RV rv;
131 	CK_ULONG slotcount;
132 	CK_SLOT_ID_PTR slot_list;
133 	CK_SLOT_ID slot_id;
134 	CK_ULONG i;
135 
136 	if (hSession == NULL || criteria == NULL) {
137 		return (CKR_ARGUMENTS_BAD);
138 	}
139 
140 	/* initialize PKCS #11 */
141 	if (!pkcs11_initialized) {
142 		rv = C_Initialize(NULL);
143 		if ((rv != CKR_OK) &&
144 		    (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
145 			return (rv);
146 		}
147 	}
148 
149 	/* get slot count */
150 	rv = C_GetSlotList(0, NULL, &slotcount);
151 	if (rv != CKR_OK) {
152 		return (rv);
153 	}
154 
155 	if (slotcount == 0) {
156 		return (CKR_FUNCTION_FAILED);
157 	}
158 
159 
160 	/* allocate memory for slot list */
161 	slot_list = malloc(slotcount * sizeof (CK_SLOT_ID));
162 	if (slot_list == NULL) {
163 		return (CKR_HOST_MEMORY);
164 	}
165 
166 	if ((rv = C_GetSlotList(0, slot_list, &slotcount)) != CKR_OK) {
167 		free(slot_list);
168 		return (rv);
169 	}
170 
171 	/* find slot with matching criteria */
172 	for (i = 0; i < slotcount; i++) {
173 		slot_id = slot_list[i];
174 		if ((*criteria)(slot_id, args, &rv)) {
175 			break;
176 		}
177 	}
178 
179 	if (i == slotcount) {
180 		/* no matching slot found */
181 		free(slot_list);
182 		return (rv);	/* this rv is from the criteria function */
183 	}
184 
185 	rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL,
186 	    NULL, hSession);
187 
188 	free(slot_list);
189 	return (rv);
190 }
191 
192 /*
193  * SUNW_C_GetMechSession will initialize the framework and do all
194  * of the neccessary work of calling C_GetSlotList(), C_GetMechanismInfo()
195  * C_OpenSession() to create a session capable of providing the requested
196  * mechanism.
197  *
198  * If the function is called multiple times, it will return a new session
199  * without reinitializing the framework.
200  */
201 CK_RV
202 SUNW_C_GetMechSession(CK_MECHANISM_TYPE mech, CK_SESSION_HANDLE_PTR hSession)
203 {
204 	/*
205 	 * All the code in this function can be replaced with one line:
206 	 *
207 	 * return (pkcs11_GetCriteriaSession(match_mech, (void *)mech,
208 	 *	hSession));
209 	 *
210 	 */
211 	CK_RV rv;
212 	CK_ULONG slotcount;
213 	CK_SLOT_ID_PTR slot_list;
214 	CK_SLOT_ID slot_id;
215 	CK_MECHANISM_INFO mech_info;
216 	CK_ULONG i;
217 
218 	if (hSession == NULL) {
219 		return (CKR_ARGUMENTS_BAD);
220 	}
221 
222 	/* initialize PKCS #11 */
223 	if (!pkcs11_initialized) {
224 		rv = C_Initialize(NULL);
225 		if ((rv != CKR_OK) &&
226 		    (rv != CKR_CRYPTOKI_ALREADY_INITIALIZED)) {
227 			return (rv);
228 		}
229 	}
230 
231 	/* get slot count */
232 	rv = C_GetSlotList(0, NULL, &slotcount);
233 	if (rv != CKR_OK) {
234 		return (rv);
235 	}
236 
237 	if (slotcount == 0) {
238 		return (CKR_FUNCTION_FAILED);
239 	}
240 
241 
242 	/* allocate memory for slot list */
243 	slot_list = malloc(slotcount * sizeof (CK_SLOT_ID));
244 	if (slot_list == NULL) {
245 		return (CKR_HOST_MEMORY);
246 	}
247 
248 	if ((rv = C_GetSlotList(0, slot_list, &slotcount)) != CKR_OK) {
249 		free(slot_list);
250 		return (rv);
251 	}
252 
253 	/* find slot with matching mechanism */
254 	for (i = 0; i < slotcount; i++) {
255 		slot_id = slot_list[i];
256 		if (C_GetMechanismInfo(slot_id, mech, &mech_info) == CKR_OK) {
257 			/* found mechanism */
258 			break;
259 		}
260 	}
261 
262 	if (i == slotcount) {
263 		/* no matching mechanism found */
264 		free(slot_list);
265 		return (CKR_MECHANISM_INVALID);
266 	}
267 
268 	rv = C_OpenSession(slot_id, CKF_SERIAL_SESSION, NULL,
269 	    NULL, hSession);
270 
271 	free(slot_list);
272 	return (rv);
273 }
274 
275 /*
276  * SUNW_C_KeyToObject creates a secret key object for the given
277  * mechanism from the rawkey data.
278  */
279 CK_RV
280 SUNW_C_KeyToObject(CK_SESSION_HANDLE hSession, CK_MECHANISM_TYPE mech,
281     const void *rawkey, size_t rawkey_len, CK_OBJECT_HANDLE_PTR obj)
282 {
283 
284 	CK_RV rv;
285 	CK_SESSION_INFO session_info;
286 	CK_SLOT_ID slot_id;
287 	CK_MECHANISM_INFO mech_info;
288 	CK_ULONG i, j;
289 	CK_KEY_TYPE keytype;
290 	CK_ULONG num_asym_mechs, num_mapping;
291 
292 	/* template for creating generic secret key object */
293 	CK_ATTRIBUTE template[NUM_SECRETKEY_ATTRS];
294 
295 	if ((hSession == NULL) || (obj == NULL) ||
296 	    (rawkey == NULL) || (rawkey_len == 0)) {
297 		return (CKR_ARGUMENTS_BAD);
298 	}
299 
300 	/*
301 	 * Check to make sure mechanism type is not for asymmetric key
302 	 * only operations.  This function is only applicable to
303 	 * generating secret key.
304 	 */
305 	num_asym_mechs = sizeof (asymmetric_mechs) / sizeof (CK_MECHANISM_TYPE);
306 	for (i = 0; i < num_asym_mechs; i++) {
307 		if (mech == asymmetric_mechs[i]) {
308 			return (CKR_MECHANISM_INVALID);
309 		}
310 	}
311 
312 	rv = C_GetSessionInfo(hSession, &session_info);
313 	if (rv != CKR_OK) {
314 		return (rv);
315 	}
316 
317 	slot_id = session_info.slotID;
318 
319 	i = 0;
320 	template[i].type = CKA_CLASS;
321 	template[i].pValue = &objclass;
322 	template[i].ulValueLen = sizeof (objclass);
323 	i++;
324 
325 	/* get the key type for this mechanism */
326 	if ((rv = pkcs11_mech2keytype(mech, &keytype)) != CKR_OK) {
327 		return (rv);
328 	}
329 
330 	assert(i < NUM_SECRETKEY_ATTRS);
331 	template[i].type = CKA_KEY_TYPE;
332 	template[i].pValue = &keytype;
333 	template[i].ulValueLen = sizeof (keytype);
334 	i++;
335 
336 	rv = C_GetMechanismInfo(slot_id, mech, &mech_info);
337 	if (rv != CKR_OK) {
338 		return (rv);
339 	}
340 
341 	/* set the attribute type flag on object based on mechanism */
342 	num_mapping = sizeof (mapping) / sizeof (ATTRTYPE_MECHINFO_MAPPING);
343 	for (j = 0; j < num_mapping; j++) {
344 		assert(i < NUM_SECRETKEY_ATTRS);
345 		template[i].type = mapping[j].attr;
346 		template[i].ulValueLen = sizeof (falsevalue);
347 		if (mech_info.flags & ((mapping[j]).flag)) {
348 			template[i].pValue = &truevalue;
349 		} else {
350 			template[i].pValue = &falsevalue;
351 		}
352 		i++;
353 	}
354 
355 	assert(i < NUM_SECRETKEY_ATTRS);
356 	template[i].type = CKA_TOKEN;
357 	template[i].pValue = &falsevalue;
358 	template[i].ulValueLen = sizeof (falsevalue);
359 	i++;
360 
361 	assert(i < NUM_SECRETKEY_ATTRS);
362 	template[i].type = CKA_VALUE;
363 	template[i].pValue = (CK_VOID_PTR)rawkey;
364 	template[i].ulValueLen = (CK_ULONG)rawkey_len;
365 	i++;
366 
367 	rv = C_CreateObject(hSession, template, i, obj);
368 	return (rv);
369 }
370 
371 
372 /*
373  * pkcs11_PasswdToPBKD2Object will create a secret key from the given string
374  * (e.g. passphrase) using PKCS#5 Password-Based Key Derivation Function 2
375  * (PBKD2).
376  *
377  * Session must be open.  Salt and iterations use defaults.
378  */
379 CK_RV
380 pkcs11_PasswdToPBKD2Object(CK_SESSION_HANDLE hSession, char *passphrase,
381     size_t passphrase_len, void *salt, size_t salt_len, CK_ULONG iterations,
382     CK_KEY_TYPE key_type, CK_ULONG key_len, CK_FLAGS key_flags,
383     CK_OBJECT_HANDLE_PTR obj)
384 {
385 	CK_RV rv;
386 	CK_PKCS5_PBKD2_PARAMS params;
387 	CK_MECHANISM mechanism;
388 	CK_KEY_TYPE asym_key_type;
389 	CK_ULONG i, j, num_asym_mechs, num_fixed_secs, num_mapping;
390 	CK_ATTRIBUTE template[NUM_SECRETKEY_ATTRS];
391 
392 	if (hSession == NULL || obj == NULL ||
393 	    passphrase == NULL || passphrase_len == 0 ||
394 	    iterations == 0UL) {
395 		return (CKR_ARGUMENTS_BAD);
396 	}
397 
398 	/*
399 	 * Check to make sure key type is not asymmetric.  This function
400 	 * is only applicable to generating secret key.
401 	 */
402 	num_asym_mechs = sizeof (asymmetric_mechs) / sizeof (CK_MECHANISM_TYPE);
403 	for (i = 0; i < num_asym_mechs; i++) {
404 		rv = pkcs11_mech2keytype(asymmetric_mechs[i], &asym_key_type);
405 		assert(rv == CKR_OK);
406 		if (key_type == asym_key_type) {
407 			return (CKR_KEY_TYPE_INCONSISTENT);
408 		}
409 	}
410 
411 	/*
412 	 * Key length must either be 0 or the correct size for PBKD of
413 	 * fixed-size secret keys.  However, underlying key generation
414 	 * cannot have CKA_VALUE_LEN set for the key length attribute.
415 	 */
416 	num_fixed_secs =
417 	    sizeof (fixed_size_secrets) / sizeof (KEY_TYPE_SIZE_MAPPING);
418 	for (i = 0; i < num_fixed_secs; i++) {
419 		if (key_type == fixed_size_secrets[i].type) {
420 			if (key_len == fixed_size_secrets[i].len) {
421 				key_len = 0;
422 			}
423 			if (key_len == 0) {
424 				break;
425 			}
426 			return (CKR_KEY_SIZE_RANGE);
427 		}
428 	}
429 
430 	if (salt == NULL || salt_len == 0) {
431 		params.saltSource = 0;
432 		params.pSaltSourceData = NULL;
433 		params.ulSaltSourceDataLen = 0;
434 	} else {
435 		params.saltSource = CKZ_SALT_SPECIFIED;
436 		params.pSaltSourceData = salt;
437 		params.ulSaltSourceDataLen = salt_len;
438 	}
439 	params.iterations = iterations;
440 	params.prf = CKP_PKCS5_PBKD2_HMAC_SHA1;
441 	params.pPrfData = NULL;
442 	params.ulPrfDataLen = 0;
443 	params.pPassword = (CK_UTF8CHAR_PTR)passphrase;
444 	params.ulPasswordLen = (CK_ULONG_PTR)&passphrase_len;
445 	/*
446 	 * PKCS#11 spec error, ulPasswordLen should have been pulPasswordLen,
447 	 * or its type should have been CK_ULONG instead of CK_ULONG_PTR,
448 	 * but it's legacy now
449 	 */
450 
451 	mechanism.mechanism = CKM_PKCS5_PBKD2;
452 	mechanism.pParameter = &params;
453 	mechanism.ulParameterLen = sizeof (params);
454 
455 	i = 0;
456 	template[i].type = CKA_CLASS;
457 	template[i].pValue = &objclass;
458 	template[i].ulValueLen = sizeof (objclass);
459 	i++;
460 
461 	assert(i < NUM_SECRETKEY_ATTRS);
462 	template[i].type = CKA_KEY_TYPE;
463 	template[i].pValue = &key_type;
464 	template[i].ulValueLen = sizeof (key_type);
465 	i++;
466 
467 	assert(i < NUM_SECRETKEY_ATTRS);
468 	template[i].type = CKA_TOKEN;
469 	template[i].pValue = &falsevalue;
470 	template[i].ulValueLen = sizeof (falsevalue);
471 	i++;
472 
473 	if (key_len != 0) {
474 		assert(i < NUM_SECRETKEY_ATTRS);
475 		template[i].type = CKA_VALUE_LEN;
476 		template[i].pValue = &key_len;
477 		template[i].ulValueLen = sizeof (key_len);
478 		i++;
479 	}
480 
481 	/*
482 	 * C_GenerateKey may not implicitly set capability attributes,
483 	 * e.g. CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, ...
484 	 */
485 	num_mapping = sizeof (mapping) / sizeof (ATTRTYPE_MECHINFO_MAPPING);
486 	for (j = 0; j < num_mapping; j++) {
487 		assert(i < NUM_SECRETKEY_ATTRS);
488 		template[i].type = mapping[j].attr;
489 		template[i].pValue = (key_flags & ((mapping[j]).flag)) ?
490 		    &truevalue : &falsevalue;
491 		template[i].ulValueLen = sizeof (falsevalue);
492 		i++;
493 	}
494 
495 	rv = C_GenerateKey(hSession, &mechanism, template, i, obj);
496 	return (rv);
497 }
498 
499 /*
500  * pkcs11_ObjectToKey gets the rawkey data from a secret key object.
501  * The caller is responsible to free the allocated rawkey data.
502  *
503  * Optionally the object can be destroyed after the value is retrieved.
504  * As an example, after using pkcs11_PasswdToPBKD2Object() to create a
505  * secret key object from a passphrase, an app may call pkcs11_ObjectToKey
506  * to get the rawkey data.  The intermediate object may no longer be needed
507  * and should be destroyed.
508  */
509 CK_RV
510 pkcs11_ObjectToKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE obj,
511     void **rawkey, size_t *rawkey_len, boolean_t destroy_obj)
512 {
513 	CK_RV rv;
514 	CK_ATTRIBUTE template;
515 
516 	if (hSession == NULL || rawkey == NULL || rawkey_len == NULL ||
517 	    *rawkey_len == 0) {
518 		return (CKR_ARGUMENTS_BAD);
519 	}
520 
521 	template.type = CKA_VALUE;
522 	template.pValue = NULL;
523 	template.ulValueLen = 0;
524 
525 	/* First get the size of the rawkey */
526 	rv = C_GetAttributeValue(hSession, obj, &template, 1);
527 	if (rv != CKR_OK) {
528 		return (rv);
529 	}
530 
531 	template.pValue = malloc(template.ulValueLen);
532 	if (template.pValue == NULL) {
533 		return (CKR_HOST_MEMORY);
534 	}
535 
536 	/* Then get the rawkey data */
537 	rv = C_GetAttributeValue(hSession, obj, &template, 1);
538 	if (rv != CKR_OK) {
539 		free(template.pValue);
540 		return (rv);
541 	}
542 
543 	if (destroy_obj) {
544 		/*
545 		 * Could have asserted rv == CKR_OK, making threaded
546 		 * apps that share objects see stars.  Here mercy is ok.
547 		 */
548 		(void) C_DestroyObject(hSession, obj);
549 	}
550 
551 	*rawkey = template.pValue;
552 	*rawkey_len = template.ulValueLen;
553 
554 	return (CKR_OK);
555 }
556 
557 /*
558  * pkcs11_PasswdToKey will create PKCS#5 PBKD2 rawkey data from the
559  * given passphrase.  The caller is responsible to free the allocated
560  * rawkey data.
561  */
562 CK_RV
563 pkcs11_PasswdToKey(CK_SESSION_HANDLE hSession, char *passphrase,
564     size_t passphrase_len, void *salt, size_t salt_len, CK_KEY_TYPE key_type,
565     CK_ULONG key_len, void **rawkey, size_t *rawkey_len)
566 {
567 	CK_RV rv;
568 	CK_OBJECT_HANDLE obj;
569 
570 	rv = pkcs11_PasswdToPBKD2Object(hSession, passphrase, passphrase_len,
571 	    salt, salt_len, CK_PKCS5_PBKD2_ITERATIONS, key_type, key_len, 0,
572 	    &obj);
573 	if (rv != CKR_OK)
574 		return (rv);
575 	rv = pkcs11_ObjectToKey(hSession, obj, rawkey, rawkey_len, B_TRUE);
576 	return (rv);
577 }
578