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