// 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" /*h1 function*/ #define h1(x, y) { \ byte a,c; \ a = (byte) (x); \ c = (byte) ((x) >> 16); \ y = (m_T[512+a])+(m_T[512+256+c]); \ } /*h2 function*/ #define h2(x, y) { \ byte a,c; \ a = (byte) (x); \ c = (byte) ((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.begin(), 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) { while (iterationCount--) { 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 accumulated keystream // then it will ask OperateKeystream to generate one. Optionally it // will ask for an XOR of the input with the keystream while // writing the result to the output buffer. In all cases the // keystream is written to the output buffer. The optional part is // adding the input buffer and keystream. if ((operation & EnumToInt(INPUT_NULL)) != EnumToInt(INPUT_NULL)) { xorbuf(output, input, BYTES_PER_ITERATION); input += BYTES_PER_ITERATION; } output += BYTES_PER_ITERATION; } } void HC128Policy::CipherResynchronize(byte *keystreamBuffer, const byte *iv, size_t length) { CRYPTOPP_UNUSED(keystreamBuffer); GetUserKey(LITTLE_ENDIAN_ORDER, m_iv.begin(), 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