1/*
2 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5#pragma ident	"%Z%%M%	%I%	%E% SMI"
6
7/*
8 * Copyright (c) 2000 by Computer Science Laboratory,
9 *                       Rensselaer Polytechnic Institute
10 * #include STD_DISCLAIMER
11 */
12
13#include <k5-int.h>
14#include <arcfour.h>
15
16/* from a random bitstrem, construct a key */
17static krb5_error_code
18k5_arcfour_make_key(krb5_context, const krb5_data *, krb5_keyblock *);
19
20#ifndef _KERNEL
21static krb5_error_code
22setup_arcfour_crypto(CK_SESSION_HANDLE session,
23		const krb5_keyblock *key,
24		KRB5_MECH_TO_PKCS *algos,
25		CK_OBJECT_HANDLE *hKey)
26{
27	krb5_error_code ret = 0;
28	CK_RV rv;
29	CK_OBJECT_CLASS class = CKO_SECRET_KEY;
30	CK_KEY_TYPE keyType = CKK_RC4;
31	CK_BBOOL true = TRUE, false =  FALSE;
32	CK_ATTRIBUTE template[5];
33
34	if ((rv = get_algo(key->enctype, algos)) != CKR_OK) {
35		KRB5_LOG0(KRB5_ERR, "failure to get algo id in function "
36		"k5_arcfour_decrypt.");
37		return (PKCS_ERR);
38	}
39
40	template[0].type = CKA_CLASS;
41	template[0].pValue = &class;
42	template[0].ulValueLen = sizeof (class);
43	template[1].type = CKA_KEY_TYPE;
44	template[1].pValue = &keyType;
45	template[1].ulValueLen = sizeof (keyType);
46	template[2].type = CKA_TOKEN;
47	template[2].pValue = &false;
48	template[2].ulValueLen = sizeof (false);
49	template[3].type = CKA_ENCRYPT;
50	template[3].pValue = &true;
51	template[3].ulValueLen = sizeof (true);
52	template[4].type = CKA_VALUE;
53	template[4].pValue = key->contents;
54	template[4].ulValueLen = key->length;
55
56	/* Create an object handle for the key */
57	if ((rv = C_CreateObject(session, template,
58		sizeof(template)/sizeof(CK_ATTRIBUTE),
59		hKey)) != CKR_OK) {
60		KRB5_LOG(KRB5_ERR, "C_CreateObject failed in "
61		"k5_arcfour_decrypt: rv = 0x%x.", rv);
62		ret = PKCS_ERR;
63	}
64
65	return (ret);
66}
67#endif /* !_KERNEL */
68
69
70/* The workhorse of the arcfour system, this impliments the cipher */
71/* ARGSUSED */
72static krb5_error_code
73k5_arcfour_decrypt(krb5_context context,
74	const krb5_keyblock *key, const krb5_data *state,
75	const krb5_data *input, krb5_data *output)
76{
77  krb5_error_code ret = 0;
78
79#ifndef _KERNEL
80  CK_RV rv;
81  KRB5_MECH_TO_PKCS algos;
82  CK_OBJECT_HANDLE *kptr = NULL, hKey = CK_INVALID_HANDLE;
83  CK_MECHANISM mechanism;
84  CK_SESSION_HANDLE session = 0;
85  CK_ULONG outlen;
86  int need_init = 0;
87#endif
88
89  KRB5_LOG0(KRB5_INFO, "k5_arcfour_decrypt start");
90  if (key->length != 16)
91    return(KRB5_BAD_KEYSIZE);
92  if (input->length != output->length)
93    return(KRB5_BAD_MSIZE);
94
95#ifndef _KERNEL
96   /*
97    * If RC4 is being used to encrypt a stream of data blocks,
98    * the keys for encrypt and decrypt must be kept separate
99    * so that their associated state data doesn't get mixed up
100    * between operations.    The r-cmds (rlogin, rsh, rcp) use
101    * the  "init_state" function (see bottom of this module)
102    * to set up and prepare for stream encryption.
103    *
104    * Normally, the RC4 key is used as a single operation
105    * (i.e. call C_Encrypt) instead of a constantly updating
106    * stream cipher (C_EncryptUpdate).  In those cases, we just
107    * use a short-term key object defined locally. We don't
108    * have to save state between operations.
109    *
110    * This logic here is to make sure that the keys are tracked
111    * correctly depending on how they are used and that the RC4
112    * context record is properly initialized.
113    */
114   if (!context->arcfour_ctx.initialized) {
115	session = krb_ctx_hSession(context);
116	/* Just use a local, 1-time only key object */
117	kptr = (CK_OBJECT_HANDLE *)&hKey;
118	need_init = 1;
119   } else {
120	session = context->arcfour_ctx.dSession;
121	/* If the dKey handle was not defined, we need to initialize one */
122	if (context->arcfour_ctx.dKey == CK_INVALID_HANDLE) {
123		need_init = 1;
124		/* Use the long-term key object in the RC4 context area */
125		kptr =  &context->arcfour_ctx.dKey;
126	}
127   }
128
129   if (need_init) {
130	ret = setup_arcfour_crypto(session, key, &algos, kptr);
131	if (ret)
132                goto cleanup;
133
134	mechanism.mechanism = algos.enc_algo;
135	mechanism.pParameter =  NULL;
136	mechanism.ulParameterLen = 0;
137
138	rv = C_DecryptInit(session, &mechanism, *kptr);
139
140	if (rv != CKR_OK) {
141		KRB5_LOG(KRB5_ERR, "C_DecryptInit failed in "
142			"k5_arcfour_decrypt: rv = 0x%x", rv);
143		ret = PKCS_ERR;
144		goto cleanup;
145	}
146    }
147
148    outlen = (CK_ULONG)output->length;
149    if (context->arcfour_ctx.initialized)
150	rv = C_DecryptUpdate(session,
151		(CK_BYTE_PTR)input->data,
152		(CK_ULONG)input->length,
153		(CK_BYTE_PTR)output->data,
154		(CK_ULONG_PTR)&outlen);
155    else {
156	rv = C_Decrypt(session,
157		(CK_BYTE_PTR)input->data,
158		(CK_ULONG)input->length,
159		(CK_BYTE_PTR)output->data,
160		(CK_ULONG_PTR)&outlen);
161    }
162    output->length = (uint32_t)outlen;
163
164    if (rv != CKR_OK) {
165            KRB5_LOG(KRB5_ERR,
166		"C_DecryptUpdate failed in k5_arcfour_decrypt: "
167		"rv = 0x%x", rv);
168            ret = PKCS_ERR;
169    }
170cleanup:
171    if (ret)
172	bzero(output->data, input->length);
173
174    /* If we used a 1-time only key object, destroy it now */
175    if (hKey != CK_INVALID_HANDLE)
176	(void)C_DestroyObject(session, hKey);
177
178#else /* !_KERNEL */
179    KRB5_LOG(KRB5_INFO, "key->kef_mt = %ld", (ulong_t) key->kef_mt);
180    ret = k5_ef_crypto((const char *)input->data, (char *)output->data,
181		input->length, (krb5_keyblock *)key, NULL, 0);
182#endif /* !_KERNEL */
183
184  KRB5_LOG0(KRB5_INFO, "k5_arcfour_docrypt end");
185  return (ret);
186}
187
188/* ARGSUSED */
189static krb5_error_code
190k5_arcfour_encrypt(krb5_context context,
191	const krb5_keyblock *key, const krb5_data *state,
192	const krb5_data *input, krb5_data *output)
193{
194  krb5_error_code ret = 0;
195
196#ifndef _KERNEL
197  CK_RV rv;
198  KRB5_MECH_TO_PKCS algos;
199  CK_MECHANISM mechanism;
200  CK_OBJECT_HANDLE *kptr = NULL, hKey = CK_INVALID_HANDLE;
201  CK_SESSION_HANDLE session;
202  int need_init = 0;
203  CK_ULONG outlen;
204#endif
205
206  KRB5_LOG0(KRB5_INFO, "k5_arcfour_encrypt start");
207  if (key->length != 16)
208	return(KRB5_BAD_KEYSIZE);
209  if (input->length != output->length)
210	return(KRB5_BAD_MSIZE);
211
212#ifndef _KERNEL
213
214   /*
215    * See the comments in the k5_arcfour_decrypt routine (above)
216    * for an explanation of why the key handles are initialized
217    * and used as they are here.
218    */
219   if (!context->arcfour_ctx.initialized) {
220	session = krb_ctx_hSession(context);
221	kptr = (CK_OBJECT_HANDLE *)&hKey;
222	need_init = 1;
223   } else {
224	session = context->arcfour_ctx.eSession;
225	if (context->arcfour_ctx.eKey == 0) {
226		kptr = &context->arcfour_ctx.eKey;
227		need_init = 1;
228	}
229   }
230
231   if (need_init)  {
232	ret = setup_arcfour_crypto(session, key, &algos, kptr);
233	if (ret)
234                goto cleanup;
235
236	mechanism.mechanism = algos.enc_algo;
237	mechanism.pParameter =  NULL;
238	mechanism.ulParameterLen = 0;
239
240	rv = C_EncryptInit(session, &mechanism, *kptr);
241
242	if (rv != CKR_OK) {
243		KRB5_LOG(KRB5_ERR, "C_EncryptInit failed in "
244			"k5_arcfour_encrypt: rv = 0x%x", rv);
245		ret = PKCS_ERR;
246		goto cleanup;
247	}
248    }
249
250    /*
251     * If we've initialize the stream for use with r-commands,
252     * use the open-ended session handle and call.
253     */
254    outlen = (CK_ULONG)output->length;
255    if (context->arcfour_ctx.initialized)
256	rv = C_EncryptUpdate(session,
257		(CK_BYTE_PTR)input->data,
258		(CK_ULONG)input->length,
259		(CK_BYTE_PTR)output->data,
260		(CK_ULONG_PTR)&outlen);
261    else {
262	rv = C_Encrypt(session,
263		(CK_BYTE_PTR)input->data,
264		(CK_ULONG)input->length,
265		(CK_BYTE_PTR)output->data,
266		(CK_ULONG_PTR)&outlen);
267    }
268    output->length = (uint32_t)outlen;
269
270    if (rv != CKR_OK) {
271            KRB5_LOG(KRB5_ERR,
272		"C_EncryptUpdate failed in k5_arcfour_encrypt: "
273		"rv = 0x%x", rv);
274            ret = PKCS_ERR;
275    }
276cleanup:
277final_cleanup:
278    if (ret)
279	bzero(output->data, input->length);
280
281    if (hKey != CK_INVALID_HANDLE)
282	(void)C_DestroyObject(session, hKey);
283
284#else /* !_KERNEL */
285    KRB5_LOG1(KRB5_INFO, "key->kef_mt = %ld key->key_tmpl = %ld",
286		(ulong_t) key->kef_mt, (ulong_t) key->key_tmpl);
287    ret = k5_ef_crypto((const char *)input->data, (char *)output->data,
288			input->length, (krb5_keyblock *)key, NULL, 1);
289#endif /* !_KERNEL */
290
291  KRB5_LOG0(KRB5_INFO, "k5_arcfour_docrypt end");
292  return (ret);
293}
294
295/* ARGSUSED */
296static krb5_error_code
297k5_arcfour_make_key(krb5_context context,
298	const krb5_data *randombits, krb5_keyblock *key)
299{
300    krb5_error_code ret = 0;
301    KRB5_LOG0(KRB5_INFO, "k5_arcfour_make_key() start\n");
302
303    if (key->length != 16)
304	return(KRB5_BAD_KEYSIZE);
305    if (randombits->length != 16)
306	return(KRB5_CRYPTO_INTERNAL);
307
308    key->magic = KV5M_KEYBLOCK;
309    key->length = 16;
310    key->dk_list = NULL;
311#ifdef _KERNEL
312    key->kef_key.ck_data = NULL;
313    key->key_tmpl = NULL;
314    ret = init_key_kef(context->kef_cipher_mt, key);
315#else
316    key->hKey = CK_INVALID_HANDLE;
317    ret = init_key_uef(krb_ctx_hSession(context), key);
318#endif /* _KERNEL */
319
320    bcopy(randombits->data, key->contents, randombits->length);
321
322    KRB5_LOG0(KRB5_INFO, "k5_arcfour_make_key() end\n");
323    return (ret);
324}
325
326/*ARGSUSED*/
327static krb5_error_code
328k5_arcfour_init_state (krb5_context context,
329		const krb5_keyblock *key,
330		krb5_keyusage keyusage, krb5_data *new_state)
331{
332   krb5_error_code retval = 0;
333#ifndef _KERNEL
334   if (!context->arcfour_ctx.initialized) {
335	retval = krb5_open_pkcs11_session(&context->arcfour_ctx.eSession);
336	if (retval)
337		return (retval);
338	retval = krb5_open_pkcs11_session(&context->arcfour_ctx.dSession);
339	if (retval)
340		return (retval);
341	context->arcfour_ctx.initialized = 1;
342	context->arcfour_ctx.eKey = CK_INVALID_HANDLE;
343	context->arcfour_ctx.dKey = CK_INVALID_HANDLE;
344   }
345#endif
346  return (retval);
347}
348
349/* Since the arcfour cipher is identical going forwards and backwards,
350   we just call "docrypt" directly
351*/
352const struct krb5_enc_provider krb5int_enc_arcfour = {
353    1,
354    16, 16,
355    k5_arcfour_encrypt,
356    k5_arcfour_decrypt,
357    k5_arcfour_make_key,
358    k5_arcfour_init_state,
359    krb5int_default_free_state
360};
361