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