xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/crypto/combine_keys.c (revision 505d05c73a6e56769f263d4803b22eddd168ee24)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * Copyright (c) 2002 Naval Research Laboratory (NRL/CCS)
10  *
11  * Permission to use, copy, modify and distribute this software and its
12  * documentation is hereby granted, provided that both the copyright
13  * notice and this permission notice appear in all copies of the software,
14  * derivative works or modified versions, and any portions thereof.
15  *
16  * NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
17  * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
18  * RESULTING FROM THE USE OF THIS SOFTWARE.
19  *
20  * Key combination function.
21  *
22  * If Key1 and Key2 are two keys to be combined, the algorithm to combine
23  * them is as follows.
24  *
25  * Definitions:
26  *
27  * k-truncate is defined as truncating to the key size the input.
28  *
29  * DR is defined as the generate "random" data from a key
30  * (defined in crypto draft)
31  *
32  * DK is defined as the key derivation function (krb5_derive_key())
33  *
34  * (note: | means "concatenate")
35  *
36  * Combine key algorithm:
37  *
38  * R1 = DR(Key1, n-fold(Key2)) [ Output is length of Key1 ]
39  * R2 = DR(Key2, n-fold(Key1)) [ Output is length of Key2 ]
40  *
41  * rnd = n-fold(R1 | R2) [ Note: output size of nfold must be appropriately
42  *			   sized for random-to-key function ]
43  * tkey = random-to-key(rnd)
44  * Combine-Key(Key1, Key2) = DK(tkey, CombineConstant)
45  *
46  * CombineConstant is defined as the byte string:
47  *
48  * { 0x63 0x6f 0x6d 0x62 0x69 0x6e 0x65 }, which corresponds to the
49  * ASCII encoding of the string "combine"
50  */
51 
52 #include "k5-int.h"
53 #include "etypes.h"
54 #include "dk.h"
55 
56 static krb5_error_code dr
57 (krb5_context context,
58 const struct krb5_enc_provider *enc, const krb5_keyblock *inkey,
59 unsigned 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 
67 static 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 
80 krb5_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     if ((ret = dr(context, enc, key1, r1, &input)))
155 	goto cleanup;
156 
157 #if 0
158     {
159 	int i;
160 	printf("R1 =");
161 	for (i = 0; i < keybytes; i++)
162 	    printf(" %02x", (unsigned char) r1[i]);
163 	printf("\n");
164     }
165 #endif
166 
167     input.length = key1->length;
168     input.data = (char *) key1->contents;
169     if ((ret = dr(context, enc, key2, r2, &input)))
170 	goto cleanup;
171 
172 #if 0
173     {
174 	int i;
175 	printf("R2 =");
176 	for (i = 0; i < keybytes; i++)
177 	    printf(" %02x", (unsigned char) r2[i]);
178 	printf("\n");
179     }
180 #endif
181 
182     /*
183      * Concatenate the two keys together, and then run them through
184      * n-fold to reduce them to a length appropriate for the random-to-key
185      * operation.  Note here that krb5_nfold() takes sizes in bits, hence
186      * the multiply by 8.
187      */
188 
189     memcpy(combined, r1, keybytes);
190     memcpy(combined + keybytes, r2, keybytes);
191 
192     krb5_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd);
193 
194 #if 0
195     {
196 	int i;
197 	printf("rnd =");
198 	for (i = 0; i < keybytes; i++)
199 	    printf(" %02x", (unsigned char) rnd[i]);
200 	printf("\n");
201     }
202 #endif
203 
204     /*
205      * Run the "random" bits through random-to-key to produce a encryption
206      * key.
207      */
208 
209     randbits.length = keybytes;
210     randbits.data = (char *) rnd;
211     tkey.length = keylength;
212     tkey.contents = output;
213 
214     if ((ret = (*(enc->make_key))(context, &randbits, &tkey)))
215 	goto cleanup;
216 
217 #if 0
218     {
219 	int i;
220 	printf("tkey =");
221 	for (i = 0; i < tkey.length; i++)
222 	    printf(" %02x", (unsigned char) tkey.contents[i]);
223 	printf("\n");
224     }
225 #endif
226 
227     /*
228      * Run through derive-key one more time to produce the final key.
229      * Note that the input to derive-key is the ASCII string "combine".
230      */
231 
232     input.length = 7; /* Note; change this if string length changes */
233     input.data = "combine";
234 
235     /*
236      * Just FYI: _if_ we have space here in the key, then simply use it
237      * without modification.  But if the key is blank (no allocated storage)
238      * then allocate some memory for it.  This allows programs to use one of
239      * the existing keys as the output key, _or_ pass in a blank keyblock
240      * for us to allocate.  It's easier for us to allocate it since we already
241      * know the crypto library internals
242      */
243 
244     if (outkey->length == 0 || outkey->contents == NULL) {
245 	outkey->contents = (krb5_octet *) malloc(keylength);
246 	if (!outkey->contents) {
247 	    ret = ENOMEM;
248 	    goto cleanup;
249 	}
250 	outkey->length = keylength;
251 	outkey->enctype = key1->enctype;
252 	myalloc = 1;
253     }
254 
255     if ((ret = krb5_derive_key(context, enc, &tkey, outkey, &input))) {
256 	if (myalloc) {
257 	    free(outkey->contents);
258 	    outkey->contents = NULL;
259 	}
260 	goto cleanup;
261     }
262 
263 #if 0
264     {
265 	int i;
266 	printf("output =");
267 	for (i = 0; i < outkey->length; i++)
268 	    printf(" %02x", (unsigned char) outkey->contents[i]);
269 	printf("\n");
270     }
271 #endif
272 
273     ret = 0;
274 
275 cleanup:
276     memset(r1, 0, keybytes);
277     memset(r2, 0, keybytes);
278     memset(rnd, 0, keybytes);
279     memset(combined, 0, keybytes * 2);
280     memset(output, 0, keylength);
281 
282     free(r1);
283     free(r2);
284     free(rnd);
285     free(combined);
286     free(output);
287 
288     return (ret);
289 }
290 
291 /*
292  * Our DR function; mostly taken from derive.c
293  */
294 
295 static krb5_error_code dr
296 (	krb5_context context,
297 	const struct krb5_enc_provider *enc,
298 	const krb5_keyblock *inkey,
299 	unsigned char *out,
300 	const krb5_data *in_constant)
301 {
302     size_t blocksize, keybytes, keylength, n;
303     unsigned char *inblockdata, *outblockdata;
304     krb5_data inblock, outblock;
305 
306     blocksize = enc->block_size;
307     keybytes = enc->keybytes;
308     keylength = enc->keylength;
309 
310     /* allocate and set up buffers */
311 
312     if ((inblockdata = (unsigned char *) malloc(blocksize)) == NULL)
313 	return(ENOMEM);
314 
315     if ((outblockdata = (unsigned char *) malloc(blocksize)) == NULL) {
316 	free(inblockdata);
317 	return(ENOMEM);
318     }
319 
320     inblock.data = (char *) inblockdata;
321     inblock.length = blocksize;
322 
323     outblock.data = (char *) outblockdata;
324     outblock.length = blocksize;
325 
326     /* initialize the input block */
327 
328     if (in_constant->length == inblock.length) {
329 	memcpy(inblock.data, in_constant->data, inblock.length);
330     } else {
331 	krb5_nfold(in_constant->length*8, (unsigned char *) in_constant->data,
332 		   inblock.length*8, (unsigned char *) inblock.data);
333     }
334 
335     /* loop encrypting the blocks until enough key bytes are generated */
336 
337     n = 0;
338     while (n < keybytes) {
339 	(*(enc->encrypt))(context, inkey, 0, &inblock, &outblock);
340 
341 	if ((keybytes - n) <= outblock.length) {
342 	    memcpy(out+n, outblock.data, (keybytes - n));
343 	    break;
344 	}
345 
346 	memcpy(out+n, outblock.data, outblock.length);
347 	memcpy(inblock.data, outblock.data, outblock.length);
348 	n += outblock.length;
349     }
350 
351     /* clean memory, free resources and exit */
352 
353     memset(inblockdata, 0, blocksize);
354     memset(outblockdata, 0, blocksize);
355 
356     free(outblockdata);
357     free(inblockdata);
358 
359     return(0);
360 }
361 
362