xref: /onnv-gate/usr/src/uts/common/gssapi/mechs/krb5/crypto/dk/derive.c (revision 7934:6aeeafc994de)
10Sstevel@tonic-gate /*
2*7934SMark.Phalan@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate /*
70Sstevel@tonic-gate  * Copyright (C) 1998 by the FundsXpress, INC.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * All rights reserved.
100Sstevel@tonic-gate  *
110Sstevel@tonic-gate  * Export of this software from the United States of America may require
120Sstevel@tonic-gate  * a specific license from the United States Government.  It is the
130Sstevel@tonic-gate  * responsibility of any person or organization contemplating export to
140Sstevel@tonic-gate  * obtain such a license before exporting.
150Sstevel@tonic-gate  *
160Sstevel@tonic-gate  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
170Sstevel@tonic-gate  * distribute this software and its documentation for any purpose and
180Sstevel@tonic-gate  * without fee is hereby granted, provided that the above copyright
19*7934SMark.Phalan@Sun.COM  * notice appear in all copies and that both that copyright notice and
200Sstevel@tonic-gate  * this permission notice appear in supporting documentation, and that
210Sstevel@tonic-gate  * the name of FundsXpress. not be used in advertising or publicity pertaining
220Sstevel@tonic-gate  * to distribution of the software without specific, written prior
230Sstevel@tonic-gate  * permission.  FundsXpress makes no representations about the suitability of
240Sstevel@tonic-gate  * this software for any purpose.  It is provided "as is" without express
250Sstevel@tonic-gate  * or implied warranty.
260Sstevel@tonic-gate  *
270Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
280Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
290Sstevel@tonic-gate  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
32*7934SMark.Phalan@Sun.COM #include "k5-int.h"
33*7934SMark.Phalan@Sun.COM #include "dk.h"
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */
360Sstevel@tonic-gate 
370Sstevel@tonic-gate /*
380Sstevel@tonic-gate  * Search for a derived key based on the input key,
390Sstevel@tonic-gate  * the  usage constant and the dkid byte.
400Sstevel@tonic-gate  *
410Sstevel@tonic-gate  * Return *derived key on success, NULL on failure.
420Sstevel@tonic-gate  */
430Sstevel@tonic-gate krb5_keyblock *
find_derived_key(krb5_keyusage usage,uchar_t dkid,krb5_keyblock * key)440Sstevel@tonic-gate find_derived_key(krb5_keyusage usage, uchar_t dkid,
450Sstevel@tonic-gate 		krb5_keyblock *key)
460Sstevel@tonic-gate {
470Sstevel@tonic-gate 	krb5_dk_node *dknode = key->dk_list;
480Sstevel@tonic-gate 
490Sstevel@tonic-gate 	while (dknode != NULL) {
500Sstevel@tonic-gate 		if (usage == dknode->usage &&
510Sstevel@tonic-gate 		    dkid == dknode->dkid) {
520Sstevel@tonic-gate 			KRB5_LOG1(KRB5_INFO,
530Sstevel@tonic-gate 				"find_derived_key - MATCH FOUND %d 0x%0x",
540Sstevel@tonic-gate 				usage, dkid);
550Sstevel@tonic-gate 			return(dknode->derived_key);
560Sstevel@tonic-gate 		}
570Sstevel@tonic-gate 		dknode = dknode->next;
580Sstevel@tonic-gate 	}
590Sstevel@tonic-gate 	KRB5_LOG0(KRB5_INFO, "find_derived_key - no match");
600Sstevel@tonic-gate 	return(NULL);
610Sstevel@tonic-gate }
620Sstevel@tonic-gate 
630Sstevel@tonic-gate /*
640Sstevel@tonic-gate  * Add a derived key to the dk_list for the indicated key.
650Sstevel@tonic-gate  */
660Sstevel@tonic-gate krb5_error_code
add_derived_key(krb5_keyblock * key,krb5_keyusage usage,uchar_t dkid,krb5_keyblock * derived_key)670Sstevel@tonic-gate add_derived_key(krb5_keyblock *key,
680Sstevel@tonic-gate 		krb5_keyusage usage, uchar_t dkid,
690Sstevel@tonic-gate 		krb5_keyblock *derived_key)
700Sstevel@tonic-gate {
710Sstevel@tonic-gate 	krb5_dk_node *dknode;
720Sstevel@tonic-gate 	KRB5_LOG1(KRB5_INFO, "add_derived_key: %d 0x%0x",
730Sstevel@tonic-gate 		usage, dkid);
740Sstevel@tonic-gate 
750Sstevel@tonic-gate 	if (key->dk_list == NULL) {
760Sstevel@tonic-gate 		key->dk_list = MALLOC(sizeof(krb5_dk_node));
770Sstevel@tonic-gate 		if (key->dk_list == NULL)
780Sstevel@tonic-gate 			return (ENOMEM);
790Sstevel@tonic-gate 		dknode = key->dk_list;
800Sstevel@tonic-gate 	} else {
810Sstevel@tonic-gate 		dknode = key->dk_list;
820Sstevel@tonic-gate 		/*
830Sstevel@tonic-gate 		 * Find last derived key in list
840Sstevel@tonic-gate 		 */
850Sstevel@tonic-gate 		while (dknode->next != NULL)
860Sstevel@tonic-gate 			dknode = dknode->next;
870Sstevel@tonic-gate 		dknode->next = MALLOC(sizeof(krb5_dk_node));
880Sstevel@tonic-gate 		if (dknode->next == NULL)
890Sstevel@tonic-gate 			return (ENOMEM);
900Sstevel@tonic-gate 		dknode = dknode->next;
910Sstevel@tonic-gate 	}
920Sstevel@tonic-gate 	dknode->usage = usage;
930Sstevel@tonic-gate 	dknode->dkid = dkid;
940Sstevel@tonic-gate 	dknode->derived_key = derived_key;
950Sstevel@tonic-gate 	dknode->next = NULL;
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 	return (0);
980Sstevel@tonic-gate }
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate /*
1010Sstevel@tonic-gate  * Utility function to create a new keyblock
1020Sstevel@tonic-gate  * Return NULL on failure.
1030Sstevel@tonic-gate  */
1040Sstevel@tonic-gate krb5_keyblock *
krb5_create_derived_keyblock(int keysize)1050Sstevel@tonic-gate krb5_create_derived_keyblock(int keysize)
1060Sstevel@tonic-gate {
1070Sstevel@tonic-gate 	krb5_keyblock *key = MALLOC(sizeof(krb5_keyblock));
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	KRB5_LOG0(KRB5_INFO, "krb5_create_derived_keyblock()");
1100Sstevel@tonic-gate 	if (key == NULL)
1110Sstevel@tonic-gate 		return (NULL);
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	bzero(key, sizeof(krb5_keyblock));
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	key->length = keysize;
1160Sstevel@tonic-gate 	key->contents = (uchar_t *)MALLOC(key->length);
1170Sstevel@tonic-gate 	if (key->contents == NULL) {
1180Sstevel@tonic-gate 		FREE(key, sizeof(krb5_keyblock));
1190Sstevel@tonic-gate 		return (NULL);
1200Sstevel@tonic-gate 	}
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 	bzero(key->contents, key->length);
1230Sstevel@tonic-gate #ifdef _KERNEL
1240Sstevel@tonic-gate 	key->kef_mt = CRYPTO_MECH_INVALID;
1250Sstevel@tonic-gate 	key->key_tmpl = NULL;
1260Sstevel@tonic-gate #else
1270Sstevel@tonic-gate 	key->hKey = CK_INVALID_HANDLE;
1280Sstevel@tonic-gate #endif /* _KERNEL */
1290Sstevel@tonic-gate 	return(key);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate  * initialize the derived key values in the context.
1340Sstevel@tonic-gate  */
1350Sstevel@tonic-gate 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)1360Sstevel@tonic-gate init_derived_keydata(krb5_context context,
1370Sstevel@tonic-gate 		    const struct krb5_enc_provider *enc,
1380Sstevel@tonic-gate 		    krb5_keyblock *key,
1390Sstevel@tonic-gate 		    krb5_keyusage usage,
1400Sstevel@tonic-gate 		    krb5_keyblock **d_encr_key,
1410Sstevel@tonic-gate 		    krb5_keyblock **d_hmac_key)
1420Sstevel@tonic-gate {
1430Sstevel@tonic-gate 	krb5_error_code rv = 0;
1440Sstevel@tonic-gate 	unsigned char constantdata[K5CLENGTH];
1450Sstevel@tonic-gate 	krb5_keyblock *cached_key;
1460Sstevel@tonic-gate 	krb5_data d1;
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate 	KRB5_LOG0(KRB5_INFO,"init_ef_derived_keydata().");
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	/*
1510Sstevel@tonic-gate 	 * Get a derived encryption key, either from the cache
1520Sstevel@tonic-gate 	 * or by calculation.
1530Sstevel@tonic-gate 	 */
1540Sstevel@tonic-gate 	cached_key = find_derived_key(usage, DK_ENCR_KEY_BYTE, key);
1550Sstevel@tonic-gate 	if (cached_key != NULL)
1560Sstevel@tonic-gate 		*d_encr_key = cached_key;
1570Sstevel@tonic-gate 	else {
1580Sstevel@tonic-gate 		*d_encr_key = krb5_create_derived_keyblock(key->length);
1590Sstevel@tonic-gate 		if (*d_encr_key == NULL) {
1600Sstevel@tonic-gate 			return (ENOMEM);
1610Sstevel@tonic-gate 		}
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 		(*d_encr_key)->enctype = key->enctype;
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate 		constantdata[0] = (usage>>24)&0xff;
1660Sstevel@tonic-gate 		constantdata[1] = (usage>>16)&0xff;
1670Sstevel@tonic-gate 		constantdata[2] = (usage>>8)&0xff;
1680Sstevel@tonic-gate 		constantdata[3] = usage&0xff;
1690Sstevel@tonic-gate 		constantdata[4] = DK_ENCR_KEY_BYTE;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 		d1.data = (char *)constantdata;
1720Sstevel@tonic-gate 		d1.length = sizeof(constantdata);
1730Sstevel@tonic-gate 		rv = krb5_derive_key(context, enc, key,
1740Sstevel@tonic-gate 				    *d_encr_key, &d1);
1750Sstevel@tonic-gate 		if (rv != 0) {
1760Sstevel@tonic-gate 			krb5_free_keyblock(context, *d_encr_key);
1770Sstevel@tonic-gate 			*d_encr_key = NULL;
1780Sstevel@tonic-gate 			return (rv);
1790Sstevel@tonic-gate 		}
1800Sstevel@tonic-gate 		rv = add_derived_key(key, usage, DK_ENCR_KEY_BYTE,
1810Sstevel@tonic-gate 			    *d_encr_key);
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 		if (rv != 0) {
1840Sstevel@tonic-gate 			krb5_free_keyblock(context, *d_encr_key);
1850Sstevel@tonic-gate 			*d_encr_key = NULL;
1860Sstevel@tonic-gate 			return (rv);
1870Sstevel@tonic-gate 		}
1880Sstevel@tonic-gate 	}
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate 	/*
1910Sstevel@tonic-gate 	 * Get a derived HMAC key, either from the cache
1920Sstevel@tonic-gate 	 * or by calculation.
1930Sstevel@tonic-gate 	 */
1940Sstevel@tonic-gate 	cached_key = find_derived_key(usage, DK_HASH_KEY_BYTE, key);
1950Sstevel@tonic-gate 	if (cached_key != NULL)
1960Sstevel@tonic-gate 		*d_hmac_key = cached_key;
1970Sstevel@tonic-gate 	else {
1980Sstevel@tonic-gate 		*d_hmac_key = krb5_create_derived_keyblock(key->length);
1990Sstevel@tonic-gate 		if (*d_hmac_key == NULL) {
2000Sstevel@tonic-gate 			return (ENOMEM);
2010Sstevel@tonic-gate 		}
2020Sstevel@tonic-gate 		(*d_hmac_key)->enctype = key->enctype;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 		constantdata[0] = (usage>>24)&0xff;
2050Sstevel@tonic-gate 		constantdata[1] = (usage>>16)&0xff;
2060Sstevel@tonic-gate 		constantdata[2] = (usage>>8)&0xff;
2070Sstevel@tonic-gate 		constantdata[3] = usage&0xff;
2080Sstevel@tonic-gate 		constantdata[4] = DK_HASH_KEY_BYTE;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 		d1.data = (char *)constantdata;
2110Sstevel@tonic-gate 		d1.length = sizeof(constantdata);
2120Sstevel@tonic-gate 		rv = krb5_derive_key(context, enc, key, *d_hmac_key, &d1);
2130Sstevel@tonic-gate 		if (rv != 0) {
2140Sstevel@tonic-gate 			krb5_free_keyblock(context, *d_hmac_key);
2150Sstevel@tonic-gate 			*d_hmac_key = NULL;
2160Sstevel@tonic-gate 			return (rv);
2170Sstevel@tonic-gate 		}
2180Sstevel@tonic-gate #ifdef _KERNEL
2190Sstevel@tonic-gate 		/*
2200Sstevel@tonic-gate 		 * By default, derived keys get the "mech_type"
2210Sstevel@tonic-gate 		 * that was associated with their parent.
2220Sstevel@tonic-gate 		 * we need to switch the mech type of the derived HMAC key
2230Sstevel@tonic-gate 		 * to correspond to the mech type for the hmac key.
2240Sstevel@tonic-gate 		 */
2250Sstevel@tonic-gate 		if ((*d_hmac_key)->kef_mt != context->kef_hash_mt) {
2260Sstevel@tonic-gate 			(*d_hmac_key)->kef_mt = context->kef_hash_mt;
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 			if ((*d_hmac_key)->key_tmpl != NULL) {
2290Sstevel@tonic-gate 				crypto_destroy_ctx_template((*d_hmac_key)->key_tmpl);
2300Sstevel@tonic-gate 				(*d_hmac_key)->key_tmpl = NULL;
2310Sstevel@tonic-gate 			}
2320Sstevel@tonic-gate 			rv = update_key_template(*d_hmac_key);
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 			if (rv != 0) {
2350Sstevel@tonic-gate 				krb5_free_keyblock(context, *d_hmac_key);
2360Sstevel@tonic-gate 				*d_hmac_key = NULL;
2370Sstevel@tonic-gate 				return (rv);
2380Sstevel@tonic-gate 			}
2390Sstevel@tonic-gate 		}
2400Sstevel@tonic-gate #endif /* _KERNEL */
2410Sstevel@tonic-gate 		if (rv == 0) {
2420Sstevel@tonic-gate 			rv = add_derived_key(key, usage, DK_HASH_KEY_BYTE,
2430Sstevel@tonic-gate 				    *d_hmac_key);
2440Sstevel@tonic-gate 			if (rv != 0) {
2450Sstevel@tonic-gate 				krb5_free_keyblock(context, *d_hmac_key);
2460Sstevel@tonic-gate 				*d_hmac_key = NULL;
2470Sstevel@tonic-gate 				return (rv);
2480Sstevel@tonic-gate 			}
2490Sstevel@tonic-gate 		}
2500Sstevel@tonic-gate 	}
2510Sstevel@tonic-gate 	KRB5_LOG0(KRB5_INFO,"init_ef_derived_keydata() end.");
2520Sstevel@tonic-gate 	return (rv);
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate 
255*7934SMark.Phalan@Sun.COM 
2560Sstevel@tonic-gate krb5_error_code
krb5_derive_key(context,enc,inkey,outkey,in_constant)2570Sstevel@tonic-gate krb5_derive_key(context, enc, inkey, outkey, in_constant)
2580Sstevel@tonic-gate      krb5_context context;
2590Sstevel@tonic-gate      krb5_const struct krb5_enc_provider *enc;
2600Sstevel@tonic-gate      krb5_const krb5_keyblock *inkey;
2610Sstevel@tonic-gate      krb5_keyblock *outkey;
2620Sstevel@tonic-gate      krb5_const krb5_data *in_constant;
2630Sstevel@tonic-gate {
2640Sstevel@tonic-gate     size_t blocksize, keybytes, keylength, n;
2650Sstevel@tonic-gate     unsigned char *inblockdata, *outblockdata, *rawkey;
2660Sstevel@tonic-gate     krb5_data inblock, outblock;
2670Sstevel@tonic-gate     krb5_error_code ret = 0;
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate     KRB5_LOG0(KRB5_INFO, "krb5_derive_key() start");
2700Sstevel@tonic-gate 
271781Sgtb     blocksize = enc->block_size;
272781Sgtb     keybytes = enc->keybytes;
273781Sgtb     keylength = enc->keylength;
274781Sgtb 
2750Sstevel@tonic-gate 
2760Sstevel@tonic-gate     if ((inkey->length != keylength) ||
2770Sstevel@tonic-gate 	(outkey->length != keylength))
2780Sstevel@tonic-gate 	return(KRB5_CRYPTO_INTERNAL);
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate     /* allocate and set up buffers */
2810Sstevel@tonic-gate     if ((inblockdata = (unsigned char *) MALLOC(blocksize)) == NULL)
2820Sstevel@tonic-gate 	return(ENOMEM);
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate     if ((outblockdata = (unsigned char *) MALLOC(blocksize)) == NULL) {
2850Sstevel@tonic-gate 	FREE(inblockdata, blocksize);
2860Sstevel@tonic-gate 	return(ENOMEM);
2870Sstevel@tonic-gate     }
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate     if ((rawkey = (unsigned char *) MALLOC(keybytes)) == NULL) {
2900Sstevel@tonic-gate 	FREE(outblockdata, blocksize);
2910Sstevel@tonic-gate 	FREE(inblockdata, blocksize);
2920Sstevel@tonic-gate 	return(ENOMEM);
2930Sstevel@tonic-gate     }
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate     inblock.data = (char *) inblockdata;
2960Sstevel@tonic-gate     inblock.length = blocksize;
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate     outblock.data = (char *) outblockdata;
2990Sstevel@tonic-gate     outblock.length = blocksize;
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate     /* initialize the input block */
3020Sstevel@tonic-gate     if (in_constant->length == inblock.length) {
3030Sstevel@tonic-gate 	(void) memcpy(inblock.data, in_constant->data, inblock.length);
3040Sstevel@tonic-gate     } else {
3050Sstevel@tonic-gate 	krb5_nfold(in_constant->length*8,
3060Sstevel@tonic-gate 		(krb5_const unsigned char *) in_constant->data,
3070Sstevel@tonic-gate 		   inblock.length*8, (unsigned char *) inblock.data);
3080Sstevel@tonic-gate     }
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate     /* loop encrypting the blocks until enough key bytes are generated */
3110Sstevel@tonic-gate     n = 0;
3120Sstevel@tonic-gate     while (n < keybytes) {
3130Sstevel@tonic-gate       ret = (*(enc->encrypt))(context, inkey, 0, &inblock, &outblock);
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate       if (ret) {
3160Sstevel@tonic-gate 	KRB5_LOG(KRB5_INFO, "krb5_derive_key() encrypt error: %d", ret);
3170Sstevel@tonic-gate 	goto cleanup;
3180Sstevel@tonic-gate       }
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 	if ((keybytes - n) <= outblock.length) {
3210Sstevel@tonic-gate 	    (void) memcpy(rawkey+n, outblock.data, (keybytes - n));
3220Sstevel@tonic-gate 	    break;
3230Sstevel@tonic-gate 	}
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	(void) memcpy(rawkey+n, outblock.data, outblock.length);
3260Sstevel@tonic-gate 	(void) memcpy(inblock.data, outblock.data, outblock.length);
3270Sstevel@tonic-gate 	n += outblock.length;
3280Sstevel@tonic-gate     }
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate     /* postprocess the key */
3310Sstevel@tonic-gate     inblock.data = (char *) rawkey;
3320Sstevel@tonic-gate     inblock.length = keybytes;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate     outkey->enctype = inkey->enctype;
3350Sstevel@tonic-gate     ret = (*(enc->make_key))(context, &inblock, outkey);
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate     /* clean memory, free resources and exit */
3380Sstevel@tonic-gate cleanup:
3390Sstevel@tonic-gate     (void) memset(inblockdata, 0, blocksize);
3400Sstevel@tonic-gate     (void) memset(outblockdata, 0, blocksize);
3410Sstevel@tonic-gate     (void) memset(rawkey, 0, keybytes);
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate     FREE(rawkey, keybytes);
3440Sstevel@tonic-gate     FREE(outblockdata, blocksize);
3450Sstevel@tonic-gate     FREE(inblockdata, blocksize);
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate     KRB5_LOG0(KRB5_INFO, "krb5_derive_key() end");
3480Sstevel@tonic-gate     return(ret);
3490Sstevel@tonic-gate }
350