dk_encrypt.c revision 7934:6aeeafc994de
180709Sjake/* 280709Sjake * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 380709Sjake * Use is subject to license terms. 480709Sjake */ 580709Sjake 680709Sjake 780709Sjake/* 880709Sjake * Copyright (C) 1998 by the FundsXpress, INC. 980709Sjake * 1080709Sjake * All rights reserved. 1180709Sjake * 1280709Sjake * Export of this software from the United States of America may require 1380709Sjake * a specific license from the United States Government. It is the 1480709Sjake * responsibility of any person or organization contemplating export to 1580709Sjake * obtain such a license before exporting. 1680709Sjake * 1780709Sjake * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 1880709Sjake * distribute this software and its documentation for any purpose and 1980709Sjake * without fee is hereby granted, provided that the above copyright 2080709Sjake * notice appear in all copies and that both that copyright notice and 2180709Sjake * this permission notice appear in supporting documentation, and that 2280709Sjake * the name of FundsXpress. not be used in advertising or publicity pertaining 2380709Sjake * to distribution of the software without specific, written prior 2480709Sjake * permission. FundsXpress makes no representations about the suitability of 2580709Sjake * this software for any purpose. It is provided "as is" without express 2680709Sjake * or implied warranty. 2780709Sjake * 2880709Sjake * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 2980709Sjake * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 3080709Sjake * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 3180709Sjake */ 3280709Sjake 3380709Sjake#include "k5-int.h" 3480709Sjake#include "dk.h" 3597446Sjake 3680709Sjake#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */ 3780709Sjake 3880709Sjake/* the spec says that the confounder size and padding are specific to 3980709Sjake the encryption algorithm. This code (dk_encrypt_length and 4080709Sjake dk_encrypt) assume the confounder is always the blocksize, and the 4180709Sjake padding is always zero bytes up to the blocksize. If these 4280709Sjake assumptions ever fails, the keytype table should be extended to 4397027Sjake include these bits of info. */ 4497027Sjake 4597027Sjakevoid 4697027Sjakekrb5_dk_encrypt_length(const struct krb5_enc_provider *enc, 4797027Sjake const struct krb5_hash_provider *hash, 4880709Sjake size_t inputlen, size_t *length) 4997027Sjake{ 5097027Sjake size_t blocksize, hashsize; 5197027Sjake 5297027Sjake blocksize = enc->block_size; 5397027Sjake hashsize = hash->hashsize; 5480709Sjake *length = krb5_roundup(blocksize+inputlen, blocksize) + hashsize; 5597027Sjake} 5697027Sjake 5788652Sjakekrb5_error_code 5888652Sjakekrb5_dk_encrypt(krb5_context context, 5988652Sjake const struct krb5_enc_provider *enc, 6088652Sjake const struct krb5_hash_provider *hash, 6188652Sjake const krb5_keyblock *key, krb5_keyusage usage, 6280709Sjake const krb5_data *ivec, const krb5_data *input, 6388652Sjake krb5_data *output) 6488652Sjake{ 6588652Sjake size_t blocksize, plainlen, enclen; 6688652Sjake krb5_error_code ret; 6788652Sjake krb5_data d1, d2; 6888652Sjake unsigned char *plaintext = NULL, *cn; 6988652Sjake krb5_keyblock *derived_encr_key = NULL; 7097027Sjake krb5_keyblock *derived_hmac_key = NULL; 7180709Sjake 7280709Sjake KRB5_LOG0(KRB5_INFO, "krb5_dk_encrypt() start"); 7388652Sjake 7482903Sjake /* 7588652Sjake * Derive the encryption and hmac keys. 7688652Sjake * This routine is optimized to fetch the DK 7788652Sjake * from the original key's DK list. 7888652Sjake */ 7988652Sjake ret = init_derived_keydata(context, enc, 8088652Sjake (krb5_keyblock *)key, 8188652Sjake usage, 8288652Sjake &derived_encr_key, 8380709Sjake &derived_hmac_key); 8491224Sjake if (ret) 8591224Sjake return (ret); 8697027Sjake 8797027Sjake blocksize = enc->block_size; 8897027Sjake plainlen = krb5_roundup(blocksize+input->length, blocksize); 8997027Sjake 9097027Sjake krb5_dk_encrypt_length(enc, hash, input->length, &enclen); 9197027Sjake 9297027Sjake if (output->length < enclen) 9397027Sjake return(KRB5_BAD_MSIZE); 9484182Sjake 9597027Sjake if ((plaintext = (unsigned char *) MALLOC(plainlen)) == NULL) { 9697027Sjake return(ENOMEM); 9797027Sjake } 9897027Sjake 9997027Sjake /* put together the plaintext */ 10097027Sjake d1.length = blocksize; 10197446Sjake d1.data = (char *) plaintext; 10297446Sjake 10397446Sjake if ((ret = krb5_c_random_make_octets(context, &d1))) 10497446Sjake goto cleanup; 10597027Sjake 10683053Sobrien (void) memcpy(plaintext+blocksize, input->data, input->length); 10791224Sjake 10880709Sjake (void) memset(plaintext+blocksize+input->length, 0, 10997446Sjake plainlen - (blocksize+input->length)); 11097446Sjake 11180709Sjake /* encrypt the plaintext */ 11280709Sjake d1.length = plainlen; 11391224Sjake d1.data = (char *) plaintext; 11497027Sjake 11580709Sjake d2.length = plainlen; 11697027Sjake d2.data = output->data; 11780709Sjake 11880709Sjake /* 11980709Sjake * Always use the derived encryption key here. 12097027Sjake */ 12180709Sjake if ((ret = ((*(enc->encrypt))(context, derived_encr_key, 12297027Sjake ivec, &d1, &d2)))) 12380709Sjake goto cleanup; 12480709Sjake 12580709Sjake 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 156cleanup: 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*/ 166void 167krb5int_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*/ 184static krb5_error_code 185trunc_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 219krb5_error_code 220krb5int_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 */ 313cleanup: 314 (void) memset(plaintext, 0, plainlen); 315 316 FREE(plaintext, plainlen); 317 318 return(ret); 319} 320