Home | History | Annotate | Download | only in common
      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