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