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