From 073ef0ea0148852d8ddbe16a74d7515200d64d7d Mon Sep 17 00:00:00 2001 From: lotodore Date: Sun, 29 Aug 2010 15:39:46 +0000 Subject: [PATCH] Adding AES128 encryption/decryption which works both with gcrypt and openssl. This can be used for encryption of player's cards (not implemented yet). --- src/core/common/crypthelper.cpp | 154 ++++++++++++++++++++++++-------- src/core/crypthelper.h | 9 +- 2 files changed, 125 insertions(+), 38 deletions(-) diff --git a/src/core/common/crypthelper.cpp b/src/core/common/crypthelper.cpp index 6bc42ef6..dba32af9 100644 --- a/src/core/common/crypthelper.cpp +++ b/src/core/common/crypthelper.cpp @@ -231,68 +231,148 @@ CryptHelper::HMACSha1(const unsigned char *keyData, unsigned keySize, const unsi return retVal; } +void +CryptHelper::BytesToKey(const unsigned char *keyData, unsigned keySize, unsigned char *key, unsigned char *iv) +{ + // The key/iv derivation is kind of like EVP_BytesToKey of OpenSSL with count 2 and no salt. + // EVP_BytesToKey is not used because there is nothing like it in GnuTLS or gcrypt. + SHA1Buf tmpBuf1, tmpBuf2, keyBuf1, keyBuf2; + // First 20 bytes + CryptHelper::SHA1Hash(keyData, keySize, tmpBuf1); + CryptHelper::SHA1Hash(tmpBuf1.GetData(), tmpBuf1.GetDataSize(), keyBuf1); + // Second 20 bytes (we only need a total of 32 bytes, but anyway). + unsigned tmpKeySize = keySize + keyBuf1.GetDataSize(); + unsigned char *tmpKeyData = new unsigned char[tmpKeySize]; + // Concatenate our first hash and the key data. + memcpy(tmpKeyData, keyBuf1.GetData(), keyBuf1.GetDataSize()); + memcpy(tmpKeyData + keyBuf1.GetDataSize(), keyData, keySize); + CryptHelper::SHA1Hash(tmpKeyData, tmpKeySize, tmpBuf2); + CryptHelper::SHA1Hash(tmpBuf2.GetData(), tmpBuf2.GetDataSize(), keyBuf2); + delete[] tmpKeyData; + // Copy the hashes to key/iv. + memcpy(key, keyBuf1.GetData(), AES_BLOCK_SIZE); + unsigned tmpivBytes = keyBuf1.GetDataSize() - AES_BLOCK_SIZE; + memcpy(iv, keyBuf1.GetData() + AES_BLOCK_SIZE, tmpivBytes); + memcpy(iv + tmpivBytes, keyBuf2.GetData(), AES_BLOCK_SIZE - tmpivBytes); +} + bool -CryptHelper::AES128Encrypt(const unsigned char *keyData, unsigned keySize, const unsigned char *plainData, unsigned plainSize, std::vector &outCipher) +CryptHelper::AES128Encrypt(const unsigned char *keyData, unsigned keySize, const string &plainStr, std::vector &outCipher) { bool retVal = false; + unsigned plainSize = static_cast(plainStr.size()); if (keySize && plainSize) { - const int AESBlockSize = 16; - // The key/iv derivation is kind of like EVP_BytesToKey of OpenSSL with count 2 and no salt. - // EVP_BytesToKey is not used because there is nothing like it in GnuTLS or gcrypt. - SHA1Buf tmpBuf1, tmpBuf2, keyBuf1, keyBuf2; - unsigned char key[AESBlockSize]; - unsigned char iv[AESBlockSize]; - // First 20 bytes - CryptHelper::SHA1Hash(keyData, keySize, tmpBuf1); - CryptHelper::SHA1Hash(tmpBuf1.GetData(), tmpBuf1.GetDataSize(), keyBuf1); - // Second 20 bytes (we only need a total of 32 bytes, but anyway). - unsigned tmpKeySize = keySize + keyBuf1.GetDataSize(); - unsigned char *tmpKeyData = new unsigned char[tmpKeySize]; - // Concatenate our first hash and the key data. - memcpy(tmpKeyData, keyBuf1.GetData(), keyBuf1.GetDataSize()); - memcpy(tmpKeyData + keyBuf1.GetDataSize(), keyData, keySize); - CryptHelper::SHA1Hash(tmpKeyData, tmpKeySize, tmpBuf2); - CryptHelper::SHA1Hash(tmpBuf2.GetData(), tmpBuf2.GetDataSize(), keyBuf2); - delete[] tmpKeyData; - // Copy the hashes to key/iv. - memcpy(key, keyBuf1.GetData(), sizeof(key)); - unsigned tmpivBytes = keyBuf1.GetDataSize() - sizeof(key); - memcpy(iv, keyBuf1.GetData() + sizeof(key), tmpivBytes); - memcpy(iv + tmpivBytes, keyBuf2.GetData(), sizeof(iv) - tmpivBytes); + unsigned char key[AES_BLOCK_SIZE]; + unsigned char iv[AES_BLOCK_SIZE]; + BytesToKey(keyData, keySize, key, iv); + // Add padding to plain data. + unsigned paddedPlainSize = ADD_PADDING(plainSize); + unsigned char *paddedPlainStr = new unsigned char[ADD_PADDING(plainSize)]; + memset(paddedPlainStr, 0, paddedPlainSize); + memcpy(paddedPlainStr, plainStr.c_str(), plainSize); // Perform the encryption. - int cipherSize = plainSize + AESBlockSize; // Maximum possible size + 1 + int cipherSize = paddedPlainSize; outCipher.resize(cipherSize); #ifdef HAVE_OPENSSL EVP_CIPHER_CTX encryptCtx; EVP_CIPHER_CTX_init(&encryptCtx); - EVP_EncryptInit(&encryptCtx, EVP_aes_128_cbc(), key, iv); - int updateCipherSize = cipherSize; - EVP_EncryptUpdate(&encryptCtx, &outCipher[0], &updateCipherSize, plainData, plainSize); - if (updateCipherSize) + int outCipherSize = cipherSize; + + int success = EVP_EncryptInit(&encryptCtx, EVP_aes_128_cbc(), key, iv); + EVP_CIPHER_CTX_set_padding(&encryptCtx, 0); + if (success) { - int finalCipherSize = cipherSize - updateCipherSize; - EVP_EncryptFinal(&encryptCtx, &outCipher[updateCipherSize], &finalCipherSize); - outCipher.resize(updateCipherSize + finalCipherSize); - retVal = true; + success = EVP_EncryptUpdate(&encryptCtx, &outCipher[0], &outCipherSize, paddedPlainStr, paddedPlainSize); + + if (success && outCipherSize) + { + // Since padding is off, this will not modify the cipher. However, parameters need to be set. + EVP_EncryptFinal(&encryptCtx, &outCipher[0], &outCipherSize); + retVal = true; + } } else outCipher.clear(); #else gcry_cipher_hd_t hd; - gcry_error_t err = gcry_cipher_open (&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0); + gcry_error_t err = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0); if (!err) { gcry_cipher_setkey(hd, key, sizeof(key)); gcry_cipher_setiv(hd, iv, sizeof(iv)); - err = gcry_cipher_encrypt(hd, &outCipher[0], cipherSize, plainData, plainSize); + err = gcry_cipher_encrypt(hd, &outCipher[0], cipherSize, paddedPlainStr, paddedPlainSize); if (!err) - { retVal = true; - } + else + outCipher.clear(); } + else + outCipher.clear(); + + gcry_cipher_close(hd); #endif } return retVal; } + +bool +CryptHelper::AES128Decrypt(const unsigned char *keyData, unsigned keySize, const unsigned char *cipher, unsigned cipherSize, string &outPlain) +{ + bool retVal = false; + if (keySize && cipherSize) + { + unsigned char key[AES_BLOCK_SIZE]; + unsigned char iv[AES_BLOCK_SIZE]; + BytesToKey(keyData, keySize, key, iv); + outPlain.resize(cipherSize); +#ifdef HAVE_OPENSSL + EVP_CIPHER_CTX decryptCtx; + EVP_CIPHER_CTX_init(&decryptCtx); + int outPlainSize = cipherSize; + + int success = EVP_DecryptInit(&decryptCtx, EVP_aes_128_cbc(), key, iv); + EVP_CIPHER_CTX_set_padding(&decryptCtx, 0); + if (success) + { + success = EVP_DecryptUpdate(&decryptCtx, (unsigned char *)&outPlain[0], &outPlainSize, cipher, cipherSize); + + if (success && outPlainSize) + { + // Since padding is off, this will not modify the plain text. However, parameters need to be set. + EVP_DecryptFinal(&decryptCtx, (unsigned char *)outPlain.c_str(), &outPlainSize); + retVal = true; + } + } + else + outPlain.clear(); +#else + gcry_cipher_hd_t hd; + gcry_error_t err = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, 0); + if (!err) + { + gcry_cipher_setkey(hd, key, sizeof(key)); + gcry_cipher_setiv(hd, iv, sizeof(iv)); + err = gcry_cipher_decrypt(hd, &outPlain[0], outPlain.size(), cipher, cipherSize); + if (!err) + retVal = true; + else + outPlain.clear(); + } + else + outPlain.clear(); + + gcry_cipher_close(hd); +#endif + // Remove trailing zeroes (padding). + if (!outPlain.empty()) + { + size_t pos = outPlain.find_first_of('\0'); + if (pos != string::npos) + outPlain = outPlain.substr(0, pos); + } + } + return retVal; +} + diff --git a/src/core/crypthelper.h b/src/core/crypthelper.h index a131ddc8..e778558e 100644 --- a/src/core/crypthelper.h +++ b/src/core/crypthelper.h @@ -27,6 +27,9 @@ #define MD5_DATA_SIZE 16 #define SHA1_DATA_SIZE 20 +#define AES_BLOCK_SIZE 16 +#define ADD_PADDING(x) ((((x) + 15) >> 4) << 4) + class HashBuf { public: @@ -76,7 +79,11 @@ public: static bool MD5Sum(const std::string &fileName, MD5Buf &buf); static bool SHA1Hash(const unsigned char *data, unsigned dataSize, SHA1Buf &buf); static bool HMACSha1(const unsigned char *keyData, unsigned keySize, const unsigned char *plainData, unsigned plainSize, SHA1Buf &buf); - static bool AES128Encrypt(const unsigned char *keyData, unsigned keySize, const unsigned char *plainData, unsigned plainSize, std::vector &outCipher); + static bool AES128Encrypt(const unsigned char *keyData, unsigned keySize, const std::string &plainStr, std::vector &outCipher); + static bool AES128Decrypt(const unsigned char *keyData, unsigned keySize, const unsigned char *cipher, unsigned cipherSize, std::string &outPlain); + +private: + static void BytesToKey(const unsigned char *keyData, unsigned keySize, unsigned char *key, unsigned char *iv); }; #endif