xref: /illumos-gate/usr/src/lib/pkcs11/pkcs11_softtoken/common/softBlowfishCrypt.c (revision 2f172c55ef76964744bc62b4500ece87f3089b4d)
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