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