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