xref: /titanic_41/usr/src/uts/common/gssapi/mechs/krb5/crypto/combine_keys.c (revision 159d09a20817016f09b3ea28d1bdada4a336bb91)
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 */
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 
enctype_ok(krb5_enctype e)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 
krb5int_c_combine_keys(krb5_context context,krb5_keyblock * key1,krb5_keyblock * key2,krb5_keyblock * outkey)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     /* 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 
279 cleanup:
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 */
dr(krb5_context context,const struct krb5_enc_provider * enc,const krb5_keyblock * inkey,unsigned char * out,const krb5_data * in_constant)300 static 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