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