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