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