1/*
2 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7/*
8 * Copyright (C) 1998 by the FundsXpress, INC.
9 *
10 * All rights reserved.
11 *
12 * Export of this software from the United States of America may require
13 * a specific license from the United States Government.  It is the
14 * responsibility of any person or organization contemplating export to
15 * obtain such a license before exporting.
16 *
17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18 * distribute this software and its documentation for any purpose and
19 * without fee is hereby granted, provided that the above copyright
20 * notice appear in all copies and that both that copyright notice and
21 * this permission notice appear in supporting documentation, and that
22 * the name of FundsXpress. not be used in advertising or publicity pertaining
23 * to distribution of the software without specific, written prior
24 * permission.  FundsXpress makes no representations about the suitability of
25 * this software for any purpose.  It is provided "as is" without express
26 * or implied warranty.
27 *
28 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31 */
32
33#include "k5-int.h"
34#include "dk.h"
35
36#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */
37
38/* the spec says that the confounder size and padding are specific to
39   the encryption algorithm.  This code (dk_encrypt_length and
40   dk_encrypt) assume the confounder is always the blocksize, and the
41   padding is always zero bytes up to the blocksize.  If these
42   assumptions ever fails, the keytype table should be extended to
43   include these bits of info. */
44
45void
46krb5_dk_encrypt_length(const struct krb5_enc_provider *enc,
47		       const struct krb5_hash_provider *hash,
48		       size_t inputlen, size_t *length)
49{
50    size_t blocksize, hashsize;
51
52    blocksize = enc->block_size;
53    hashsize = hash->hashsize;
54    *length = krb5_roundup(blocksize+inputlen, blocksize) + hashsize;
55}
56
57krb5_error_code
58krb5_dk_encrypt(krb5_context context,
59		const struct krb5_enc_provider *enc,
60		const struct krb5_hash_provider *hash,
61		const krb5_keyblock *key, krb5_keyusage usage,
62		const krb5_data *ivec, const krb5_data *input,
63		krb5_data *output)
64{
65    size_t blocksize, plainlen, enclen;
66    krb5_error_code ret;
67    krb5_data d1, d2;
68    unsigned char *plaintext = NULL, *cn;
69    krb5_keyblock *derived_encr_key = NULL;
70    krb5_keyblock *derived_hmac_key = NULL;
71
72    KRB5_LOG0(KRB5_INFO, "krb5_dk_encrypt() start");
73
74    /*
75     * Derive the encryption and hmac keys.
76     * This routine is optimized to fetch the DK
77     * from the original key's DK list.
78     */
79    ret = init_derived_keydata(context, enc,
80			    (krb5_keyblock *)key,
81			    usage,
82			    &derived_encr_key,
83			    &derived_hmac_key);
84    if (ret)
85	    return (ret);
86
87    blocksize = enc->block_size;
88    plainlen = krb5_roundup(blocksize+input->length, blocksize);
89
90    krb5_dk_encrypt_length(enc, hash, input->length, &enclen);
91
92    if (output->length < enclen)
93	return(KRB5_BAD_MSIZE);
94
95    if ((plaintext = (unsigned char *) MALLOC(plainlen)) == NULL) {
96	return(ENOMEM);
97    }
98
99    /* put together the plaintext */
100    d1.length = blocksize;
101    d1.data = (char *) plaintext;
102
103    if ((ret = krb5_c_random_make_octets(context, &d1)))
104	goto cleanup;
105
106    (void) memcpy(plaintext+blocksize, input->data, input->length);
107
108    (void) memset(plaintext+blocksize+input->length, 0,
109	   plainlen - (blocksize+input->length));
110
111    /* encrypt the plaintext */
112    d1.length = plainlen;
113    d1.data = (char *) plaintext;
114
115    d2.length = plainlen;
116    d2.data = output->data;
117
118    /*
119     * Always use the derived encryption key here.
120     */
121    if ((ret = ((*(enc->encrypt))(context, derived_encr_key,
122		ivec, &d1, &d2))))
123	goto cleanup;
124
125    if (ivec != NULL && ivec->length == blocksize)
126	cn = (unsigned char *) d2.data + d2.length - blocksize;
127    else
128	cn = NULL;
129
130    /* hash the plaintext */
131
132    d2.length = enclen - plainlen;
133    d2.data = output->data+plainlen;
134
135    output->length = enclen;
136
137#ifdef _KERNEL
138    if ((ret = krb5_hmac(context, derived_hmac_key, &d1, &d2))) {
139	(void) memset(d2.data, 0, d2.length);
140	goto cleanup;
141    }
142#else
143    if ((ret = krb5_hmac(context, hash, derived_hmac_key,
144			1, &d1, &d2))) {
145	(void) memset(d2.data, 0, d2.length);
146	goto cleanup;
147    }
148#endif /* _KERNEL */
149
150    /* update ivec */
151    if (cn != NULL)
152	(void) memcpy(ivec->data, cn, blocksize);
153
154    /* ret is set correctly by the prior call */
155
156cleanup:
157    FREE(plaintext, plainlen);
158
159    KRB5_LOG(KRB5_INFO, "krb5_dk_encrypt() end, ret=%d\n", ret);
160    return(ret);
161}
162
163/* Not necessarily "AES", per se, but "a CBC+CTS mode block cipher
164   with a 96-bit truncated HMAC".  */
165/*ARGSUSED*/
166void
167krb5int_aes_encrypt_length(enc, hash, inputlen, length)
168	const struct krb5_enc_provider *enc;
169	const struct krb5_hash_provider *hash;
170	size_t inputlen;
171	size_t *length;
172{
173    size_t blocksize, hashsize;
174
175    blocksize = enc->block_size;
176    hashsize = 96 / 8;
177
178    /* No roundup, since CTS requires no padding once we've hit the
179       block size.  */
180    *length = blocksize+inputlen + hashsize;
181}
182
183/*ARGSUSED*/
184static krb5_error_code
185trunc_hmac (krb5_context context,
186	    const struct krb5_hash_provider *hash,
187            const krb5_keyblock *ki, int num,
188            const krb5_data *input, krb5_data *output)
189{
190    size_t hashsize;
191    krb5_error_code ret;
192    char buff[256]; /* sufficiently large enough to hold current hmacs */
193    krb5_data tmphash;
194
195    hashsize = hash->hashsize;
196    if (hashsize < output->length)
197	return (KRB5_CRYPTO_INTERNAL);
198
199    tmphash.length = hashsize;
200    tmphash.data = buff;
201
202#ifdef _KERNEL
203    ret = krb5_hmac(context, ki, input, &tmphash);
204#else
205    ret = krb5_hmac(context, hash, ki, num, input, &tmphash);
206#endif /* _KERNEL */
207
208    if (ret)
209	(void) memset(output->data, 0, output->length);
210    else
211	/* truncate the HMAC output accordingly */
212	(void) memcpy(output->data, tmphash.data, output->length);
213
214    (void) memset(buff, 0, sizeof(buff));
215    return (ret);
216}
217
218
219krb5_error_code
220krb5int_aes_dk_encrypt(krb5_context context,
221	const struct krb5_enc_provider *enc,
222	const struct krb5_hash_provider *hash,
223	const krb5_keyblock *key,
224	krb5_keyusage usage,
225	const krb5_data *ivec,
226	const krb5_data *input,
227	krb5_data *output)
228{
229    size_t blocksize, plainlen, enclen;
230    krb5_error_code ret;
231    krb5_data d1, d2;
232    unsigned char *plaintext, *cn;
233    krb5_keyblock *derived_encr_key = NULL;
234    krb5_keyblock *derived_hmac_key = NULL;
235
236    /*
237     * Derive the encryption and hmac keys.
238     * This routine is optimized to fetch the DK
239     * from the original key's DK list.
240     */
241    ret = init_derived_keydata(context, enc,
242                            (krb5_keyblock *)key,
243                            usage,
244                            &derived_encr_key,
245                            &derived_hmac_key);
246    if (ret)
247            return (ret);
248
249    blocksize = enc->block_size;
250    plainlen = blocksize+input->length;
251
252    krb5int_aes_encrypt_length(enc, hash, input->length, &enclen);
253
254    /* key->length, ivec will be tested in enc->encrypt */
255    if (output->length < enclen)
256	return(KRB5_BAD_MSIZE);
257
258    if ((plaintext = (unsigned char *) MALLOC(plainlen)) == NULL) {
259	 return(ENOMEM);
260    }
261
262    d1.length = blocksize;
263    d1.data = (char *)plaintext;
264
265    if ((ret = krb5_c_random_make_octets(context, &d1)))
266	goto cleanup;
267
268    (void) memcpy(plaintext+blocksize, input->data, input->length);
269
270    /* Ciphertext stealing; there should be no more.  */
271    if (plainlen != blocksize + input->length) {
272	ret = KRB5_BAD_KEYSIZE;
273	goto cleanup;
274    }
275
276    /* encrypt the plaintext */
277
278    d1.length = plainlen;
279    d1.data = (char *)plaintext;
280
281    d2.length = plainlen;
282    d2.data = output->data;
283
284    if ((ret = ((*(enc->encrypt))(context, derived_encr_key, ivec, &d1, &d2))))
285	goto cleanup;
286
287    if (ivec != NULL && ivec->length == blocksize) {
288	int nblocks = (d2.length + blocksize - 1) / blocksize;
289	cn = (uchar_t *) d2.data + blocksize * (nblocks - 2);
290    } else {
291	cn = NULL;
292    }
293
294    /* hash the plaintext */
295    d2.length = enclen - plainlen;
296    d2.data = output->data+plainlen;
297    if (d2.length != 96 / 8)
298	goto cleanup;
299
300    if ((ret = trunc_hmac(context, hash, derived_hmac_key, 1, &d1, &d2))) {
301	(void) memset(d2.data, 0, d2.length);
302	goto cleanup;
303    }
304
305    output->length = enclen;
306
307    /* update ivec */
308    if (cn != NULL) {
309	(void) memcpy(ivec->data, cn, blocksize);
310    }
311
312    /* ret is set correctly by the prior call */
313cleanup:
314    (void) memset(plaintext, 0, plainlen);
315
316    FREE(plaintext, plainlen);
317
318    return(ret);
319}
320