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 if (ret)
277 bzero(output->data, input->length);
278
279 if (hKey != CK_INVALID_HANDLE)
280 (void)C_DestroyObject(session, hKey);
281
282 #else /* !_KERNEL */
283 KRB5_LOG1(KRB5_INFO, "key->kef_mt = %ld key->key_tmpl = %ld",
284 (ulong_t) key->kef_mt, (ulong_t) key->key_tmpl);
285 ret = k5_ef_crypto((const char *)input->data, (char *)output->data,
286 input->length, (krb5_keyblock *)key, NULL, 1);
287 #endif /* !_KERNEL */
288
289 KRB5_LOG0(KRB5_INFO, "k5_arcfour_docrypt end");
290 return (ret);
291 }
292
293 /* ARGSUSED */
294 static krb5_error_code
k5_arcfour_make_key(krb5_context context,const krb5_data * randombits,krb5_keyblock * key)295 k5_arcfour_make_key(krb5_context context,
296 const krb5_data *randombits, krb5_keyblock *key)
297 {
298 krb5_error_code ret = 0;
299 KRB5_LOG0(KRB5_INFO, "k5_arcfour_make_key() start\n");
300
301 if (key->length != 16)
302 return(KRB5_BAD_KEYSIZE);
303 if (randombits->length != 16)
304 return(KRB5_CRYPTO_INTERNAL);
305
306 key->magic = KV5M_KEYBLOCK;
307 key->length = 16;
308 key->dk_list = NULL;
309 #ifdef _KERNEL
310 key->kef_key.ck_data = NULL;
311 key->key_tmpl = NULL;
312 ret = init_key_kef(context->kef_cipher_mt, key);
313 #else
314 key->hKey = CK_INVALID_HANDLE;
315 ret = init_key_uef(krb_ctx_hSession(context), key);
316 #endif /* _KERNEL */
317
318 bcopy(randombits->data, key->contents, randombits->length);
319
320 KRB5_LOG0(KRB5_INFO, "k5_arcfour_make_key() end\n");
321 return (ret);
322 }
323
324 /*ARGSUSED*/
325 static krb5_error_code
k5_arcfour_init_state(krb5_context context,const krb5_keyblock * key,krb5_keyusage keyusage,krb5_data * new_state)326 k5_arcfour_init_state (krb5_context context,
327 const krb5_keyblock *key,
328 krb5_keyusage keyusage, krb5_data *new_state)
329 {
330 krb5_error_code retval = 0;
331 #ifndef _KERNEL
332 if (!context->arcfour_ctx.initialized) {
333 retval = krb5_open_pkcs11_session(&context->arcfour_ctx.eSession);
334 if (retval)
335 return (retval);
336 retval = krb5_open_pkcs11_session(&context->arcfour_ctx.dSession);
337 if (retval)
338 return (retval);
339 context->arcfour_ctx.initialized = 1;
340 context->arcfour_ctx.eKey = CK_INVALID_HANDLE;
341 context->arcfour_ctx.dKey = CK_INVALID_HANDLE;
342 }
343 #endif
344 return (retval);
345 }
346
347 /* Since the arcfour cipher is identical going forwards and backwards,
348 we just call "docrypt" directly
349 */
350 const struct krb5_enc_provider krb5int_enc_arcfour = {
351 1,
352 16, 16,
353 k5_arcfour_encrypt,
354 k5_arcfour_decrypt,
355 k5_arcfour_make_key,
356 k5_arcfour_init_state,
357 krb5int_default_free_state
358 };
359