/* FUBUKI encryption system, by Hagita-Matsumoto-Nishimura-Saito */
/* Coded by Makoto Matsumoto, 2001/12/25 */
/* Re-Coded by Makoto Matsumoto, 2005/03/29 */
/* Multiplication-based by Makoto Matsumoto, 2005/04/10 */
/* Variable Tuple by Takuji Nishimura, 2005/04/12 */
/* Tough against differential crypt attack, 2005/04/13 */
/* The algorithm is fixed, 2005/04/15 */
#include <stdio.h>

#include "ecrypt-config.h"
#include "ecrypt-machine.h"
#include "ecrypt-portable.h"

#include "ecrypt-sync.h"

void genrand_tuple_int32(ECRYPT_ctx* ctx, u32 rand_tuple[], s32 len);
void init_by_array(ECRYPT_ctx* ctx, u32 init_key[], int key_length);

#ifdef ECRYPT_API
#include "mt-fubuki.c"
#endif

/* This is a stream cipher. One word means a 32 bit word. */
/* Tuple words will be gather to make one Block. */
/* Typically Tuple is 4. Log_Tuple is log2(Tuple) */
#define Log_Tuple 2

#define Tuple (U32C(1) << Log_Tuple)

#define Low_Mask (Tuple-1)

#define Iteration 4 /* <=32: number of primitive encrypt function iterations */

#define Multi_Size 32 /* number of constant multipliers */

#define Log_Add_Size 5 /* Log of Add_Size */
#define Add_Size 32 /* number of constant adders */

/* for debug */
void print_block(u32 block[]) {
    s32 i;
    for (i=0; i<Tuple; i++) printf("%8x ", block[i]);
    printf("\n");
}


/**********************************************/
/********  Precomputation of Tables  **********/
/**********************************************/

/* Compute Multiplication Constants: 3 mod 8, 7 mod 16 */
void prepare_multi(ECRYPT_ctx* ctx) {
    s32 i;
    for (i=0; i<Multi_Size; i+=4) {
	genrand_tuple_int32(ctx, &ctx->multi_table[i], 4);
    }
    for (i=0; i< Multi_Size; i+=2) {
	ctx->multi_table[i] = (ctx->multi_table[i] & U32C(0xfffffff8)) | U32C(0x3);
	ctx->multi_table[i] |= (U32C(0x80000000) >> (i % 8));
	ctx->multi_table[i] &= ~(U32C(0x40000000) >> (i % 8));

	ctx->multi_table[i+1] = (ctx->multi_table[i+1] & U32C(0xfffffff0)) | U32C(0x7);
	ctx->multi_table[i+1] |= (U32C(0x80000000) >> (i+1 % 8));
	ctx->multi_table[i+1] &= ~(U32C(0x40000000) >> (i+1 % 8));
    }  
}

/* compute multiplicative inverse mod 2^32 */
unsigned int inv_mod_32(u32 m)
{
    u32 inv;
    s32 i;
    if ((m & U32C(0x1)) == 0) { printf("error\n"); return -1;}
    inv = 1;
    for (i=30; i>=0; i--) {
      if (((inv * m - 1) << i) != 0) inv |= (0x1 << (32-i-1));
    }
    return inv;
}


/* prepare the table of inverses */
void prepare_multi_inv(ECRYPT_ctx* ctx) {
    s32 i;
    for (i=0; i< Multi_Size; i++) {
	ctx->inv_table[i] = inv_mod_32(ctx->multi_table[i]);
    }
}

/* Compute addition constants */
void prepare_add_table(ECRYPT_ctx* ctx) {
    s32 i;

    genrand_tuple_int32(ctx, ctx->add_table, Add_Size);

    for (i=0; i< Add_Size; i++) {
        u32 s;
	s = (i * 1103515245 + 12345) & (Add_Size - 1);
	s ^= (s >> (Log_Add_Size / 2));
        ctx->add_table[i] <<= Log_Add_Size;
        ctx->add_table[i] |= s;
    }  
}

/**********************************************/
/*******Primitive Ecnryption Families**********/
/**********************************************/

/**********************************************/
/*******PEF (almost word wise operation)*******/
/**********************************************/

/* word wise encrypt by Exor Mult table-Plus Rotate */
/* rotate number is between 16 - 23 */
void crypt_empr(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    s32 i, s;
    u32 param[Tuple];
    genrand_tuple_int32(ctx, param, Tuple);
    for (i=0; i<Tuple; i++) {
      s = ((param[i] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] ^= param[i];
      block[i] *= ctx->multi_table[param[(i+1) % Tuple]>> (32 - 5)];
      block[(i+ctx->jump) & Low_Mask] += ctx->add_table[block[i] >> (32 - Log_Add_Size)];
      block[i] = ((~block[i]) << (32 - s)) | (block[i] >> s);
    }
}

void crypt_empr_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    s32 i, s;
    for (i=Tuple-1; i>=0; i--) {
      s = ((param[i] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] = ((~block[i]) >> (32 - s)) | (block[i] << s);
      block[(i+ctx->jump) & Low_Mask] -= ctx->add_table[block[i] >> (32 - Log_Add_Size)];
      block[i] *= ctx->inv_table[param[(i+1) % Tuple]>> (32 - 5)];
      block[i] ^= param[i];
    }
}

/* word wise encrypt by Exor Mult table-Exor Rotate */
/* rotate number is between 16 - 23 */
void crypt_emer(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    s32 i, s;
    u32 param[Tuple];
    genrand_tuple_int32(ctx, param, Tuple);
    for (i=0; i<Tuple; i++) {
      s = ((param[i] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] ^= param[i];
      block[i] *= ctx->multi_table[param[(i+2) % Tuple]>> (32 - 5)];
      block[(i+ctx->jump) & Low_Mask] ^= ctx->add_table[block[i] >> (32 - Log_Add_Size)];
      block[i] = ((~block[i]) << (32 - s)) | (block[i] >> s);
    }
}

void crypt_emer_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    s32 i, s;
    for (i=Tuple-1; i>=0; i--) {
      s = ((param[i] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] = ((~block[i]) >> (32 - s)) | (block[i] << s);
      block[(i+ctx->jump) & Low_Mask] ^= ctx->add_table[block[i] >> (32 - Log_Add_Size)];
      block[i] *= ctx->inv_table[param[(i+2) % Tuple]>> (32 - 5)];
      block[i] ^= param[i];
    }
}

/* word wise encrypt by Exor Mult table-Plus Shift */
/* Shift number is betwee 16 - 23 */
void crypt_emps(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    s32 i, s;
    u32 param[Tuple];
    genrand_tuple_int32(ctx, param, Tuple);
    for (i=0; i<Tuple; i++) {
      s = ((param[i] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] ^= param[i];
      block[i] *= ctx->multi_table[param[(i+2) % Tuple]>> (32 - 5)];
      block[(i+ctx->jump) & Low_Mask] += ctx->add_table[block[i] >> (32 - Log_Add_Size)];
      block[i] ^= ((~block[i]) >> s);
    }
}

void crypt_emps_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    s32 i, s;
    for (i=Tuple-1; i>=0; i--) {
      s = ((param[i] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] ^= ((~block[i]) >> s);
      block[(i+ctx->jump) & Low_Mask] -= ctx->add_table[block[i] >> (32 - Log_Add_Size)];
      block[i] *= ctx->inv_table[param[(i+2) % Tuple]>> (32 - 5)];
      block[i] ^= param[i];
    }
}

/* word wise encrypt by Exor Mult table-Exor Shift */
/* Shift number is betwee 16 - 23 */
void crypt_emes(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    s32 i, s;
    u32 param[Tuple];
    genrand_tuple_int32(ctx, param, Tuple);
    for (i=0; i<Tuple; i++) {
      s = ((param[i] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] ^= param[i];
      block[i] *= ctx->multi_table[param[(i+3) % Tuple]>> (32 - 5)];
      block[(i+ctx->jump) & Low_Mask] ^= ctx->add_table[block[i] >> (32 - Log_Add_Size)];
      block[i] ^= ((~block[i]) >> s);
    }
}

void crypt_emes_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    s32 i, s;
    for (i=Tuple-1; i>=0; i--) {
      s = ((param[i] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] ^= ((~block[i]) >> s);
      block[(i+ctx->jump) & Low_Mask] ^= ctx->add_table[block[i] >> (32 - Log_Add_Size)];
      block[i] *= ctx->inv_table[param[(i+3) % Tuple]>> (32 - 5)];
      block[i] ^= param[i];
    }
}

/* Inter-word operations */
/* multiply to one and add to the other */
void crypt_ma(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    s32 i, j, s;
    u32 param[Tuple];
    genrand_tuple_int32(ctx, param, Tuple);

    for (i=0; i<Tuple; i++) {
      j = (i - ctx->jump) & Low_Mask;
      s = ((param[j] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] += (block[j]*param[i]);
      block[i] ^= ((~block[i]) >> s);
    }
}

void crypt_ma_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    s32 i, j, s;

    for (i=Tuple-1; i>=0; i--) {
      j = (i - ctx->jump) & Low_Mask;
      s = ((param[j] >> (32 - 4)) | 0x10 ) & 0x17;
      block[i] ^= ((~block[i]) >> s);
      block[i] -= (block[j]*param[i]);
    }
}

/* multiply two words, exor to another words, and  minus */
void crypt_mem(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    s32 i, j, k;
    u32 param[Tuple];
    genrand_tuple_int32(ctx, param, Tuple);

    for (i=0; i<Tuple; i++) {
      j = (i - ctx->jump) & Low_Mask;
      k = param[j] >> (32 - Log_Tuple);
      if (k==i) k=(k-1) & Low_Mask;
      block[i] ^= (block[j]*block[k]);
      block[i] -= param[i];
      block[i] ^= (block[i] >> 16);
    }
}

void crypt_mem_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    s32 i, j, k;

    for (i=Tuple-1; i>=0; i--) {
      j = (i - ctx->jump) & Low_Mask;
      k = param[j] >> (32 - Log_Tuple);
      if (k==i) k=(k-1) & Low_Mask;
      block[i] ^= (block[i] >> 16);
      block[i] += param[i];
      block[i] ^= (block[j]*block[k]);
    }
}

/* (one word OR param) times another word) is EXORed to another */
void crypt_ome(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    s32 i, j, k;
    u32 param[Tuple];
    genrand_tuple_int32(ctx, param, Tuple);

    for (i=0; i<Tuple; i++) {
      j = (i - ctx->jump) & Low_Mask;
      k = param[j] >> (32 - Log_Tuple);
      if (k==i) k=(k-1) & Low_Mask;
      block[i] ^= (block[k]|param[i])*block[j];
      block[i] ^= (block[i] >> 16);
    }
}

void crypt_ome_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    s32 i, j, k;

    for (i=Tuple-1; i>=0; i--) {
      j = (i - ctx->jump) & Low_Mask;
      k = param[j] >> (32 - Log_Tuple);
      if (k==i) k=(k-1) & Low_Mask;
      block[i] ^= (block[i] >> 16);
      block[i] ^= (block[k]|param[i])*block[j];
    }
}

/* (one word EXOR param) times another word) is EXORed to another */

void crypt_eme(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    s32 i, j, k;
    u32 param[Tuple];
    genrand_tuple_int32(ctx, param, Tuple);

    for (i=0; i<Tuple; i++) {
      j = (i - ctx->jump) & Low_Mask;
      k = param[j] >> (32 - Log_Tuple);
      if (k==i) k=(k-1) & Low_Mask;
      block[i] ^= (block[k]^param[i])*block[j];
      block[i] ^= (block[i] >> 17);
    }
}

void crypt_eme_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    s32 i, j, k;

    for (i=Tuple-1; i>=0; i--) {
      j = (i - ctx->jump) & Low_Mask;
      k = param[j] >> (32 - Log_Tuple);
      if (k==i) k=(k-1) & Low_Mask;
      block[i] ^= (block[i] >> 17);
      block[i] ^= (block[k]^param[i])*block[j];
    }
}

/* vertical partial rotation with bit inversion*/
void crypt_vert_rotate(ECRYPT_ctx* ctx, u32 block[Tuple])
{
    u32 key, rkey, s;
    s32 i, j, jump_odd;
    u32 param[Tuple];

    jump_odd = (ctx->jump - 1) | 0x1; 

    genrand_tuple_int32(ctx, param, Tuple);

    key = ((param[0]+param[Tuple-1])<< 2) + 1;
    rkey = ~key;
    s = block[0];
    j = 0; 
    for (i=0; i<Tuple ; i++) {
      int u;
      u = (j-jump_odd) & Low_Mask;
      block[j] = (block[j] & rkey) | (~block[u] & key);
      j = u;
    }
    block[j] = (block[j] & rkey) | (~s & key);
    for (i=0; i<Tuple; i++) {
      block[i] += param[i];
    }
}

void crypt_vert_rotate_inv(ECRYPT_ctx* ctx, u32 block[Tuple], u32 param[Tuple])
{
    u32 key, rkey, s;
    s32 i, j, jump_odd;

    jump_odd = (ctx->jump - 1)| 0x1; 

    for (i=0; i<Tuple; i++) {
      block[i] -= param[i];
    }

    key = ((param[0]+param[Tuple-1])<< 2) + 1;
    rkey = ~key;
    s = block[0];
    j = 0; 
    for (i=0; i<Tuple; i++) {
      int u;
      u = (j+jump_odd) & Low_Mask;
      block[j] = (block[j] & rkey) | (~block[u] & key);
      j = u;
    }
    block[j] = (block[j] & rkey) | (~s & key);
}

void set_buf(u32 buf[],  const u8* text, u32 cpos, u32 msglen)
{
    u32 x;
    s32 i, j, s, t, diff;

    diff = msglen - cpos; 
    if ( diff >= 4*Tuple ) {
	for (i=0; i<Tuple; i++) {
	    x = (u32)text[cpos++];
	    x |= ((u32)text[cpos++]) << 8;
	    x |= ((u32)text[cpos++]) << 16;
	    x |= ((u32)text[cpos++]) << 24;
	    buf[i] = x;
	}
    }
    else {
	for (i=0; i<Tuple; i++) buf[i] = 0;
	s = diff / 4;
	t = diff % 4;
	for (i=0; i<s; i++) {
	    x = (u32)text[cpos++];
	    x |= ((u32)text[cpos++]) << 8;
	    x |= ((u32)text[cpos++]) << 16;
	    x |= ((u32)text[cpos++]) << 24;
	    buf[i] = x;
	}
	x = 0;
	for (j=0; j<t; j++) {
	    x |= (u32)text[cpos++] << (8*j);
	}
	buf[i] = x;
    }
}

void set_array(u32 buf[], u8* text, u32 cpos)
{
    s32 i;
    for (i=0; i<Tuple; i++) {
	text[cpos++] = (u8)(buf[i] & U32C(0xFF));
	text[cpos++] = (u8)((buf[i]>>8) & U32C(0xFF));
	text[cpos++] = (u8)((buf[i]>>16) & U32C(0xFF));
	text[cpos++] = (u8)((buf[i]>>24) & U32C(0xFF));
    }
}


void hmnencode(ECRYPT_ctx* ctx, const u8* plaintext,  u8* ciphertext,  u32 msglen)  /* Message length in bytes. */ 

{
    s32 i, j, repeat; 
    u32 msgbuf[Tuple];
    u32 cinpos, coutpos;

    repeat = msglen/(4*Tuple);
    if (msglen % (4*Tuple)) 
	repeat++;
    cinpos = coutpos = 0;;
    for (i=0; i<repeat; i++) {
	u32 func_choice[Tuple];

	set_buf(msgbuf, plaintext, cinpos, msglen);
	cinpos += 4*Tuple;

	genrand_tuple_int32(ctx, func_choice, 4);
	
	func_choice[2] *= (func_choice[0] | 0x1UL);
	func_choice[3] *= (func_choice[1] | 0x1UL);
	func_choice[0] ^= (func_choice[3] >> 5);
	func_choice[1] ^= (func_choice[2] >> 5);

	ctx->jump = 1;
	for (j=0; j< 2*Iteration;) {
	    s32 c, t;

	    t = j >> 4; 
	    c = (func_choice[t] >> ((j++ & 0xfUL) * 2)) & 0x3UL;
	    switch (c) {
	    case 0: crypt_empr(ctx, msgbuf); break;
	    case 1: crypt_emer(ctx, msgbuf); break;
	    case 2: crypt_emps(ctx, msgbuf); break;
	    case 3: crypt_emes(ctx, msgbuf); break;
	    }


	    if ((ctx->jump <<= 1) >= Tuple) ctx->jump = 1;

	    t = j >> 4; 
	    c = (func_choice[t] >> ((j++ & 0xfUL) * 2)) & 0x3UL;

	    switch (c) {
	    case 0: crypt_ma(ctx, msgbuf); break;
	    case 1: crypt_mem(ctx, msgbuf); break;
	    case 2: crypt_ome(ctx, msgbuf); break;
	    case 3: crypt_eme(ctx, msgbuf); break;
	    }
	    if ((ctx->jump <<= 1) >= Tuple) ctx->jump = 1;

	    crypt_vert_rotate(ctx, msgbuf);
	    if ((ctx->jump <<= 1) >= Tuple) ctx->jump = 1;
	}

	set_array(msgbuf, ciphertext, coutpos);
	coutpos += 4*Tuple;
    }
}

void hmndecode(ECRYPT_ctx* ctx, const u8* ciphertext, u8* plaintext, u32 msglen) /* Message length in bytes. */ 
{
    s32 i, j, k, repeat;
    u32 temp_rand[3*Iteration][Tuple];
    u32 msgbuf[Tuple];
    s32 cinpos, coutpos;

    repeat = msglen/(4*Tuple);

    cinpos = coutpos = 0;
    for (i=0; i<repeat; i++) {
	u32 func_choice[Tuple];

	set_buf(msgbuf, ciphertext, cinpos, msglen);
	cinpos += 4*Tuple;

	genrand_tuple_int32(ctx, func_choice, 4);

	func_choice[2] *= (func_choice[0] | 0x1UL);
	func_choice[3] *= (func_choice[1] | 0x1UL);
	func_choice[0] ^= (func_choice[3] >> 5);
	func_choice[1] ^= (func_choice[2] >> 5);

	for (k=0; k< 3*Iteration; k++) 
	    genrand_tuple_int32(ctx, temp_rand[k], Tuple);

	ctx->jump = 1 << ((3*Iteration-1) % Log_Tuple);

	for (j=2*Iteration -1; j>=0;) {
	    s32 c, t;

	    t = j >> 4; 
	    c = (func_choice[t] >> ((j-- & 0xfUL) * 2)) & 0x3UL;

	    crypt_vert_rotate_inv(ctx, msgbuf,temp_rand[--k]);
	    if ((ctx->jump >>= 1) == 0) ctx->jump = Tuple >> 1;

	    switch (c) {
	    case 0: crypt_ma_inv(ctx, msgbuf,temp_rand[--k]); break;
	    case 1: crypt_mem_inv(ctx, msgbuf,temp_rand[--k]); break;
	    case 2: crypt_ome_inv(ctx, msgbuf,temp_rand[--k]); break;
	    case 3: crypt_eme_inv(ctx, msgbuf,temp_rand[--k]); break;
	    }
	    if ((ctx->jump >>= 1) == 0) ctx->jump = Tuple >> 1;

	    t = j >> 4; 
	    c = (func_choice[t] >> ((j-- & 0xfUL) * 2)) & 0x3UL;

	    switch (c) {
	    case 0: crypt_empr_inv(ctx, msgbuf,temp_rand[--k]); break;
	    case 1: crypt_emer_inv(ctx, msgbuf,temp_rand[--k]); break;
	    case 2: crypt_emps_inv(ctx, msgbuf,temp_rand[--k]); break;
	    case 3: crypt_emes_inv(ctx, msgbuf,temp_rand[--k]); break;
	    }
	    if ((ctx->jump >>= 1) == 0) ctx->jump = Tuple >> 1;
	}

	set_array(msgbuf, plaintext, coutpos);
	coutpos += 4*Tuple;
    }
}

void ECRYPT_init()
{
    /* do nothing */
}

void ECRYPT_keysetup(
  ECRYPT_ctx* ctx, 
  const u8* key, 
  u32 keysize,                /* Key size in bits. */ 
  u32 ivsize)                /* IV size in bits. */ 
{
    s32 i;

    ctx->keysize = keysize;
    ctx->ivsize = ivsize;
    
    for (i=0; i<keysize/8; i++)
	ctx->key[i] = key[i];
}


void ECRYPT_ivsetup(
  ECRYPT_ctx* ctx, 
  const u8* iv)
{
    s32 i,j,k,t,s;
    u32 x, init_array[(ECRYPT_MAXKEYSIZE+ECRYPT_MAXIVSIZE)/32];

    for (i=0; i<ctx->ivsize/8; i++)
	ctx->iv[i] = iv[i];

    j = 0;
    t = ctx->keysize/32;
    for (i=0; i<t; i++) {
	x = (u32)ctx->key[j++];
	x |= ((u32)ctx->key[j++]) << 8;
	x |= ((u32)ctx->key[j++]) << 16;
	x |= ((u32)ctx->key[j++]) << 24;
	init_array[i] = x;
    }
    if ( ctx->keysize % 32 != 0 ) {
	x = 0;
	k = (ctx->keysize % 32)/8;
	for (i=0; i<k; i++) {
	    x |= ((u32)ctx->key[j++]) << (8*k);
	}
	init_array[t++] = x;
    }

    j = 0;
    s = ctx->ivsize/32;
    for (i=0; i<s; i++) {
	x = (u32)ctx->iv[j++];
	x |= ((u32)ctx->iv[j++]) << 8;
	x |= ((u32)ctx->iv[j++]) << 16;
	x |= ((u32)ctx->iv[j++]) << 24;
	init_array[t+i] = x;
    }
    if ( ctx->ivsize % 32 != 0 ) {
	x = 0;
	k = (ctx->ivsize % 32)/8;
	for (i=0; i<k; i++) {
	    x |= ((u32)ctx->iv[j++]) << (8*k);
	}
	init_array[t+(s++)] = x;
    }
    init_by_array(ctx, init_array, t+s);

    prepare_multi(ctx);
    prepare_multi_inv(ctx);
    prepare_add_table(ctx);
}

void ECRYPT_keystream_bytes(
  ECRYPT_ctx* ctx,
  u8* keystream,
  u32 length) /* length % (4*Tuple) == 0 */   /* Length of keystream in bytes. */
{
    s32 i, j, repeat; 
    u32 msgbuf[Tuple];
    u32 coutpos;

    repeat = length/(4*Tuple);
    if (length % (4*Tuple)) 
	repeat++;
    coutpos = 0;;
    for (i=0; i<repeat; i++) {
	u32 func_choice[Tuple];

	for (j=0; j<Tuple; j++) msgbuf[j] = 0;

	genrand_tuple_int32(ctx, func_choice, 4);
	
	func_choice[2] *= (func_choice[0] | 0x1UL);
	func_choice[3] *= (func_choice[1] | 0x1UL);
	func_choice[0] ^= (func_choice[3] >> 5);
	func_choice[1] ^= (func_choice[2] >> 5);

	for (j=0; j< 2*Iteration;) {
	    s32 c, t;

	    t = j >> 4; 
	    c = (func_choice[t] >> ((j++ & 0xfUL) * 2)) & 0x3UL;
	    switch (c) {
	    case 0: crypt_empr(ctx, msgbuf); break;
	    case 1: crypt_emer(ctx, msgbuf); break;
	    case 2: crypt_emps(ctx, msgbuf); break;
	    case 3: crypt_emes(ctx, msgbuf); break;
	    }

	    t = j >> 4; 
	    c = (func_choice[t] >> ((j++ & 0xfUL) * 2)) & 0x3UL;

	    switch (c) {
	    case 0: crypt_ma(ctx, msgbuf); break;
	    case 1: crypt_mem(ctx, msgbuf); break;
	    case 2: crypt_ome(ctx, msgbuf); break;
	    case 3: crypt_eme(ctx, msgbuf); break;
	    }
	    crypt_vert_rotate(ctx, msgbuf);
	}

	set_array(msgbuf, keystream, coutpos);
	coutpos += 4*Tuple;
    }
}

void ECRYPT_encrypt_bytes(
  ECRYPT_ctx* ctx, 
  const u8* plaintext, 
  u8* ciphertext, 
  u32 msglen)                /* Message length in bytes. */ 
{
    hmnencode(ctx, plaintext, ciphertext, msglen);
}

void ECRYPT_decrypt_bytes(
  ECRYPT_ctx* ctx, 
  const u8* ciphertext, 
  u8* plaintext, 
  u32 msglen)                /* Message length in bytes. */ 
{
    if ( (msglen % (4*Tuple)) != 0 ) {
	printf("ECRYPT_decrypt_bytes: msglen should be multiple of %d.\n", 4*Tuple);
    	return;
    }
    hmndecode(ctx, ciphertext, plaintext, msglen);
}

#ifndef ECRYPT_API

int main(void) 
{
    int i;
    ECRYPT_ctx x;
    u8 plaintext[128], plaintext2[128], ciphertext[128];

    for (i=0; i<128; i++){
      plaintext[i]=0;
    }

    ECRYPT_keysetup(&x, "1234567812345678", 128, 128);
    ECRYPT_ivsetup(&x,  "8765432187654321");
    ECRYPT_encrypt_bytes(&x, plaintext, ciphertext, 128);
    for (i=0; i<16; i++)
	printf("%2x ",  ciphertext[i]);
    printf("\n");

    ECRYPT_ivsetup(&x,  "8765432187654321");
    ECRYPT_decrypt_bytes(&x, ciphertext, plaintext2, 128);
    for (i=0; i<16; i++)
	printf("%2x ",  plaintext2[i]);
    printf("\n");

    return 0;
}

#endif
