/* $Id: 3des.c,v 1.11 1999/06/25 05:33:38 levitte Exp $ */

#include "gnu_extras.h"

#include <string.h>
#include <lib$routines.h>
#include <starlet.h>
#ifdef __GNUC__
#define __DECC
#endif
#include <openssl/des.h>
#include <openssl/opensslv.h>
#ifdef __GNUC__
#undef __DECC
#endif
#include "des_hack.h"
#include "fish.h"
#include "ssh.h"
#include "util.h"
#include "fishmsg.h"

typedef struct {
    des_key_schedule k1;
    des_key_schedule k2;
    des_key_schedule k3;
    des_cblock ivec1;
    des_cblock ivec2;
    des_cblock ivec3;
} T3des_state;

void ssh_3des_encrypt(unsigned char *in, unsigned char *out, long len,
    void *encryptstate_)
{
    T3des_state *encryptstate = encryptstate_;

    des_ncbc_encrypt(des_cblock_arg(in), des_cblock_arg(out), len,
		     encryptstate->k1, des_cblock_ivec(encryptstate->ivec1),
		     DES_ENCRYPT);
    des_ncbc_encrypt(des_cblock_arg(out), des_cblock_arg(out), len,
		     encryptstate->k2, des_cblock_ivec(encryptstate->ivec2),
		     DES_DECRYPT);
    des_ncbc_encrypt(des_cblock_arg(out), des_cblock_arg(out), len,
		     encryptstate->k3, des_cblock_ivec(encryptstate->ivec3),
		     DES_ENCRYPT);
}

void ssh_3des_decrypt(unsigned char *in, unsigned char *out, long len,
    void *decryptstate_)
{
    T3des_state *decryptstate = decryptstate_;

    des_ncbc_encrypt(des_cblock_arg(in), des_cblock_arg(out), len,
		     decryptstate->k3, des_cblock_ivec(decryptstate->ivec3),
		     DES_DECRYPT);
    des_ncbc_encrypt(des_cblock_arg(out), des_cblock_arg(out), len,
		     decryptstate->k2, des_cblock_ivec(decryptstate->ivec2),
		     DES_ENCRYPT);
    des_ncbc_encrypt(des_cblock_arg(out), des_cblock_arg(out), len,
		     decryptstate->k1, des_cblock_ivec(decryptstate->ivec1),
		     DES_DECRYPT);
}

void ssh_3des_clean(ssh_state *state)
{
    xfree(state->encryptstate);
    xfree(state->decryptstate);
    state->encrypt = NULL;
    state->decrypt = NULL;
    state->cryptclean = NULL;
}

void *ssh_3des_encryptstate(ssh_state *state, int keylen, Erf erf, void *erfp)
{
    T3des_state *encryptstate = xmalloc(sizeof(T3des_state));
    des_cblock k1, k2, k3;

    if (!encryptstate) {
	ssh_F_memfull();
	return NULL;
    }
    memcpy(k1,state->sesskey,sizeof(k1));
    memcpy(k2,state->sesskey+8,sizeof(k2));
    if (keylen <= 16)
	memcpy(k3,state->sesskey,sizeof(k3));
    else
	memcpy(k3,state->sesskey+16,sizeof(k3));

    des_set_key(des_cblock_set_arg(k1),encryptstate->k1);
    des_set_key(des_cblock_set_arg(k2),encryptstate->k2);
    des_set_key(des_cblock_set_arg(k3),encryptstate->k3);

    memset(encryptstate->ivec1, 0, 8);
    memset(encryptstate->ivec2, 0, 8);
    memset(encryptstate->ivec3, 0, 8);

    return encryptstate;
}

void *ssh_3des_decryptstate(ssh_state *state, int keylen, Erf erf, void *erfp)
{
    T3des_state *encryptstate = xmalloc(sizeof(T3des_state));
    des_cblock k1, k2, k3;

    if (!encryptstate) {
	ssh_F_memfull();
	return NULL;
    }
    memcpy(k1,state->sesskey,sizeof(k1));
    memcpy(k2,state->sesskey+8,sizeof(k2));
    if (keylen <= 16)
	memcpy(k3,state->sesskey,sizeof(k3));
    else
	memcpy(k3,state->sesskey+16,sizeof(k3));

    des_set_key(des_cblock_set_arg(k1),encryptstate->k1);
    des_set_key(des_cblock_set_arg(k2),encryptstate->k2);
    des_set_key(des_cblock_set_arg(k3),encryptstate->k3);

    memset(encryptstate->ivec1, 0, 8);
    memset(encryptstate->ivec2, 0, 8);
    memset(encryptstate->ivec3, 0, 8);

    return encryptstate;
}

/* Emacs local variables

Local variables:
eval: (set-c-style "BSD")
end:

*/
