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