From: Greg Hudson Date: Mon, 30 Nov 2009 16:19:24 +0000 (+0000) Subject: Add an AEAD provider for enctypes which use krb5_old_encrypt and X-Git-Tag: krb5-1.8-alpha1~121 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=2b02e102d3c4e3bb54c936d301371d6b3d88cb52;p=krb5.git Add an AEAD provider for enctypes which use krb5_old_encrypt and krb5_old_decrypt; this makes every enctype have an AEAD provider. To make this work, expose make_unkeyed_checksum_iov to other files (under the name krb5int_hash_iov) and make krb5int_c_padding_length take into account the header length. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23388 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/lib/crypto/krb/aead.c b/src/lib/crypto/krb/aead.c index d6c5bbfba..04fdab008 100644 --- a/src/lib/crypto/krb/aead.c +++ b/src/lib/crypto/krb/aead.c @@ -54,11 +54,11 @@ krb5int_c_locate_iov(krb5_crypto_iov *data, return iov; } -static krb5_error_code -make_unkeyed_checksum_iov(const struct krb5_hash_provider *hash_provider, - const krb5_crypto_iov *data, - size_t num_data, - krb5_data *output) +/* Glue the IOV interface to the hash provider's old list-of-buffers. */ +krb5_error_code +krb5int_hash_iov(const struct krb5_hash_provider *hash_provider, + const krb5_crypto_iov *data, size_t num_data, + krb5_data *output) { krb5_data *sign_data; size_t num_sign_data; @@ -125,8 +125,7 @@ krb5int_c_make_checksum_iov(const struct krb5_cksumtypes *cksum_type, key, usage, data, num_data, cksum_data); } else { - ret = make_unkeyed_checksum_iov(cksum_type->hash, data, num_data, - cksum_data); + ret = krb5int_hash_iov(cksum_type->hash, data, num_data, cksum_data); } if (ret == 0) { @@ -432,9 +431,22 @@ krb5int_c_padding_length(const struct krb5_aead_provider *aead, size_t data_length, unsigned int *pad_length) { - unsigned int padding; + unsigned int header, padding; krb5_error_code ret; + /* + * Add in the header length since the header is encrypted along with the + * data. (arcfour violates this assumption since not all of the header is + * encrypted, but that's okay since it has no padding. If there is ever an + * enctype using a similar token format and a block cipher, we will have to + * move this logic into an enctype-dependent function.) + */ + ret = (*aead->crypto_length)(aead, enc, hash, KRB5_CRYPTO_TYPE_HEADER, + &header); + if (ret != 0) + return ret; + data_length += header; + ret = (*aead->crypto_length)(aead, enc, hash, KRB5_CRYPTO_TYPE_PADDING, &padding); if (ret != 0) diff --git a/src/lib/crypto/krb/aead.h b/src/lib/crypto/krb/aead.h index fd06500e8..dcadfee22 100644 --- a/src/lib/crypto/krb/aead.h +++ b/src/lib/crypto/krb/aead.h @@ -35,6 +35,11 @@ krb5int_c_locate_iov(krb5_crypto_iov *data, size_t num_data, krb5_cryptotype type); +krb5_error_code +krb5int_hash_iov(const struct krb5_hash_provider *hash_provider, + const krb5_crypto_iov *data, size_t num_data, + krb5_data *output); + krb5_error_code krb5int_c_make_checksum_iov(const struct krb5_cksumtypes *cksum, krb5_key key, diff --git a/src/lib/crypto/krb/etypes.c b/src/lib/crypto/krb/etypes.c index bd9bb97d5..9e150348e 100644 --- a/src/lib/crypto/krb/etypes.c +++ b/src/lib/crypto/krb/etypes.c @@ -52,7 +52,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = { krb5int_des_string_to_key, krb5int_des_prf, CKSUMTYPE_RSA_MD5, - NULL, /*AEAD*/ + &krb5int_aead_old, ETYPE_WEAK }, { ENCTYPE_DES_CBC_MD4, "des-cbc-md4", { 0 }, "DES cbc mode with RSA-MD4", @@ -62,7 +62,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = { krb5int_des_string_to_key, krb5int_des_prf, CKSUMTYPE_RSA_MD4, - NULL, /*AEAD*/ + &krb5int_aead_old, ETYPE_WEAK }, { ENCTYPE_DES_CBC_MD5, "des-cbc-md5", { "des" }, "DES cbc mode with RSA-MD5", @@ -72,7 +72,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = { krb5int_des_string_to_key, krb5int_des_prf, CKSUMTYPE_RSA_MD5, - NULL, /*AEAD*/ + &krb5int_aead_old, ETYPE_WEAK }, { ENCTYPE_DES_CBC_RAW, "des-cbc-raw", { 0 }, "DES cbc mode raw", @@ -115,7 +115,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = { krb5int_dk_string_to_key, NULL, /*PRF*/ 0, - NULL, + &krb5int_aead_old, ETYPE_WEAK }, { ENCTYPE_ARCFOUR_HMAC, "arcfour-hmac", { "rc4-hmac", "arcfour-hmac-md5" }, diff --git a/src/lib/crypto/krb/old/Makefile.in b/src/lib/crypto/krb/old/Makefile.in index ea704cca5..aadeacc03 100644 --- a/src/lib/crypto/krb/old/Makefile.in +++ b/src/lib/crypto/krb/old/Makefile.in @@ -1,6 +1,6 @@ mydir=lib/crypto/krb/old BUILDTOP=$(REL)..$(S)..$(S)..$(S).. -LOCALINCLUDES = -I$(srcdir)/../../@CRYPTO_IMPL@/des -I$(srcdir) +LOCALINCLUDES = -I$(srcdir)/../../@CRYPTO_IMPL@/des -I$(srcdir)/.. -I$(srcdir) DEFS= ##DOS##BUILDTOP = ..\..\.. @@ -12,12 +12,12 @@ PROG_RPATH=$(KRB5_LIBDIR) RUN_SETUP = @KRB5_RUN_ENV@ KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf -STLIBOBJS= old_decrypt.o old_encrypt.o des_stringtokey.o +STLIBOBJS= old_aead.o old_decrypt.o old_encrypt.o des_stringtokey.o -OBJS= $(OUTPRE)des_stringtokey.$(OBJEXT) $(OUTPRE)old_decrypt.$(OBJEXT) $(OUTPRE)old_encrypt.$(OBJEXT) +OBJS= $(OUTPRE)des_stringtokey.$(OBJEXT) $(OUTPRE)old_aead.$(OBJEXT) $(OUTPRE)old_decrypt.$(OBJEXT) $(OUTPRE)old_encrypt.$(OBJEXT) -SRCS= $(srcdir)/des_stringtokey.c $(srcdir)/old_decrypt.c \ - $(srcdir)/old_encrypt.c +SRCS= $(srcdir)/des_stringtokey.c $(srcdir)/old_aead.c \ + $(srcdir)/old_decrypt.c $(srcdir)/old_encrypt.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/krb/old/old.h b/src/lib/crypto/krb/old/old.h index 1ed19a087..58f4f5a79 100644 --- a/src/lib/crypto/krb/old/old.h +++ b/src/lib/crypto/krb/old/old.h @@ -49,3 +49,5 @@ krb5_error_code krb5int_des_string_to_key(const struct krb5_enc_provider *enc, const krb5_data *salt, const krb5_data *params, krb5_keyblock *key); + +extern const struct krb5_aead_provider krb5int_aead_old; diff --git a/src/lib/crypto/krb/old/old_aead.c b/src/lib/crypto/krb/old/old_aead.c new file mode 100644 index 000000000..3dea3c36d --- /dev/null +++ b/src/lib/crypto/krb/old/old_aead.c @@ -0,0 +1,209 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * lib/crypto/old/old_aead.c + * + * Copyright 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + + +#include "k5-int.h" +#include "old.h" +#include "aead.h" + +static krb5_error_code +krb5int_old_crypto_length(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + krb5_cryptotype type, + unsigned int *length) +{ + switch (type) { + case KRB5_CRYPTO_TYPE_HEADER: + *length = enc->block_size + hash->hashsize; + break; + case KRB5_CRYPTO_TYPE_PADDING: + *length = enc->block_size; + break; + case KRB5_CRYPTO_TYPE_TRAILER: + *length = 0; + break; + case KRB5_CRYPTO_TYPE_CHECKSUM: + *length = hash->hashsize; + break; + default: + assert(0 && "invalid cryptotype passed to krb5int_old_crypto_length"); + break; + } + + return 0; +} + +static krb5_error_code +krb5int_old_encrypt_iov(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + krb5_key key, krb5_keyusage usage, + const krb5_data *ivec, krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + krb5_crypto_iov *header, *trailer, *padding; + krb5_data checksum, confounder, crcivec = empty_data(); + unsigned int plainlen, padsize; + size_t i; + + /* E(Confounder | Checksum | Plaintext | Pad) */ + + plainlen = enc->block_size + hash->hashsize; + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + if (iov->flags == KRB5_CRYPTO_TYPE_DATA) + plainlen += iov->data.length; + } + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || + header->data.length < enc->block_size + hash->hashsize) + return KRB5_BAD_MSIZE; + + /* Trailer may be absent. */ + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer != NULL) + trailer->data.length = 0; + + /* Check that the input data is correctly padded. */ + padsize = krb5_roundup(plainlen, enc->block_size) - plainlen; + padding = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_PADDING); + if (padsize > 0 && (padding == NULL || padding->data.length < padsize)) + return KRB5_BAD_MSIZE; + if (padding) { + padding->data.length = padsize; + memset(padding->data.data, 0, padsize); + } + + /* Generate a confounder in the header block. */ + confounder = make_data(header->data.data, enc->block_size); + ret = krb5_c_random_make_octets(0, &confounder); + if (ret != 0) + goto cleanup; + checksum = make_data(header->data.data + enc->block_size, hash->hashsize); + memset(checksum.data, 0, hash->hashsize); + + /* Checksum the plaintext with zeroed checksum and padding. */ + ret = krb5int_hash_iov(hash, data, num_data, &checksum); + if (ret != 0) + goto cleanup; + + /* Use the key as the ivec for des-cbc-crc if none was provided. */ + if (key->keyblock.enctype == ENCTYPE_DES_CBC_CRC && ivec == NULL) { + ret = alloc_data(&crcivec, key->keyblock.length); + memcpy(crcivec.data, key->keyblock.contents, key->keyblock.length); + ivec = &crcivec; + } + + ret = enc->encrypt_iov(key, ivec, data, num_data); + if (ret != 0) + goto cleanup; + +cleanup: + zapfree(crcivec.data, crcivec.length); + return ret; +} + +static krb5_error_code +krb5int_old_decrypt_iov(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + krb5_key key, krb5_keyusage usage, + const krb5_data *ivec, krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + krb5_crypto_iov *header, *trailer; + krb5_data checksum, crcivec = empty_data(); + char *saved_checksum = NULL; + size_t i; + unsigned int cipherlen = 0; + + /* Check that the input data is correctly padded. */ + for (i = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + cipherlen += iov->data.length; + } + if (cipherlen % enc->block_size != 0) + return KRB5_BAD_MSIZE; + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || + header->data.length != enc->block_size + hash->hashsize) + return KRB5_BAD_MSIZE; + + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer != NULL && trailer->data.length != 0) + return KRB5_BAD_MSIZE; + + /* Use the key as the ivec for des-cbc-crc if none was provided. */ + if (key->keyblock.enctype == ENCTYPE_DES_CBC_CRC && ivec == NULL) { + ret = alloc_data(&crcivec, key->keyblock.length); + memcpy(crcivec.data, key->keyblock.contents, key->keyblock.length); + ivec = &crcivec; + } + + /* Decrypt the ciphertext. */ + ret = enc->decrypt_iov(key, ivec, data, num_data); + if (ret != 0) + goto cleanup; + + /* Save the checksum, then zero it out in the plaintext. */ + checksum = make_data(header->data.data + enc->block_size, hash->hashsize); + saved_checksum = k5alloc(hash->hashsize, &ret); + if (saved_checksum == NULL) + goto cleanup; + memcpy(saved_checksum, checksum.data, checksum.length); + memset(checksum.data, 0, checksum.length); + + /* + * Checksum the plaintext (with zeroed checksum field), storing the result + * back into the plaintext field we just zeroed out. Then compare it to + * the saved checksum. + */ + ret = krb5int_hash_iov(hash, data, num_data, &checksum); + if (memcmp(checksum.data, saved_checksum, checksum.length) != 0) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + goto cleanup; + } + +cleanup: + zapfree(crcivec.data, crcivec.length); + zapfree(saved_checksum, hash->hashsize); + return ret; +} + +const struct krb5_aead_provider krb5int_aead_old = { + krb5int_old_crypto_length, + krb5int_old_encrypt_iov, + krb5int_old_decrypt_iov +};