#ifndef PIPE_CS_IIP11_H
#define PIPE_CS_IIP11_H

#include "misc/compat.h"
#include <stdlib.h>
#include "pipe/pipe.h"
#include "base/bignum.h"
#include "crypt/blowfish.h"
#include "pipe/c-stream.h"

#define CSIIP11_KEY_LENGTH 20
#define CSIIP11_KEY_SET_COUNT 1
#define CSIIP11_KEY_SET_LENGTH (CSIIP11_KEY_LENGTH * CSIIP11_KEY_SET_COUNT)
#define CSIIP11_BLOCK_LENGTH 8 
#define CSIIP11_BLOCK_LENGTH1 7
#define CSIIP11_BLOCK_ROUND(x) ((x + CSIIP11_BLOCK_LENGTH1) & (~CSIIP11_BLOCK_LENGTH1))

typedef struct CryptStreamIIP11{
	CryptStream cs;
	//Pipe *backPipe;
	//Pipe *thisPipe;

	int inShake;
	int outShake;
	BigNum *privkey; //local private key
	BigNum *localkey; //local public key
	BigNum *remotekey; //remote public key
	BigNum *sharedkey; //the shared key
	DataBlock *insessionkey; //session key from key exchange
	DataBlock *outsessionkey; //session key from key exchange

	int inNeeded;
	int outCount;

	int outerkeylength;
	int innerkeylength;

	uint32 crc32;

	BlowfishContext *blowfishin;
	BlowfishContext *blowfishout;

	BigNum *setremotekey;

	uint8 counterin[CSIIP11_BLOCK_LENGTH];
	uint8 counterout[CSIIP11_BLOCK_LENGTH];

	uint8 cbcout[CSIIP11_BLOCK_LENGTH];
	uint8 cbcin[CSIIP11_BLOCK_LENGTH];
	uint8 cbctemp[CSIIP11_BLOCK_LENGTH];

} CryptStreamIIP11;

#define CSIIP11_WRITES_TILL_ROTATE 52

#define CSIIP11_BLANKCOUNTER {0, 0, 0, 0,  0, 0, 0, 0}
//#define BLANKCRYPTSTREAMIIP11 {{(CSWriteFunc) csiip11Write, (CSReadFunc) csiip11Read, (CSFreeFunc) csiip11Free}, NULL, 1, 1, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, BLANKCOUNTER, BLANKCOUNTER}
#define CSIIP11_INNERKEYLENGTH 1024
#define CSIIP11_OUTERKEYLENGTH 2048
#define BLANKCRYPTSTREAMIIP11 {{(CSWriteFunc) csiip11Write, (CSReadFunc) csiip11Read, (CSFreeFunc) csiip11Free}, 1, 1, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, CSIIP11_INNERKEYLENGTH, CSIIP11_OUTERKEYLENGTH, 0, NULL, NULL, NULL, CSIIP11_BLANKCOUNTER, CSIIP11_BLANKCOUNTER, CSIIP11_BLANKCOUNTER, CSIIP11_BLANKCOUNTER, CSIIP11_BLANKCOUNTER}


CryptStream *csiip11Make(char *csname, char *options);
void csiip11Init(CryptStreamIIP11 *cs, Pipe *thispipe);//, Pipe *backpipe
void csiip11Free(CryptStreamIIP11 *cs);
void csiip11Decrypt(CryptStreamIIP11 *cs);
void csiip11Encrypt(CryptStreamIIP11 *cs);
void csiip11InHandshake(CryptStreamIIP11 *cs);
void csiip11Outhandshake(CryptStreamIIP11 *cs);
void csiip11Read(CryptStreamIIP11 *cs);
void csiip11Write(CryptStreamIIP11 *cs);



//private functions
void csiip11Close(CryptStreamIIP11 *cs);
void csiip11SetKey(CryptStreamIIP11 *cs, DataBlock *db);
void csiip11RotateSessionKey(DataBlock *key, DataBlock *rand);
void csiip11ApplyCounter(uint8 *data, uint8 *counter);
void csiip11ApplyCBC(uint8 *data, uint8 *counter);
void csiip11Decode(CryptStreamIIP11 *cs, DataBlock *db);
void csiip11Encode(CryptStreamIIP11 *cs, DataBlock *db);
void csiip11EncryptWrite(CryptStreamIIP11 *cs, DataBlock *db);
void csiip11EncryptSend(CryptStreamIIP11 *cs, DataBlock *db);
void csiip11OutHandshake(CryptStreamIIP11 *cs);


#endif //PIPE_CS_IIP11_H

