1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 /*
8  * Copyright (C) 1998 by the FundsXpress, INC.
9  *
10  * All rights reserved.
11  *
12  * Export of this software from the United States of America may require
13  * a specific license from the United States Government.  It is the
14  * responsibility of any person or organization contemplating export to
15  * obtain such a license before exporting.
16  *
17  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18  * distribute this software and its documentation for any purpose and
19  * without fee is hereby granted, provided that the above copyright
20  * notice appear in all copies and that both that copyright notice and
21  * this permission notice appear in supporting documentation, and that
22  * the name of FundsXpress. not be used in advertising or publicity pertaining
23  * to distribution of the software without specific, written prior
24  * permission.  FundsXpress makes no representations about the suitability of
25  * this software for any purpose.  It is provided "as is" without express
26  * or implied warranty.
27  *
28  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31  */
32 
33 /* Solaris Kerberos:
34  * this code is based on the
35  * usr/src/lib/gss_mechs/mech_krb5/crypto/keyhash_provider/k5_md5des.c
36  * file, but has been modified to use the Solaris resident md5.o kernel
37  * module and associated header /usr/include/sys/md5.o.
38  * This means that the MD5* functions are called instead of krb5_MD5*.
39  */
40 
41 #include <des_int.h>
42 #include <krb5.h>
43 #include <keyhash_provider.h>
44 #include <sys/kmem.h>
45 #include <sys/crypto/api.h>
46 
47 #define CONFLENGTH 8
48 
49 /* Force acceptance of krb5-beta5 md5des checksum for now. */
50 #define KRB5_MD5DES_BETA5_COMPAT
51 
52 /* des-cbc(xorkey, conf | rsa-md5(conf | data)) */
53 
54 /* this could be done in terms of the md5 and des providers, but
55    that's less efficient, and there's no need for this to be generic */
56 
57 /*ARGSUSED*/
58 static krb5_error_code
k5_md5des_hash(krb5_context context,krb5_const krb5_keyblock * key,krb5_keyusage usage,krb5_const krb5_data * ivec,krb5_const krb5_data * input,krb5_data * output)59 k5_md5des_hash(krb5_context context,
60 	krb5_const krb5_keyblock *key,
61 	krb5_keyusage usage,
62 	krb5_const krb5_data *ivec,
63 	krb5_const krb5_data *input, krb5_data *output)
64 {
65     krb5_error_code ret = 0;
66     krb5_data data;
67     unsigned char conf[CONFLENGTH];
68     unsigned char xorkey[MIT_DES_KEYSIZE];
69     int i;
70     krb5_data *hash_input;
71     char *outptr;
72     krb5_keyblock newkey;
73 
74     if (key->length != MIT_DES_KEYSIZE)
75 	return(KRB5_BAD_KEYSIZE);
76     if (ivec)
77 	return(KRB5_CRYPTO_INTERNAL);
78     if (output->length != (CONFLENGTH + MD5_CKSUM_LENGTH))
79 	return(KRB5_CRYPTO_INTERNAL);
80 
81     /* create the confounder */
82     data.length = CONFLENGTH;
83     data.data = (char *) conf;
84     if ((ret = krb5_c_random_make_octets(context, &data)))
85 	return(ret);
86 
87     /* hash the confounder, then the input data */
88     hash_input = (krb5_data *)MALLOC(sizeof(krb5_data) * 2);
89     if (hash_input == NULL)
90 	return(KRB5_RC_MALLOC);
91 
92     hash_input[0].data = (char *)conf;
93     hash_input[0].length = CONFLENGTH;
94     hash_input[1].data = input->data;
95     hash_input[1].length = input->length;
96 
97     /* Save the pointer to the beginning of the output buffer */
98     outptr = (char *)output->data;
99 
100     /*
101      * Move the output ptr ahead so we can write the hash
102      * digest directly into the buffer.
103      */
104     output->data = output->data + CONFLENGTH;
105 
106     /* Use generic hash function that calls to kEF */
107     if (k5_ef_hash(context, 2, hash_input, output)) {
108 	FREE(hash_input, sizeof(krb5_data) * 2);
109 	return(KRB5_KEF_ERROR);
110     }
111 
112     /* restore the original ptr to the output data */
113     output->data = outptr;
114 
115     /*
116      * Put the confounder in the beginning of the buffer to be
117      * encrypted.
118      */
119     bcopy(conf, output->data, CONFLENGTH);
120 
121     bcopy(key->contents, xorkey, sizeof(xorkey));
122     for (i=0; i<sizeof(xorkey); i++)
123 	xorkey[i] ^= 0xf0;
124 
125     /*
126      * Solaris Kerberos:
127      * Encryption Framework checks for parity and weak keys.
128      */
129     bzero(&newkey, sizeof(krb5_keyblock));
130     newkey.enctype = key->enctype;
131     newkey.contents = xorkey;
132     newkey.length = sizeof(xorkey);
133     newkey.dk_list = NULL;
134     newkey.kef_key.ck_data = NULL;
135     ret = init_key_kef(context->kef_cipher_mt, &newkey);
136     if (ret) {
137 	FREE(hash_input, sizeof(krb5_data) * 2);
138 	return (ret);
139     }
140 
141     /* encrypt it, in place.  this has a return value, but it's
142        always zero.  */
143     ret = mit_des_cbc_encrypt(context, (krb5_pointer) output->data,
144 	(krb5_pointer) output->data, output->length,
145 	&newkey, (unsigned char*) mit_des_zeroblock, 1);
146 
147     FREE(hash_input, sizeof(krb5_data) * 2);
148     (void)crypto_destroy_ctx_template(newkey.key_tmpl);
149     return(ret);
150 }
151 
152 /*ARGSUSED*/
153 static krb5_error_code
k5_md5des_verify(krb5_context context,krb5_const krb5_keyblock * key,krb5_keyusage usage,krb5_const krb5_data * ivec,krb5_const krb5_data * input,krb5_const krb5_data * hash,krb5_boolean * valid)154 k5_md5des_verify(krb5_context context,
155 	krb5_const krb5_keyblock *key,
156 	krb5_keyusage usage,
157 	krb5_const krb5_data *ivec,
158 	krb5_const krb5_data *input,
159 	krb5_const krb5_data *hash,
160 	krb5_boolean *valid)
161 {
162     krb5_error_code ret = 0;
163     unsigned char plaintext[CONFLENGTH + MD5_CKSUM_LENGTH];
164     unsigned char xorkey[8];
165     int i;
166     int compathash = 0;
167     krb5_octet outtmp[MD5_CKSUM_LENGTH];
168     size_t hisize;
169     krb5_data *hash_input;
170     krb5_data hash_output;
171     krb5_keyblock newkey;
172 
173     if (key->length != MIT_DES_KEYSIZE)
174 	return(KRB5_BAD_KEYSIZE);
175     if (ivec)
176 	return(KRB5_CRYPTO_INTERNAL);
177     if (hash->length != (CONFLENGTH + MD5_CKSUM_LENGTH)) {
178 #ifdef KRB5_MD5DES_BETA5_COMPAT
179 	if (hash->length != MD5_CKSUM_LENGTH)
180 	    return(KRB5_CRYPTO_INTERNAL);
181 	else
182 	    compathash = 1;
183 #else
184 	return(KRB5_CRYPTO_INTERNAL);
185 #endif
186     }
187 
188     /* create and schedule the encryption key */
189     (void) bcopy(key->contents, xorkey, sizeof(xorkey));
190     if (!compathash) {
191 	for (i=0; i<sizeof(xorkey); i++)
192 	    xorkey[i] ^= 0xf0;
193     }
194 
195     /*
196      * Solaris Kerberos:
197      * Encryption Framework checks for parity and weak keys
198      */
199     bzero(&newkey, sizeof(krb5_keyblock));
200     newkey.enctype = key->enctype;
201     newkey.contents = xorkey;
202     newkey.length = sizeof(xorkey);
203     newkey.dk_list = NULL;
204     newkey.kef_key.ck_data = NULL;
205     ret = init_key_kef(context->kef_cipher_mt, &newkey);
206 
207     /* decrypt it.  this has a return value, but it's always zero.  */
208     if (!compathash) {
209 	ret = mit_des_cbc_encrypt(context, (krb5_pointer) hash->data,
210 			    (krb5_pointer) plaintext, hash->length,
211 			    &newkey, (unsigned char*) mit_des_zeroblock, 0);
212     } else {
213 	ret = mit_des_cbc_encrypt(context, (krb5_pointer) hash->data,
214 			    (krb5_pointer) plaintext, hash->length,
215 			    &newkey, xorkey, 0);
216     }
217     if (ret) goto cleanup;
218 
219     /* hash the confounder, then the input data */
220     i = 1;
221     if (!compathash)
222 	i++;
223 
224     hisize = sizeof(krb5_data) * i;
225     hash_input = (krb5_data *)MALLOC(hisize);
226     if (hash_input == NULL)
227 	return(KRB5_RC_MALLOC);
228 
229     i=0;
230     if (!compathash) {
231     	hash_input[i].data = (char *)plaintext;
232     	hash_input[i].length = CONFLENGTH;
233 	i++;
234     }
235     hash_input[i].data = input->data;
236     hash_input[i].length = input->length;
237 
238     hash_output.data = (char *)outtmp;
239     hash_output.length = sizeof(outtmp);
240 
241     if (k5_ef_hash(context, 1, hash_input, &hash_output)) {
242 	ret = KRB5_KEF_ERROR;
243 	goto cleanup;
244     }
245 
246     /* compare the decrypted hash to the computed one */
247     if (!compathash) {
248 	*valid = !bcmp((const void *)(plaintext+CONFLENGTH),
249 		(void *)outtmp, MD5_CKSUM_LENGTH);
250     } else {
251 	*valid = !bcmp((const void *)plaintext,
252 		(void *)outtmp, MD5_CKSUM_LENGTH);
253     }
254     bzero((void *)plaintext, sizeof(plaintext));
255 
256 cleanup:
257     if (hash_input != NULL && hisize > 0)
258 	    FREE(hash_input, hisize);
259     (void)crypto_destroy_ctx_template(newkey.key_tmpl);
260 
261     return(ret);
262 }
263 
264 const struct krb5_keyhash_provider krb5int_keyhash_md5des = {
265     CONFLENGTH+MD5_CKSUM_LENGTH,
266     k5_md5des_hash,
267     k5_md5des_verify
268 };
269