xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/crypto/enc_provider/arcfour_provider.c (revision 8e458de0baeb1fee50643403223bc7e909a48464)
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 */
17 static krb5_error_code
18 k5_arcfour_make_key(krb5_context, const krb5_data *, krb5_keyblock *);
19 
20 #ifndef _KERNEL
21 static krb5_error_code
22 setup_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 */
72 static krb5_error_code
73 k5_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     }
170 cleanup:
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 */
189 static krb5_error_code
190 k5_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     }
276 cleanup:
277 final_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 */
296 static krb5_error_code
297 k5_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*/
327 static krb5_error_code
328 k5_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 */
352 const 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