// serpent.cpp - originally written and placed in the public domain by Wei Dai

#include "pch.h"

#include "serpent.h"
#include "secblock.h"
#include "misc.h"

#include "serpentp.h"

NAMESPACE_BEGIN(CryptoPP)

void Serpent_KeySchedule(word32 *k, unsigned int rounds, const byte *userKey, size_t keylen)
{
	FixedSizeSecBlock<word32, 8> k0;
	GetUserKey(LITTLE_ENDIAN_ORDER, k0.begin(), 8, userKey, keylen);
	if (keylen < 32)
		k0[keylen/4] |= word32(1) << ((keylen%4)*8);

	word32 t = k0[7];
	unsigned int i;
	for (i = 0; i < 8; ++i)
		k[i] = k0[i] = t = rotlConstant<11>(k0[i] ^ k0[(i + 3) % 8] ^ k0[(i + 5) % 8] ^ t ^ 0x9e3779b9 ^ i);
	for (i = 8; i < 4*(rounds+1); ++i)
		k[i] = t = rotlConstant<11>(k[i-8] ^ k[i-5] ^ k[i-3] ^ t ^ 0x9e3779b9 ^ i);
	k -= 20;

	word32 a,b,c,d,e;
	for (i=0; i<rounds/8; i++)
	{
		afterS2(LK); afterS2(S3); afterS3(SK);
		afterS1(LK); afterS1(S2); afterS2(SK);
		afterS0(LK); afterS0(S1); afterS1(SK);
		beforeS0(LK); beforeS0(S0); afterS0(SK);
		k += 8*4;
		afterS6(LK); afterS6(S7); afterS7(SK);
		afterS5(LK); afterS5(S6); afterS6(SK);
		afterS4(LK); afterS4(S5); afterS5(SK);
		afterS3(LK); afterS3(S4); afterS4(SK);
	}
	afterS2(LK); afterS2(S3); afterS3(SK);
}

void Serpent::Base::UncheckedSetKey(const byte *userKey, unsigned int keylen, const NameValuePairs &)
{
	AssertValidKeyLength(keylen);
	Serpent_KeySchedule(m_key, 32, userKey, keylen);
}

typedef BlockGetAndPut<word32, LittleEndian> Block;

void Serpent::Enc::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
{
	word32 a, b, c, d, e;

	Block::Get(inBlock)(a)(b)(c)(d);

	const word32 *k = m_key;
	unsigned int i=1;

	do
	{
		beforeS0(KX); beforeS0(S0); afterS0(LT);
		afterS0(KX); afterS0(S1); afterS1(LT);
		afterS1(KX); afterS1(S2); afterS2(LT);
		afterS2(KX); afterS2(S3); afterS3(LT);
		afterS3(KX); afterS3(S4); afterS4(LT);
		afterS4(KX); afterS4(S5); afterS5(LT);
		afterS5(KX); afterS5(S6); afterS6(LT);
		afterS6(KX); afterS6(S7);

		if (i == 4)
			break;

		++i;
		c = b;
		b = e;
		e = d;
		d = a;
		a = e;
		k += 32;
		beforeS0(LT);
	}
	while (true);

	afterS7(KX);

	Block::Put(xorBlock, outBlock)(d)(e)(b)(a);
}

void Serpent::Dec::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
{
	word32 a, b, c, d, e=0;

	Block::Get(inBlock)(a)(b)(c)(d);

	const word32 *k = m_key + 96;
	unsigned int i=4;

	beforeI7(KX);
	goto start;

	do
	{
		c = b;
		b = d;
		d = e;
		k -= 32;
		beforeI7(ILT);
start:
		            beforeI7(I7); afterI7(KX);
		afterI7(ILT); afterI7(I6); afterI6(KX);
		afterI6(ILT); afterI6(I5); afterI5(KX);
		afterI5(ILT); afterI5(I4); afterI4(KX);
		afterI4(ILT); afterI4(I3); afterI3(KX);
		afterI3(ILT); afterI3(I2); afterI2(KX);
		afterI2(ILT); afterI2(I1); afterI1(KX);
		afterI1(ILT); afterI1(I0); afterI0(KX);
	}
	while (--i != 0);

	Block::Put(xorBlock, outBlock)(a)(d)(b)(e);
}

NAMESPACE_END
