// Crypto.cpp
// Implements classes that wrap the cryptographic code library
#include "Globals.h"
#include "Crypto.h"
#include "polarssl/pk.h"
/*
// Self-test the hash formatting for known values:
// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48
// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1
// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6
class Test
{
public:
Test(void)
{
AString DigestNotch, DigestJeb, DigestSimon;
Byte Digest[20];
cSHA1Checksum Checksum;
Checksum.Update((const Byte *)"Notch", 5);
Checksum.Finalize(Digest);
cSHA1Checksum::DigestToJava(Digest, DigestNotch);
Checksum.Restart();
Checksum.Update((const Byte *)"jeb_", 4);
Checksum.Finalize(Digest);
cSHA1Checksum::DigestToJava(Digest, DigestJeb);
Checksum.Restart();
Checksum.Update((const Byte *)"simon", 5);
Checksum.Finalize(Digest);
cSHA1Checksum::DigestToJava(Digest, DigestSimon);
printf("Notch: \"%s\"\n", DigestNotch.c_str());
printf("jeb_: \"%s\"\n", DigestJeb.c_str());
printf("simon: \"%s\"\n", DigestSimon.c_str());
assert(DigestNotch == "4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48");
assert(DigestJeb == "-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1");
assert(DigestSimon == "88e16a1019277b15d58faf0541e11910eb756f6");
}
} test;
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cRSAPrivateKey:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cPublicKey:
cPublicKey::cPublicKey(const AString & a_PublicKeyDER)
{
pk_init(&m_Pk);
if (pk_parse_public_key(&m_Pk, (const Byte *)a_PublicKeyDER.data(), a_PublicKeyDER.size()) != 0)
{
ASSERT(!"Cannot parse PubKey");
return;
}
InitRnd();
}
cPublicKey::~cPublicKey()
{
pk_free(&m_Pk);
}
int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength)
{
size_t DecryptedLen = a_DecryptedMaxLength;
int res = pk_decrypt(&m_Pk,
a_EncryptedData, a_EncryptedLength,
a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength,
ctr_drbg_random, &m_Ctr_drbg
);
if (res != 0)
{
return res;
}
return (int)DecryptedLen;
}
int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength)
{
size_t EncryptedLength = a_EncryptedMaxLength;
int res = pk_encrypt(&m_Pk,
a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength,
ctr_drbg_random, &m_Ctr_drbg
);
if (res != 0)
{
return res;
}
return (int)EncryptedLength;
}
void cPublicKey::InitRnd(void)
{
entropy_init(&m_Entropy);
const unsigned char pers[] = "rsa_genkey";
ctr_drbg_init(&m_Ctr_drbg, entropy_func, &m_Entropy, pers, sizeof(pers) - 1);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cAESCFBDecryptor:
cAESCFBDecryptor::cAESCFBDecryptor(void) :
m_IVOffset(0),
m_IsValid(false)
{
}
cAESCFBDecryptor::~cAESCFBDecryptor()
{
// Clear the leftover in-memory data, so that they can't be accessed by a backdoor
memset(&m_Aes, 0, sizeof(m_Aes));
}
void cAESCFBDecryptor::Init(const Byte a_Key[16], const Byte a_IV[16])
{
ASSERT(!IsValid()); // Cannot Init twice
memcpy(m_IV, a_IV, 16);
aes_setkey_enc(&m_Aes, a_Key, 128);
m_IsValid = true;
}
void cAESCFBDecryptor::ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length)
{
ASSERT(IsValid()); // Must Init() first
// PolarSSL doesn't support AES-CFB8, need to implement it manually:
for (size_t i = 0; i < a_Length; i++)
{
Byte Buffer[sizeof(m_IV)];
aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer);
for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++)
{
m_IV[idx] = m_IV[idx + 1];
}
m_IV[sizeof(m_IV) - 1] = a_EncryptedIn[i];
a_DecryptedOut[i] = a_EncryptedIn[i] ^ Buffer[0];
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cAESCFBEncryptor:
cAESCFBEncryptor::cAESCFBEncryptor(void) :
m_IVOffset(0),
m_IsValid(false)
{
}
cAESCFBEncryptor::~cAESCFBEncryptor()
{
// Clear the leftover in-memory data, so that they can't be accessed by a backdoor
memset(&m_Aes, 0, sizeof(m_Aes));
}
void cAESCFBEncryptor::Init(const Byte a_Key[16], const Byte a_IV[16])
{
ASSERT(!IsValid()); // Cannot Init twice
ASSERT(m_IVOffset == 0);
memcpy(m_IV, a_IV, 16);
aes_setkey_enc(&m_Aes, a_Key, 128);
m_IsValid = true;
}
void cAESCFBEncryptor::ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length)
{
ASSERT(IsValid()); // Must Init() first
// PolarSSL doesn't do AES-CFB8, so we need to implement it ourselves:
for (size_t i = 0; i < a_Length; i++)
{
Byte Buffer[sizeof(m_IV)];
aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer);
for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++)
{
m_IV[idx] = m_IV[idx + 1];
}
a_EncryptedOut[i] = a_PlainIn[i] ^ Buffer[0];
m_IV[sizeof(m_IV) - 1] = a_EncryptedOut[i];
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cSHA1Checksum:
cSHA1Checksum::cSHA1Checksum(void) :
m_DoesAcceptInput(true)
{
sha1_starts(&m_Sha1);
}
void cSHA1Checksum::Update(const Byte * a_Data, size_t a_Length)
{
ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed
sha1_update(&m_Sha1, a_Data, a_Length);
}
void cSHA1Checksum::Finalize(cSHA1Checksum::Checksum & a_Output)
{
ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed
sha1_finish(&m_Sha1, a_Output);
m_DoesAcceptInput = false;
}
void cSHA1Checksum::DigestToJava(const Checksum & a_Digest, AString & a_Out)
{
Checksum Digest;
memcpy(Digest, a_Digest, sizeof(Digest));
bool IsNegative = (Digest[0] >= 0x80);
if (IsNegative)
{
// Two's complement:
bool carry = true; // Add one to the whole number
for (int i = 19; i >= 0; i--)
{
Digest[i] = ~Digest[i];
if (carry)
{
carry = (Digest[i] == 0xff);
Digest[i]++;
}
}
}
a_Out.clear();
a_Out.reserve(40);
for (int i = 0; i < 20; i++)
{
AppendPrintf(a_Out, "%02x", Digest[i]);
}
while ((a_Out.length() > 0) && (a_Out[0] == '0'))
{
a_Out.erase(0, 1);
}
if (IsNegative)
{
a_Out.insert(0, "-");
}
}
void cSHA1Checksum::Restart(void)
{
sha1_starts(&m_Sha1);
m_DoesAcceptInput = true;
}