1 /* 2 * Copyright 2005 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 #include <k5-int.h> 35 #include <dk.h> 36 37 #define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */ 38 39 /* the spec says that the confounder size and padding are specific to 40 the encryption algorithm. This code (dk_encrypt_length and 41 dk_encrypt) assume the confounder is always the blocksize, and the 42 padding is always zero bytes up to the blocksize. If these 43 assumptions ever fails, the keytype table should be extended to 44 include these bits of info. */ 45 46 void 47 krb5_dk_encrypt_length(const struct krb5_enc_provider *enc, 48 const struct krb5_hash_provider *hash, 49 size_t inputlen, size_t *length) 50 { 51 size_t blocksize, hashsize; 52 53 blocksize = enc->block_size; 54 hashsize = hash->hashsize; 55 56 *length = krb5_roundup(blocksize+inputlen, blocksize) + hashsize; 57 } 58 59 krb5_error_code 60 krb5_dk_encrypt( 61 krb5_context context, 62 krb5_const struct krb5_enc_provider *enc, 63 krb5_const struct krb5_hash_provider *hash, 64 krb5_const krb5_keyblock *key, 65 krb5_keyusage usage, 66 krb5_const krb5_data *ivec, 67 krb5_const krb5_data *input, 68 krb5_data *output) 69 { 70 size_t blocksize, plainlen, enclen; 71 krb5_error_code ret; 72 krb5_data d1, d2; 73 unsigned char *plaintext = NULL, *cn; 74 krb5_keyblock *derived_encr_key = NULL; 75 krb5_keyblock *derived_hmac_key = NULL; 76 77 KRB5_LOG0(KRB5_INFO, "krb5_dk_encrypt() start"); 78 79 /* 80 * Derive the encryption and hmac keys. 81 * This routine is optimized to fetch the DK 82 * from the original key's DK list. 83 */ 84 ret = init_derived_keydata(context, enc, 85 (krb5_keyblock *)key, 86 usage, 87 &derived_encr_key, 88 &derived_hmac_key); 89 if (ret) 90 return (ret); 91 92 blocksize = enc->block_size; 93 plainlen = krb5_roundup(blocksize+input->length, blocksize); 94 95 krb5_dk_encrypt_length(enc, hash, input->length, &enclen); 96 97 if (output->length < enclen) 98 return(KRB5_BAD_MSIZE); 99 100 if ((plaintext = (unsigned char *) MALLOC(plainlen)) == NULL) { 101 return(ENOMEM); 102 } 103 104 /* put together the plaintext */ 105 d1.length = blocksize; 106 d1.data = (char *) plaintext; 107 108 if ((ret = krb5_c_random_make_octets(context, &d1))) 109 goto cleanup; 110 111 (void) memcpy(plaintext+blocksize, input->data, input->length); 112 113 (void) memset(plaintext+blocksize+input->length, 0, 114 plainlen - (blocksize+input->length)); 115 116 /* encrypt the plaintext */ 117 d1.length = plainlen; 118 d1.data = (char *) plaintext; 119 120 d2.length = plainlen; 121 d2.data = output->data; 122 123 /* 124 * Always use the derived encryption key here. 125 */ 126 if ((ret = ((*(enc->encrypt))(context, derived_encr_key, 127 ivec, &d1, &d2)))) 128 goto cleanup; 129 130 if (ivec != NULL && ivec->length == blocksize) 131 cn = (unsigned char *) d2.data + d2.length - blocksize; 132 else 133 cn = NULL; 134 135 /* hash the plaintext */ 136 d2.length = enclen - plainlen; 137 d2.data = output->data+plainlen; 138 139 output->length = enclen; 140 141 #ifdef _KERNEL 142 if ((ret = krb5_hmac(context, derived_hmac_key, &d1, &d2))) { 143 (void) memset(d2.data, 0, d2.length); 144 goto cleanup; 145 } 146 #else 147 if ((ret = krb5_hmac(context, hash, derived_hmac_key, 148 1, &d1, &d2))) { 149 (void) memset(d2.data, 0, d2.length); 150 goto cleanup; 151 } 152 #endif /* _KERNEL */ 153 154 /* update ivec */ 155 if (cn != NULL) 156 (void) memcpy(ivec->data, cn, blocksize); 157 158 /* ret is set correctly by the prior call */ 159 160 cleanup: 161 FREE(plaintext, plainlen); 162 163 KRB5_LOG(KRB5_INFO, "krb5_dk_encrypt() end, ret=%d\n", ret); 164 return(ret); 165 } 166 167 /* Not necessarily "AES", per se, but "a CBC+CTS mode block cipher 168 with a 96-bit truncated HMAC". */ 169 /*ARGSUSED*/ 170 void 171 krb5int_aes_encrypt_length(enc, hash, inputlen, length) 172 const struct krb5_enc_provider *enc; 173 const struct krb5_hash_provider *hash; 174 size_t inputlen; 175 size_t *length; 176 { 177 size_t blocksize, hashsize; 178 179 blocksize = enc->block_size; 180 hashsize = 96 / 8; 181 182 /* No roundup, since CTS requires no padding once we've hit the 183 block size. */ 184 *length = blocksize+inputlen + hashsize; 185 } 186 187 /*ARGSUSED*/ 188 static krb5_error_code 189 trunc_hmac (krb5_context context, 190 const struct krb5_hash_provider *hash, 191 const krb5_keyblock *ki, int num, 192 const krb5_data *input, krb5_data *output) 193 { 194 size_t hashsize; 195 krb5_error_code ret; 196 char buff[256]; /* sufficiently large enough to hold current hmacs */ 197 krb5_data tmphash; 198 199 hashsize = hash->hashsize; 200 if (hashsize < output->length) 201 return (KRB5_CRYPTO_INTERNAL); 202 203 tmphash.length = hashsize; 204 tmphash.data = buff; 205 206 #ifdef _KERNEL 207 ret = krb5_hmac(context, ki, input, &tmphash); 208 #else 209 ret = krb5_hmac(context, hash, ki, num, input, &tmphash); 210 #endif /* _KERNEL */ 211 212 if (ret) 213 (void) memset(output->data, 0, output->length); 214 else 215 /* truncate the HMAC output accordingly */ 216 (void) memcpy(output->data, tmphash.data, output->length); 217 218 (void) memset(buff, 0, sizeof(buff)); 219 return (ret); 220 } 221 222 223 krb5_error_code 224 krb5int_aes_dk_encrypt(krb5_context context, 225 const struct krb5_enc_provider *enc, 226 const struct krb5_hash_provider *hash, 227 const krb5_keyblock *key, 228 krb5_keyusage usage, 229 const krb5_data *ivec, 230 const krb5_data *input, 231 krb5_data *output) 232 { 233 size_t blocksize, plainlen, enclen; 234 krb5_error_code ret; 235 krb5_data d1, d2; 236 unsigned char *plaintext, *cn; 237 krb5_keyblock *derived_encr_key = NULL; 238 krb5_keyblock *derived_hmac_key = NULL; 239 240 /* 241 * Derive the encryption and hmac keys. 242 * This routine is optimized to fetch the DK 243 * from the original key's DK list. 244 */ 245 ret = init_derived_keydata(context, enc, 246 (krb5_keyblock *)key, 247 usage, 248 &derived_encr_key, 249 &derived_hmac_key); 250 if (ret) 251 return (ret); 252 253 blocksize = enc->block_size; 254 plainlen = blocksize+input->length; 255 256 krb5int_aes_encrypt_length(enc, hash, input->length, &enclen); 257 258 /* key->length, ivec will be tested in enc->encrypt */ 259 if (output->length < enclen) 260 return(KRB5_BAD_MSIZE); 261 262 if ((plaintext = (unsigned char *) MALLOC(plainlen)) == NULL) { 263 return(ENOMEM); 264 } 265 266 d1.length = blocksize; 267 d1.data = (char *)plaintext; 268 269 if ((ret = krb5_c_random_make_octets(context, &d1))) 270 goto cleanup; 271 272 (void) memcpy(plaintext+blocksize, input->data, input->length); 273 274 /* Ciphertext stealing; there should be no more. */ 275 if (plainlen != blocksize + input->length) { 276 ret = KRB5_BAD_KEYSIZE; 277 goto cleanup; 278 } 279 280 /* encrypt the plaintext */ 281 282 d1.length = plainlen; 283 d1.data = (char *)plaintext; 284 285 d2.length = plainlen; 286 d2.data = output->data; 287 288 if ((ret = ((*(enc->encrypt))(context, derived_encr_key, ivec, &d1, &d2)))) 289 goto cleanup; 290 291 if (ivec != NULL && ivec->length == blocksize) { 292 int nblocks = (d2.length + blocksize - 1) / blocksize; 293 cn = (uchar_t *) d2.data + blocksize * (nblocks - 2); 294 } else { 295 cn = NULL; 296 } 297 298 /* hash the plaintext */ 299 d2.length = enclen - plainlen; 300 d2.data = output->data+plainlen; 301 if (d2.length != 96 / 8) 302 goto cleanup; 303 304 if ((ret = trunc_hmac(context, hash, derived_hmac_key, 1, &d1, &d2))) { 305 (void) memset(d2.data, 0, d2.length); 306 goto cleanup; 307 } 308 309 output->length = enclen; 310 311 /* update ivec */ 312 if (cn != NULL) { 313 (void) memcpy(ivec->data, cn, blocksize); 314 } 315 316 /* ret is set correctly by the prior call */ 317 cleanup: 318 (void) memset(plaintext, 0, plainlen); 319 320 FREE(plaintext, plainlen); 321 322 return(ret); 323 } 324