xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/crypto/combine_keys.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * Copyright 2004 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     (*(enc->keysize))(&keybytes, &keylength);
112 
113     /*
114      * Allocate and set up buffers
115      */
116 
117     if ((r1 = (unsigned char *) malloc(keybytes)) == NULL)
118 	return (ENOMEM);
119 
120     if ((r2 = (unsigned char *) malloc(keybytes)) == NULL) {
121 	free(r1);
122 	return (ENOMEM);
123     }
124 
125     if ((rnd = (unsigned char *) malloc(keybytes)) == NULL) {
126 	free(r1);
127 	free(r2);
128 	return (ENOMEM);
129     }
130 
131     if ((combined = (unsigned char *) malloc(keybytes * 2)) == NULL) {
132 	free(r1);
133 	free(r2);
134 	free(rnd);
135 	return (ENOMEM);
136     }
137 
138     if ((output = (unsigned char *) malloc(keylength)) == NULL) {
139 	free(r1);
140 	free(r2);
141 	free(rnd);
142 	free(combined);
143 	return (ENOMEM);
144     }
145 
146     /*
147      * Get R1 and R2 (by running the input keys through the DR algorithm.
148      * Note this is most of derive-key, but not all.
149      */
150 
151     input.length = key2->length;
152     input.data = (char *) key2->contents;
153     if ((ret = dr(context, enc, key1, r1, &input)))
154 	goto cleanup;
155 
156 #if 0
157     {
158 	int i;
159 	printf("R1 =");
160 	for (i = 0; i < keybytes; i++)
161 	    printf(" %02x", (unsigned char) r1[i]);
162 	printf("\n");
163     }
164 #endif
165 
166     input.length = key1->length;
167     input.data = (char *) key1->contents;
168     if ((ret = dr(context, enc, key2, r2, &input)))
169 	goto cleanup;
170 
171 #if 0
172     {
173 	int i;
174 	printf("R2 =");
175 	for (i = 0; i < keybytes; i++)
176 	    printf(" %02x", (unsigned char) r2[i]);
177 	printf("\n");
178     }
179 #endif
180 
181     /*
182      * Concatenate the two keys together, and then run them through
183      * n-fold to reduce them to a length appropriate for the random-to-key
184      * operation.  Note here that krb5_nfold() takes sizes in bits, hence
185      * the multiply by 8.
186      */
187 
188     memcpy(combined, r1, keybytes);
189     memcpy(combined + keybytes, r2, keybytes);
190 
191     krb5_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd);
192 
193 #if 0
194     {
195 	int i;
196 	printf("rnd =");
197 	for (i = 0; i < keybytes; i++)
198 	    printf(" %02x", (unsigned char) rnd[i]);
199 	printf("\n");
200     }
201 #endif
202 
203     /*
204      * Run the "random" bits through random-to-key to produce a encryption
205      * key.
206      */
207 
208     randbits.length = keybytes;
209     randbits.data = (char *) rnd;
210     tkey.length = keylength;
211     tkey.contents = output;
212 
213     if ((ret = (*(enc->make_key))(context, &randbits, &tkey)))
214 	goto cleanup;
215 
216 #if 0
217     {
218 	int i;
219 	printf("tkey =");
220 	for (i = 0; i < tkey.length; i++)
221 	    printf(" %02x", (unsigned char) tkey.contents[i]);
222 	printf("\n");
223     }
224 #endif
225 
226     /*
227      * Run through derive-key one more time to produce the final key.
228      * Note that the input to derive-key is the ASCII string "combine".
229      */
230 
231     input.length = 7; /* Note; change this if string length changes */
232     input.data = "combine";
233 
234     /*
235      * Just FYI: _if_ we have space here in the key, then simply use it
236      * without modification.  But if the key is blank (no allocated storage)
237      * then allocate some memory for it.  This allows programs to use one of
238      * the existing keys as the output key, _or_ pass in a blank keyblock
239      * for us to allocate.  It's easier for us to allocate it since we already
240      * know the crypto library internals
241      */
242 
243     if (outkey->length == 0 || outkey->contents == NULL) {
244 	outkey->contents = (krb5_octet *) malloc(keylength);
245 	if (!outkey->contents) {
246 	    ret = ENOMEM;
247 	    goto cleanup;
248 	}
249 	outkey->length = keylength;
250 	outkey->enctype = key1->enctype;
251 	myalloc = 1;
252     }
253 
254     if ((ret = krb5_derive_key(context, enc, &tkey, outkey, &input))) {
255 	if (myalloc) {
256 	    free(outkey->contents);
257 	    outkey->contents = NULL;
258 	}
259 	goto cleanup;
260     }
261 
262 #if 0
263     {
264 	int i;
265 	printf("output =");
266 	for (i = 0; i < outkey->length; i++)
267 	    printf(" %02x", (unsigned char) outkey->contents[i]);
268 	printf("\n");
269     }
270 #endif
271 
272     ret = 0;
273 
274 cleanup:
275     memset(r1, 0, keybytes);
276     memset(r2, 0, keybytes);
277     memset(rnd, 0, keybytes);
278     memset(combined, 0, keybytes * 2);
279     memset(output, 0, keylength);
280 
281     free(r1);
282     free(r2);
283     free(rnd);
284     free(combined);
285     free(output);
286 
287     return (ret);
288 }
289 
290 /*
291  * Our DR function; mostly taken from derive.c
292  */
293 
294 static krb5_error_code dr
295 (	krb5_context context,
296 	const struct krb5_enc_provider *enc,
297 	const krb5_keyblock *inkey,
298 	unsigned char *out,
299 	const krb5_data *in_constant)
300 {
301     size_t blocksize, keybytes, keylength, n;
302     unsigned char *inblockdata, *outblockdata;
303     krb5_data inblock, outblock;
304 
305     (*(enc->block_size))(&blocksize);
306     (*(enc->keysize))(&keybytes, &keylength);
307 
308     /* allocate and set up buffers */
309 
310     if ((inblockdata = (unsigned char *) malloc(blocksize)) == NULL)
311 	return(ENOMEM);
312 
313     if ((outblockdata = (unsigned char *) malloc(blocksize)) == NULL) {
314 	free(inblockdata);
315 	return(ENOMEM);
316     }
317 
318     inblock.data = (char *) inblockdata;
319     inblock.length = blocksize;
320 
321     outblock.data = (char *) outblockdata;
322     outblock.length = blocksize;
323 
324     /* initialize the input block */
325 
326     if (in_constant->length == inblock.length) {
327 	memcpy(inblock.data, in_constant->data, inblock.length);
328     } else {
329 	krb5_nfold(in_constant->length*8, (unsigned char *) in_constant->data,
330 		   inblock.length*8, (unsigned char *) inblock.data);
331     }
332 
333     /* loop encrypting the blocks until enough key bytes are generated */
334 
335     n = 0;
336     while (n < keybytes) {
337 	(*(enc->encrypt))(context, inkey, 0, &inblock, &outblock);
338 
339 	if ((keybytes - n) <= outblock.length) {
340 	    memcpy(out+n, outblock.data, (keybytes - n));
341 	    break;
342 	}
343 
344 	memcpy(out+n, outblock.data, outblock.length);
345 	memcpy(inblock.data, outblock.data, outblock.length);
346 	n += outblock.length;
347     }
348 
349     /* clean memory, free resources and exit */
350 
351     memset(inblockdata, 0, blocksize);
352     memset(outblockdata, 0, blocksize);
353 
354     free(outblockdata);
355     free(inblockdata);
356 
357     return(0);
358 }
359 
360