combine_keys.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright (c) 2002 Naval Research Laboratory (NRL/CCS)
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the software,
* derivative works or modified versions, and any portions thereof.
*
* NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
* DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
* RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Key combination function.
*
* If Key1 and Key2 are two keys to be combined, the algorithm to combine
* them is as follows.
*
* Definitions:
*
* k-truncate is defined as truncating to the key size the input.
*
* DR is defined as the generate "random" data from a key
* (defined in crypto draft)
*
* DK is defined as the key derivation function (krb5_derive_key())
*
* (note: | means "concatenate")
*
* Combine key algorithm:
*
* R1 = DR(Key1, n-fold(Key2)) [ Output is length of Key1 ]
* R2 = DR(Key2, n-fold(Key1)) [ Output is length of Key2 ]
*
* rnd = n-fold(R1 | R2) [ Note: output size of nfold must be appropriately
* sized for random-to-key function ]
* tkey = random-to-key(rnd)
* Combine-Key(Key1, Key2) = DK(tkey, CombineConstant)
*
* CombineConstant is defined as the byte string:
*
* { 0x63 0x6f 0x6d 0x62 0x69 0x6e 0x65 }, which corresponds to the
* ASCII encoding of the string "combine"
*/
#include "k5-int.h"
#include "etypes.h"
#include "dk.h"
static krb5_error_code dr
(krb5_context context,
const struct krb5_enc_provider *enc, const krb5_keyblock *inkey,
unsigned char *outdata, const krb5_data *in_constant);
/*
* We only support this combine_keys algorithm for des and 3des keys.
* Everything else should use the PRF defined in the crypto framework.
* We don't implement that yet.
*/
static krb5_boolean enctype_ok (krb5_enctype e)
{
switch (e) {
case ENCTYPE_DES_CBC_CRC:
case ENCTYPE_DES_CBC_MD4:
case ENCTYPE_DES_CBC_MD5:
case ENCTYPE_DES3_CBC_SHA1:
return 1;
default:
return 0;
}
}
krb5_error_code krb5int_c_combine_keys
(krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2, krb5_keyblock *outkey)
{
unsigned char *r1, *r2, *combined, *rnd, *output;
size_t keybytes, keylength;
const struct krb5_enc_provider *enc;
krb5_data input, randbits;
krb5_keyblock tkey;
krb5_error_code ret;
int i, myalloc = 0;
if (!(enctype_ok(key1->enctype)&&enctype_ok(key2->enctype)))
return (KRB5_CRYPTO_INTERNAL);
if (key1->length != key2->length || key1->enctype != key2->enctype)
return (KRB5_CRYPTO_INTERNAL);
/*
* Find our encryption algorithm
*/
for (i = 0; i < krb5_enctypes_length; i++) {
if (krb5_enctypes_list[i].etype == key1->enctype)
break;
}
if (i == krb5_enctypes_length)
return (KRB5_BAD_ENCTYPE);
enc = krb5_enctypes_list[i].enc;
(*(enc->keysize))(&keybytes, &keylength);
/*
* Allocate and set up buffers
*/
if ((r1 = (unsigned char *) malloc(keybytes)) == NULL)
return (ENOMEM);
if ((r2 = (unsigned char *) malloc(keybytes)) == NULL) {
free(r1);
return (ENOMEM);
}
if ((rnd = (unsigned char *) malloc(keybytes)) == NULL) {
free(r1);
free(r2);
return (ENOMEM);
}
if ((combined = (unsigned char *) malloc(keybytes * 2)) == NULL) {
free(r1);
free(r2);
free(rnd);
return (ENOMEM);
}
if ((output = (unsigned char *) malloc(keylength)) == NULL) {
free(r1);
free(r2);
free(rnd);
free(combined);
return (ENOMEM);
}
/*
* Get R1 and R2 (by running the input keys through the DR algorithm.
* Note this is most of derive-key, but not all.
*/
input.length = key2->length;
input.data = (char *) key2->contents;
if ((ret = dr(context, enc, key1, r1, &input)))
goto cleanup;
#if 0
{
int i;
printf("R1 =");
for (i = 0; i < keybytes; i++)
printf(" %02x", (unsigned char) r1[i]);
printf("\n");
}
#endif
input.length = key1->length;
input.data = (char *) key1->contents;
if ((ret = dr(context, enc, key2, r2, &input)))
goto cleanup;
#if 0
{
int i;
printf("R2 =");
for (i = 0; i < keybytes; i++)
printf(" %02x", (unsigned char) r2[i]);
printf("\n");
}
#endif
/*
* Concatenate the two keys together, and then run them through
* n-fold to reduce them to a length appropriate for the random-to-key
* operation. Note here that krb5_nfold() takes sizes in bits, hence
* the multiply by 8.
*/
memcpy(combined, r1, keybytes);
memcpy(combined + keybytes, r2, keybytes);
krb5_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd);
#if 0
{
int i;
printf("rnd =");
for (i = 0; i < keybytes; i++)
printf(" %02x", (unsigned char) rnd[i]);
printf("\n");
}
#endif
/*
* Run the "random" bits through random-to-key to produce a encryption
* key.
*/
randbits.length = keybytes;
randbits.data = (char *) rnd;
tkey.length = keylength;
tkey.contents = output;
if ((ret = (*(enc->make_key))(context, &randbits, &tkey)))
goto cleanup;
#if 0
{
int i;
printf("tkey =");
for (i = 0; i < tkey.length; i++)
printf(" %02x", (unsigned char) tkey.contents[i]);
printf("\n");
}
#endif
/*
* Run through derive-key one more time to produce the final key.
* Note that the input to derive-key is the ASCII string "combine".
*/
input.length = 7; /* Note; change this if string length changes */
input.data = "combine";
/*
* Just FYI: _if_ we have space here in the key, then simply use it
* without modification. But if the key is blank (no allocated storage)
* then allocate some memory for it. This allows programs to use one of
* the existing keys as the output key, _or_ pass in a blank keyblock
* for us to allocate. It's easier for us to allocate it since we already
* know the crypto library internals
*/
if (outkey->length == 0 || outkey->contents == NULL) {
outkey->contents = (krb5_octet *) malloc(keylength);
if (!outkey->contents) {
ret = ENOMEM;
goto cleanup;
}
outkey->length = keylength;
outkey->enctype = key1->enctype;
myalloc = 1;
}
if ((ret = krb5_derive_key(context, enc, &tkey, outkey, &input))) {
if (myalloc) {
free(outkey->contents);
outkey->contents = NULL;
}
goto cleanup;
}
#if 0
{
int i;
printf("output =");
for (i = 0; i < outkey->length; i++)
printf(" %02x", (unsigned char) outkey->contents[i]);
printf("\n");
}
#endif
ret = 0;
cleanup:
memset(r1, 0, keybytes);
memset(r2, 0, keybytes);
memset(rnd, 0, keybytes);
memset(combined, 0, keybytes * 2);
memset(output, 0, keylength);
free(r1);
free(r2);
free(rnd);
free(combined);
free(output);
return (ret);
}
/*
* Our DR function; mostly taken from derive.c
*/
static krb5_error_code dr
( krb5_context context,
const struct krb5_enc_provider *enc,
const krb5_keyblock *inkey,
unsigned char *out,
const krb5_data *in_constant)
{
size_t blocksize, keybytes, keylength, n;
unsigned char *inblockdata, *outblockdata;
krb5_data inblock, outblock;
(*(enc->block_size))(&blocksize);
(*(enc->keysize))(&keybytes, &keylength);
/* allocate and set up buffers */
if ((inblockdata = (unsigned char *) malloc(blocksize)) == NULL)
return(ENOMEM);
if ((outblockdata = (unsigned char *) malloc(blocksize)) == NULL) {
free(inblockdata);
return(ENOMEM);
}
inblock.data = (char *) inblockdata;
inblock.length = blocksize;
outblock.data = (char *) outblockdata;
outblock.length = blocksize;
/* initialize the input block */
if (in_constant->length == inblock.length) {
memcpy(inblock.data, in_constant->data, inblock.length);
} else {
krb5_nfold(in_constant->length*8, (unsigned char *) in_constant->data,
inblock.length*8, (unsigned char *) inblock.data);
}
/* loop encrypting the blocks until enough key bytes are generated */
n = 0;
while (n < keybytes) {
(*(enc->encrypt))(context, inkey, 0, &inblock, &outblock);
if ((keybytes - n) <= outblock.length) {
memcpy(out+n, outblock.data, (keybytes - n));
break;
}
memcpy(out+n, outblock.data, outblock.length);
memcpy(inblock.data, outblock.data, outblock.length);
n += outblock.length;
}
/* clean memory, free resources and exit */
memset(inblockdata, 0, blocksize);
memset(outblockdata, 0, blocksize);
free(outblockdata);
free(inblockdata);
return(0);
}