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 #include <pthread.h>
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <strings.h>
     30 #include <sys/types.h>
     31 #include <security/cryptoki.h>
     32 #include "softSession.h"
     33 #include "softObject.h"
     34 #include "softCrypt.h"
     35 #include <blowfish_impl.h>
     36 
     37 CK_RV
     38 soft_blowfish_crypt_init_common(soft_session_t *session_p,
     39     CK_MECHANISM_PTR pMechanism, soft_object_t *key_p, boolean_t encrypt) {
     40 
     41 	size_t size;
     42 	soft_blowfish_ctx_t *soft_blowfish_ctx;
     43 
     44 	soft_blowfish_ctx = calloc(1, sizeof (soft_blowfish_ctx_t));
     45 	if (soft_blowfish_ctx == NULL) {
     46 		return (CKR_HOST_MEMORY);
     47 	}
     48 
     49 	soft_blowfish_ctx->key_sched = blowfish_alloc_keysched(&size, 0);
     50 
     51 	if (soft_blowfish_ctx->key_sched == NULL) {
     52 		free(soft_blowfish_ctx);
     53 		return (CKR_HOST_MEMORY);
     54 	}
     55 
     56 	soft_blowfish_ctx->keysched_len = size;
     57 
     58 	(void) pthread_mutex_lock(&session_p->session_mutex);
     59 	if (encrypt) {
     60 		/* Called by C_EncryptInit */
     61 		session_p->encrypt.context = soft_blowfish_ctx;
     62 		session_p->encrypt.mech.mechanism = pMechanism->mechanism;
     63 	} else {
     64 		/* Called by C_DecryptInit */
     65 		session_p->decrypt.context = soft_blowfish_ctx;
     66 		session_p->decrypt.mech.mechanism = pMechanism->mechanism;
     67 	}
     68 	(void) pthread_mutex_unlock(&session_p->session_mutex);
     69 
     70 	/*
     71 	 * If this is a non-sensitive key and it does NOT have
     72 	 * a key schedule yet, then allocate one and expand it.
     73 	 * Otherwise, if it's a non-sensitive key, and it DOES have
     74 	 * a key schedule already attached to it, just copy the
     75 	 * pre-expanded schedule to the context and avoid the
     76 	 * extra key schedule expansion operation.
     77 	 */
     78 	if (!(key_p->bool_attr_mask & SENSITIVE_BOOL_ON)) {
     79 		if (OBJ_KEY_SCHED(key_p) == NULL) {
     80 			void *ks;
     81 
     82 			(void) pthread_mutex_lock(&key_p->object_mutex);
     83 			if (OBJ_KEY_SCHED(key_p) == NULL) {
     84 				ks = blowfish_alloc_keysched(&size, 0);
     85 				if (ks == NULL) {
     86 					(void) pthread_mutex_unlock(
     87 					    &key_p->object_mutex);
     88 					free(soft_blowfish_ctx);
     89 					return (CKR_HOST_MEMORY);
     90 				}
     91 
     92 				blowfish_init_keysched(OBJ_SEC_VALUE(key_p),
     93 				    (OBJ_SEC_VALUE_LEN(key_p) * 8), ks);
     94 
     95 				OBJ_KEY_SCHED_LEN(key_p) = size;
     96 				OBJ_KEY_SCHED(key_p) = ks;
     97 			}
     98 			(void) pthread_mutex_unlock(&key_p->object_mutex);
     99 		}
    100 		(void) memcpy(soft_blowfish_ctx->key_sched,
    101 		    OBJ_KEY_SCHED(key_p), OBJ_KEY_SCHED_LEN(key_p));
    102 		soft_blowfish_ctx->keysched_len = OBJ_KEY_SCHED_LEN(key_p);
    103 
    104 	} else {
    105 		/*
    106 		 * Initialize key schedule for Blowfish.
    107 		 * blowfish_init_keysched() requires key length in bits.
    108 		 */
    109 		blowfish_init_keysched(OBJ_SEC_VALUE(key_p),
    110 		    (OBJ_SEC_VALUE_LEN(key_p) * 8),
    111 		    soft_blowfish_ctx->key_sched);
    112 	}
    113 	return (CKR_OK);
    114 }
    115 
    116 
    117 /*
    118  * soft_blowfish_encrypt_common()
    119  *
    120  * Arguments:
    121  *      session_p:	pointer to soft_session_t struct
    122  *	pData:		pointer to the input data to be encrypted
    123  *	ulDataLen:	length of the input data
    124  *	pEncrypted:	pointer to the output data after encryption
    125  *	pulEncryptedLen: pointer to the length of the output data
    126  *	update:		boolean flag indicates caller is soft_encrypt
    127  *			or soft_encrypt_update
    128  *
    129  * Description:
    130  *      This function calls the corresponding encrypt routine based
    131  *	on the mechanism.
    132  *
    133  * Returns:
    134  *      CKR_OK: success
    135  *      CKR_BUFFER_TOO_SMALL: the output buffer provided by application
    136  *			      is too small
    137  *	CKR_FUNCTION_FAILED: encrypt function failed
    138  *	CKR_DATA_LEN_RANGE: the input data is not a multiple of blocksize
    139  */
    140 CK_RV
    141 soft_blowfish_encrypt_common(soft_session_t *session_p, CK_BYTE_PTR pData,
    142     CK_ULONG ulDataLen, CK_BYTE_PTR pEncrypted, CK_ULONG_PTR pulEncryptedLen,
    143     boolean_t update) {
    144 
    145 	int rc = 0;
    146 	CK_RV rv = CKR_OK;
    147 	soft_blowfish_ctx_t *soft_blowfish_ctx =
    148 	    (soft_blowfish_ctx_t *)session_p->encrypt.context;
    149 	blowfish_ctx_t *blowfish_ctx;
    150 	CK_BYTE *in_buf = NULL;
    151 	CK_BYTE *out_buf = NULL;
    152 	CK_ULONG out_len;
    153 	CK_ULONG total_len;
    154 	CK_ULONG remain;
    155 	crypto_data_t out;
    156 
    157 	/*
    158 	 * Blowfish only takes input length that is a multiple of blocksize
    159 	 * for C_Encrypt function with the mechanism CKM_BLOWFISH_CBC.
    160 	 *
    161 	 */
    162 	if (!update) {
    163 		if ((ulDataLen % BLOWFISH_BLOCK_LEN) != 0) {
    164 			rv = CKR_DATA_LEN_RANGE;
    165 			goto cleanup;
    166 		}
    167 
    168 		out_len = ulDataLen;
    169 		/*
    170 		 * If application asks for the length of the output buffer
    171 		 * to hold the ciphertext?
    172 		 */
    173 		if (pEncrypted == NULL) {
    174 			*pulEncryptedLen = out_len;
    175 			return (CKR_OK);
    176 		}
    177 
    178 		/* Is the application-supplied buffer large enough? */
    179 		if (*pulEncryptedLen < out_len) {
    180 			*pulEncryptedLen = out_len;
    181 			return (CKR_BUFFER_TOO_SMALL);
    182 		}
    183 
    184 		in_buf = pData;
    185 		out_buf = pEncrypted;
    186 	} else {
    187 		/*
    188 		 * Called by C_EncryptUpdate
    189 		 *
    190 		 * Add the lengths of last remaining data and current
    191 		 * plaintext together to get the total input length.
    192 		 */
    193 		total_len = soft_blowfish_ctx->remain_len + ulDataLen;
    194 
    195 		/*
    196 		 * If the total input length is less than one blocksize,
    197 		 * we will need to delay encryption until when more data
    198 		 * comes in next C_EncryptUpdate or when C_EncryptFinal
    199 		 * is called.
    200 		 */
    201 		if (total_len < BLOWFISH_BLOCK_LEN) {
    202 			if (pEncrypted != NULL) {
    203 				/*
    204 				 * Save input data and its length in
    205 				 * the remaining buffer of BLOWFISH context.
    206 				 */
    207 				(void) memcpy(soft_blowfish_ctx->data +
    208 				    soft_blowfish_ctx->remain_len, pData,
    209 				    ulDataLen);
    210 				soft_blowfish_ctx->remain_len += ulDataLen;
    211 			}
    212 
    213 			/* Set encrypted data length to 0. */
    214 			*pulEncryptedLen = 0;
    215 			return (CKR_OK);
    216 		}
    217 
    218 		/* Compute the length of remaing data. */
    219 		remain = total_len % BLOWFISH_BLOCK_LEN;
    220 
    221 		/*
    222 		 * Make sure that the output length is a multiple of
    223 		 * blocksize.
    224 		 */
    225 		out_len = total_len - remain;
    226 
    227 		/*
    228 		 * If application asks for the length of the output buffer
    229 		 * to hold the ciphertext?
    230 		 */
    231 		if (pEncrypted == NULL) {
    232 			*pulEncryptedLen = out_len;
    233 			return (CKR_OK);
    234 		}
    235 
    236 		/* Is the application-supplied buffer large enough? */
    237 		if (*pulEncryptedLen < out_len) {
    238 			*pulEncryptedLen = out_len;
    239 			return (CKR_BUFFER_TOO_SMALL);
    240 		}
    241 
    242 		if (soft_blowfish_ctx->remain_len != 0) {
    243 			/*
    244 			 * Copy last remaining data and current input data
    245 			 * to the output buffer.
    246 			 */
    247 			(void) memmove(pEncrypted +
    248 			    soft_blowfish_ctx->remain_len,
    249 			    pData, out_len - soft_blowfish_ctx->remain_len);
    250 			(void) memcpy(pEncrypted, soft_blowfish_ctx->data,
    251 			    soft_blowfish_ctx->remain_len);
    252 			bzero(soft_blowfish_ctx->data,
    253 			    soft_blowfish_ctx->remain_len);
    254 
    255 			in_buf = pEncrypted;
    256 		} else {
    257 			in_buf = pData;
    258 		}
    259 		out_buf = pEncrypted;
    260 	}
    261 
    262 	/*
    263 	 * Begin Encryption now.
    264 	 */
    265 
    266 	out.cd_format = CRYPTO_DATA_RAW;
    267 	out.cd_offset = 0;
    268 	out.cd_length = out_len;
    269 	out.cd_raw.iov_base = (char *)out_buf;
    270 	out.cd_raw.iov_len = out_len;
    271 
    272 	/* Encrypt multiple blocks of data. */
    273 	rc = blowfish_encrypt_contiguous_blocks(
    274 		(blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc,
    275 		    (char *)in_buf, out_len, &out);
    276 
    277 	if (rc == 0) {
    278 		*pulEncryptedLen = out_len;
    279 		if (update) {
    280 			/*
    281 			 * For encrypt update, if there is remaining data,
    282 			 * save it and it's length in the context.
    283 			 */
    284 			if (remain != 0)
    285 				(void) memcpy(soft_blowfish_ctx->data, pData +
    286 				    (ulDataLen - remain), remain);
    287 
    288 			soft_blowfish_ctx->remain_len = remain;
    289 			return (CKR_OK);
    290 		}
    291 
    292 	} else {
    293 		*pulEncryptedLen = 0;
    294 		rv = CKR_FUNCTION_FAILED;
    295 	}
    296 
    297 cleanup:
    298 	(void) pthread_mutex_lock(&session_p->session_mutex);
    299 	blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc;
    300 	if (blowfish_ctx != NULL) {
    301 		bzero(blowfish_ctx->bc_keysched,
    302 		    blowfish_ctx->bc_keysched_len);
    303 		free(soft_blowfish_ctx->blowfish_cbc);
    304 	}
    305 
    306 	bzero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len);
    307 	free(soft_blowfish_ctx->key_sched);
    308 	free(session_p->encrypt.context);
    309 	session_p->encrypt.context = NULL;
    310 	(void) pthread_mutex_unlock(&session_p->session_mutex);
    311 
    312 	return (rv);
    313 }
    314 
    315 
    316 CK_RV
    317 soft_blowfish_decrypt_common(soft_session_t *session_p, CK_BYTE_PTR pEncrypted,
    318     CK_ULONG ulEncryptedLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen,
    319     boolean_t update) {
    320 
    321 	int rc = 0;
    322 	CK_RV rv = CKR_OK;
    323 	soft_blowfish_ctx_t *soft_blowfish_ctx =
    324 	    (soft_blowfish_ctx_t *)session_p->decrypt.context;
    325 	blowfish_ctx_t *blowfish_ctx;
    326 	CK_BYTE *in_buf = NULL;
    327 	CK_BYTE *out_buf = NULL;
    328 	CK_ULONG out_len;
    329 	CK_ULONG total_len;
    330 	CK_ULONG remain;
    331 	crypto_data_t out;
    332 
    333 	/*
    334 	 * Blowfish only takes input length that is a multiple of 16 bytes
    335 	 * for C_Decrypt function using CKM_BLOWFISH_CBC.
    336 	 */
    337 
    338 	if (!update) {
    339 		/* Called by C_Decrypt */
    340 		if ((ulEncryptedLen % BLOWFISH_BLOCK_LEN) != 0) {
    341 			rv = CKR_ENCRYPTED_DATA_LEN_RANGE;
    342 			goto cleanup;
    343 		}
    344 
    345 		/*
    346 		 * If application asks for the length of the output buffer
    347 		 * to hold the plaintext?
    348 		 */
    349 		if (pData == NULL) {
    350 			*pulDataLen = ulEncryptedLen;
    351 			return (CKR_OK);
    352 		}
    353 
    354 		/* Is the application-supplied buffer large enough? */
    355 		if (*pulDataLen < ulEncryptedLen) {
    356 			*pulDataLen = ulEncryptedLen;
    357 			return (CKR_BUFFER_TOO_SMALL);
    358 		}
    359 		out_len = ulEncryptedLen;
    360 		in_buf = pEncrypted;
    361 		out_buf = pData;
    362 	} else {
    363 		/*
    364 		 * Called by C_DecryptUpdate
    365 		 *
    366 		 * Add the lengths of last remaining data and current
    367 		 * input data together to get the total input length.
    368 		 */
    369 		total_len = soft_blowfish_ctx->remain_len + ulEncryptedLen;
    370 
    371 		if (total_len < BLOWFISH_BLOCK_LEN) {
    372 			if (pData != NULL) {
    373 				(void) memcpy(soft_blowfish_ctx->data +
    374 				    soft_blowfish_ctx->remain_len,
    375 				    pEncrypted, ulEncryptedLen);
    376 
    377 				soft_blowfish_ctx->remain_len += ulEncryptedLen;
    378 			}
    379 
    380 			/* Set output data length to 0. */
    381 			*pulDataLen = 0;
    382 			return (CKR_OK);
    383 		}
    384 
    385 		/* Compute the length of remaining data. */
    386 		remain = total_len % BLOWFISH_BLOCK_LEN;
    387 
    388 		/*
    389 		 * Make sure that the output length is a multiple of
    390 		 * blocksize.
    391 		 */
    392 		out_len = total_len - remain;
    393 
    394 		/*
    395 		 * if application asks for the length of the output buffer
    396 		 * to hold the plaintext?
    397 		 */
    398 		if (pData == NULL) {
    399 			*pulDataLen = out_len;
    400 			return (CKR_OK);
    401 		}
    402 
    403 		/*
    404 		 * Is the application-supplied buffer large enough?
    405 		 */
    406 		if (*pulDataLen < out_len) {
    407 			*pulDataLen = out_len;
    408 			return (CKR_BUFFER_TOO_SMALL);
    409 		}
    410 
    411 		if (soft_blowfish_ctx->remain_len != 0) {
    412 			/*
    413 			 * Copy last remaining data and current input data
    414 			 * to the output buffer.
    415 			 */
    416 			(void) memmove(pData + soft_blowfish_ctx->remain_len,
    417 			    pEncrypted,
    418 			    out_len - soft_blowfish_ctx->remain_len);
    419 			(void) memcpy(pData, soft_blowfish_ctx->data,
    420 			    soft_blowfish_ctx->remain_len);
    421 			bzero(soft_blowfish_ctx->data,
    422 			    soft_blowfish_ctx->remain_len);
    423 
    424 
    425 			in_buf = pData;
    426 		} else {
    427 			in_buf = pEncrypted;
    428 		}
    429 
    430 		out_buf = pData;
    431 	}
    432 
    433 	out.cd_format = CRYPTO_DATA_RAW;
    434 	out.cd_offset = 0;
    435 	out.cd_length = out_len;
    436 	out.cd_raw.iov_base = (char *)out_buf;
    437 	out.cd_raw.iov_len = out_len;
    438 
    439 	/* Decrypt multiple blocks of data. */
    440 	rc = blowfish_decrypt_contiguous_blocks(
    441 		(blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc,
    442 		(char *)in_buf, out_len, &out);
    443 
    444 	if (rc == 0) {
    445 		*pulDataLen = out_len;
    446 		if (update) {
    447 			/*
    448 			 * For decrypt update, if there is remaining data,
    449 			 * save it and its length in the context.
    450 			 */
    451 			if (remain != 0)
    452 				(void) memcpy(soft_blowfish_ctx->data,
    453 				    pEncrypted + (ulEncryptedLen - remain),
    454 				    remain);
    455 			soft_blowfish_ctx->remain_len = remain;
    456 			return (CKR_OK);
    457 		}
    458 
    459 
    460 	} else {
    461 		*pulDataLen = 0;
    462 		rv = CKR_FUNCTION_FAILED;
    463 	}
    464 
    465 cleanup:
    466 	(void) pthread_mutex_lock(&session_p->session_mutex);
    467 	blowfish_ctx = (blowfish_ctx_t *)soft_blowfish_ctx->blowfish_cbc;
    468 	if (blowfish_ctx != NULL) {
    469 		bzero(blowfish_ctx->bc_keysched,
    470 		    blowfish_ctx->bc_keysched_len);
    471 		free(soft_blowfish_ctx->blowfish_cbc);
    472 	}
    473 
    474 	bzero(soft_blowfish_ctx->key_sched, soft_blowfish_ctx->keysched_len);
    475 	free(soft_blowfish_ctx->key_sched);
    476 	free(session_p->decrypt.context);
    477 	session_p->decrypt.context = NULL;
    478 	(void) pthread_mutex_unlock(&session_p->session_mutex);
    479 
    480 	return (rv);
    481 }
    482 
    483 /*
    484  * Allocate and initialize a context for BLOWFISH CBC mode of operation.
    485  */
    486 
    487 void *
    488 blowfish_cbc_ctx_init(void *key_sched, size_t size, uint8_t *ivec)
    489 {
    490 
    491 	cbc_ctx_t *cbc_ctx;
    492 
    493 	if ((cbc_ctx = calloc(1, sizeof (cbc_ctx_t))) == NULL)
    494 		return (NULL);
    495 
    496 	cbc_ctx->cbc_keysched = key_sched;
    497 
    498 	(void) memcpy(&cbc_ctx->cbc_iv[0], ivec, BLOWFISH_BLOCK_LEN);
    499 
    500 	cbc_ctx->cbc_lastp = (uint8_t *)&(cbc_ctx->cbc_iv);
    501 	cbc_ctx->cbc_keysched_len = size;
    502 	cbc_ctx->cbc_flags |= CBC_MODE;
    503 
    504 	return (cbc_ctx);
    505 }
    506