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*/
58static krb5_error_code
59k5_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*/
153static krb5_error_code
154k5_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
256cleanup:
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
264const struct krb5_keyhash_provider krb5int_keyhash_md5des = {
265    CONFLENGTH+MD5_CKSUM_LENGTH,
266    k5_md5des_hash,
267    k5_md5des_verify
268};
269