/******************************************************************************/
/* Filename : rabbit.c                                                    */
/*----------------------------------------------------------------------------*/
/* File ini merupakan modifikasi source code Rabbit API yang terdapat pada    */
/* Algorithm Spesification (http://www.cryptico.com)                          */
/*                                                                            */
/******************************************************************************/


#include <openssl/rabbit.h>
#include "rabbit_locl.h"
#include <stddef.h>

/* Hanya untuk pengujian */
/*  
  
static cc_uint32 rabbit_rotl(cc_uint32 x,int rot);
static cc_uint32 rabbit_g_func(cc_uint32 x);
static void rabbit_next_state(RABBIT_KEY *key);
int rabbit_key_setup(RABBIT_KEY *key, const cc_byte *key_input, size_t key_length);
int rabbit_iv_setup(const RABBIT_KEY *master_key,
    RABBIT_KEY *key, const cc_byte *iv, size_t iv_length);
int rabbit_cipher(RABBIT_KEY *key, const cc_byte *in,
    cc_byte *out, size_t data_length);
int rabbit_prng(RABBIT_KEY *key, cc_byte *out,
    size_t data_length);

main() {
       RABBIT_KEY master_key;
       RABBIT_KEY key;
       cc_byte out[32], key_input[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
                                         0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
       int i;
       
       rabbit_key_setup(&key, key_input);
       rabbit_prng(&key, out, 32);
           
       for (i=0; i<16; i++)
           printf("%2x ", out[i]);
       printf("\n");
       for (i=16; i<32; i++)
           printf("%2x ", out[i]);
       printf("\n");
       
       return 0;
}      
*/

         
static cc_uint32 rabbit_rotl(cc_uint32 x,int rot)
{
    return(x<<rot)|(x>>(32-rot));
}

static cc_uint32 rabbit_g_func(cc_uint32 x)
{
     /*Temporary variables*/
     cc_uint32 a, b, h, l;
     
     /*Construct high and low argument for squaring*/
     a = x&0xFFFF;
     b = x>>16;

     /*Calculate high and low result of squaring*/
     h = ((((a*a)>>17) + (a*b))>>15) + b*b;
     l = x*x;

     /*Return high XOR low*/
     return h^l;
}

static void rabbit_next_state(RABBIT_KEY *key)
{
     /*Temporary variables*/
     cc_uint32 g[8],c_old[8],i;

     /*Save old counter values*/
     for(i=0;i<8;i++)
     c_old[i]=key->c[i];
     
     /*Calculate new counter values*/
     key->c[0]+=0x4D34D34D+key->carry;
     key->c[1]+=0xD34D34D3+(key->c[0]<c_old[0]);
     key->c[2]+=0x34D34D34+(key->c[1]<c_old[1]);
     key->c[3]+=0x4D34D34D+(key->c[2]<c_old[2]);
     key->c[4]+=0xD34D34D3+(key->c[3]<c_old[3]);
     key->c[5]+=0x34D34D34+(key->c[4]<c_old[4]);
     key->c[6]+=0x4D34D34D+(key->c[5]<c_old[5]);
     key->c[7]+=0xD34D34D3+(key->c[6]<c_old[6]);
     key->carry=(key->c[7]<c_old[7]);

     /*Calculate the g-functions*/
     for(i=0;i<8;i++)
          g[i]=rabbit_g_func(key->x[i] + key->c[i]);
          
     /*Calculate new state values*/
     key->x[0]=g[0]+rabbit_rotl(g[7],16)+rabbit_rotl(g[6],16);
     key->x[1]=g[1]+rabbit_rotl(g[0],8)+g[7];
     key->x[2]=g[2]+rabbit_rotl(g[1],16)+rabbit_rotl(g[0],16);
     key->x[3]=g[3]+rabbit_rotl(g[2],8)+g[1];
     key->x[4]=g[4]+rabbit_rotl(g[3],16)+rabbit_rotl(g[2],16);
     key->x[5]=g[5]+rabbit_rotl(g[4],8)+g[3];
     key->x[6]=g[6]+rabbit_rotl(g[5],16)+rabbit_rotl(g[4],16);
     key->x[7]=g[7]+rabbit_rotl(g[6],8)+g[5];
}

/*Pada fungsi 'rabbit_key_setup', parameter 'key_length' dapat dihilangkan, 
  karena parameter ini sebenarnya hanya untuk mencegah panjang kunci <16 
*/
void rabbit_key_setup(RABBIT_KEY *key,const cc_byte *key_input)
{
     /*Temporary variables*/
     cc_uint32 k0,k1,k2,k3,i;

     /*Return error if the keysize is not 16 bytes*/
     /*if(key_length!=16)
     return -1;*/

     /*Generate four subkeys*/
     k0 = *(cc_uint32 *)(key_input+0);
     k1 = *(cc_uint32 *)(key_input+4);
     k2 = *(cc_uint32 *)(key_input+8);
     k3 = *(cc_uint32 *)(key_input+12);

     /*Generate initial state variables*/
     key->x[0]=k0;
     key->x[2]=k1;
     key->x[4]=k2;
     key->x[6]=k3;
     key->x[1]=(k3<<16)|(k2>>16);
     key->x[3]=(k0<<16)|(k3>>16);
     key->x[5]=(k1<<16)|(k0>>16);
     key->x[7]=(k2<<16)|(k1>>16);

     /*Generate initial counter values*/
     key->c[0]=rabbit_rotl(k2,16);
     key->c[2]=rabbit_rotl(k3,16);
     key->c[4]=rabbit_rotl(k0,16);
     key->c[6]=rabbit_rotl(k1,16);
     key->c[1]=(k0&0xFFFF0000)|(k1&0xFFFF);
     key->c[3]=(k1&0xFFFF0000)|(k2&0xFFFF);
     key->c[5]=(k2&0xFFFF0000)|(k3&0xFFFF);
     key->c[7]=(k3&0xFFFF0000)|(k0&0xFFFF);

     key->carry=0;               /*Clear carry bit*/
     
     for(i=0;i<4;i++)                   /*Iterate the system four times*/
          rabbit_next_state(key);

     for(i=0;i<8;i++)                   /*Modify the counters*/
          key->c[i]^=key->x[(i+4)&0x7];

     /*return 0;*/                          /*Return success*/
}

void rabbit_iv_setup(const RABBIT_KEY *master_key,
    RABBIT_KEY *key,const cc_byte *iv,size_t iv_length)
{
     cc_uint32 i0,i1,i2,i3,i;         /*Temporary variables*/
     
     /*Return error if the IV size is not 8 bytes*/
     /*if(iv_length!=8)
     return -1;*/
     
     /*Generate four subvectors*/
     i0 = *(cc_uint32*)(iv+0);
     i2 = *(cc_uint32*)(iv+4);
     i1 = (i0>>16)|(i2&0xFFFF0000);
     i3 = (i2<<16)|(i0&0x0000FFFF);

     /*Modify counter values*/
     key->c[0]=master_key->c[0]^i0;
     key->c[1]=master_key->c[1]^i1;
     key->c[2]=master_key->c[2]^i2;
     key->c[3]=master_key->c[3]^i3;
     key->c[4]=master_key->c[4]^i0;
     key->c[5]=master_key->c[5]^i1;
     key->c[6]=master_key->c[6]^i2;
     key->c[7]=master_key->c[7]^i3;
          
     for(i=0;i<8;i++)                            /*Copy internal state values*/
          key->x[i]=master_key->x[i];
          key->carry=master_key->carry;
     
     for(i=0;i<4;i++)                            /*Iterate the system four times*/
          rabbit_next_state(key);

     /*return 0;*/                                   /*Return success*/
}

void rabbit(RABBIT_KEY *key, const cc_byte *in, cc_byte *out, 
     unsigned int data_length)
{
     cc_uint32 i;                                 /*Temporary variables*/

     /*if(data_length%16)
     return -1;*/
     
     for(i=0;i<data_length;i+=16)
     {
          /*Iterate the system*/
          rabbit_next_state(key);

          /*Encrypt 16 bytes of data*/
          *(cc_uint32*)(out+0)=*(cc_uint32*)(in+0)^
          key->x[0]^(key->x[5]>>16)^(key->x[3]<<16);
          
          *(cc_uint32*)(out+4)=*(cc_uint32*)(in+4)^
          key->x[2]^(key->x[7]>>16)^(key->x[5]<<16);
          
          *(cc_uint32*)(out+8)=*(cc_uint32*)(in+8)^
          key->x[4]^(key->x[1]>>16)^(key->x[7]<<16);

          *(cc_uint32*)(out+12)=*(cc_uint32*)(in+12)^
          key->x[6]^(key->x[3]>>16)^(key->x[1]<<16);
          
          /*Increment pointers to source and destination data*/
          in+=16;
          out+=16;
     }

     /*Return success*/
     /*return 0;*/
}

/* Hanya untuk keperluan pengujian */
/*
int rabbit_prng(RABBIT_KEY *key,cc_byte *out, size_t data_length)
{
     cc_uint32 i;
     
     if(data_length%16)
          return -1;

     for(i=0;i<data_length;i+=16)
     {
          rabbit_next_state(key);
          
          *(cc_uint32*)(out+0)=key->x[0]^
          (key->x[5]>>16)^(key->x[3]<<16);
          
          *(cc_uint32*)(out+4)=key->x[2]^
          (key->x[7]>>16)^(key->x[5]<<16);
          
          *(cc_uint32*)(out+8)=key->x[4]^
          (key->x[1]>>16)^(key->x[7]<<16);
          
          *(cc_uint32*)(out+12)=key->x[6]^
          (key->x[3]>>16)^(key->x[1]<<16);
          
          out+=16;
     }
     
return 0;
}
*/
