Skip to content

Commit

Permalink
Port Kalyna to big-endian (Issue 411)
Browse files Browse the repository at this point in the history
Tested on GCC110 from the GCC compile farm. Performance is comparable to AES on GCC110.
  • Loading branch information
noloader committed May 8, 2017
1 parent c35e616 commit 1d7dfc6
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 51 deletions.
209 changes: 161 additions & 48 deletions kalyna.cpp
Expand Up @@ -4,9 +4,10 @@
// Standard of Ukraine: The Kalyna Block Cipher" (http://eprint.iacr.org/2015/650.pdf). Second
// was Roman Oliynykov and Oleksandr Kazymyrov's GitHub with the reference implementation
// (http://github.com/Roman-Oliynykov/Kalyna-reference). The third and most utilized resource
// was Keru Kuro's implementation of Kalyna in CppCrypto (http://sourceforge.net/projects/cppcrypto/).
// Kuro has an outstanding implementation that performed better than the reference implementation
// and out intial attempts.
// was Keru Kuro's public domain implementation of Kalyna in CppCrypto
// (http://sourceforge.net/projects/cppcrypto/). Kuro has an outstanding implementation that
// performed better than the reference implementation and out intial attempts. The only downside
// was the missing big endian port.

#include "pch.h"
#include "config.h"
Expand All @@ -20,7 +21,7 @@ NAMESPACE_BEGIN(CryptoPP)
NAMESPACE_BEGIN(KalynaTab)

// T can be shared between Kupyna and Kalyna; IT, S and IS are Kalyna specific
extern const word64 T[8][256]; // Substitution
extern const word64 T[8][256]; // Columns
extern const word64 IT[8][256]; // Inverse
extern const byte S[4][256]; // Substitution
extern const byte IS[4][256]; // Inverse
Expand All @@ -31,10 +32,54 @@ NAMESPACE_END
ANONYMOUS_NAMESPACE_BEGIN

using CryptoPP::word64;
using CryptoPP::KalynaTab::S;
using CryptoPP::KalynaTab::T;
using CryptoPP::KalynaTab::IS;
using CryptoPP::KalynaTab::S;
using CryptoPP::KalynaTab::IT;
using CryptoPP::KalynaTab::IS;

template <unsigned int NB>
inline void MakeOddKey(const word64 evenkey[NB], word64 oddkey[NB])
{
#if defined(IS_BIG_ENDIAN)
if (NB == 2)
{
oddkey[0] = (evenkey[1] << 8) | (evenkey[0] >> 56);
oddkey[1] = (evenkey[0] << 8) | (evenkey[1] >> 56);
}
else if (NB == 4)
{
oddkey[0] = (evenkey[2] << 40) | (evenkey[1] >> 24);
oddkey[1] = (evenkey[3] << 40) | (evenkey[2] >> 24);
oddkey[2] = (evenkey[0] << 40) | (evenkey[3] >> 24);
oddkey[3] = (evenkey[1] << 40) | (evenkey[0] >> 24);
}
else if (NB == 8)
{
oddkey[0] = (evenkey[3] << 40) | (evenkey[2] >> 24);
oddkey[1] = (evenkey[4] << 40) | (evenkey[3] >> 24);
oddkey[2] = (evenkey[5] << 40) | (evenkey[4] >> 24);
oddkey[3] = (evenkey[6] << 40) | (evenkey[5] >> 24);

oddkey[4] = (evenkey[7] << 40) | (evenkey[6] >> 24);
oddkey[5] = (evenkey[0] << 40) | (evenkey[7] >> 24);
oddkey[6] = (evenkey[1] << 40) | (evenkey[0] >> 24);
oddkey[7] = (evenkey[2] << 40) | (evenkey[1] >> 24);
}
else
{
CRYPTOPP_ASSERT(0);
}
#else
static const unsigned int S = (NB == 2) ? 16 : (NB == 4) ? 32 : (NB == 8) ? 64 : -1;
static const unsigned int T = (NB == 2) ? 7 : (NB == 4) ? 11 : (NB == 8) ? 19 : -1;

const byte* even = reinterpret_cast<const byte*>(evenkey);
byte* odd = reinterpret_cast<byte*>(oddkey);

memcpy(odd, even + T, S - T);
memcpy(odd + S - T, even, T);
#endif
}

template <unsigned int NB>
inline void SwapBlocks(word64 k[NB])
Expand Down Expand Up @@ -362,19 +407,6 @@ inline void G512(const word64 x[8], word64 y[8], const word64 k[8])
T[4][(byte)(x[3] >> 32)] ^ T[5][(byte)(x[2] >> 40)] ^ T[6][(byte)(x[1] >> 48)] ^ T[7][(byte)(x[0] >> 56)];
}

template <unsigned int NB>
inline void MakeOddKey(const word64 evenkey[NB], word64 oddkey[NB])
{
static const unsigned int S = (NB == 2) ? 16 : (NB == 4) ? 32 : (NB == 8) ? 64 : -1;
static const unsigned int T = (NB == 2) ? 7 : (NB == 4) ? 11 : (NB == 8) ? 19 : -1;

const byte* even = reinterpret_cast<const byte*>(evenkey);
byte* odd = reinterpret_cast<byte*>(oddkey);

memcpy(odd, even + T, S - T);
memcpy(odd + S - T, even, T);
}

ANONYMOUS_NAMESPACE_END

NAMESPACE_BEGIN(CryptoPP)
Expand Down Expand Up @@ -562,7 +594,7 @@ void Kalyna::Base::SetKey_44(const word64 key[4])
word64 *ks = m_wspace+0, *ksc = m_wspace+4, *t1 = m_wspace+8;
word64 *t2 = m_wspace+12, *k = m_wspace+16;

memset(t1, 0, 4*8);
memset(t1, 0, 32);
t1[0] = (256 + 256 + 64) / 64;

AddKey<4>(t1, t2, key);
Expand Down Expand Up @@ -904,7 +936,18 @@ void Kalyna::Base::SetKey_88(const word64 key[8])

void Kalyna::Base::ProcessBlock_22(const word64 inBlock[2], word64 outBlock[2]) const
{
word64 *t1 = m_wspace+0, *t2 = m_wspace+2;
#if defined(IS_BIG_ENDIAN)
word64 *t1 = m_wspace+0, *t2 = m_wspace+2, *msg = m_wspace+4;
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(inBlock);
block(msg[0])(msg[1]);
inBlock = msg;
}
#else
word64 *t1 = outBlock, *t2 = m_wspace+2;
#endif

if (IsForwardTransformation())
{
AddKey<2>(inBlock, t1, m_rkeys);
Expand Down Expand Up @@ -935,12 +978,29 @@ void Kalyna::Base::ProcessBlock_22(const word64 inBlock[2], word64 outBlock[2])
IGL128(t2, t1, &m_rkeys[0]);
}

memcpy(outBlock, t1, 16);
#if defined(IS_BIG_ENDIAN)
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(t1);
block(outBlock[0])(outBlock[1]);
}
#endif
}

void Kalyna::Base::ProcessBlock_24(const word64 inBlock[2], word64 outBlock[2]) const
{
word64 *t1 = m_wspace+0, *t2 = m_wspace+2;
#if defined(IS_BIG_ENDIAN)
word64 *t1 = m_wspace+0, *t2 = m_wspace+2, *msg = m_wspace+4;
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(inBlock);
block(msg[0])(msg[1]);
inBlock = msg;
}
#else
word64 *t1 = outBlock, *t2 = m_wspace+2;
#endif

if (IsForwardTransformation())
{
AddKey<2>(inBlock, t1, m_rkeys);
Expand Down Expand Up @@ -979,12 +1039,29 @@ void Kalyna::Base::ProcessBlock_24(const word64 inBlock[2], word64 outBlock[2])
IGL128(t2, t1, &m_rkeys[0]);
}

memcpy(outBlock, t1, 16);
#if defined(IS_BIG_ENDIAN)
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(t1);
block(outBlock[0])(outBlock[1]);
}
#endif
}

void Kalyna::Base::ProcessBlock_44(const word64 inBlock[4], word64 outBlock[4]) const
{
word64 *t1 = m_wspace+0, *t2 = m_wspace+4;
#if defined(IS_BIG_ENDIAN)
word64 *t1 = m_wspace+0, *t2 = m_wspace+4, *msg = m_wspace+8;
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(inBlock);
block(msg[0])(msg[1])(msg[2])(msg[3]);
inBlock = msg;
}
#else
word64 *t1 = outBlock, *t2 = m_wspace+4;
#endif

if (IsForwardTransformation())
{
AddKey<4>(inBlock, t1, m_rkeys);
Expand Down Expand Up @@ -1023,13 +1100,27 @@ void Kalyna::Base::ProcessBlock_44(const word64 inBlock[4], word64 outBlock[4])
IGL256(t2, t1, &m_rkeys[0]);
}

memcpy(outBlock, t1, 32);
#if defined(IS_BIG_ENDIAN)
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(t1); block(outBlock[0])(outBlock[1])(outBlock[2])(outBlock[3]);
}
#endif
}

void Kalyna::Base::ProcessBlock_48(const word64 inBlock[4], word64 outBlock[4]) const
{
// word64 t1[4], t2[4];
word64 *t1 = m_wspace+0, *t2 = m_wspace+4;
#if defined(IS_BIG_ENDIAN)
word64 *t1 = m_wspace+0, *t2 = m_wspace+4, *msg = m_wspace+8;
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(inBlock);
block(msg[0])(msg[1])(msg[2])(msg[3]);
inBlock = msg;
}
#else
word64 *t1 = outBlock, *t2 = m_wspace+4;
#endif

if (IsForwardTransformation())
{
Expand Down Expand Up @@ -1077,13 +1168,28 @@ void Kalyna::Base::ProcessBlock_48(const word64 inBlock[4], word64 outBlock[4])
IGL256(t2, t1, &m_rkeys[0]);
}

memcpy(outBlock, t1, 32);
#if defined(IS_BIG_ENDIAN)
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(t1);
block(outBlock[0])(outBlock[1])(outBlock[2])(outBlock[3]);
}
#endif
}

void Kalyna::Base::ProcessBlock_88(const word64 inBlock[8], word64 outBlock[8]) const
{
// word64 t1[8], t2[8];
word64 *t1 = m_wspace+0, *t2 = m_wspace+8;
#if defined(IS_BIG_ENDIAN)
word64 *t1 = m_wspace+0, *t2 = m_wspace+8, *msg = m_wspace+16;
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(inBlock);
block(msg[0])(msg[1])(msg[2])(msg[3])(msg[4])(msg[5])(msg[6])(msg[7]);
inBlock = msg;
}
#else
word64 *t1 = outBlock, *t2 = m_wspace+8;
#endif

if (IsForwardTransformation())
{
Expand Down Expand Up @@ -1131,15 +1237,22 @@ void Kalyna::Base::ProcessBlock_88(const word64 inBlock[8], word64 outBlock[8])
IGL512(t2, t1, &m_rkeys[0]);
}

memcpy(outBlock, t1, 64);
#if defined(IS_BIG_ENDIAN)
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block block(t1);
block(outBlock[0])(outBlock[1])(outBlock[2])(outBlock[3])
(outBlock[4])(outBlock[5])(outBlock[6])(outBlock[7]);
}
#endif
}

// *********************** Library routines *********************** //

void Kalyna::Base::UncheckedSetKey(const byte *key, unsigned int keylen, const NameValuePairs &params)
{
typedef GetBlock<word64, LittleEndian, false> Block;
Block userkey(key);
Block block(key);

switch (keylen)
{
Expand Down Expand Up @@ -1169,41 +1282,41 @@ void Kalyna::Base::UncheckedSetKey(const byte *key, unsigned int keylen, const N
m_rkeys.New(11*2);
m_wspace.New(2*6);

userkey(m_mkey[0])(m_mkey[1]);
block(m_mkey[0])(m_mkey[1]);
SetKey_22(m_mkey.data());
break;
case (2 << 8) | 4: // 256 key, 128 block
m_mkey.New(4);
m_rkeys.New(15*2);
m_wspace.New(6*2+4);

userkey(m_mkey[0])(m_mkey[1])(m_mkey[2])(m_mkey[3]);
block(m_mkey[0])(m_mkey[1])(m_mkey[2])(m_mkey[3]);
SetKey_24(m_mkey.data());
break;
case (4 << 8) | 4: // 256 key, 256 block
m_mkey.New(4);
m_rkeys.New(15*4);
m_wspace.New(5*4);

userkey(m_mkey[0])(m_mkey[1])(m_mkey[2])(m_mkey[3]);
block(m_mkey[0])(m_mkey[1])(m_mkey[2])(m_mkey[3]);
SetKey_44(m_mkey.data());
break;
case (4 << 8) | 8: // 512 key, 256 block
m_mkey.New(8);
m_rkeys.New(19*4);
m_wspace.New(6*4+8);

userkey(m_mkey[0])(m_mkey[1])(m_mkey[2])(m_mkey[3])
(m_mkey[4])(m_mkey[5])(m_mkey[6])(m_mkey[7]);
block(m_mkey[0])(m_mkey[1])(m_mkey[2])(m_mkey[3])
(m_mkey[4])(m_mkey[5])(m_mkey[6])(m_mkey[7]);
SetKey_48(m_mkey.data());
break;
case (8 << 8) | 8: // 512 key, 512 block
m_mkey.New(8);
m_rkeys.New(19*8);
m_wspace.New(5*8);

userkey(m_mkey[0])(m_mkey[1])(m_mkey[2])(m_mkey[3])
(m_mkey[4])(m_mkey[5])(m_mkey[6])(m_mkey[7]);
block(m_mkey[0])(m_mkey[1])(m_mkey[2])(m_mkey[3])
(m_mkey[4])(m_mkey[5])(m_mkey[6])(m_mkey[7]);
SetKey_88(m_mkey.data());
break;
default:
Expand All @@ -1213,14 +1326,14 @@ void Kalyna::Base::UncheckedSetKey(const byte *key, unsigned int keylen, const N

void Kalyna::Base::ProcessAndXorBlock(const byte *inBlock, const byte *xorBlock, byte *outBlock) const
{
// Timing attack countermeasure. see comments in Rijndael for more details
const int cacheLineSize = GetCacheLineSize();
volatile word32 _u = 0;
word32 u = _u;

for (unsigned int i=0; i<COUNTOF(KalynaTab::S); i+=cacheLineSize)
u &= *reinterpret_cast<const word32*>(KalynaTab::S+i);
m_wspace[0] = u;
// Timing attack countermeasure. see comments in Rijndael for more details
const int cacheLineSize = GetCacheLineSize();
volatile word32 _u = 0;
word32 u = _u;

for (unsigned int i=0; i<256; i+=cacheLineSize)
u &= *reinterpret_cast<const word32*>(KalynaTab::S+i);
m_wspace[0] = u;

switch ((m_nb << 8) | m_nk)
{
Expand Down
6 changes: 3 additions & 3 deletions kalyna.h
Expand Up @@ -7,7 +7,7 @@
//! Ruzhentsev, Kuznetsov, Gorbenko, Dyrda, Dolgov, Pushkaryov, Mordvinov and Kaidalov's "A New Encryption
//! Standard of Ukraine: The Kalyna Block Cipher" (http://eprint.iacr.org/2015/650.pdf). Second was Roman
//! Oliynykov and Oleksandr Kazymyrov's GitHub with the reference implementation
//! (http://github.com/Roman-Oliynykov/Kalyna-reference). The third resource was Keru Kuro's implementation
//! (http://github.com/Roman-Oliynykov/Kalyna-reference). The third resource was Keru Kuro's implementation
//! of Kalyna in CppCrypto (http://sourceforge.net/projects/cppcrypto/). Kuro has an outstanding
//! implementation that performed better than the reference implementation and out intial attempts.

Expand Down Expand Up @@ -107,8 +107,8 @@ class Kalyna : public Kalyna_Info, public BlockCipherDocumentation
//! and parenthesis to identify key length. For example, Kalyna-128(256) is Kalyna
//! with a 128-bit block size and a 256-bit key length. If a mode is associated
//! with the object, then it follows as expected. For example, Kalyna-128(256)/ECB.
//! DSTU is a little more complex with more parameters, dashes, underscores, but the
//! library does not use the delimiters or full convention.
//! DSTU is a little more complex with more parameters, dashes, underscores, but the
//! library does not use the delimiters or full convention.
std::string AlgorithmName() const {
return m_blocksize ? "Kalyna-" + IntToString(m_blocksize*8) + "(" + IntToString(m_kl*8) + ")" : StaticAlgorithmName();
}
Expand Down

1 comment on commit 1d7dfc6

@noloader
Copy link
Collaborator Author

@noloader noloader commented on 1d7dfc6 May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks to Roman Oliynykov and Oleksandr Kazymyrov for helping us with Kalyna and DSTU 7624:2014.

Also see Issue 411.

Please sign in to comment.