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