From dd31eb80c001d4571f89eb2be52dbdc022c60ae9 Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Thu, 5 Jul 2018 00:29:07 -0400 Subject: Add HC-128 stream cipher (GH #679) --- hc128.cpp | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 hc128.cpp (limited to 'hc128.cpp') diff --git a/hc128.cpp b/hc128.cpp new file mode 100644 index 00000000..4d8759a6 --- /dev/null +++ b/hc128.cpp @@ -0,0 +1,277 @@ +// hc128.cpp - written and placed in the public domain by Jeffrey Walton +// based on public domain code by Hongjun Wu. +// +// The reference materials and source files are available at +// The eSTREAM Project, http://www.ecrypt.eu.org/stream/e2-hc128.html. + +#include "pch.h" +#include "config.h" + +#include "hc128.h" +#include "secblock.h" +#include "misc.h" + +#define u8 byte +#define u32 word32 + +/*h1 function*/ +#define h1(x, y) { \ + u8 a,c; \ + a = (u8) (x); \ + c = (u8) ((x) >> 16); \ + y = (m_T[512+a])+(m_T[512+256+c]); \ +} + +/*h2 function*/ +#define h2(x, y) { \ + u8 a,c; \ + a = (u8) (x); \ + c = (u8) ((x) >> 16); \ + y = (m_T[a])+(m_T[256+c]); \ +} + +/*one step of HC-128, update P and generate 32 bits keystream*/ +#define step_P(u,v,a,b,c,d,n){ \ + word32 tem0,tem1,tem2,tem3; \ + h1(m_X[(d)],tem3); \ + tem0 = rotrConstant<23>(m_T[(v)]); \ + tem1 = rotrConstant<10>(m_X[(c)]); \ + tem2 = rotrConstant<8>(m_X[(b)]); \ + (m_T[(u)]) += tem2+(tem0 ^ tem1); \ + (m_X[(a)]) = (m_T[(u)]); \ + (n) = tem3 ^ (m_T[(u)]); \ +} + +/*one step of HC-128, update Q and generate 32 bits keystream*/ +#define step_Q(u,v,a,b,c,d,n){ \ + word32 tem0,tem1,tem2,tem3; \ + h2(m_Y[(d)],tem3); \ + tem0 = rotrConstant<(32-23)>(m_T[(v)]); \ + tem1 = rotrConstant<(32-10)>(m_Y[(c)]); \ + tem2 = rotrConstant<(32-8)>(m_Y[(b)]); \ + (m_T[(u)]) += tem2 + (tem0 ^ tem1); \ + (m_Y[(a)]) = (m_T[(u)]); \ + (n) = tem3 ^ (m_T[(u)]) ; \ +} + +/*update table P*/ +#define update_P(u,v,a,b,c,d){ \ + word32 tem0,tem1,tem2,tem3; \ + tem0 = rotrConstant<23>(m_T[(v)]); \ + tem1 = rotrConstant<10>(m_X[(c)]); \ + tem2 = rotrConstant<8>(m_X[(b)]); \ + h1(m_X[(d)],tem3); \ + (m_T[(u)]) = ((m_T[(u)]) + tem2+(tem0^tem1)) ^ tem3; \ + (m_X[(a)]) = (m_T[(u)]); \ +} + +/*update table Q*/ +#define update_Q(u,v,a,b,c,d){ \ + word32 tem0,tem1,tem2,tem3; \ + tem0 = rotrConstant<(32-23)>(m_T[(v)]); \ + tem1 = rotrConstant<(32-10)>(m_Y[(c)]); \ + tem2 = rotrConstant<(32-8)>(m_Y[(b)]); \ + h2(m_Y[(d)],tem3); \ + (m_T[(u)]) = ((m_T[(u)]) + tem2+(tem0^tem1)) ^ tem3; \ + (m_Y[(a)]) = (m_T[(u)]); \ +} + +ANONYMOUS_NAMESPACE_BEGIN + +using CryptoPP::word32; +using CryptoPP::rotrConstant; + +inline word32 f1(word32 x) +{ + return rotrConstant<7>(x) ^ rotrConstant<18>(x) ^ ((x) >> 3); +} + +inline word32 f2(word32 x) +{ + return rotrConstant<17>(x) ^ rotrConstant<19>(x) ^ ((x) >> 10); +} + +ANONYMOUS_NAMESPACE_END + +NAMESPACE_BEGIN(CryptoPP) + +/*16 steps of HC-128, generate 512 bits keystream*/ +void HC128Policy::GenerateKeystream(word32 keystream[16]) +{ + unsigned int cc = m_ctr & 0x1ff; + unsigned int dd = (cc + 16) & 0x1ff; + + if (m_ctr < 512) + { + m_ctr = (m_ctr + 16) & 0x3ff; + step_P(cc + 0, cc + 1, 0, 6, 13, 4, keystream[0]); + step_P(cc + 1, cc + 2, 1, 7, 14, 5, keystream[1]); + step_P(cc + 2, cc + 3, 2, 8, 15, 6, keystream[2]); + step_P(cc + 3, cc + 4, 3, 9, 0, 7, keystream[3]); + step_P(cc + 4, cc + 5, 4, 10, 1, 8, keystream[4]); + step_P(cc + 5, cc + 6, 5, 11, 2, 9, keystream[5]); + step_P(cc + 6, cc + 7, 6, 12, 3, 10, keystream[6]); + step_P(cc + 7, cc + 8, 7, 13, 4, 11, keystream[7]); + step_P(cc + 8, cc + 9, 8, 14, 5, 12, keystream[8]); + step_P(cc + 9, cc + 10, 9, 15, 6, 13, keystream[9]); + step_P(cc + 10, cc + 11, 10, 0, 7, 14, keystream[10]); + step_P(cc + 11, cc + 12, 11, 1, 8, 15, keystream[11]); + step_P(cc + 12, cc + 13, 12, 2, 9, 0, keystream[12]); + step_P(cc + 13, cc + 14, 13, 3, 10, 1, keystream[13]); + step_P(cc + 14, cc + 15, 14, 4, 11, 2, keystream[14]); + step_P(cc + 15, dd + 0, 15, 5, 12, 3, keystream[15]); + } + else + { + m_ctr = (m_ctr + 16) & 0x3ff; + step_Q(512 + cc + 0, 512 + cc + 1, 0, 6, 13, 4, keystream[0]); + step_Q(512 + cc + 1, 512 + cc + 2, 1, 7, 14, 5, keystream[1]); + step_Q(512 + cc + 2, 512 + cc + 3, 2, 8, 15, 6, keystream[2]); + step_Q(512 + cc + 3, 512 + cc + 4, 3, 9, 0, 7, keystream[3]); + step_Q(512 + cc + 4, 512 + cc + 5, 4, 10, 1, 8, keystream[4]); + step_Q(512 + cc + 5, 512 + cc + 6, 5, 11, 2, 9, keystream[5]); + step_Q(512 + cc + 6, 512 + cc + 7, 6, 12, 3, 10, keystream[6]); + step_Q(512 + cc + 7, 512 + cc + 8, 7, 13, 4, 11, keystream[7]); + step_Q(512 + cc + 8, 512 + cc + 9, 8, 14, 5, 12, keystream[8]); + step_Q(512 + cc + 9, 512 + cc + 10, 9, 15, 6, 13, keystream[9]); + step_Q(512 + cc + 10, 512 + cc + 11, 10, 0, 7, 14, keystream[10]); + step_Q(512 + cc + 11, 512 + cc + 12, 11, 1, 8, 15, keystream[11]); + step_Q(512 + cc + 12, 512 + cc + 13, 12, 2, 9, 0, keystream[12]); + step_Q(512 + cc + 13, 512 + cc + 14, 13, 3, 10, 1, keystream[13]); + step_Q(512 + cc + 14, 512 + cc + 15, 14, 4, 11, 2, keystream[14]); + step_Q(512 + cc + 15, 512 + dd + 0, 15, 5, 12, 3, keystream[15]); + } +} + +/*16 steps of HC-128, without generating keystream, */ +/*but use the outputs to update P and Q*/ +void HC128Policy::SetupUpdate() /*each time 16 steps*/ +{ + unsigned int cc = m_ctr & 0x1ff; + unsigned int dd = (cc + 16) & 0x1ff; + + if (m_ctr < 512) + { + m_ctr = (m_ctr + 16) & 0x3ff; + update_P(cc + 0, cc + 1, 0, 6, 13, 4); + update_P(cc + 1, cc + 2, 1, 7, 14, 5); + update_P(cc + 2, cc + 3, 2, 8, 15, 6); + update_P(cc + 3, cc + 4, 3, 9, 0, 7); + update_P(cc + 4, cc + 5, 4, 10, 1, 8); + update_P(cc + 5, cc + 6, 5, 11, 2, 9); + update_P(cc + 6, cc + 7, 6, 12, 3, 10); + update_P(cc + 7, cc + 8, 7, 13, 4, 11); + update_P(cc + 8, cc + 9, 8, 14, 5, 12); + update_P(cc + 9, cc + 10, 9, 15, 6, 13); + update_P(cc + 10, cc + 11, 10, 0, 7, 14); + update_P(cc + 11, cc + 12, 11, 1, 8, 15); + update_P(cc + 12, cc + 13, 12, 2, 9, 0); + update_P(cc + 13, cc + 14, 13, 3, 10, 1); + update_P(cc + 14, cc + 15, 14, 4, 11, 2); + update_P(cc + 15, dd + 0, 15, 5, 12, 3); + } + else + { + m_ctr = (m_ctr + 16) & 0x3ff; + update_Q(512 + cc + 0, 512 + cc + 1, 0, 6, 13, 4); + update_Q(512 + cc + 1, 512 + cc + 2, 1, 7, 14, 5); + update_Q(512 + cc + 2, 512 + cc + 3, 2, 8, 15, 6); + update_Q(512 + cc + 3, 512 + cc + 4, 3, 9, 0, 7); + update_Q(512 + cc + 4, 512 + cc + 5, 4, 10, 1, 8); + update_Q(512 + cc + 5, 512 + cc + 6, 5, 11, 2, 9); + update_Q(512 + cc + 6, 512 + cc + 7, 6, 12, 3, 10); + update_Q(512 + cc + 7, 512 + cc + 8, 7, 13, 4, 11); + update_Q(512 + cc + 8, 512 + cc + 9, 8, 14, 5, 12); + update_Q(512 + cc + 9, 512 + cc + 10, 9, 15, 6, 13); + update_Q(512 + cc + 10, 512 + cc + 11, 10, 0, 7, 14); + update_Q(512 + cc + 11, 512 + cc + 12, 11, 1, 8, 15); + update_Q(512 + cc + 12, 512 + cc + 13, 12, 2, 9, 0); + update_Q(512 + cc + 13, 512 + cc + 14, 13, 3, 10, 1); + update_Q(512 + cc + 14, 512 + cc + 15, 14, 4, 11, 2); + update_Q(512 + cc + 15, 512 + dd + 0, 15, 5, 12, 3); + } +} + +void HC128Policy::CipherSetKey(const NameValuePairs ¶ms, const byte *userKey, size_t keylen) +{ + CRYPTOPP_UNUSED(params); + + GetUserKey(LITTLE_ENDIAN_ORDER, m_key, 4, userKey, keylen); + for (unsigned int i = 4; i < 8; i++) + m_key[i] = m_key[i - 4]; +} + +void HC128Policy::OperateKeystream(KeystreamOperation operation, byte *output, const byte *input, size_t iterationCount) +{ + size_t msglen = GetBytesPerIteration() * iterationCount; + while (msglen >= 64) + { + word32 keystream[16]; + GenerateKeystream(keystream); + + PutWord(false, LITTLE_ENDIAN_ORDER, output + 0, keystream[0]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 4, keystream[1]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 8, keystream[2]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 12, keystream[3]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 16, keystream[4]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 20, keystream[5]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 24, keystream[6]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 28, keystream[7]); + + PutWord(false, LITTLE_ENDIAN_ORDER, output + 32, keystream[8]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 36, keystream[9]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 40, keystream[10]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 44, keystream[11]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 48, keystream[12]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 52, keystream[13]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 56, keystream[14]); + PutWord(false, LITTLE_ENDIAN_ORDER, output + 60, keystream[15]); + + // If AdditiveCipherTemplate does not have an acculated keystream + // then it will ask OperateKeystream to XOR the plaintext with + // the keystream and write it to the ciphertext buffer. + if ((operation & INPUT_NULL) != INPUT_NULL) + xorbuf(output, input, 64); + + msglen -= 64; input += 64; output += 64; + } +} + +void HC128Policy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length) +{ + CRYPTOPP_UNUSED(keystreamBuffer); + + GetUserKey(LITTLE_ENDIAN_ORDER, m_iv, 4, iv, length); + for (unsigned int i = 4; i < 8; i++) + m_iv[i] = m_iv[i - 4]; + + /* expand the key and IV into the table T */ + /* (expand the key and IV into the table P and Q) */ + + for (unsigned int i = 0; i < 8; i++) + m_T[i] = m_key[i]; + for (unsigned int i = 8; i < 16; i++) + m_T[i] = m_iv[i - 8]; + + for (unsigned int i = 16; i < (256 + 16); i++) + m_T[i] = f2(m_T[i - 2]) + m_T[i - 7] + f1(m_T[i - 15]) + m_T[i - 16] + i; + + for (unsigned int i = 0; i < 16; i++) + m_T[i] = m_T[256 + i]; + + for (unsigned int i = 16; i < 1024; i++) + m_T[i] = f2(m_T[i - 2]) + m_T[i - 7] + f1(m_T[i - 15]) + m_T[i - 16] + 256 + i; + + /* initialize counter1024, X and Y */ + m_ctr = 0; + for (unsigned int i = 0; i < 16; i++) + m_X[i] = m_T[512 - 16 + i]; + for (unsigned int i = 0; i < 16; i++) + m_Y[i] = m_T[512 + 512 - 16 + i]; + + /* run the cipher 1024 steps before generating the output */ + for (unsigned int i = 0; i < 64; i++) + SetupUpdate(); +} + +NAMESPACE_END -- cgit v1.2.1