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 = ¶ms; 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