Crypto modularity proj: Populae openssl/arcfour dir
authorZhanna Tsitkov <tsitkova@mit.edu>
Thu, 1 Oct 2009 22:54:27 +0000 (22:54 +0000)
committerZhanna Tsitkov <tsitkova@mit.edu>
Thu, 1 Oct 2009 22:54:27 +0000 (22:54 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@22825 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/crypto/openssl/arcfour/arcfour-int.h [new file with mode: 0644]
src/lib/crypto/openssl/arcfour/arcfour.c [new file with mode: 0644]
src/lib/crypto/openssl/arcfour/arcfour.h [new file with mode: 0644]
src/lib/crypto/openssl/arcfour/arcfour_aead.c [new file with mode: 0644]
src/lib/crypto/openssl/arcfour/arcfour_s2k.c [new file with mode: 0644]

diff --git a/src/lib/crypto/openssl/arcfour/arcfour-int.h b/src/lib/crypto/openssl/arcfour/arcfour-int.h
new file mode 100644 (file)
index 0000000..d9db0be
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+
+ARCFOUR cipher (based on a cipher posted on the Usenet in Spring-95).
+This cipher is widely believed and has been tested to be equivalent
+with the RC4 cipher from RSA Data Security, Inc.  (RC4 is a trademark
+of RSA Data Security)
+
+*/
+#ifndef ARCFOUR_INT_H
+#define ARCFOUR_INT_H
+
+#include "arcfour.h"
+#include <openssl/evp.h>
+
+#define CONFOUNDERLENGTH 8
+
+typedef struct
+{
+    EVP_CIPHER_CTX  evp_ctx;
+    unsigned int x;
+    unsigned int y;
+    unsigned char state[256]; 
+   
+} ArcfourContext;
+
+typedef struct {
+    int initialized;
+    ArcfourContext ctx;
+} ArcFourCipherState;
+
+krb5_keyusage krb5int_arcfour_translate_usage(krb5_keyusage usage);
+
+extern const char *const krb5int_arcfour_l40;
+
+#endif /* ARCFOUR_INT_H */
diff --git a/src/lib/crypto/openssl/arcfour/arcfour.c b/src/lib/crypto/openssl/arcfour/arcfour.c
new file mode 100644 (file)
index 0000000..687f276
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+
+ARCFOUR cipher (based on a cipher posted on the Usenet in Spring-95).
+This cipher is widely believed and has been tested to be equivalent
+with the RC4 cipher from RSA Data Security, Inc.  (RC4 is a trademark
+of RSA Data Security)
+
+*/
+#include "k5-int.h"
+#include "arcfour-int.h"
+#include "hash_provider/hash_provider.h"
+
+const char *const krb5int_arcfour_l40 = "fortybits";
+
+void
+krb5_arcfour_encrypt_length(const struct krb5_enc_provider *enc,
+                           const struct krb5_hash_provider *hash,
+                           size_t inputlen, size_t *length)
+{
+  size_t blocksize, hashsize;
+
+  blocksize = enc->block_size;
+  hashsize = hash->hashsize;
+
+  /* checksum + (confounder + inputlen, in even blocksize) */
+  *length = hashsize + krb5_roundup(8 + inputlen, blocksize);
+}
+
+ krb5_keyusage
+ krb5int_arcfour_translate_usage(krb5_keyusage usage)
+{
+  switch (usage) {
+  case 1:                      /* AS-REQ PA-ENC-TIMESTAMP padata timestamp,  */
+    return 1;
+  case 2:                      /* ticket from kdc */
+    return 2;
+  case 3:                      /* as-rep encrypted part */
+    return 8;
+  case 4:                      /* tgs-req authz data */
+    return 4;
+  case 5:                      /* tgs-req authz data in subkey */
+    return 5;
+  case 6:                      /* tgs-req authenticator cksum */
+    return 6;
+case 7:                                /* tgs-req authenticator */
+  return 7;
+    case 8:
+    return 8;
+  case 9:                      /* tgs-rep encrypted with subkey */
+    return 9;
+  case 10:                     /* ap-rep authentication cksum */
+    return 10;                 /* xxx  Microsoft never uses this*/
+  case 11:                     /* app-req authenticator */
+    return 11;
+  case 12:                     /* app-rep encrypted part */
+    return 12;
+  case 23: /* sign wrap token*/
+    return 13;
+  default:
+      return usage;
+}
+}
+
+/* RFC 4757 */ 
+krb5_error_code
+krb5_arcfour_encrypt(const struct krb5_enc_provider *enc,
+                    const struct krb5_hash_provider *hash,
+                    const krb5_keyblock *key, krb5_keyusage usage,
+                    const krb5_data *ivec, const krb5_data *input,
+                    krb5_data *output)
+{
+  krb5_keyblock k1, k2, k3;
+  krb5_data d1, d2, d3, salt, plaintext, checksum, ciphertext, confounder;
+  krb5_keyusage ms_usage;
+  size_t keylength, keybytes, blocksize, hashsize;
+  krb5_error_code ret;
+
+  blocksize = enc->block_size;
+  keybytes = enc->keybytes;
+  keylength = enc->keylength;
+  hashsize = hash->hashsize;
+
+  d1.length=keybytes;
+  d1.data=malloc(d1.length);
+  if (d1.data == NULL)
+    return (ENOMEM);
+  k1 = *key;
+  k1.length=d1.length;
+  k1.contents= (void *) d1.data;
+
+  d2.length=keybytes;
+  d2.data=malloc(d2.length);
+  if (d2.data == NULL) {
+    free(d1.data);
+    return (ENOMEM);
+  }
+  k2 = *key;
+  k2.length=d2.length;
+  k2.contents=(void *) d2.data;
+
+  d3.length=keybytes;
+  d3.data=malloc(d3.length);
+  if (d3.data == NULL) {
+    free(d1.data);
+    free(d2.data);
+    return (ENOMEM);
+  }
+  k3 = *key;
+  k3.length=d3.length;
+  k3.contents= (void *) d3.data;
+
+  salt.length=14;
+  salt.data=malloc(salt.length);
+  if (salt.data == NULL) {
+    free(d1.data);
+    free(d2.data);
+    free(d3.data);
+    return (ENOMEM);
+  }
+
+  /* is "input" already blocksize aligned?  if it is, then we need this
+     step, otherwise we do not */
+  plaintext.length=krb5_roundup(input->length+CONFOUNDERLENGTH,blocksize);
+  plaintext.data=malloc(plaintext.length);
+  if (plaintext.data == NULL) {
+    free(d1.data);
+    free(d2.data);
+    free(d3.data);
+    free(salt.data);
+    return(ENOMEM);
+  }
+
+  /* setup convienient pointers into the allocated data */
+  checksum.length=hashsize;
+  checksum.data=output->data;
+  ciphertext.length=krb5_roundup(input->length+CONFOUNDERLENGTH,blocksize);
+  ciphertext.data=output->data+hashsize;
+  confounder.length=CONFOUNDERLENGTH;
+  confounder.data=plaintext.data;
+  output->length = plaintext.length+hashsize;
+
+  /* begin the encryption, computer K1 */
+  ms_usage=krb5int_arcfour_translate_usage(usage);
+  if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
+    strncpy(salt.data, krb5int_arcfour_l40, salt.length);
+    store_32_le(ms_usage, salt.data+10);
+  } else {
+    salt.length=4;
+    store_32_le(ms_usage, salt.data);
+  }
+  krb5_hmac(hash, key, 1, &salt, &d1);
+
+  memcpy(k2.contents, k1.contents, k2.length);
+
+  if (key->enctype==ENCTYPE_ARCFOUR_HMAC_EXP)
+    memset(k1.contents+7, 0xab, 9);
+
+  ret=krb5_c_random_make_octets(/* XXX */ 0, &confounder);
+  memcpy(plaintext.data+confounder.length, input->data, input->length);
+  if (ret)
+    goto cleanup;
+
+  krb5_hmac(hash, &k2, 1, &plaintext, &checksum);
+
+  krb5_hmac(hash, &k1, 1, &checksum, &d3);
+
+  ret=(*(enc->encrypt))(&k3, ivec, &plaintext, &ciphertext);
+
+ cleanup:
+  memset(d1.data, 0, d1.length);
+  memset(d2.data, 0, d2.length);
+  memset(d3.data, 0, d3.length);
+  memset(salt.data, 0, salt.length);
+  memset(plaintext.data, 0, plaintext.length);
+
+  free(d1.data);
+  free(d2.data);
+  free(d3.data);
+  free(salt.data);
+  free(plaintext.data);
+  return (ret);
+}
+
+/* This is the arcfour-hmac decryption routine */
+krb5_error_code
+krb5_arcfour_decrypt(const struct krb5_enc_provider *enc,
+                    const struct krb5_hash_provider *hash,
+                    const krb5_keyblock *key, krb5_keyusage usage,
+                    const krb5_data *ivec, const krb5_data *input,
+                    krb5_data *output)
+{
+  krb5_keyblock k1,k2,k3;
+  krb5_data d1,d2,d3,salt,ciphertext,plaintext,checksum;
+  krb5_keyusage ms_usage;
+  size_t keybytes, keylength, hashsize, blocksize;
+  krb5_error_code ret;
+
+  blocksize = enc->block_size;
+  keybytes = enc->keybytes;
+  keylength = enc->keylength;
+  hashsize = hash->hashsize;
+
+  d1.length=keybytes;
+  d1.data=malloc(d1.length);
+  if (d1.data == NULL)
+    return (ENOMEM);
+  k1 = *key;
+  k1.length=d1.length;
+  k1.contents= (void *) d1.data;
+
+  d2.length=keybytes;
+  d2.data=malloc(d2.length);
+  if (d2.data == NULL) {
+    free(d1.data);
+    return (ENOMEM);
+  }
+  k2 = *key;
+  k2.length=d2.length;
+  k2.contents= (void *) d2.data;
+
+  d3.length=keybytes;
+  d3.data=malloc(d3.length);
+  if  (d3.data == NULL) {
+    free(d1.data);
+    free(d2.data);
+    return (ENOMEM);
+  }
+  k3 = *key;
+  k3.length=d3.length;
+  k3.contents= (void *) d3.data;
+
+  salt.length=14;
+  salt.data=malloc(salt.length);
+  if(salt.data==NULL) {
+    free(d1.data);
+    free(d2.data);
+    free(d3.data);
+    return (ENOMEM);
+  }
+
+  ciphertext.length=input->length-hashsize;
+  ciphertext.data=input->data+hashsize;
+  plaintext.length=ciphertext.length;
+  plaintext.data=malloc(plaintext.length);
+  if (plaintext.data == NULL) {
+    free(d1.data);
+    free(d2.data);
+    free(d3.data);
+    free(salt.data);
+    return (ENOMEM);
+  }
+
+  checksum.length=hashsize;
+  checksum.data=input->data;
+
+  ms_usage=krb5int_arcfour_translate_usage(usage);
+
+  /* We may have to try two ms_usage values; see below. */
+  do {
+      /* compute the salt */
+      if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
+         strncpy(salt.data, krb5int_arcfour_l40, salt.length);
+         store_32_le(ms_usage, salt.data + 10);
+      } else {
+         salt.length = 4;
+         store_32_le(ms_usage, salt.data);
+      }
+      ret = krb5_hmac(hash, key, 1, &salt, &d1);
+      if (ret)
+         goto cleanup;
+
+      memcpy(k2.contents, k1.contents, k2.length);
+
+      if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP)
+         memset(k1.contents + 7, 0xab, 9);
+
+      ret = krb5_hmac(hash, &k1, 1, &checksum, &d3);
+      if (ret)
+         goto cleanup;
+
+      ret = (*(enc->decrypt))(&k3, ivec, &ciphertext, &plaintext);
+      if (ret)
+         goto cleanup;
+
+      ret = krb5_hmac(hash, &k2, 1, &plaintext, &d1);
+      if (ret)
+         goto cleanup;
+
+      if (memcmp(checksum.data, d1.data, hashsize) != 0) {
+         if (ms_usage == 9) {
+             /*
+              * RFC 4757 specifies usage 8 for TGS-REP encrypted
+              * parts encrypted in a subkey, but the value used by MS
+              * is actually 9.  We now use 9 to start with, but fall
+              * back to 8 on failure in case we are communicating
+              * with a KDC using the value from the RFC.
+              */
+             ms_usage = 8;
+             continue;
+         }
+         ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+         goto cleanup;
+      }
+
+      break;
+  } while (1);
+
+  memcpy(output->data, plaintext.data+CONFOUNDERLENGTH,
+        (plaintext.length-CONFOUNDERLENGTH));
+  output->length=plaintext.length-CONFOUNDERLENGTH;
+
+ cleanup:
+  memset(d1.data, 0, d1.length);
+  memset(d2.data, 0, d2.length);
+  memset(d3.data, 0, d2.length);
+  memset(salt.data, 0, salt.length);
+  memset(plaintext.data, 0, plaintext.length);
+
+  free(d1.data);
+  free(d2.data);
+  free(d3.data);
+  free(salt.data);
+  free(plaintext.data);
+  return (ret);
+}
+
diff --git a/src/lib/crypto/openssl/arcfour/arcfour.h b/src/lib/crypto/openssl/arcfour/arcfour.h
new file mode 100644 (file)
index 0000000..be408fe
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef ARCFOUR_H
+#define ARCFOUR_H
+
+extern void
+krb5_arcfour_encrypt_length(const struct krb5_enc_provider *,
+                       const struct krb5_hash_provider *,
+                       size_t,
+                       size_t *);
+
+extern 
+krb5_error_code krb5_arcfour_encrypt(const struct krb5_enc_provider *,
+                       const struct krb5_hash_provider *,
+                       const krb5_keyblock *,
+                       krb5_keyusage,
+                       const krb5_data *,
+                       const krb5_data *,
+                       krb5_data *);
+
+extern 
+krb5_error_code krb5_arcfour_decrypt(const struct krb5_enc_provider *,
+                       const struct krb5_hash_provider *,
+                       const krb5_keyblock *,
+                       krb5_keyusage,
+                       const krb5_data *,
+                       const krb5_data *,
+                       krb5_data *);
+
+extern krb5_error_code krb5int_arcfour_string_to_key(
+     const struct krb5_enc_provider *,
+     const krb5_data *,
+     const krb5_data *,
+     const krb5_data *,
+     krb5_keyblock *);
+
+extern const struct krb5_enc_provider krb5int_enc_arcfour;
+extern const struct krb5_aead_provider krb5int_aead_arcfour;
+ krb5_error_code krb5int_arcfour_prf(
+                                        const struct krb5_enc_provider *enc,
+                                        const struct krb5_hash_provider *hash,
+                                        const krb5_keyblock *key,
+                                        const krb5_data *in, krb5_data *out);
+
+#endif /* ARCFOUR_H */
diff --git a/src/lib/crypto/openssl/arcfour/arcfour_aead.c b/src/lib/crypto/openssl/arcfour/arcfour_aead.c
new file mode 100644 (file)
index 0000000..cff7d66
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * lib/crypto/arcfour/arcfour_aead.c
+ *
+ * Copyright 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ */
+
+
+#include "k5-int.h"
+#include "arcfour.h"
+#include "arcfour-int.h"
+#include "aead.h"
+
+/* AEAD */
+
+static krb5_error_code
+krb5int_arcfour_crypto_length(const struct krb5_aead_provider *aead,
+                             const struct krb5_enc_provider *enc,
+                             const struct krb5_hash_provider *hash,
+                             krb5_cryptotype type,
+                             unsigned int *length)
+{
+    switch (type) {
+    case KRB5_CRYPTO_TYPE_HEADER:
+       *length = hash->hashsize + CONFOUNDERLENGTH;
+       break;
+    case KRB5_CRYPTO_TYPE_PADDING:
+       *length = 0;
+       break;
+    case KRB5_CRYPTO_TYPE_TRAILER:
+       *length = 0;
+       break;
+    case KRB5_CRYPTO_TYPE_CHECKSUM:
+       *length = hash->hashsize;
+       break;
+    default:
+       assert(0 && "invalid cryptotype passed to krb5int_arcfour_crypto_length");
+       break;
+    }
+
+    return 0;
+}
+
+static krb5_error_code
+alloc_derived_key(const struct krb5_enc_provider *enc,
+                 krb5_keyblock *dst,
+                 krb5_data *data,
+                 const krb5_keyblock *src)
+{
+    data->length = enc->keybytes;
+    data->data = malloc(data->length);
+    if (data->data == NULL)
+       return ENOMEM;
+
+    *dst = *src;
+    dst->length = data->length;
+    dst->contents = (void *)data->data;
+
+    return 0;
+}
+
+static krb5_error_code
+krb5int_arcfour_encrypt_iov(const struct krb5_aead_provider *aead,
+                           const struct krb5_enc_provider *enc,
+                           const struct krb5_hash_provider *hash,
+                           const krb5_keyblock *key,
+                           krb5_keyusage usage,
+                           const krb5_data *ivec,
+                           krb5_crypto_iov *data,
+                           size_t num_data)
+{
+    krb5_error_code ret;
+    krb5_crypto_iov *header, *trailer;
+    krb5_keyblock k1, k2, k3;
+    krb5_data d1, d2, d3;
+    krb5_data checksum, confounder, header_data;
+    krb5_keyusage ms_usage;
+    char salt_data[14];
+    krb5_data salt;
+    size_t i;
+
+    d1.length = d2.length = d3.length = 0;
+    d1.data = d2.data = d3.data = NULL;
+
+    /*
+     * Caller must have provided space for the header, padding
+     * and trailer; per RFC 4757 we will arrange it as:
+     *
+     *     Checksum | E(Confounder | Plaintext) 
+     */
+
+    header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+    if (header == NULL ||
+       header->data.length < hash->hashsize + CONFOUNDERLENGTH)
+       return KRB5_BAD_MSIZE;
+
+    header_data = header->data;
+
+    /* Trailer may be absent */
+    trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+    if (trailer != NULL)
+       trailer->data.length = 0;
+
+    /* Ensure that there is no padding */
+    for (i = 0; i < num_data; i++) {
+       if (data[i].flags == KRB5_CRYPTO_TYPE_PADDING)
+           data[i].data.length = 0;
+    }
+
+    ret = alloc_derived_key(enc, &k1, &d1, key);
+    if (ret != 0)
+       goto cleanup;
+
+    ret = alloc_derived_key(enc, &k2, &d2, key);
+    if (ret != 0)
+       goto cleanup;
+
+    ret = alloc_derived_key(enc, &k3, &d3, key);
+    if (ret != 0)
+       goto cleanup;
+
+    /* Begin the encryption, compute K1 */
+    salt.data = salt_data;
+    salt.length = sizeof(salt_data);
+
+    ms_usage = krb5int_arcfour_translate_usage(usage);
+
+    if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
+       strncpy(salt.data, krb5int_arcfour_l40, salt.length);
+       store_32_le(ms_usage, salt.data + 10);
+    } else {
+       salt.length = 4;
+       store_32_le(ms_usage, salt.data);
+    }
+    ret = krb5_hmac(hash, key, 1, &salt, &d1);
+    if (ret != 0)
+       goto cleanup;
+
+    memcpy(k2.contents, k1.contents, k2.length);
+
+    if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP)
+       memset(k1.contents + 7, 0xAB, 9);
+
+    header->data.length = hash->hashsize + CONFOUNDERLENGTH;
+
+    confounder.data = header->data.data + hash->hashsize;
+    confounder.length = CONFOUNDERLENGTH;
+
+    ret = krb5_c_random_make_octets(0, &confounder);
+    if (ret != 0)
+       goto cleanup;
+
+    checksum.data = header->data.data;
+    checksum.length = hash->hashsize;
+
+    /* Adjust pointers so confounder is at start of header */
+    header->data.length -= hash->hashsize;
+    header->data.data   += hash->hashsize;
+
+    ret = krb5int_hmac_iov(hash, &k2, data, num_data, &checksum);
+    if (ret != 0)
+       goto cleanup;
+
+    ret = krb5_hmac(hash, &k1, 1, &checksum, &d3);
+    if (ret != 0)
+       goto cleanup;
+
+    ret = enc->encrypt_iov(&k3, ivec, data, num_data);
+    if (ret != 0)
+       goto cleanup;
+
+cleanup:
+    header->data = header_data; /* restore header pointers */
+
+    if (d1.data != NULL) {
+       memset(d1.data, 0, d1.length);
+       free(d1.data);
+    }
+    if (d2.data != NULL) {
+       memset(d2.data, 0, d2.length);
+       free(d2.data);
+    }
+    if (d3.data != NULL) {
+       memset(d3.data, 0, d3.length);
+       free(d3.data);
+    }
+
+    return ret;
+}
+
+static krb5_error_code
+krb5int_arcfour_decrypt_iov(const struct krb5_aead_provider *aead,
+                           const struct krb5_enc_provider *enc,
+                           const struct krb5_hash_provider *hash,
+                           const krb5_keyblock *key,
+                           krb5_keyusage usage,
+                           const krb5_data *ivec,
+                           krb5_crypto_iov *data,
+                           size_t num_data)
+{
+    krb5_error_code ret;
+    krb5_crypto_iov *header, *trailer;
+    krb5_keyblock k1, k2, k3;
+    krb5_data d1, d2, d3;
+    krb5_data checksum, header_data;
+    krb5_keyusage ms_usage;
+    char salt_data[14];
+    krb5_data salt;
+
+    d1.length = d2.length = d3.length = 0;
+    d1.data = d2.data = d3.data = NULL;
+
+    header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+    if (header == NULL ||
+        header->data.length != hash->hashsize + CONFOUNDERLENGTH)
+       return KRB5_BAD_MSIZE;
+
+    header_data = header->data;
+
+    trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+    if (trailer != NULL && trailer->data.length != 0)
+       return KRB5_BAD_MSIZE;
+    
+    ret = alloc_derived_key(enc, &k1, &d1, key);
+    if (ret != 0)
+       goto cleanup;
+
+    ret = alloc_derived_key(enc, &k2, &d2, key);
+    if (ret != 0)
+       goto cleanup;
+
+    ret = alloc_derived_key(enc, &k3, &d3, key);
+    if (ret != 0)
+       goto cleanup;
+
+    /* Begin the decryption, compute K1 */
+    salt.data = salt_data;
+    salt.length = sizeof(salt_data);
+
+    ms_usage = krb5int_arcfour_translate_usage(usage);
+
+    if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
+       strncpy(salt.data, krb5int_arcfour_l40, salt.length);
+       store_32_le(ms_usage, (unsigned char *)salt.data + 10);
+    } else {
+       salt.length = 4;
+       store_32_le(ms_usage, (unsigned char *)salt.data);
+    }
+    ret = krb5_hmac(hash, key, 1, &salt, &d1);
+    if (ret != 0)
+       goto cleanup;
+
+    memcpy(k2.contents, k1.contents, k2.length);
+
+    if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP)
+       memset(k1.contents + 7, 0xAB, 9);
+
+    checksum.data = header->data.data;
+    checksum.length = hash->hashsize;
+
+    /* Adjust pointers so confounder is at start of header */
+    header->data.length -= hash->hashsize;
+    header->data.data   += hash->hashsize;
+
+    ret = krb5_hmac(hash, &k1, 1, &checksum, &d3);
+    if (ret != 0)
+       goto cleanup;
+
+    ret = enc->decrypt_iov(&k3, ivec, data, num_data);
+    if (ret != 0)
+       goto cleanup;
+
+    ret = krb5int_hmac_iov(hash, &k2, data, num_data, &d1);
+    if (ret != 0)
+       goto cleanup;
+
+    if (memcmp(checksum.data, d1.data, hash->hashsize) != 0) {
+       ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+       goto cleanup;
+    }
+
+cleanup:
+    header->data = header_data; /* restore header pointers */
+
+    if (d1.data != NULL) {
+       memset(d1.data, 0, d1.length);
+       free(d1.data);
+    }
+    if (d2.data != NULL) {
+       memset(d2.data, 0, d2.length);
+       free(d2.data);
+    }
+    if (d3.data != NULL) {
+       memset(d3.data, 0, d3.length);
+       free(d3.data);
+    }
+
+    return ret;
+}
+
+const struct krb5_aead_provider krb5int_aead_arcfour = {
+    krb5int_arcfour_crypto_length,
+    krb5int_arcfour_encrypt_iov,
+    krb5int_arcfour_decrypt_iov
+};
+
diff --git a/src/lib/crypto/openssl/arcfour/arcfour_s2k.c b/src/lib/crypto/openssl/arcfour/arcfour_s2k.c
new file mode 100644 (file)
index 0000000..41053ed
--- /dev/null
@@ -0,0 +1,59 @@
+#include "k5-int.h"
+#include "k5-utf8.h"
+#include "rsa-md4.h"
+#include "arcfour-int.h"
+
+#if TARGET_OS_MAC && !defined(DEPEND)
+#include <CoreFoundation/CFString.h>
+#endif
+
+krb5_error_code
+krb5int_arcfour_string_to_key(const struct krb5_enc_provider *enc,
+                             const krb5_data *string, const krb5_data *salt,
+                             const krb5_data *params, krb5_keyblock *key)
+{
+  krb5_error_code err = 0;
+  krb5_MD4_CTX md4_context;
+  unsigned char *copystr;
+  size_t copystrlen;
+
+  if (params != NULL)
+      return KRB5_ERR_BAD_S2K_PARAMS;
+  
+  if (key->length != 16)
+    return (KRB5_BAD_MSIZE);
+
+  /* We ignore salt per the Microsoft spec*/
+
+  /* compute the space needed for the new string.
+     Since the password must be stored in unicode, we need to increase
+     that number by 2x.
+  */
+
+  err = krb5int_utf8cs_to_ucs2les(string->data, string->length, &copystr, &copystrlen);
+  if (err)
+    return err;
+
+  /* the actual MD4 hash of the data */
+  krb5_MD4Init(&md4_context);
+  krb5_MD4Update(&md4_context, copystr, copystrlen);
+  krb5_MD4Final(&md4_context);
+  memcpy(key->contents, md4_context.digest, 16);
+
+#if 0  
+  /* test the string_to_key function */
+  printf("Hash=");
+  {
+    int counter;
+    for(counter=0;counter<16;counter++)
+      printf("%02x", md4_context.digest[counter]);
+    printf("\n");
+  }
+#endif /* 0 */
+
+  /* Zero out the data behind us */
+  memset(copystr, 0, copystrlen);
+  memset(&md4_context, 0, sizeof(md4_context));
+  free(copystr);
+  return err;
+}