1/*
2 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*
7 * Copyright (C) 1998 by the FundsXpress, INC.
8 *
9 * All rights reserved.
10 *
11 * Export of this software from the United States of America may require
12 * a specific license from the United States Government.  It is the
13 * responsibility of any person or organization contemplating export to
14 * obtain such a license before exporting.
15 *
16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17 * distribute this software and its documentation for any purpose and
18 * without fee is hereby granted, provided that the above copyright
19 * notice appear in all copies and that both that copyright notice and
20 * this permission notice appear in supporting documentation, and that
21 * the name of FundsXpress. not be used in advertising or publicity pertaining
22 * to distribution of the software without specific, written prior
23 * permission.  FundsXpress makes no representations about the suitability of
24 * this software for any purpose.  It is provided "as is" without express
25 * or implied warranty.
26 *
27 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
28 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
29 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30 */
31
32#include "k5-int.h"
33#include "etypes.h"
34#include "dk.h"
35
36#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */
37
38/*
39 * Derive the key for checksum calculation.
40 * This is only called (currently) for SHA1-DES3
41 * checksum types.
42 *
43 * The primary benefit here is that a KEF template
44 * is created for use when doing the HMAC operation which
45 * saves ALOT of computation cycles and improves performance.
46 */
47static krb5_error_code
48derive_cksum_key(krb5_context context,
49		struct krb5_enc_provider *enc,
50		const krb5_keyblock *key,
51		krb5_keyusage usage,
52		krb5_keyblock **outkey)
53{
54	krb5_error_code ret = 0;
55	krb5_keyblock *cached_key = NULL;
56	krb5_data d1;
57	unsigned char constantdata[K5CLENGTH];
58
59	cached_key = find_derived_key(usage, DK_CKSUM_KEY_BYTE,
60				    (krb5_keyblock *)key);
61	if (cached_key)
62		*outkey = cached_key;
63	else {
64		*outkey = krb5_create_derived_keyblock(key->length);
65		if (*outkey == NULL)
66			return (ENOMEM);
67
68		constantdata[0] = (usage>>24)&0xff;
69		constantdata[1] = (usage>>16)&0xff;
70		constantdata[2] = (usage>>8)&0xff;
71		constantdata[3] = usage&0xff;
72		constantdata[4] = DK_CKSUM_KEY_BYTE;
73
74		d1.data = (char *)constantdata;
75		d1.length = sizeof(constantdata);
76
77		ret = krb5_derive_key(context, enc, key,
78				    *outkey, &d1);
79		if (ret) {
80			krb5_free_keyblock(context, *outkey);
81			*outkey = NULL;
82			return (ret);
83		}
84#ifdef _KERNEL
85		/*
86		 * By default, derived keys get the "mech_type"
87		 * that was associated with their parent.
88		 * we need to switch the mech_type to correspond
89		 * to the checksum mech type.
90		 */
91		if (ret == 0 &&
92		    (*outkey)->kef_mt != context->kef_cksum_mt) {
93			(*outkey)->kef_mt = context->kef_cksum_mt;
94			if ((*outkey)->key_tmpl != NULL) {
95				crypto_destroy_ctx_template((*outkey)->key_tmpl);
96				(*outkey)->key_tmpl = NULL;
97			}
98			ret = update_key_template(*outkey);
99		}
100#endif /* _KERNEL */
101		if (ret == 0)
102			ret = add_derived_key((krb5_keyblock *)key, usage,
103			    DK_CKSUM_KEY_BYTE,
104			    *outkey);
105	}
106finish:
107	KRB5_LOG0(KRB5_INFO, "derive_cksum_key() end.");
108	return (ret);
109}
110
111/* ARGSUSED */
112krb5_error_code
113krb5_dk_make_checksum(context, hash, key, usage, input, output)
114     krb5_context context;
115     krb5_const struct krb5_hash_provider *hash;
116     krb5_const krb5_keyblock *key;
117     krb5_keyusage usage;
118     krb5_const krb5_data *input;
119     krb5_data *output;
120{
121    int i;
122    krb5_error_code ret;
123    krb5_keyblock *cksum_key = NULL;
124    struct krb5_enc_provider *enc = NULL;
125
126    KRB5_LOG0(KRB5_INFO, "krb5_dk_make_checksum() start");
127
128    for (i=0; i<krb5_enctypes_length; i++) {
129	if (krb5_enctypes_list[i].etype == key->enctype)
130	    break;
131    }
132
133    if (i == krb5_enctypes_length) {
134	KRB5_LOG(KRB5_ERR, "krb5_ck_make_checksum bad enctype: %d",
135		key->enctype);
136	return(KRB5_BAD_ENCTYPE);
137    }
138    enc = (struct krb5_enc_provider *)krb5_enctypes_list[i].enc;
139
140#ifdef _KERNEL
141    if (key->kef_key.ck_data == NULL &&
142	(ret = init_key_kef(krb5_enctypes_list[i].kef_cipher_mt,
143			    (krb5_keyblock *)key)))
144	    goto cleanup;
145#endif
146    ret = derive_cksum_key(context, enc, key, usage, &cksum_key);
147    if (ret != 0)
148	    goto cleanup;
149
150#ifdef _KERNEL
151    if ((ret = krb5_hmac(context, (krb5_keyblock *)cksum_key,
152			input, output))) {
153	KRB5_LOG(KRB5_ERR, "krb5_hmac error: %0x", ret);
154	(void) memset(output->data, 0, output->length);
155    }
156#else
157    if ((ret = krb5_hmac(context, hash, cksum_key, 1, input, output)) != 0) {
158	KRB5_LOG(KRB5_ERR, "krb5_hmac error: %0x", ret);
159	(void) memset(output->data, 0, output->length);
160    }
161#endif /* _KERNEL */
162cleanup:
163
164    KRB5_LOG0(KRB5_INFO, "krb5_dk_make_checksum() end");
165    return(ret);
166}
167
168