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#include "krb5_locl.h"
35
36/*
37 * AES
38 */
39
40#ifdef __APPLE_TARGET_EMBEDDED__
41
42#include <CommonCrypto/CommonCryptor.h>
43#include <CommonCrypto/CommonCryptorSPI.h>
44
45/*
46 * CommonCrypto based
47 */
48
49struct cc_schedule {
50    CCCryptorRef enc;
51    CCCryptorRef dec;
52    struct _krb5_key_type *kt;
53};
54
55static void
56_krb5_cc_schedule(krb5_context context,
57		  struct _krb5_key_type *kt,
58		  struct _krb5_key_data *kd)
59{
60    struct cc_schedule *key = kd->schedule->data;
61    CCAlgorithm alg = (CCAlgorithm)kt->evp;
62
63    key->kt = kt;
64
65    if (CCCryptorCreateWithMode(kCCEncrypt,
66				kCCModeCBC,
67				alg,
68				ccCBCCTS3,
69				NULL,
70				kd->key->keyvalue.data,
71				kd->key->keyvalue.length,
72				NULL,
73				0,
74				0,
75				0,
76				&key->enc) != 0)
77	abort();
78
79    if (CCCryptorCreateWithMode(kCCDecrypt,
80				kCCModeCBC,
81				alg,
82				ccCBCCTS3,
83				NULL,
84				kd->key->keyvalue.data,
85				kd->key->keyvalue.length,
86				NULL,
87				0,
88				0,
89				0,
90				&key->dec) != 0)
91	abort();
92}
93
94static void
95_krb5_cc_cleanup(krb5_context context, struct _krb5_key_data *kd)
96{
97    struct cc_schedule *key = kd->schedule->data;
98    CCCryptorRelease(key->enc);
99    CCCryptorRelease(key->dec);
100}
101
102static struct _krb5_key_type keytype_aes128 = {
103    ENCTYPE_AES128_CTS_HMAC_SHA1_96,
104    "aes-128",
105    128,
106    16,
107    sizeof(struct cc_schedule),
108    NULL,
109    _krb5_cc_schedule,
110    _krb5_AES_salt,
111    NULL,
112    _krb5_cc_cleanup,
113    (void *)kCCAlgorithmAES128
114};
115
116static struct _krb5_key_type keytype_aes256 = {
117    ENCTYPE_AES256_CTS_HMAC_SHA1_96,
118    "aes-256",
119    256,
120    32,
121    sizeof(struct cc_schedule),
122    NULL,
123    _krb5_cc_schedule,
124    _krb5_AES_salt,
125    NULL,
126    _krb5_cc_cleanup,
127    (void *)kCCAlgorithmAES128
128};
129
130struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes128 = {
131    CKSUMTYPE_HMAC_SHA1_96_AES_128,
132    "hmac-sha1-96-aes128",
133    64,
134    12,
135    F_KEYED | F_CPROOF | F_DERIVED,
136    _krb5_SP_HMAC_SHA1_checksum,
137    NULL
138};
139
140struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes256 = {
141    CKSUMTYPE_HMAC_SHA1_96_AES_256,
142    "hmac-sha1-96-aes256",
143    64,
144    12,
145    F_KEYED | F_CPROOF | F_DERIVED,
146    _krb5_SP_HMAC_SHA1_checksum,
147    NULL
148};
149
150static krb5_error_code
151cc_AES_PRF(krb5_context context,
152	   krb5_crypto crypto,
153	   const krb5_data *in,
154	   krb5_data *out)
155{
156    struct _krb5_checksum_type *ct = crypto->et->checksum;
157    krb5_error_code ret;
158    Checksum result;
159    krb5_keyblock *derived;
160    size_t prfsize;
161
162    result.cksumtype = ct->type;
163    ret = krb5_data_alloc(&result.checksum, ct->checksumsize);
164    if (ret) {
165	krb5_set_error_message(context, ret, N_("malloc: out memory", ""));
166	return ret;
167    }
168
169    ret = (*ct->checksum)(context, NULL, in->data, in->length, 0, &result);
170    if (ret) {
171	krb5_data_free(&result.checksum);
172	return ret;
173    }
174
175    if (result.checksum.length < crypto->et->blocksize)
176	krb5_abortx(context, "internal prf error");
177
178    derived = NULL;
179    ret = krb5_derive_key(context, crypto->key.key,
180			  crypto->et->type, "prf", 3, &derived);
181    if (ret)
182	krb5_abortx(context, "krb5_derive_key");
183
184    prfsize = (result.checksum.length / crypto->et->blocksize) * crypto->et->blocksize;
185    heim_assert(prfsize == crypto->et->prf_length, "prfsize not same ?");
186
187    ret = krb5_data_alloc(out, crypto->et->blocksize);
188    if (ret)
189	krb5_abortx(context, "malloc failed");
190
191    {
192	CCAlgorithm alg = (CCAlgorithm)crypto->et->keytype->evp;
193	CCCryptorStatus s;
194	size_t moved;
195
196	s = CCCrypt(kCCEncrypt,
197		    alg,
198		    0,
199		    derived->keyvalue.data,
200		    crypto->et->keytype->size,
201		    NULL,
202		    result.checksum.data,
203		    prfsize,
204		    out->data,
205		    prfsize,
206		    &moved);
207	if (s)
208	    krb5_abortx(context, "encrypt failed");
209	if (moved != prfsize)
210	    krb5_abortx(context, "encrypt failed");
211
212    }
213
214    krb5_data_free(&result.checksum);
215    krb5_free_keyblock(context, derived);
216
217    return ret;
218}
219
220static const unsigned char zero_ivec[EVP_MAX_BLOCK_LENGTH] = { 0 };
221
222static krb5_error_code
223_krb5_cc_encrypt_cts(krb5_context context,
224		     struct _krb5_key_data *key,
225		     void *data,
226		     size_t len,
227		     krb5_boolean encryptp,
228		     int usage,
229		     void *ivec)
230{
231    struct cc_schedule *ctx = key->schedule->data;
232    size_t blocksize;
233    unsigned char *p, *p0;
234    size_t moved, plen = len;
235    CCCryptorStatus s;
236    CCCryptorRef c;
237
238    c = encryptp ? ctx->enc : ctx->dec;
239
240    blocksize = 16; /* XXX only for aes now */
241
242    if (len < blocksize) {
243	krb5_set_error_message(context, EINVAL,
244			       "message block too short");
245	return EINVAL;
246    } else if (len == blocksize) {
247	struct _krb5_key_type *kt = ctx->kt;
248	CCAlgorithm alg = (CCAlgorithm)kt->evp;
249
250	s = CCCrypt(encryptp ? kCCEncrypt : kCCDecrypt,
251		    alg,
252		    0,
253		    key->key->keyvalue.data,
254		    key->key->keyvalue.length,
255		    NULL,
256		    data,
257		    len,
258		    data,
259		    len,
260		    &moved);
261	heim_assert(s == 0, "CCCrypt failed");
262	heim_assert(moved == len, "moved == len");
263
264	return 0;
265    }
266
267    p0 = p = malloc(len);
268    if (p0 == NULL)
269	return ENOMEM;
270
271    if (ivec)
272	CCCryptorReset(c, ivec);
273    else
274	CCCryptorReset(c, zero_ivec);
275
276    s = CCCryptorUpdate(c, data, len, p, plen, &moved);
277    heim_assert(s == 0, "CCCryptorUpdate failed");
278    plen -= moved;
279    p += moved;
280    s = CCCryptorFinal(c, p, plen, &moved);
281    heim_assert(s == 0, "CCCryptorFinal failed");
282    plen -= moved;
283    heim_assert(plen == 0, "plen == 0");
284
285    memcpy(data, p0, len);
286    free(p0);
287
288    return 0;
289}
290
291
292struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha1 = {
293    ETYPE_AES128_CTS_HMAC_SHA1_96,
294    "aes128-cts-hmac-sha1-96",
295    16,
296    1,
297    16,
298    &keytype_aes128,
299    &_krb5_checksum_sha1,
300    &_krb5_checksum_hmac_sha1_aes128,
301    F_DERIVED,
302    _krb5_cc_encrypt_cts,
303    16,
304    cc_AES_PRF
305};
306
307struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha1 = {
308    ETYPE_AES256_CTS_HMAC_SHA1_96,
309    "aes256-cts-hmac-sha1-96",
310    16,
311    1,
312    16,
313    &keytype_aes256,
314    &_krb5_checksum_sha1,
315    &_krb5_checksum_hmac_sha1_aes256,
316    F_DERIVED,
317    _krb5_cc_encrypt_cts,
318    16,
319    cc_AES_PRF
320};
321
322#else
323
324/*
325 * EVP based
326 */
327
328static struct _krb5_key_type keytype_aes128 = {
329    KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
330    "aes-128",
331    128,
332    16,
333    sizeof(struct _krb5_evp_schedule),
334    NULL,
335    _krb5_evp_schedule,
336    _krb5_AES_salt,
337    NULL,
338    _krb5_evp_cleanup,
339    EVP_aes_128_cbc
340};
341
342static struct _krb5_key_type keytype_aes256 = {
343    KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
344    "aes-256",
345    256,
346    32,
347    sizeof(struct _krb5_evp_schedule),
348    NULL,
349    _krb5_evp_schedule,
350    _krb5_AES_salt,
351    NULL,
352    _krb5_evp_cleanup,
353    EVP_aes_256_cbc
354};
355
356struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes128 = {
357    CKSUMTYPE_HMAC_SHA1_96_AES_128,
358    "hmac-sha1-96-aes128",
359    64,
360    12,
361    F_KEYED | F_CPROOF | F_DERIVED,
362    _krb5_SP_HMAC_SHA1_checksum,
363    NULL
364};
365
366struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes256 = {
367    CKSUMTYPE_HMAC_SHA1_96_AES_256,
368    "hmac-sha1-96-aes256",
369    64,
370    12,
371    F_KEYED | F_CPROOF | F_DERIVED,
372    _krb5_SP_HMAC_SHA1_checksum,
373    NULL
374};
375
376static krb5_error_code
377AES_PRF(krb5_context context,
378	krb5_crypto crypto,
379	const krb5_data *in,
380	krb5_data *out)
381{
382    struct _krb5_checksum_type *ct = crypto->et->checksum;
383    krb5_error_code ret;
384    Checksum result;
385    krb5_keyblock *derived;
386
387    result.cksumtype = ct->type;
388    ret = krb5_data_alloc(&result.checksum, ct->checksumsize);
389    if (ret) {
390	krb5_set_error_message(context, ret, N_("malloc: out memory", ""));
391	return ret;
392    }
393
394    ret = (*ct->checksum)(context, NULL, in->data, in->length, 0, &result);
395    if (ret) {
396	krb5_data_free(&result.checksum);
397	return ret;
398    }
399
400    if (result.checksum.length < crypto->et->blocksize)
401	krb5_abortx(context, "internal prf error");
402
403    derived = NULL;
404    ret = krb5_derive_key(context, crypto->key.key,
405			  crypto->et->type, "prf", 3, &derived);
406    if (ret)
407	krb5_abortx(context, "krb5_derive_key");
408
409    ret = krb5_data_alloc(out, crypto->et->blocksize);
410    if (ret)
411	krb5_abortx(context, "malloc failed");
412
413    {
414	const EVP_CIPHER *c = (*crypto->et->keytype->evp)();
415	EVP_CIPHER_CTX ctx;
416
417	EVP_CIPHER_CTX_init(&ctx); /* ivec all zero */
418	EVP_CipherInit_ex(&ctx, c, NULL, derived->keyvalue.data, NULL, 1);
419	EVP_Cipher(&ctx, out->data, result.checksum.data,
420		   crypto->et->blocksize);
421	EVP_CIPHER_CTX_cleanup(&ctx);
422    }
423
424    krb5_data_free(&result.checksum);
425    krb5_free_keyblock(context, derived);
426
427    return ret;
428}
429
430struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha1 = {
431    ETYPE_AES128_CTS_HMAC_SHA1_96,
432    "aes128-cts-hmac-sha1-96",
433    16,
434    1,
435    16,
436    &keytype_aes128,
437    &_krb5_checksum_sha1,
438    &_krb5_checksum_hmac_sha1_aes128,
439    F_DERIVED,
440    _krb5_evp_encrypt_cts,
441    16,
442    AES_PRF
443};
444
445struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha1 = {
446    ETYPE_AES256_CTS_HMAC_SHA1_96,
447    "aes256-cts-hmac-sha1-96",
448    16,
449    1,
450    16,
451    &keytype_aes256,
452    &_krb5_checksum_sha1,
453    &_krb5_checksum_hmac_sha1_aes256,
454    F_DERIVED,
455    _krb5_evp_encrypt_cts,
456    16,
457    AES_PRF
458};
459
460#endif
461