1/*
2 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7/*
8 * Copyright (c) 2002 Naval Research Laboratory (NRL/CCS)
9 *
10 * Permission to use, copy, modify and distribute this software and its
11 * documentation is hereby granted, provided that both the copyright
12 * notice and this permission notice appear in all copies of the software,
13 * derivative works or modified versions, and any portions thereof.
14 *
15 * NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
16 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
17 * RESULTING FROM THE USE OF THIS SOFTWARE.
18 *
19 * Key combination function.
20 *
21 * If Key1 and Key2 are two keys to be combined, the algorithm to combine
22 * them is as follows.
23 *
24 * Definitions:
25 *
26 * k-truncate is defined as truncating to the key size the input.
27 *
28 * DR is defined as the generate "random" data from a key
29 * (defined in crypto draft)
30 *
31 * DK is defined as the key derivation function (krb5_derive_key())
32 *
33 * (note: | means "concatenate")
34 *
35 * Combine key algorithm:
36 *
37 * R1 = DR(Key1, n-fold(Key2)) [ Output is length of Key1 ]
38 * R2 = DR(Key2, n-fold(Key1)) [ Output is length of Key2 ]
39 *
40 * rnd = n-fold(R1 | R2) [ Note: output size of nfold must be appropriately
41 *			   sized for random-to-key function ]
42 * tkey = random-to-key(rnd)
43 * Combine-Key(Key1, Key2) = DK(tkey, CombineConstant)
44 *
45 * CombineConstant is defined as the byte string:
46 *
47 * { 0x63 0x6f 0x6d 0x62 0x69 0x6e 0x65 }, which corresponds to the
48 * ASCII encoding of the string "combine"
49 */
50
51#include "k5-int.h"
52#include "etypes.h"
53#include "dk.h"
54
55/* Solaris Kerberos */
56static krb5_error_code dr
57(krb5_context context,
58const struct krb5_enc_provider *enc, const krb5_keyblock *inkey,
59unsigned char *outdata, const krb5_data *in_constant);
60
61/*
62 * We only support this combine_keys algorithm for des and 3des keys.
63 * Everything else should use the PRF defined in the crypto framework.
64 * We don't implement that yet.
65 */
66
67static krb5_boolean  enctype_ok (krb5_enctype e)
68{
69    switch (e) {
70    case ENCTYPE_DES_CBC_CRC:
71    case ENCTYPE_DES_CBC_MD4:
72    case ENCTYPE_DES_CBC_MD5:
73    case ENCTYPE_DES3_CBC_SHA1:
74	return 1;
75    default:
76	return 0;
77    }
78}
79
80krb5_error_code krb5int_c_combine_keys
81(krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2, krb5_keyblock *outkey)
82{
83    unsigned char *r1, *r2, *combined, *rnd, *output;
84    size_t keybytes, keylength;
85    const struct krb5_enc_provider *enc;
86    krb5_data input, randbits;
87    krb5_keyblock tkey;
88    krb5_error_code ret;
89    int i, myalloc = 0;
90    if (!(enctype_ok(key1->enctype)&&enctype_ok(key2->enctype)))
91	return (KRB5_CRYPTO_INTERNAL);
92
93
94    if (key1->length != key2->length || key1->enctype != key2->enctype)
95	return (KRB5_CRYPTO_INTERNAL);
96
97    /*
98     * Find our encryption algorithm
99     */
100
101    for (i = 0; i < krb5_enctypes_length; i++) {
102	if (krb5_enctypes_list[i].etype == key1->enctype)
103	    break;
104    }
105
106    if (i == krb5_enctypes_length)
107	return (KRB5_BAD_ENCTYPE);
108
109    enc = krb5_enctypes_list[i].enc;
110
111    keybytes = enc->keybytes;
112    keylength = enc->keylength;
113
114    /*
115     * Allocate and set up buffers
116     */
117
118    if ((r1 = (unsigned char *) malloc(keybytes)) == NULL)
119	return (ENOMEM);
120
121    if ((r2 = (unsigned char *) malloc(keybytes)) == NULL) {
122	free(r1);
123	return (ENOMEM);
124    }
125
126    if ((rnd = (unsigned char *) malloc(keybytes)) == NULL) {
127	free(r1);
128	free(r2);
129	return (ENOMEM);
130    }
131
132    if ((combined = (unsigned char *) malloc(keybytes * 2)) == NULL) {
133	free(r1);
134	free(r2);
135	free(rnd);
136	return (ENOMEM);
137    }
138
139    if ((output = (unsigned char *) malloc(keylength)) == NULL) {
140	free(r1);
141	free(r2);
142	free(rnd);
143	free(combined);
144	return (ENOMEM);
145    }
146
147    /*
148     * Get R1 and R2 (by running the input keys through the DR algorithm.
149     * Note this is most of derive-key, but not all.
150     */
151
152    input.length = key2->length;
153    input.data = (char *) key2->contents;
154    /* Solaris Kerberos */
155    if ((ret = dr(context, enc, key1, r1, &input)))
156	goto cleanup;
157
158#if 0
159    {
160	int i;
161	printf("R1 =");
162	for (i = 0; i < keybytes; i++)
163	    printf(" %02x", (unsigned char) r1[i]);
164	printf("\n");
165    }
166#endif
167
168    input.length = key1->length;
169    input.data = (char *) key1->contents;
170    /* Solaris Kerberos */
171    if ((ret = dr(context, enc, key2, r2, &input)))
172	goto cleanup;
173
174#if 0
175    {
176	int i;
177	printf("R2 =");
178	for (i = 0; i < keybytes; i++)
179	    printf(" %02x", (unsigned char) r2[i]);
180	printf("\n");
181    }
182#endif
183
184    /*
185     * Concatenate the two keys together, and then run them through
186     * n-fold to reduce them to a length appropriate for the random-to-key
187     * operation.  Note here that krb5_nfold() takes sizes in bits, hence
188     * the multiply by 8.
189     */
190
191    memcpy(combined, r1, keybytes);
192    memcpy(combined + keybytes, r2, keybytes);
193
194    krb5_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd);
195
196#if 0
197    {
198	int i;
199	printf("rnd =");
200	for (i = 0; i < keybytes; i++)
201	    printf(" %02x", (unsigned char) rnd[i]);
202	printf("\n");
203    }
204#endif
205
206    /*
207     * Run the "random" bits through random-to-key to produce a encryption
208     * key.
209     */
210
211    randbits.length = keybytes;
212    randbits.data = (char *) rnd;
213    tkey.length = keylength;
214    tkey.contents = output;
215
216    /* Solaris Kerberos */
217    if ((ret = (*(enc->make_key))(context, &randbits, &tkey)))
218	goto cleanup;
219
220#if 0
221    {
222	int i;
223	printf("tkey =");
224	for (i = 0; i < tkey.length; i++)
225	    printf(" %02x", (unsigned char) tkey.contents[i]);
226	printf("\n");
227    }
228#endif
229
230    /*
231     * Run through derive-key one more time to produce the final key.
232     * Note that the input to derive-key is the ASCII string "combine".
233     */
234
235    input.length = 7; /* Note; change this if string length changes */
236    input.data = "combine";
237
238    /*
239     * Just FYI: _if_ we have space here in the key, then simply use it
240     * without modification.  But if the key is blank (no allocated storage)
241     * then allocate some memory for it.  This allows programs to use one of
242     * the existing keys as the output key, _or_ pass in a blank keyblock
243     * for us to allocate.  It's easier for us to allocate it since we already
244     * know the crypto library internals
245     */
246
247    if (outkey->length == 0 || outkey->contents == NULL) {
248	outkey->contents = (krb5_octet *) malloc(keylength);
249	if (!outkey->contents) {
250	    ret = ENOMEM;
251	    goto cleanup;
252	}
253	outkey->length = keylength;
254	outkey->enctype = key1->enctype;
255	myalloc = 1;
256    }
257
258    /* Solaris Kerberos */
259    if ((ret = krb5_derive_key(context, enc, &tkey, outkey, &input))) {
260	if (myalloc) {
261	    free(outkey->contents);
262	    outkey->contents = NULL;
263	}
264	goto cleanup;
265    }
266
267#if 0
268    {
269	int i;
270	printf("output =");
271	for (i = 0; i < outkey->length; i++)
272	    printf(" %02x", (unsigned char) outkey->contents[i]);
273	printf("\n");
274    }
275#endif
276
277    ret = 0;
278
279cleanup:
280    memset(r1, 0, keybytes);
281    memset(r2, 0, keybytes);
282    memset(rnd, 0, keybytes);
283    memset(combined, 0, keybytes * 2);
284    memset(output, 0, keylength);
285
286    free(r1);
287    free(r2);
288    free(rnd);
289    free(combined);
290    free(output);
291
292    return (ret);
293}
294
295/*
296 * Our DR function; mostly taken from derive.c
297 */
298
299    /* Solaris Kerberos */
300static krb5_error_code dr
301(	krb5_context context,
302	const struct krb5_enc_provider *enc,
303	const krb5_keyblock *inkey,
304	unsigned char *out,
305	const krb5_data *in_constant)
306{
307    size_t blocksize, keybytes, keylength, n;
308    unsigned char *inblockdata, *outblockdata;
309    krb5_data inblock, outblock;
310
311    blocksize = enc->block_size;
312    keybytes = enc->keybytes;
313    keylength = enc->keylength;
314
315    /* allocate and set up buffers */
316
317    if ((inblockdata = (unsigned char *) malloc(blocksize)) == NULL)
318	return(ENOMEM);
319
320    if ((outblockdata = (unsigned char *) malloc(blocksize)) == NULL) {
321	free(inblockdata);
322	return(ENOMEM);
323    }
324
325    inblock.data = (char *) inblockdata;
326    inblock.length = blocksize;
327
328    outblock.data = (char *) outblockdata;
329    outblock.length = blocksize;
330
331    /* initialize the input block */
332
333    if (in_constant->length == inblock.length) {
334	memcpy(inblock.data, in_constant->data, inblock.length);
335    } else {
336	krb5_nfold(in_constant->length*8, (unsigned char *) in_constant->data,
337		   inblock.length*8, (unsigned char *) inblock.data);
338    }
339
340    /* loop encrypting the blocks until enough key bytes are generated */
341
342    n = 0;
343    while (n < keybytes) {
344	/* Solaris Kerberos */
345	(*(enc->encrypt))(context, inkey, 0, &inblock, &outblock);
346
347	if ((keybytes - n) <= outblock.length) {
348	    memcpy(out+n, outblock.data, (keybytes - n));
349	    break;
350	}
351
352	memcpy(out+n, outblock.data, outblock.length);
353	memcpy(inblock.data, outblock.data, outblock.length);
354	n += outblock.length;
355    }
356
357    /* clean memory, free resources and exit */
358
359    memset(inblockdata, 0, blocksize);
360    memset(outblockdata, 0, blocksize);
361
362    free(outblockdata);
363    free(inblockdata);
364
365    return(0);
366}
367
368