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
37CK_RV
38soft_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 */
140CK_RV
141soft_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
297cleanup:
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
316CK_RV
317soft_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
465cleanup:
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
487void *
488blowfish_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