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  */
47 static krb5_error_code
derive_cksum_key(krb5_context context,struct krb5_enc_provider * enc,const krb5_keyblock * key,krb5_keyusage usage,krb5_keyblock ** outkey)48 derive_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 	}
106 finish:
107 	KRB5_LOG0(KRB5_INFO, "derive_cksum_key() end.");
108 	return (ret);
109 }
110 
111 /* ARGSUSED */
112 krb5_error_code
krb5_dk_make_checksum(context,hash,key,usage,input,output)113 krb5_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 */
162 cleanup:
163 
164     KRB5_LOG0(KRB5_INFO, "krb5_dk_make_checksum() end");
165     return(ret);
166 }
167 
168