/*----------------------------------------------------------------------------+
 |                                                                            |
 |   Package: krypto                                                          |
 |   Authors: Thomas Wu and Eugene Jhong                                      |
 |                                                                            |
 +----------------------------------------------------------------------------*/

/*
 * Copyright (c) 1997 Stanford University
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
 * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "krypto.h"
#include "krypto_locl.h"

extern void cipher_crypt_ecb ();
extern void cipher_crypt_cbc ();
extern void cipher_crypt_cfb ();
extern void cipher_crypt_ofb ();

extern cipher_desc NONEdesc;

#ifndef NOENCRYPTION

#ifdef CIPHER_ARCFOUR
extern cipher_desc ARCFOURdesc;
#endif
#ifdef CIPHER_BLOWFISH
extern cipher_desc BF_ECBdesc;
extern cipher_desc BF_CBCdesc;
extern cipher_desc BF_CFB64desc;
extern cipher_desc BF_OFB64desc;
#endif
#ifdef CIPHER_DES
extern cipher_desc DES_ECBdesc;
extern cipher_desc DES_CBCdesc;
extern cipher_desc DES_CFB64desc;
extern cipher_desc DES_OFB64desc;
extern cipher_desc DES3_ECBdesc;
extern cipher_desc DES3_CBCdesc;
extern cipher_desc DES3_CFB64desc;
extern cipher_desc DES3_OFB64desc;
#endif
#ifdef CIPHER_IDEA
extern cipher_desc IDEA_ECBdesc;
extern cipher_desc IDEA_CBCdesc;
extern cipher_desc IDEA_CFB64desc;
extern cipher_desc IDEA_OFB64desc;
#endif
#ifdef CIPHER_CAST5
extern cipher_desc CAST5_ECBdesc;
extern cipher_desc CAST5_CBCdesc;
extern cipher_desc CAST5_CFB64desc;
extern cipher_desc CAST5_OFB64desc;
#endif

#endif

static cipher_desc *allCiphers[] =
{
  &NONEdesc,

#ifndef NOENCRYPTION

#ifdef CIPHER_BLOWFISH
  &BF_ECBdesc, &BF_CBCdesc, &BF_CFB64desc, &BF_OFB64desc,
#endif
#ifdef CIPHER_CAST5
  &CAST5_ECBdesc, &CAST5_CBCdesc, &CAST5_CFB64desc, &CAST5_OFB64desc,
#endif
#ifdef CIPHER_DES
  &DES_ECBdesc, &DES_CBCdesc, &DES_CFB64desc, &DES_OFB64desc,
  &DES3_ECBdesc, &DES3_CBCdesc, &DES3_CFB64desc, &DES3_OFB64desc,
#endif
#ifdef CIPHER_IDEA
  &IDEA_ECBdesc, &IDEA_CBCdesc, &IDEA_CFB64desc, &IDEA_OFB64desc,
#endif
#ifdef CIPHER_ARCFOUR
  &ARCFOURdesc,
#endif

#endif

  0
};


#define NCIPHERS (sizeof (allCiphers) / sizeof (cipher_desc *))
static unsigned char cipherlist[NCIPHERS] = "";


unsigned char *
cipher_getlist()
{
  cipher_desc **descp;
  unsigned char *s;

  if(*cipherlist == '\0') {
    for(s = cipherlist, descp = allCiphers; *descp; ++descp, ++s)
      *s = (*descp)->id;
    *s = '\0';
  }
  return cipherlist;
}

cipher_desc *
cipher_getdescbyname (str)
  char *str;
{
  cipher_desc **descp;

  for (descp = allCiphers; *descp; ++descp)
    if(strcasecmp ((*descp)->name, str) == 0) return *descp;
  return 0;
}

cipher_desc *
cipher_getdescbyid (id)
  unsigned char id;
{
  cipher_desc **descp;

  for (descp = allCiphers; *descp; ++descp)
    if ((*descp)->id == id) return *descp;
  return 0;
}

int
cipher_supported (list, id)
  unsigned char *list;
  unsigned char id;
{
  int i;
  
  for (i = 0; i < strlen (list); i++)
    if (list[i] == id) return 1;

  return 0;
}

cipher *
cipher_new (cd)
  cipher_desc *cd;
{
  cipher *newcipher;

  if ((newcipher = (cipher *) malloc (sizeof (cipher))) == 0)
    return 0;

  if ((newcipher->iv = (unsigned char *) malloc (cd->blklen)) == 0)
    return 0;

  newcipher->cipher = cd;
  newcipher->context = 0;

  return newcipher;
}

void
cipher_delete (c)
  cipher *c;
{
  (c->cipher->delete) (c->context);
  memset (c->iv, 0, c->cipher->blklen);
  free (c->iv);
  free (c);
}

unsigned
cipher_getminkeylen (c)
  cipher *c;
{
  return c->cipher->keylen;
}

unsigned
cipher_getblklen (c)
  cipher *c;
{
  return c->cipher->blklen;
}

unsigned long
cipher_getoutlen (c, inlen)
  cipher *c;
  unsigned long inlen;
{
  return ((inlen + c->cipher->inblklen - 1) / c->cipher->inblklen) *
    c->cipher->inblklen;
}

int
cipher_setiv (c, iv, len)
  cipher *c;
  unsigned char *iv;
  unsigned len;
{
  if (len != c->cipher->blklen) return -1;
  memcpy (c->iv, iv, len);
  return 0;
}

int
cipher_initencrypt (c, key, klen)
  cipher *c;
  unsigned char *key;
  unsigned klen;
{
  if (c->context != 0) (c->cipher->delete) (c->context);

  c->state = CIPHER_STATE_ENCRYPT;
  c->num = 0;
  memset (c->iv, 0, c->cipher->blklen);

  c->context = (c->cipher->new) (key, klen);
  if (c->context == NULL) return -1;
  else return 0;
}

int
cipher_initdecrypt (c, key, klen)
  cipher *c;
  unsigned char *key;
  unsigned klen;
{
  if (c->context != 0) (c->cipher->delete) (c->context);

  c->state = CIPHER_STATE_DECRYPT;
  c->num = 0;
  memset (c->iv, 0, c->cipher->blklen);

  c->context = (c->cipher->new) (key, klen);  
  if (c->context == NULL) return -1;
  else return 0;
}

void
cipher_crypt (c, in, out, len)
  cipher *c;
  unsigned char *in;
  unsigned char *out;
  unsigned long len;
{
  switch (c->cipher->type)
  {
  case CIPHER_TYPE_STREAM:
   
    memcpy (out, in, len);
    c->cipher->crypt (c->context, out, len, c->state);
    break;

  case CIPHER_TYPE_BLOCK:

    switch (c->cipher->mode)
    {
    case CIPHER_MODE_ECB: cipher_crypt_ecb (c, in, out, len); break;
    case CIPHER_MODE_CBC: cipher_crypt_cbc (c, in, out, len); break;
    case CIPHER_MODE_CFB64: cipher_crypt_cfb (c, in, out, len); break;
    case CIPHER_MODE_OFB64: cipher_crypt_ofb (c, in, out, len); break;
    }
    break;

  default:

    break;
  }
}
