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

#ifndef OPENCL_LION_H__
#define OPENCL_LION_H__

#include <opencl/opencl.h>

namespace OpenCL {

template<typename H, typename S, u32bit BS = 1024>
class Lion : public BlockCipher
   {
   public:
      static const u32bit BLOCKSIZE = BS;

      void clear() throw();
      std::string name() const;
      BlockCipher* clone() const;
      Lion(HashFunction* = new H, StreamCipher* = new S, u32bit = BS);
   private:
      void enc(const byte[], byte[]) const;
      void dec(const byte[], byte[]) const;
      void key(const byte[], u32bit);
      const u32bit LEFT_SIZE, RIGHT_SIZE;
      HashFunction* hash;
      StreamCipher* cipher;
      SecureVector<byte> key1, key2;
   };

/*************************************************
* Lion Encryption                                *
*************************************************/
template<typename H, typename S, u32bit BS>
void Lion<H,S,BS>::enc(const byte in[], byte out[]) const
   {
   SecureVector<byte> buffer(LEFT_SIZE);

   xor_buf(buffer, in, key1, LEFT_SIZE);
   cipher->set_key(buffer, LEFT_SIZE);
   cipher->encrypt(in + LEFT_SIZE, out + LEFT_SIZE, RIGHT_SIZE);

   hash->update(out + LEFT_SIZE, RIGHT_SIZE);
   hash->final(buffer);
   xor_buf(out, in, buffer, LEFT_SIZE);

   xor_buf(buffer, out, key2, LEFT_SIZE);
   cipher->set_key(buffer, LEFT_SIZE);
   cipher->encrypt(out + LEFT_SIZE, RIGHT_SIZE);
   }

/*************************************************
* Lion Decryption                                *
*************************************************/
template<typename H, typename S, u32bit BS>
void Lion<H,S,BS>::dec(const byte in[], byte out[]) const
   {
   SecureVector<byte> buffer(LEFT_SIZE);

   xor_buf(buffer, in, key2, LEFT_SIZE);
   cipher->set_key(buffer, LEFT_SIZE);
   cipher->encrypt(in + LEFT_SIZE, out + LEFT_SIZE, RIGHT_SIZE);

   hash->update(out + LEFT_SIZE, RIGHT_SIZE);
   hash->final(buffer);
   xor_buf(out, in, buffer, LEFT_SIZE);

   xor_buf(buffer, out, key1, LEFT_SIZE);
   cipher->set_key(buffer, LEFT_SIZE);
   cipher->encrypt(out + LEFT_SIZE, RIGHT_SIZE);
   }

/*************************************************
* Lion Key Schedule                              *
*************************************************/
template<typename H, typename S, u32bit BS>
void Lion<H,S,BS>::key(const byte key[], u32bit length)
   {
   clear();
   key1.copy(key,              length / 2);
   key2.copy(key + length / 2, length / 2);
   }

/*************************************************
* Return the name of this type                   *
*************************************************/
template<typename H, typename S, u32bit BS>
std::string Lion<H,S,BS>::name() const
   {
   return "Lion<" + hash->name() + "," + cipher->name() + ">";
   }

/*************************************************
* Return a clone of this object                  *
*************************************************/
template<typename H, typename S, u32bit BS>
BlockCipher* Lion<H,S,BS>::clone() const
   {
   return new Lion<H,S,BS>;
   }

/*************************************************
* Clear memory of sensitive data                 *
*************************************************/
template<typename H, typename S, u32bit BS>
void Lion<H,S,BS>::clear() throw()
   {
   hash->clear();
   cipher->clear();
   key1.clear();
   key2.clear();
   }

/*************************************************
* Lion Constructor                               *
*************************************************/
template<typename H, typename S, u32bit BS>
Lion<H,S,BS>::Lion(HashFunction* h, StreamCipher* sc, u32bit block_len) :
   BlockCipher(block_len, 2, 2*h->OUTPUT_LENGTH, 2),
   LEFT_SIZE(h->OUTPUT_LENGTH), RIGHT_SIZE(BLOCK_SIZE - LEFT_SIZE)
   {
   if(2*LEFT_SIZE + 1 > BLOCK_SIZE)
      throw Invalid_Argument(name() + ": Chosen block size is too small");
   if(!sc->valid_keylength(LEFT_SIZE))
      throw Exception(name() + ": This stream/hash combination is invalid");
   hash = h;
   cipher = sc;
   key1.create(LEFT_SIZE);
   key2.create(LEFT_SIZE);
   }

}

#endif
