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