/*************************************************
* Luby-Rackoff Header File                       *
* (C) 1999-2002 The OpenCL Project               *
*************************************************/

#ifndef OPENCL_LUBY_RACKOFF_H__
#define OPENCL_LUBY_RACKOFF_H__

#include <opencl/opencl.h>

namespace OpenCL {

template<typename H>
class LubyRackoff : public BlockCipher
   {
   public:
      static const u32bit BLOCKSIZE = 2*H::HASHLENGTH;

      void clear() throw() { K1.clear(); K2.clear(); }
      std::string name() const { return "LubyRackoff<" + hash->name() + ">"; }
      BlockCipher* clone() const { return new LubyRackoff<H>; }
      LubyRackoff(HashFunction* = new H);
      ~LubyRackoff() { delete hash; }
   private:
      void enc(const byte[], byte[]) const;
      void dec(const byte[], byte[]) const;
      void key(const byte[], u32bit);
      HashFunction* hash;
      SecureVector<byte> K1, K2;
   };

/*************************************************
* Luby-Rackoff Encryption                        *
*************************************************/
template<typename H>
void LubyRackoff<H>::enc(const byte in[], byte out[]) const
   {
   SecureVector<byte> buffer(hash->OUTPUT_LENGTH);
   hash->update(K1, K1.size());
   hash->update(in, hash->OUTPUT_LENGTH);
   hash->final(buffer);
   xor_buf(out + hash->OUTPUT_LENGTH, in + hash->OUTPUT_LENGTH, buffer,
           hash->OUTPUT_LENGTH);

   hash->update(K2, K2.size());
   hash->update(out + hash->OUTPUT_LENGTH, hash->OUTPUT_LENGTH);
   hash->final(buffer);
   xor_buf(out, in, buffer, hash->OUTPUT_LENGTH);

   hash->update(K1, K1.size());
   hash->update(out, hash->OUTPUT_LENGTH);
   hash->final(buffer);
   xor_buf(out + hash->OUTPUT_LENGTH, buffer, hash->OUTPUT_LENGTH);

   hash->update(K2, K1.size());
   hash->update(out + hash->OUTPUT_LENGTH, hash->OUTPUT_LENGTH);
   hash->final(buffer);
   xor_buf(out, buffer, hash->OUTPUT_LENGTH);
   }

/*************************************************
* Luby-Rackoff Decryption                        *
*************************************************/
template<typename H>
void LubyRackoff<H>::dec(const byte in[], byte out[]) const
   {
   SecureVector<byte> buffer(hash->OUTPUT_LENGTH);
   hash->update(K2, K2.size());
   hash->update(in + hash->OUTPUT_LENGTH, hash->OUTPUT_LENGTH);
   hash->final(buffer);
   xor_buf(out, in, buffer, hash->OUTPUT_LENGTH);

   hash->update(K1, K1.size());
   hash->update(out, hash->OUTPUT_LENGTH);
   hash->final(buffer);
   xor_buf(out + hash->OUTPUT_LENGTH, in + hash->OUTPUT_LENGTH, buffer,
           hash->OUTPUT_LENGTH);

   hash->update(K2, K2.size());
   hash->update(out + hash->OUTPUT_LENGTH, hash->OUTPUT_LENGTH);
   hash->final(buffer);
   xor_buf(out, buffer, hash->OUTPUT_LENGTH);

   hash->update(K1, K1.size());
   hash->update(out, hash->OUTPUT_LENGTH);
   hash->final(buffer);
   xor_buf(out + hash->OUTPUT_LENGTH, buffer, hash->OUTPUT_LENGTH);
   }

/*************************************************
* Luby-Rackoff Key Schedule                      *
*************************************************/
template<typename H>
void LubyRackoff<H>::key(const byte key[], u32bit length)
   {
   K1.resize_and_copy(key, length / 2);
   K2.resize_and_copy(key + length / 2, length / 2);
   }

/*************************************************
* Luby-Rackoff Key Schedule                      *
*************************************************/
template<typename H>
LubyRackoff<H>::LubyRackoff(HashFunction* h) :
   BlockCipher(2*h->OUTPUT_LENGTH, 2, 32, 2)
   {
   hash = h;
   }

}

#endif
