#include "ruby.h"
#include "blowfish.h"
#include "string.h"


static char ruby_blowfish_c[] = "$Id: ruby-blowfish.c,v 1.2 2001/05/11 07:18:04 hds Exp $";


#define INIT_IVEC     "\xFE\xDC\xBA\x98\x76\x54\x32\x10"
#define MAX_BUF_SIZE  16383   /* Maximum network buffer size (< 2^14) */


typedef struct
{
  BF_KEY key;
  unsigned char iVec[8];
  int pos;
  unsigned char cryptBuf[MAX_BUF_SIZE];
}
BFState_t;


static VALUE blowfish_new( VALUE class, VALUE str )
{
  VALUE obj;
  BFState_t *bf;

  obj = Data_Make_Struct( class, BFState_t, 0, free, bf );

  BF_set_key( &(bf->key), RSTRING(str)->len, RSTRING(str)->ptr );
  memcpy( bf->iVec, INIT_IVEC, 8 );
  bf->pos = 0 ;

  return obj;
}


//
// ECB-Mode
//

static VALUE blowfish_ecb_encrypt( VALUE self, VALUE str )
{
  BFState_t *bf;

  Check_Type( str, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;
  
  BF_ecb_encrypt( RSTRING(str)->ptr, bf->cryptBuf,
    &(bf->key), BF_ENCRYPT );
  return rb_str_new( bf->cryptBuf, RSTRING(str)->len );
}


static VALUE blowfish_ecb_decrypt( VALUE self, VALUE str )
{
  BFState_t *bf;
  
  Check_Type( str, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;
  
  BF_ecb_encrypt( RSTRING(str)->ptr, bf->cryptBuf,
    &(bf->key), BF_DECRYPT );
  return rb_str_new( bf->cryptBuf, RSTRING(str)->len );
}


//
// CBC-Mode
//

static VALUE blowfish_cbc_encrypt( VALUE self, VALUE str )
{
  BFState_t *bf;
  
  Check_Type( str, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;
  
  BF_cbc_encrypt( RSTRING(str)->ptr, bf->cryptBuf, RSTRING(str)->len,
    &(bf->key), bf->iVec, BF_ENCRYPT );
  return rb_str_new( bf->cryptBuf, RSTRING(str)->len );
}


static VALUE blowfish_cbc_decrypt( VALUE self, VALUE str )
{
  BFState_t *bf;
  
  Check_Type( str, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;
  
  BF_cbc_encrypt( RSTRING(str)->ptr, bf->cryptBuf, RSTRING(str)->len,
    &(bf->key), bf->iVec, BF_DECRYPT );

  return rb_str_new( bf->cryptBuf, RSTRING(str)->len ) ;
}


//
// CFB64-Mode
//

static VALUE blowfish_cfb64_encrypt( VALUE self, VALUE str )
{
  BFState_t *bf;
  
  Check_Type( str, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;
  
  BF_cfb64_encrypt( RSTRING(str)->ptr, bf->cryptBuf, RSTRING(str)->len,
    &(bf->key), bf->iVec,
    &(bf->pos), BF_ENCRYPT );
  return rb_str_new( bf->cryptBuf, RSTRING(str)->len );
}


static VALUE blowfish_cfb64_decrypt( VALUE self, VALUE str )
{
  BFState_t *bf;
  
  Check_Type( str, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;
  
  BF_cfb64_encrypt( RSTRING(str)->ptr, bf->cryptBuf, RSTRING(str)->len,
    &(bf->key), bf->iVec,
    &(bf->pos), BF_DECRYPT );

  return rb_str_new( bf->cryptBuf, RSTRING(str)->len ) ;
}


//
// OFB64-Mode
//

static VALUE blowfish_ofb64_encrypt( VALUE self, VALUE str )
{
  BFState_t *bf;
  
  Check_Type( str, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;
  
  BF_ofb64_encrypt( RSTRING(str)->ptr, bf->cryptBuf, RSTRING(str)->len,
    &(bf->key), bf->iVec, &(bf->pos) );
  return rb_str_new( bf->cryptBuf, RSTRING(str)->len );
}


static VALUE blowfish_ofb64_decrypt( VALUE self, VALUE str )
{
  BFState_t *bf;
  
  Check_Type( str, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;
  
  BF_ofb64_encrypt( RSTRING(str)->ptr, bf->cryptBuf, RSTRING(str)->len,
    &(bf->key), bf->iVec, &(bf->pos) );

  return rb_str_new( bf->cryptBuf, RSTRING(str)->len ) ;
}


//
// Change the Initialisation Vector
//

static VALUE blowfish_change_ivec( VALUE self, VALUE iv )
{
  BFState_t *bf;

  Check_Type( iv, T_STRING );
  Data_Get_Struct( self, BFState_t, bf ) ;

  memcpy( bf->iVec, RSTRING(iv)->ptr, 8 );

  return self;
}


//
// Initialisation
//

void Init_Blowfish()
{
    VALUE cBF = rb_define_class( "Blowfish", rb_cObject );

    rb_define_singleton_method( cBF, "new", blowfish_new, 1 );
    rb_define_method( cBF, "ecb_encrypt", blowfish_ecb_encrypt, 1 ) ;
    rb_define_method( cBF, "ecb_decrypt", blowfish_ecb_decrypt, 1 ) ;
    rb_define_method( cBF, "cbc_encrypt", blowfish_cbc_encrypt, 1 ) ;
    rb_define_method( cBF, "cbc_decrypt", blowfish_cbc_decrypt, 1 ) ;
    rb_define_method( cBF, "cfb64_encrypt", blowfish_cfb64_encrypt, 1 ) ;
    rb_define_method( cBF, "cfb64_decrypt", blowfish_cfb64_decrypt, 1 ) ;
    rb_define_method( cBF, "ofb64_encrypt", blowfish_ofb64_encrypt, 1 ) ;
    rb_define_method( cBF, "ofb64_decrypt", blowfish_ofb64_decrypt, 1 ) ;
    rb_define_method( cBF, "change_ivec", blowfish_change_ivec, 1 ) ;
}

//
// EOF
//
