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
setup_arcfour_crypto(CK_SESSION_HANDLE session,const krb5_keyblock * key,KRB5_MECH_TO_PKCS * algos,CK_OBJECT_HANDLE * hKey)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
k5_arcfour_decrypt(krb5_context context,const krb5_keyblock * key,const krb5_data * state,const krb5_data * input,krb5_data * output)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
k5_arcfour_encrypt(krb5_context context,const krb5_keyblock * key,const krb5_data * state,const krb5_data * input,krb5_data * output)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
k5_arcfour_make_key(krb5_context context,const krb5_data * randombits,krb5_keyblock * key)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
k5_arcfour_init_state(krb5_context context,const krb5_keyblock * key,krb5_keyusage keyusage,krb5_data * new_state)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