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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <pthread.h>
     27 #include <stdlib.h>
     28 #include <errno.h>
     29 #include <sys/crypto/ioctl.h>
     30 #include <security/cryptoki.h>
     31 #include "kernelGlobal.h"
     32 #include "kernelSession.h"
     33 #include "kernelObject.h"
     34 
     35 
     36 CK_RV
     37 C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
     38     CK_OBJECT_HANDLE hKey)
     39 {
     40 
     41 	CK_RV rv;
     42 	kernel_session_t *session_p;
     43 	kernel_object_t	*key_p;
     44 	boolean_t ses_lock_held = B_FALSE;
     45 	crypto_encrypt_init_t encrypt_init;
     46 	crypto_mech_type_t k_mech_type;
     47 	int r;
     48 
     49 	if (!kernel_initialized)
     50 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
     51 
     52 	if (pMechanism == NULL) {
     53 		return (CKR_ARGUMENTS_BAD);
     54 	}
     55 
     56 	/* Get the kernel's internal mechanism number. */
     57 	rv = kernel_mech(pMechanism->mechanism, &k_mech_type);
     58 	if (rv != CKR_OK)
     59 		return (rv);
     60 
     61 	/* Obtain the session pointer. */
     62 	rv = handle2session(hSession, &session_p);
     63 	if (rv != CKR_OK)
     64 		return (rv);
     65 
     66 	/* Obtain the object pointer. */
     67 	HANDLE2OBJECT(hKey, key_p, rv);
     68 	if (rv != CKR_OK) {
     69 		REFRELE(session_p, ses_lock_held);
     70 		return (rv);
     71 	}
     72 
     73 	/* Check to see if key object allows for encryption. */
     74 	if (key_p->is_lib_obj && !(key_p->bool_attr_mask & ENCRYPT_BOOL_ON)) {
     75 		rv = CKR_KEY_TYPE_INCONSISTENT;
     76 		goto clean_exit;
     77 	}
     78 
     79 	(void) pthread_mutex_lock(&session_p->session_mutex);
     80 	ses_lock_held = B_TRUE;
     81 
     82 	/*
     83 	 * This active flag will remain ON until application calls either
     84 	 * C_Encrypt or C_EncryptFinal to actually obtain the final piece
     85 	 * of ciphertext.
     86 	 */
     87 	session_p->encrypt.flags = CRYPTO_OPERATION_ACTIVE;
     88 
     89 	/* set up key data */
     90 	if (!key_p->is_lib_obj) {
     91 		encrypt_init.ei_key.ck_format = CRYPTO_KEY_REFERENCE;
     92 		encrypt_init.ei_key.ck_obj_id = key_p->k_handle;
     93 	} else {
     94 		if (key_p->class == CKO_SECRET_KEY) {
     95 			encrypt_init.ei_key.ck_format = CRYPTO_KEY_RAW;
     96 			encrypt_init.ei_key.ck_data =
     97 			    get_symmetric_key_value(key_p);
     98 			if (encrypt_init.ei_key.ck_data == NULL) {
     99 				rv = CKR_HOST_MEMORY;
    100 				goto clean_exit;
    101 			}
    102 			encrypt_init.ei_key.ck_length =
    103 			    OBJ_SEC(key_p)->sk_value_len << 3;
    104 
    105 		} else if (key_p->key_type == CKK_RSA) {
    106 			if (get_rsa_public_key(key_p, &encrypt_init.ei_key) !=
    107 			    CKR_OK) {
    108 				rv = CKR_HOST_MEMORY;
    109 				goto clean_exit;
    110 			}
    111 		} else {
    112 			rv = CKR_KEY_TYPE_INCONSISTENT;
    113 			goto clean_exit;
    114 		}
    115 	}
    116 
    117 	encrypt_init.ei_session = session_p->k_session;
    118 	session_p->encrypt.mech = *pMechanism;
    119 
    120 	/* Cache this capability value for efficiency */
    121 	if (INPLACE_MECHANISM(session_p->encrypt.mech.mechanism)) {
    122 		session_p->encrypt.flags |= CRYPTO_OPERATION_INPLACE_OK;
    123 	}
    124 	(void) pthread_mutex_unlock(&session_p->session_mutex);
    125 
    126 	ses_lock_held = B_FALSE;
    127 	encrypt_init.ei_mech.cm_type = k_mech_type;
    128 	encrypt_init.ei_mech.cm_param = pMechanism->pParameter;
    129 	encrypt_init.ei_mech.cm_param_len = pMechanism->ulParameterLen;
    130 
    131 	while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_INIT, &encrypt_init)) < 0) {
    132 		if (errno != EINTR)
    133 			break;
    134 	}
    135 	if (r < 0) {
    136 		rv = CKR_FUNCTION_FAILED;
    137 	} else {
    138 		if (encrypt_init.ei_return_value != CRYPTO_SUCCESS) {
    139 			rv = crypto2pkcs11_error_number(
    140 			    encrypt_init.ei_return_value);
    141 		}
    142 	}
    143 
    144 	/* Free memory allocated for decrypt_init.di_key */
    145 	if (key_p->is_lib_obj) {
    146 		if (key_p->class == CKO_SECRET_KEY) {
    147 			free(encrypt_init.ei_key.ck_data);
    148 		} else if (key_p->key_type == CKK_RSA) {
    149 			free_key_attributes(&encrypt_init.ei_key);
    150 		}
    151 	}
    152 
    153 	if (rv != CKR_OK) {
    154 		(void) pthread_mutex_lock(&session_p->session_mutex);
    155 		session_p->encrypt.flags &= ~CRYPTO_OPERATION_ACTIVE;
    156 		ses_lock_held = B_TRUE;
    157 	}
    158 
    159 clean_exit:
    160 	OBJ_REFRELE(key_p);
    161 	REFRELE(session_p, ses_lock_held);
    162 	return (rv);
    163 }
    164 
    165 
    166 CK_RV
    167 C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen,
    168     CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen)
    169 {
    170 
    171 	CK_RV rv;
    172 	kernel_session_t *session_p;
    173 	boolean_t ses_lock_held = B_FALSE;
    174 	boolean_t inplace;
    175 	crypto_encrypt_t encrypt;
    176 	int r;
    177 
    178 	if (!kernel_initialized)
    179 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
    180 
    181 	/* Obtain the session pointer. */
    182 	rv = handle2session(hSession, &session_p);
    183 	if (rv != CKR_OK)
    184 		return (rv);
    185 
    186 	if (pData == NULL) {
    187 		rv = CKR_ARGUMENTS_BAD;
    188 		goto clean_exit;
    189 	}
    190 
    191 	/*
    192 	 * Only check if pulEncryptedDataLen is NULL.
    193 	 * No need to check if pEncryptedData is NULL because
    194 	 * application might just ask for the length of buffer to hold
    195 	 * the ciphertext.
    196 	 */
    197 	if (pulEncryptedDataLen == NULL) {
    198 		rv = CKR_ARGUMENTS_BAD;
    199 		goto clean_exit;
    200 	}
    201 
    202 	(void) pthread_mutex_lock(&session_p->session_mutex);
    203 	ses_lock_held = B_TRUE;
    204 
    205 	/* Application must call C_EncryptInit before calling C_Encrypt. */
    206 	if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) {
    207 		REFRELE(session_p, ses_lock_held);
    208 		return (CKR_OPERATION_NOT_INITIALIZED);
    209 	}
    210 
    211 	/*
    212 	 * C_Encrypt must be called without intervening C_EncryptUpdate
    213 	 * calls.
    214 	 */
    215 	if (session_p->encrypt.flags & CRYPTO_OPERATION_UPDATE) {
    216 		/*
    217 		 * C_Encrypt can not be used to terminate a multi-part
    218 		 * operation, so we'll leave the active encrypt operation
    219 		 * flag on and let the application continue with the
    220 		 * encrypt update operation.
    221 		 */
    222 		REFRELE(session_p, ses_lock_held);
    223 		return (CKR_FUNCTION_FAILED);
    224 	}
    225 
    226 	encrypt.ce_session = session_p->k_session;
    227 
    228 	/*
    229 	 * Certain mechanisms, where the length of the ciphertext is
    230 	 * same as the transformed plaintext, can be optimized
    231 	 * by the kernel into an in-place operation. Unfortunately,
    232 	 * some applications use a ciphertext buffer that is larger
    233 	 * than it needs to be. We fix that here.
    234 	 */
    235 	inplace = (session_p->encrypt.flags & CRYPTO_OPERATION_INPLACE_OK) != 0;
    236 	if (ulDataLen < *pulEncryptedDataLen && inplace) {
    237 		encrypt.ce_encrlen = ulDataLen;
    238 	} else {
    239 		encrypt.ce_encrlen = *pulEncryptedDataLen;
    240 	}
    241 	(void) pthread_mutex_unlock(&session_p->session_mutex);
    242 	ses_lock_held = B_FALSE;
    243 
    244 	encrypt.ce_datalen = ulDataLen;
    245 	encrypt.ce_databuf = (char *)pData;
    246 	encrypt.ce_encrbuf = (char *)pEncryptedData;
    247 	encrypt.ce_flags =
    248 	    ((inplace && (pEncryptedData != NULL)) ||
    249 	    (pData == pEncryptedData)) &&
    250 	    (encrypt.ce_encrlen == encrypt.ce_datalen) ?
    251 	    CRYPTO_INPLACE_OPERATION : 0;
    252 
    253 	while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT, &encrypt)) < 0) {
    254 		if (errno != EINTR)
    255 			break;
    256 	}
    257 	if (r < 0) {
    258 		rv = CKR_FUNCTION_FAILED;
    259 	} else {
    260 		rv = crypto2pkcs11_error_number(encrypt.ce_return_value);
    261 	}
    262 
    263 	if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL)
    264 		*pulEncryptedDataLen = encrypt.ce_encrlen;
    265 
    266 	if ((rv == CKR_BUFFER_TOO_SMALL) ||
    267 	    (rv == CKR_OK && pEncryptedData == NULL)) {
    268 		/*
    269 		 * We will not terminate the active encrypt operation flag,
    270 		 * when the application-supplied buffer is too small, or
    271 		 * the application asks for the length of buffer to hold
    272 		 * the ciphertext.
    273 		 */
    274 		REFRELE(session_p, ses_lock_held);
    275 		return (rv);
    276 	}
    277 
    278 clean_exit:
    279 	/*
    280 	 * Terminates the active encrypt operation.
    281 	 * Application needs to call C_EncryptInit again for next
    282 	 * encrypt operation.
    283 	 */
    284 	(void) pthread_mutex_lock(&session_p->session_mutex);
    285 	session_p->encrypt.flags = 0;
    286 	ses_lock_held = B_TRUE;
    287 	REFRELE(session_p, ses_lock_held);
    288 
    289 	return (rv);
    290 }
    291 
    292 
    293 CK_RV
    294 C_EncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
    295     CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
    296     CK_ULONG_PTR pulEncryptedPartLen)
    297 {
    298 
    299 	CK_RV rv;
    300 	kernel_session_t *session_p;
    301 	boolean_t ses_lock_held = B_FALSE;
    302 	boolean_t inplace;
    303 	crypto_encrypt_update_t encrypt_update;
    304 	int r;
    305 
    306 	if (!kernel_initialized)
    307 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
    308 
    309 	/* Obtain the session pointer. */
    310 	rv = handle2session(hSession, &session_p);
    311 	if (rv != CKR_OK)
    312 		return (rv);
    313 
    314 	if (pPart == NULL) {
    315 		rv = CKR_ARGUMENTS_BAD;
    316 		goto clean_exit;
    317 	}
    318 
    319 	/*
    320 	 * Only check if pulEncryptedPartLen is NULL.
    321 	 * No need to check if pEncryptedPart is NULL because
    322 	 * application might just ask for the length of buffer to hold
    323 	 * the ciphertext.
    324 	 */
    325 	if (pulEncryptedPartLen == NULL) {
    326 		rv = CKR_ARGUMENTS_BAD;
    327 		goto clean_exit;
    328 	}
    329 
    330 	(void) pthread_mutex_lock(&session_p->session_mutex);
    331 	ses_lock_held = B_TRUE;
    332 
    333 	/*
    334 	 * Application must call C_EncryptInit before calling
    335 	 * C_EncryptUpdate.
    336 	 */
    337 	if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) {
    338 		REFRELE(session_p, ses_lock_held);
    339 		return (CKR_OPERATION_NOT_INITIALIZED);
    340 	}
    341 
    342 	session_p->encrypt.flags |= CRYPTO_OPERATION_UPDATE;
    343 
    344 	encrypt_update.eu_session = session_p->k_session;
    345 	(void) pthread_mutex_unlock(&session_p->session_mutex);
    346 	ses_lock_held = B_FALSE;
    347 
    348 	encrypt_update.eu_datalen = ulPartLen;
    349 	encrypt_update.eu_databuf = (char *)pPart;
    350 	encrypt_update.eu_encrlen = *pulEncryptedPartLen;
    351 	encrypt_update.eu_encrbuf = (char *)pEncryptedPart;
    352 
    353 	inplace = (session_p->encrypt.flags & CRYPTO_OPERATION_INPLACE_OK) != 0;
    354 	encrypt_update.eu_flags =
    355 	    ((inplace && (pEncryptedPart != NULL)) ||
    356 	    (pPart == pEncryptedPart)) &&
    357 	    (encrypt_update.eu_encrlen == encrypt_update.eu_datalen) ?
    358 	    CRYPTO_INPLACE_OPERATION : 0;
    359 
    360 	while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_UPDATE,
    361 	    &encrypt_update)) < 0) {
    362 		if (errno != EINTR)
    363 			break;
    364 	}
    365 	if (r < 0) {
    366 		rv = CKR_FUNCTION_FAILED;
    367 	} else {
    368 		rv = crypto2pkcs11_error_number(
    369 		    encrypt_update.eu_return_value);
    370 	}
    371 
    372 	/*
    373 	 * If CKR_OK or CKR_BUFFER_TOO_SMALL, set the output length.
    374 	 * We don't terminate the current encryption operation.
    375 	 */
    376 	if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL) {
    377 		*pulEncryptedPartLen = encrypt_update.eu_encrlen;
    378 		REFRELE(session_p, ses_lock_held);
    379 		return (rv);
    380 	}
    381 
    382 clean_exit:
    383 	/*
    384 	 * After an error occurred, terminate the current encrypt
    385 	 * operation by resetting the active and update flags.
    386 	 */
    387 	(void) pthread_mutex_lock(&session_p->session_mutex);
    388 	session_p->encrypt.flags = 0;
    389 	ses_lock_held = B_TRUE;
    390 	REFRELE(session_p, ses_lock_held);
    391 
    392 	return (rv);
    393 }
    394 
    395 
    396 CK_RV
    397 C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart,
    398     CK_ULONG_PTR pulLastEncryptedPartLen)
    399 {
    400 
    401 	CK_RV rv;
    402 	kernel_session_t *session_p;
    403 	boolean_t ses_lock_held = B_FALSE;
    404 	crypto_encrypt_final_t encrypt_final;
    405 	int r;
    406 
    407 	if (!kernel_initialized)
    408 		return (CKR_CRYPTOKI_NOT_INITIALIZED);
    409 
    410 	/* Obtain the session pointer. */
    411 	rv = handle2session(hSession, &session_p);
    412 	if (rv != CKR_OK)
    413 		return (rv);
    414 
    415 	if (pulLastEncryptedPartLen == NULL) {
    416 		rv = CKR_ARGUMENTS_BAD;
    417 		goto clean_exit;
    418 	}
    419 
    420 	(void) pthread_mutex_lock(&session_p->session_mutex);
    421 	ses_lock_held = B_TRUE;
    422 
    423 	/*
    424 	 * Application must call C_EncryptInit before calling
    425 	 * C_EncryptFinal.
    426 	 */
    427 	if (!(session_p->encrypt.flags & CRYPTO_OPERATION_ACTIVE)) {
    428 		REFRELE(session_p, ses_lock_held);
    429 		return (CKR_OPERATION_NOT_INITIALIZED);
    430 	}
    431 
    432 	encrypt_final.ef_session = session_p->k_session;
    433 	(void) pthread_mutex_unlock(&session_p->session_mutex);
    434 	ses_lock_held = B_FALSE;
    435 
    436 	encrypt_final.ef_encrlen = *pulLastEncryptedPartLen;
    437 	encrypt_final.ef_encrbuf = (char *)pLastEncryptedPart;
    438 
    439 	while ((r = ioctl(kernel_fd, CRYPTO_ENCRYPT_FINAL,
    440 	    &encrypt_final)) < 0) {
    441 		if (errno != EINTR)
    442 			break;
    443 	}
    444 	if (r < 0) {
    445 		rv = CKR_FUNCTION_FAILED;
    446 	} else {
    447 		rv = crypto2pkcs11_error_number(encrypt_final.ef_return_value);
    448 	}
    449 
    450 	if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL)
    451 		*pulLastEncryptedPartLen = encrypt_final.ef_encrlen;
    452 
    453 	if ((rv == CKR_BUFFER_TOO_SMALL) ||
    454 	    (rv == CKR_OK && pLastEncryptedPart == NULL)) {
    455 		/*
    456 		 * We will not terminate the active encrypt operation flag,
    457 		 * when the application-supplied buffer is too small, or
    458 		 * the application asks for the length of buffer to hold
    459 		 * the ciphertext.
    460 		 */
    461 		REFRELE(session_p, ses_lock_held);
    462 		return (rv);
    463 	}
    464 
    465 clean_exit:
    466 	/* Terminates the active encrypt operation. */
    467 	(void) pthread_mutex_lock(&session_p->session_mutex);
    468 	session_p->encrypt.flags = 0;
    469 	ses_lock_held = B_TRUE;
    470 	REFRELE(session_p, ses_lock_held);
    471 
    472 	return (rv);
    473 }
    474