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