xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/crypto/dk/derive.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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 /*
39  * Search for a derived key based on the input key,
40  * the  usage constant and the dkid byte.
41  *
42  * Return *derived key on success, NULL on failure.
43  */
44 krb5_keyblock *
45 find_derived_key(krb5_keyusage usage, uchar_t dkid,
46 		krb5_keyblock *key)
47 {
48 	krb5_dk_node *dknode = key->dk_list;
49 
50 	while (dknode != NULL) {
51 		if (usage == dknode->usage &&
52 		    dkid == dknode->dkid) {
53 			KRB5_LOG1(KRB5_INFO,
54 				"find_derived_key - MATCH FOUND %d 0x%0x",
55 				usage, dkid);
56 			return(dknode->derived_key);
57 		}
58 		dknode = dknode->next;
59 	}
60 	KRB5_LOG0(KRB5_INFO, "find_derived_key - no match");
61 	return(NULL);
62 }
63 
64 /*
65  * Add a derived key to the dk_list for the indicated key.
66  */
67 krb5_error_code
68 add_derived_key(krb5_keyblock *key,
69 		krb5_keyusage usage, uchar_t dkid,
70 		krb5_keyblock *derived_key)
71 {
72 	krb5_dk_node *dknode;
73 	KRB5_LOG1(KRB5_INFO, "add_derived_key: %d 0x%0x",
74 		usage, dkid);
75 
76 	if (key->dk_list == NULL) {
77 		key->dk_list = MALLOC(sizeof(krb5_dk_node));
78 		if (key->dk_list == NULL)
79 			return (ENOMEM);
80 		dknode = key->dk_list;
81 	} else {
82 		dknode = key->dk_list;
83 		/*
84 		 * Find last derived key in list
85 		 */
86 		while (dknode->next != NULL)
87 			dknode = dknode->next;
88 		dknode->next = MALLOC(sizeof(krb5_dk_node));
89 		if (dknode->next == NULL)
90 			return (ENOMEM);
91 		dknode = dknode->next;
92 	}
93 	dknode->usage = usage;
94 	dknode->dkid = dkid;
95 	dknode->derived_key = derived_key;
96 	dknode->next = NULL;
97 
98 	return (0);
99 }
100 
101 /*
102  * Utility function to create a new keyblock
103  * Return NULL on failure.
104  */
105 krb5_keyblock *
106 krb5_create_derived_keyblock(int keysize)
107 {
108 	krb5_keyblock *key = MALLOC(sizeof(krb5_keyblock));
109 
110 	KRB5_LOG0(KRB5_INFO, "krb5_create_derived_keyblock()");
111 	if (key == NULL)
112 		return (NULL);
113 
114 	bzero(key, sizeof(krb5_keyblock));
115 
116 	key->length = keysize;
117 	key->contents = (uchar_t *)MALLOC(key->length);
118 	if (key->contents == NULL) {
119 		FREE(key, sizeof(krb5_keyblock));
120 		return (NULL);
121 	}
122 
123 	bzero(key->contents, key->length);
124 #ifdef _KERNEL
125 	key->kef_mt = CRYPTO_MECH_INVALID;
126 	key->key_tmpl = NULL;
127 #else
128 	key->hKey = CK_INVALID_HANDLE;
129 #endif /* _KERNEL */
130 	return(key);
131 }
132 
133 /*
134  * initialize the derived key values in the context.
135  */
136 krb5_error_code
137 init_derived_keydata(krb5_context context,
138 		    const struct krb5_enc_provider *enc,
139 		    krb5_keyblock *key,
140 		    krb5_keyusage usage,
141 		    krb5_keyblock **d_encr_key,
142 		    krb5_keyblock **d_hmac_key)
143 {
144 	krb5_error_code rv = 0;
145 	unsigned char constantdata[K5CLENGTH];
146 	krb5_keyblock *cached_key;
147 	krb5_data d1;
148 
149 	KRB5_LOG0(KRB5_INFO,"init_ef_derived_keydata().");
150 
151 	/*
152 	 * Get a derived encryption key, either from the cache
153 	 * or by calculation.
154 	 */
155 	cached_key = find_derived_key(usage, DK_ENCR_KEY_BYTE, key);
156 	if (cached_key != NULL)
157 		*d_encr_key = cached_key;
158 	else {
159 		*d_encr_key = krb5_create_derived_keyblock(key->length);
160 		if (*d_encr_key == NULL) {
161 			return (ENOMEM);
162 		}
163 
164 		(*d_encr_key)->enctype = key->enctype;
165 
166 		constantdata[0] = (usage>>24)&0xff;
167 		constantdata[1] = (usage>>16)&0xff;
168 		constantdata[2] = (usage>>8)&0xff;
169 		constantdata[3] = usage&0xff;
170 		constantdata[4] = DK_ENCR_KEY_BYTE;
171 
172 		d1.data = (char *)constantdata;
173 		d1.length = sizeof(constantdata);
174 		rv = krb5_derive_key(context, enc, key,
175 				    *d_encr_key, &d1);
176 		if (rv != 0) {
177 			krb5_free_keyblock(context, *d_encr_key);
178 			*d_encr_key = NULL;
179 			return (rv);
180 		}
181 		rv = add_derived_key(key, usage, DK_ENCR_KEY_BYTE,
182 			    *d_encr_key);
183 
184 		if (rv != 0) {
185 			krb5_free_keyblock(context, *d_encr_key);
186 			*d_encr_key = NULL;
187 			return (rv);
188 		}
189 	}
190 
191 	/*
192 	 * Get a derived HMAC key, either from the cache
193 	 * or by calculation.
194 	 */
195 	cached_key = find_derived_key(usage, DK_HASH_KEY_BYTE, key);
196 	if (cached_key != NULL)
197 		*d_hmac_key = cached_key;
198 	else {
199 		*d_hmac_key = krb5_create_derived_keyblock(key->length);
200 		if (*d_hmac_key == NULL) {
201 			return (ENOMEM);
202 		}
203 		(*d_hmac_key)->enctype = key->enctype;
204 
205 		constantdata[0] = (usage>>24)&0xff;
206 		constantdata[1] = (usage>>16)&0xff;
207 		constantdata[2] = (usage>>8)&0xff;
208 		constantdata[3] = usage&0xff;
209 		constantdata[4] = DK_HASH_KEY_BYTE;
210 
211 		d1.data = (char *)constantdata;
212 		d1.length = sizeof(constantdata);
213 		rv = krb5_derive_key(context, enc, key, *d_hmac_key, &d1);
214 		if (rv != 0) {
215 			krb5_free_keyblock(context, *d_hmac_key);
216 			*d_hmac_key = NULL;
217 			return (rv);
218 		}
219 #ifdef _KERNEL
220 		/*
221 		 * By default, derived keys get the "mech_type"
222 		 * that was associated with their parent.
223 		 * we need to switch the mech type of the derived HMAC key
224 		 * to correspond to the mech type for the hmac key.
225 		 */
226 		if ((*d_hmac_key)->kef_mt != context->kef_hash_mt) {
227 			(*d_hmac_key)->kef_mt = context->kef_hash_mt;
228 
229 			if ((*d_hmac_key)->key_tmpl != NULL) {
230 				crypto_destroy_ctx_template((*d_hmac_key)->key_tmpl);
231 				(*d_hmac_key)->key_tmpl = NULL;
232 			}
233 			rv = update_key_template(*d_hmac_key);
234 
235 			if (rv != 0) {
236 				krb5_free_keyblock(context, *d_hmac_key);
237 				*d_hmac_key = NULL;
238 				return (rv);
239 			}
240 		}
241 #endif /* _KERNEL */
242 		if (rv == 0) {
243 			rv = add_derived_key(key, usage, DK_HASH_KEY_BYTE,
244 				    *d_hmac_key);
245 			if (rv != 0) {
246 				krb5_free_keyblock(context, *d_hmac_key);
247 				*d_hmac_key = NULL;
248 				return (rv);
249 			}
250 		}
251 	}
252 	KRB5_LOG0(KRB5_INFO,"init_ef_derived_keydata() end.");
253 	return (rv);
254 }
255 
256 krb5_error_code
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     (*(enc->block_size))(&blocksize);
272     (*(enc->keysize))(&keybytes, &keylength);
273 
274     if ((inkey->length != keylength) ||
275 	(outkey->length != keylength))
276 	return(KRB5_CRYPTO_INTERNAL);
277 
278     /* allocate and set up buffers */
279     if ((inblockdata = (unsigned char *) MALLOC(blocksize)) == NULL)
280 	return(ENOMEM);
281 
282     if ((outblockdata = (unsigned char *) MALLOC(blocksize)) == NULL) {
283 	FREE(inblockdata, blocksize);
284 	return(ENOMEM);
285     }
286 
287     if ((rawkey = (unsigned char *) MALLOC(keybytes)) == NULL) {
288 	FREE(outblockdata, blocksize);
289 	FREE(inblockdata, blocksize);
290 	return(ENOMEM);
291     }
292 
293     inblock.data = (char *) inblockdata;
294     inblock.length = blocksize;
295 
296     outblock.data = (char *) outblockdata;
297     outblock.length = blocksize;
298 
299     /* initialize the input block */
300     if (in_constant->length == inblock.length) {
301 	(void) memcpy(inblock.data, in_constant->data, inblock.length);
302     } else {
303 	krb5_nfold(in_constant->length*8,
304 		(krb5_const unsigned char *) in_constant->data,
305 		   inblock.length*8, (unsigned char *) inblock.data);
306     }
307 
308     /* loop encrypting the blocks until enough key bytes are generated */
309     n = 0;
310     while (n < keybytes) {
311       ret = (*(enc->encrypt))(context, inkey, 0, &inblock, &outblock);
312 
313       if (ret) {
314 	KRB5_LOG(KRB5_INFO, "krb5_derive_key() encrypt error: %d", ret);
315 	goto cleanup;
316       }
317 
318 	if ((keybytes - n) <= outblock.length) {
319 	    (void) memcpy(rawkey+n, outblock.data, (keybytes - n));
320 	    break;
321 	}
322 
323 	(void) memcpy(rawkey+n, outblock.data, outblock.length);
324 	(void) memcpy(inblock.data, outblock.data, outblock.length);
325 	n += outblock.length;
326     }
327 
328     /* postprocess the key */
329     inblock.data = (char *) rawkey;
330     inblock.length = keybytes;
331 
332     outkey->enctype = inkey->enctype;
333     ret = (*(enc->make_key))(context, &inblock, outkey);
334 
335     /* clean memory, free resources and exit */
336 cleanup:
337     (void) memset(inblockdata, 0, blocksize);
338     (void) memset(outblockdata, 0, blocksize);
339     (void) memset(rawkey, 0, keybytes);
340 
341     FREE(rawkey, keybytes);
342     FREE(outblockdata, blocksize);
343     FREE(inblockdata, blocksize);
344 
345     KRB5_LOG0(KRB5_INFO, "krb5_derive_key() end");
346     return(ret);
347 }
348