1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * Copyright (C) 1998 by the FundsXpress, INC.
8 *
9 * All rights reserved.
10 *
11 * Export of this software from the United States of America may require
12 * a specific license from the United States Government. It is the
13 * responsibility of any person or organization contemplating export to
14 * obtain such a license before exporting.
15 *
16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17 * distribute this software and its documentation for any purpose and
18 * without fee is hereby granted, provided that the above copyright
19 * notice appear in all copies and that both that copyright notice and
20 * this permission notice appear in supporting documentation, and that
21 * the name of FundsXpress. not be used in advertising or publicity pertaining
22 * to distribution of the software without specific, written prior
23 * permission. FundsXpress makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
26 *
27 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
28 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
29 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30 */
31
32 #include "k5-int.h"
33 #include "dk.h"
34
35 #define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */
36
37 /*
38 * Search for a derived key based on the input key,
39 * the usage constant and the dkid byte.
40 *
41 * Return *derived key on success, NULL on failure.
42 */
43 krb5_keyblock *
find_derived_key(krb5_keyusage usage,uchar_t dkid,krb5_keyblock * key)44 find_derived_key(krb5_keyusage usage, uchar_t dkid,
45 krb5_keyblock *key)
46 {
47 krb5_dk_node *dknode = key->dk_list;
48
49 while (dknode != NULL) {
50 if (usage == dknode->usage &&
51 dkid == dknode->dkid) {
52 KRB5_LOG1(KRB5_INFO,
53 "find_derived_key - MATCH FOUND %d 0x%0x",
54 usage, dkid);
55 return(dknode->derived_key);
56 }
57 dknode = dknode->next;
58 }
59 KRB5_LOG0(KRB5_INFO, "find_derived_key - no match");
60 return(NULL);
61 }
62
63 /*
64 * Add a derived key to the dk_list for the indicated key.
65 */
66 krb5_error_code
add_derived_key(krb5_keyblock * key,krb5_keyusage usage,uchar_t dkid,krb5_keyblock * derived_key)67 add_derived_key(krb5_keyblock *key,
68 krb5_keyusage usage, uchar_t dkid,
69 krb5_keyblock *derived_key)
70 {
71 krb5_dk_node *dknode;
72 KRB5_LOG1(KRB5_INFO, "add_derived_key: %d 0x%0x",
73 usage, dkid);
74
75 if (key->dk_list == NULL) {
76 key->dk_list = MALLOC(sizeof(krb5_dk_node));
77 if (key->dk_list == NULL)
78 return (ENOMEM);
79 dknode = key->dk_list;
80 } else {
81 dknode = key->dk_list;
82 /*
83 * Find last derived key in list
84 */
85 while (dknode->next != NULL)
86 dknode = dknode->next;
87 dknode->next = MALLOC(sizeof(krb5_dk_node));
88 if (dknode->next == NULL)
89 return (ENOMEM);
90 dknode = dknode->next;
91 }
92 dknode->usage = usage;
93 dknode->dkid = dkid;
94 dknode->derived_key = derived_key;
95 dknode->next = NULL;
96
97 return (0);
98 }
99
100 /*
101 * Utility function to create a new keyblock
102 * Return NULL on failure.
103 */
104 krb5_keyblock *
krb5_create_derived_keyblock(int keysize)105 krb5_create_derived_keyblock(int keysize)
106 {
107 krb5_keyblock *key = MALLOC(sizeof(krb5_keyblock));
108
109 KRB5_LOG0(KRB5_INFO, "krb5_create_derived_keyblock()");
110 if (key == NULL)
111 return (NULL);
112
113 bzero(key, sizeof(krb5_keyblock));
114
115 key->length = keysize;
116 key->contents = (uchar_t *)MALLOC(key->length);
117 if (key->contents == NULL) {
118 FREE(key, sizeof(krb5_keyblock));
119 return (NULL);
120 }
121
122 bzero(key->contents, key->length);
123 #ifdef _KERNEL
124 key->kef_mt = CRYPTO_MECH_INVALID;
125 key->key_tmpl = NULL;
126 #else
127 key->hKey = CK_INVALID_HANDLE;
128 #endif /* _KERNEL */
129 return(key);
130 }
131
132 /*
133 * initialize the derived key values in the context.
134 */
135 krb5_error_code
init_derived_keydata(krb5_context context,const struct krb5_enc_provider * enc,krb5_keyblock * key,krb5_keyusage usage,krb5_keyblock ** d_encr_key,krb5_keyblock ** d_hmac_key)136 init_derived_keydata(krb5_context context,
137 const struct krb5_enc_provider *enc,
138 krb5_keyblock *key,
139 krb5_keyusage usage,
140 krb5_keyblock **d_encr_key,
141 krb5_keyblock **d_hmac_key)
142 {
143 krb5_error_code rv = 0;
144 unsigned char constantdata[K5CLENGTH];
145 krb5_keyblock *cached_key;
146 krb5_data d1;
147
148 KRB5_LOG0(KRB5_INFO,"init_ef_derived_keydata().");
149
150 /*
151 * Get a derived encryption key, either from the cache
152 * or by calculation.
153 */
154 cached_key = find_derived_key(usage, DK_ENCR_KEY_BYTE, key);
155 if (cached_key != NULL)
156 *d_encr_key = cached_key;
157 else {
158 *d_encr_key = krb5_create_derived_keyblock(key->length);
159 if (*d_encr_key == NULL) {
160 return (ENOMEM);
161 }
162
163 (*d_encr_key)->enctype = key->enctype;
164
165 constantdata[0] = (usage>>24)&0xff;
166 constantdata[1] = (usage>>16)&0xff;
167 constantdata[2] = (usage>>8)&0xff;
168 constantdata[3] = usage&0xff;
169 constantdata[4] = DK_ENCR_KEY_BYTE;
170
171 d1.data = (char *)constantdata;
172 d1.length = sizeof(constantdata);
173 rv = krb5_derive_key(context, enc, key,
174 *d_encr_key, &d1);
175 if (rv != 0) {
176 krb5_free_keyblock(context, *d_encr_key);
177 *d_encr_key = NULL;
178 return (rv);
179 }
180 rv = add_derived_key(key, usage, DK_ENCR_KEY_BYTE,
181 *d_encr_key);
182
183 if (rv != 0) {
184 krb5_free_keyblock(context, *d_encr_key);
185 *d_encr_key = NULL;
186 return (rv);
187 }
188 }
189
190 /*
191 * Get a derived HMAC key, either from the cache
192 * or by calculation.
193 */
194 cached_key = find_derived_key(usage, DK_HASH_KEY_BYTE, key);
195 if (cached_key != NULL)
196 *d_hmac_key = cached_key;
197 else {
198 *d_hmac_key = krb5_create_derived_keyblock(key->length);
199 if (*d_hmac_key == NULL) {
200 return (ENOMEM);
201 }
202 (*d_hmac_key)->enctype = key->enctype;
203
204 constantdata[0] = (usage>>24)&0xff;
205 constantdata[1] = (usage>>16)&0xff;
206 constantdata[2] = (usage>>8)&0xff;
207 constantdata[3] = usage&0xff;
208 constantdata[4] = DK_HASH_KEY_BYTE;
209
210 d1.data = (char *)constantdata;
211 d1.length = sizeof(constantdata);
212 rv = krb5_derive_key(context, enc, key, *d_hmac_key, &d1);
213 if (rv != 0) {
214 krb5_free_keyblock(context, *d_hmac_key);
215 *d_hmac_key = NULL;
216 return (rv);
217 }
218 #ifdef _KERNEL
219 /*
220 * By default, derived keys get the "mech_type"
221 * that was associated with their parent.
222 * we need to switch the mech type of the derived HMAC key
223 * to correspond to the mech type for the hmac key.
224 */
225 if ((*d_hmac_key)->kef_mt != context->kef_hash_mt) {
226 (*d_hmac_key)->kef_mt = context->kef_hash_mt;
227
228 if ((*d_hmac_key)->key_tmpl != NULL) {
229 crypto_destroy_ctx_template((*d_hmac_key)->key_tmpl);
230 (*d_hmac_key)->key_tmpl = NULL;
231 }
232 rv = update_key_template(*d_hmac_key);
233
234 if (rv != 0) {
235 krb5_free_keyblock(context, *d_hmac_key);
236 *d_hmac_key = NULL;
237 return (rv);
238 }
239 }
240 #endif /* _KERNEL */
241 if (rv == 0) {
242 rv = add_derived_key(key, usage, DK_HASH_KEY_BYTE,
243 *d_hmac_key);
244 if (rv != 0) {
245 krb5_free_keyblock(context, *d_hmac_key);
246 *d_hmac_key = NULL;
247 return (rv);
248 }
249 }
250 }
251 KRB5_LOG0(KRB5_INFO,"init_ef_derived_keydata() end.");
252 return (rv);
253 }
254
255
256 krb5_error_code
krb5_derive_key(context,enc,inkey,outkey,in_constant)257 krb5_derive_key(context, enc, inkey, outkey, in_constant)
258 krb5_context context;
259 krb5_const struct krb5_enc_provider *enc;
260 krb5_const krb5_keyblock *inkey;
261 krb5_keyblock *outkey;
262 krb5_const krb5_data *in_constant;
263 {
264 size_t blocksize, keybytes, keylength, n;
265 unsigned char *inblockdata, *outblockdata, *rawkey;
266 krb5_data inblock, outblock;
267 krb5_error_code ret = 0;
268
269 KRB5_LOG0(KRB5_INFO, "krb5_derive_key() start");
270
271 blocksize = enc->block_size;
272 keybytes = enc->keybytes;
273 keylength = enc->keylength;
274
275
276 if ((inkey->length != keylength) ||
277 (outkey->length != keylength))
278 return(KRB5_CRYPTO_INTERNAL);
279
280 /* allocate and set up buffers */
281 if ((inblockdata = (unsigned char *) MALLOC(blocksize)) == NULL)
282 return(ENOMEM);
283
284 if ((outblockdata = (unsigned char *) MALLOC(blocksize)) == NULL) {
285 FREE(inblockdata, blocksize);
286 return(ENOMEM);
287 }
288
289 if ((rawkey = (unsigned char *) MALLOC(keybytes)) == NULL) {
290 FREE(outblockdata, blocksize);
291 FREE(inblockdata, blocksize);
292 return(ENOMEM);
293 }
294
295 inblock.data = (char *) inblockdata;
296 inblock.length = blocksize;
297
298 outblock.data = (char *) outblockdata;
299 outblock.length = blocksize;
300
301 /* initialize the input block */
302 if (in_constant->length == inblock.length) {
303 (void) memcpy(inblock.data, in_constant->data, inblock.length);
304 } else {
305 krb5_nfold(in_constant->length*8,
306 (krb5_const unsigned char *) in_constant->data,
307 inblock.length*8, (unsigned char *) inblock.data);
308 }
309
310 /* loop encrypting the blocks until enough key bytes are generated */
311 n = 0;
312 while (n < keybytes) {
313 ret = (*(enc->encrypt))(context, inkey, 0, &inblock, &outblock);
314
315 if (ret) {
316 KRB5_LOG(KRB5_INFO, "krb5_derive_key() encrypt error: %d", ret);
317 goto cleanup;
318 }
319
320 if ((keybytes - n) <= outblock.length) {
321 (void) memcpy(rawkey+n, outblock.data, (keybytes - n));
322 break;
323 }
324
325 (void) memcpy(rawkey+n, outblock.data, outblock.length);
326 (void) memcpy(inblock.data, outblock.data, outblock.length);
327 n += outblock.length;
328 }
329
330 /* postprocess the key */
331 inblock.data = (char *) rawkey;
332 inblock.length = keybytes;
333
334 outkey->enctype = inkey->enctype;
335 ret = (*(enc->make_key))(context, &inblock, outkey);
336
337 /* clean memory, free resources and exit */
338 cleanup:
339 (void) memset(inblockdata, 0, blocksize);
340 (void) memset(outblockdata, 0, blocksize);
341 (void) memset(rawkey, 0, keybytes);
342
343 FREE(rawkey, keybytes);
344 FREE(outblockdata, blocksize);
345 FREE(inblockdata, blocksize);
346
347 KRB5_LOG0(KRB5_INFO, "krb5_derive_key() end");
348 return(ret);
349 }
350