Patches contributed by David E. Cross" <crossd@cs.rpi.edu> to add
authorSam Hartman <hartmans@mit.edu>
Thu, 18 Oct 2001 19:38:48 +0000 (19:38 +0000)
committerSam Hartman <hartmans@mit.edu>
Thu, 18 Oct 2001 19:38:48 +0000 (19:38 +0000)
RC4-hmac support.  This directory is taken from a diff at a url posted
to krbdev with krb5int_hash_md5 substituted for krb5_hash_md5 so that
the code would compile.
krb5int_enc_arcfour has been substituted for krb5_enc_arcfour as well.

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

src/lib/crypto/arcfour/Makefile.in [new file with mode: 0644]
src/lib/crypto/arcfour/arcfour-int.h [new file with mode: 0644]
src/lib/crypto/arcfour/arcfour.c [new file with mode: 0644]
src/lib/crypto/arcfour/arcfour.h [new file with mode: 0644]
src/lib/crypto/arcfour/string_to_key.c [new file with mode: 0644]

diff --git a/src/lib/crypto/arcfour/Makefile.in b/src/lib/crypto/arcfour/Makefile.in
new file mode 100644 (file)
index 0000000..1fb2806
--- /dev/null
@@ -0,0 +1,37 @@
+thisconfigdir=./..
+myfulldir=lib/crypto/arcfour
+mydir=arcfour
+BUILDTOP=$(REL)$(U)$(S)$(U)$(S)$(U)
+LOCALINCLUDES = -I$(srcdir)/.. -I$(srcdir)/../md4
+
+##DOS##BUILDTOP = ..\..\..
+##DOS##PREFIXDIR=arcfour
+##DOS##OBJFILE=..\$(OUTPRE)arcfour.lst
+##WIN16##LIBNAME=..\crypto.lib
+
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+
+RUN_SETUP = @KRB5_RUN_ENV@ KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf
+
+STLIBOBJS=\
+       arcfour.o       \
+       string_to_key.o
+
+OBJS=\
+       $(OUTPRE)arcfour.$(OBJEXT)      \
+       $(OUTPRE)string_to_key.$(OBJEXT)
+
+SRCS=\
+       $(srcdir)/arcfour.c     \
+       $(srcdir)/string_to_key.c
+
+##DOS##LIBOBJS = $(OBJS)
+
+all-unix:: all-libobjs
+
+includes:: depend
+
+depend:: $(SRCS)
+
+clean-unix:: clean-libobjs
diff --git a/src/lib/crypto/arcfour/arcfour-int.h b/src/lib/crypto/arcfour/arcfour-int.h
new file mode 100644 (file)
index 0000000..2b25576
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+
+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"
+
+#define CONFOUNDERLENGTH 8
+
+typedef struct
+{
+   unsigned int x;
+   unsigned int y;
+   unsigned char state[256];
+} ArcfourContext;
+
+/* gets the next byte from the PRNG */
+static inline unsigned int k5_arcfour_byte(ArcfourContext *);
+
+/* Initializes the context and sets the key. */
+static krb5_error_code k5_arcfour_init(ArcfourContext *ctx, const unsigned char *key, 
+                 unsigned int keylen);
+
+/* Encrypts/decrypts data. */
+static void k5_arcfour_crypt(ArcfourContext *ctx, unsigned char *dest, 
+                    const unsigned char *src, unsigned int len);
+
+/* Interface layer to kerb5 crypto layer */
+static krb5_error_code
+k5_arcfour_docrypt(krb5_const krb5_keyblock *, krb5_const krb5_data *,
+                  krb5_const krb5_data *, krb5_data *);
+
+
+/* The blocksize for the enctype */
+static void k5_arcfour_blocksize(size_t *);
+
+/* keysize for the enctype (number of bytes, and length of key (parity/etc) */
+static void k5_arcfour_keysize(size_t *, size_t *);
+
+/* from a random bitstrem, construct a key */
+static krb5_error_code
+k5_arcfour_make_key(krb5_const krb5_data *, krb5_keyblock *);
+
+#endif /* ARCFOUR_INT_H */
diff --git a/src/lib/crypto/arcfour/arcfour.c b/src/lib/crypto/arcfour/arcfour.c
new file mode 100644 (file)
index 0000000..d96a651
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+
+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"
+const unsigned char *l40 = "fortybits";
+
+void
+krb5_arcfour_encrypt_length(enc, hash, inputlen, length)
+     krb5_const struct krb5_enc_provider *enc;
+     krb5_const struct krb5_hash_provider *hash;
+     size_t inputlen;
+     size_t *length;
+{
+  size_t blocksize, hashsize;
+
+  (*(enc->block_size))(&blocksize);
+  (*(hash->hash_size))(&hashsize);
+
+
+  /* checksum + (confounder + inputlen, in even blocksize) */
+  *length = hashsize + krb5_roundup(8 + inputlen, blocksize);
+}
+
+static krb5_keyusage arcfour_translate_usage(krb5_keyusage usage)
+{
+  return usage;
+}
+
+krb5_error_code
+krb5_arcfour_encrypt(enc, hash, key, usage, ivec, input, output)
+     krb5_const struct krb5_enc_provider *enc;
+     krb5_const struct krb5_hash_provider *hash;
+     krb5_const krb5_keyblock *key;
+     krb5_keyusage usage;
+     krb5_const krb5_data *ivec;
+     krb5_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;
+
+  (*(enc->block_size))(&blocksize);
+  (*(enc->keysize))(&keybytes, &keylength);
+  (*(hash->hash_size))(&hashsize);
+  
+  d1.length=keybytes;
+  d1.data=malloc(d1.length);
+  if (d1.data == NULL)
+    return (ENOMEM);
+  memcpy(&k1, key, sizeof (krb5_keyblock));
+  k1.length=d1.length;
+  k1.contents=d1.data;
+
+  d2.length=keybytes;
+  d2.data=malloc(d2.length);
+  if (d2.data == NULL) {
+    free(d1.data);
+    return (ENOMEM);
+  }
+  memcpy(&k2, key, sizeof (krb5_keyblock));
+  k2.length=d2.length;
+  k2.contents=d2.data;
+
+  d3.length=keybytes;
+  d3.data=malloc(d3.length);
+  if (d3.data == NULL) {
+    free(d1.data);
+    free(d2.data);
+    return (ENOMEM);
+  }
+  memcpy(&k3, key, sizeof (krb5_keyblock));
+  k3.length=d3.length;
+  k3.contents=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;
+  
+  /* begin the encryption, computer K1 */
+  ms_usage=arcfour_translate_usage(usage);
+  if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
+    strncpy(salt.data, l40, salt.length);
+    salt.data[10]=ms_usage & 0xff;
+    salt.data[11]=(ms_usage >> 8) & 0xff;
+    salt.data[12]=(ms_usage >> 16) & 0xff;
+    salt.data[13]=(ms_usage >> 24) & 0xff;
+  } else {
+    salt.length=4;
+    salt.data[0]=ms_usage & 0xff;
+    salt.data[1]=(ms_usage >> 8) & 0xff;
+    salt.data[2]=(ms_usage >> 16) & 0xff;
+    salt.data[3]=(ms_usage >> 24) & 0xff;
+  }
+  krb5_hmac(hash, key, 1, &salt, &d1);
+
+  memcpy(k2.contents, k1.contents, k2.length);
+
+  if (key->enctype==ENCTYPE_ARCFOUR_HMAC)
+    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(enc, hash, key, usage, ivec, input, output)
+     krb5_const struct krb5_enc_provider *enc;
+     krb5_const struct krb5_hash_provider *hash;
+     krb5_const krb5_keyblock *key;
+     krb5_keyusage usage;
+     krb5_const krb5_data *ivec;
+     krb5_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;
+
+  (*(enc->block_size))(&blocksize);
+  (*(enc->keysize))(&keybytes, &keylength);
+  (*(hash->hash_size))(&hashsize);
+
+  d1.length=keybytes;
+  d1.data=malloc(d1.length);
+  if (d1.data == NULL)
+    return (ENOMEM);
+  memcpy(&k1, key, sizeof (krb5_keyblock));
+  k1.length=d1.length;
+  k1.contents=d1.data;
+  
+  d2.length=keybytes;
+  d2.data=malloc(d2.length);
+  if (d2.data == NULL) {
+    free(d1.data);
+    return (ENOMEM);
+  }
+  memcpy(&k2, key, sizeof(krb5_keyblock));
+  k2.length=d2.length;
+  k2.contents=d2.data;
+
+  d3.length=keybytes;
+  d3.data=malloc(d3.length);
+  if  (d3.data == NULL) {
+    free(d1.data);
+    free(d2.data);
+    return (ENOMEM);
+  }
+  memcpy(&k3, key, sizeof(krb5_keyblock));
+  k3.length=d3.length;
+  k3.contents=d3.data;
+
+  salt.length=14;
+  salt.data=malloc(salt.length);
+  if(salt.data==NULL) {
+    free(d1.data);
+    free(d2.data);
+    free(d3.data);
+  }
+
+  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);
+  }
+
+  checksum.length=hashsize;
+  checksum.data=input->data;
+
+  /* compute the salt */
+  ms_usage=arcfour_translate_usage(usage);
+  if (key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
+    strncpy(salt.data, l40, salt.length);
+    salt.data[10]=ms_usage & 0xff;
+    salt.data[11]=(ms_usage>>8) & 0xff;
+    salt.data[12]=(ms_usage>>16) & 0xff;
+    salt.data[13]=(ms_usage>>24) & 0xff;
+  } else {
+    salt.length=4;
+    salt.data[0]=ms_usage & 0xff;
+    salt.data[1]=(ms_usage>>8) & 0xff;
+    salt.data[2]=(ms_usage>>16) & 0xff;
+    salt.data[3]=(ms_usage>>24) & 0xff;
+  }
+  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) {
+    ret=KRB5KRB_AP_ERR_BAD_INTEGRITY;
+    goto cleanup;
+  }
+
+  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/arcfour/arcfour.h b/src/lib/crypto/arcfour/arcfour.h
new file mode 100644 (file)
index 0000000..2b04680
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef ARCFOUR_H
+#define ARCFOUR_H
+
+void
+krb5_arcfour_encrypt_length(krb5_const struct krb5_enc_provider *,
+                       krb5_const struct krb5_hash_provider *,
+                       size_t,
+                       size_t *);
+
+krb5_error_code krb5_arcfour_encrypt(krb5_const struct krb5_enc_provider *,
+                       krb5_const struct krb5_hash_provider *,
+                       krb5_const krb5_keyblock *,
+                       krb5_keyusage,
+                       krb5_const krb5_data *,
+                       krb5_const krb5_data *,
+                       krb5_data *);
+
+krb5_error_code krb5_arcfour_decrypt(krb5_const struct krb5_enc_provider *,
+                       krb5_const struct krb5_hash_provider *,
+                       krb5_const krb5_keyblock *,
+                       krb5_keyusage,
+                       krb5_const krb5_data *,
+                       krb5_const krb5_data *,
+                       krb5_data *);
+
+krb5_error_code krb5_arcfour_string_to_key(
+     krb5_const struct krb5_enc_provider *,
+     krb5_const krb5_data *,
+     krb5_const krb5_data *,
+     krb5_keyblock *);
+
+const struct krb5_enc_provider krb5int_enc_arcfour;
+#endif /* ARCFOUR_H */
diff --git a/src/lib/crypto/arcfour/string_to_key.c b/src/lib/crypto/arcfour/string_to_key.c
new file mode 100644 (file)
index 0000000..d41bc25
--- /dev/null
@@ -0,0 +1,71 @@
+#include "k5-int.h"
+#include "rsa-md4.h"
+#include "arcfour-int.h"
+
+static void asctouni(unsigned char *unicode, unsigned char *ascii, int len)
+{
+       int counter;
+       for (counter=0;counter<len;counter++) {
+               unicode[2*counter]=ascii[counter];
+               unicode[2*counter + 1]=0x00;
+       }
+}
+
+krb5_error_code
+krb5_arcfour_string_to_key(enc, string, salt, key)
+       krb5_const struct krb5_enc_provider *enc;
+       krb5_const krb5_data *string;
+       krb5_const krb5_data *salt;
+       krb5_keyblock *key;
+{
+  int len,slen,saltlen,counter;
+  unsigned char *copystr;
+  krb5_MD4_CTX md4_context;
+  
+  if (key->length != 16)
+    return (KRB5_BAD_MSIZE);
+
+  /* handle the salt...
+     We really don't salt our key, else it won't work with MSFT, but
+     handle it anyway
+  */
+  saltlen=salt?salt->length:0;
+
+  /* compute the space needed for the new string.
+     Since the password must be stored in unicode, we need to increase
+     that number by 2x.
+
+     This should be re-evauated in the future, it makes the assumption that
+     thes user's password is in ascii.
+  */
+  slen = ((string->length)>128)?128:string->length;
+  len=(slen)*2 + saltlen;
+
+  copystr = malloc((size_t) len);
+  if (copystr == NULL)
+    return ENOMEM;
+
+  /* make the string.  start by creating the unicode version of the password
+     then copy the salt to the end of the string */
+  asctouni(copystr, string->data, slen );
+  memcpy(copystr+(slen*2), salt->data, saltlen);
+
+  /* the actual MD4 hash of the data */
+  krb5_MD4Init(&md4_context);
+  krb5_MD4Update(&md4_context, (unsigned char *)copystr, len);
+  krb5_MD4Final(&md4_context);
+  memcpy(key->contents, md4_context.digest, 16);
+
+#if 0  
+  /* test the string_to_key function */
+  printf("Hash=");
+  for(counter=0;counter<16;counter++)
+    printf("%02x", md4_context.digest[counter]);
+  printf("\n");
+#endif /* 0 */
+
+  /* Zero out the data behind us */
+  bzero(copystr, len);
+  bzero(&md4_context, sizeof(md4_context));
+  return 0;
+}