xref: /onnv-gate/usr/src/uts/common/gssapi/mechs/krb5/crypto/combine_keys.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 /*
80Sstevel@tonic-gate  * Copyright (c) 2002 Naval Research Laboratory (NRL/CCS)
90Sstevel@tonic-gate  *
100Sstevel@tonic-gate  * Permission to use, copy, modify and distribute this software and its
110Sstevel@tonic-gate  * documentation is hereby granted, provided that both the copyright
120Sstevel@tonic-gate  * notice and this permission notice appear in all copies of the software,
130Sstevel@tonic-gate  * derivative works or modified versions, and any portions thereof.
140Sstevel@tonic-gate  *
150Sstevel@tonic-gate  * NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
160Sstevel@tonic-gate  * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
170Sstevel@tonic-gate  * RESULTING FROM THE USE OF THIS SOFTWARE.
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * Key combination function.
200Sstevel@tonic-gate  *
210Sstevel@tonic-gate  * If Key1 and Key2 are two keys to be combined, the algorithm to combine
220Sstevel@tonic-gate  * them is as follows.
230Sstevel@tonic-gate  *
240Sstevel@tonic-gate  * Definitions:
250Sstevel@tonic-gate  *
260Sstevel@tonic-gate  * k-truncate is defined as truncating to the key size the input.
270Sstevel@tonic-gate  *
280Sstevel@tonic-gate  * DR is defined as the generate "random" data from a key
290Sstevel@tonic-gate  * (defined in crypto draft)
300Sstevel@tonic-gate  *
310Sstevel@tonic-gate  * DK is defined as the key derivation function (krb5_derive_key())
320Sstevel@tonic-gate  *
330Sstevel@tonic-gate  * (note: | means "concatenate")
340Sstevel@tonic-gate  *
350Sstevel@tonic-gate  * Combine key algorithm:
360Sstevel@tonic-gate  *
370Sstevel@tonic-gate  * R1 = DR(Key1, n-fold(Key2)) [ Output is length of Key1 ]
380Sstevel@tonic-gate  * R2 = DR(Key2, n-fold(Key1)) [ Output is length of Key2 ]
390Sstevel@tonic-gate  *
400Sstevel@tonic-gate  * rnd = n-fold(R1 | R2) [ Note: output size of nfold must be appropriately
410Sstevel@tonic-gate  *			   sized for random-to-key function ]
420Sstevel@tonic-gate  * tkey = random-to-key(rnd)
430Sstevel@tonic-gate  * Combine-Key(Key1, Key2) = DK(tkey, CombineConstant)
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  * CombineConstant is defined as the byte string:
460Sstevel@tonic-gate  *
470Sstevel@tonic-gate  * { 0x63 0x6f 0x6d 0x62 0x69 0x6e 0x65 }, which corresponds to the
480Sstevel@tonic-gate  * ASCII encoding of the string "combine"
490Sstevel@tonic-gate  */
500Sstevel@tonic-gate 
510Sstevel@tonic-gate #include "k5-int.h"
520Sstevel@tonic-gate #include "etypes.h"
530Sstevel@tonic-gate #include "dk.h"
540Sstevel@tonic-gate 
55*7934SMark.Phalan@Sun.COM /* Solaris Kerberos */
560Sstevel@tonic-gate static krb5_error_code dr
570Sstevel@tonic-gate (krb5_context context,
580Sstevel@tonic-gate const struct krb5_enc_provider *enc, const krb5_keyblock *inkey,
590Sstevel@tonic-gate unsigned char *outdata, const krb5_data *in_constant);
600Sstevel@tonic-gate 
610Sstevel@tonic-gate /*
620Sstevel@tonic-gate  * We only support this combine_keys algorithm for des and 3des keys.
630Sstevel@tonic-gate  * Everything else should use the PRF defined in the crypto framework.
640Sstevel@tonic-gate  * We don't implement that yet.
650Sstevel@tonic-gate  */
660Sstevel@tonic-gate 
enctype_ok(krb5_enctype e)670Sstevel@tonic-gate static krb5_boolean  enctype_ok (krb5_enctype e)
680Sstevel@tonic-gate {
690Sstevel@tonic-gate     switch (e) {
700Sstevel@tonic-gate     case ENCTYPE_DES_CBC_CRC:
710Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD4:
720Sstevel@tonic-gate     case ENCTYPE_DES_CBC_MD5:
730Sstevel@tonic-gate     case ENCTYPE_DES3_CBC_SHA1:
740Sstevel@tonic-gate 	return 1;
750Sstevel@tonic-gate     default:
760Sstevel@tonic-gate 	return 0;
770Sstevel@tonic-gate     }
780Sstevel@tonic-gate }
790Sstevel@tonic-gate 
krb5int_c_combine_keys(krb5_context context,krb5_keyblock * key1,krb5_keyblock * key2,krb5_keyblock * outkey)800Sstevel@tonic-gate krb5_error_code krb5int_c_combine_keys
810Sstevel@tonic-gate (krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2, krb5_keyblock *outkey)
820Sstevel@tonic-gate {
830Sstevel@tonic-gate     unsigned char *r1, *r2, *combined, *rnd, *output;
840Sstevel@tonic-gate     size_t keybytes, keylength;
850Sstevel@tonic-gate     const struct krb5_enc_provider *enc;
860Sstevel@tonic-gate     krb5_data input, randbits;
870Sstevel@tonic-gate     krb5_keyblock tkey;
880Sstevel@tonic-gate     krb5_error_code ret;
890Sstevel@tonic-gate     int i, myalloc = 0;
900Sstevel@tonic-gate     if (!(enctype_ok(key1->enctype)&&enctype_ok(key2->enctype)))
910Sstevel@tonic-gate 	return (KRB5_CRYPTO_INTERNAL);
920Sstevel@tonic-gate 
930Sstevel@tonic-gate 
940Sstevel@tonic-gate     if (key1->length != key2->length || key1->enctype != key2->enctype)
950Sstevel@tonic-gate 	return (KRB5_CRYPTO_INTERNAL);
960Sstevel@tonic-gate 
970Sstevel@tonic-gate     /*
980Sstevel@tonic-gate      * Find our encryption algorithm
990Sstevel@tonic-gate      */
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate     for (i = 0; i < krb5_enctypes_length; i++) {
1020Sstevel@tonic-gate 	if (krb5_enctypes_list[i].etype == key1->enctype)
1030Sstevel@tonic-gate 	    break;
1040Sstevel@tonic-gate     }
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate     if (i == krb5_enctypes_length)
1070Sstevel@tonic-gate 	return (KRB5_BAD_ENCTYPE);
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate     enc = krb5_enctypes_list[i].enc;
1100Sstevel@tonic-gate 
111781Sgtb     keybytes = enc->keybytes;
112781Sgtb     keylength = enc->keylength;
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate     /*
1150Sstevel@tonic-gate      * Allocate and set up buffers
1160Sstevel@tonic-gate      */
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate     if ((r1 = (unsigned char *) malloc(keybytes)) == NULL)
1190Sstevel@tonic-gate 	return (ENOMEM);
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate     if ((r2 = (unsigned char *) malloc(keybytes)) == NULL) {
1220Sstevel@tonic-gate 	free(r1);
1230Sstevel@tonic-gate 	return (ENOMEM);
1240Sstevel@tonic-gate     }
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate     if ((rnd = (unsigned char *) malloc(keybytes)) == NULL) {
1270Sstevel@tonic-gate 	free(r1);
1280Sstevel@tonic-gate 	free(r2);
1290Sstevel@tonic-gate 	return (ENOMEM);
1300Sstevel@tonic-gate     }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate     if ((combined = (unsigned char *) malloc(keybytes * 2)) == NULL) {
1330Sstevel@tonic-gate 	free(r1);
1340Sstevel@tonic-gate 	free(r2);
1350Sstevel@tonic-gate 	free(rnd);
1360Sstevel@tonic-gate 	return (ENOMEM);
1370Sstevel@tonic-gate     }
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate     if ((output = (unsigned char *) malloc(keylength)) == NULL) {
1400Sstevel@tonic-gate 	free(r1);
1410Sstevel@tonic-gate 	free(r2);
1420Sstevel@tonic-gate 	free(rnd);
1430Sstevel@tonic-gate 	free(combined);
1440Sstevel@tonic-gate 	return (ENOMEM);
1450Sstevel@tonic-gate     }
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate     /*
1480Sstevel@tonic-gate      * Get R1 and R2 (by running the input keys through the DR algorithm.
1490Sstevel@tonic-gate      * Note this is most of derive-key, but not all.
1500Sstevel@tonic-gate      */
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate     input.length = key2->length;
1530Sstevel@tonic-gate     input.data = (char *) key2->contents;
154*7934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
1550Sstevel@tonic-gate     if ((ret = dr(context, enc, key1, r1, &input)))
1560Sstevel@tonic-gate 	goto cleanup;
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate #if 0
1590Sstevel@tonic-gate     {
1600Sstevel@tonic-gate 	int i;
1610Sstevel@tonic-gate 	printf("R1 =");
1620Sstevel@tonic-gate 	for (i = 0; i < keybytes; i++)
1630Sstevel@tonic-gate 	    printf(" %02x", (unsigned char) r1[i]);
1640Sstevel@tonic-gate 	printf("\n");
1650Sstevel@tonic-gate     }
1660Sstevel@tonic-gate #endif
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate     input.length = key1->length;
1690Sstevel@tonic-gate     input.data = (char *) key1->contents;
170*7934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
1710Sstevel@tonic-gate     if ((ret = dr(context, enc, key2, r2, &input)))
1720Sstevel@tonic-gate 	goto cleanup;
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate #if 0
1750Sstevel@tonic-gate     {
1760Sstevel@tonic-gate 	int i;
1770Sstevel@tonic-gate 	printf("R2 =");
1780Sstevel@tonic-gate 	for (i = 0; i < keybytes; i++)
1790Sstevel@tonic-gate 	    printf(" %02x", (unsigned char) r2[i]);
1800Sstevel@tonic-gate 	printf("\n");
1810Sstevel@tonic-gate     }
1820Sstevel@tonic-gate #endif
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate     /*
1850Sstevel@tonic-gate      * Concatenate the two keys together, and then run them through
1860Sstevel@tonic-gate      * n-fold to reduce them to a length appropriate for the random-to-key
1870Sstevel@tonic-gate      * operation.  Note here that krb5_nfold() takes sizes in bits, hence
1880Sstevel@tonic-gate      * the multiply by 8.
1890Sstevel@tonic-gate      */
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate     memcpy(combined, r1, keybytes);
1920Sstevel@tonic-gate     memcpy(combined + keybytes, r2, keybytes);
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate     krb5_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd);
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate #if 0
1970Sstevel@tonic-gate     {
1980Sstevel@tonic-gate 	int i;
1990Sstevel@tonic-gate 	printf("rnd =");
2000Sstevel@tonic-gate 	for (i = 0; i < keybytes; i++)
2010Sstevel@tonic-gate 	    printf(" %02x", (unsigned char) rnd[i]);
2020Sstevel@tonic-gate 	printf("\n");
2030Sstevel@tonic-gate     }
2040Sstevel@tonic-gate #endif
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate     /*
2070Sstevel@tonic-gate      * Run the "random" bits through random-to-key to produce a encryption
2080Sstevel@tonic-gate      * key.
2090Sstevel@tonic-gate      */
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate     randbits.length = keybytes;
2120Sstevel@tonic-gate     randbits.data = (char *) rnd;
2130Sstevel@tonic-gate     tkey.length = keylength;
2140Sstevel@tonic-gate     tkey.contents = output;
2150Sstevel@tonic-gate 
216*7934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
2170Sstevel@tonic-gate     if ((ret = (*(enc->make_key))(context, &randbits, &tkey)))
2180Sstevel@tonic-gate 	goto cleanup;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate #if 0
2210Sstevel@tonic-gate     {
2220Sstevel@tonic-gate 	int i;
2230Sstevel@tonic-gate 	printf("tkey =");
2240Sstevel@tonic-gate 	for (i = 0; i < tkey.length; i++)
2250Sstevel@tonic-gate 	    printf(" %02x", (unsigned char) tkey.contents[i]);
2260Sstevel@tonic-gate 	printf("\n");
2270Sstevel@tonic-gate     }
2280Sstevel@tonic-gate #endif
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate     /*
2310Sstevel@tonic-gate      * Run through derive-key one more time to produce the final key.
2320Sstevel@tonic-gate      * Note that the input to derive-key is the ASCII string "combine".
2330Sstevel@tonic-gate      */
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate     input.length = 7; /* Note; change this if string length changes */
2360Sstevel@tonic-gate     input.data = "combine";
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate     /*
2390Sstevel@tonic-gate      * Just FYI: _if_ we have space here in the key, then simply use it
2400Sstevel@tonic-gate      * without modification.  But if the key is blank (no allocated storage)
2410Sstevel@tonic-gate      * then allocate some memory for it.  This allows programs to use one of
2420Sstevel@tonic-gate      * the existing keys as the output key, _or_ pass in a blank keyblock
2430Sstevel@tonic-gate      * for us to allocate.  It's easier for us to allocate it since we already
2440Sstevel@tonic-gate      * know the crypto library internals
2450Sstevel@tonic-gate      */
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate     if (outkey->length == 0 || outkey->contents == NULL) {
2480Sstevel@tonic-gate 	outkey->contents = (krb5_octet *) malloc(keylength);
2490Sstevel@tonic-gate 	if (!outkey->contents) {
2500Sstevel@tonic-gate 	    ret = ENOMEM;
2510Sstevel@tonic-gate 	    goto cleanup;
2520Sstevel@tonic-gate 	}
2530Sstevel@tonic-gate 	outkey->length = keylength;
2540Sstevel@tonic-gate 	outkey->enctype = key1->enctype;
2550Sstevel@tonic-gate 	myalloc = 1;
2560Sstevel@tonic-gate     }
2570Sstevel@tonic-gate 
258*7934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
2590Sstevel@tonic-gate     if ((ret = krb5_derive_key(context, enc, &tkey, outkey, &input))) {
2600Sstevel@tonic-gate 	if (myalloc) {
2610Sstevel@tonic-gate 	    free(outkey->contents);
2620Sstevel@tonic-gate 	    outkey->contents = NULL;
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate 	goto cleanup;
2650Sstevel@tonic-gate     }
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate #if 0
2680Sstevel@tonic-gate     {
2690Sstevel@tonic-gate 	int i;
2700Sstevel@tonic-gate 	printf("output =");
2710Sstevel@tonic-gate 	for (i = 0; i < outkey->length; i++)
2720Sstevel@tonic-gate 	    printf(" %02x", (unsigned char) outkey->contents[i]);
2730Sstevel@tonic-gate 	printf("\n");
2740Sstevel@tonic-gate     }
2750Sstevel@tonic-gate #endif
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate     ret = 0;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate cleanup:
2800Sstevel@tonic-gate     memset(r1, 0, keybytes);
2810Sstevel@tonic-gate     memset(r2, 0, keybytes);
2820Sstevel@tonic-gate     memset(rnd, 0, keybytes);
2830Sstevel@tonic-gate     memset(combined, 0, keybytes * 2);
2840Sstevel@tonic-gate     memset(output, 0, keylength);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate     free(r1);
2870Sstevel@tonic-gate     free(r2);
2880Sstevel@tonic-gate     free(rnd);
2890Sstevel@tonic-gate     free(combined);
2900Sstevel@tonic-gate     free(output);
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate     return (ret);
2930Sstevel@tonic-gate }
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate /*
2960Sstevel@tonic-gate  * Our DR function; mostly taken from derive.c
2970Sstevel@tonic-gate  */
2980Sstevel@tonic-gate 
299*7934SMark.Phalan@Sun.COM     /* Solaris Kerberos */
dr(krb5_context context,const struct krb5_enc_provider * enc,const krb5_keyblock * inkey,unsigned char * out,const krb5_data * in_constant)3000Sstevel@tonic-gate static krb5_error_code dr
3010Sstevel@tonic-gate (	krb5_context context,
3020Sstevel@tonic-gate 	const struct krb5_enc_provider *enc,
3030Sstevel@tonic-gate 	const krb5_keyblock *inkey,
3040Sstevel@tonic-gate 	unsigned char *out,
3050Sstevel@tonic-gate 	const krb5_data *in_constant)
3060Sstevel@tonic-gate {
3070Sstevel@tonic-gate     size_t blocksize, keybytes, keylength, n;
3080Sstevel@tonic-gate     unsigned char *inblockdata, *outblockdata;
3090Sstevel@tonic-gate     krb5_data inblock, outblock;
3100Sstevel@tonic-gate 
311781Sgtb     blocksize = enc->block_size;
312781Sgtb     keybytes = enc->keybytes;
313781Sgtb     keylength = enc->keylength;
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate     /* allocate and set up buffers */
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate     if ((inblockdata = (unsigned char *) malloc(blocksize)) == NULL)
3180Sstevel@tonic-gate 	return(ENOMEM);
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate     if ((outblockdata = (unsigned char *) malloc(blocksize)) == NULL) {
3210Sstevel@tonic-gate 	free(inblockdata);
3220Sstevel@tonic-gate 	return(ENOMEM);
3230Sstevel@tonic-gate     }
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate     inblock.data = (char *) inblockdata;
3260Sstevel@tonic-gate     inblock.length = blocksize;
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate     outblock.data = (char *) outblockdata;
3290Sstevel@tonic-gate     outblock.length = blocksize;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate     /* initialize the input block */
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate     if (in_constant->length == inblock.length) {
3340Sstevel@tonic-gate 	memcpy(inblock.data, in_constant->data, inblock.length);
3350Sstevel@tonic-gate     } else {
3360Sstevel@tonic-gate 	krb5_nfold(in_constant->length*8, (unsigned char *) in_constant->data,
3370Sstevel@tonic-gate 		   inblock.length*8, (unsigned char *) inblock.data);
3380Sstevel@tonic-gate     }
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate     /* loop encrypting the blocks until enough key bytes are generated */
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate     n = 0;
3430Sstevel@tonic-gate     while (n < keybytes) {
344*7934SMark.Phalan@Sun.COM 	/* Solaris Kerberos */
3450Sstevel@tonic-gate 	(*(enc->encrypt))(context, inkey, 0, &inblock, &outblock);
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	if ((keybytes - n) <= outblock.length) {
3480Sstevel@tonic-gate 	    memcpy(out+n, outblock.data, (keybytes - n));
3490Sstevel@tonic-gate 	    break;
3500Sstevel@tonic-gate 	}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	memcpy(out+n, outblock.data, outblock.length);
3530Sstevel@tonic-gate 	memcpy(inblock.data, outblock.data, outblock.length);
3540Sstevel@tonic-gate 	n += outblock.length;
3550Sstevel@tonic-gate     }
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate     /* clean memory, free resources and exit */
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate     memset(inblockdata, 0, blocksize);
3600Sstevel@tonic-gate     memset(outblockdata, 0, blocksize);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate     free(outblockdata);
3630Sstevel@tonic-gate     free(inblockdata);
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate     return(0);
3660Sstevel@tonic-gate }
3670Sstevel@tonic-gate 
368