Add an AEAD provider for enctypes which use krb5_old_encrypt and
authorGreg Hudson <ghudson@mit.edu>
Mon, 30 Nov 2009 16:19:24 +0000 (16:19 +0000)
committerGreg Hudson <ghudson@mit.edu>
Mon, 30 Nov 2009 16:19:24 +0000 (16:19 +0000)
krb5_old_decrypt; this makes every enctype have an AEAD provider.  To
make this work, expose make_unkeyed_checksum_iov to other files (under
the name krb5int_hash_iov) and make krb5int_c_padding_length take into
account the header length.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23388 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/crypto/krb/aead.c
src/lib/crypto/krb/aead.h
src/lib/crypto/krb/etypes.c
src/lib/crypto/krb/old/Makefile.in
src/lib/crypto/krb/old/old.h
src/lib/crypto/krb/old/old_aead.c [new file with mode: 0644]

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