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