1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 #pragma ident "%Z%%M% %I% %E% SMI" 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 #include <k5-int.h> 34 #include <dk.h> 35 36 #define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */ 37 38 /* 39 * Search for a derived key based on the input key, 40 * the usage constant and the dkid byte. 41 * 42 * Return *derived key on success, NULL on failure. 43 */ 44 krb5_keyblock * 45 find_derived_key(krb5_keyusage usage, uchar_t dkid, 46 krb5_keyblock *key) 47 { 48 krb5_dk_node *dknode = key->dk_list; 49 50 while (dknode != NULL) { 51 if (usage == dknode->usage && 52 dkid == dknode->dkid) { 53 KRB5_LOG1(KRB5_INFO, 54 "find_derived_key - MATCH FOUND %d 0x%0x", 55 usage, dkid); 56 return(dknode->derived_key); 57 } 58 dknode = dknode->next; 59 } 60 KRB5_LOG0(KRB5_INFO, "find_derived_key - no match"); 61 return(NULL); 62 } 63 64 /* 65 * Add a derived key to the dk_list for the indicated key. 66 */ 67 krb5_error_code 68 add_derived_key(krb5_keyblock *key, 69 krb5_keyusage usage, uchar_t dkid, 70 krb5_keyblock *derived_key) 71 { 72 krb5_dk_node *dknode; 73 KRB5_LOG1(KRB5_INFO, "add_derived_key: %d 0x%0x", 74 usage, dkid); 75 76 if (key->dk_list == NULL) { 77 key->dk_list = MALLOC(sizeof(krb5_dk_node)); 78 if (key->dk_list == NULL) 79 return (ENOMEM); 80 dknode = key->dk_list; 81 } else { 82 dknode = key->dk_list; 83 /* 84 * Find last derived key in list 85 */ 86 while (dknode->next != NULL) 87 dknode = dknode->next; 88 dknode->next = MALLOC(sizeof(krb5_dk_node)); 89 if (dknode->next == NULL) 90 return (ENOMEM); 91 dknode = dknode->next; 92 } 93 dknode->usage = usage; 94 dknode->dkid = dkid; 95 dknode->derived_key = derived_key; 96 dknode->next = NULL; 97 98 return (0); 99 } 100 101 /* 102 * Utility function to create a new keyblock 103 * Return NULL on failure. 104 */ 105 krb5_keyblock * 106 krb5_create_derived_keyblock(int keysize) 107 { 108 krb5_keyblock *key = MALLOC(sizeof(krb5_keyblock)); 109 110 KRB5_LOG0(KRB5_INFO, "krb5_create_derived_keyblock()"); 111 if (key == NULL) 112 return (NULL); 113 114 bzero(key, sizeof(krb5_keyblock)); 115 116 key->length = keysize; 117 key->contents = (uchar_t *)MALLOC(key->length); 118 if (key->contents == NULL) { 119 FREE(key, sizeof(krb5_keyblock)); 120 return (NULL); 121 } 122 123 bzero(key->contents, key->length); 124 #ifdef _KERNEL 125 key->kef_mt = CRYPTO_MECH_INVALID; 126 key->key_tmpl = NULL; 127 #else 128 key->hKey = CK_INVALID_HANDLE; 129 #endif /* _KERNEL */ 130 return(key); 131 } 132 133 /* 134 * initialize the derived key values in the context. 135 */ 136 krb5_error_code 137 init_derived_keydata(krb5_context context, 138 const struct krb5_enc_provider *enc, 139 krb5_keyblock *key, 140 krb5_keyusage usage, 141 krb5_keyblock **d_encr_key, 142 krb5_keyblock **d_hmac_key) 143 { 144 krb5_error_code rv = 0; 145 unsigned char constantdata[K5CLENGTH]; 146 krb5_keyblock *cached_key; 147 krb5_data d1; 148 149 KRB5_LOG0(KRB5_INFO,"init_ef_derived_keydata()."); 150 151 /* 152 * Get a derived encryption key, either from the cache 153 * or by calculation. 154 */ 155 cached_key = find_derived_key(usage, DK_ENCR_KEY_BYTE, key); 156 if (cached_key != NULL) 157 *d_encr_key = cached_key; 158 else { 159 *d_encr_key = krb5_create_derived_keyblock(key->length); 160 if (*d_encr_key == NULL) { 161 return (ENOMEM); 162 } 163 164 (*d_encr_key)->enctype = key->enctype; 165 166 constantdata[0] = (usage>>24)&0xff; 167 constantdata[1] = (usage>>16)&0xff; 168 constantdata[2] = (usage>>8)&0xff; 169 constantdata[3] = usage&0xff; 170 constantdata[4] = DK_ENCR_KEY_BYTE; 171 172 d1.data = (char *)constantdata; 173 d1.length = sizeof(constantdata); 174 rv = krb5_derive_key(context, enc, key, 175 *d_encr_key, &d1); 176 if (rv != 0) { 177 krb5_free_keyblock(context, *d_encr_key); 178 *d_encr_key = NULL; 179 return (rv); 180 } 181 rv = add_derived_key(key, usage, DK_ENCR_KEY_BYTE, 182 *d_encr_key); 183 184 if (rv != 0) { 185 krb5_free_keyblock(context, *d_encr_key); 186 *d_encr_key = NULL; 187 return (rv); 188 } 189 } 190 191 /* 192 * Get a derived HMAC key, either from the cache 193 * or by calculation. 194 */ 195 cached_key = find_derived_key(usage, DK_HASH_KEY_BYTE, key); 196 if (cached_key != NULL) 197 *d_hmac_key = cached_key; 198 else { 199 *d_hmac_key = krb5_create_derived_keyblock(key->length); 200 if (*d_hmac_key == NULL) { 201 return (ENOMEM); 202 } 203 (*d_hmac_key)->enctype = key->enctype; 204 205 constantdata[0] = (usage>>24)&0xff; 206 constantdata[1] = (usage>>16)&0xff; 207 constantdata[2] = (usage>>8)&0xff; 208 constantdata[3] = usage&0xff; 209 constantdata[4] = DK_HASH_KEY_BYTE; 210 211 d1.data = (char *)constantdata; 212 d1.length = sizeof(constantdata); 213 rv = krb5_derive_key(context, enc, key, *d_hmac_key, &d1); 214 if (rv != 0) { 215 krb5_free_keyblock(context, *d_hmac_key); 216 *d_hmac_key = NULL; 217 return (rv); 218 } 219 #ifdef _KERNEL 220 /* 221 * By default, derived keys get the "mech_type" 222 * that was associated with their parent. 223 * we need to switch the mech type of the derived HMAC key 224 * to correspond to the mech type for the hmac key. 225 */ 226 if ((*d_hmac_key)->kef_mt != context->kef_hash_mt) { 227 (*d_hmac_key)->kef_mt = context->kef_hash_mt; 228 229 if ((*d_hmac_key)->key_tmpl != NULL) { 230 crypto_destroy_ctx_template((*d_hmac_key)->key_tmpl); 231 (*d_hmac_key)->key_tmpl = NULL; 232 } 233 rv = update_key_template(*d_hmac_key); 234 235 if (rv != 0) { 236 krb5_free_keyblock(context, *d_hmac_key); 237 *d_hmac_key = NULL; 238 return (rv); 239 } 240 } 241 #endif /* _KERNEL */ 242 if (rv == 0) { 243 rv = add_derived_key(key, usage, DK_HASH_KEY_BYTE, 244 *d_hmac_key); 245 if (rv != 0) { 246 krb5_free_keyblock(context, *d_hmac_key); 247 *d_hmac_key = NULL; 248 return (rv); 249 } 250 } 251 } 252 KRB5_LOG0(KRB5_INFO,"init_ef_derived_keydata() end."); 253 return (rv); 254 } 255 256 krb5_error_code 257 krb5_derive_key(context, enc, inkey, outkey, in_constant) 258 krb5_context context; 259 krb5_const struct krb5_enc_provider *enc; 260 krb5_const krb5_keyblock *inkey; 261 krb5_keyblock *outkey; 262 krb5_const krb5_data *in_constant; 263 { 264 size_t blocksize, keybytes, keylength, n; 265 unsigned char *inblockdata, *outblockdata, *rawkey; 266 krb5_data inblock, outblock; 267 krb5_error_code ret = 0; 268 269 KRB5_LOG0(KRB5_INFO, "krb5_derive_key() start"); 270 271 blocksize = enc->block_size; 272 keybytes = enc->keybytes; 273 keylength = enc->keylength; 274 275 276 if ((inkey->length != keylength) || 277 (outkey->length != keylength)) 278 return(KRB5_CRYPTO_INTERNAL); 279 280 /* allocate and set up buffers */ 281 if ((inblockdata = (unsigned char *) MALLOC(blocksize)) == NULL) 282 return(ENOMEM); 283 284 if ((outblockdata = (unsigned char *) MALLOC(blocksize)) == NULL) { 285 FREE(inblockdata, blocksize); 286 return(ENOMEM); 287 } 288 289 if ((rawkey = (unsigned char *) MALLOC(keybytes)) == NULL) { 290 FREE(outblockdata, blocksize); 291 FREE(inblockdata, blocksize); 292 return(ENOMEM); 293 } 294 295 inblock.data = (char *) inblockdata; 296 inblock.length = blocksize; 297 298 outblock.data = (char *) outblockdata; 299 outblock.length = blocksize; 300 301 /* initialize the input block */ 302 if (in_constant->length == inblock.length) { 303 (void) memcpy(inblock.data, in_constant->data, inblock.length); 304 } else { 305 krb5_nfold(in_constant->length*8, 306 (krb5_const unsigned char *) in_constant->data, 307 inblock.length*8, (unsigned char *) inblock.data); 308 } 309 310 /* loop encrypting the blocks until enough key bytes are generated */ 311 n = 0; 312 while (n < keybytes) { 313 ret = (*(enc->encrypt))(context, inkey, 0, &inblock, &outblock); 314 315 if (ret) { 316 KRB5_LOG(KRB5_INFO, "krb5_derive_key() encrypt error: %d", ret); 317 goto cleanup; 318 } 319 320 if ((keybytes - n) <= outblock.length) { 321 (void) memcpy(rawkey+n, outblock.data, (keybytes - n)); 322 break; 323 } 324 325 (void) memcpy(rawkey+n, outblock.data, outblock.length); 326 (void) memcpy(inblock.data, outblock.data, outblock.length); 327 n += outblock.length; 328 } 329 330 /* postprocess the key */ 331 inblock.data = (char *) rawkey; 332 inblock.length = keybytes; 333 334 outkey->enctype = inkey->enctype; 335 ret = (*(enc->make_key))(context, &inblock, outkey); 336 337 /* clean memory, free resources and exit */ 338 cleanup: 339 (void) memset(inblockdata, 0, blocksize); 340 (void) memset(outblockdata, 0, blocksize); 341 (void) memset(rawkey, 0, keybytes); 342 343 FREE(rawkey, keybytes); 344 FREE(outblockdata, blocksize); 345 FREE(inblockdata, blocksize); 346 347 KRB5_LOG0(KRB5_INFO, "krb5_derive_key() end"); 348 return(ret); 349 } 350