Properly Decrypt AES in GCM mode in Crypto++ - c++

I've been trying to encrypt and decrypt a file using AES in GCM mode using Crypto++. What this code is supposed to do is, given a password, hash it using PBKDF2< HMAC< SHA256>>, then encrypt and decrypt a file using the password hash as the key. After searching all over on Stack Overflow I've gotten this far:
using namespace Cryptopp;
const std::string password = "password";
const int iterations = 1000000;
const std::string fileName = "test.txt";
SecByteBlock derived(32);
SecByteBlock salt(16);
const int TAG_SIZE = AES::BLOCKSIZE;
AutoSeededRandomPool rng;
rng.GenerateBlock(salt, 16);
// KDF function
PKCS5_PBKDF2_HMAC<SHA256> kdf;
kdf.DeriveKey(
derived.data(),
derived.size(),
0,
(byte*)password.data(),
password.size(),
salt.data(),
salt.size(),
iterations);
// Key the cipher
GCM<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(derived.data(), 16, derived.data() + 16, 16);
FileSource(fileName.c_str(), true,
new AuthenticatedEncryptionFilter(encryptor,
new FileSink(fileName.c_str()), false, TAG_SIZE));
// Key the cipher
GCM<AES>::Decryption decryptor;
decryptor.SetKeyWithIV(derived.data(), 16, derived.data() + 16, 16);
AuthenticatedDecryptionFilter decryptionFilter(decryptor,
new FileSink(fileName.c_str()), 16, TAG_SIZE);
FileSource(fileName.c_str(), true, new Redirector(decryptionFilter));
If the use of half of the derived hash from PBKDF2 as the key and half as the IV seems weird, this code is largely copied from How to use a custom key in Crypto++. Is this good practice in cryptography? Or should I generate a separate IV every time I encrypt?
Crypto++ throws a HashVerificationFailed exception, meaning the data was changed since encryption. So obviously I'm doing something wrong. What's wrong with my code?

Related

How to generate a secure random STRING (Key and IV) for AES-256-CBC WinApi ? [C/C++]

I need to generate (in C/C++) a secure random string (32byte) to use as Key and another (16byte) to use as IV for AES-256-CBC encryption using WinApi. The problem is that I need to save the generated string in a text file in order to manually test the decryption using OpenSSL in terminal.
What can I use to generate the secure random string other than CryptGenRandom? The problem of CryptGenRandom is that it generates a random byte sequence that I can't save/use as OpenSSL input because it's not an ASCII text:
openssl aes-256-cbc -d -K "..." -iv ".." -in encrypted.txt
Is there an alternative?
This is my working code:
// handles for csp and key
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
BYTE *szKey = (BYTE*)calloc(DEFAULT_AES_KEY_SIZE + 1, sizeof(BYTE));
BYTE *szIV = (BYTE*)calloc(DEFAULT_IV_SIZE + 1, sizeof(BYTE));
char* ciphertext= 0;
DWORD dwPlainSize = lstrlenA(*plaintext), dwBufSize = 0;
AES256KEYBLOB AESBlob;
memset(&AESBlob, 0, sizeof(AESBlob));
// initalize key and plaintext
StrCpyA((LPSTR)szKey, "00112233445566778899001122334455");
StrCpyA((LPSTR)szIV, "4455667788990011");
// generate key & IV
//if (!CryptGenRandom(hProv, DEFAULT_AES_KEY_SIZE, szKey)) {goto error;}
//if (!CryptGenRandom(hProv, DEFAULT_IV_SIZE, szIV)) {goto error;}
// blob data for CryptImportKey() function (include key and version and so on...)
AESBlob.bhHdr.bType = PLAINTEXTKEYBLOB;
AESBlob.bhHdr.bVersion = CUR_BLOB_VERSION;
AESBlob.bhHdr.reserved = 0;
AESBlob.bhHdr.aiKeyAlg = CALG_AES_256;
AESBlob.dwKeySize = DEFAULT_AES_KEY_SIZE;
StrCpyA((LPSTR)AESBlob.szBytes, (LPCSTR)szKey);
// create a cryptographic service provider (CSP)
if (!CryptAcquireContextA(&hProv, NULL, MS_ENH_RSA_AES_PROV_A, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {goto error;}
// populate crypto provider (CSP)
if (!CryptImportKey(hProv, (BYTE*)&AESBlob, sizeof(AES256KEYBLOB), NULL, CRYPT_EXPORTABLE, &hKey)) { goto error; }
if (!CryptSetKeyParam(hKey, KP_IV, szIV, 0)) { goto error; }
// ciphertext allocation
dwBufSize = BUFFER_FOR_PLAINTEXT + dwPlainSize;
ciphertext = (char*)calloc(dwBufSize, sizeof(char));
memcpy_s(ciphertext, dwBufSize, *plaintext, dwPlainSize);
// encryption
if (!CryptEncrypt(hKey, NULL, TRUE, 0, (BYTE*)ciphertext, &dwPlainSize, dwBufSize) ) { goto error; }
The key and IV are given to OpenSSL as hexadecimal. There's no need to generate visible ASCII characters only. You could have figured that out yourself just by pretending that the generated key and IV was "Hello". You get an error message that's pretty helpful.
$> openssl aes-256-cbc -d -K Hello -iv Hello
hex string is too short, padding with zero bytes to length
non-hex digit
invalid hex iv value
And hey, look at your code,
// initalize key and plaintext
StrCpyA((LPSTR)szKey, "00112233445566778899001122334455");
StrCpyA((LPSTR)szIV, "4455667788990011");
These string are not only ASCII text, they happen to be hex as well. It looks to as if you just need to convert hex to bytes in your code or convert bytes to a hex string if you have the bytes in code and want to generate the command line.

First 16 bytes of AES-128 CFB-8 decryption are damaged

I've been working on a project recently that should connect to a server with the help of a protocol. So far so good, but when I combed to decrypt the packages, I quickly noticed that something is not working properly.
The first 16 bytes of all packets are decrypted incorrectly. I have tried it with different libraries but that does not work either. I work in the C++ language and have so far used Crypto++ and OpenSSL for decryption, without success.
Under this Link you can find the protocol, here the decryption protocol Link and here is my corresponding code:
OpenSSL:
void init() {
unsigned char* sharedSecret = new unsigned char[AES_BLOCK_SIZE];
std::generate(sharedSecret,
sharedSecret + AES_BLOCK_SIZE,
std::bind(&RandomGenerator::GetInt, &m_RNG, 0, 255));
for (int i = 0; i < 16; i++) {
sharedSecretKey += sharedSecret[i];
}
// Initialize AES encryption and decryption
if (!(m_EncryptCTX = EVP_CIPHER_CTX_new()))
std::cout << "123" << std::endl;
if (!(EVP_EncryptInit_ex(m_EncryptCTX, EVP_aes_128_cfb8(), nullptr, (unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str())))
std::cout << "123" << std::endl;
if (!(m_DecryptCTX = EVP_CIPHER_CTX_new()))
std::cout << "123" << std::endl;
if (!(EVP_DecryptInit_ex(m_DecryptCTX, EVP_aes_128_cfb8(), nullptr, (unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str())))
std::cout << "123" << std::endl;
m_BlockSize = EVP_CIPHER_block_size(EVP_aes_128_cfb8());
}
std::string result;
int size = 0;
result.resize(1000);
EVP_DecryptUpdate(m_DecryptCTX, &((unsigned char*)result.c_str())[0], &size, &sendString[0], data.size());
Crypto++:
CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption AESDecryptor((byte*)sharedSecret.c_str(), (unsigned int)16, sharedSecret.c_str(), 1);
std::string sTarget("");
CryptoPP::StringSource ss(data, true, new CryptoPP::StreamTransformationFilter(AESDecryptor, new CryptoPP::StringSink(sTarget)));
I think important to mention is that I use one and the same shared secret for the key and the iv (initialization vector). In other posts, this was often labeled as a problem. I do not know how to fix it in this case because the protocol want it.
I would be looking forward to a constructive feedback.
EVP_EncryptInit_ex(m_EncryptCTX, EVP_aes_128_cfb8(), nullptr,
(unsigned char*)sharedSecretKey.c_str(), (unsigned char*)sharedSecretKey.c_str()))
And:
CFB_Mode<AES>::Decryption AESDecryptor((byte*)sharedSecret.c_str(),
(unsigned int)16, sharedSecret.c_str(), 1);
std::string sTarget("");
StringSource ss(data, true, new StreamTransformationFilter(AESDecryptor, new StringSink(sTarget)));
It is not readily apparent, but you need to set feedback size for the mode of operation of the block cipher in Crypto++. The Crypto++ feedback size is 128 by default.
The code to set the feedback size of CFB mode can be found at CFB Mode on the Crypto++ wiki. You want the 3rd or 4th example down the page.
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(iv));
That is kind of an awkward way to pass parameters. It is documented in the sources files and on the wiki at NameValuePairs. It allows you to pass arbitrary parameters through consistent interfaces. It is powerful once you acquire a taste for it.
And then use params to key the encryptor and decryptor:
CFB_Mode< AES >::Encryption enc;
enc.SetKey( key, key.size(), params );
// CFB mode must not use padding. Specifying
// a scheme will result in an exception
StringSource ss1( plain, true,
new StreamTransformationFilter( enc,
new StringSink( cipher )
) // StreamTransformationFilter
); // StringSource
I believe your calls would look something like this (if I am parsing the OpenSSL correctly):
const byte* ptr = reinterpret_cast<const byte*>(sharedSecret.c_str());
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(ptr, 16));
CFB_Mode< AES >::Encryption enc;
enc.SetKey( ptr, 16, params );
In your production code you should use unique key and iv. So do something like this using HKDF:
std::string seed(AES_BLOCK_SIZE, '0');
std::generate(seed, seed + AES_BLOCK_SIZE,
std::bind(&RandomGenerator::GetInt, &m_RNG, 0, 255));
SecByteBlock sharedSecret(32);
const byte usage[] = "Key and IV v1";
HKDF<SHA256> hkdf;
hkdf.DeriveKey(sharedSecret, 32, &seed[0], 16, usage, COUNTOF(usage), nullptr, 0);
AlgorithmParameters params =
MakeParameters(Name::FeedbackSize(), 1 /*8-bits*/)
(Name::IV(), ConstByteArrayParameter(sharedSecret+16, 16));
CFB_Mode< AES >::Encryption enc;
enc.SetKey(sharedSecret+0, 0, params);
In the code above, sharedSecret is twice as large as it needs to be. You derive the key and iv from the seed using HDKF. sharedSecret+0 is the 16-byte key, and sharedSecret+16 is the 16-byte iv.

Re-encrypting the encrypted data file generates a decrypted output

I wrote the encryption function using Crypto++ library, function behaves correctly when a file encryption is done for the first time. If the same encrypted file is passed again for encryption, generates the output which includes encrypted and decrypted data.
bool EncryptDataFile(const char* inputFile, const char* outputFile)
{
try
{
std::vector<byte> key = HexDecoding(PASSCODE);
std::vector<byte> iv = HexDecoding(INITIALIZATION_VECTOR);
GCM<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key.data(), key.size(), iv.data(), iv.size());
FileSource fs(inputFile, true,
new AuthenticatedEncryptionFilter(encryptor,
new FileSink(outputFile), false, TAG_SIZE));
}
catch(...)
{
return false;
}
return true;
}
Input.txt:
Privacy and Security
Output1.txt - first time encryption output:
{)ªei ?ñìCzN[hç&Ää€|Ùrñ½…
Ä
Input "Output1.txt", Output "Output2.txt" - second time encryption:
Privacy and Security]®Ÿwþñ úeS„£Fpä40WL ,ÈR¯M
It has revealed the original data. An not sure what is missing here.
If the same encrypted file is passed again for encryption, generates the output which includes encrypted and decrypted data.
If I am parsing things correctly, you are saying m ≅ Enc(Enc(m)) instead of c = Enc(Enc(m)) in your encryption scheme. This is one of the reasons why you should avoid designing your own scheme.
This can happen in several scenarios, like with a stream cipher or block cipher in counter mode when re-using a key and iv.
You should using a different security context for each message or encryption operation. With some hand waiving, that means change the key or iv for each message or encryption operation.
std::vector<byte> key = HexDecoding(PASSCODE);
std::vector<byte> iv = HexDecoding(INITIALIZATION_VECTOR);
This is likely your problem. You need to use a different security context for each message or encryption operation.
Here is how you fix it. You use a key derivation function to derive different security parameters for each encryption. In the code below, the 32-byte key is divided into two 16-byte keys. The same applies to the iv. The first encryption uses key+0 and iv+0; and the second encryption uses key+16 and iv+16.
cryptopp$ cat test.cxx
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "aes.h"
#include "gcm.h"
#include "hex.h"
#include "hkdf.h"
#include "sha.h"
#include <string>
#include <iostream>
int main(int argc, char* argv[])
{
using namespace CryptoPP;
std::string password = "super secret password";
SecByteBlock key(32), iv(32);
HKDF<SHA256> hkdf;
hkdf.DeriveKey(key, key.size(),
(const byte*)password.data(), password.size(),
NULL, 0, // salt
(const byte*)"key derivation", 14);
hkdf.DeriveKey(iv, iv.size(),
(const byte*)password.data(), password.size(),
NULL, 0, // salt
(const byte*)"iv derivation", 13);
std::string m = "Yoda said, Do or do not. There is no try.";
std::string c1, c2;
GCM<AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key, 16, iv, 16);
StringSource(m, true, new AuthenticatedEncryptionFilter(
encryptor, new StringSink(c1)));
encryptor.SetKeyWithIV(key+16, 16, iv+16, 16);
StringSource(c1, true, new AuthenticatedEncryptionFilter(
encryptor, new StringSink(c2)));
std::cout << "Hex(m):" << std::endl;
StringSource(m, true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
std::cout << "Hex(Enc(m)):" << std::endl;
StringSource(c1, true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
std::cout << "Hex(Enc(Enc(m))):" << std::endl;
StringSource(c2, true, new HexEncoder(new FileSink(std::cout)));
std::cout << std::endl;
return 0;
}
Here is a run of the program:
cryptopp$ ./test.exe
Hex(m):
596F646120736169642C20446F206F7220646F206E6F742E205468657265206973206E6F20747279
2E
Hex(Enc(m)):
D4A9063DE7400E90627DE90D16346DC5A99740C55F6FEE092A99071F55F1BDB25A72B7422126CCC4
09B5B5C0076E39EBF7256D5DC3151A738D
Hex(Enc(Enc(m))):
83A459F2D4A1627624AF162590465AC705C8AC0F4D915E4A4A9D300156C5F9E042CAA47903353F0A
A1FAE408D5747DD223AC4F9AEF3C320EEF7E79E08AB2C6FBEAE7A3A5B4978C45C7
I think your scheme has some additional problems. For example, if you encrypt the message "Attack at dawn!" multiple times, then you get the same ciphertext on each run. It is leaking information, and it lacks ciphertext indistinguishability.
I think you should avoid your scheme, and use an Elliptic Curve Integrated Encryption Scheme (ECIES). It avoids most of the latent problems in your scheme, and achieves IND-CCA2.
The downside to ECIES is, you have to manage a public/private keypair. It is not a big downside, though. You are already managing a password and iv, so changing from a password to a private key is not much more work.

Encrypt file with AES 128 mode cbc using public key X509 V3 (PKCS7)

I need to encrypt file with AES 128 mode cbc.
The key of AES need to encrypt using public key X509 V3.
All this need to save in binary file PKCS7.
BIO* certBIO = BIO_new_mem_buf((void*)&certData[0], certData.size());
if (certBIO)
x509 = d2i_X509_bio(certBIO, 0);
BIO_free(certBIO);
sk_X509_push(x509_stack, x509);
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, &inData[0], inData.size());
BIO_flush(bio);
PKCS7* pkcs7_encrypt = PKCS7_encrypt(x509_stack, bio, EVP_aes_128_cbc(), PKCS7_BINARY);
FILE *fpPKCS7 = fopen(szPKCS7File, "wb");
if (!fpPKCS7)
return 1;
i2d_PKCS7_fp(fpPKCS7, pkcs7_encrypt);
fclose(fpPKCS7);
X509_free(x509);
sk_X509_pop_free(x509_stack, X509_free);
Is it correct code ?
The function i2d_PKCS7_fp crashes.
I found solution:
PKCS7* pkcs7_encrypt = PKCS7_encrypt(x509_stack, bioIn, EVP_aes_128_cbc(), PKCS7_BINARY);
BIO *bioOut = BIO_new(BIO_s_file());
BIO_write_filename(bioOut, szPKCS7File);
i2d_PKCS7_bio(bioOut, pkcs7_encrypt);
But i have a question else.
What information really in this file ?
Is it contains the encrypted key of AES by public key of certificat ?

'message hash or MAC not valid' exception after decryption

I'm trying to make a program that encrypts files (.jpg and .avi) using the crypto++ libraries. My aim is to make a program that successfully encrypts video files using AES-256.
I did text examples of AES encryption from here and they ran successfully (meaning that the library is setup correctly). However, the following simple code produces the exception
HashVerificationFilter: message hash or MAC not valid
Code:
AutoSeededRandomPool prng;
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
prng.GenerateBlock(key, key.size());
SecByteBlock iv(AES::BLOCKSIZE);
prng.GenerateBlock(iv, iv.size());
string ofilename = "testimage.png";
string efilename;
string rfilename = "testimagerecovered.png";
try
{
GCM< AES >::Encryption e;
e.SetKeyWithIV(key, key.size(), iv, iv.size());
ifstream ofile(ofilename.c_str(), ios::binary);
ofile.seekg(0, ios_base::beg);
FileSource fs1(ofilename.c_str(), true,
new AuthenticatedEncryptionFilter(e,
new StringSink(efilename)));
GCM< AES >::Decryption d2;
d2.SetKeyWithIV(key, key.size(), iv, sizeof(iv));
StringSource fs2(efilename, true,
new AuthenticatedDecryptionFilter( d2,
new FileSink (rfilename.c_str()),
AuthenticatedDecryptionFilter::THROW_EXCEPTION));
}
catch(const Exception &e)
{
cerr << e.what() << endl;
exit(1);
}
return 0;
I suspect I am not implementing the AES algorithm correctly. However, I am unable to find a solution for the last two days. I'm using Eclipse Luna on Ubuntu 14.04.
PS I have gone through the following answers
How to read an image to a string for encrypting Crypto++
How to loop over Blowfish Crypto++
Please use iv.size() rather than sizeof(iv) when you try to set d2.SetKeyWithIV, just like what you have done to e.SetKeyWithIV.
Because in this program, the value of iv.size() is 16, but sizeof(iv) is 24. Then it will work.
GCM< AES >::Decryption d2;
d2.SetKeyWithIV(key, key.size(), iv, iv.size()); //here was a misuse of sizeof(iv)
StringSource fs2(efilename, true,
new AuthenticatedDecryptionFilter( d2,
new FileSink (rfilename.c_str()),
AuthenticatedDecryptionFilter::THROW_EXCEPTION));
The code which has passed my test is as above.