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 
45 void
krb5_dk_encrypt_length(const struct krb5_enc_provider * enc,const struct krb5_hash_provider * hash,size_t inputlen,size_t * length)46 krb5_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 
57 krb5_error_code
krb5_dk_encrypt(krb5_context context,const struct krb5_enc_provider * enc,const struct krb5_hash_provider * hash,const krb5_keyblock * key,krb5_keyusage usage,const krb5_data * ivec,const krb5_data * input,krb5_data * output)58 krb5_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 
156 cleanup:
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*/
166 void
krb5int_aes_encrypt_length(enc,hash,inputlen,length)167 krb5int_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*/
184 static krb5_error_code
trunc_hmac(krb5_context context,const struct krb5_hash_provider * hash,const krb5_keyblock * ki,int num,const krb5_data * input,krb5_data * output)185 trunc_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 
219 krb5_error_code
krb5int_aes_dk_encrypt(krb5_context context,const struct krb5_enc_provider * enc,const struct krb5_hash_provider * hash,const krb5_keyblock * key,krb5_keyusage usage,const krb5_data * ivec,const krb5_data * input,krb5_data * output)220 krb5int_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 */
313 cleanup:
314     (void) memset(plaintext, 0, plainlen);
315 
316     FREE(plaintext, plainlen);
317 
318     return(ret);
319 }
320