Import of code from heimdal
authorHeimdal Developers <heimdal-discuss@sics.se>
Thu, 27 Jan 2011 12:22:24 +0000 (12:22 +0000)
committerDerrick Brashear <shadow@dementia.org>
Wed, 16 Feb 2011 16:47:22 +0000 (08:47 -0800)
This commit updates the code imported from heimdal to
8ce821c38744b30e226ff024f675668b991b984d (switch-from-svn-to-git-1998-g8ce821c)

New files are:
krb5/crypto-aes.c
krb5/crypto-evp.c
krb5/crypto.c
krb5/crypto.h
krb5/data.c
krb5/keyblock.c
krb5/store-int.c

Change-Id: I511d54e1353a3662babd2d12611fd31b8e3061a5
Reviewed-on: http://gerrit.openafs.org/3947
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Derrick Brashear <shadow@dementia.org>

src/external/heimdal/krb5/crypto-aes.c [new file with mode: 0644]
src/external/heimdal/krb5/crypto-evp.c [new file with mode: 0644]
src/external/heimdal/krb5/crypto.c [new file with mode: 0644]
src/external/heimdal/krb5/crypto.h [new file with mode: 0644]
src/external/heimdal/krb5/data.c [new file with mode: 0644]
src/external/heimdal/krb5/keyblock.c [new file with mode: 0644]
src/external/heimdal/krb5/store-int.c [new file with mode: 0644]

diff --git a/src/external/heimdal/krb5/crypto-aes.c b/src/external/heimdal/krb5/crypto-aes.c
new file mode 100644 (file)
index 0000000..25c675c
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+/*
+ * AES
+ */
+
+static struct key_type keytype_aes128 = {
+    KEYTYPE_AES128,
+    "aes-128",
+    128,
+    16,
+    sizeof(struct evp_schedule),
+    NULL,
+    _krb5_evp_schedule,
+    _krb5_AES_salt,
+    NULL,
+    _krb5_evp_cleanup,
+    EVP_aes_128_cbc
+};
+
+static struct key_type keytype_aes256 = {
+    KEYTYPE_AES256,
+    "aes-256",
+    256,
+    32,
+    sizeof(struct evp_schedule),
+    NULL,
+    _krb5_evp_schedule,
+    _krb5_AES_salt,
+    NULL,
+    _krb5_evp_cleanup,
+    EVP_aes_256_cbc
+};
+
+struct checksum_type _krb5_checksum_hmac_sha1_aes128 = {
+    CKSUMTYPE_HMAC_SHA1_96_AES_128,
+    "hmac-sha1-96-aes128",
+    64,
+    12,
+    F_KEYED | F_CPROOF | F_DERIVED,
+    _krb5_SP_HMAC_SHA1_checksum,
+    NULL
+};
+
+struct checksum_type _krb5_checksum_hmac_sha1_aes256 = {
+    CKSUMTYPE_HMAC_SHA1_96_AES_256,
+    "hmac-sha1-96-aes256",
+    64,
+    12,
+    F_KEYED | F_CPROOF | F_DERIVED,
+    _krb5_SP_HMAC_SHA1_checksum,
+    NULL
+};
+
+static krb5_error_code
+AES_PRF(krb5_context context,
+       krb5_crypto crypto,
+       const krb5_data *in,
+       krb5_data *out)
+{
+    struct checksum_type *ct = crypto->et->checksum;
+    krb5_error_code ret;
+    Checksum result;
+    krb5_keyblock *derived;
+
+    result.cksumtype = ct->type;
+    ret = krb5_data_alloc(&result.checksum, ct->checksumsize);
+    if (ret) {
+       krb5_set_error_message(context, ret, N_("malloc: out memory", ""));
+       return ret;
+    }
+
+    ret = (*ct->checksum)(context, NULL, in->data, in->length, 0, &result);
+    if (ret) {
+       krb5_data_free(&result.checksum);
+       return ret;
+    }
+
+    if (result.checksum.length < crypto->et->blocksize)
+       krb5_abortx(context, "internal prf error");
+
+    derived = NULL;
+    ret = krb5_derive_key(context, crypto->key.key,
+                         crypto->et->type, "prf", 3, &derived);
+    if (ret)
+       krb5_abortx(context, "krb5_derive_key");
+
+    ret = krb5_data_alloc(out, crypto->et->blocksize);
+    if (ret)
+       krb5_abortx(context, "malloc failed");
+
+    {
+       const EVP_CIPHER *c = (*crypto->et->keytype->evp)();
+       EVP_CIPHER_CTX ctx;
+
+       EVP_CIPHER_CTX_init(&ctx); /* ivec all zero */
+       EVP_CipherInit_ex(&ctx, c, NULL, derived->keyvalue.data, NULL, 1);
+       EVP_Cipher(&ctx, out->data, result.checksum.data,
+                  crypto->et->blocksize);
+       EVP_CIPHER_CTX_cleanup(&ctx);
+    }
+
+    krb5_data_free(&result.checksum);
+    krb5_free_keyblock(context, derived);
+
+    return ret;
+}
+
+struct encryption_type _krb5_enctype_aes128_cts_hmac_sha1 = {
+    ETYPE_AES128_CTS_HMAC_SHA1_96,
+    "aes128-cts-hmac-sha1-96",
+    16,
+    1,
+    16,
+    &keytype_aes128,
+    &_krb5_checksum_sha1,
+    &_krb5_checksum_hmac_sha1_aes128,
+    F_DERIVED,
+    _krb5_evp_encrypt_cts,
+    16,
+    AES_PRF
+};
+
+struct encryption_type _krb5_enctype_aes256_cts_hmac_sha1 = {
+    ETYPE_AES256_CTS_HMAC_SHA1_96,
+    "aes256-cts-hmac-sha1-96",
+    16,
+    1,
+    16,
+    &keytype_aes256,
+    &_krb5_checksum_sha1,
+    &_krb5_checksum_hmac_sha1_aes256,
+    F_DERIVED,
+    _krb5_evp_encrypt_cts,
+    16,
+    AES_PRF
+};
diff --git a/src/external/heimdal/krb5/crypto-evp.c b/src/external/heimdal/krb5/crypto-evp.c
new file mode 100644 (file)
index 0000000..69d1e26
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+void
+_krb5_evp_schedule(krb5_context context,
+                  struct key_type *kt,
+                  struct key_data *kd)
+{
+    struct evp_schedule *key = kd->schedule->data;
+    const EVP_CIPHER *c = (*kt->evp)();
+
+    EVP_CIPHER_CTX_init(&key->ectx);
+    EVP_CIPHER_CTX_init(&key->dctx);
+
+    EVP_CipherInit_ex(&key->ectx, c, NULL, kd->key->keyvalue.data, NULL, 1);
+    EVP_CipherInit_ex(&key->dctx, c, NULL, kd->key->keyvalue.data, NULL, 0);
+}
+
+void
+_krb5_evp_cleanup(krb5_context context, struct key_data *kd)
+{
+    struct evp_schedule *key = kd->schedule->data;
+    EVP_CIPHER_CTX_cleanup(&key->ectx);
+    EVP_CIPHER_CTX_cleanup(&key->dctx);
+}
+
+krb5_error_code
+_krb5_evp_encrypt(krb5_context context,
+               struct key_data *key,
+               void *data,
+               size_t len,
+               krb5_boolean encryptp,
+               int usage,
+               void *ivec)
+{
+    struct evp_schedule *ctx = key->schedule->data;
+    EVP_CIPHER_CTX *c;
+    c = encryptp ? &ctx->ectx : &ctx->dctx;
+    if (ivec == NULL) {
+       /* alloca ? */
+       size_t len2 = EVP_CIPHER_CTX_iv_length(c);
+       void *loiv = malloc(len2);
+       if (loiv == NULL) {
+           krb5_clear_error_message(context);
+           return ENOMEM;
+       }
+       memset(loiv, 0, len2);
+       EVP_CipherInit_ex(c, NULL, NULL, NULL, loiv, -1);
+       free(loiv);
+    } else
+       EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1);
+    EVP_Cipher(c, data, data, len);
+    return 0;
+}
+
+static const unsigned char zero_ivec[EVP_MAX_BLOCK_LENGTH] = { 0 };
+
+krb5_error_code
+_krb5_evp_encrypt_cts(krb5_context context,
+                     struct key_data *key,
+                     void *data,
+                     size_t len,
+                     krb5_boolean encryptp,
+                     int usage,
+                     void *ivec)
+{
+    size_t i, blocksize;
+    struct evp_schedule *ctx = key->schedule->data;
+    char tmp[EVP_MAX_BLOCK_LENGTH], ivec2[EVP_MAX_BLOCK_LENGTH];
+    EVP_CIPHER_CTX *c;
+    unsigned char *p;
+
+    c = encryptp ? &ctx->ectx : &ctx->dctx;
+
+    blocksize = EVP_CIPHER_CTX_block_size(c);
+
+    if (len < blocksize) {
+       krb5_set_error_message(context, EINVAL,
+                              "message block too short");
+       return EINVAL;
+    } else if (len == blocksize) {
+       EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
+       EVP_Cipher(c, data, data, len);
+       return 0;
+    }
+
+    if (ivec)
+       EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1);
+    else
+       EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
+
+    if (encryptp) {
+
+       p = data;
+       i = ((len - 1) / blocksize) * blocksize;
+       EVP_Cipher(c, p, p, i);
+       p += i - blocksize;
+       len -= i;
+       memcpy(ivec2, p, blocksize);
+
+       for (i = 0; i < len; i++)
+           tmp[i] = p[i + blocksize] ^ ivec2[i];
+       for (; i < blocksize; i++)
+           tmp[i] = 0 ^ ivec2[i];
+
+       EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
+       EVP_Cipher(c, p, tmp, blocksize);
+
+       memcpy(p + blocksize, ivec2, len);
+       if (ivec)
+           memcpy(ivec, p, blocksize);
+    } else {
+       char tmp2[EVP_MAX_BLOCK_LENGTH], tmp3[EVP_MAX_BLOCK_LENGTH];
+
+       p = data;
+       if (len > blocksize * 2) {
+           /* remove last two blocks and round up, decrypt this with cbc, then do cts dance */
+           i = ((((len - blocksize * 2) + blocksize - 1) / blocksize) * blocksize);
+           memcpy(ivec2, p + i - blocksize, blocksize);
+           EVP_Cipher(c, p, p, i);
+           p += i;
+           len -= i + blocksize;
+       } else {
+           if (ivec)
+               memcpy(ivec2, ivec, blocksize);
+           else
+               memcpy(ivec2, zero_ivec, blocksize);
+           len -= blocksize;
+       }
+
+       memcpy(tmp, p, blocksize);
+       EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
+       EVP_Cipher(c, tmp2, p, blocksize);
+
+       memcpy(tmp3, p + blocksize, len);
+       memcpy(tmp3 + len, tmp2 + len, blocksize - len); /* xor 0 */
+
+       for (i = 0; i < len; i++)
+           p[i + blocksize] = tmp2[i] ^ tmp3[i];
+
+       EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
+       EVP_Cipher(c, p, tmp3, blocksize);
+
+       for (i = 0; i < blocksize; i++)
+           p[i] ^= ivec2[i];
+       if (ivec)
+           memcpy(ivec, tmp, blocksize);
+    }
+    return 0;
+}
diff --git a/src/external/heimdal/krb5/crypto.c b/src/external/heimdal/krb5/crypto.c
new file mode 100644 (file)
index 0000000..33f92e3
--- /dev/null
@@ -0,0 +1,2648 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define KRB5_DEPRECATED
+
+#include "krb5_locl.h"
+
+#ifndef HEIMDAL_SMALLER
+#define DES3_OLD_ENCTYPE 1
+#endif
+
+static krb5_error_code _get_derived_key(krb5_context, krb5_crypto,
+                                       unsigned, struct key_data**);
+static struct key_data *_new_derived_key(krb5_crypto crypto, unsigned usage);
+
+static void free_key_schedule(krb5_context,
+                             struct key_data *,
+                             struct encryption_type *);
+
+/************************************************************
+ *                                                          *
+ ************************************************************/
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_enctype_keysize(krb5_context context,
+                    krb5_enctype type,
+                    size_t *keysize)
+{
+    struct encryption_type *et = _krb5_find_enctype(type);
+    if(et == NULL) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              N_("encryption type %d not supported", ""),
+                              type);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    *keysize = et->keytype->size;
+    return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_enctype_keybits(krb5_context context,
+                    krb5_enctype type,
+                    size_t *keybits)
+{
+    struct encryption_type *et = _krb5_find_enctype(type);
+    if(et == NULL) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              "encryption type %d not supported",
+                              type);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    *keybits = et->keytype->bits;
+    return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_generate_random_keyblock(krb5_context context,
+                             krb5_enctype type,
+                             krb5_keyblock *key)
+{
+    krb5_error_code ret;
+    struct encryption_type *et = _krb5_find_enctype(type);
+    if(et == NULL) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              N_("encryption type %d not supported", ""),
+                              type);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    ret = krb5_data_alloc(&key->keyvalue, et->keytype->size);
+    if(ret)
+       return ret;
+    key->keytype = type;
+    if(et->keytype->random_key)
+       (*et->keytype->random_key)(context, key);
+    else
+       krb5_generate_random_block(key->keyvalue.data,
+                                  key->keyvalue.length);
+    return 0;
+}
+
+static krb5_error_code
+_key_schedule(krb5_context context,
+             struct key_data *key)
+{
+    krb5_error_code ret;
+    struct encryption_type *et = _krb5_find_enctype(key->key->keytype);
+    struct key_type *kt;
+
+    if (et == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                               N_("encryption type %d not supported", ""),
+                               key->key->keytype);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+
+    kt = et->keytype;
+
+    if(kt->schedule == NULL)
+       return 0;
+    if (key->schedule != NULL)
+       return 0;
+    ALLOC(key->schedule, 1);
+    if(key->schedule == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    ret = krb5_data_alloc(key->schedule, kt->schedule_size);
+    if(ret) {
+       free(key->schedule);
+       key->schedule = NULL;
+       return ret;
+    }
+    (*kt->schedule)(context, kt, key);
+    return 0;
+}
+
+/************************************************************
+ *                                                          *
+ ************************************************************/
+
+static krb5_error_code
+SHA1_checksum(krb5_context context,
+             struct key_data *key,
+             const void *data,
+             size_t len,
+             unsigned usage,
+             Checksum *C)
+{
+    if (EVP_Digest(data, len, C->checksum.data, NULL, EVP_sha1(), NULL) != 1)
+       krb5_abortx(context, "sha1 checksum failed");
+    return 0;
+}
+
+/* HMAC according to RFC2104 */
+krb5_error_code
+_krb5_internal_hmac(krb5_context context,
+                   struct checksum_type *cm,
+                   const void *data,
+                   size_t len,
+                   unsigned usage,
+                   struct key_data *keyblock,
+                   Checksum *result)
+{
+    unsigned char *ipad, *opad;
+    unsigned char *key;
+    size_t key_len;
+    int i;
+
+    ipad = malloc(cm->blocksize + len);
+    if (ipad == NULL)
+       return ENOMEM;
+    opad = malloc(cm->blocksize + cm->checksumsize);
+    if (opad == NULL) {
+       free(ipad);
+       return ENOMEM;
+    }
+    memset(ipad, 0x36, cm->blocksize);
+    memset(opad, 0x5c, cm->blocksize);
+
+    if(keyblock->key->keyvalue.length > cm->blocksize){
+       (*cm->checksum)(context,
+                       keyblock,
+                       keyblock->key->keyvalue.data,
+                       keyblock->key->keyvalue.length,
+                       usage,
+                       result);
+       key = result->checksum.data;
+       key_len = result->checksum.length;
+    } else {
+       key = keyblock->key->keyvalue.data;
+       key_len = keyblock->key->keyvalue.length;
+    }
+    for(i = 0; i < key_len; i++){
+       ipad[i] ^= key[i];
+       opad[i] ^= key[i];
+    }
+    memcpy(ipad + cm->blocksize, data, len);
+    (*cm->checksum)(context, keyblock, ipad, cm->blocksize + len,
+                   usage, result);
+    memcpy(opad + cm->blocksize, result->checksum.data,
+          result->checksum.length);
+    (*cm->checksum)(context, keyblock, opad,
+                   cm->blocksize + cm->checksumsize, usage, result);
+    memset(ipad, 0, cm->blocksize + len);
+    free(ipad);
+    memset(opad, 0, cm->blocksize + cm->checksumsize);
+    free(opad);
+
+    return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_hmac(krb5_context context,
+         krb5_cksumtype cktype,
+         const void *data,
+         size_t len,
+         unsigned usage,
+         krb5_keyblock *key,
+         Checksum *result)
+{
+    struct checksum_type *c = _krb5_find_checksum(cktype);
+    struct key_data kd;
+    krb5_error_code ret;
+
+    if (c == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("checksum type %d not supported", ""),
+                               cktype);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+
+    kd.key = key;
+    kd.schedule = NULL;
+
+    ret = _krb5_internal_hmac(context, c, data, len, usage, &kd, result);
+
+    if (kd.schedule)
+       krb5_free_data(context, kd.schedule);
+
+    return ret;
+}
+
+krb5_error_code
+_krb5_SP_HMAC_SHA1_checksum(krb5_context context,
+                           struct key_data *key,
+                           const void *data,
+                           size_t len,
+                           unsigned usage,
+                           Checksum *result)
+{
+    struct checksum_type *c = _krb5_find_checksum(CKSUMTYPE_SHA1);
+    Checksum res;
+    char sha1_data[20];
+    krb5_error_code ret;
+
+    res.checksum.data = sha1_data;
+    res.checksum.length = sizeof(sha1_data);
+
+    ret = _krb5_internal_hmac(context, c, data, len, usage, key, &res);
+    if (ret)
+       krb5_abortx(context, "hmac failed");
+    memcpy(result->checksum.data, res.checksum.data, result->checksum.length);
+    return 0;
+}
+
+struct checksum_type _krb5_checksum_sha1 = {
+    CKSUMTYPE_SHA1,
+    "sha1",
+    64,
+    20,
+    F_CPROOF,
+    SHA1_checksum,
+    NULL
+};
+
+struct checksum_type *
+_krb5_find_checksum(krb5_cksumtype type)
+{
+    int i;
+    for(i = 0; i < _krb5_num_checksums; i++)
+       if(_krb5_checksum_types[i]->type == type)
+           return _krb5_checksum_types[i];
+    return NULL;
+}
+
+static krb5_error_code
+get_checksum_key(krb5_context context,
+                krb5_crypto crypto,
+                unsigned usage,  /* not krb5_key_usage */
+                struct checksum_type *ct,
+                struct key_data **key)
+{
+    krb5_error_code ret = 0;
+
+    if(ct->flags & F_DERIVED)
+       ret = _get_derived_key(context, crypto, usage, key);
+    else if(ct->flags & F_VARIANT) {
+       int i;
+
+       *key = _new_derived_key(crypto, 0xff/* KRB5_KU_RFC1510_VARIANT */);
+       if(*key == NULL) {
+           krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+           return ENOMEM;
+       }
+       ret = krb5_copy_keyblock(context, crypto->key.key, &(*key)->key);
+       if(ret)
+           return ret;
+       for(i = 0; i < (*key)->key->keyvalue.length; i++)
+           ((unsigned char*)(*key)->key->keyvalue.data)[i] ^= 0xF0;
+    } else {
+       *key = &crypto->key;
+    }
+    if(ret == 0)
+       ret = _key_schedule(context, *key);
+    return ret;
+}
+
+static krb5_error_code
+create_checksum (krb5_context context,
+                struct checksum_type *ct,
+                krb5_crypto crypto,
+                unsigned usage,
+                void *data,
+                size_t len,
+                Checksum *result)
+{
+    krb5_error_code ret;
+    struct key_data *dkey;
+    int keyed_checksum;
+
+    if (ct->flags & F_DISABLED) {
+       krb5_clear_error_message (context);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+    keyed_checksum = (ct->flags & F_KEYED) != 0;
+    if(keyed_checksum && crypto == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("Checksum type %s is keyed but no "
+                                  "crypto context (key) was passed in", ""),
+                               ct->name);
+       return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
+    }
+    if(keyed_checksum) {
+       ret = get_checksum_key(context, crypto, usage, ct, &dkey);
+       if (ret)
+           return ret;
+    } else
+       dkey = NULL;
+    result->cksumtype = ct->type;
+    ret = krb5_data_alloc(&result->checksum, ct->checksumsize);
+    if (ret)
+       return (ret);
+    return (*ct->checksum)(context, dkey, data, len, usage, result);
+}
+
+static int
+arcfour_checksum_p(struct checksum_type *ct, krb5_crypto crypto)
+{
+    return (ct->type == CKSUMTYPE_HMAC_MD5) &&
+       (crypto->key.key->keytype == KEYTYPE_ARCFOUR);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_create_checksum(krb5_context context,
+                    krb5_crypto crypto,
+                    krb5_key_usage usage,
+                    int type,
+                    void *data,
+                    size_t len,
+                    Checksum *result)
+{
+    struct checksum_type *ct = NULL;
+    unsigned keyusage;
+
+    /* type 0 -> pick from crypto */
+    if (type) {
+       ct = _krb5_find_checksum(type);
+    } else if (crypto) {
+       ct = crypto->et->keyed_checksum;
+       if (ct == NULL)
+           ct = crypto->et->checksum;
+    }
+
+    if(ct == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("checksum type %d not supported", ""),
+                               type);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+
+    if (arcfour_checksum_p(ct, crypto)) {
+       keyusage = usage;
+       _krb5_usage2arcfour(context, &keyusage);
+    } else
+       keyusage = CHECKSUM_USAGE(usage);
+
+    return create_checksum(context, ct, crypto, keyusage,
+                          data, len, result);
+}
+
+static krb5_error_code
+verify_checksum(krb5_context context,
+               krb5_crypto crypto,
+               unsigned usage, /* not krb5_key_usage */
+               void *data,
+               size_t len,
+               Checksum *cksum)
+{
+    krb5_error_code ret;
+    struct key_data *dkey;
+    int keyed_checksum;
+    Checksum c;
+    struct checksum_type *ct;
+
+    ct = _krb5_find_checksum(cksum->cksumtype);
+    if (ct == NULL || (ct->flags & F_DISABLED)) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("checksum type %d not supported", ""),
+                               cksum->cksumtype);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+    if(ct->checksumsize != cksum->checksum.length) {
+       krb5_clear_error_message (context);
+       krb5_set_error_message(context, KRB5KRB_AP_ERR_BAD_INTEGRITY,
+                              N_("Decrypt integrity check failed for checksum type %s, "
+                                 "length was %u, expected %u", ""),
+                              ct->name, (unsigned)cksum->checksum.length,
+                              (unsigned)ct->checksumsize);
+
+       return KRB5KRB_AP_ERR_BAD_INTEGRITY; /* XXX */
+    }
+    keyed_checksum = (ct->flags & F_KEYED) != 0;
+    if(keyed_checksum) {
+       struct checksum_type *kct;
+       if (crypto == NULL) {
+           krb5_set_error_message(context, KRB5_PROG_SUMTYPE_NOSUPP,
+                                  N_("Checksum type %s is keyed but no "
+                                     "crypto context (key) was passed in", ""),
+                                  ct->name);
+           return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
+       }
+       kct = crypto->et->keyed_checksum;
+       if (kct != NULL && kct->type != ct->type) {
+           krb5_set_error_message(context, KRB5_PROG_SUMTYPE_NOSUPP,
+                                  N_("Checksum type %s is keyed, but "
+                                     "the key type %s passed didnt have that checksum "
+                                     "type as the keyed type", ""),
+                                   ct->name, crypto->et->name);
+           return KRB5_PROG_SUMTYPE_NOSUPP; /* XXX */
+       }
+
+       ret = get_checksum_key(context, crypto, usage, ct, &dkey);
+       if (ret)
+           return ret;
+    } else
+       dkey = NULL;
+
+    /*
+     * If checksum have a verify function, lets use that instead of
+     * calling ->checksum and then compare result.
+     */
+
+    if(ct->verify) {
+       ret = (*ct->verify)(context, dkey, data, len, usage, cksum);
+       if (ret)
+           krb5_set_error_message(context, ret,
+                                  N_("Decrypt integrity check failed for checksum "
+                                     "type %s, key type %s", ""),
+                                  ct->name, (crypto != NULL)? crypto->et->name : "(none)");
+       return ret;
+    }
+
+    ret = krb5_data_alloc (&c.checksum, ct->checksumsize);
+    if (ret)
+       return ret;
+
+    ret = (*ct->checksum)(context, dkey, data, len, usage, &c);
+    if (ret) {
+       krb5_data_free(&c.checksum);
+       return ret;
+    }
+
+    if(krb5_data_ct_cmp(&c.checksum, &cksum->checksum) != 0) {
+       ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+       krb5_set_error_message(context, ret,
+                              N_("Decrypt integrity check failed for checksum "
+                                 "type %s, key type %s", ""),
+                              ct->name, crypto ? crypto->et->name : "(unkeyed)");
+    } else {
+       ret = 0;
+    }
+    krb5_data_free (&c.checksum);
+    return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_checksum(krb5_context context,
+                    krb5_crypto crypto,
+                    krb5_key_usage usage,
+                    void *data,
+                    size_t len,
+                    Checksum *cksum)
+{
+    struct checksum_type *ct;
+    unsigned keyusage;
+
+    ct = _krb5_find_checksum(cksum->cksumtype);
+    if(ct == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("checksum type %d not supported", ""),
+                               cksum->cksumtype);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+
+    if (arcfour_checksum_p(ct, crypto)) {
+       keyusage = usage;
+       _krb5_usage2arcfour(context, &keyusage);
+    } else
+       keyusage = CHECKSUM_USAGE(usage);
+
+    return verify_checksum(context, crypto, keyusage,
+                          data, len, cksum);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_get_checksum_type(krb5_context context,
+                              krb5_crypto crypto,
+                             krb5_cksumtype *type)
+{
+    struct checksum_type *ct = NULL;
+
+    if (crypto != NULL) {
+        ct = crypto->et->keyed_checksum;
+        if (ct == NULL)
+            ct = crypto->et->checksum;
+    }
+
+    if (ct == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("checksum type not found", ""));
+        return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+
+    *type = ct->type;
+
+    return 0;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_checksumsize(krb5_context context,
+                 krb5_cksumtype type,
+                 size_t *size)
+{
+    struct checksum_type *ct = _krb5_find_checksum(type);
+    if(ct == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("checksum type %d not supported", ""),
+                               type);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+    *size = ct->checksumsize;
+    return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_checksum_is_keyed(krb5_context context,
+                      krb5_cksumtype type)
+{
+    struct checksum_type *ct = _krb5_find_checksum(type);
+    if(ct == NULL) {
+       if (context)
+           krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                                   N_("checksum type %d not supported", ""),
+                                   type);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+    return ct->flags & F_KEYED;
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_checksum_is_collision_proof(krb5_context context,
+                                krb5_cksumtype type)
+{
+    struct checksum_type *ct = _krb5_find_checksum(type);
+    if(ct == NULL) {
+       if (context)
+           krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                                   N_("checksum type %d not supported", ""),
+                                   type);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+    return ct->flags & F_CPROOF;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_checksum_disable(krb5_context context,
+                     krb5_cksumtype type)
+{
+    struct checksum_type *ct = _krb5_find_checksum(type);
+    if(ct == NULL) {
+       if (context)
+           krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                                   N_("checksum type %d not supported", ""),
+                                   type);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+    ct->flags |= F_DISABLED;
+    return 0;
+}
+
+/************************************************************
+ *                                                          *
+ ************************************************************/
+
+struct encryption_type *
+_krb5_find_enctype(krb5_enctype type)
+{
+    int i;
+    for(i = 0; i < _krb5_num_etypes; i++)
+       if(_krb5_etypes[i]->type == type)
+           return _krb5_etypes[i];
+    return NULL;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_enctype_to_string(krb5_context context,
+                      krb5_enctype etype,
+                      char **string)
+{
+    struct encryption_type *e;
+    e = _krb5_find_enctype(etype);
+    if(e == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                               N_("encryption type %d not supported", ""),
+                               etype);
+       *string = NULL;
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    *string = strdup(e->name);
+    if(*string == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_enctype(krb5_context context,
+                      const char *string,
+                      krb5_enctype *etype)
+{
+    int i;
+    for(i = 0; i < _krb5_num_etypes; i++)
+       if(strcasecmp(_krb5_etypes[i]->name, string) == 0){
+           *etype = _krb5_etypes[i]->type;
+           return 0;
+       }
+    krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                           N_("encryption type %s not supported", ""),
+                           string);
+    return KRB5_PROG_ETYPE_NOSUPP;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_enctype_to_keytype(krb5_context context,
+                       krb5_enctype etype,
+                       krb5_keytype *keytype)
+{
+    struct encryption_type *e = _krb5_find_enctype(etype);
+    if(e == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                               N_("encryption type %d not supported", ""),
+                               etype);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    *keytype = e->keytype->type; /* XXX */
+    return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_enctype_valid(krb5_context context,
+                  krb5_enctype etype)
+{
+    struct encryption_type *e = _krb5_find_enctype(etype);
+    if(e == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                               N_("encryption type %d not supported", ""),
+                               etype);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    if (e->flags & F_DISABLED) {
+       krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                               N_("encryption type %s is disabled", ""),
+                               e->name);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    return 0;
+}
+
+/**
+ * Return the coresponding encryption type for a checksum type.
+ *
+ * @param context Kerberos context
+ * @param ctype The checksum type to get the result enctype for
+ * @param etype The returned encryption, when the matching etype is
+ * not found, etype is set to ETYPE_NULL.
+ *
+ * @return Return an error code for an failure or 0 on success.
+ * @ingroup krb5_crypto
+ */
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_cksumtype_to_enctype(krb5_context context,
+                         krb5_cksumtype ctype,
+                         krb5_enctype *etype)
+{
+    int i;
+
+    *etype = ETYPE_NULL;
+
+    for(i = 0; i < _krb5_num_etypes; i++) {
+       if(_krb5_etypes[i]->keyed_checksum &&
+          _krb5_etypes[i]->keyed_checksum->type == ctype)
+           {
+               *etype = _krb5_etypes[i]->type;
+               return 0;
+           }
+    }
+
+    krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                           N_("checksum type %d not supported", ""),
+                           (int)ctype);
+    return KRB5_PROG_SUMTYPE_NOSUPP;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_cksumtype_valid(krb5_context context,
+                    krb5_cksumtype ctype)
+{
+    struct checksum_type *c = _krb5_find_checksum(ctype);
+    if (c == NULL) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("checksum type %d not supported", ""),
+                               ctype);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+    if (c->flags & F_DISABLED) {
+       krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP,
+                               N_("checksum type %s is disabled", ""),
+                               c->name);
+       return KRB5_PROG_SUMTYPE_NOSUPP;
+    }
+    return 0;
+}
+
+
+static krb5_boolean
+derived_crypto(krb5_context context,
+              krb5_crypto crypto)
+{
+    return (crypto->et->flags & F_DERIVED) != 0;
+}
+
+static krb5_boolean
+special_crypto(krb5_context context,
+              krb5_crypto crypto)
+{
+    return (crypto->et->flags & F_SPECIAL) != 0;
+}
+
+#define CHECKSUMSIZE(C) ((C)->checksumsize)
+#define CHECKSUMTYPE(C) ((C)->type)
+
+static krb5_error_code
+encrypt_internal_derived(krb5_context context,
+                        krb5_crypto crypto,
+                        unsigned usage,
+                        const void *data,
+                        size_t len,
+                        krb5_data *result,
+                        void *ivec)
+{
+    size_t sz, block_sz, checksum_sz, total_sz;
+    Checksum cksum;
+    unsigned char *p, *q;
+    krb5_error_code ret;
+    struct key_data *dkey;
+    const struct encryption_type *et = crypto->et;
+
+    checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
+
+    sz = et->confoundersize + len;
+    block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+    total_sz = block_sz + checksum_sz;
+    p = calloc(1, total_sz);
+    if(p == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+
+    q = p;
+    krb5_generate_random_block(q, et->confoundersize); /* XXX */
+    q += et->confoundersize;
+    memcpy(q, data, len);
+
+    ret = create_checksum(context,
+                         et->keyed_checksum,
+                         crypto,
+                         INTEGRITY_USAGE(usage),
+                         p,
+                         block_sz,
+                         &cksum);
+    if(ret == 0 && cksum.checksum.length != checksum_sz) {
+       free_Checksum (&cksum);
+       krb5_clear_error_message (context);
+       ret = KRB5_CRYPTO_INTERNAL;
+    }
+    if(ret)
+       goto fail;
+    memcpy(p + block_sz, cksum.checksum.data, cksum.checksum.length);
+    free_Checksum (&cksum);
+    ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+    if(ret)
+       goto fail;
+    ret = _key_schedule(context, dkey);
+    if(ret)
+       goto fail;
+    ret = (*et->encrypt)(context, dkey, p, block_sz, 1, usage, ivec);
+    if (ret)
+       goto fail;
+    result->data = p;
+    result->length = total_sz;
+    return 0;
+ fail:
+    memset(p, 0, total_sz);
+    free(p);
+    return ret;
+}
+
+
+static krb5_error_code
+encrypt_internal(krb5_context context,
+                krb5_crypto crypto,
+                const void *data,
+                size_t len,
+                krb5_data *result,
+                void *ivec)
+{
+    size_t sz, block_sz, checksum_sz;
+    Checksum cksum;
+    unsigned char *p, *q;
+    krb5_error_code ret;
+    const struct encryption_type *et = crypto->et;
+
+    checksum_sz = CHECKSUMSIZE(et->checksum);
+
+    sz = et->confoundersize + checksum_sz + len;
+    block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+    p = calloc(1, block_sz);
+    if(p == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+
+    q = p;
+    krb5_generate_random_block(q, et->confoundersize); /* XXX */
+    q += et->confoundersize;
+    memset(q, 0, checksum_sz);
+    q += checksum_sz;
+    memcpy(q, data, len);
+
+    ret = create_checksum(context,
+                         et->checksum,
+                         crypto,
+                         0,
+                         p,
+                         block_sz,
+                         &cksum);
+    if(ret == 0 && cksum.checksum.length != checksum_sz) {
+       krb5_clear_error_message (context);
+       free_Checksum(&cksum);
+       ret = KRB5_CRYPTO_INTERNAL;
+    }
+    if(ret)
+       goto fail;
+    memcpy(p + et->confoundersize, cksum.checksum.data, cksum.checksum.length);
+    free_Checksum(&cksum);
+    ret = _key_schedule(context, &crypto->key);
+    if(ret)
+       goto fail;
+    ret = (*et->encrypt)(context, &crypto->key, p, block_sz, 1, 0, ivec);
+    if (ret) {
+       memset(p, 0, block_sz);
+       free(p);
+       return ret;
+    }
+    result->data = p;
+    result->length = block_sz;
+    return 0;
+ fail:
+    memset(p, 0, block_sz);
+    free(p);
+    return ret;
+}
+
+static krb5_error_code
+encrypt_internal_special(krb5_context context,
+                        krb5_crypto crypto,
+                        int usage,
+                        const void *data,
+                        size_t len,
+                        krb5_data *result,
+                        void *ivec)
+{
+    struct encryption_type *et = crypto->et;
+    size_t cksum_sz = CHECKSUMSIZE(et->checksum);
+    size_t sz = len + cksum_sz + et->confoundersize;
+    char *tmp, *p;
+    krb5_error_code ret;
+
+    tmp = malloc (sz);
+    if (tmp == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    p = tmp;
+    memset (p, 0, cksum_sz);
+    p += cksum_sz;
+    krb5_generate_random_block(p, et->confoundersize);
+    p += et->confoundersize;
+    memcpy (p, data, len);
+    ret = (*et->encrypt)(context, &crypto->key, tmp, sz, TRUE, usage, ivec);
+    if (ret) {
+       memset(tmp, 0, sz);
+       free(tmp);
+       return ret;
+    }
+    result->data   = tmp;
+    result->length = sz;
+    return 0;
+}
+
+static krb5_error_code
+decrypt_internal_derived(krb5_context context,
+                        krb5_crypto crypto,
+                        unsigned usage,
+                        void *data,
+                        size_t len,
+                        krb5_data *result,
+                        void *ivec)
+{
+    size_t checksum_sz;
+    Checksum cksum;
+    unsigned char *p;
+    krb5_error_code ret;
+    struct key_data *dkey;
+    struct encryption_type *et = crypto->et;
+    unsigned long l;
+
+    checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
+    if (len < checksum_sz + et->confoundersize) {
+       krb5_set_error_message(context, KRB5_BAD_MSIZE,
+                              N_("Encrypted data shorter then "
+                                 "checksum + confunder", ""));
+       return KRB5_BAD_MSIZE;
+    }
+
+    if (((len - checksum_sz) % et->padsize) != 0) {
+       krb5_clear_error_message(context);
+       return KRB5_BAD_MSIZE;
+    }
+
+    p = malloc(len);
+    if(len != 0 && p == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    memcpy(p, data, len);
+
+    len -= checksum_sz;
+
+    ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+    ret = _key_schedule(context, dkey);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+    ret = (*et->encrypt)(context, dkey, p, len, 0, usage, ivec);
+    if (ret) {
+       free(p);
+       return ret;
+    }
+
+    cksum.checksum.data   = p + len;
+    cksum.checksum.length = checksum_sz;
+    cksum.cksumtype       = CHECKSUMTYPE(et->keyed_checksum);
+
+    ret = verify_checksum(context,
+                         crypto,
+                         INTEGRITY_USAGE(usage),
+                         p,
+                         len,
+                         &cksum);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+    l = len - et->confoundersize;
+    memmove(p, p + et->confoundersize, l);
+    result->data = realloc(p, l);
+    if(result->data == NULL && l != 0) {
+       free(p);
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    result->length = l;
+    return 0;
+}
+
+static krb5_error_code
+decrypt_internal(krb5_context context,
+                krb5_crypto crypto,
+                void *data,
+                size_t len,
+                krb5_data *result,
+                void *ivec)
+{
+    krb5_error_code ret;
+    unsigned char *p;
+    Checksum cksum;
+    size_t checksum_sz, l;
+    struct encryption_type *et = crypto->et;
+
+    if ((len % et->padsize) != 0) {
+       krb5_clear_error_message(context);
+       return KRB5_BAD_MSIZE;
+    }
+    checksum_sz = CHECKSUMSIZE(et->checksum);
+    if (len < checksum_sz + et->confoundersize) {
+       krb5_set_error_message(context, KRB5_BAD_MSIZE,
+                              N_("Encrypted data shorter then "
+                                 "checksum + confunder", ""));
+       return KRB5_BAD_MSIZE;
+    }
+
+    p = malloc(len);
+    if(len != 0 && p == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    memcpy(p, data, len);
+
+    ret = _key_schedule(context, &crypto->key);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+    ret = (*et->encrypt)(context, &crypto->key, p, len, 0, 0, ivec);
+    if (ret) {
+       free(p);
+       return ret;
+    }
+    ret = krb5_data_copy(&cksum.checksum, p + et->confoundersize, checksum_sz);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+    memset(p + et->confoundersize, 0, checksum_sz);
+    cksum.cksumtype = CHECKSUMTYPE(et->checksum);
+    ret = verify_checksum(context, NULL, 0, p, len, &cksum);
+    free_Checksum(&cksum);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+    l = len - et->confoundersize - checksum_sz;
+    memmove(p, p + et->confoundersize + checksum_sz, l);
+    result->data = realloc(p, l);
+    if(result->data == NULL && l != 0) {
+       free(p);
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    result->length = l;
+    return 0;
+}
+
+static krb5_error_code
+decrypt_internal_special(krb5_context context,
+                        krb5_crypto crypto,
+                        int usage,
+                        void *data,
+                        size_t len,
+                        krb5_data *result,
+                        void *ivec)
+{
+    struct encryption_type *et = crypto->et;
+    size_t cksum_sz = CHECKSUMSIZE(et->checksum);
+    size_t sz = len - cksum_sz - et->confoundersize;
+    unsigned char *p;
+    krb5_error_code ret;
+
+    if ((len % et->padsize) != 0) {
+       krb5_clear_error_message(context);
+       return KRB5_BAD_MSIZE;
+    }
+    if (len < cksum_sz + et->confoundersize) {
+       krb5_set_error_message(context, KRB5_BAD_MSIZE,
+                              N_("Encrypted data shorter then "
+                                 "checksum + confunder", ""));
+       return KRB5_BAD_MSIZE;
+    }
+
+    p = malloc (len);
+    if (p == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    memcpy(p, data, len);
+
+    ret = (*et->encrypt)(context, &crypto->key, p, len, FALSE, usage, ivec);
+    if (ret) {
+       free(p);
+       return ret;
+    }
+
+    memmove (p, p + cksum_sz + et->confoundersize, sz);
+    result->data = realloc(p, sz);
+    if(result->data == NULL && sz != 0) {
+       free(p);
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    result->length = sz;
+    return 0;
+}
+
+static krb5_crypto_iov *
+find_iv(krb5_crypto_iov *data, int num_data, int type)
+{
+    int i;
+    for (i = 0; i < num_data; i++)
+       if (data[i].flags == type)
+           return &data[i];
+    return NULL;
+}
+
+/**
+ * Inline encrypt a kerberos message
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param ivec initial cbc/cts vector
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ *
+ * Kerberos encrypted data look like this:
+ *
+ * 1. KRB5_CRYPTO_TYPE_HEADER
+ * 2. array [1,...] KRB5_CRYPTO_TYPE_DATA and array [0,...]
+ *    KRB5_CRYPTO_TYPE_SIGN_ONLY in any order, however the receiver
+ *    have to aware of the order. KRB5_CRYPTO_TYPE_SIGN_ONLY is
+ *    commonly used headers and trailers.
+ * 3. KRB5_CRYPTO_TYPE_PADDING, at least on padsize long if padsize > 1
+ * 4. KRB5_CRYPTO_TYPE_TRAILER
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_encrypt_iov_ivec(krb5_context context,
+                     krb5_crypto crypto,
+                     unsigned usage,
+                     krb5_crypto_iov *data,
+                     int num_data,
+                     void *ivec)
+{
+    size_t headersz, trailersz, len;
+    int i;
+    size_t sz, block_sz, pad_sz;
+    Checksum cksum;
+    unsigned char *p, *q;
+    krb5_error_code ret;
+    struct key_data *dkey;
+    const struct encryption_type *et = crypto->et;
+    krb5_crypto_iov *tiv, *piv, *hiv;
+
+    if (num_data < 0) {
+        krb5_clear_error_message(context);
+       return KRB5_CRYPTO_INTERNAL;
+    }
+
+    if(!derived_crypto(context, crypto)) {
+       krb5_clear_error_message(context);
+       return KRB5_CRYPTO_INTERNAL;
+    }
+
+    headersz = et->confoundersize;
+    trailersz = CHECKSUMSIZE(et->keyed_checksum);
+
+    for (len = 0, i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+           continue;
+       len += data[i].data.length;
+    }
+
+    sz = headersz + len;
+    block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+
+    pad_sz = block_sz - sz;
+
+    /* header */
+
+    hiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+    if (hiv == NULL || hiv->data.length != headersz)
+       return KRB5_BAD_MSIZE;
+
+    krb5_generate_random_block(hiv->data.data, hiv->data.length);
+
+    /* padding */
+    piv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
+    /* its ok to have no TYPE_PADDING if there is no padding */
+    if (piv == NULL && pad_sz != 0)
+       return KRB5_BAD_MSIZE;
+    if (piv) {
+       if (piv->data.length < pad_sz)
+           return KRB5_BAD_MSIZE;
+       piv->data.length = pad_sz;
+       if (pad_sz)
+           memset(piv->data.data, pad_sz, pad_sz);
+       else
+           piv = NULL;
+    }
+
+    /* trailer */
+    tiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+    if (tiv == NULL || tiv->data.length != trailersz)
+       return KRB5_BAD_MSIZE;
+
+    /*
+     * XXX replace with EVP_Sign? at least make create_checksum an iov
+     * function.
+     * XXX CTS EVP is broken, can't handle multi buffers :(
+     */
+
+    len = block_sz;
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+           continue;
+       len += data[i].data.length;
+    }
+
+    p = q = malloc(len);
+
+    memcpy(q, hiv->data.data, hiv->data.length);
+    q += hiv->data.length;
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+           data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+           continue;
+       memcpy(q, data[i].data.data, data[i].data.length);
+       q += data[i].data.length;
+    }
+    if (piv)
+       memset(q, 0, piv->data.length);
+
+    ret = create_checksum(context,
+                         et->keyed_checksum,
+                         crypto,
+                         INTEGRITY_USAGE(usage),
+                         p,
+                         len,
+                         &cksum);
+    free(p);
+    if(ret == 0 && cksum.checksum.length != trailersz) {
+       free_Checksum (&cksum);
+       krb5_clear_error_message (context);
+       ret = KRB5_CRYPTO_INTERNAL;
+    }
+    if(ret)
+       return ret;
+
+    /* save cksum at end */
+    memcpy(tiv->data.data, cksum.checksum.data, cksum.checksum.length);
+    free_Checksum (&cksum);
+
+    /* XXX replace with EVP_Cipher */
+    p = q = malloc(block_sz);
+    if(p == NULL)
+       return ENOMEM;
+
+    memcpy(q, hiv->data.data, hiv->data.length);
+    q += hiv->data.length;
+
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+           continue;
+       memcpy(q, data[i].data.data, data[i].data.length);
+       q += data[i].data.length;
+    }
+    if (piv)
+       memset(q, 0, piv->data.length);
+
+
+    ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+    ret = _key_schedule(context, dkey);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+
+    ret = (*et->encrypt)(context, dkey, p, block_sz, 1, usage, ivec);
+    if (ret) {
+       free(p);
+       return ret;
+    }
+
+    /* now copy data back to buffers */
+    q = p;
+
+    memcpy(hiv->data.data, q, hiv->data.length);
+    q += hiv->data.length;
+
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+           continue;
+       memcpy(data[i].data.data, q, data[i].data.length);
+       q += data[i].data.length;
+    }
+    if (piv)
+       memcpy(piv->data.data, q, pad_sz);
+
+    free(p);
+
+    return ret;
+}
+
+/**
+ * Inline decrypt a Kerberos message.
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param ivec initial cbc/cts vector
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ *
+ * 1. KRB5_CRYPTO_TYPE_HEADER
+ * 2. one KRB5_CRYPTO_TYPE_DATA and array [0,...] of KRB5_CRYPTO_TYPE_SIGN_ONLY in
+ *  any order, however the receiver have to aware of the
+ *  order. KRB5_CRYPTO_TYPE_SIGN_ONLY is commonly used unencrypoted
+ *  protocol headers and trailers. The output data will be of same
+ *  size as the input data or shorter.
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_decrypt_iov_ivec(krb5_context context,
+                     krb5_crypto crypto,
+                     unsigned usage,
+                     krb5_crypto_iov *data,
+                     unsigned int num_data,
+                     void *ivec)
+{
+    unsigned int i;
+    size_t headersz, trailersz, len;
+    Checksum cksum;
+    unsigned char *p, *q;
+    krb5_error_code ret;
+    struct key_data *dkey;
+    struct encryption_type *et = crypto->et;
+    krb5_crypto_iov *tiv, *hiv;
+
+    if (num_data < 0) {
+        krb5_clear_error_message(context);
+       return KRB5_CRYPTO_INTERNAL;
+    }
+
+    if(!derived_crypto(context, crypto)) {
+       krb5_clear_error_message(context);
+       return KRB5_CRYPTO_INTERNAL;
+    }
+
+    headersz = et->confoundersize;
+
+    hiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+    if (hiv == NULL || hiv->data.length != headersz)
+       return KRB5_BAD_MSIZE;
+
+    /* trailer */
+    trailersz = CHECKSUMSIZE(et->keyed_checksum);
+
+    tiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+    if (tiv->data.length != trailersz)
+       return KRB5_BAD_MSIZE;
+
+    /* Find length of data we will decrypt */
+
+    len = headersz;
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+           continue;
+       len += data[i].data.length;
+    }
+
+    if ((len % et->padsize) != 0) {
+       krb5_clear_error_message(context);
+       return KRB5_BAD_MSIZE;
+    }
+
+    /* XXX replace with EVP_Cipher */
+
+    p = q = malloc(len);
+    if (p == NULL)
+       return ENOMEM;
+
+    memcpy(q, hiv->data.data, hiv->data.length);
+    q += hiv->data.length;
+
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+           continue;
+       memcpy(q, data[i].data.data, data[i].data.length);
+       q += data[i].data.length;
+    }
+
+    ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+    ret = _key_schedule(context, dkey);
+    if(ret) {
+       free(p);
+       return ret;
+    }
+
+    ret = (*et->encrypt)(context, dkey, p, len, 0, usage, ivec);
+    if (ret) {
+       free(p);
+       return ret;
+    }
+
+    /* copy data back to buffers */
+    memcpy(hiv->data.data, p, hiv->data.length);
+    q = p + hiv->data.length;
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+           continue;
+       memcpy(data[i].data.data, q, data[i].data.length);
+       q += data[i].data.length;
+    }
+
+    free(p);
+
+    /* check signature */
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+           continue;
+       len += data[i].data.length;
+    }
+
+    p = q = malloc(len);
+    if (p == NULL)
+       return ENOMEM;
+
+    memcpy(q, hiv->data.data, hiv->data.length);
+    q += hiv->data.length;
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+           data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+           continue;
+       memcpy(q, data[i].data.data, data[i].data.length);
+       q += data[i].data.length;
+    }
+
+    cksum.checksum.data   = tiv->data.data;
+    cksum.checksum.length = tiv->data.length;
+    cksum.cksumtype       = CHECKSUMTYPE(et->keyed_checksum);
+
+    ret = verify_checksum(context,
+                         crypto,
+                         INTEGRITY_USAGE(usage),
+                         p,
+                         len,
+                         &cksum);
+    free(p);
+    return ret;
+}
+
+/**
+ * Create a Kerberos message checksum.
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param type output data
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_create_checksum_iov(krb5_context context,
+                        krb5_crypto crypto,
+                        unsigned usage,
+                        krb5_crypto_iov *data,
+                        unsigned int num_data,
+                        krb5_cksumtype *type)
+{
+    Checksum cksum;
+    krb5_crypto_iov *civ;
+    krb5_error_code ret;
+    int i;
+    size_t len;
+    char *p, *q;
+
+    if (num_data < 0) {
+        krb5_clear_error_message(context);
+       return KRB5_CRYPTO_INTERNAL;
+    }
+
+    if(!derived_crypto(context, crypto)) {
+       krb5_clear_error_message(context);
+       return KRB5_CRYPTO_INTERNAL;
+    }
+
+    civ = find_iv(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM);
+    if (civ == NULL)
+       return KRB5_BAD_MSIZE;
+
+    len = 0;
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+           data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+           continue;
+       len += data[i].data.length;
+    }
+
+    p = q = malloc(len);
+
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+           data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+           continue;
+       memcpy(q, data[i].data.data, data[i].data.length);
+       q += data[i].data.length;
+    }
+
+    ret = krb5_create_checksum(context, crypto, usage, 0, p, len, &cksum);
+    free(p);
+    if (ret)
+       return ret;
+
+    if (type)
+       *type = cksum.cksumtype;
+
+    if (cksum.checksum.length > civ->data.length) {
+       krb5_set_error_message(context, KRB5_BAD_MSIZE,
+                              N_("Checksum larger then input buffer", ""));
+       free_Checksum(&cksum);
+       return KRB5_BAD_MSIZE;
+    }
+
+    civ->data.length = cksum.checksum.length;
+    memcpy(civ->data.data, cksum.checksum.data, civ->data.length);
+    free_Checksum(&cksum);
+
+    return 0;
+}
+
+/**
+ * Verify a Kerberos message checksum.
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param type return checksum type if not NULL
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_checksum_iov(krb5_context context,
+                        krb5_crypto crypto,
+                        unsigned usage,
+                        krb5_crypto_iov *data,
+                        unsigned int num_data,
+                        krb5_cksumtype *type)
+{
+    struct encryption_type *et = crypto->et;
+    Checksum cksum;
+    krb5_crypto_iov *civ;
+    krb5_error_code ret;
+    int i;
+    size_t len;
+    char *p, *q;
+
+    if (num_data < 0) {
+        krb5_clear_error_message(context);
+       return KRB5_CRYPTO_INTERNAL;
+    }
+
+    if(!derived_crypto(context, crypto)) {
+       krb5_clear_error_message(context);
+       return KRB5_CRYPTO_INTERNAL;
+    }
+
+    civ = find_iv(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM);
+    if (civ == NULL)
+       return KRB5_BAD_MSIZE;
+
+    len = 0;
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+           data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+           continue;
+       len += data[i].data.length;
+    }
+
+    p = q = malloc(len);
+
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+           data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+           continue;
+       memcpy(q, data[i].data.data, data[i].data.length);
+       q += data[i].data.length;
+    }
+
+    cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum);
+    cksum.checksum.length = civ->data.length;
+    cksum.checksum.data = civ->data.data;
+
+    ret = krb5_verify_checksum(context, crypto, usage, p, len, &cksum);
+    free(p);
+
+    if (ret == 0 && type)
+       *type = cksum.cksumtype;
+
+    return ret;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_length(krb5_context context,
+                  krb5_crypto crypto,
+                  int type,
+                  size_t *len)
+{
+    if (!derived_crypto(context, crypto)) {
+       krb5_set_error_message(context, EINVAL, "not a derived crypto");
+       return EINVAL;
+    }
+
+    switch(type) {
+    case KRB5_CRYPTO_TYPE_EMPTY:
+       *len = 0;
+       return 0;
+    case KRB5_CRYPTO_TYPE_HEADER:
+       *len = crypto->et->blocksize;
+       return 0;
+    case KRB5_CRYPTO_TYPE_DATA:
+    case KRB5_CRYPTO_TYPE_SIGN_ONLY:
+       /* len must already been filled in */
+       return 0;
+    case KRB5_CRYPTO_TYPE_PADDING:
+       if (crypto->et->padsize > 1)
+           *len = crypto->et->padsize;
+       else
+           *len = 0;
+       return 0;
+    case KRB5_CRYPTO_TYPE_TRAILER:
+       *len = CHECKSUMSIZE(crypto->et->keyed_checksum);
+       return 0;
+    case KRB5_CRYPTO_TYPE_CHECKSUM:
+       if (crypto->et->keyed_checksum)
+           *len = CHECKSUMSIZE(crypto->et->keyed_checksum);
+       else
+           *len = CHECKSUMSIZE(crypto->et->checksum);
+       return 0;
+    }
+    krb5_set_error_message(context, EINVAL,
+                          "%d not a supported type", type);
+    return EINVAL;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_length_iov(krb5_context context,
+                      krb5_crypto crypto,
+                      krb5_crypto_iov *data,
+                      unsigned int num_data)
+{
+    krb5_error_code ret;
+    int i;
+
+    for (i = 0; i < num_data; i++) {
+       ret = krb5_crypto_length(context, crypto,
+                                data[i].flags,
+                                &data[i].data.length);
+       if (ret)
+           return ret;
+    }
+    return 0;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_encrypt_ivec(krb5_context context,
+                 krb5_crypto crypto,
+                 unsigned usage,
+                 const void *data,
+                 size_t len,
+                 krb5_data *result,
+                 void *ivec)
+{
+    if(derived_crypto(context, crypto))
+       return encrypt_internal_derived(context, crypto, usage,
+                                       data, len, result, ivec);
+    else if (special_crypto(context, crypto))
+       return encrypt_internal_special (context, crypto, usage,
+                                        data, len, result, ivec);
+    else
+       return encrypt_internal(context, crypto, data, len, result, ivec);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_encrypt(krb5_context context,
+            krb5_crypto crypto,
+            unsigned usage,
+            const void *data,
+            size_t len,
+            krb5_data *result)
+{
+    return krb5_encrypt_ivec(context, crypto, usage, data, len, result, NULL);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_encrypt_EncryptedData(krb5_context context,
+                          krb5_crypto crypto,
+                          unsigned usage,
+                          void *data,
+                          size_t len,
+                          int kvno,
+                          EncryptedData *result)
+{
+    result->etype = CRYPTO_ETYPE(crypto);
+    if(kvno){
+       ALLOC(result->kvno, 1);
+       *result->kvno = kvno;
+    }else
+       result->kvno = NULL;
+    return krb5_encrypt(context, crypto, usage, data, len, &result->cipher);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_decrypt_ivec(krb5_context context,
+                 krb5_crypto crypto,
+                 unsigned usage,
+                 void *data,
+                 size_t len,
+                 krb5_data *result,
+                 void *ivec)
+{
+    if(derived_crypto(context, crypto))
+       return decrypt_internal_derived(context, crypto, usage,
+                                       data, len, result, ivec);
+    else if (special_crypto (context, crypto))
+       return decrypt_internal_special(context, crypto, usage,
+                                       data, len, result, ivec);
+    else
+       return decrypt_internal(context, crypto, data, len, result, ivec);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_decrypt(krb5_context context,
+            krb5_crypto crypto,
+            unsigned usage,
+            void *data,
+            size_t len,
+            krb5_data *result)
+{
+    return krb5_decrypt_ivec (context, crypto, usage, data, len, result,
+                             NULL);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_decrypt_EncryptedData(krb5_context context,
+                          krb5_crypto crypto,
+                          unsigned usage,
+                          const EncryptedData *e,
+                          krb5_data *result)
+{
+    return krb5_decrypt(context, crypto, usage,
+                       e->cipher.data, e->cipher.length, result);
+}
+
+/************************************************************
+ *                                                          *
+ ************************************************************/
+
+krb5_error_code
+_krb5_derive_key(krb5_context context,
+                struct encryption_type *et,
+                struct key_data *key,
+                const void *constant,
+                size_t len)
+{
+    unsigned char *k = NULL;
+    unsigned int nblocks = 0, i;
+    krb5_error_code ret = 0;
+    struct key_type *kt = et->keytype;
+
+    ret = _key_schedule(context, key);
+    if(ret)
+       return ret;
+    if(et->blocksize * 8 < kt->bits || len != et->blocksize) {
+       nblocks = (kt->bits + et->blocksize * 8 - 1) / (et->blocksize * 8);
+       k = malloc(nblocks * et->blocksize);
+       if(k == NULL) {
+           ret = ENOMEM;
+           krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+           goto out;
+       }
+       ret = _krb5_n_fold(constant, len, k, et->blocksize);
+       if (ret) {
+           krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+           goto out;
+       }
+
+       for(i = 0; i < nblocks; i++) {
+           if(i > 0)
+               memcpy(k + i * et->blocksize,
+                      k + (i - 1) * et->blocksize,
+                      et->blocksize);
+           (*et->encrypt)(context, key, k + i * et->blocksize, et->blocksize,
+                          1, 0, NULL);
+       }
+    } else {
+       /* this case is probably broken, but won't be run anyway */
+       void *c = malloc(len);
+       size_t res_len = (kt->bits + 7) / 8;
+
+       if(len != 0 && c == NULL) {
+           ret = ENOMEM;
+           krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+           goto out;
+       }
+       memcpy(c, constant, len);
+       (*et->encrypt)(context, key, c, len, 1, 0, NULL);
+       k = malloc(res_len);
+       if(res_len != 0 && k == NULL) {
+           free(c);
+           ret = ENOMEM;
+           krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+           goto out;
+       }
+       ret = _krb5_n_fold(c, len, k, res_len);
+       free(c);
+       if (ret) {
+           krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+           goto out;
+       }
+    }
+
+    /* XXX keytype dependent post-processing */
+    switch(kt->type) {
+    case KEYTYPE_DES3:
+       _krb5_DES3_random_to_key(context, key->key, k, nblocks * et->blocksize);
+       break;
+    case KEYTYPE_AES128:
+    case KEYTYPE_AES256:
+       memcpy(key->key->keyvalue.data, k, key->key->keyvalue.length);
+       break;
+    default:
+       ret = KRB5_CRYPTO_INTERNAL;
+       krb5_set_error_message(context, ret,
+                              N_("derive_key() called with unknown keytype (%u)", ""),
+                              kt->type);
+       break;
+    }
+ out:
+    if (key->schedule) {
+       free_key_schedule(context, key, et);
+       key->schedule = NULL;
+    }
+    if (k) {
+       memset(k, 0, nblocks * et->blocksize);
+       free(k);
+    }
+    return ret;
+}
+
+static struct key_data *
+_new_derived_key(krb5_crypto crypto, unsigned usage)
+{
+    struct key_usage *d = crypto->key_usage;
+    d = realloc(d, (crypto->num_key_usage + 1) * sizeof(*d));
+    if(d == NULL)
+       return NULL;
+    crypto->key_usage = d;
+    d += crypto->num_key_usage++;
+    memset(d, 0, sizeof(*d));
+    d->usage = usage;
+    return &d->key;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_derive_key(krb5_context context,
+               const krb5_keyblock *key,
+               krb5_enctype etype,
+               const void *constant,
+               size_t constant_len,
+               krb5_keyblock **derived_key)
+{
+    krb5_error_code ret;
+    struct encryption_type *et;
+    struct key_data d;
+
+    *derived_key = NULL;
+
+    et = _krb5_find_enctype (etype);
+    if (et == NULL) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              N_("encryption type %d not supported", ""),
+                              etype);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+
+    ret = krb5_copy_keyblock(context, key, &d.key);
+    if (ret)
+       return ret;
+
+    d.schedule = NULL;
+    ret = _krb5_derive_key(context, et, &d, constant, constant_len);
+    if (ret == 0)
+       ret = krb5_copy_keyblock(context, d.key, derived_key);
+    _krb5_free_key_data(context, &d, et);
+    return ret;
+}
+
+static krb5_error_code
+_get_derived_key(krb5_context context,
+                krb5_crypto crypto,
+                unsigned usage,
+                struct key_data **key)
+{
+    int i;
+    struct key_data *d;
+    unsigned char constant[5];
+
+    for(i = 0; i < crypto->num_key_usage; i++)
+       if(crypto->key_usage[i].usage == usage) {
+           *key = &crypto->key_usage[i].key;
+           return 0;
+       }
+    d = _new_derived_key(crypto, usage);
+    if(d == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    krb5_copy_keyblock(context, crypto->key.key, &d->key);
+    _krb5_put_int(constant, usage, 5);
+    _krb5_derive_key(context, crypto->et, d, constant, sizeof(constant));
+    *key = d;
+    return 0;
+}
+
+/**
+ * Create a crypto context used for all encryption and signature
+ * operation. The encryption type to use is taken from the key, but
+ * can be overridden with the enctype parameter.  This can be useful
+ * for encryptions types which is compatiable (DES for example).
+ *
+ * To free the crypto context, use krb5_crypto_destroy().
+ *
+ * @param context Kerberos context
+ * @param key the key block information with all key data
+ * @param etype the encryption type
+ * @param crypto the resulting crypto context
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_init(krb5_context context,
+                const krb5_keyblock *key,
+                krb5_enctype etype,
+                krb5_crypto *crypto)
+{
+    krb5_error_code ret;
+    ALLOC(*crypto, 1);
+    if(*crypto == NULL) {
+       krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+       return ENOMEM;
+    }
+    if(etype == ETYPE_NULL)
+       etype = key->keytype;
+    (*crypto)->et = _krb5_find_enctype(etype);
+    if((*crypto)->et == NULL || ((*crypto)->et->flags & F_DISABLED)) {
+       free(*crypto);
+       *crypto = NULL;
+       krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                               N_("encryption type %d not supported", ""),
+                               etype);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    if((*crypto)->et->keytype->size != key->keyvalue.length) {
+       free(*crypto);
+       *crypto = NULL;
+       krb5_set_error_message (context, KRB5_BAD_KEYSIZE,
+                               "encryption key has bad length");
+       return KRB5_BAD_KEYSIZE;
+    }
+    ret = krb5_copy_keyblock(context, key, &(*crypto)->key.key);
+    if(ret) {
+       free(*crypto);
+       *crypto = NULL;
+       return ret;
+    }
+    (*crypto)->key.schedule = NULL;
+    (*crypto)->num_key_usage = 0;
+    (*crypto)->key_usage = NULL;
+    return 0;
+}
+
+static void
+free_key_schedule(krb5_context context,
+                 struct key_data *key,
+                 struct encryption_type *et)
+{
+    if (et->keytype->cleanup)
+       (*et->keytype->cleanup)(context, key);
+    memset(key->schedule->data, 0, key->schedule->length);
+    krb5_free_data(context, key->schedule);
+}
+
+void
+_krb5_free_key_data(krb5_context context, struct key_data *key,
+             struct encryption_type *et)
+{
+    krb5_free_keyblock(context, key->key);
+    if(key->schedule) {
+       free_key_schedule(context, key, et);
+       key->schedule = NULL;
+    }
+}
+
+static void
+free_key_usage(krb5_context context, struct key_usage *ku,
+              struct encryption_type *et)
+{
+    _krb5_free_key_data(context, &ku->key, et);
+}
+
+/**
+ * Free a crypto context created by krb5_crypto_init().
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to free
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_destroy(krb5_context context,
+                   krb5_crypto crypto)
+{
+    int i;
+
+    for(i = 0; i < crypto->num_key_usage; i++)
+       free_key_usage(context, &crypto->key_usage[i], crypto->et);
+    free(crypto->key_usage);
+    _krb5_free_key_data(context, &crypto->key, crypto->et);
+    free (crypto);
+    return 0;
+}
+
+/**
+ * Return the blocksize used algorithm referenced by the crypto context
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to query
+ * @param blocksize the resulting blocksize
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_getblocksize(krb5_context context,
+                        krb5_crypto crypto,
+                        size_t *blocksize)
+{
+    *blocksize = crypto->et->blocksize;
+    return 0;
+}
+
+/**
+ * Return the encryption type used by the crypto context
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to query
+ * @param enctype the resulting encryption type
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_getenctype(krb5_context context,
+                      krb5_crypto crypto,
+                      krb5_enctype *enctype)
+{
+    *enctype = crypto->et->type;
+    return 0;
+}
+
+/**
+ * Return the padding size used by the crypto context
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to query
+ * @param padsize the return padding size
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_getpadsize(krb5_context context,
+                       krb5_crypto crypto,
+                       size_t *padsize)
+{
+    *padsize = crypto->et->padsize;
+    return 0;
+}
+
+/**
+ * Return the confounder size used by the crypto context
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to query
+ * @param confoundersize the returned confounder size
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_getconfoundersize(krb5_context context,
+                              krb5_crypto crypto,
+                              size_t *confoundersize)
+{
+    *confoundersize = crypto->et->confoundersize;
+    return 0;
+}
+
+
+/**
+ * Disable encryption type
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to disable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_enctype_disable(krb5_context context,
+                    krb5_enctype enctype)
+{
+    struct encryption_type *et = _krb5_find_enctype(enctype);
+    if(et == NULL) {
+       if (context)
+           krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                                   N_("encryption type %d not supported", ""),
+                                   enctype);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    et->flags |= F_DISABLED;
+    return 0;
+}
+
+/**
+ * Enable encryption type
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to enable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_enctype_enable(krb5_context context,
+                   krb5_enctype enctype)
+{
+    struct encryption_type *et = _krb5_find_enctype(enctype);
+    if(et == NULL) {
+       if (context)
+           krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+                                   N_("encryption type %d not supported", ""),
+                                   enctype);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    et->flags &= ~F_DISABLED;
+    return 0;
+}
+
+/**
+ * Enable or disable all weak encryption types
+ *
+ * @param context Kerberos 5 context
+ * @param enable true to enable, false to disable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_allow_weak_crypto(krb5_context context,
+                      krb5_boolean enable)
+{
+    int i;
+
+    for(i = 0; i < _krb5_num_etypes; i++)
+       if(_krb5_etypes[i]->flags & F_WEAK) {
+           if(enable)
+               _krb5_etypes[i]->flags &= ~F_DISABLED;
+           else
+               _krb5_etypes[i]->flags |= F_DISABLED;
+       }
+    return 0;
+}
+
+static size_t
+wrapped_length (krb5_context context,
+               krb5_crypto  crypto,
+               size_t       data_len)
+{
+    struct encryption_type *et = crypto->et;
+    size_t padsize = et->padsize;
+    size_t checksumsize = CHECKSUMSIZE(et->checksum);
+    size_t res;
+
+    res =  et->confoundersize + checksumsize + data_len;
+    res =  (res + padsize - 1) / padsize * padsize;
+    return res;
+}
+
+static size_t
+wrapped_length_dervied (krb5_context context,
+                       krb5_crypto  crypto,
+                       size_t       data_len)
+{
+    struct encryption_type *et = crypto->et;
+    size_t padsize = et->padsize;
+    size_t res;
+
+    res =  et->confoundersize + data_len;
+    res =  (res + padsize - 1) / padsize * padsize;
+    if (et->keyed_checksum)
+       res += et->keyed_checksum->checksumsize;
+    else
+       res += et->checksum->checksumsize;
+    return res;
+}
+
+/*
+ * Return the size of an encrypted packet of length `data_len'
+ */
+
+KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL
+krb5_get_wrapped_length (krb5_context context,
+                        krb5_crypto  crypto,
+                        size_t       data_len)
+{
+    if (derived_crypto (context, crypto))
+       return wrapped_length_dervied (context, crypto, data_len);
+    else
+       return wrapped_length (context, crypto, data_len);
+}
+
+/*
+ * Return the size of an encrypted packet of length `data_len'
+ */
+
+static size_t
+crypto_overhead (krb5_context context,
+                krb5_crypto  crypto)
+{
+    struct encryption_type *et = crypto->et;
+    size_t res;
+
+    res = CHECKSUMSIZE(et->checksum);
+    res += et->confoundersize;
+    if (et->padsize > 1)
+       res += et->padsize;
+    return res;
+}
+
+static size_t
+crypto_overhead_dervied (krb5_context context,
+                        krb5_crypto  crypto)
+{
+    struct encryption_type *et = crypto->et;
+    size_t res;
+
+    if (et->keyed_checksum)
+       res = CHECKSUMSIZE(et->keyed_checksum);
+    else
+       res = CHECKSUMSIZE(et->checksum);
+    res += et->confoundersize;
+    if (et->padsize > 1)
+       res += et->padsize;
+    return res;
+}
+
+KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL
+krb5_crypto_overhead (krb5_context context, krb5_crypto crypto)
+{
+    if (derived_crypto (context, crypto))
+       return crypto_overhead_dervied (context, crypto);
+    else
+       return crypto_overhead (context, crypto);
+}
+
+/**
+ * Converts the random bytestring to a protocol key according to
+ * Kerberos crypto frame work. It may be assumed that all the bits of
+ * the input string are equally random, even though the entropy
+ * present in the random source may be limited.
+ *
+ * @param context Kerberos 5 context
+ * @param type the enctype resulting key will be of
+ * @param data input random data to convert to a key
+ * @param size size of input random data, at least krb5_enctype_keysize() long
+ * @param key key, output key, free with krb5_free_keyblock_contents()
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_random_to_key(krb5_context context,
+                  krb5_enctype type,
+                  const void *data,
+                  size_t size,
+                  krb5_keyblock *key)
+{
+    krb5_error_code ret;
+    struct encryption_type *et = _krb5_find_enctype(type);
+    if(et == NULL) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              N_("encryption type %d not supported", ""),
+                              type);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    if ((et->keytype->bits + 7) / 8 > size) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              N_("encryption key %s needs %d bytes "
+                                 "of random to make an encryption key "
+                                 "out of it", ""),
+                              et->name, (int)et->keytype->size);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    ret = krb5_data_alloc(&key->keyvalue, et->keytype->size);
+    if(ret)
+       return ret;
+    key->keytype = type;
+    if (et->keytype->random_to_key)
+       (*et->keytype->random_to_key)(context, key, data, size);
+    else
+       memcpy(key->keyvalue.data, data, et->keytype->size);
+
+    return 0;
+}
+
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_prf_length(krb5_context context,
+                      krb5_enctype type,
+                      size_t *length)
+{
+    struct encryption_type *et = _krb5_find_enctype(type);
+
+    if(et == NULL || et->prf_length == 0) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              N_("encryption type %d not supported", ""),
+                              type);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+
+    *length = et->prf_length;
+    return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_prf(krb5_context context,
+               const krb5_crypto crypto,
+               const krb5_data *input,
+               krb5_data *output)
+{
+    struct encryption_type *et = crypto->et;
+
+    krb5_data_zero(output);
+
+    if(et->prf == NULL) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              "kerberos prf for %s not supported",
+                              et->name);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+
+    return (*et->prf)(context, crypto, input, output);
+}
+
+static krb5_error_code
+krb5_crypto_prfplus(krb5_context context,
+                   const krb5_crypto crypto,
+                   const krb5_data *input,
+                   size_t length,
+                   krb5_data *output)
+{
+    krb5_error_code ret;
+    krb5_data input2;
+    unsigned char i = 1;
+    unsigned char *p;
+
+    krb5_data_zero(&input2);
+    krb5_data_zero(output);
+
+    krb5_clear_error_message(context);
+
+    ret = krb5_data_alloc(output, length);
+    if (ret) goto out;
+    ret = krb5_data_alloc(&input2, input->length + 1);
+    if (ret) goto out;
+
+    krb5_clear_error_message(context);
+
+    memcpy(((unsigned char *)input2.data) + 1, input->data, input->length);
+
+    p = output->data;
+
+    while (length) {
+       krb5_data block;
+
+       ((unsigned char *)input2.data)[0] = i++;
+
+       ret = krb5_crypto_prf(context, crypto, &input2, &block);
+       if (ret)
+           goto out;
+
+       if (block.length < length) {
+           memcpy(p, block.data, block.length);
+           length -= block.length;
+       } else {
+           memcpy(p, block.data, length);
+           length = 0;
+       }
+       p += block.length;
+       krb5_data_free(&block);
+    }
+
+ out:
+    krb5_data_free(&input2);
+    if (ret)
+       krb5_data_free(output);
+    return 0;
+}
+
+/**
+ * The FX-CF2 key derivation function, used in FAST and preauth framework.
+ *
+ * @param context Kerberos 5 context
+ * @param crypto1 first key to combine
+ * @param crypto2 second key to combine
+ * @param pepper1 factor to combine with first key to garante uniqueness
+ * @param pepper2 factor to combine with second key to garante uniqueness
+ * @param enctype the encryption type of the resulting key
+ * @param res allocated key, free with krb5_free_keyblock_contents()
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_crypto_fx_cf2(krb5_context context,
+                  const krb5_crypto crypto1,
+                  const krb5_crypto crypto2,
+                  krb5_data *pepper1,
+                  krb5_data *pepper2,
+                  krb5_enctype enctype,
+                  krb5_keyblock *res)
+{
+    krb5_error_code ret;
+    krb5_data os1, os2;
+    size_t i, keysize;
+
+    memset(res, 0, sizeof(*res));
+
+    ret = krb5_enctype_keysize(context, enctype, &keysize);
+    if (ret)
+       return ret;
+
+    ret = krb5_data_alloc(&res->keyvalue, keysize);
+    if (ret)
+       goto out;
+    ret = krb5_crypto_prfplus(context, crypto1, pepper1, keysize, &os1);
+    if (ret)
+       goto out;
+    ret = krb5_crypto_prfplus(context, crypto2, pepper2, keysize, &os2);
+    if (ret)
+       goto out;
+
+    res->keytype = enctype;
+    {
+       unsigned char *p1 = os1.data, *p2 = os2.data, *p3 = res->keyvalue.data;
+       for (i = 0; i < keysize; i++)
+           p3[i] = p1[i] ^ p2[i];
+    }
+ out:
+    if (ret)
+       krb5_data_free(&res->keyvalue);
+    krb5_data_free(&os1);
+    krb5_data_free(&os2);
+
+    return ret;
+}
+
+
+
+#ifndef HEIMDAL_SMALLER
+
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes.
+ *
+ * @ingroup krb5_deprecated
+ */
+
+KRB5_DEPRECATED
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_keytype_to_enctypes (krb5_context context,
+                         krb5_keytype keytype,
+                         unsigned *len,
+                         krb5_enctype **val)
+{
+    int i;
+    unsigned n = 0;
+    krb5_enctype *ret;
+
+    for (i = _krb5_num_etypes - 1; i >= 0; --i) {
+       if (_krb5_etypes[i]->keytype->type == keytype
+           && !(_krb5_etypes[i]->flags & F_PSEUDO)
+           && krb5_enctype_valid(context, _krb5_etypes[i]->type) == 0)
+           ++n;
+    }
+    if (n == 0) {
+       krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+                              "Keytype have no mapping");
+       return KRB5_PROG_KEYTYPE_NOSUPP;
+    }
+
+    ret = malloc(n * sizeof(*ret));
+    if (ret == NULL && n != 0) {
+       krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+       return ENOMEM;
+    }
+    n = 0;
+    for (i = _krb5_num_etypes - 1; i >= 0; --i) {
+       if (_krb5_etypes[i]->keytype->type == keytype
+           && !(_krb5_etypes[i]->flags & F_PSEUDO)
+           && krb5_enctype_valid(context, _krb5_etypes[i]->type) == 0)
+           ret[n++] = _krb5_etypes[i]->type;
+    }
+    *len = n;
+    *val = ret;
+    return 0;
+}
+
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes.
+ *
+ * @ingroup krb5_deprecated
+ */
+
+/* if two enctypes have compatible keys */
+KRB5_DEPRECATED
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_enctypes_compatible_keys(krb5_context context,
+                             krb5_enctype etype1,
+                             krb5_enctype etype2)
+{
+    struct encryption_type *e1 = _krb5_find_enctype(etype1);
+    struct encryption_type *e2 = _krb5_find_enctype(etype2);
+    return e1 != NULL && e2 != NULL && e1->keytype == e2->keytype;
+}
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/src/external/heimdal/krb5/crypto.h b/src/external/heimdal/krb5/crypto.h
new file mode 100644 (file)
index 0000000..c57221b
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HEIMDAL_SMALLER
+#define DES3_OLD_ENCTYPE 1
+#endif
+
+struct key_data {
+    krb5_keyblock *key;
+    krb5_data *schedule;
+};
+
+struct key_usage {
+    unsigned usage;
+    struct key_data key;
+};
+
+struct krb5_crypto_data {
+    struct encryption_type *et;
+    struct key_data key;
+    int num_key_usage;
+    struct key_usage *key_usage;
+};
+
+#define CRYPTO_ETYPE(C) ((C)->et->type)
+
+/* bits for `flags' below */
+#define F_KEYED                 1      /* checksum is keyed */
+#define F_CPROOF        2      /* checksum is collision proof */
+#define F_DERIVED       4      /* uses derived keys */
+#define F_VARIANT       8      /* uses `variant' keys (6.4.3) */
+#define F_PSEUDO       16      /* not a real protocol type */
+#define F_SPECIAL      32      /* backwards */
+#define F_DISABLED     64      /* enctype/checksum disabled */
+#define F_WEAK        128      /* enctype is considered weak */
+
+struct salt_type {
+    krb5_salttype type;
+    const char *name;
+    krb5_error_code (*string_to_key)(krb5_context, krb5_enctype, krb5_data,
+                                    krb5_salt, krb5_data, krb5_keyblock*);
+};
+
+struct key_type {
+    krb5_keytype type; /* XXX */
+    const char *name;
+    size_t bits;
+    size_t size;
+    size_t schedule_size;
+    void (*random_key)(krb5_context, krb5_keyblock*);
+    void (*schedule)(krb5_context, struct key_type *, struct key_data *);
+    struct salt_type *string_to_key;
+    void (*random_to_key)(krb5_context, krb5_keyblock*, const void*, size_t);
+    void (*cleanup)(krb5_context, struct key_data *);
+    const EVP_CIPHER *(*evp)(void);
+};
+
+struct checksum_type {
+    krb5_cksumtype type;
+    const char *name;
+    size_t blocksize;
+    size_t checksumsize;
+    unsigned flags;
+    krb5_error_code (*checksum)(krb5_context context,
+                               struct key_data *key,
+                               const void *buf, size_t len,
+                               unsigned usage,
+                               Checksum *csum);
+    krb5_error_code (*verify)(krb5_context context,
+                             struct key_data *key,
+                             const void *buf, size_t len,
+                             unsigned usage,
+                             Checksum *csum);
+};
+
+struct encryption_type {
+    krb5_enctype type;
+    const char *name;
+    size_t blocksize;
+    size_t padsize;
+    size_t confoundersize;
+    struct key_type *keytype;
+    struct checksum_type *checksum;
+    struct checksum_type *keyed_checksum;
+    unsigned flags;
+    krb5_error_code (*encrypt)(krb5_context context,
+                              struct key_data *key,
+                              void *data, size_t len,
+                              krb5_boolean encryptp,
+                              int usage,
+                              void *ivec);
+    size_t prf_length;
+    krb5_error_code (*prf)(krb5_context,
+                          krb5_crypto, const krb5_data *, krb5_data *);
+};
+
+#define ENCRYPTION_USAGE(U) (((U) << 8) | 0xAA)
+#define INTEGRITY_USAGE(U) (((U) << 8) | 0x55)
+#define CHECKSUM_USAGE(U) (((U) << 8) | 0x99)
+
+/* Checksums */
+
+extern struct checksum_type _krb5_checksum_none;
+extern struct checksum_type _krb5_checksum_crc32;
+extern struct checksum_type _krb5_checksum_rsa_md4;
+extern struct checksum_type _krb5_checksum_rsa_md4_des;
+extern struct checksum_type _krb5_checksum_rsa_md5_des;
+extern struct checksum_type _krb5_checksum_rsa_md5_des3;
+extern struct checksum_type _krb5_checksum_rsa_md5;
+extern struct checksum_type _krb5_checksum_hmac_sha1_des3;
+extern struct checksum_type _krb5_checksum_hmac_sha1_aes128;
+extern struct checksum_type _krb5_checksum_hmac_sha1_aes256;
+extern struct checksum_type _krb5_checksum_hmac_md5;
+extern struct checksum_type _krb5_checksum_sha1;
+
+extern struct checksum_type *_krb5_checksum_types[];
+extern int _krb5_num_checksums;
+
+/* Salts */
+
+extern struct salt_type _krb5_AES_salt[];
+extern struct salt_type _krb5_arcfour_salt[];
+extern struct salt_type _krb5_des_salt[];
+extern struct salt_type _krb5_des3_salt[];
+extern struct salt_type _krb5_des3_salt_derived[];
+
+/* Encryption types */
+
+extern struct encryption_type _krb5_enctype_aes256_cts_hmac_sha1;
+extern struct encryption_type _krb5_enctype_aes128_cts_hmac_sha1;
+extern struct encryption_type _krb5_enctype_des3_cbc_sha1;
+extern struct encryption_type _krb5_enctype_des3_cbc_md5;
+extern struct encryption_type _krb5_enctype_des3_cbc_none;
+extern struct encryption_type _krb5_enctype_arcfour_hmac_md5;
+extern struct encryption_type _krb5_enctype_des_cbc_md5;
+extern struct encryption_type _krb5_enctype_old_des3_cbc_sha1;
+extern struct encryption_type _krb5_enctype_des_cbc_crc;
+extern struct encryption_type _krb5_enctype_des_cbc_md4;
+extern struct encryption_type _krb5_enctype_des_cbc_md5;
+extern struct encryption_type _krb5_enctype_des_cbc_none;
+extern struct encryption_type _krb5_enctype_des_cfb64_none;
+extern struct encryption_type _krb5_enctype_des_pcbc_none;
+extern struct encryption_type _krb5_enctype_null;
+
+extern struct encryption_type *_krb5_etypes[];
+extern int _krb5_num_etypes;
+
+/* Interface to the EVP crypto layer provided by hcrypto */
+struct evp_schedule {
+    EVP_CIPHER_CTX ectx;
+    EVP_CIPHER_CTX dctx;
+};
diff --git a/src/external/heimdal/krb5/data.c b/src/external/heimdal/krb5/data.c
new file mode 100644 (file)
index 0000000..f62a553
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+/**
+ * Reset the (potentially uninitalized) krb5_data structure.
+ *
+ * @param p krb5_data to reset.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_data_zero(krb5_data *p)
+{
+    p->length = 0;
+    p->data   = NULL;
+}
+
+/**
+ * Free the content of krb5_data structure, its ok to free a zeroed
+ * structure (with memset() or krb5_data_zero()). When done, the
+ * structure will be zeroed. The same function is called
+ * krb5_free_data_contents() in MIT Kerberos.
+ *
+ * @param p krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_data_free(krb5_data *p)
+{
+    if(p->data != NULL)
+       free(p->data);
+    krb5_data_zero(p);
+}
+
+/**
+ * Free krb5_data (and its content).
+ *
+ * @param context Kerberos 5 context.
+ * @param p krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_data(krb5_context context,
+              krb5_data *p)
+{
+    krb5_data_free(p);
+    free(p);
+}
+
+/**
+ * Allocate data of and krb5_data.
+ *
+ * @param p krb5_data to allocate.
+ * @param len size to allocate.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_data_alloc(krb5_data *p, int len)
+{
+    p->data = malloc(len);
+    if(len && p->data == NULL)
+       return ENOMEM;
+    p->length = len;
+    return 0;
+}
+
+/**
+ * Grow (or shrink) the content of krb5_data to a new size.
+ *
+ * @param p krb5_data to free.
+ * @param len new size.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_data_realloc(krb5_data *p, int len)
+{
+    void *tmp;
+    tmp = realloc(p->data, len);
+    if(len && !tmp)
+       return ENOMEM;
+    p->data = tmp;
+    p->length = len;
+    return 0;
+}
+
+/**
+ * Copy the data of len into the krb5_data.
+ *
+ * @param p krb5_data to copy into.
+ * @param data data to copy..
+ * @param len new size.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_data_copy(krb5_data *p, const void *data, size_t len)
+{
+    if (len) {
+       if(krb5_data_alloc(p, len))
+           return ENOMEM;
+       memmove(p->data, data, len);
+    } else
+       p->data = NULL;
+    p->length = len;
+    return 0;
+}
+
+/**
+ * Copy the data into a newly allocated krb5_data.
+ *
+ * @param context Kerberos 5 context.
+ * @param indata the krb5_data data to copy
+ * @param outdata new krb5_date to copy too. Free with krb5_free_data().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_copy_data(krb5_context context,
+              const krb5_data *indata,
+              krb5_data **outdata)
+{
+    krb5_error_code ret;
+    ALLOC(*outdata, 1);
+    if(*outdata == NULL) {
+       krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+       return ENOMEM;
+    }
+    ret = der_copy_octet_string(indata, *outdata);
+    if(ret) {
+       krb5_clear_error_message (context);
+       free(*outdata);
+       *outdata = NULL;
+    }
+    return ret;
+}
+
+/**
+ * Compare to data.
+ *
+ * @param data1 krb5_data to compare
+ * @param data2 krb5_data to compare
+ *
+ * @return return the same way as memcmp(), useful when sorting.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_data_cmp(const krb5_data *data1, const krb5_data *data2)
+{
+    if (data1->length != data2->length)
+       return data1->length - data2->length;
+    return memcmp(data1->data, data2->data, data1->length);
+}
+
+/**
+ * Compare to data not exposing timing information from the checksum data
+ *
+ * @param data1 krb5_data to compare
+ * @param data2 krb5_data to compare
+ *
+ * @return returns zero for same data, otherwise non zero.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_data_ct_cmp(const krb5_data *data1, const krb5_data *data2)
+{
+    if (data1->length != data2->length)
+       return data1->length - data2->length;
+    return ct_memcmp(data1->data, data2->data, data1->length);
+}
diff --git a/src/external/heimdal/krb5/keyblock.c b/src/external/heimdal/krb5/keyblock.c
new file mode 100644 (file)
index 0000000..9ba9c4b
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+/**
+ * Zero out a keyblock
+ *
+ * @param keyblock keyblock to zero out
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_keyblock_zero(krb5_keyblock *keyblock)
+{
+    keyblock->keytype = 0;
+    krb5_data_zero(&keyblock->keyvalue);
+}
+
+/**
+ * Free a keyblock's content, also zero out the content of the keyblock.
+ *
+ * @param context a Kerberos 5 context
+ * @param keyblock keyblock content to free, NULL is valid argument
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_keyblock_contents(krb5_context context,
+                           krb5_keyblock *keyblock)
+{
+    if(keyblock) {
+       if (keyblock->keyvalue.data != NULL)
+           memset(keyblock->keyvalue.data, 0, keyblock->keyvalue.length);
+       krb5_data_free (&keyblock->keyvalue);
+       keyblock->keytype = ENCTYPE_NULL;
+    }
+}
+
+/**
+ * Free a keyblock, also zero out the content of the keyblock, uses
+ * krb5_free_keyblock_contents() to free the content.
+ *
+ * @param context a Kerberos 5 context
+ * @param keyblock keyblock to free, NULL is valid argument
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_keyblock(krb5_context context,
+                  krb5_keyblock *keyblock)
+{
+    if(keyblock){
+       krb5_free_keyblock_contents(context, keyblock);
+       free(keyblock);
+    }
+}
+
+/**
+ * Copy a keyblock, free the output keyblock with
+ * krb5_free_keyblock_contents().
+ *
+ * @param context a Kerberos 5 context
+ * @param inblock the key to copy
+ * @param to the output key.
+ *
+ * @return 0 on success or a Kerberos 5 error code
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_copy_keyblock_contents (krb5_context context,
+                            const krb5_keyblock *inblock,
+                            krb5_keyblock *to)
+{
+    return copy_EncryptionKey(inblock, to);
+}
+
+/**
+ * Copy a keyblock, free the output keyblock with
+ * krb5_free_keyblock().
+ *
+ * @param context a Kerberos 5 context
+ * @param inblock the key to copy
+ * @param to the output key.
+ *
+ * @return 0 on success or a Kerberos 5 error code
+ *
+ * @ingroup krb5_crypto
+ */
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_copy_keyblock (krb5_context context,
+                   const krb5_keyblock *inblock,
+                   krb5_keyblock **to)
+{
+    krb5_error_code ret;
+    krb5_keyblock *k;
+
+    *to = NULL;
+
+    k = calloc (1, sizeof(*k));
+    if (k == NULL) {
+       krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+       return ENOMEM;
+    }
+
+    ret = krb5_copy_keyblock_contents (context, inblock, k);
+    if (ret) {
+      free(k);
+      return ret;
+    }
+    *to = k;
+    return 0;
+}
+
+/**
+ * Get encryption type of a keyblock.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_enctype KRB5_LIB_CALL
+krb5_keyblock_get_enctype(const krb5_keyblock *block)
+{
+    return block->keytype;
+}
+
+/**
+ * Fill in `key' with key data of type `enctype' from `data' of length
+ * `size'. Key should be freed using krb5_free_keyblock_contents().
+ *
+ * @return 0 on success or a Kerberos 5 error code
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_keyblock_init(krb5_context context,
+                  krb5_enctype type,
+                  const void *data,
+                  size_t size,
+                  krb5_keyblock *key)
+{
+    krb5_error_code ret;
+    size_t len;
+
+    memset(key, 0, sizeof(*key));
+
+    ret = krb5_enctype_keysize(context, type, &len);
+    if (ret)
+       return ret;
+
+    if (len != size) {
+       krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+                              "Encryption key %d is %lu bytes "
+                              "long, %lu was passed in",
+                              type, (unsigned long)len, (unsigned long)size);
+       return KRB5_PROG_ETYPE_NOSUPP;
+    }
+    ret = krb5_data_copy(&key->keyvalue, data, len);
+    if(ret) {
+       krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+       return ret;
+    }
+    key->keytype = type;
+
+    return 0;
+}
diff --git a/src/external/heimdal/krb5/store-int.c b/src/external/heimdal/krb5/store-int.c
new file mode 100644 (file)
index 0000000..0a18d0d
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1997-2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+_krb5_put_int(void *buffer, unsigned long value, size_t size)
+{
+    unsigned char *p = buffer;
+    int i;
+    for (i = size - 1; i >= 0; i--) {
+       p[i] = value & 0xff;
+       value >>= 8;
+    }
+    return size;
+}
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+_krb5_get_int(void *buffer, unsigned long *value, size_t size)
+{
+    unsigned char *p = buffer;
+    unsigned long v = 0;
+    int i;
+    for (i = 0; i < size; i++)
+       v = (v << 8) + p[i];
+    *value = v;
+    return size;
+}