dk_encrypt.c revision 7934:6aeeafc994de
180709Sjake/*
280709Sjake * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
380709Sjake * Use is subject to license terms.
480709Sjake */
580709Sjake
680709Sjake
780709Sjake/*
880709Sjake * Copyright (C) 1998 by the FundsXpress, INC.
980709Sjake *
1080709Sjake * All rights reserved.
1180709Sjake *
1280709Sjake * Export of this software from the United States of America may require
1380709Sjake * a specific license from the United States Government.  It is the
1480709Sjake * responsibility of any person or organization contemplating export to
1580709Sjake * obtain such a license before exporting.
1680709Sjake *
1780709Sjake * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
1880709Sjake * distribute this software and its documentation for any purpose and
1980709Sjake * without fee is hereby granted, provided that the above copyright
2080709Sjake * notice appear in all copies and that both that copyright notice and
2180709Sjake * this permission notice appear in supporting documentation, and that
2280709Sjake * the name of FundsXpress. not be used in advertising or publicity pertaining
2380709Sjake * to distribution of the software without specific, written prior
2480709Sjake * permission.  FundsXpress makes no representations about the suitability of
2580709Sjake * this software for any purpose.  It is provided "as is" without express
2680709Sjake * or implied warranty.
2780709Sjake *
2880709Sjake * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
2980709Sjake * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
3080709Sjake * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
3180709Sjake */
3280709Sjake
3380709Sjake#include "k5-int.h"
3480709Sjake#include "dk.h"
3597446Sjake
3680709Sjake#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */
3780709Sjake
3880709Sjake/* the spec says that the confounder size and padding are specific to
3980709Sjake   the encryption algorithm.  This code (dk_encrypt_length and
4080709Sjake   dk_encrypt) assume the confounder is always the blocksize, and the
4180709Sjake   padding is always zero bytes up to the blocksize.  If these
4280709Sjake   assumptions ever fails, the keytype table should be extended to
4397027Sjake   include these bits of info. */
4497027Sjake
4597027Sjakevoid
4697027Sjakekrb5_dk_encrypt_length(const struct krb5_enc_provider *enc,
4797027Sjake		       const struct krb5_hash_provider *hash,
4880709Sjake		       size_t inputlen, size_t *length)
4997027Sjake{
5097027Sjake    size_t blocksize, hashsize;
5197027Sjake
5297027Sjake    blocksize = enc->block_size;
5397027Sjake    hashsize = hash->hashsize;
5480709Sjake    *length = krb5_roundup(blocksize+inputlen, blocksize) + hashsize;
5597027Sjake}
5697027Sjake
5788652Sjakekrb5_error_code
5888652Sjakekrb5_dk_encrypt(krb5_context context,
5988652Sjake		const struct krb5_enc_provider *enc,
6088652Sjake		const struct krb5_hash_provider *hash,
6188652Sjake		const krb5_keyblock *key, krb5_keyusage usage,
6280709Sjake		const krb5_data *ivec, const krb5_data *input,
6388652Sjake		krb5_data *output)
6488652Sjake{
6588652Sjake    size_t blocksize, plainlen, enclen;
6688652Sjake    krb5_error_code ret;
6788652Sjake    krb5_data d1, d2;
6888652Sjake    unsigned char *plaintext = NULL, *cn;
6988652Sjake    krb5_keyblock *derived_encr_key = NULL;
7097027Sjake    krb5_keyblock *derived_hmac_key = NULL;
7180709Sjake
7280709Sjake    KRB5_LOG0(KRB5_INFO, "krb5_dk_encrypt() start");
7388652Sjake
7482903Sjake    /*
7588652Sjake     * Derive the encryption and hmac keys.
7688652Sjake     * This routine is optimized to fetch the DK
7788652Sjake     * from the original key's DK list.
7888652Sjake     */
7988652Sjake    ret = init_derived_keydata(context, enc,
8088652Sjake			    (krb5_keyblock *)key,
8188652Sjake			    usage,
8288652Sjake			    &derived_encr_key,
8380709Sjake			    &derived_hmac_key);
8491224Sjake    if (ret)
8591224Sjake	    return (ret);
8697027Sjake
8797027Sjake    blocksize = enc->block_size;
8897027Sjake    plainlen = krb5_roundup(blocksize+input->length, blocksize);
8997027Sjake
9097027Sjake    krb5_dk_encrypt_length(enc, hash, input->length, &enclen);
9197027Sjake
9297027Sjake    if (output->length < enclen)
9397027Sjake	return(KRB5_BAD_MSIZE);
9484182Sjake
9597027Sjake    if ((plaintext = (unsigned char *) MALLOC(plainlen)) == NULL) {
9697027Sjake	return(ENOMEM);
9797027Sjake    }
9897027Sjake
9997027Sjake    /* put together the plaintext */
10097027Sjake    d1.length = blocksize;
10197446Sjake    d1.data = (char *) plaintext;
10297446Sjake
10397446Sjake    if ((ret = krb5_c_random_make_octets(context, &d1)))
10497446Sjake	goto cleanup;
10597027Sjake
10683053Sobrien    (void) memcpy(plaintext+blocksize, input->data, input->length);
10791224Sjake
10880709Sjake    (void) memset(plaintext+blocksize+input->length, 0,
10997446Sjake	   plainlen - (blocksize+input->length));
11097446Sjake
11180709Sjake    /* encrypt the plaintext */
11280709Sjake    d1.length = plainlen;
11391224Sjake    d1.data = (char *) plaintext;
11497027Sjake
11580709Sjake    d2.length = plainlen;
11697027Sjake    d2.data = output->data;
11780709Sjake
11880709Sjake    /*
11980709Sjake     * Always use the derived encryption key here.
12097027Sjake     */
12180709Sjake    if ((ret = ((*(enc->encrypt))(context, derived_encr_key,
12297027Sjake		ivec, &d1, &d2))))
12380709Sjake	goto cleanup;
12480709Sjake
12580709Sjake    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