/*
 * This file is part of the SSH Library
 *
 * Copyright (c) 2009 by Aris Adamantiadis
 *
 * The SSH Library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version.
 *
 * The SSH Library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with the SSH Library; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/crypto.h"
#include "libssh/wrapper.h"
#include "libssh/libcrypto.h"

#ifdef HAVE_LIBCRYPTO

#include <openssl/sha.h>
#include <openssl/md5.h>
#include <openssl/dsa.h>
#include <openssl/rsa.h>
#include <openssl/hmac.h>
#include <openssl/opensslv.h>
#include <openssl/rand.h>

#ifdef HAVE_OPENSSL_AES_H
#define HAS_AES
#include <openssl/aes.h>
#endif
#ifdef HAVE_OPENSSL_BLOWFISH_H
#define HAS_BLOWFISH
#include <openssl/blowfish.h>
#endif
#ifdef HAVE_OPENSSL_DES_H
#define HAS_DES
#include <openssl/des.h>
#endif

#if (OPENSSL_VERSION_NUMBER<0x00907000L)
#define OLD_CRYPTO
#endif

#include "libssh/crypto.h"

struct ssh_mac_ctx_struct {
  enum ssh_mac_e mac_type;
  union {
    SHACTX sha1_ctx;
    SHA256CTX sha256_ctx;
    SHA384CTX sha384_ctx;
    SHA512CTX sha512_ctx;
  } ctx;
};

static int alloc_key(struct ssh_cipher_struct *cipher) {
    cipher->key = malloc(cipher->keylen);
    if (cipher->key == NULL) {
      return -1;
    }

    return 0;
}

void ssh_reseed(void){
#ifndef _WIN32
    struct timeval tv;
    gettimeofday(&tv, NULL);
    RAND_add(&tv, sizeof(tv), 0.0);
#endif
}

SHACTX sha1_init(void)
{
    int rc;
    SHACTX c = EVP_MD_CTX_create();
    if (c == NULL) {
        return NULL;
    }
    EVP_MD_CTX_init(c);
    rc = EVP_DigestInit_ex(c, EVP_sha1(), NULL);
    if (rc == 0) {
        EVP_MD_CTX_destroy(c);
        c = NULL;
    }
    return c;
}

void sha1_update(SHACTX c, const void *data, unsigned long len)
{
    EVP_DigestUpdate(c, data, len);
}

void sha1_final(unsigned char *md, SHACTX c)
{
    unsigned int mdlen = 0;

    EVP_DigestFinal(c, md, &mdlen);
    EVP_MD_CTX_destroy(c);
}

void sha1(unsigned char *digest, int len, unsigned char *hash)
{
    SHACTX c = sha1_init();
    if (c != NULL) {
        sha1_update(c, digest, len);
        sha1_final(hash, c);
    }
}

#ifdef HAVE_OPENSSL_ECC
static const EVP_MD *nid_to_evpmd(int nid)
{
    switch (nid) {
        case NID_X9_62_prime256v1:
            return EVP_sha256();
        case NID_secp384r1:
            return EVP_sha384();
        case NID_secp521r1:
            return EVP_sha512();
        default:
            return NULL;
    }

    return NULL;
}

void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen)
{
    const EVP_MD *evp_md = nid_to_evpmd(nid);
    EVP_MD_CTX md;

    EVP_DigestInit(&md, evp_md);
    EVP_DigestUpdate(&md, digest, len);
    EVP_DigestFinal(&md, hash, hlen);
}

EVPCTX evp_init(int nid)
{
    const EVP_MD *evp_md = nid_to_evpmd(nid);

    EVPCTX ctx = malloc(sizeof(EVP_MD_CTX));
    if (ctx == NULL) {
        return NULL;
    }

    EVP_DigestInit(ctx, evp_md);

    return ctx;
}

void evp_update(EVPCTX ctx, const void *data, unsigned long len)
{
    EVP_DigestUpdate(ctx, data, len);
}

void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen)
{
    EVP_DigestFinal(ctx, md, mdlen);
}
#endif

SHA256CTX sha256_init(void)
{
    int rc;
    SHA256CTX c = EVP_MD_CTX_create();
    if (c == NULL) {
        return NULL;
    }
    EVP_MD_CTX_init(c);
    rc = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
    if (rc == 0) {
        EVP_MD_CTX_destroy(c);
        c = NULL;
    }
    return c;
}

void sha256_update(SHA256CTX c, const void *data, unsigned long len)
{
    EVP_DigestUpdate(c, data, len);
}

void sha256_final(unsigned char *md, SHA256CTX c)
{
    unsigned int mdlen = 0;

    EVP_DigestFinal(c, md, &mdlen);
    EVP_MD_CTX_destroy(c);
}

void sha256(unsigned char *digest, int len, unsigned char *hash)
{
    SHA256CTX c = sha256_init();
    if (c != NULL) {
        sha256_update(c, digest, len);
        sha256_final(hash, c);
    }
}

SHA384CTX sha384_init(void)
{
    int rc;
    SHA384CTX c = EVP_MD_CTX_create();
    if (c == NULL) {
        return NULL;
    }
    EVP_MD_CTX_init(c);
    rc = EVP_DigestInit_ex(c, EVP_sha384(), NULL);
    if (rc == 0) {
        EVP_MD_CTX_destroy(c);
        c = NULL;
    }
    return c;
}

void sha384_update(SHA384CTX c, const void *data, unsigned long len)
{
    EVP_DigestUpdate(c, data, len);
}

void sha384_final(unsigned char *md, SHA384CTX c)
{
    unsigned int mdlen = 0;

    EVP_DigestFinal(c, md, &mdlen);
    EVP_MD_CTX_destroy(c);
}

void sha384(unsigned char *digest, int len, unsigned char *hash)
{
    SHA384CTX c = sha384_init();
    if (c != NULL) {
        sha384_update(c, digest, len);
        sha384_final(hash, c);
    }
}

SHA512CTX sha512_init(void)
{
    int rc = 0;
    SHA512CTX c = EVP_MD_CTX_create();
    if (c == NULL) {
        return NULL;
    }
    EVP_MD_CTX_init(c);
    rc = EVP_DigestInit_ex(c, EVP_sha512(), NULL);
    if (rc == 0) {
        EVP_MD_CTX_destroy(c);
        c = NULL;
    }
    return c;
}

void sha512_update(SHA512CTX c, const void *data, unsigned long len)
{
    EVP_DigestUpdate(c, data, len);
}

void sha512_final(unsigned char *md, SHA512CTX c)
{
    unsigned int mdlen = 0;

    EVP_DigestFinal(c, md, &mdlen);
    EVP_MD_CTX_destroy(c);
}

void sha512(unsigned char *digest, int len, unsigned char *hash)
{
    SHA512CTX c = sha512_init();
    if (c != NULL) {
        sha512_update(c, digest, len);
        sha512_final(hash, c);
    }
}

MD5CTX md5_init(void)
{
    int rc;
    MD5CTX c = EVP_MD_CTX_create();
    if (c == NULL) {
        return NULL;
    }
    EVP_MD_CTX_init(c);
    rc = EVP_DigestInit_ex(c, EVP_md5(), NULL);
    if(rc == 0) {
        EVP_MD_CTX_destroy(c);
        c = NULL;
    }
    return c;
}

void md5_update(MD5CTX c, const void *data, unsigned long len)
{
    EVP_DigestUpdate(c, data, len);
}

void md5_final(unsigned char *md, MD5CTX c)
{
    unsigned int mdlen = 0;

    EVP_DigestFinal(c, md, &mdlen);
    EVP_MD_CTX_destroy(c);
}

ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){
  ssh_mac_ctx ctx = malloc(sizeof(struct ssh_mac_ctx_struct));
  if (ctx == NULL) {
    return NULL;
  }

  ctx->mac_type=type;
  switch(type){
    case SSH_MAC_SHA1:
      ctx->ctx.sha1_ctx = sha1_init();
      return ctx;
    case SSH_MAC_SHA256:
      ctx->ctx.sha256_ctx = sha256_init();
      return ctx;
    case SSH_MAC_SHA384:
      ctx->ctx.sha384_ctx = sha384_init();
      return ctx;
    case SSH_MAC_SHA512:
      ctx->ctx.sha512_ctx = sha512_init();
      return ctx;
    default:
      SAFE_FREE(ctx);
      return NULL;
  }
}

void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) {
  switch(ctx->mac_type){
    case SSH_MAC_SHA1:
      sha1_update(ctx->ctx.sha1_ctx, data, len);
      break;
    case SSH_MAC_SHA256:
      sha256_update(ctx->ctx.sha256_ctx, data, len);
      break;
    case SSH_MAC_SHA384:
      sha384_update(ctx->ctx.sha384_ctx, data, len);
      break;
    case SSH_MAC_SHA512:
      sha512_update(ctx->ctx.sha512_ctx, data, len);
      break;
    default:
      break;
  }
}

void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) {
  switch(ctx->mac_type){
    case SSH_MAC_SHA1:
      sha1_final(md,ctx->ctx.sha1_ctx);
      break;
    case SSH_MAC_SHA256:
      sha256_final(md,ctx->ctx.sha256_ctx);
      break;
    case SSH_MAC_SHA384:
      sha384_final(md,ctx->ctx.sha384_ctx);
      break;
    case SSH_MAC_SHA512:
      sha512_final(md,ctx->ctx.sha512_ctx);
      break;
    default:
      break;
  }
  SAFE_FREE(ctx);
}

HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) {
  HMACCTX ctx = NULL;

  ctx = malloc(sizeof(*ctx));
  if (ctx == NULL) {
    return NULL;
  }

#ifndef OLD_CRYPTO
  HMAC_CTX_init(ctx); // openssl 0.9.7 requires it.
#endif

  switch(type) {
    case SSH_HMAC_SHA1:
      HMAC_Init(ctx, key, len, EVP_sha1());
      break;
    case SSH_HMAC_SHA256:
      HMAC_Init(ctx, key, len, EVP_sha256());
      break;
    case SSH_HMAC_SHA384:
      HMAC_Init(ctx, key, len, EVP_sha384());
      break;
    case SSH_HMAC_SHA512:
      HMAC_Init(ctx, key, len, EVP_sha512());
      break;
    case SSH_HMAC_MD5:
      HMAC_Init(ctx, key, len, EVP_md5());
      break;
    default:
      SAFE_FREE(ctx);
      ctx = NULL;
  }

  return ctx;
}

void hmac_update(HMACCTX ctx, const void *data, unsigned long len) {
  HMAC_Update(ctx, data, len);
}

void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len) {
  HMAC_Final(ctx,hashmacbuf,len);

#ifndef OLD_CRYPTO
  HMAC_CTX_cleanup(ctx);
#else
  HMAC_cleanup(ctx);
#endif

  SAFE_FREE(ctx);
}


/* EVP wrapper function for encrypt/decrypt */
static void EVP_encrypt(struct ssh_cipher_struct *cipher,
                        void *in,
                        void *out,
                        unsigned long len,
                        const EVP_CIPHER* evpCipher)
{
    int datalen = 0;
    int outlen = 0;
    int outlenfinal = 0;
    int ctxIvLen = 0;
    int rc = 0;

    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);

    rc = EVP_EncryptInit_ex(&ctx, evpCipher, NULL, (unsigned char*)cipher->key, cipher->IV);
    if (rc == 1) {
        EVP_CIPHER_CTX_set_padding(&ctx, 0);

        rc = EVP_EncryptUpdate(&ctx, (unsigned char *)out, &outlen, (unsigned char *)in, len);
        if(rc == 1) {
            datalen += outlen;

            rc = EVP_EncryptFinal_ex(&ctx, (unsigned char *)out + outlen, &outlenfinal);
            if(rc == 1) {
                datalen += outlenfinal;

                /* Get size of source buffer from ctx object */
                ctxIvLen = EVP_CIPHER_CTX_iv_length(&ctx);

                /*
                 * Copy updated IV from ctx to ssh struct
                 * There is no access to the post encrypt/decrypt updated iv
                 * from openssl via an API so I'm just accessing it directly.
                 */
                memcpy(cipher->IV, ctx.iv, ctxIvLen);
            } else {
                printf("EVP_EncryptFinal_ex failed\n");
            }
        } else {
            printf("EVP_EncryptUpdate failed\n");
        }
    } else {
        printf("EVP_EncryptInit_ex failed\n");
    }
    /* Cleanup */
    EVP_CIPHER_CTX_cleanup(&ctx);
}

static void EVP_decrypt(struct ssh_cipher_struct *cipher,
                        void *in,
                        void *out,
                        unsigned long len,
                        const EVP_CIPHER* evpCipher)
{
    int datalen = 0;
    int outlen = 0;
    int outlenfinal = 0;
    int ctxIvLen = 0;
    int rc = 0;

    EVP_CIPHER_CTX ctx;
    EVP_CIPHER_CTX_init(&ctx);

    rc = EVP_DecryptInit_ex(&ctx, evpCipher, NULL, (unsigned char*)cipher->key, cipher->IV);
    if (rc == 1) {
        EVP_CIPHER_CTX_set_padding(&ctx, 0);

        rc = EVP_DecryptUpdate(&ctx, (unsigned char *)out, &outlen, (unsigned char *)in, len);
        if (rc == 1) {
            datalen += outlen;

            rc = EVP_DecryptFinal_ex(&ctx, (unsigned char *)out + outlen, &outlenfinal);
            if (rc == 1) {
                datalen += outlenfinal;

                /* Get size of source buffer from ctx object */
                ctxIvLen = EVP_CIPHER_CTX_iv_length(&ctx);

                /*
                 * Copy updated IV from ctx to ssh struct
                 * There is no access to the post encrypt/decrypt updated iv
                 * from openssl via an API so I'm just accessing it directly.
                 */
                memcpy(cipher->IV, ctx.iv, ctxIvLen);
            } else {
                printf("EVP_DecryptFinal_ex failed\n");
            }
        } else {
            printf("EVP_DecryptUpdate failed\n");
        }
    } else {
        printf("EVP_DecryptInit_ex failed\n");
    }

    /* Cleanup */
    EVP_CIPHER_CTX_cleanup(&ctx);
}

//EVP wrapper function for encrypt/decrypt
#ifdef HAS_BLOWFISH
/* the wrapper functions for blowfish */
static int blowfish_set_key(struct ssh_cipher_struct *cipher,
                            void *key,
                            void *IV)
{
  if (cipher->key == NULL) {
    if (alloc_key(cipher) < 0) {
      return -1;
    }
    BF_set_key(cipher->key, 16, key);
  }
  cipher->IV = IV;
  return 0;
}

static void blowfish_encrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_ENCRYPT);
}

static void blowfish_decrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
  BF_cbc_encrypt(in, out, len, cipher->key, cipher->IV, BF_DECRYPT);
}
#endif /* HAS_BLOWFISH */

#ifdef HAS_AES
static int aes_set_encrypt_key(struct ssh_cipher_struct *cipher,
                               void *key,
                               void *IV)
{
    size_t size = 0;
    if (cipher->key == NULL) {
        int rc;

        rc = alloc_key(cipher);
        if (rc < 0) {
            return -1;
        }
        /* cipher->keylen is the size of the cipher->key buffer so clear it
         * Don't read or write invalid memory
         * For the EVP functions, save the raw key which gets transformed
         * in the EVP_EncryptInit(...)
         */
        memset(cipher->key, 0, cipher->keylen);
        size = cipher->keylen;

        /*
         * Check if source buffer is smaller than destination buffer (hard coded
         * as 32 in kex1.c - encryptkey, decryptkey, encryptIV, decryptIV)
         * This should probably be a define, or a member of
         * struct ssh_crypto_struct and passed as a parameter.
         */
        if (size > 32) {
            size = 32;
        }
        memcpy(cipher->key, key, size);
    }
    cipher->IV = IV;
    return 0;
}

static int aes_set_decrypt_key(struct ssh_cipher_struct *cipher,
                               void *key,
                               void *IV)
{
    size_t size = 0;

    if (cipher->key == NULL) {
        int rc;

        rc = alloc_key(cipher);
        if (rc < 0) {
            return -1;
        }
        /*
         * cipher->keylen is the size of the cipher->key buffer so clear it
         * keyLen param is the size of the void* key param
         * Don't read or write invalid memory
         * For the EVP functions, save the raw key which gets transformed
         * in the EVP_EncryptInit(...)
         */
        memset(cipher->key, 0, cipher->keylen);
        size = cipher->keylen;

        /*
         * Check if source buffer is smaller than destination buffer (hard coded
         * as 32 in kex1.c - encryptkey, decryptkey, encryptIV, decryptIV)
         * This should probably be a define, or a member of
         * struct ssh_crypto_struct and passed as a parameter.
         */
        if (size > 32) {
            size = 32;
        }
        memcpy(cipher->key, key, size);
    }
    cipher->IV = IV;
    return 0;
}

static void aes_encrypt(struct ssh_cipher_struct *cipher,
                        void *in,
                        void *out,
                        unsigned long len)
{
    const EVP_CIPHER *pCipherType = NULL;

    if (cipher->keysize == 128) {
        pCipherType = EVP_aes_128_cbc();
    } else if(cipher->keysize == 192) {
        pCipherType = EVP_aes_192_cbc();
    } else if(cipher->keysize == 256) {
        pCipherType = EVP_aes_256_cbc();
    }

    EVP_encrypt(cipher, in, out, len, pCipherType);
}

static void aes_decrypt(struct ssh_cipher_struct *cipher,
                        void *in,
                        void *out,
                        unsigned long len)
{
    const EVP_CIPHER *pCipherType = NULL;

    if (cipher->keysize == 128) {
        pCipherType = EVP_aes_128_cbc();
    } else if(cipher->keysize == 192) {
        pCipherType = EVP_aes_192_cbc();
    } else if(cipher->keysize == 256) {
        pCipherType = EVP_aes_256_cbc();
    }

    EVP_decrypt(cipher, in, out, len, pCipherType);
}

#ifndef BROKEN_AES_CTR
/* OpenSSL until 0.9.7c has a broken AES_ctr128_encrypt implementation which
 * increments the counter from 2^64 instead of 1. It's better not to use it
 */

/** @internal
 * @brief encrypts/decrypts data with stream cipher AES_ctr128. 128 bits is actually
 * the size of the CTR counter and incidentally the blocksize, but not the keysize.
 * @param len[in] must be a multiple of AES128 block size.
 */
static void aes_ctr128_encrypt(struct ssh_cipher_struct *cipher,
                               void *in,
                               void *out,
                               unsigned long len)
{
    if (cipher->keysize == 128) {
        EVP_encrypt(cipher, in, out, len, EVP_aes_128_ctr());
    } else if(cipher->keysize == 192) {
        EVP_encrypt(cipher, in, out, len, EVP_aes_192_ctr());
    } else if(cipher->keysize == 256) {
        EVP_encrypt(cipher, in, out, len, EVP_aes_256_ctr());
    }
}
#endif /* BROKEN_AES_CTR */
#endif /* HAS_AES */

#ifdef HAS_DES
static int des3_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV)
{
    size_t size = 0;

    if (cipher->key == NULL) {
        int rc;

        rc = alloc_key(cipher);
        if (rc < 0) {
            return -1;
        }

        /*
         * cipher->keylen is the size of the cipher->key buffer so clear it
         * keyLen param is the size of the void* key param
         * Don't read or write invalid memory
         * For the EVP functions, save the raw key which gets transformed
         * in the EVP_EncryptInit(...)
         */
        memset(cipher->key, 0, cipher->keylen);
        size = cipher->keylen;

        /*
         * Check if source buffer is smaller than destination buffer (hard coded
         * as 32 in kex1.c - encryptkey, decryptkey, encryptIV, decryptIV)
         * This should probably be a define, or a member of
         * struct ssh_crypto_struct and passed as a parameter.
         */
        if (size > 32) {
            size = 32;
        }
        memcpy(cipher->key, key, size);
    }
    cipher->IV = IV;
    return 0;
}

static void des3_encrypt(struct ssh_cipher_struct *cipher,
                         void *in,
                         void *out,
                         unsigned long len)
{
    EVP_encrypt(cipher, in, out, len, EVP_des_ede3_cbc());
}

static void des3_decrypt(struct ssh_cipher_struct *cipher,
                         void *in,
                         void *out,
                         unsigned long len)
{
    EVP_decrypt(cipher, in, out, len, EVP_des_ede3_cbc());
}

static void des3_1_encrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
#ifdef DEBUG_CRYPTO
  ssh_print_hexa("Encrypt IV before", cipher->IV, 24);
#endif
  DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1);
  DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
      (void*)((uint8_t*)cipher->IV + 8), 0);
  DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
      (void*)((uint8_t*)cipher->IV + 16), 1);
#ifdef DEBUG_CRYPTO
  ssh_print_hexa("Encrypt IV after", cipher->IV, 24);
#endif
}

static void des3_1_decrypt(struct ssh_cipher_struct *cipher, void *in,
    void *out, unsigned long len) {
#ifdef DEBUG_CRYPTO
  ssh_print_hexa("Decrypt IV before", cipher->IV, 24);
#endif

  DES_ncbc_encrypt(in, out, len, (void*)((uint8_t*)cipher->key + 2 * sizeof(DES_key_schedule)),
      cipher->IV, 0);
  DES_ncbc_encrypt(out, in, len, (void*)((uint8_t*)cipher->key + sizeof(DES_key_schedule)),
      (void*)((uint8_t*)cipher->IV + 8), 1);
  DES_ncbc_encrypt(in, out, len, cipher->key, (void*)((uint8_t*)cipher->IV + 16), 0);

#ifdef DEBUG_CRYPTO
  ssh_print_hexa("Decrypt IV after", cipher->IV, 24);
#endif
}

static int des1_set_key(struct ssh_cipher_struct *cipher, void *key, void *IV)
{
  if(!cipher->key){
    if (alloc_key(cipher) < 0) {
      return -1;
    }
    DES_set_odd_parity(key);
    DES_set_key_unchecked(key,cipher->key);
  }
  cipher->IV=IV;
  return 0;
}

static void des1_1_encrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
                           unsigned long len){

  DES_ncbc_encrypt(in, out, len, cipher->key, cipher->IV, 1);
}

static void des1_1_decrypt(struct ssh_cipher_struct *cipher, void *in, void *out,
                           unsigned long len){

  DES_ncbc_encrypt(in,out,len, cipher->key, cipher->IV, 0);
}

#endif /* HAS_DES */

/*
 * The table of supported ciphers
 *
 * WARNING: If you modify ssh_cipher_struct, you must make sure the order is
 * correct!
 */
static struct ssh_cipher_struct ssh_ciphertab[] = {
#ifdef HAS_BLOWFISH
  {
    "blowfish-cbc",
    8,
    sizeof (BF_KEY),
    NULL,
    NULL,
    128,
    blowfish_set_key,
    blowfish_set_key,
    blowfish_encrypt,
    blowfish_decrypt
  },
#endif /* HAS_BLOWFISH */
#ifdef HAS_AES
#ifndef BROKEN_AES_CTR
  {
    "aes128-ctr",
    16,
    sizeof(AES_KEY),
    NULL,
    NULL,
    128,
    aes_set_encrypt_key,
    aes_set_encrypt_key,
    aes_ctr128_encrypt,
    aes_ctr128_encrypt
  },
  {
    "aes192-ctr",
    16,
    sizeof(AES_KEY),
    NULL,
    NULL,
    192,
    aes_set_encrypt_key,
    aes_set_encrypt_key,
    aes_ctr128_encrypt,
    aes_ctr128_encrypt
  },
  {
    "aes256-ctr",
    16,
    sizeof(AES_KEY),
    NULL,
    NULL,
    256,
    aes_set_encrypt_key,
    aes_set_encrypt_key,
    aes_ctr128_encrypt,
    aes_ctr128_encrypt
  },
#endif /* BROKEN_AES_CTR */
  {
    "aes128-cbc",
    16,
    sizeof(AES_KEY),
    NULL,
    NULL,
    128,
    aes_set_encrypt_key,
    aes_set_decrypt_key,
    aes_encrypt,
    aes_decrypt
  },
  {
    "aes192-cbc",
    16,
    sizeof(AES_KEY),
    NULL,
    NULL,
    192,
    aes_set_encrypt_key,
    aes_set_decrypt_key,
    aes_encrypt,
    aes_decrypt
  },
  {
    "aes256-cbc",
    16,
    sizeof(AES_KEY),
    NULL,
    NULL,
    256,
    aes_set_encrypt_key,
    aes_set_decrypt_key,
    aes_encrypt,
    aes_decrypt
  },
#endif /* HAS_AES */
#ifdef HAS_DES
  {
    "3des-cbc",
    8,
    sizeof(DES_key_schedule) * 3,
    NULL,
    NULL,
    192,
    des3_set_key,
    des3_set_key,
    des3_encrypt,
    des3_decrypt
  },
  {
    "3des-cbc-ssh1",
    8,
    sizeof(DES_key_schedule) * 3,
    NULL,
    NULL,
    192,
    des3_set_key,
    des3_set_key,
    des3_1_encrypt,
    des3_1_decrypt
  },
  {
    "des-cbc-ssh1",
    8,
    sizeof(DES_key_schedule),
    NULL,
    NULL,
    64,
    des1_set_key,
    des1_set_key,
    des1_1_encrypt,
    des1_1_decrypt
  },
#endif /* HAS_DES */
  {
    NULL,
    0,
    0,
    NULL,
    NULL,
    0,
    NULL,
    NULL,
    NULL,
    NULL
  }
};


struct ssh_cipher_struct *ssh_get_ciphertab(void)
{
  return ssh_ciphertab;
}

#endif /* LIBCRYPTO */

