Skip to content

HC-256 Inconsistent with Other Libraries #1280

Open
@Demonslay335

Description

@Demonslay335

While developing support for an external app that uses the HC-256 cipher, I noticed inconsistencies with the CryptoPP::HC256 algorithm's output. Further digging showed that when either the key or IV contain non-repeating bytes (e.g. not just all 00 or 36...), the output does not match that of other libraries, or the original reference library.

I have tested against BouncyCastle and the original hc256.c ECRYPT source (which I had to use instead of CryptoPP for this project).

Compiled CryptoPP 8.9.0 using Visual Studio (x64) on Windows 10.

I have found the problem seems to be with the endian flip happening with the key and IV at these two locations. Such an endian flip is not present in BouncyCastle or hc256.c.

cryptopp/hc256.cpp

Lines 107 to 111 in 60f81a7

for (unsigned int i = 0; i < 32; i++)
{
m_key[i >> 2] = m_key[i >> 2] | userKey[i];
m_key[i >> 2] = rotlConstant<8>(m_key[i >> 2]);
}

cryptopp/hc256.cpp

Lines 135 to 139 in 60f81a7

for (unsigned int i = 0; i < 32; i++)
{
m_iv[i >> 2] = m_iv[i >> 2] | iv[i];
m_iv[i >> 2] = rotlConstant<8>(m_iv[i >> 2]);
}

If I replace the loop bodies with these respective changes, the unit output matches that of BouncyCastle and hc256.c.

m_key[i >> 2] |= ((word32)userKey[i] << (8 * (i & 0x3)));

...

m_iv[i >> 2] |= ((word32)iv[i] << (8 * (i & 0x3)));

Here is a test program that uses a BouncyCastle test vector to show the mismatch. The code throws an assertion with CryptoPP 8.9.0, and passes with the above changes.

#include <cryptopp/hc256.h>

int main()
{
  auto key = std::vector<uint8_t>{
      0x00, 0x53, 0xA6, 0xF9, 0x4C, 0x9F, 0xF2, 0x45, 0x98, 0xEB, 0x3E, 0x91, 0xE4, 0x37, 0x8A, 0xDD,
      0x30, 0x83, 0xD6, 0x29, 0x7C, 0xCF, 0x22, 0x75, 0xC8, 0x1B, 0x6E, 0xC1, 0x14, 0x67, 0xBA, 0x0D
  };
  auto iv = std::vector<uint8_t>{
      0x0D, 0x74, 0xDB, 0x42, 0xA9, 0x10, 0x77, 0xDE, 0x45, 0xAC, 0x13, 0x7A, 0xE1, 0x48, 0xAF, 0x16,
      0x7D, 0xE4, 0x4B, 0xB2, 0x19, 0x80, 0xE7, 0x4E, 0xB5, 0x1C, 0x83, 0xEA, 0x51, 0xB8, 0x1F, 0x86
  };
  auto buffer = std::vector<uint8_t>(0x40, 0x00);
  
  // BouncyCastle Set 6, vector# 0
  // https://github.com/bcgit/bc-java/blob/0520ebcd3291d6d3176ea25c571f1c7e0a963847/core/src/test/java/org/bouncycastle/crypto/test/HCFamilyTest.java#L104-L112
  // https://github.com/bcgit/bc-csharp/blob/f5432325fbeadade37ec2542a44b3702395c7650/crypto/test/data/hc256/hc256/ecrypt_HC-256_256K_256IV.txt#L3151-L3159
  auto expected = std::vector<uint8_t>{
      0x23, 0xD9, 0xE7, 0x0A, 0x45, 0xEB, 0x01, 0x27, 0x88, 0x4D, 0x66, 0xD9, 0xF6, 0xF2, 0x3C, 0x01,
      0xD1, 0xF8, 0x8A, 0xFD, 0x62, 0x92, 0x70, 0x12, 0x72, 0x47, 0x25, 0x6C, 0x1F, 0xFF, 0x91, 0xE9,
      0x1A, 0x79, 0x7B, 0xD9, 0x8A, 0xDD, 0x23, 0xAE, 0x15, 0xBE, 0xE6, 0xEE, 0xA3, 0xCE, 0xFD, 0xBF,
      0xA3, 0xED, 0x6D, 0x22, 0xD9, 0xC4, 0xF4, 0x59, 0xDB, 0x10, 0xC4, 0x0C, 0xDF, 0x4F, 0x4D, 0xFF
  };
  
  CryptoPP::HC256::Encryption hc256;
  hc256.SetKeyWithIV(key.data(), key.size(), iv.data(), iv.size());
  
  hc256.ProcessData(buffer.data(), buffer.data(), buffer.size());
  CRYPTOPP_ASSERT(CRYPTOPP_VERSION >= 890);
  CRYPTOPP_ASSERT(memcmp(buffer.data(), expected.data(), buffer.size()) == 0);
}

Furthermore, the sample on the wiki is not decryptable with BouncyCastle.

Key: CBCBA550FE1A5C34E0C4D6A14D056A08032EA11CF700BD7B9D409B68DC47B9B7
IV: 2E58581CFD129ACE30BD1F86739C713A867550A3DD6D9E0886F63C01B6F9531F
Plain: HC-256 stream cipher test

CryptoPP Cipher: 957BD81955B68A34A4C151D73F89E837945C364CE5B2942CC2
BouncyCastle Cipher: 3531AD8ADC1DEFED1C9D3B705D8B9E87063D1D05E40C19ADB2

The CryptoPP::HC128 algorithm appears fine; it also does not do endian flipping of the key and IV, instead explicitly using GetUserKey(LITTLE_ENDIAN_ORDER ...).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions