From b5d22c309b734f6d3908fafa0e681ef7844ad9b3 Mon Sep 17 00:00:00 2001 From: Sam Hartman Date: Tue, 2 Dec 2008 20:10:20 +0000 Subject: Crypto IOV API per Projects/AEAD encryption API Merge in the mskrb-crypto-iov branch at r21259 in order to move an implementation of http://k5wiki.kerberos.org/wiki/Projects/AEAD_encryption_API onto the trunk. This branch contains a subset of the commits on the mskrb-integ branch that implement the krb5 library part of the crypto IOV API. ticket: new Status: open git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@21263 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/crypto/Makefile.in | 47 +-- src/lib/crypto/aead.c | 431 ++++++++++++++++++++++++++++ src/lib/crypto/aead.h | 94 ++++++ src/lib/crypto/arcfour/Makefile.in | 3 + src/lib/crypto/arcfour/arcfour-int.h | 1 + src/lib/crypto/arcfour/arcfour.c | 6 +- src/lib/crypto/arcfour/arcfour.h | 2 + src/lib/crypto/arcfour/arcfour_aead.c | 325 +++++++++++++++++++++ src/lib/crypto/crypto_length.c | 192 +++++++++++++ src/lib/crypto/decrypt_iov.c | 61 ++++ src/lib/crypto/des/Makefile.in | 3 + src/lib/crypto/des/d3_aead.c | 198 +++++++++++++ src/lib/crypto/des/des_int.h | 15 + src/lib/crypto/dk/Makefile.in | 3 + src/lib/crypto/dk/checksum.c | 71 +++++ src/lib/crypto/dk/dk.h | 12 + src/lib/crypto/dk/dk_aead.c | 387 +++++++++++++++++++++++++ src/lib/crypto/enc_provider/aes.c | 177 +++++++++++- src/lib/crypto/enc_provider/des3.c | 83 +++++- src/lib/crypto/enc_provider/enc_provider.h | 1 + src/lib/crypto/enc_provider/rc4.c | 61 +++- src/lib/crypto/encrypt_iov.c | 55 ++++ src/lib/crypto/etypes.c | 60 ++-- src/lib/crypto/hmac.c | 38 +++ src/lib/crypto/keyhash_provider/descbc.c | 2 + src/lib/crypto/keyhash_provider/hmac_md5.c | 61 +++- src/lib/crypto/keyhash_provider/k5_md4des.c | 4 +- src/lib/crypto/keyhash_provider/k5_md5des.c | 4 +- src/lib/crypto/libk5crypto.exports | 8 + src/lib/crypto/make_checksum_iov.c | 68 +++++ src/lib/crypto/t_encrypt.c | 45 ++- src/lib/crypto/verify_checksum_iov.c | 92 ++++++ src/lib/krb5/os/accessor.c | 1 + 33 files changed, 2561 insertions(+), 50 deletions(-) create mode 100644 src/lib/crypto/aead.c create mode 100644 src/lib/crypto/aead.h create mode 100644 src/lib/crypto/arcfour/arcfour_aead.c create mode 100644 src/lib/crypto/crypto_length.c create mode 100644 src/lib/crypto/decrypt_iov.c create mode 100644 src/lib/crypto/des/d3_aead.c create mode 100644 src/lib/crypto/dk/dk_aead.c create mode 100644 src/lib/crypto/encrypt_iov.c create mode 100644 src/lib/crypto/make_checksum_iov.c create mode 100644 src/lib/crypto/verify_checksum_iov.c (limited to 'src/lib') diff --git a/src/lib/crypto/Makefile.in b/src/lib/crypto/Makefile.in index 2a3015adc4..a4ab872b0c 100644 --- a/src/lib/crypto/Makefile.in +++ b/src/lib/crypto/Makefile.in @@ -34,16 +34,20 @@ PROG_LIBPATH=-L$(TOPLIBD) PROG_RPATH=$(KRB5_LIBDIR) STLIBOBJS=\ + aead.o \ block_size.o \ checksum_length.o \ cksumtype_to_string.o \ cksumtypes.o \ coll_proof_cksum.o \ combine_keys.o \ + crypto_length.o \ crypto_libinit.o \ default_state.o \ decrypt.o \ + decrypt_iov.o \ encrypt.o \ + encrypt_iov.o \ encrypt_length.o \ enctype_compare.o \ enctype_to_string.o \ @@ -54,6 +58,7 @@ STLIBOBJS=\ keyed_checksum_types.o \ keylengths.o \ make_checksum.o \ + make_checksum_iov.o \ make_random_key.o \ mandatory_sumtype.o \ nfold.o \ @@ -68,19 +73,24 @@ STLIBOBJS=\ string_to_key.o \ valid_cksumtype.o \ valid_enctype.o \ - verify_checksum.o + verify_checksum.o \ + verify_checksum_iov.o OBJS=\ + $(OUTPRE)aead.$(OBJEXT) \ $(OUTPRE)block_size.$(OBJEXT) \ $(OUTPRE)checksum_length.$(OBJEXT) \ $(OUTPRE)cksumtype_to_string.$(OBJEXT) \ $(OUTPRE)cksumtypes.$(OBJEXT) \ $(OUTPRE)coll_proof_cksum.$(OBJEXT) \ $(OUTPRE)combine_keys.$(OBJEXT) \ + $(OUTPRE)crypto_length.$(OBJEXT) \ $(OUTPRE)crypto_libinit.$(OBJEXT) \ $(OUTPRE)default_state.$(OBJEXT) \ $(OUTPRE)decrypt.$(OBJEXT) \ + $(OUTPRE)decrypt_iov.$(OBJEXT) \ $(OUTPRE)encrypt.$(OBJEXT) \ + $(OUTPRE)encrypt_iov.$(OBJEXT) \ $(OUTPRE)encrypt_length.$(OBJEXT) \ $(OUTPRE)enctype_compare.$(OBJEXT) \ $(OUTPRE)enctype_to_string.$(OBJEXT) \ @@ -91,6 +101,7 @@ OBJS=\ $(OUTPRE)keyed_checksum_types.$(OBJEXT) \ $(OUTPRE)keylengths.$(OBJEXT) \ $(OUTPRE)make_checksum.$(OBJEXT) \ + $(OUTPRE)make_checksum_iov.$(OBJEXT) \ $(OUTPRE)make_random_key.$(OBJEXT) \ $(OUTPRE)mandatory_sumtype.$(OBJEXT) \ $(OUTPRE)nfold.$(OBJEXT) \ @@ -105,19 +116,24 @@ OBJS=\ $(OUTPRE)string_to_key.$(OBJEXT) \ $(OUTPRE)valid_cksumtype.$(OBJEXT) \ $(OUTPRE)valid_enctype.$(OBJEXT) \ - $(OUTPRE)verify_checksum.$(OBJEXT) + $(OUTPRE)verify_checksum.$(OBJEXT) \ + $(OUTPRE)verify_checksum_iov.$(OBJEXT) SRCS=\ + $(srcdir)/aead.c \ $(srcdir)/block_size.c \ $(srcdir)/checksum_length.c \ $(srcdir)/cksumtype_to_string.c \ $(srcdir)/cksumtypes.c \ $(srcdir)/coll_proof_cksum.c \ $(srcdir)/combine_keys.c \ + $(srcdir)/crypto_length.c \ $(srcdir)/crypto_libinit.c \ $(srcdir)/default_state.c \ $(srcdir)/decrypt.c \ + $(srcdir)/decrypt_iov.c \ $(srcdir)/encrypt.c \ + $(srcdir)/encrypt_iov.c \ $(srcdir)/encrypt_length.c \ $(srcdir)/enctype_compare.c \ $(srcdir)/enctype_to_string.c \ @@ -128,6 +144,7 @@ SRCS=\ $(srcdir)/keyed_checksum_types.c\ $(srcdir)/keylengths.c \ $(srcdir)/make_checksum.c \ + $(srcdir)/make_checksum_iov.c \ $(srcdir)/make_random_key.c \ $(srcdir)/mandatory_sumtype.c \ $(srcdir)/nfold.c \ @@ -142,7 +159,8 @@ SRCS=\ $(srcdir)/string_to_key.c \ $(srcdir)/valid_cksumtype.c \ $(srcdir)/valid_enctype.c \ - $(srcdir)/verify_checksum.c + $(srcdir)/verify_checksum.c \ + $(srcdir)/verify_checksum_iov.c LIBBASE=k5crypto @@ -368,6 +386,16 @@ check-windows:: # Makefile dependencies follow. This must be the last section in # the Makefile.in file # +aead.so aead.po $(OUTPRE)aead.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-gmt_mktime.h \ + $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \ + $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \ + $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \ + $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \ + $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ + aead.c etypes.h aead.h block_size.so block_size.po $(OUTPRE)block_size.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ @@ -422,17 +450,6 @@ coll_proof_cksum.so coll_proof_cksum.po $(OUTPRE)coll_proof_cksum.$(OBJEXT): \ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ $(SRCTOP)/include/socket-utils.h cksumtypes.h coll_proof_cksum.c -combine_keys.so combine_keys.po $(OUTPRE)combine_keys.$(OBJEXT): \ - $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ - $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ - $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ - $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ - $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ - $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ - $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \ - $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \ - $(SRCTOP)/include/socket-utils.h $(srcdir)/dk/dk.h \ - combine_keys.c etypes.h crypto_libinit.so crypto_libinit.po $(OUTPRE)crypto_libinit.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ @@ -445,8 +462,6 @@ crypto_libinit.so crypto_libinit.po $(OUTPRE)crypto_libinit.$(OBJEXT): \ $(SRCTOP)/include/socket-utils.h crypto_libinit.c default_state.so default_state.po $(OUTPRE)default_state.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ - $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ - $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h $(SRCTOP)/include/k5-err.h \ $(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \ $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \ $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \ diff --git a/src/lib/crypto/aead.c b/src/lib/crypto/aead.c new file mode 100644 index 0000000000..6d042e60da --- /dev/null +++ b/src/lib/crypto/aead.c @@ -0,0 +1,431 @@ +/* + * lib/crypto/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 "etypes.h" +#include "cksumtypes.h" +#include "dk.h" +#include "aead.h" + +krb5_crypto_iov * KRB5_CALLCONV +krb5int_c_locate_iov(krb5_crypto_iov *data, + size_t num_data, + krb5_cryptotype type) +{ + size_t i; + krb5_crypto_iov *iov = NULL; + + if (data == NULL) + return NULL; + + for (i = 0; i < num_data; i++) { + if (data[i].flags == type) { + if (iov == NULL) + iov = &data[i]; + else + return NULL; /* can't appear twice */ + } + } + + 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) +{ + krb5_data *sign_data; + size_t num_sign_data; + krb5_error_code ret; + size_t i, j; + + /* Create a checksum over all the data to be signed */ + for (i = 0, num_sign_data = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (SIGN_IOV(iov)) + num_sign_data++; + } + + /* XXX cleanup to avoid alloc */ + sign_data = (krb5_data *)calloc(num_sign_data, sizeof(krb5_data)); + if (sign_data == NULL) + return ENOMEM; + + for (i = 0, j = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (SIGN_IOV(iov)) + sign_data[j++] = iov->data; + } + + ret = hash_provider->hash(num_sign_data, sign_data, output); + + free(sign_data); + + return ret; +} + +krb5_error_code KRB5_CALLCONV +krb5int_c_make_checksum_iov(const struct krb5_cksumtypes *cksum_type, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_crypto_iov *data, + size_t num_data, + krb5_data *cksum_data) +{ + int e1, e2; + krb5_error_code ret; + + if (cksum_type->keyhash != NULL) { + /* check if key is compatible */ + + if (cksum_type->keyed_etype) { + for (e1=0; e1keyed_etype) + break; + + for (e2=0; e2enctype) + break; + + if ((e1 == krb5_enctypes_length) || + (e2 == krb5_enctypes_length) || + (krb5_enctypes_list[e1].enc != krb5_enctypes_list[e2].enc)) { + ret = KRB5_BAD_ENCTYPE; + goto cleanup; + } + } + + if (cksum_type->keyhash->hash_iov == NULL) { + return KRB5_BAD_ENCTYPE; + } + + ret = (*(cksum_type->keyhash->hash_iov))(key, usage, 0, + data, num_data, cksum_data); + } else if (cksum_type->flags & KRB5_CKSUMFLAG_DERIVE) { + ret = krb5_dk_make_checksum_iov(cksum_type->hash, + key, usage, data, num_data, + cksum_data); + } else { + ret = make_unkeyed_checksum_iov(cksum_type->hash, data, num_data, + cksum_data); + } + + if (ret == 0) { + if (cksum_type->trunc_size) { + cksum_data->length = cksum_type->trunc_size; + } + } + +cleanup: + if (ret != 0) { + memset(cksum_data->data, 0, cksum_data->length); + } + + return ret; +} + +const struct krb5_cksumtypes * KRB5_CALLCONV +krb5int_c_find_checksum_type(krb5_cksumtype cksumtype) +{ + size_t i; + + for (i = 0; i < krb5_cksumtypes_length; i++) { + if (krb5_cksumtypes_list[i].ctype == cksumtype) + break; + } + + if (i == krb5_cksumtypes_length) + return NULL; + + return &krb5_cksumtypes_list[i]; +} + +#ifdef DEBUG_IOV +static void +dump_block(const char *tag, + size_t i, + size_t j, + unsigned char *block, + size_t block_size) +{ + size_t k; + + printf("[%s: %d.%d] ", tag, i, j); + + for (k = 0; k < block_size; k++) + printf("%02x ", block[k] & 0xFF); + + printf("\n"); +} +#endif + +static int +process_block_p(const krb5_crypto_iov *data, + size_t num_data, + struct iov_block_state *iov_state, + size_t i) +{ + const krb5_crypto_iov *iov = &data[i]; + int process_block; + + switch (iov->flags) { + case KRB5_CRYPTO_TYPE_SIGN_ONLY: + process_block = iov_state->include_sign_only; + break; + case KRB5_CRYPTO_TYPE_PADDING: + process_block = (iov_state->pad_to_boundary == 0); + break; + case KRB5_CRYPTO_TYPE_HEADER: + process_block = (iov_state->ignore_header == 0); + break; + case KRB5_CRYPTO_TYPE_DATA: + process_block = 1; + break; + default: + process_block = 0; + break; + } + + return process_block; +} + +/* + * Returns TRUE if, having reached the end of the current buffer, + * we should pad the rest of the block with zeros. + */ +static int +pad_to_boundary_p(const krb5_crypto_iov *data, + size_t num_data, + struct iov_block_state *iov_state, + size_t i, + size_t j) +{ + /* If the pad_to_boundary flag is unset, return FALSE */ + if (iov_state->pad_to_boundary == 0) + return 0; + + /* If we haven't got any data, we need to get some */ + if (j == 0) + return 0; + + /* No boundary between adjacent buffers marked for processing */ + if (data[iov_state->iov_pos].flags == data[i].flags) + return 0; + + return 1; +} + +krb5_boolean KRB5_CALLCONV +krb5int_c_iov_get_block(unsigned char *block, + size_t block_size, + const krb5_crypto_iov *data, + size_t num_data, + struct iov_block_state *iov_state) +{ + size_t i, j = 0; + + for (i = iov_state->iov_pos; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + size_t nbytes; + + if (!process_block_p(data, num_data, iov_state, i)) + continue; + + if (pad_to_boundary_p(data, num_data, iov_state, i, j)) + break; + + iov_state->iov_pos = i; + + nbytes = iov->data.length - iov_state->data_pos; + if (nbytes > block_size - j) + nbytes = block_size - j; + + memcpy(block + j, iov->data.data + iov_state->data_pos, nbytes); + + iov_state->data_pos += nbytes; + j += nbytes; + + assert(j <= block_size); + + if (j == block_size) + break; + + assert(iov_state->data_pos == iov->data.length); + + iov_state->data_pos = 0; + } + + iov_state->iov_pos = i; + + if (j != block_size) + memset(block + j, 0, block_size - j); + +#ifdef DEBUG_IOV + if (iov_state->iov_pos < num_data) + dump_block("get_block", i, j, block, block_size); +#endif + + return (iov_state->iov_pos < num_data); +} + +krb5_boolean KRB5_CALLCONV +krb5int_c_iov_put_block(const krb5_crypto_iov *data, + size_t num_data, + unsigned char *block, + size_t block_size, + struct iov_block_state *iov_state) +{ + size_t i, j = 0; + + for (i = iov_state->iov_pos; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + size_t nbytes; + + if (!process_block_p(data, num_data, iov_state, i)) + continue; + + if (pad_to_boundary_p(data, num_data, iov_state, i, j)) + break; + + iov_state->iov_pos = i; + + nbytes = iov->data.length - iov_state->data_pos; + if (nbytes > block_size - j) + nbytes = block_size - j; + + memcpy(iov->data.data + iov_state->data_pos, block + j, nbytes); + + iov_state->data_pos += nbytes; + j += nbytes; + + assert(j <= block_size); + + if (j == block_size) + break; + + assert(iov_state->data_pos == iov->data.length); + + iov_state->data_pos = 0; + } + + iov_state->iov_pos = i; + +#ifdef DEBUG_IOV + if (iov_state->iov_pos < num_data) + dump_block("put_block", i, j, block, block_size); +#endif + + return (iov_state->iov_pos < num_data); +} + +krb5_error_code KRB5_CALLCONV +krb5int_c_iov_decrypt_stream(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *key, + krb5_keyusage keyusage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + size_t header_len, trailer_len, padding_len; + krb5_crypto_iov *iov; + krb5_crypto_iov *stream; + size_t i, j; + int got_data = 0; + + stream = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_STREAM); + assert(stream != NULL); + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_HEADER, &header_len); + if (ret != 0) + return ret; + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_TRAILER, &trailer_len); + if (ret != 0) + return ret; + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_PADDING, &padding_len); + if (ret != 0) + return ret; + + if (stream->data.length < header_len + trailer_len) + return KRB5_BAD_MSIZE; + + iov = (krb5_crypto_iov *)calloc(num_data + 2, sizeof(krb5_crypto_iov)); + if (iov == NULL) + return ENOMEM; + + i = 0; + + iov[i].flags = KRB5_CRYPTO_TYPE_HEADER; /* takes place of STREAM */ + iov[i].data.data = stream->data.data; + iov[i].data.length = header_len; + i++; + + for (j = 0; j < num_data; j++) { + if (data[j].flags == KRB5_CRYPTO_TYPE_DATA) { + if (got_data) { + free(iov); + return KRB5_BAD_MSIZE; + } + + got_data++; + + data[j].data.data = stream->data.data + header_len; + data[j].data.length = stream->data.length - header_len - trailer_len; + } + if (data[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY || + data[j].flags == KRB5_CRYPTO_TYPE_DATA) + iov[i++] = data[j]; + } + + /* XXX not self-describing with respect to length, this is the best we can do */ + iov[i].flags = KRB5_CRYPTO_TYPE_PADDING; + iov[i].data.data = NULL; + iov[i].data.length = 0; + i++; + + iov[i].flags = KRB5_CRYPTO_TYPE_TRAILER; + iov[i].data.data = stream->data.data + stream->data.length - trailer_len; + iov[i].data.length = trailer_len; + i++; + + assert(i <= num_data + 2); + + ret = aead->decrypt_iov(aead, enc, hash, key, keyusage, ivec, iov, i); + + free(iov); + + return ret; +} + diff --git a/src/lib/crypto/aead.h b/src/lib/crypto/aead.h new file mode 100644 index 0000000000..f183d1a05b --- /dev/null +++ b/src/lib/crypto/aead.h @@ -0,0 +1,94 @@ +/* + * lib/crypto/aead.h + * + * 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" + +/* AEAD helpers */ + +krb5_crypto_iov * KRB5_CALLCONV +krb5int_c_locate_iov(krb5_crypto_iov *data, + size_t num_data, + krb5_cryptotype type); + +krb5_error_code KRB5_CALLCONV +krb5int_c_make_checksum_iov(const struct krb5_cksumtypes *cksum, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_crypto_iov *data, + size_t num_data, + krb5_data *cksum_data); + +const struct krb5_cksumtypes * KRB5_CALLCONV +krb5int_c_find_checksum_type(krb5_cksumtype cksumtype); + +#define ENCRYPT_CONF_IOV(_iov) ((_iov)->flags == KRB5_CRYPTO_TYPE_HEADER) + +#define ENCRYPT_DATA_IOV(_iov) ((_iov)->flags == KRB5_CRYPTO_TYPE_DATA || \ + (_iov)->flags == KRB5_CRYPTO_TYPE_PADDING) + +#define ENCRYPT_IOV(_iov) (ENCRYPT_CONF_IOV(_iov) || ENCRYPT_DATA_IOV(_iov)) + +#define SIGN_IOV(_iov) (ENCRYPT_IOV(_iov) || \ + (_iov)->flags == KRB5_CRYPTO_TYPE_SIGN_ONLY ) + +struct iov_block_state { + size_t iov_pos; /* index into iov array */ + size_t data_pos; /* index into iov contents */ + unsigned int ignore_header : 1; /* have/should we process HEADER */ + unsigned int include_sign_only : 1; /* should we process SIGN_ONLY blocks */ + unsigned int pad_to_boundary : 1; /* should we zero fill blocks until next buffer */ +}; + +#define IOV_BLOCK_STATE_INIT(_state) ((_state)->iov_pos = \ + (_state)->data_pos = \ + (_state)->ignore_header = \ + (_state)->include_sign_only = \ + (_state)->pad_to_boundary = 0) + +krb5_boolean KRB5_CALLCONV +krb5int_c_iov_get_block(unsigned char *block, + size_t block_size, + const krb5_crypto_iov *data, + size_t num_data, + struct iov_block_state *iov_state); + +krb5_boolean KRB5_CALLCONV +krb5int_c_iov_put_block(const krb5_crypto_iov *data, + size_t num_data, + unsigned char *block, + size_t block_size, + struct iov_block_state *iov_state); + +krb5_error_code KRB5_CALLCONV +krb5int_c_iov_decrypt_stream(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *key, + krb5_keyusage keyusage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data); + diff --git a/src/lib/crypto/arcfour/Makefile.in b/src/lib/crypto/arcfour/Makefile.in index d03ca878a6..ff5b51dcfb 100644 --- a/src/lib/crypto/arcfour/Makefile.in +++ b/src/lib/crypto/arcfour/Makefile.in @@ -16,14 +16,17 @@ RUN_SETUP = @KRB5_RUN_ENV@ KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf STLIBOBJS=\ arcfour.o \ + arcfour_aead.o \ arcfour_s2k.o OBJS=\ $(OUTPRE)arcfour.$(OBJEXT) \ + $(OUTPRE)arcfour_aead.$(OBJEXT) \ $(OUTPRE)arcfour_s2k.$(OBJEXT) SRCS=\ $(srcdir)/arcfour.c \ + $(srcdir)/arcfour_aead.c\ $(srcdir)/arcfour_s2k.c ##DOS##LIBOBJS = $(OBJS) diff --git a/src/lib/crypto/arcfour/arcfour-int.h b/src/lib/crypto/arcfour/arcfour-int.h index 398fe57a1d..efd7a02829 100644 --- a/src/lib/crypto/arcfour/arcfour-int.h +++ b/src/lib/crypto/arcfour/arcfour-int.h @@ -27,5 +27,6 @@ typedef struct { krb5_keyusage krb5int_arcfour_translate_usage(krb5_keyusage usage); +extern const char *const krb5int_arcfour_l40; #endif /* ARCFOUR_INT_H */ diff --git a/src/lib/crypto/arcfour/arcfour.c b/src/lib/crypto/arcfour/arcfour.c index a2df5ddf59..8c9e8e1a4e 100644 --- a/src/lib/crypto/arcfour/arcfour.c +++ b/src/lib/crypto/arcfour/arcfour.c @@ -8,7 +8,7 @@ of RSA Data Security) */ #include "k5-int.h" #include "arcfour-int.h" -static const char *const l40 = "fortybits"; +const char *const krb5int_arcfour_l40 = "fortybits"; void krb5_arcfour_encrypt_length(const struct krb5_enc_provider *enc, @@ -139,7 +139,7 @@ krb5_arcfour_encrypt(const struct krb5_enc_provider *enc, /* begin the encryption, computer K1 */ ms_usage=krb5int_arcfour_translate_usage(usage); if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { - strncpy(salt.data, l40, salt.length); + strncpy(salt.data, krb5int_arcfour_l40, salt.length); store_32_le(ms_usage, salt.data+10); } else { salt.length=4; @@ -253,7 +253,7 @@ krb5_arcfour_decrypt(const struct krb5_enc_provider *enc, /* compute the salt */ ms_usage=krb5int_arcfour_translate_usage(usage); if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { - strncpy(salt.data, l40, salt.length); + strncpy(salt.data, krb5int_arcfour_l40, salt.length); salt.data[10]=ms_usage & 0xff; salt.data[11]=(ms_usage>>8) & 0xff; salt.data[12]=(ms_usage>>16) & 0xff; diff --git a/src/lib/crypto/arcfour/arcfour.h b/src/lib/crypto/arcfour/arcfour.h index c6e4353348..e8ff203ca1 100644 --- a/src/lib/crypto/arcfour/arcfour.h +++ b/src/lib/crypto/arcfour/arcfour.h @@ -33,4 +33,6 @@ extern krb5_error_code krb5int_arcfour_string_to_key( krb5_keyblock *); extern const struct krb5_enc_provider krb5int_enc_arcfour; +extern const struct krb5_aead_provider krb5int_aead_arcfour; + #endif /* ARCFOUR_H */ diff --git a/src/lib/crypto/arcfour/arcfour_aead.c b/src/lib/crypto/arcfour/arcfour_aead.c new file mode 100644 index 0000000000..40151d96be --- /dev/null +++ b/src/lib/crypto/arcfour/arcfour_aead.c @@ -0,0 +1,325 @@ +/* + * lib/crypto/arcfour/arcfour_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 "arcfour.h" +#include "arcfour-int.h" +#include "aead.h" + +/* AEAD */ + +static krb5_error_code +krb5int_arcfour_crypto_length(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + krb5_cryptotype type, + size_t *length) +{ + switch (type) { + case KRB5_CRYPTO_TYPE_HEADER: + *length = hash->hashsize + CONFOUNDERLENGTH; + break; + case KRB5_CRYPTO_TYPE_PADDING: + *length = 0; + 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_arcfour_crypto_length"); + break; + } + + return 0; +} + +static krb5_error_code +alloc_derived_key(const struct krb5_enc_provider *enc, + krb5_keyblock *dst, + krb5_data *data, + const krb5_keyblock *src) +{ + data->length = enc->keybytes; + data->data = malloc(data->length); + if (data->data == NULL) + return ENOMEM; + + *dst = *src; + dst->length = data->length; + dst->contents = (void *)data->data; + + return 0; +} + +static krb5_error_code +krb5int_arcfour_encrypt_iov(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *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_keyblock k1, k2, k3; + krb5_data d1, d2, d3; + krb5_data checksum, confounder, header_data; + krb5_keyusage ms_usage; + char salt_data[14]; + krb5_data salt; + size_t i; + + d1.length = d2.length = d3.length = 0; + d1.data = d2.data = d3.data = NULL; + + /* + * Caller must have provided space for the header, padding + * and trailer; per RFC 4757 we will arrange it as: + * + * Checksum | E(Confounder | Plaintext) + */ + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || + header->data.length < hash->hashsize + CONFOUNDERLENGTH) + return KRB5_BAD_MSIZE; + + header_data = header->data; + + /* Trailer may be absent */ + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer != NULL) + trailer->data.length = 0; + + /* Ensure that there is no padding */ + for (i = 0; i < num_data; i++) { + if (data[i].flags == KRB5_CRYPTO_TYPE_PADDING) + data[i].data.length = 0; + } + + ret = alloc_derived_key(enc, &k1, &d1, key); + if (ret != 0) + goto cleanup; + + ret = alloc_derived_key(enc, &k2, &d2, key); + if (ret != 0) + goto cleanup; + + ret = alloc_derived_key(enc, &k3, &d3, key); + if (ret != 0) + goto cleanup; + + /* Begin the encryption, compute K1 */ + salt.data = salt_data; + salt.length = sizeof(salt_data); + + ms_usage = krb5int_arcfour_translate_usage(usage); + + if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + strncpy(salt.data, krb5int_arcfour_l40, salt.length); + store_32_le(ms_usage, (unsigned char *)salt.data + 10); + } else { + salt.length = 4; + store_32_le(ms_usage, (unsigned char *)salt.data); + } + ret = krb5_hmac(hash, key, 1, &salt, &d1); + if (ret != 0) + goto cleanup; + + memcpy(k2.contents, k1.contents, k2.length); + + if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) + memset(k1.contents + 7, 0xAB, 9); + + header->data.length = hash->hashsize + CONFOUNDERLENGTH; + + confounder.data = header->data.data + hash->hashsize; + confounder.length = CONFOUNDERLENGTH; + + ret = krb5_c_random_make_octets(0, &confounder); + if (ret != 0) + goto cleanup; + + checksum.data = header->data.data; + checksum.length = hash->hashsize; + + /* Adjust pointers so confounder is at start of header */ + header->data.length -= hash->hashsize; + header->data.data += hash->hashsize; + + ret = krb5_hmac_iov(hash, &k2, data, num_data, &checksum); + if (ret != 0) + goto cleanup; + + ret = krb5_hmac(hash, &k1, 1, &checksum, &d3); + if (ret != 0) + goto cleanup; + + ret = enc->encrypt_iov(&k3, ivec, data, num_data); + if (ret != 0) + goto cleanup; + +cleanup: + header->data = header_data; /* restore header pointers */ + + if (d1.data != NULL) { + memset(d1.data, 0, d1.length); + free(d1.data); + } + if (d2.data != NULL) { + memset(d2.data, 0, d2.length); + free(d2.data); + } + if (d3.data != NULL) { + memset(d3.data, 0, d3.length); + free(d3.data); + } + + return ret; +} + +static krb5_error_code +krb5int_arcfour_decrypt_iov(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *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_keyblock k1, k2, k3; + krb5_data d1, d2, d3; + krb5_data checksum, header_data; + krb5_keyusage ms_usage; + char salt_data[14]; + krb5_data salt; + + d1.length = d2.length = d3.length = 0; + d1.data = d2.data = d3.data = NULL; + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || + header->data.length != hash->hashsize + CONFOUNDERLENGTH) + return KRB5_BAD_MSIZE; + + header_data = header->data; + + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer != NULL && trailer->data.length != 0) + return KRB5_BAD_MSIZE; + + ret = alloc_derived_key(enc, &k1, &d1, key); + if (ret != 0) + goto cleanup; + + ret = alloc_derived_key(enc, &k2, &d2, key); + if (ret != 0) + goto cleanup; + + ret = alloc_derived_key(enc, &k3, &d3, key); + if (ret != 0) + goto cleanup; + + /* Begin the decryption, compute K1 */ + salt.data = salt_data; + salt.length = sizeof(salt_data); + + ms_usage = krb5int_arcfour_translate_usage(usage); + + if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) { + strncpy(salt.data, krb5int_arcfour_l40, salt.length); + store_32_le(ms_usage, (unsigned char *)salt.data + 10); + } else { + salt.length = 4; + store_32_le(ms_usage, (unsigned char *)salt.data); + } + ret = krb5_hmac(hash, key, 1, &salt, &d1); + if (ret != 0) + goto cleanup; + + memcpy(k2.contents, k1.contents, k2.length); + + if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) + memset(k1.contents + 7, 0xAB, 9); + + checksum.data = header->data.data; + checksum.length = hash->hashsize; + + /* Adjust pointers so confounder is at start of header */ + header->data.length -= hash->hashsize; + header->data.data += hash->hashsize; + + ret = krb5_hmac(hash, &k1, 1, &checksum, &d3); + if (ret != 0) + goto cleanup; + + ret = enc->decrypt_iov(&k3, ivec, data, num_data); + if (ret != 0) + goto cleanup; + + ret = krb5_hmac_iov(hash, &k2, data, num_data, &d1); + if (ret != 0) + goto cleanup; + + if (memcmp(checksum.data, d1.data, hash->hashsize) != 0) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + goto cleanup; + } + +cleanup: + header->data = header_data; /* restore header pointers */ + + if (d1.data != NULL) { + memset(d1.data, 0, d1.length); + free(d1.data); + } + if (d2.data != NULL) { + memset(d2.data, 0, d2.length); + free(d2.data); + } + if (d3.data != NULL) { + memset(d3.data, 0, d3.length); + free(d3.data); + } + + return ret; +} + +const struct krb5_aead_provider krb5int_aead_arcfour = { + krb5int_arcfour_crypto_length, + krb5int_arcfour_encrypt_iov, + krb5int_arcfour_decrypt_iov +}; + diff --git a/src/lib/crypto/crypto_length.c b/src/lib/crypto/crypto_length.c new file mode 100644 index 0000000000..4a64b90f91 --- /dev/null +++ b/src/lib/crypto/crypto_length.c @@ -0,0 +1,192 @@ +/* + * lib/crypto/crypto_length.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 "etypes.h" +#include "aead.h" + +krb5_error_code KRB5_CALLCONV +krb5_c_crypto_length(krb5_context context, + krb5_enctype enctype, + krb5_cryptotype type, + unsigned int *size) +{ + int i; + const struct krb5_keytypes *ktp = NULL; + krb5_error_code ret; + + for (i = 0; i < krb5_enctypes_length; i++) { + if (krb5_enctypes_list[i].etype == enctype) { + ktp = &krb5_enctypes_list[i]; + break; + } + } + + if (ktp == NULL || ktp->aead == NULL) { + return KRB5_BAD_ENCTYPE; + } + + switch (type) { + case KRB5_CRYPTO_TYPE_EMPTY: + case KRB5_CRYPTO_TYPE_SIGN_ONLY: + *size = 0; + ret = 0; + break; + case KRB5_CRYPTO_TYPE_DATA: + *size = (size_t)~0; /* match Heimdal */ + ret = 0; + break; + case KRB5_CRYPTO_TYPE_HEADER: + case KRB5_CRYPTO_TYPE_PADDING: + case KRB5_CRYPTO_TYPE_TRAILER: + case KRB5_CRYPTO_TYPE_CHECKSUM: + ret = ktp->aead->crypto_length(ktp->aead, ktp->enc, ktp->hash, type, size); + break; + default: + ret = EINVAL; + break; + } + + return ret; +} + +static krb5_error_code +k5_padding_length(const struct krb5_keytypes *ktp, + size_t data_length, + unsigned int *pad_length) +{ + unsigned int padding; + krb5_error_code ret; + + ret = ktp->aead->crypto_length(ktp->aead, ktp->enc, ktp->hash, + KRB5_CRYPTO_TYPE_PADDING, &padding); + if (ret != 0) + return ret; + + if (padding == 0 || (data_length % padding) == 0) + *pad_length = 0; + else + *pad_length = padding - (data_length % padding); + + return 0; +} + + +krb5_error_code KRB5_CALLCONV +krb5_c_padding_length(krb5_context context, + krb5_enctype enctype, + size_t data_length, + unsigned int *pad_length) +{ + int i; + const struct krb5_keytypes *ktp = NULL; + + for (i = 0; i < krb5_enctypes_length; i++) { + if (krb5_enctypes_list[i].etype == enctype) { + ktp = &krb5_enctypes_list[i]; + break; + } + } + + if (ktp == NULL || ktp->aead == NULL) { + return KRB5_BAD_ENCTYPE; + } + + return k5_padding_length(ktp, data_length, pad_length); +} + +krb5_error_code KRB5_CALLCONV +krb5_c_crypto_length_iov(krb5_context context, + krb5_enctype enctype, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret = 0; + size_t i; + const struct krb5_keytypes *ktp = NULL; + size_t data_length = 0, pad_length; + krb5_crypto_iov *padding = NULL; + + /* + * XXX need to rejig internal interface so we can accurately + * report variable header lengths + */ + + for (i = 0; i < krb5_enctypes_length; i++) { + if (krb5_enctypes_list[i].etype == enctype) { + ktp = &krb5_enctypes_list[i]; + break; + } + } + + if (ktp == NULL || ktp->aead == NULL) { + return KRB5_BAD_ENCTYPE; + } + + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + switch (iov->flags) { + case KRB5_CRYPTO_TYPE_DATA: + data_length += iov->data.length; + break; + case KRB5_CRYPTO_TYPE_PADDING: + if (padding != NULL) + return EINVAL; + + padding = iov; + break; + case KRB5_CRYPTO_TYPE_HEADER: + case KRB5_CRYPTO_TYPE_TRAILER: + case KRB5_CRYPTO_TYPE_CHECKSUM: + ret = ktp->aead->crypto_length(ktp->aead, ktp->enc, ktp->hash, iov->flags, &iov->data.length); + break; + case KRB5_CRYPTO_TYPE_EMPTY: + case KRB5_CRYPTO_TYPE_SIGN_ONLY: + default: + break; + } + + if (ret != 0) + break; + } + + if (ret != 0) + return ret; + + ret = k5_padding_length(ktp, data_length, &pad_length); + if (ret != 0) + return ret; + + if (pad_length != 0 && padding == NULL) + return EINVAL; + + if (padding != NULL) + padding->data.length = pad_length; + + return 0; +} + diff --git a/src/lib/crypto/decrypt_iov.c b/src/lib/crypto/decrypt_iov.c new file mode 100644 index 0000000000..1a98b06570 --- /dev/null +++ b/src/lib/crypto/decrypt_iov.c @@ -0,0 +1,61 @@ +/* + * lib/crypto/encrypt_iov.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 "etypes.h" +#include "aead.h" + +krb5_error_code KRB5_CALLCONV +krb5_c_decrypt_iov(krb5_context context, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_data *cipher_state, + krb5_crypto_iov *data, + size_t num_data) +{ + int i; + const struct krb5_keytypes *ktp = NULL; + + for (i = 0; i < krb5_enctypes_length; i++) { + if (krb5_enctypes_list[i].etype == key->enctype) { + ktp = &krb5_enctypes_list[i]; + break; + } + } + + if (ktp == NULL || ktp->aead == NULL) { + return KRB5_BAD_ENCTYPE; + } + + if (krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_STREAM) != NULL) { + return krb5int_c_iov_decrypt_stream(ktp->aead, ktp->enc, ktp->hash, + key, usage, cipher_state, data, num_data); + } + + return ktp->aead->decrypt_iov(ktp->aead, ktp->enc, ktp->hash, + key, usage, cipher_state, data, num_data); +} + diff --git a/src/lib/crypto/des/Makefile.in b/src/lib/crypto/des/Makefile.in index 10660bbf40..6c5fc89ae1 100644 --- a/src/lib/crypto/des/Makefile.in +++ b/src/lib/crypto/des/Makefile.in @@ -16,6 +16,7 @@ RUN_SETUP = @KRB5_RUN_ENV@ KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf STLIBOBJS=\ afsstring2key.o \ d3_cbc.o \ + d3_aead.o \ d3_kysched.o \ f_cbc.o \ f_cksum.o \ @@ -28,6 +29,7 @@ STLIBOBJS=\ OBJS= $(OUTPRE)afsstring2key.$(OBJEXT) \ $(OUTPRE)d3_cbc.$(OBJEXT) \ + $(OUTPRE)d3_aead.$(OBJEXT) \ $(OUTPRE)d3_kysched.$(OBJEXT) \ $(OUTPRE)f_cbc.$(OBJEXT) \ $(OUTPRE)f_cksum.$(OBJEXT) \ @@ -40,6 +42,7 @@ OBJS= $(OUTPRE)afsstring2key.$(OBJEXT) \ SRCS= $(srcdir)/afsstring2key.c \ $(srcdir)/d3_cbc.c \ + $(srcdir)/d3_aead.c \ $(srcdir)/d3_kysched.c \ $(srcdir)/f_cbc.c \ $(srcdir)/f_cksum.c \ diff --git a/src/lib/crypto/des/d3_aead.c b/src/lib/crypto/des/d3_aead.c new file mode 100644 index 0000000000..8463fc8837 --- /dev/null +++ b/src/lib/crypto/des/d3_aead.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2008 by the Massachusetts Institute of Technology. + * Copyright 1995 by Richard P. Basch. All Rights Reserved. + * Copyright 1995 by Lehman Brothers, Inc. 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 Richard P. Basch, Lehman Brothers and M.I.T. not be used + * in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Richard P. Basch, + * Lehman Brothers and M.I.T. make no representations about the suitability + * of this software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +#include "des_int.h" +#include "f_tables.h" +#include "../aead.h" + +void +krb5int_des3_cbc_encrypt_iov(krb5_crypto_iov *data, + unsigned long num_data, + const mit_des_key_schedule ks1, + const mit_des_key_schedule ks2, + const mit_des_key_schedule ks3, + mit_des_cblock ivec) +{ + unsigned DES_INT32 left, right; + const unsigned DES_INT32 *kp1, *kp2, *kp3; + const unsigned char *ip; + unsigned char *op; + struct iov_block_state input_pos, output_pos; + unsigned char iblock[MIT_DES_BLOCK_LENGTH]; + unsigned char oblock[MIT_DES_BLOCK_LENGTH]; + + IOV_BLOCK_STATE_INIT(&input_pos); + IOV_BLOCK_STATE_INIT(&output_pos); + + /* + * Get key pointer here. This won't need to be reinitialized + */ + kp1 = (const unsigned DES_INT32 *)ks1; + kp2 = (const unsigned DES_INT32 *)ks2; + kp3 = (const unsigned DES_INT32 *)ks3; + + /* + * Initialize left and right with the contents of the initial + * vector. + */ + if (ivec != NULL) + ip = ivec; + else + ip = mit_des_zeroblock; + GET_HALF_BLOCK(left, ip); + GET_HALF_BLOCK(right, ip); + + /* + * Suitably initialized, now work the length down 8 bytes + * at a time. + */ + for (;;) { + unsigned DES_INT32 temp; + + ip = iblock; + op = oblock; + + if (!krb5int_c_iov_get_block(iblock, MIT_DES_BLOCK_LENGTH, data, num_data, &input_pos)) + break; + + GET_HALF_BLOCK(temp, ip); + left ^= temp; + GET_HALF_BLOCK(temp, ip); + right ^= temp; + + /* + * Encrypt what we have + */ + DES_DO_ENCRYPT(left, right, kp1); + DES_DO_DECRYPT(left, right, kp2); + DES_DO_ENCRYPT(left, right, kp3); + + /* + * Copy the results out + */ + PUT_HALF_BLOCK(left, op); + PUT_HALF_BLOCK(right, op); + + krb5int_c_iov_put_block(data, num_data, oblock, MIT_DES_BLOCK_LENGTH, &output_pos); + } + + if (ivec != NULL) + memcpy(ivec, oblock, MIT_DES_BLOCK_LENGTH); +} + +void +krb5int_des3_cbc_decrypt_iov(krb5_crypto_iov *data, + unsigned long num_data, + const mit_des_key_schedule ks1, + const mit_des_key_schedule ks2, + const mit_des_key_schedule ks3, + mit_des_cblock ivec) +{ + unsigned DES_INT32 left, right; + const unsigned DES_INT32 *kp1, *kp2, *kp3; + const unsigned char *ip; + unsigned DES_INT32 ocipherl, ocipherr; + unsigned DES_INT32 cipherl, cipherr; + unsigned char *op; + struct iov_block_state input_pos, output_pos; + unsigned char iblock[MIT_DES_BLOCK_LENGTH]; + unsigned char oblock[MIT_DES_BLOCK_LENGTH]; + + IOV_BLOCK_STATE_INIT(&input_pos); + IOV_BLOCK_STATE_INIT(&output_pos); + + /* + * Get key pointer here. This won't need to be reinitialized + */ + kp1 = (const unsigned DES_INT32 *)ks1; + kp2 = (const unsigned DES_INT32 *)ks2; + kp3 = (const unsigned DES_INT32 *)ks3; + + /* + * Decrypting is harder than encrypting because of + * the necessity of remembering a lot more things. + * Should think about this a little more... + */ + + if (num_data == 0) + return; + + /* + * Prime the old cipher with ivec. + */ + if (ivec != NULL) + ip = ivec; + else + ip = mit_des_zeroblock; + GET_HALF_BLOCK(ocipherl, ip); + GET_HALF_BLOCK(ocipherr, ip); + + /* + * Now do this in earnest until we run out of length. + */ + for (;;) { + /* + * Read a block from the input into left and + * right. Save this cipher block for later. + */ + + if (!krb5int_c_iov_get_block(iblock, MIT_DES_BLOCK_LENGTH, data, num_data, &input_pos)) + break; + + ip = iblock; + op = oblock; + + GET_HALF_BLOCK(left, ip); + GET_HALF_BLOCK(right, ip); + cipherl = left; + cipherr = right; + + /* + * Decrypt this. + */ + DES_DO_DECRYPT(left, right, kp3); + DES_DO_ENCRYPT(left, right, kp2); + DES_DO_DECRYPT(left, right, kp1); + + /* + * Xor with the old cipher to get plain + * text. Output 8 or less bytes of this. + */ + left ^= ocipherl; + right ^= ocipherr; + + PUT_HALF_BLOCK(left, op); + PUT_HALF_BLOCK(right, op); + + /* + * Save current cipher block here + */ + ocipherl = cipherl; + ocipherr = cipherr; + + krb5int_c_iov_put_block(data, num_data, oblock, MIT_DES_BLOCK_LENGTH, &output_pos); + } + + if (ivec != NULL) + memcpy(ivec, oblock, MIT_DES_BLOCK_LENGTH); +} diff --git a/src/lib/crypto/des/des_int.h b/src/lib/crypto/des/des_int.h index c330a935ad..f040564499 100644 --- a/src/lib/crypto/des/des_int.h +++ b/src/lib/crypto/des/des_int.h @@ -240,6 +240,21 @@ krb5int_des3_cbc_decrypt(const mit_des_cblock *in, const mit_des_key_schedule ks3, const mit_des_cblock ivec); +void +krb5int_des3_cbc_encrypt_iov(krb5_crypto_iov *data, + unsigned long num_data, + const mit_des_key_schedule ks1, + const mit_des_key_schedule ks2, + const mit_des_key_schedule ks3, + mit_des_cblock ivec); + +void +krb5int_des3_cbc_decrypt_iov(krb5_crypto_iov *data, + unsigned long num_data, + const mit_des_key_schedule ks1, + const mit_des_key_schedule ks2, + const mit_des_key_schedule ks3, + mit_des_cblock ivec); #define mit_des3_cbc_encrypt(in,out,length,ks1,ks2,ks3,ivec,enc) \ ((enc ? krb5int_des3_cbc_encrypt : krb5int_des3_cbc_decrypt) \ diff --git a/src/lib/crypto/dk/Makefile.in b/src/lib/crypto/dk/Makefile.in index 08393ba4a5..f8bfef139a 100644 --- a/src/lib/crypto/dk/Makefile.in +++ b/src/lib/crypto/dk/Makefile.in @@ -16,6 +16,7 @@ RUN_SETUP = @KRB5_RUN_ENV@ KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf STLIBOBJS=\ checksum.o \ + dk_aead.o \ dk_decrypt.o \ dk_encrypt.o \ derive.o \ @@ -24,6 +25,7 @@ STLIBOBJS=\ OBJS=\ $(OUTPRE)checksum.$(OBJEXT) \ + $(OUTPRE)dk_aead.$(OBJEXT) \ $(OUTPRE)dk_decrypt.$(OBJEXT) \ $(OUTPRE)dk_encrypt.$(OBJEXT) \ $(OUTPRE)derive.$(OBJEXT) \ @@ -32,6 +34,7 @@ OBJS=\ SRCS=\ $(srcdir)/checksum.c \ + $(srcdir)/dk_aead.c \ $(srcdir)/dk_decrypt.c \ $(srcdir)/dk_encrypt.c \ $(srcdir)/dk_prf.c \ diff --git a/src/lib/crypto/dk/checksum.c b/src/lib/crypto/dk/checksum.c index 2f30cb740d..a0ec05c5d5 100644 --- a/src/lib/crypto/dk/checksum.c +++ b/src/lib/crypto/dk/checksum.c @@ -27,6 +27,7 @@ #include "k5-int.h" #include "etypes.h" #include "dk.h" +#include "aead.h" #define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */ @@ -101,3 +102,73 @@ cleanup: return(ret); } +krb5_error_code +krb5_dk_make_checksum_iov(const struct krb5_hash_provider *hash, + const krb5_keyblock *key, krb5_keyusage usage, + const krb5_crypto_iov *data, size_t num_data, + krb5_data *output) +{ + int i; + const struct krb5_enc_provider *enc; + size_t blocksize, keybytes, keylength; + krb5_error_code ret; + unsigned char constantdata[K5CLENGTH]; + krb5_data datain; + unsigned char *kcdata; + krb5_keyblock kc; + + for (i=0; ienctype) + break; + } + + if (i == krb5_enctypes_length) + return(KRB5_BAD_ENCTYPE); + + enc = krb5_enctypes_list[i].enc; + + /* allocate and set to-be-derived keys */ + + blocksize = enc->block_size; + keybytes = enc->keybytes; + keylength = enc->keylength; + + /* key->length will be tested in enc->encrypt + output->length will be tested in krb5_hmac */ + + if ((kcdata = (unsigned char *) malloc(keylength)) == NULL) + return(ENOMEM); + + kc.contents = kcdata; + kc.length = keylength; + + /* derive the key */ + + datain.data = (char *) constantdata; + datain.length = K5CLENGTH; + + datain.data[0] = (usage>>24)&0xff; + datain.data[1] = (usage>>16)&0xff; + datain.data[2] = (usage>>8)&0xff; + datain.data[3] = usage&0xff; + + datain.data[4] = (char) 0x99; + + if ((ret = krb5_derive_key(enc, key, &kc, &datain)) != 0) + goto cleanup; + + /* hash the data */ + + if ((ret = krb5_hmac_iov(hash, &kc, data, num_data, output)) != 0) + memset(output->data, 0, output->length); + + /* ret is set correctly by the prior call */ + +cleanup: + memset(kcdata, 0, keylength); + + free(kcdata); + + return(ret); +} + diff --git a/src/lib/crypto/dk/dk.h b/src/lib/crypto/dk/dk.h index 47bda6ebf2..a8def7a93d 100644 --- a/src/lib/crypto/dk/dk.h +++ b/src/lib/crypto/dk/dk.h @@ -84,7 +84,19 @@ krb5_error_code krb5_dk_make_checksum const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *input, krb5_data *output); +krb5_error_code +krb5_dk_make_checksum_iov(const struct krb5_hash_provider *hash, + const krb5_keyblock *key, krb5_keyusage usage, + const krb5_crypto_iov *data, size_t num_data, + krb5_data *output); + krb5_error_code krb5_derive_random(const struct krb5_enc_provider *enc, const krb5_keyblock *inkey, krb5_data *outrnd, const krb5_data *in_constant); + +/* AEAD */ + +extern const struct krb5_aead_provider krb5int_aead_dk; +extern const struct krb5_aead_provider krb5int_aead_aes; + diff --git a/src/lib/crypto/dk/dk_aead.c b/src/lib/crypto/dk/dk_aead.c new file mode 100644 index 0000000000..839b13344e --- /dev/null +++ b/src/lib/crypto/dk/dk_aead.c @@ -0,0 +1,387 @@ +/* + * lib/crypto/dk/dk_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 "dk.h" +#include "aead.h" + +#define K5CLENGTH 5 /* 32 bit net byte order integer + one byte seed */ + +/* AEAD */ + +static krb5_error_code +krb5int_dk_crypto_length(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + krb5_cryptotype type, + size_t *length) +{ + switch (type) { + case KRB5_CRYPTO_TYPE_HEADER: + case KRB5_CRYPTO_TYPE_PADDING: + *length = enc->block_size; + break; + case KRB5_CRYPTO_TYPE_TRAILER: + case KRB5_CRYPTO_TYPE_CHECKSUM: + *length = hash->hashsize; + break; + default: + assert(0 && "invalid cryptotype passed to krb5int_dk_crypto_length"); + break; + } + + return 0; +} + +static krb5_error_code +krb5int_dk_encrypt_iov(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + unsigned char constantdata[K5CLENGTH]; + krb5_data d1, d2; + krb5_crypto_iov *header, *trailer, *padding; + krb5_keyblock ke, ki; + size_t i; + size_t blocksize = 0; + size_t plainlen = 0; + size_t hmacsize = 0; + size_t padsize = 0; + unsigned char *cksum = NULL; + + ke.contents = ki.contents = NULL; + ke.length = ki.length = 0; + + /* E(Confounder | Plaintext | Pad) | Checksum */ + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_PADDING, &blocksize); + if (ret != 0) + return ret; + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_TRAILER, &hmacsize); + if (ret != 0) + return ret; + + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + if (iov->flags == KRB5_CRYPTO_TYPE_DATA) + plainlen += iov->data.length; + } + + /* Validate header and trailer lengths. */ + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || header->data.length < enc->block_size) + return KRB5_BAD_MSIZE; + + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer == NULL || trailer->data.length < hmacsize) + return KRB5_BAD_MSIZE; + + if (blocksize != 0) { + /* Check that the input data is correctly padded */ + if (plainlen % blocksize) + padsize = blocksize - (plainlen % blocksize); + } + + padding = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_PADDING); + if (padsize && (padding == NULL || padding->data.length < padsize)) + return KRB5_BAD_MSIZE; + + if (padding != NULL) { + memset(padding->data.data, 0, padsize); + padding->data.length = padsize; + } + + ke.length = enc->keylength; + ke.contents = malloc(ke.length); + if (ke.contents == NULL) { + ret = ENOMEM; + goto cleanup; + } + ki.length = enc->keylength; + ki.contents = malloc(ki.length); + if (ki.contents == NULL) { + ret = ENOMEM; + goto cleanup; + } + cksum = (unsigned char *)malloc(hash->hashsize); + if (cksum == NULL) { + ret = ENOMEM; + goto cleanup; + } + + /* derive the keys */ + + d1.data = (char *)constantdata; + d1.length = K5CLENGTH; + + d1.data[0] = (usage >> 24) & 0xFF; + d1.data[1] = (usage >> 16) & 0xFF; + d1.data[2] = (usage >> 8 ) & 0xFF; + d1.data[3] = (usage ) & 0xFF; + + d1.data[4] = 0xAA; + + ret = krb5_derive_key(enc, key, &ke, &d1); + if (ret != 0) + goto cleanup; + + d1.data[4] = 0x55; + + ret = krb5_derive_key(enc, key, &ki, &d1); + if (ret != 0) + goto cleanup; + + /* generate confounder */ + + header->data.length = enc->block_size; + + ret = krb5_c_random_make_octets(/* XXX */ NULL, &header->data); + if (ret != 0) + goto cleanup; + + /* hash the plaintext */ + d2.length = hash->hashsize; + d2.data = (char *)cksum; + + ret = krb5_hmac_iov(hash, &ki, data, num_data, &d2); + if (ret != 0) + goto cleanup; + + /* encrypt the plaintext (header | data | padding) */ + assert(enc->encrypt_iov != NULL); + + ret = enc->encrypt_iov(&ke, ivec, data, num_data); /* will update ivec */ + if (ret != 0) + goto cleanup; + + /* possibly truncate the hash */ + assert(hmacsize <= d2.length); + + memcpy(trailer->data.data, cksum, hmacsize); + trailer->data.length = hmacsize; + +cleanup: + if (ke.contents != NULL) { + memset(ke.contents, 0, ke.length); + free(ke.contents); + } + if (ki.contents != NULL) { + memset(ki.contents, 0, ki.length); + free(ki.contents); + } + if (cksum != NULL) { + free(cksum); + } + + return ret; +} + +static krb5_error_code +krb5int_dk_decrypt_iov(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + unsigned char constantdata[K5CLENGTH]; + krb5_data d1; + krb5_crypto_iov *header, *trailer; + krb5_keyblock ke, ki; + size_t i; + size_t blocksize = 0; /* careful, this is enc block size not confounder len */ + size_t cipherlen = 0; + size_t hmacsize = 0; + unsigned char *cksum = NULL; + + ke.contents = ki.contents = NULL; + ke.length = ki.length = 0; + + /* E(Confounder | Plaintext | Pad) | Checksum */ + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_PADDING, &blocksize); + if (ret != 0) + return ret; + + ret = aead->crypto_length(aead, enc, hash, KRB5_CRYPTO_TYPE_TRAILER, &hmacsize); + if (ret != 0) + return ret; + + for (i = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_DATA_IOV(iov)) + cipherlen += iov->data.length; + } + + if (blocksize == 0) { + /* Check for correct input length in CTS mode */ + if (enc->block_size != 0 && cipherlen < enc->block_size) + return KRB5_BAD_MSIZE; + } else { + /* Check that the input data is correctly padded */ + if ((cipherlen % blocksize) != 0) + return KRB5_BAD_MSIZE; + } + + /* Validate header and trailer lengths */ + + header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (header == NULL || header->data.length != enc->block_size) + return KRB5_BAD_MSIZE; + + trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (trailer == NULL || trailer->data.length != hmacsize) + return KRB5_BAD_MSIZE; + + ke.length = enc->keylength; + ke.contents = malloc(ke.length); + if (ke.contents == NULL) { + ret = ENOMEM; + goto cleanup; + } + ki.length = enc->keylength; + ki.contents = malloc(ki.length); + if (ki.contents == NULL) { + ret = ENOMEM; + goto cleanup; + } + cksum = (unsigned char *)malloc(hash->hashsize); + if (cksum == NULL) { + ret = ENOMEM; + goto cleanup; + } + + /* derive the keys */ + + d1.data = (char *)constantdata; + d1.length = K5CLENGTH; + + d1.data[0] = (usage >> 24) & 0xFF; + d1.data[1] = (usage >> 16) & 0xFF; + d1.data[2] = (usage >> 8 ) & 0xFF; + d1.data[3] = (usage ) & 0xFF; + + d1.data[4] = 0xAA; + + ret = krb5_derive_key(enc, key, &ke, &d1); + if (ret != 0) + goto cleanup; + + d1.data[4] = 0x55; + + ret = krb5_derive_key(enc, key, &ki, &d1); + if (ret != 0) + goto cleanup; + + /* decrypt the plaintext (header | data | padding) */ + assert(enc->decrypt_iov != NULL); + + ret = enc->decrypt_iov(&ke, ivec, data, num_data); /* will update ivec */ + if (ret != 0) + goto cleanup; + + /* verify the hash */ + d1.length = hash->hashsize; /* non-truncated length */ + d1.data = (char *)cksum; + + ret = krb5_hmac_iov(hash, &ki, data, num_data, &d1); + if (ret != 0) + goto cleanup; + + /* compare only the possibly truncated length */ + if (memcmp(cksum, trailer->data.data, hmacsize) != 0) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + goto cleanup; + } + +cleanup: + if (ke.contents != NULL) { + memset(ke.contents, 0, ke.length); + free(ke.contents); + } + if (ki.contents != NULL) { + memset(ki.contents, 0, ki.length); + free(ki.contents); + } + if (cksum != NULL) { + free(cksum); + } + + return ret; +} + +const struct krb5_aead_provider krb5int_aead_dk = { + krb5int_dk_crypto_length, + krb5int_dk_encrypt_iov, + krb5int_dk_decrypt_iov +}; + +static krb5_error_code +krb5int_aes_crypto_length(const struct krb5_aead_provider *aead, + const struct krb5_enc_provider *enc, + const struct krb5_hash_provider *hash, + krb5_cryptotype type, + size_t *length) +{ + switch (type) { + case KRB5_CRYPTO_TYPE_HEADER: + *length = enc->block_size; + break; + case KRB5_CRYPTO_TYPE_PADDING: + *length = 0; + break; + case KRB5_CRYPTO_TYPE_TRAILER: + case KRB5_CRYPTO_TYPE_CHECKSUM: + *length = 96 / 8; + break; + default: + assert(0 && "invalid cryptotype passed to krb5int_aes_crypto_length"); + break; + } + + return 0; +} + +const struct krb5_aead_provider krb5int_aead_aes = { + krb5int_aes_crypto_length, + krb5int_dk_encrypt_iov, + krb5int_dk_decrypt_iov +}; + diff --git a/src/lib/crypto/enc_provider/aes.c b/src/lib/crypto/enc_provider/aes.c index fde1a81f0e..e025cc3ebc 100644 --- a/src/lib/crypto/enc_provider/aes.c +++ b/src/lib/crypto/enc_provider/aes.c @@ -1,7 +1,7 @@ /* - * lib/crypto/enc_provider/aes.h + * lib/crypto/enc_provider/aes.c * - * Copyright (C) 2003, 2007 by the Massachusetts Institute of Technology. + * Copyright (C) 2003, 2007, 2008 by the Massachusetts Institute of Technology. * All rights reserved. * * Export of this software from the United States of America may @@ -27,6 +27,7 @@ #include "k5-int.h" #include "enc_provider.h" #include "aes.h" +#include "../aead.h" #if 0 aes_rval aes_blk_len(unsigned int blen, aes_ctx cx[1]); @@ -197,6 +198,169 @@ krb5int_aes_decrypt(const krb5_keyblock *key, const krb5_data *ivec, return 0; } +static krb5_error_code +krb5int_aes_encrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + aes_ctx ctx; + char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE]; + int nblocks = 0, blockno; + size_t input_length, i; + + if (aes_enc_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + if (ivec != NULL) + memcpy(tmp, ivec->data, BLOCK_SIZE); + else + memset(tmp, 0, BLOCK_SIZE); + + for (i = 0, input_length = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + input_length += iov->data.length; + } + + nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE; + + assert(nblocks > 1); + + { + char blockN2[BLOCK_SIZE]; /* second last */ + char blockN1[BLOCK_SIZE]; /* last block */ + struct iov_block_state input_pos, output_pos; + + IOV_BLOCK_STATE_INIT(&input_pos); + IOV_BLOCK_STATE_INIT(&output_pos); + + for (blockno = 0; blockno < nblocks - 2; blockno++) { + char blockN[BLOCK_SIZE]; + + krb5int_c_iov_get_block((unsigned char *)blockN, BLOCK_SIZE, data, num_data, &input_pos); + xorblock(tmp, blockN); + enc(tmp2, tmp, &ctx); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2, BLOCK_SIZE, &output_pos); + + /* Set up for next block. */ + memcpy(tmp, tmp2, BLOCK_SIZE); + } + + /* Do final CTS step for last two blocks (the second of which + may or may not be incomplete). */ + + /* First, get the last two blocks */ + memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */ + krb5int_c_iov_get_block((unsigned char *)blockN2, BLOCK_SIZE, data, num_data, &input_pos); + krb5int_c_iov_get_block((unsigned char *)blockN1, BLOCK_SIZE, data, num_data, &input_pos); + + /* Encrypt second last block */ + xorblock(tmp, blockN2); + enc(tmp2, tmp, &ctx); + memcpy(blockN2, tmp2, BLOCK_SIZE); /* blockN2 now contains first block */ + memcpy(tmp, tmp2, BLOCK_SIZE); + + /* Encrypt last block */ + xorblock(tmp, blockN1); + enc(tmp2, tmp, &ctx); + memcpy(blockN1, tmp2, BLOCK_SIZE); + if (ivec != NULL) + memcpy(ivec->data, tmp2, BLOCK_SIZE); + + /* Put the last two blocks back into the ivec (reverse order) */ + krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN1, BLOCK_SIZE, &output_pos); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN2, BLOCK_SIZE, &output_pos); + } + + return 0; +} + +static krb5_error_code +krb5int_aes_decrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + aes_ctx ctx; + char tmp[BLOCK_SIZE], tmp2[BLOCK_SIZE], tmp3[BLOCK_SIZE]; + int nblocks = 0, blockno; + size_t input_length, i; + + CHECK_SIZES; + + if (aes_dec_key(key->contents, key->length, &ctx) != aes_good) + abort(); + + if (ivec != NULL) + memcpy(tmp, ivec->data, BLOCK_SIZE); + else + memset(tmp, 0, BLOCK_SIZE); + + for (i = 0, input_length = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + input_length += iov->data.length; + } + + nblocks = (input_length + BLOCK_SIZE - 1) / BLOCK_SIZE; + + assert(nblocks > 1); + + { + char blockN2[BLOCK_SIZE]; /* second last */ + char blockN1[BLOCK_SIZE]; /* last block */ + struct iov_block_state input_pos, output_pos; + + IOV_BLOCK_STATE_INIT(&input_pos); + IOV_BLOCK_STATE_INIT(&output_pos); + + for (blockno = 0; blockno < nblocks - 2; blockno++) { + char blockN[BLOCK_SIZE]; + + krb5int_c_iov_get_block((unsigned char *)blockN, BLOCK_SIZE, data, num_data, &input_pos); + dec(tmp2, blockN, &ctx); + xorblock(tmp2, tmp); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)tmp2, BLOCK_SIZE, &output_pos); + memcpy(tmp, blockN, BLOCK_SIZE); + } + + /* Do last two blocks, the second of which (next-to-last block + of plaintext) may be incomplete. */ + + /* First, get the last two encrypted blocks */ + memset(blockN1, 0, sizeof(blockN1)); /* pad last block with zeros */ + krb5int_c_iov_get_block((unsigned char *)blockN2, BLOCK_SIZE, data, num_data, &input_pos); + krb5int_c_iov_get_block((unsigned char *)blockN1, BLOCK_SIZE, data, num_data, &input_pos); + + /* Decrypt second last block */ + dec(tmp2, blockN2, &ctx); + /* Set tmp3 to last ciphertext block (already padded) */ + memcpy(tmp3, blockN1, BLOCK_SIZE); + /* Set tmp2 to last (possibly partial) plaintext block, and + save it. */ + xorblock(tmp2, tmp3); + memcpy(blockN1, tmp2, BLOCK_SIZE); + /* Maybe keep the trailing part, and copy in the last + ciphertext block. */ + memcpy(tmp2, tmp3, BLOCK_SIZE); + dec(tmp3, tmp2, &ctx); + xorblock(tmp3, tmp); + /* Copy out ivec first before we clobber blockN2 with plaintext */ + if (ivec != NULL) + memcpy(ivec->data, blockN2, BLOCK_SIZE); + memcpy(blockN2, tmp3, BLOCK_SIZE); + + /* Put the last two blocks back into the ivec */ + krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN2, BLOCK_SIZE, &output_pos); + krb5int_c_iov_put_block(data, num_data, (unsigned char *)blockN1, BLOCK_SIZE, &output_pos); + } + + return 0; +} + static krb5_error_code k5_aes_make_key(const krb5_data *randombits, krb5_keyblock *key) { @@ -230,7 +394,9 @@ const struct krb5_enc_provider krb5int_enc_aes128 = { krb5int_aes_decrypt, k5_aes_make_key, krb5int_aes_init_state, - krb5int_default_free_state + krb5int_default_free_state, + krb5int_aes_encrypt_iov, + krb5int_aes_decrypt_iov }; const struct krb5_enc_provider krb5int_enc_aes256 = { @@ -240,5 +406,8 @@ const struct krb5_enc_provider krb5int_enc_aes256 = { krb5int_aes_decrypt, k5_aes_make_key, krb5int_aes_init_state, - krb5int_default_free_state + krb5int_default_free_state, + krb5int_aes_encrypt_iov, + krb5int_aes_decrypt_iov }; + diff --git a/src/lib/crypto/enc_provider/des3.c b/src/lib/crypto/enc_provider/des3.c index 51e4ce7967..e7a07f64cc 100644 --- a/src/lib/crypto/enc_provider/des3.c +++ b/src/lib/crypto/enc_provider/des3.c @@ -26,6 +26,7 @@ #include "k5-int.h" #include "des_int.h" +#include "../aead.h" static krb5_error_code validate_and_schedule(const krb5_keyblock *key, const krb5_data *ivec, @@ -53,6 +54,37 @@ validate_and_schedule(const krb5_keyblock *key, const krb5_data *ivec, return 0; } +static krb5_error_code +validate_and_schedule_iov(const krb5_keyblock *key, const krb5_data *ivec, + const krb5_crypto_iov *data, size_t num_data, + mit_des3_key_schedule *schedule) +{ + size_t i, input_length; + + for (i = 0, input_length = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + input_length += iov->data.length; + } + + if (key->length != 24) + return(KRB5_BAD_KEYSIZE); + if ((input_length%8) != 0) + return(KRB5_BAD_MSIZE); + if (ivec && (ivec->length != 8)) + return(KRB5_BAD_MSIZE); + + switch (mit_des3_key_sched(*(mit_des3_cblock *)key->contents, + *schedule)) { + case -1: + return(KRB5DES_BAD_KEYPAR); + case -2: + return(KRB5DES_WEAK_KEY); + } + return 0; +} + static krb5_error_code k5_des3_encrypt(const krb5_keyblock *key, const krb5_data *ivec, const krb5_data *input, krb5_data *output) @@ -129,6 +161,52 @@ k5_des3_make_key(const krb5_data *randombits, krb5_keyblock *key) return(0); } +static krb5_error_code +k5_des3_encrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + mit_des3_key_schedule schedule; + krb5_error_code err; + + err = validate_and_schedule_iov(key, ivec, data, num_data, &schedule); + if (err) + return err; + + /* this has a return value, but the code always returns zero */ + krb5int_des3_cbc_encrypt_iov(data, num_data, + schedule[0], schedule[1], schedule[2], + ivec != NULL ? (const unsigned char *) ivec->data : NULL); + + zap(schedule, sizeof(schedule)); + + return(0); +} + +static krb5_error_code +k5_des3_decrypt_iov(const krb5_keyblock *key, + const krb5_data *ivec, + krb5_crypto_iov *data, + size_t num_data) +{ + mit_des3_key_schedule schedule; + krb5_error_code err; + + err = validate_and_schedule_iov(key, ivec, data, num_data, &schedule); + if (err) + return err; + + /* this has a return value, but the code always returns zero */ + krb5int_des3_cbc_decrypt_iov(data, num_data, + schedule[0], schedule[1], schedule[2], + ivec != NULL ? (const unsigned char *) ivec->data : NULL); + + zap(schedule, sizeof(schedule)); + + return(0); +} + const struct krb5_enc_provider krb5int_enc_des3 = { 8, 21, 24, @@ -136,5 +214,8 @@ const struct krb5_enc_provider krb5int_enc_des3 = { k5_des3_decrypt, k5_des3_make_key, krb5int_des_init_state, - krb5int_default_free_state + krb5int_default_free_state, + k5_des3_encrypt_iov, + k5_des3_decrypt_iov }; + diff --git a/src/lib/crypto/enc_provider/enc_provider.h b/src/lib/crypto/enc_provider/enc_provider.h index 5754d1a2d5..4c370c14dd 100644 --- a/src/lib/crypto/enc_provider/enc_provider.h +++ b/src/lib/crypto/enc_provider/enc_provider.h @@ -31,3 +31,4 @@ extern const struct krb5_enc_provider krb5int_enc_des3; extern const struct krb5_enc_provider krb5int_enc_arcfour; extern const struct krb5_enc_provider krb5int_enc_aes128; extern const struct krb5_enc_provider krb5int_enc_aes256; + diff --git a/src/lib/crypto/enc_provider/rc4.c b/src/lib/crypto/enc_provider/rc4.c index a88ad79376..b950a605b8 100644 --- a/src/lib/crypto/enc_provider/rc4.c +++ b/src/lib/crypto/enc_provider/rc4.c @@ -9,6 +9,7 @@ #include "k5-int.h" #include "arcfour-int.h" #include "enc_provider.h" +#include "../aead.h" /* gets the next byte from the PRNG */ #if ((__GNUC__ >= 2) ) static __inline__ unsigned int k5_arcfour_byte(ArcfourContext *); @@ -156,6 +157,61 @@ k5_arcfour_docrypt(const krb5_keyblock *key, const krb5_data *state, return 0; } +/* In-place encryption */ +static krb5_error_code +k5_arcfour_docrypt_iov(const krb5_keyblock *key, + const krb5_data *state, + krb5_crypto_iov *data, + size_t num_data) +{ + ArcfourContext *arcfour_ctx = NULL; + ArcFourCipherState *cipher_state = NULL; + krb5_error_code ret; + size_t i; + + if (key->length != 16) + return KRB5_BAD_KEYSIZE; + if (state != NULL && (state->length != sizeof(ArcFourCipherState))) + return KRB5_BAD_MSIZE; + + if (state != NULL) { + cipher_state = (ArcFourCipherState *)state->data; + arcfour_ctx = &cipher_state->ctx; + if (cipher_state->initialized == 0) { + ret = k5_arcfour_init(arcfour_ctx, key->contents, key->length); + if (ret != 0) + return ret; + + cipher_state->initialized = 1; + } + } else { + arcfour_ctx = (ArcfourContext *)malloc(sizeof(ArcfourContext)); + if (arcfour_ctx == NULL) + return ENOMEM; + + ret = k5_arcfour_init(arcfour_ctx, key->contents, key->length); + if (ret != 0) { + free(arcfour_ctx); + return ret; + } + } + + for (i = 0; i < num_data; i++) { + krb5_crypto_iov *iov = &data[i]; + + if (ENCRYPT_IOV(iov)) + k5_arcfour_crypt(arcfour_ctx, (unsigned char *)iov->data.data, + (const unsigned char *)iov->data.data, iov->data.length); + } + + if (state == NULL) { + memset(arcfour_ctx, 0, sizeof(ArcfourContext)); + free(arcfour_ctx); + } + + return 0; +} + static krb5_error_code k5_arcfour_make_key(const krb5_data *randombits, krb5_keyblock *key) { @@ -208,5 +264,8 @@ const struct krb5_enc_provider krb5int_enc_arcfour = { k5_arcfour_docrypt, k5_arcfour_make_key, k5_arcfour_init_state, /*xxx not implemented yet*/ - krb5int_default_free_state + krb5int_default_free_state, + k5_arcfour_docrypt_iov, + k5_arcfour_docrypt_iov }; + diff --git a/src/lib/crypto/encrypt_iov.c b/src/lib/crypto/encrypt_iov.c new file mode 100644 index 0000000000..a35c5b5770 --- /dev/null +++ b/src/lib/crypto/encrypt_iov.c @@ -0,0 +1,55 @@ +/* + * lib/crypto/encrypt_iov.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 "etypes.h" + +krb5_error_code KRB5_CALLCONV +krb5_c_encrypt_iov(krb5_context context, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_data *cipher_state, + krb5_crypto_iov *data, + size_t num_data) +{ + int i; + const struct krb5_keytypes *ktp = NULL; + + for (i = 0; i < krb5_enctypes_length; i++) { + if (krb5_enctypes_list[i].etype == key->enctype) { + ktp = &krb5_enctypes_list[i]; + break; + } + } + + if (ktp == NULL || ktp->aead == NULL) { + return KRB5_BAD_ENCTYPE; + } + + return ktp->aead->encrypt_iov(ktp->aead, ktp->enc, ktp->hash, + key, usage, cipher_state, data, num_data); +} + diff --git a/src/lib/crypto/etypes.c b/src/lib/crypto/etypes.c index 4273e2826e..340fc31d33 100644 --- a/src/lib/crypto/etypes.c +++ b/src/lib/crypto/etypes.c @@ -48,7 +48,8 @@ const struct krb5_keytypes krb5_enctypes_list[] = { krb5_old_encrypt_length, krb5_old_encrypt, krb5_old_decrypt, krb5int_des_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_RSA_MD5 }, + CKSUMTYPE_RSA_MD5, + NULL /*AEAD*/ }, { ENCTYPE_DES_CBC_MD4, "des-cbc-md4", "DES cbc mode with RSA-MD4", &krb5int_enc_des, &krb5int_hash_md4, @@ -56,7 +57,8 @@ const struct krb5_keytypes krb5_enctypes_list[] = { krb5_old_encrypt_length, krb5_old_encrypt, krb5_old_decrypt, krb5int_des_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_RSA_MD4 }, + CKSUMTYPE_RSA_MD4, + NULL /*AEAD*/ }, { ENCTYPE_DES_CBC_MD5, "des-cbc-md5", "DES cbc mode with RSA-MD5", &krb5int_enc_des, &krb5int_hash_md5, @@ -64,7 +66,8 @@ const struct krb5_keytypes krb5_enctypes_list[] = { krb5_old_encrypt_length, krb5_old_encrypt, krb5_old_decrypt, krb5int_des_string_to_key, NULL, /*PRF*/ -CKSUMTYPE_RSA_MD5 }, + CKSUMTYPE_RSA_MD5, + NULL /*AEAD*/ }, { ENCTYPE_DES_CBC_MD5, "des", "DES cbc mode with RSA-MD5", /* alias */ &krb5int_enc_des, &krb5int_hash_md5, @@ -72,7 +75,8 @@ CKSUMTYPE_RSA_MD5 }, krb5_old_encrypt_length, krb5_old_encrypt, krb5_old_decrypt, krb5int_des_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_RSA_MD5 }, + CKSUMTYPE_RSA_MD5, + NULL /*AEAD*/ }, { ENCTYPE_DES_CBC_RAW, "des-cbc-raw", "DES cbc mode raw", @@ -81,7 +85,8 @@ CKSUMTYPE_RSA_MD5 }, krb5_raw_encrypt_length, krb5_raw_encrypt, krb5_raw_decrypt, krb5int_des_string_to_key, NULL, /*PRF*/ - 0 }, + 0, + NULL /*AEAD*/ }, { ENCTYPE_DES3_CBC_RAW, "des3-cbc-raw", "Triple DES cbc mode raw", &krb5int_enc_des3, NULL, @@ -89,7 +94,8 @@ CKSUMTYPE_RSA_MD5 }, krb5_raw_encrypt_length, krb5_raw_encrypt, krb5_raw_decrypt, krb5int_dk_string_to_key, NULL, /*PRF*/ - 0 }, + 0, + NULL /*AEAD*/ }, { ENCTYPE_DES3_CBC_SHA1, "des3-cbc-sha1", "Triple DES cbc mode with HMAC/sha1", @@ -98,7 +104,8 @@ CKSUMTYPE_RSA_MD5 }, krb5_dk_encrypt_length, krb5_dk_encrypt, krb5_dk_decrypt, krb5int_dk_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_SHA1_DES3 }, + CKSUMTYPE_HMAC_SHA1_DES3, + &krb5int_aead_dk }, { ENCTYPE_DES3_CBC_SHA1, /* alias */ "des3-hmac-sha1", "Triple DES cbc mode with HMAC/sha1", &krb5int_enc_des3, &krb5int_hash_sha1, @@ -106,7 +113,8 @@ CKSUMTYPE_RSA_MD5 }, krb5_dk_encrypt_length, krb5_dk_encrypt, krb5_dk_decrypt, krb5int_dk_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_SHA1_DES3 }, + CKSUMTYPE_HMAC_SHA1_DES3, + &krb5int_aead_dk }, { ENCTYPE_DES3_CBC_SHA1, /* alias */ "des3-cbc-sha1-kd", "Triple DES cbc mode with HMAC/sha1", &krb5int_enc_des3, &krb5int_hash_sha1, @@ -114,7 +122,8 @@ CKSUMTYPE_RSA_MD5 }, krb5_dk_encrypt_length, krb5_dk_encrypt, krb5_dk_decrypt, krb5int_dk_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_SHA1_DES3 }, + CKSUMTYPE_HMAC_SHA1_DES3, + &krb5int_aead_dk }, { ENCTYPE_DES_HMAC_SHA1, "des-hmac-sha1", "DES with HMAC/sha1", @@ -123,7 +132,8 @@ CKSUMTYPE_RSA_MD5 }, krb5_dk_encrypt_length, krb5_dk_encrypt, krb5_dk_decrypt, krb5int_dk_string_to_key, NULL, /*PRF*/ - 0 }, + 0, + NULL }, { ENCTYPE_ARCFOUR_HMAC, "arcfour-hmac","ArcFour with HMAC/md5", &krb5int_enc_arcfour, &krb5int_hash_md5, @@ -131,7 +141,8 @@ CKSUMTYPE_RSA_MD5 }, krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_decrypt, krb5int_arcfour_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_MD5_ARCFOUR }, + CKSUMTYPE_HMAC_MD5_ARCFOUR, + &krb5int_aead_arcfour }, { ENCTYPE_ARCFOUR_HMAC, /* alias */ "rc4-hmac", "ArcFour with HMAC/md5", &krb5int_enc_arcfour, &krb5int_hash_md5, @@ -139,7 +150,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_decrypt, krb5int_arcfour_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_MD5_ARCFOUR }, + CKSUMTYPE_HMAC_MD5_ARCFOUR, + &krb5int_aead_arcfour }, { ENCTYPE_ARCFOUR_HMAC, /* alias */ "arcfour-hmac-md5", "ArcFour with HMAC/md5", &krb5int_enc_arcfour, &krb5int_hash_md5, @@ -147,7 +159,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_decrypt, krb5int_arcfour_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_MD5_ARCFOUR }, + CKSUMTYPE_HMAC_MD5_ARCFOUR, + &krb5int_aead_arcfour }, { ENCTYPE_ARCFOUR_HMAC_EXP, "arcfour-hmac-exp", "Exportable ArcFour with HMAC/md5", &krb5int_enc_arcfour, @@ -156,7 +169,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_decrypt, krb5int_arcfour_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_MD5_ARCFOUR }, + CKSUMTYPE_HMAC_MD5_ARCFOUR, + &krb5int_aead_arcfour }, { ENCTYPE_ARCFOUR_HMAC_EXP, /* alias */ "rc4-hmac-exp", "Exportable ArcFour with HMAC/md5", &krb5int_enc_arcfour, @@ -165,7 +179,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_decrypt, krb5int_arcfour_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_MD5_ARCFOUR }, + CKSUMTYPE_HMAC_MD5_ARCFOUR, + &krb5int_aead_arcfour }, { ENCTYPE_ARCFOUR_HMAC_EXP, /* alias */ "arcfour-hmac-md5-exp", "Exportable ArcFour with HMAC/md5", &krb5int_enc_arcfour, @@ -174,7 +189,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5_arcfour_decrypt, krb5int_arcfour_string_to_key, NULL, /*PRF*/ - CKSUMTYPE_HMAC_MD5_ARCFOUR }, + CKSUMTYPE_HMAC_MD5_ARCFOUR, + &krb5int_aead_arcfour }, { ENCTYPE_AES128_CTS_HMAC_SHA1_96, "aes128-cts-hmac-sha1-96", "AES-128 CTS mode with 96-bit SHA-1 HMAC", @@ -183,7 +199,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5int_aes_encrypt_length, krb5int_aes_dk_encrypt, krb5int_aes_dk_decrypt, krb5int_aes_string_to_key, krb5int_dk_prf, - CKSUMTYPE_HMAC_SHA1_96_AES128 }, + CKSUMTYPE_HMAC_SHA1_96_AES128, + &krb5int_aead_aes }, { ENCTYPE_AES128_CTS_HMAC_SHA1_96, /* alias */ "aes128-cts", "AES-128 CTS mode with 96-bit SHA-1 HMAC", &krb5int_enc_aes128, &krb5int_hash_sha1, @@ -191,7 +208,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5int_aes_encrypt_length, krb5int_aes_dk_encrypt, krb5int_aes_dk_decrypt, krb5int_aes_string_to_key, krb5int_dk_prf, - CKSUMTYPE_HMAC_SHA1_96_AES128 }, + CKSUMTYPE_HMAC_SHA1_96_AES128, + &krb5int_aead_aes }, { ENCTYPE_AES256_CTS_HMAC_SHA1_96, "aes256-cts-hmac-sha1-96", "AES-256 CTS mode with 96-bit SHA-1 HMAC", &krb5int_enc_aes256, &krb5int_hash_sha1, @@ -199,7 +217,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5int_aes_encrypt_length, krb5int_aes_dk_encrypt, krb5int_aes_dk_decrypt, krb5int_aes_string_to_key, krb5int_dk_prf, - CKSUMTYPE_HMAC_SHA1_96_AES256 }, + CKSUMTYPE_HMAC_SHA1_96_AES256, + &krb5int_aead_aes }, { ENCTYPE_AES256_CTS_HMAC_SHA1_96, /* alias */ "aes256-cts", "AES-256 CTS mode with 96-bit SHA-1 HMAC", &krb5int_enc_aes256, &krb5int_hash_sha1, @@ -207,7 +226,8 @@ krb5_arcfour_encrypt_length, krb5_arcfour_encrypt, krb5int_aes_encrypt_length, krb5int_aes_dk_encrypt, krb5int_aes_dk_decrypt, krb5int_aes_string_to_key, krb5int_dk_prf, - CKSUMTYPE_HMAC_SHA1_96_AES256 }, + CKSUMTYPE_HMAC_SHA1_96_AES256, + &krb5int_aead_aes }, }; const int krb5_enctypes_length = diff --git a/src/lib/crypto/hmac.c b/src/lib/crypto/hmac.c index 3c02726455..42785f5729 100644 --- a/src/lib/crypto/hmac.c +++ b/src/lib/crypto/hmac.c @@ -25,6 +25,7 @@ */ #include "k5-int.h" +#include "aead.h" /* * the HMAC transform looks like: @@ -125,3 +126,40 @@ cleanup: return(ret); } + +krb5_error_code +krb5_hmac_iov(const struct krb5_hash_provider *hash, const krb5_keyblock *key, + const krb5_crypto_iov *data, size_t num_data, krb5_data *output) +{ + krb5_data *sign_data; + size_t num_sign_data; + krb5_error_code ret; + size_t i, j; + + /* Create a checksum over all the data to be signed */ + for (i = 0, num_sign_data = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (SIGN_IOV(iov)) + num_sign_data++; + } + /* XXX cleanup to avoid alloc */ + sign_data = (krb5_data *)calloc(num_sign_data, sizeof(krb5_data)); + if (sign_data == NULL) + return ENOMEM; + + for (i = 0, j = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (SIGN_IOV(iov)) + sign_data[j++] = iov->data; + } + + /* caller must store checksum in iov as it may be TYPE_TRAILER or TYPE_CHECKSUM */ + ret = krb5_hmac(hash, key, num_sign_data, sign_data, output); + + free(sign_data); + + return ret; +} + diff --git a/src/lib/crypto/keyhash_provider/descbc.c b/src/lib/crypto/keyhash_provider/descbc.c index 23b33fc07d..bf68e324ce 100644 --- a/src/lib/crypto/keyhash_provider/descbc.c +++ b/src/lib/crypto/keyhash_provider/descbc.c @@ -66,5 +66,7 @@ k5_descbc_hash(const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *i const struct krb5_keyhash_provider krb5int_keyhash_descbc = { 8, k5_descbc_hash, + NULL, + NULL, NULL }; diff --git a/src/lib/crypto/keyhash_provider/hmac_md5.c b/src/lib/crypto/keyhash_provider/hmac_md5.c index a2472a8329..0f510925d9 100644 --- a/src/lib/crypto/keyhash_provider/hmac_md5.c +++ b/src/lib/crypto/keyhash_provider/hmac_md5.c @@ -35,6 +35,7 @@ #include "arcfour-int.h" #include "rsa-md5.h" #include "hash_provider.h" +#include "../aead.h" static krb5_error_code k5_hmac_md5_hash (const krb5_keyblock *key, krb5_keyusage usage, @@ -86,11 +87,67 @@ k5_hmac_md5_hash (const krb5_keyblock *key, krb5_keyusage usage, return ret; } - +static krb5_error_code +k5_hmac_md5_hash_iov (const krb5_keyblock *key, krb5_keyusage usage, + const krb5_data *iv, + const krb5_crypto_iov *data, size_t num_data, + krb5_data *output) +{ + krb5_keyusage ms_usage; + krb5_error_code ret; + krb5_keyblock ks; + krb5_data ds, ks_constant, md5tmp; + krb5_MD5_CTX ctx; + char t[4]; + size_t i; + + ds.length = key->length; + ks.length = key->length; + ds.data = malloc(ds.length); + if (ds.data == NULL) + return ENOMEM; + ks.contents = (void *) ds.data; + + ks_constant.data = "signaturekey"; + ks_constant.length = strlen(ks_constant.data)+1; /* Including null*/ + + ret = krb5_hmac( &krb5int_hash_md5, key, 1, + &ks_constant, &ds); + if (ret) + goto cleanup; + + krb5_MD5Init (&ctx); + ms_usage = krb5int_arcfour_translate_usage (usage); + t[0] = (ms_usage) & 0xff; + t[1] = (ms_usage>>8) & 0xff; + t[2] = (ms_usage >>16) & 0xff; + t[3] = (ms_usage>>24) & 0XFF; + krb5_MD5Update (&ctx, (unsigned char * ) &t, 4); + for (i = 0; i < num_data; i++) { + const krb5_crypto_iov *iov = &data[i]; + + if (SIGN_IOV(iov)) + krb5_MD5Update (&ctx, (unsigned char *)iov->data.data, + (unsigned int)iov->data.length); + } + krb5_MD5Final(&ctx); + md5tmp.data = (void *) ctx.digest; + md5tmp.length = 16; + ret = krb5_hmac ( &krb5int_hash_md5, &ks, 1, &md5tmp, + output); + + cleanup: + memset(&ctx, 0, sizeof(ctx)); + memset (ks.contents, 0, ks.length); + free (ks.contents); + return ret; +} const struct krb5_keyhash_provider krb5int_keyhash_hmac_md5 = { 16, k5_hmac_md5_hash, - NULL /*checksum again*/ + NULL, /*checksum again*/ + k5_hmac_md5_hash_iov, + NULL /*checksum again */ }; diff --git a/src/lib/crypto/keyhash_provider/k5_md4des.c b/src/lib/crypto/keyhash_provider/k5_md4des.c index 9f19f4f968..fceb58ebd4 100644 --- a/src/lib/crypto/keyhash_provider/k5_md4des.c +++ b/src/lib/crypto/keyhash_provider/k5_md4des.c @@ -188,5 +188,7 @@ k5_md4des_verify(const krb5_keyblock *key, krb5_keyusage usage, const struct krb5_keyhash_provider krb5int_keyhash_md4des = { CONFLENGTH+RSA_MD4_CKSUM_LENGTH, k5_md4des_hash, - k5_md4des_verify + k5_md4des_verify, + NULL, + NULL }; diff --git a/src/lib/crypto/keyhash_provider/k5_md5des.c b/src/lib/crypto/keyhash_provider/k5_md5des.c index e70965b791..0175c68ab7 100644 --- a/src/lib/crypto/keyhash_provider/k5_md5des.c +++ b/src/lib/crypto/keyhash_provider/k5_md5des.c @@ -185,5 +185,7 @@ k5_md5des_verify(const krb5_keyblock *key, krb5_keyusage usage, const krb5_data const struct krb5_keyhash_provider krb5int_keyhash_md5des = { CONFLENGTH+RSA_MD5_CKSUM_LENGTH, k5_md5des_hash, - k5_md5des_verify + k5_md5des_verify, + NULL, + NULL }; diff --git a/src/lib/crypto/libk5crypto.exports b/src/lib/crypto/libk5crypto.exports index d705a6b5f3..9fe8e69e15 100644 --- a/src/lib/crypto/libk5crypto.exports +++ b/src/lib/crypto/libk5crypto.exports @@ -14,8 +14,12 @@ krb5_arcfour_encrypt krb5_arcfour_encrypt_length krb5_c_block_size krb5_c_checksum_length +krb5_c_crypto_length +krb5_c_crypto_length_iov krb5_c_decrypt +krb5_c_decrypt_iov krb5_c_encrypt +krb5_c_encrypt_iov krb5_c_encrypt_length krb5_c_enctype_compare krb5_c_free_state @@ -25,7 +29,9 @@ krb5_c_is_keyed_cksum krb5_c_keyed_checksum_types krb5_c_keylengths krb5_c_make_checksum +krb5_c_make_checksum_iov krb5_c_make_random_key +krb5_c_padding_length krb5_c_prf krb5_c_prf_length krb5_c_random_add_entropy @@ -38,6 +44,7 @@ krb5_c_string_to_key_with_params krb5_c_valid_cksumtype krb5_c_valid_enctype krb5_c_verify_checksum +krb5_c_verify_checksum_iov krb5_calculate_checksum krb5_checksum_size krb5_cksumtype_to_string @@ -62,6 +69,7 @@ krb5_finish_key krb5_finish_random_key krb5_free_cksumtypes krb5_hmac +krb5_hmac_iov krb5_init_random_key krb5_nfold krb5_old_decrypt diff --git a/src/lib/crypto/make_checksum_iov.c b/src/lib/crypto/make_checksum_iov.c new file mode 100644 index 0000000000..0d862c8352 --- /dev/null +++ b/src/lib/crypto/make_checksum_iov.c @@ -0,0 +1,68 @@ +/* + * lib/crypto/make_checksum_iov.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 "cksumtypes.h" +#include "aead.h" + +krb5_error_code KRB5_CALLCONV +krb5_c_make_checksum_iov(krb5_context context, + krb5_cksumtype cksumtype, + const krb5_keyblock *key, + krb5_keyusage usage, + krb5_crypto_iov *data, + size_t num_data) +{ + krb5_error_code ret; + size_t cksumlen; + krb5_crypto_iov *checksum; + size_t i; + + for (i = 0; i < krb5_cksumtypes_length; i++) { + if (krb5_cksumtypes_list[i].ctype == cksumtype) + break; + } + + if (i == krb5_cksumtypes_length) + return(KRB5_BAD_ENCTYPE); + + if (krb5_cksumtypes_list[i].keyhash) + cksumlen = krb5_cksumtypes_list[i].keyhash->hashsize; + else + cksumlen = krb5_cksumtypes_list[i].hash->hashsize; + + checksum = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM); + if (checksum == NULL || checksum->data.length < cksumlen) + return(KRB5_BAD_MSIZE); + + checksum->data.length = cksumlen; + + ret = krb5int_c_make_checksum_iov(&krb5_cksumtypes_list[i], + key, usage, data, num_data, + &checksum->data); + + return(ret); +} diff --git a/src/lib/crypto/t_encrypt.c b/src/lib/crypto/t_encrypt.c index eb2378b5e5..ded1d8d982 100644 --- a/src/lib/crypto/t_encrypt.c +++ b/src/lib/crypto/t_encrypt.c @@ -1,7 +1,7 @@ /* main * lib/crypto/t_encrypt.c * - * Copyright2001 by the Massachusetts Institute of Technology. + * Copyright 2001, 2008by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may @@ -76,12 +76,15 @@ main () { krb5_context context = 0; krb5_data in, in2, out, out2, check, check2, state; + krb5_crypto_iov iov[5]; int i; size_t len; krb5_enc_data enc_out, enc_out2; krb5_error_code retval; krb5_keyblock *key; + memset(iov, 0, sizeof(iov)); + in.data = "This is a test.\n"; in.length = strlen (in.data); in2.data = "This is another test.\n"; @@ -118,6 +121,46 @@ main () test ("Decrypting", krb5_c_decrypt (context, key, 7, 0, &enc_out, &check)); test ("Comparing", compare_results (&in, &check)); + if ( krb5_c_crypto_length(context, key->enctype, KRB5_CRYPTO_TYPE_HEADER, &len) == 0 ){ + /* We support iov/aead*/ + int j, pos; + krb5_data signdata; + signdata.data = (char *) "This should be signed"; + signdata.length = strlen(signdata.data); + iov[0].flags= KRB5_CRYPTO_TYPE_STREAM; + iov[1].flags = KRB5_CRYPTO_TYPE_DATA; + iov[0].data = enc_out.ciphertext; + iov[1].data = out; + test("IOV stream decrypting", + krb5_c_decrypt_iov( context, key, 7, 0, iov, 2)); + test("Comparing results", + compare_results(&in, &iov[1].data)); + iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; + iov[1].flags = KRB5_CRYPTO_TYPE_DATA; + iov[1].data = in; /*We'll need to copy memory before encrypt*/ + iov[2].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + iov[2].data = signdata; + iov[3].flags = KRB5_CRYPTO_TYPE_PADDING; + iov[4].flags = KRB5_CRYPTO_TYPE_TRAILER; + test("Setting up iov lengths", + krb5_c_crypto_length_iov(context, key->enctype, iov, 5)); + for (j=0,pos=0; j <= 4; j++ ){ + if (iov[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) + continue; + iov[j].data.data = &out.data[pos]; + pos += iov[j].data.length; + } + assert (iov[1].data.length == in.length); + memcpy(iov[1].data.data, in.data, in.length); + test("iov encrypting", + krb5_c_encrypt_iov(context, key, 7, 0, iov, 5)); + assert(iov[1].data.length == in.length); + test("iov decrypting", + krb5_c_decrypt_iov(context, key, 7, 0, iov, 5)); + test("Comparing results", + compare_results(&in, &iov[1].data)); + + } enc_out.ciphertext.length = out.length; check.length = 2048; test ("init_state", diff --git a/src/lib/crypto/verify_checksum_iov.c b/src/lib/crypto/verify_checksum_iov.c new file mode 100644 index 0000000000..5627188dfb --- /dev/null +++ b/src/lib/crypto/verify_checksum_iov.c @@ -0,0 +1,92 @@ +/* + * lib/crypto/verify_checksum_iov.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 "cksumtypes.h" +#include "aead.h" + +krb5_error_code KRB5_CALLCONV +krb5_c_verify_checksum_iov(krb5_context context, + krb5_cksumtype checksum_type, + const krb5_keyblock *key, + krb5_keyusage usage, + const krb5_crypto_iov *data, + size_t num_data, + krb5_boolean *valid) +{ + unsigned int i; + size_t hashsize; + krb5_error_code ret; + krb5_data computed; + krb5_crypto_iov *checksum; + + for (i=0; iverify_iov) + return((*(krb5_cksumtypes_list[i].keyhash->verify_iov))(key, usage, 0, + &checksum->data, + data, num_data, + valid)); + + /* otherwise, make the checksum again, and compare */ + + if ((ret = krb5_c_checksum_length(context, checksum_type, &hashsize))) + return(ret); + + if (checksum->data.length != hashsize) + return(KRB5_BAD_MSIZE); + + computed.data = malloc(hashsize); + if (computed.data == NULL) { + return(ENOMEM); + } + computed.length = hashsize; + + if ((ret = krb5int_c_make_checksum_iov(&krb5_cksumtypes_list[i], key, usage, + data, num_data, &computed))) { + free(computed.data); + return(ret); + } + + *valid = (memcmp(computed.data, &checksum->data, hashsize) == 0); + + free(computed.data); + + return(0); +} diff --git a/src/lib/krb5/os/accessor.c b/src/lib/krb5/os/accessor.c index 5a4e9557a0..9eb81af763 100644 --- a/src/lib/krb5/os/accessor.c +++ b/src/lib/krb5/os/accessor.c @@ -53,6 +53,7 @@ krb5int_accessor(krb5int_access *internals, krb5_int32 version) #endif S (free_addrlist, krb5int_free_addrlist), S (krb5_hmac, krb5_hmac), + S (krb5_hmac_iov, krb5_hmac_iov), S (md5_hash_provider, &krb5int_hash_md5), S (arcfour_enc_provider, &krb5int_enc_arcfour), S (sendto_udp, &krb5int_sendto), -- cgit