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