1/*
2 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * ARCFOUR
36 */
37
38#include "krb5_locl.h"
39
40#ifdef HEIM_KRB5_ARCFOUR
41
42static struct _krb5_key_type keytype_arcfour = {
43    KRB5_ENCTYPE_ARCFOUR_HMAC_MD5,
44    "arcfour",
45    128,
46    16,
47    sizeof(struct _krb5_evp_schedule),
48    NULL,
49    _krb5_evp_schedule,
50    _krb5_arcfour_salt,
51    NULL,
52    _krb5_evp_cleanup,
53    EVP_rc4
54};
55
56/*
57 * checksum according to section 5. of draft-brezak-win2k-krb-rc4-hmac-03.txt
58 */
59
60krb5_error_code
61_krb5_HMAC_MD5_checksum(krb5_context context,
62			struct _krb5_key_data *key,
63			const void *data,
64			size_t len,
65			unsigned usage,
66			Checksum *result)
67{
68    CCDigestRef m;
69    struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5);
70    const char signature[] = "signaturekey";
71    Checksum ksign_c;
72    struct _krb5_key_data ksign;
73    krb5_keyblock kb;
74    unsigned char t[4];
75    unsigned char tmp[16];
76    unsigned char ksign_c_data[16];
77    krb5_error_code ret;
78
79    m = CCDigestCreate(kCCDigestMD5);
80    if (m == NULL) {
81	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
82	return ENOMEM;
83    }
84    ksign_c.checksum.length = sizeof(ksign_c_data);
85    ksign_c.checksum.data   = ksign_c_data;
86    ret = _krb5_internal_hmac(context, c, signature, sizeof(signature),
87			      0, key, &ksign_c);
88    if (ret) {
89	CCDigestDestroy(m);
90	return ret;
91    }
92    ksign.key = &kb;
93    kb.keyvalue = ksign_c.checksum;
94
95    t[0] = (usage >>  0) & 0xFF;
96    t[1] = (usage >>  8) & 0xFF;
97    t[2] = (usage >> 16) & 0xFF;
98    t[3] = (usage >> 24) & 0xFF;
99    CCDigestUpdate(m, t, 4);
100    CCDigestUpdate(m, data, len);
101    CCDigestFinal(m, tmp);
102    CCDigestDestroy(m);
103
104    ret = _krb5_internal_hmac(context, c, tmp, sizeof(tmp), 0, &ksign, result);
105    if (ret)
106	return ret;
107    return 0;
108}
109
110struct _krb5_checksum_type _krb5_checksum_hmac_md5 = {
111    CKSUMTYPE_HMAC_MD5,
112    "hmac-md5",
113    64,
114    16,
115    F_KEYED | F_CPROOF,
116    _krb5_HMAC_MD5_checksum,
117    NULL
118};
119
120/*
121 * section 6 of draft-brezak-win2k-krb-rc4-hmac-03
122 *
123 * warning: not for small children
124 */
125
126static krb5_error_code
127ARCFOUR_subencrypt(krb5_context context,
128		   struct _krb5_key_data *key,
129		   void *data,
130		   size_t len,
131		   unsigned usage,
132		   void *ivec)
133{
134    EVP_CIPHER_CTX ctx;
135    struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5);
136    Checksum k1_c, k2_c, k3_c, cksum;
137    struct _krb5_key_data ke;
138    krb5_keyblock kb;
139    unsigned char t[4];
140    unsigned char *cdata = data;
141    unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16];
142    krb5_error_code ret;
143
144    t[0] = (usage >>  0) & 0xFF;
145    t[1] = (usage >>  8) & 0xFF;
146    t[2] = (usage >> 16) & 0xFF;
147    t[3] = (usage >> 24) & 0xFF;
148
149    k1_c.checksum.length = sizeof(k1_c_data);
150    k1_c.checksum.data   = k1_c_data;
151
152    ret = _krb5_internal_hmac(NULL, c, t, sizeof(t), 0, key, &k1_c);
153    if (ret)
154	krb5_abortx(context, "hmac failed");
155
156    memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data));
157
158    k2_c.checksum.length = sizeof(k2_c_data);
159    k2_c.checksum.data   = k2_c_data;
160
161    ke.key = &kb;
162    kb.keyvalue = k2_c.checksum;
163
164    cksum.checksum.length = 16;
165    cksum.checksum.data   = data;
166
167    ret = _krb5_internal_hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum);
168    if (ret)
169	krb5_abortx(context, "hmac failed");
170
171    ke.key = &kb;
172    kb.keyvalue = k1_c.checksum;
173
174    k3_c.checksum.length = sizeof(k3_c_data);
175    k3_c.checksum.data   = k3_c_data;
176
177    ret = _krb5_internal_hmac(NULL, c, data, 16, 0, &ke, &k3_c);
178    if (ret)
179	krb5_abortx(context, "hmac failed");
180
181    EVP_CIPHER_CTX_init(&ctx);
182
183    EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 1);
184    EVP_Cipher(&ctx, cdata + 16, cdata + 16, len - 16);
185    EVP_CIPHER_CTX_cleanup(&ctx);
186
187    memset (k1_c_data, 0, sizeof(k1_c_data));
188    memset (k2_c_data, 0, sizeof(k2_c_data));
189    memset (k3_c_data, 0, sizeof(k3_c_data));
190    return 0;
191}
192
193static krb5_error_code
194ARCFOUR_subdecrypt(krb5_context context,
195		   struct _krb5_key_data *key,
196		   void *data,
197		   size_t len,
198		   unsigned usage,
199		   void *ivec)
200{
201    EVP_CIPHER_CTX ctx;
202    struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5);
203    Checksum k1_c, k2_c, k3_c, cksum;
204    struct _krb5_key_data ke;
205    krb5_keyblock kb;
206    unsigned char t[4];
207    unsigned char *cdata = data;
208    unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16];
209    unsigned char cksum_data[16];
210    krb5_error_code ret;
211
212    t[0] = (usage >>  0) & 0xFF;
213    t[1] = (usage >>  8) & 0xFF;
214    t[2] = (usage >> 16) & 0xFF;
215    t[3] = (usage >> 24) & 0xFF;
216
217    k1_c.checksum.length = sizeof(k1_c_data);
218    k1_c.checksum.data   = k1_c_data;
219
220    ret = _krb5_internal_hmac(NULL, c, t, sizeof(t), 0, key, &k1_c);
221    if (ret)
222	krb5_abortx(context, "hmac failed");
223
224    memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data));
225
226    k2_c.checksum.length = sizeof(k2_c_data);
227    k2_c.checksum.data   = k2_c_data;
228
229    ke.key = &kb;
230    kb.keyvalue = k1_c.checksum;
231
232    k3_c.checksum.length = sizeof(k3_c_data);
233    k3_c.checksum.data   = k3_c_data;
234
235    ret = _krb5_internal_hmac(NULL, c, cdata, 16, 0, &ke, &k3_c);
236    if (ret)
237	krb5_abortx(context, "hmac failed");
238
239    EVP_CIPHER_CTX_init(&ctx);
240    EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 0);
241    EVP_Cipher(&ctx, cdata + 16, cdata + 16, len - 16);
242    EVP_CIPHER_CTX_cleanup(&ctx);
243
244    ke.key = &kb;
245    kb.keyvalue = k2_c.checksum;
246
247    cksum.checksum.length = 16;
248    cksum.checksum.data   = cksum_data;
249
250    ret = _krb5_internal_hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum);
251    if (ret)
252	krb5_abortx(context, "hmac failed");
253
254    memset (k1_c_data, 0, sizeof(k1_c_data));
255    memset (k2_c_data, 0, sizeof(k2_c_data));
256    memset (k3_c_data, 0, sizeof(k3_c_data));
257
258    if (ct_memcmp (cksum.checksum.data, data, 16) != 0) {
259	krb5_clear_error_message (context);
260	return KRB5KRB_AP_ERR_BAD_INTEGRITY;
261    } else {
262	return 0;
263    }
264}
265
266/*
267 * convert the usage numbers used in
268 * draft-ietf-cat-kerb-key-derivation-00.txt to the ones in
269 * draft-brezak-win2k-krb-rc4-hmac-04.txt
270 */
271
272krb5_error_code
273_krb5_usage2arcfour(krb5_context context, unsigned *usage)
274{
275    switch (*usage) {
276    case KRB5_KU_AS_REP_ENC_PART : /* 3 */
277	*usage = 8;
278	return 0;
279    case KRB5_KU_USAGE_SEAL :  /* 22 */
280	*usage = 13;
281	return 0;
282    case KRB5_KU_USAGE_SIGN : /* 23 */
283        *usage = 15;
284        return 0;
285    case KRB5_KU_USAGE_SEQ: /* 24 */
286	*usage = 0;
287	return 0;
288    default :
289	return 0;
290    }
291}
292
293static krb5_error_code
294ARCFOUR_encrypt(krb5_context context,
295		struct _krb5_key_data *key,
296		void *data,
297		size_t len,
298		krb5_boolean encryptp,
299		int usage,
300		void *ivec)
301{
302    krb5_error_code ret;
303    unsigned keyusage = usage;
304
305    if((ret = _krb5_usage2arcfour (context, &keyusage)) != 0)
306	return ret;
307
308    if (encryptp)
309	return ARCFOUR_subencrypt (context, key, data, len, keyusage, ivec);
310    else
311	return ARCFOUR_subdecrypt (context, key, data, len, keyusage, ivec);
312}
313
314struct _krb5_encryption_type _krb5_enctype_arcfour_hmac_md5 = {
315    ETYPE_ARCFOUR_HMAC_MD5,
316    "arcfour-hmac-md5",
317    1,
318    1,
319    8,
320    &keytype_arcfour,
321    &_krb5_checksum_hmac_md5,
322    NULL,
323    F_SPECIAL,
324    ARCFOUR_encrypt,
325    0,
326    NULL
327};
328
329#endif /* HEIM_KRB5_ARCFOUR */
330