/* yamb.c */


#include "ecrypt-sync.h"


#define ALF 0x091B17C9


#define stepOLZ16()\
{\
   abcd = ( ( ( ( s32 )OLZ[ I0 ] ) >> 31 ) & ALF ) ^ ( OLZ[ I0 ] + OLZ[ I0 ] ) ^ OLZ[ I1 ];\
   OLZ[ I2 ] = abcd;\
   I0++;    I0 &= 0xF;\
   I1++;    I1 &= 0xF;\
   I2++;    I2 &= 0xF;\
}


#define stepOLZ64()\
{\
   abcd = ( ( ( ( s32 )OLZ[ I0 ] ) >> 31 ) & ALF ) ^ ( OLZ[ I0 ] + OLZ[ I0 ] ) ^ OLZ[ I1 ];\
   OLZ[ I2 ] = abcd;\
   I0++;    I0 &= 0x3F;\
   I1++;    I1 &= 0x3F;\
   I2++;    I2 &= 0x3F;\
}


#define stepUU()\
{\
   a = ( u8 )abcd;\
   b = ( u8 )( abcd >> 8 );\
   c = ( u8 )( abcd >> 16 );\
   d = ( u8 )( abcd >> 24 );\
   b ^= M[ a ];   M[ a ] += d;\
   c ^= M[ d ];   M[ d ] += b;\
   a ^= M[ b ];   M[ b ] += c;\
   d ^= M[ c ];   M[ c ] += a;\
   c ^= M[ a ];   M[ a ] += d;\
   b ^= M[ d ];   M[ d ] += c;\
   a ^= M[ c ];   M[ c ] += b;\
   d ^= M[ b ];   M[ b ] += a;\
   b ^= M[ a ];   M[ a ] += d;\
   c ^= M[ d ];   M[ d ] += b;\
   a ^= M[ b ];   M[ b ] += c;\
   d ^= M[ c ];   M[ c ] += a;\
   ABCD = ( u32 )a | ( ( ( u32 )b ) << 8 ) | ( ( ( u32 )c ) << 16 ) | ( ( ( u32 )d ) << 24 );\
   ABCD ^= abcd;\
}


void ECRYPT_init( void )
{
}


void ECRYPT_keysetup(
   ECRYPT_ctx* ctx,
   const u8*   key,
   u32         keysize,
   u32         ivsize )
{
   u8 i;
   
   ctx->lenkey = keysize / 8;
   ctx->leniv  = ivsize / 8;

   for( i = 0; i < ctx->lenkey; i++ )
      ctx->key[ i ] = key[ i ];
}


void ECRYPT_ivsetup(
   ECRYPT_ctx* ctx, 
   const u8*   iv )
{
   static const u8   ConstM[ 256 ] =
   {
      0x85, 0x2D, 0x43, 0x2F, 0xA6, 0x90, 0xF8, 0x1B, 0xA9, 0xB4, 0x1C, 0x58, 0xE8, 0xA5, 0xD7, 0x56,
      0x6B, 0x03, 0x38, 0x67, 0x3D, 0xB1, 0x7B, 0x0B, 0xF2, 0xCB, 0x29, 0xFC, 0x53, 0x75, 0x05, 0xCA,
      0x0E, 0xAE, 0xD1, 0x9C, 0xBC, 0xB0, 0xDF, 0x62, 0xCF, 0x3A, 0xFE, 0xDC, 0x20, 0x83, 0x88, 0x68,
      0x41, 0x24, 0x45, 0x01, 0x91, 0xF0, 0xA0, 0xF6, 0x65, 0x02, 0xB5, 0xBE, 0x0F, 0xE5, 0x13, 0xD4,
      0xBF, 0xA4, 0x86, 0x4A, 0x63, 0x82, 0x4B, 0xAC, 0x9E, 0xE2, 0x7F, 0x50, 0x6C, 0xEC, 0x31, 0x44,
      0x09, 0x94, 0x9A, 0x40, 0x06, 0xC3, 0x37, 0xF4, 0x2A, 0x57, 0x7C, 0x25, 0x99, 0xFA, 0x21, 0x3B,
      0xEE, 0x54, 0x3C, 0x22, 0xB8, 0xEB, 0x51, 0x8C, 0x87, 0x66, 0x10, 0x27, 0x6D, 0xAA, 0xCE, 0x39,
      0xE0, 0xBD, 0x8B, 0x9B, 0x69, 0xB6, 0xE7, 0x36, 0xAF, 0xDE, 0x34, 0x93, 0x9F, 0xA2, 0x60, 0x14,
      0x7D, 0xA7, 0x8D, 0x7E, 0x76, 0x48, 0x72, 0x74, 0x23, 0xCD, 0x73, 0xD9, 0x33, 0xD6, 0xB2, 0x78,
      0x9D, 0x3F, 0x32, 0x8E, 0xED, 0x5B, 0x2B, 0x4F, 0xD3, 0xE9, 0x1E, 0x4C, 0x16, 0x4E, 0xB3, 0xC5,
      0xD8, 0xF3, 0x2E, 0x26, 0x28, 0x8A, 0x12, 0x64, 0xFB, 0xA3, 0xFF, 0xAD, 0xE1, 0xB7, 0x1A, 0xD0,
      0xF1, 0xBA, 0x7A, 0xA1, 0x00, 0xD2, 0xE4, 0xC6, 0xC0, 0x30, 0x81, 0x52, 0x92, 0x46, 0x61, 0xC1,
      0x95, 0x1F, 0x2C, 0xC2, 0x4D, 0x42, 0x49, 0x07, 0x5A, 0xFD, 0x0C, 0x70, 0xCC, 0x84, 0xF9, 0xD5,
      0x5E, 0x18, 0xB9, 0x5D, 0xC9, 0x5C, 0xC4, 0x1D, 0x6E, 0x35, 0x59, 0xDB, 0x15, 0x79, 0xDD, 0xE6,
      0xDA, 0xA8, 0x89, 0x80, 0x98, 0x5F, 0xEF, 0x96, 0x19, 0xF7, 0xC7, 0x3E, 0x47, 0x0D, 0x71, 0xEA,
      0x04, 0xBB, 0x55, 0x77, 0xC8, 0x0A, 0x17, 0x97, 0xAB, 0x8F, 0x11, 0x08, 0xE3, 0x6F, 0xF5, 0x6A
   };

   static const u8   ConstOLZ[ 9 ] =
   { 
      0x4C, 0x41, 0x4E, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6F
   };

   u32   i;
   u8    a, b, c, d;
   u32   abcd, ABCD;
   u8    *M, *pu8;
   u32   *OLZ, *RZ;
   u8    I0, I1, I2, J;

   M     = ctx->M; 
   OLZ   = ctx->OLZ; 
   RZ    = ctx->RZ;
   pu8   = ( u8* )OLZ;

   for( i = 0; i < 256; i++ )
      M[ i ] = ConstM[ i ];
 
   for( i = 0; i < 32; i++ )
      pu8[ i ] = ctx->key[ i % ctx->lenkey ];
   
   for( i = 32; i < ( u32 )( 32 + ctx->leniv ); i++ )
      pu8[ i ] = iv[ i - 32 ];
   
   for( i = 32 + ctx->leniv; i < 60; i++ )
      pu8[ i ] = ConstOLZ[ ( i - 32 - ctx->leniv ) % 9 ];

   for( i = 0; i < 15; i++ )  
      OLZ[ i ] = ( ( u32 )pu8[ i * 4 ] ) | 
                 ( ( ( u32 )pu8[ i * 4 + 1 ] ) << 8 ) | 
                 ( ( ( u32 )pu8[ i * 4 + 2 ] ) << 16 ) |
                 ( ( ( u32 )pu8[ i * 4 + 3 ] ) << 24);

   I0 = 0;
   I1 = 8;
   I2 = 15;
   J  = 0;
   
   for( i = 0; i < 225; i++ )
   { 
      stepOLZ16();
      stepUU();
      RZ[ J ] = ABCD;
      J++;
      J &= 0xF;
   } 

   for( i = 0; i < 64; i++ )
   {
      stepOLZ16();
      stepUU();
      abcd = ( ABCD + RZ[ J ] );
      M[ i * 4 ]     ^= ( u8 )abcd;
      M[ i * 4 + 1 ] ^= ( u8 )( abcd >> 8 );
      M[ i * 4 + 2 ] ^= ( u8 )( abcd >> 16 );
      M[ i * 4 + 3 ] ^= ( u8 )( abcd >> 24 );
      RZ[ J ] = ABCD;
      J++;
      J &= 0xF;
   }

   for( i = 0; i < 15; i++ )
   {
      stepOLZ16();
      stepUU();
      RZ[ J ] += ABCD;
      J++;
      J &= 0xF;
   }

   for( i = 1; i < 16; i++ )
      OLZ[ i + 32 ] = RZ[ i ];

   I0 = 33;
   I1 = 41;
   I2 = 48;
   J  = 0;
   
   for( i = 0; i < 16; i++ )
   {
      stepOLZ64();
      stepUU();
      RZ[ J ] = ABCD;
      J++;
      J &= 0xF;
   }

   for( i = 0; i < 64; i++ )
       stepOLZ64();
}


void ECRYPT_process_bytes(
   int         action,
   ECRYPT_ctx* ctx,
   const u8*   input,
   u8*         output,
   u32         msglen )
{
   u8 remsize;
   u8 remainder[ ECRYPT_BLOCKLENGTH ];

   ECRYPT_process_blocks( action, ctx, input, output, ( msglen / ECRYPT_BLOCKLENGTH ) );

   if( (remsize = ( msglen % ECRYPT_BLOCKLENGTH )) )
   {
     u32 i;

      ECRYPT_keystream_blocks( ctx, remainder, 1 );
      for( i = ( msglen - remsize ); i < msglen; i++ )
         output[ i ] = input[ i ] ^ remainder[ i - ( msglen - remsize ) ];
   }
}


void ECRYPT_keystream_bytes(
   ECRYPT_ctx* ctx,
   u8*         keystream,
   u32         length )
{
   u8 remsize, i;
   u8 remainder[ ECRYPT_BLOCKLENGTH ];
   
   ECRYPT_keystream_blocks( ctx, keystream, ( length / ECRYPT_BLOCKLENGTH ) );
   
   if( (remsize = ( length % ECRYPT_BLOCKLENGTH )) )
   {
      ECRYPT_keystream_blocks( ctx, remainder, 1 );
      for( i = ( length - remsize ); i < length; i++ )
         keystream[ i ] = remainder[ i - ( length - remsize ) ];
   }
}


void ECRYPT_process_blocks(
   int         action,
   ECRYPT_ctx* ctx, 
   const u8*   input, 
   u8*         output, 
   u32         blocks )
{
   u32 i;

   ECRYPT_keystream_blocks( ctx, output, blocks );

   for( i = 0; i < ( blocks * ECRYPT_BLOCKLENGTH ); i++ )
      output[ i ] ^= input[ i ];
}


/* x86 */
#if( defined( _M_IX86 ) || defined( _MSC_VER ) || defined( __INTEL_COMPILER ) || defined( i386 ) || defined( __i386 ) )


#define  stepR( I )\
{\
   ABCD = BUF[ I ] ^ OLZ[ I ];\
   *( ( u32* )keystream + I ) = ( ABCD + RZ[ I & 0xF ] );\
   RZ[ I & 0xF ] = ABCD;\
   OLZ[ I & 0x3F ] = \
   ( ( ( ( s32 )OLZ[ ( I + 49 ) & 0x3F ] ) >> 31 ) & ALF ) ^ \
   ( OLZ[ ( I + 49 ) & 0x3F ] + OLZ[ ( I + 49 ) & 0x3F ] ) ^ OLZ[ ( I + 57 ) & 0x3F ];\
}


/* x86: Intel, MS, Borland */
#if( defined( _M_IX86 ) || defined( _MSC_VER ) || defined( __INTEL_COMPILER ) )


#define  stepasmU( Rb, Ra, Rd ) \
{\
	_asm{ movzx eax, byte ptr [ esi + Ra ] };\
	_asm{ xor Rb, eax };\
	_asm{ lea eax, [ eax+ Rd ] };\
	_asm{ mov byte ptr [ esi + Ra ], al };\
}


#define  stepU( I )\
{\
	_asm{ movzx edi, [ esi + 256 + 4 * I ] };\
	_asm{ movzx ebx, [ esi + 256 + 4 * I + 1 ] };\
	_asm{ movzx ecx, [ esi + 256 + 4 * I + 2 ] };\
	_asm{ movzx edx, [ esi + 256 + 4 * I + 3 ] };\
	stepasmU( ebx, edi, edx );\
	stepasmU( ecx, edx, ebx );\
	stepasmU( edi, ebx, ecx );\
	stepasmU( edx, ecx, edi );\
	stepasmU( ecx, edi, edx );\
	stepasmU( ebx, edx, ecx );\
	stepasmU( edi, ecx, ebx );\
	stepasmU( edx, ebx, edi );\
	stepasmU( ebx, edi, edx );\
	stepasmU( ecx, edx, ebx );\
	stepasmU( edi, ebx, ecx );\
	stepasmU( edx, ecx, edi );\
	_asm{ mov eax, edi };\
	_asm{ mov byte ptr BUF[ I * 4 ], al };\
	_asm{ mov byte ptr BUF[ I * 4 + 1 ], bl };\
	_asm{ mov byte ptr BUF[ I * 4 + 2 ], cl };\
	_asm{ mov byte ptr BUF[ I * 4 + 3 ], dl };\
}


/* x86: gcc */
#elif( defined( i386 ) || defined( __i386 ) )


#define  stepU( I )\
{\
   asm(  "movzx %c0( %%esi ), %%edi"\
         : : "n" ( 256 + 4 * I ) );\
   asm(  "movzx %c0( %%esi ), %%ebx"\
         : : "n" ( 256 + 4 * I + 1 ) );\
   asm(  "movzx %c0( %%esi ), %%ecx"\
         : : "n" ( 256 + 4 * I + 2 ) );\
   asm(  "movzx %c0( %%esi ), %%edx"\
         : : "n" ( 256 + 4 * I + 3 ) );\
   asm(  "movzb ( %esi, %edi ), %eax\n"\
         "xor %eax, %ebx\n"\
         "lea ( %eax, %edx ), %eax\n"\
         "movb %al, ( %esi, %edi )\n" );\
   asm(  "movzb ( %esi, %edx ), %eax\n"\
         "xor %eax, %ecx\n"\
         "lea ( %eax, %ebx ), %eax\n"\
         "movb %al, ( %esi, %edx )\n" );\
   asm(  "movzb ( %esi, %ebx ), %eax\n"\
         "xor %eax, %edi\n"\
         "lea ( %eax, %ecx ), %eax\n"\
         "movb %al, ( %esi, %ebx )\n" );\
   asm(  "movzb ( %esi, %ecx ), %eax\n"\
         "xor %eax, %edx\n"\
         "lea ( %eax, %edi ), %eax\n"\
         "movb %al, ( %esi, %ecx )\n" );\
   asm(  "movzb ( %esi, %edi ), %eax\n"\
         "xor %eax, %ecx\n"\
         "lea ( %eax, %edx ), %eax\n"\
         "movb %al, ( %esi, %edi )\n" );\
   asm(  "movzb ( %esi, %edx ), %eax\n"\
         "xor %eax, %ebx\n"\
         "lea ( %eax, %ecx ), %eax\n"\
         "movb %al, ( %esi, %edx )\n" );\
   asm(  "movzb ( %esi, %ecx ), %eax\n"\
         "xor %eax, %edi\n"\
         "lea ( %eax, %ebx ), %eax\n"\
         "movb %al, ( %esi, %ecx )\n" );\
   asm(  "movzb ( %esi, %ebx ), %eax\n"\
         "xor %eax, %edx\n"\
         "lea ( %eax, %edi ), %eax\n"\
         "movb %al, ( %esi, %ebx )\n" );\
   asm(  "movzb ( %esi, %edi ), %eax\n"\
         "xor %eax, %ebx\n"\
         "lea ( %eax, %edx ), %eax\n"\
         "movb %al, ( %esi, %edi )\n" );\
   asm(  "movzb ( %esi, %edx ), %eax\n"\
         "xor %eax, %ecx\n"\
         "lea ( %eax, %ebx ), %eax\n"\
         "movb %al, ( %esi, %edx )\n" );\
   asm(  "movzb ( %esi, %ebx ), %eax\n"\
         "xor %eax, %edi\n"\
         "lea ( %eax, %ecx ), %eax\n"\
         "movb %al, ( %esi, %ebx )\n" );\
   asm(  "movzb ( %esi, %ecx ), %eax\n"\
         "xor %eax, %edx\n"\
         "lea ( %eax, %edi ), %eax\n"\
         "movb %al, ( %esi, %ecx )\n" );\
   asm(  "movl %edi, %eax\n" );\
   asm(  "movb %%al, %0\n"\
         : "=m" ( ( ( u8* )( &BUF ) )[ I * 4 ] ) : );\
   asm(  "movb %%bl, %0\n"\
         : "=m" ( ( ( u8* )( &BUF ) )[ I * 4 + 1 ] ) : );\
   asm(  "movb %%cl, %0\n"\
         : "=m" ( ( ( u8* )( &BUF ) )[ I * 4 + 2 ] ) : );\
   asm(  "movb %%dl, %0\n"\
         : "=m" ( ( ( u8* )( &BUF ) )[ I * 4 + 3 ] ) : );\
}


#endif


/* others */
#else


#define  stepU( I )\
{\
   a = ( u8 )OLZ[ I ];\
   b = ( u8 )( OLZ[ I ] >> 8 );\
   c = ( u8 )( OLZ[ I ] >> 16 );\
   d = ( u8 )( OLZ[ I ] >> 24 );\
   b ^= M[ a ];   M[ a ] += d;\
   c ^= M[ d ];   M[ d ] += b;\
   a ^= M[ b ];   M[ b ] += c;\
   d ^= M[ c ];   M[ c ] += a;\
   c ^= M[ a ];   M[ a ] += d;\
   b ^= M[ d ];   M[ d ] += c;\
   a ^= M[ c ];   M[ c ] += b;\
   d ^= M[ b ];   M[ b ] += a;\
   b ^= M[ a ];   M[ a ] += d;\
   c ^= M[ d ];   M[ d ] += b;\
   a ^= M[ b ];   M[ b ] += c;\
   d ^= M[ c ];   M[ c ] += a;\
   *( ( u8* )BUF + I * 4 )       = a;\
   *( ( u8* )BUF + I * 4 + 1 )   = b;\
   *( ( u8* )BUF + I * 4 + 2 )   = c;\
   *( ( u8* )BUF + I * 4 + 3 )   = d;\
}


#define  stepR( I )\
{\
   ABCD =   ( u32 )P[ I * 4 ] | \
   ( ( u32 )P[ I * 4 + 1 ] << 8 ) | \
   ( ( u32 )P[ I * 4 + 2 ] << 16 ) | \
   ( ( u32 )P[ I * 4 + 3 ] << 24);\
   ABCD  ^= OLZ[I];\
   abcd   = ( ABCD + RZ[ I & 0xF ] );\
   keystream[ I * 4 ]      = ( u8 )abcd;\
   keystream[ I * 4 + 1 ]  = ( u8 )( abcd >> 8 );\
   keystream[ I * 4 + 2 ]  = ( u8 )( abcd >> 16 );\
   keystream[ I * 4 + 3 ]  = ( u8 )( abcd >> 24 );\
   RZ[ I & 0xF ]     = ABCD;\
   OLZ[ I & 0x3F ]   = \
   ( ( ( ( s32 )OLZ[ ( I + 49 ) & 0x3F ] ) >> 31 ) & ALF ) ^ \
   ( OLZ[ ( I + 49 ) & 0x3F ] + OLZ[ ( I + 49 ) & 0x3F ] ) ^ OLZ[ ( I + 57 ) & 0x3F ];\
}


#endif


void ECRYPT_keystream_blocks(
   ECRYPT_ctx* ctx,
   u8*         keystream,
   u32         blocks )
{
   u32   ABCD;

   u32   BUF[64];

   u8    *M;
   u32   *OLZ, *RZ;

#if( !defined( _M_IX86 ) && !defined( _MSC_VER ) && !defined( __INTEL_COMPILER ) && !defined( i386 ) && !defined( __i386 ) )

   u8    a, b, c, d;
   u32   abcd;
   u8    *P;
   P     = ( u8* )BUF;

#endif

   M     = ctx->M; 
   OLZ   = ctx->OLZ; 
   RZ    = ctx->RZ;

   while( blocks-- )
   {

#if( defined( _M_IX86 ) || defined( _MSC_VER ) || defined( __INTEL_COMPILER ) )

      _asm{ mov esi, ctx };

#elif( defined( i386 ) || defined( __i386 ) )

      asm( "pusha" );
      asm( "movl %0, %%esi" : : "g" ( ctx ) );

#endif

      stepU( 0 );
      stepU( 1 );
      stepU( 2 );
      stepU( 3 );
      stepU( 4 );
      stepU( 5 );
      stepU( 6 );
      stepU( 7 );
      stepU( 8 );
      stepU( 9 );
      stepU( 10 );
      stepU( 11 );
      stepU( 12 );
      stepU( 13 );
      stepU( 14 );
      stepU( 15 );
      stepU( 16 );
      stepU( 17 );
      stepU( 18 );
      stepU( 19 );
      stepU( 20 );
      stepU( 21 );
      stepU( 22 );
      stepU( 23 );
      stepU( 24 );
      stepU( 25 );
      stepU( 26 );
      stepU( 27 );
      stepU( 28 );
      stepU( 29 );
      stepU( 30 );
      stepU( 31 );
      stepU( 32 );
      stepU( 33 );
      stepU( 34 );
      stepU( 35 );
      stepU( 36 );
      stepU( 37 );
      stepU( 38 );
      stepU( 39 );
      stepU( 40 );
      stepU( 41 );
      stepU( 42 );
      stepU( 43 );
      stepU( 44 );
      stepU( 45 );
      stepU( 46 );
      stepU( 47 );
      stepU( 48 );
      stepU( 49 );
      stepU( 50 );
      stepU( 51 );
      stepU( 52 );
      stepU( 53 );
      stepU( 54 );
      stepU( 55 );
      stepU( 56 );
      stepU( 57 );
      stepU( 58 );
      stepU( 59 );
      stepU( 60 );
      stepU( 61 );
      stepU( 62 );
      stepU( 63 );

#if(  defined( i386 ) || defined( __i386 ) )

      asm( "popa" );

#endif

      stepR( 0 );
      stepR( 1 );
      stepR( 2 );
      stepR( 3 );
      stepR( 4 );
      stepR( 5 );
      stepR( 6 );
      stepR( 7 );
      stepR( 8 );
      stepR( 9 );
      stepR( 10 );
      stepR( 11 );
      stepR( 12 );
      stepR( 13 );
      stepR( 14 );
      stepR( 15 );
      stepR( 16 );
      stepR( 17 );
      stepR( 18 );
      stepR( 19 );
      stepR( 20 );
      stepR( 21 );
      stepR( 22 );
      stepR( 23 );
      stepR( 24 );
      stepR( 25 );
      stepR( 26 );
      stepR( 27 );
      stepR( 28 );
      stepR( 29 );
      stepR( 30 );
      stepR( 31 );
      stepR( 32 );
      stepR( 33 );
      stepR( 34 );
      stepR( 35 );
      stepR( 36 );
      stepR( 37 );
      stepR( 38 );
      stepR( 39 );
      stepR( 40 );
      stepR( 41 );
      stepR( 42 );
      stepR( 43 );
      stepR( 44 );
      stepR( 45 );
      stepR( 46 );
      stepR( 47 );
      stepR( 48 );
      stepR( 49 );
      stepR( 50 );
      stepR( 51 );
      stepR( 52 );
      stepR( 53 );
      stepR( 54 );
      stepR( 55 );
      stepR( 56 );
      stepR( 57 );
      stepR( 58 );
      stepR( 59 );
      stepR( 60 );
      stepR( 61 );
      stepR( 62 );
      stepR( 63 );

      keystream += 256;  
   }
}
