I am new with Crypto++. I want to using Crypto++ library to encrypt/decrypt a large byte array in C++. The data can be anything, so asume its binary format.
First, I tried with "byte array" (char * or char[]).
byte PlainText[] = {
'H','e','l','l','o',' ',
'W','o','r','l','d',
0x0,0x0,0x0,0x0,0x0
};
byte key[ AES::DEFAULT_KEYLENGTH ];
::memset( key, 0x01, AES::DEFAULT_KEYLENGTH );
// Encrypt
ECB_Mode< AES >::Encryption Encryptor( key, sizeof(key) );
byte cbCipherText[AES::BLOCKSIZE];
Encryptor.ProcessData( cbCipherText, PlainText, sizeof(PlainText) );
We use ProcessData() to encrypt the plain text, since it allows us to receive the result in a single line of code. Next, we enter a DMZ, and then decrypt the cipher text.
// Decrypt
ECB_Mode< AES >::Decryption Decryptor( key, sizeof(key) );
byte cbRecoveredText[AES::BLOCKSIZE];
Decryptor.ProcessData( cbRecoveredText, cbCipherText, sizeof(cbCipherText) );
The code above work perfect with small data (16KB). But it doesn't work with large data because "is not multiple of block size". Then, I think about using StreamTransformationFilter, which can automatic do the padding job for me. So I tried to encrypt and decrypt a std::string with encryptString() and decryptString() like below:
string encryptString(string plain, byte key[], int sizeKey, byte iv[], int sizeIV){
string cipher;
try{
CBC_Mode< AES >::Encryption e;
e.SetKeyWithIV(key, sizeKey, iv, sizeIV);
// The StreamTransformationFilter removes
// padding as required.
StringSource s(plain, true,
new StreamTransformationFilter(e,
new StringSink(cipher)
) // StreamTransformationFilter
); // StringSource
#if 0
StreamTransformationFilter filter(e);
filter.Put((const byte*)plain.data(), plain.size());
filter.MessageEnd();
const size_t ret = filter.MaxRetrievable();
cipher.resize(ret);
filter.Get((byte*)cipher.data(), cipher.size());
#endif
return cipher;
}
catch (const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
return NULL;
}
}
string decryptString(string cipher, byte key[], int sizeKey, byte iv[], int sizeIV){
string reco;
try{
CBC_Mode< AES >::Decryption d;
d.SetKeyWithIV(key, sizeKey, iv, sizeIV);
StringSource s(cipher, true,
new StreamTransformationFilter(d,
new StringSink(reco)
) // StreamTransformationFilter
); // StringSource
#if 0
StreamTransformationFilter filter(e);
filter.Put((const byte*)plain.data(), plain.size());
filter.MessageEnd();
const size_t ret = filter.MaxRetrievable();
cipher.resize(ret);
filter.Get((byte*)cipher.data(), cipher.size());
#endif
return reco;
}
catch (const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
return reco;
}
}
They are worked for large text file too. But, wait, my goal is encrypt/decrypt ANY byte array. And sometime they aren't string.
So I think about wrap the above two function to work with char *.
//wrap encryptString()
char* encrypt(char * plainText, byte key[], int sizeKey, byte iv[], int sizeIV){
string cipher = encryptString(plainText, key, sizeKey, iv, sizeIV);
FileUtil::writeFile("ss1.txt", cipher, cipher.length());
long len = cipher.size() + 1;
char * writable = new char[len];
std::copy(cipher.begin(), cipher.end(), writable);
writable[len] = '\0';
// don't forget to free the string after finished using it
//delete[] writable;
return writable;
}
//wrap decryptString()
char* decrypt(char * cipher, byte key[], int sizeKey, byte iv[], int sizeIV){
long len = strlen(cipher);
string recovered = decryptString(cipher, key, sizeKey, iv, sizeIV);
char * writable = new char[recovered.size() + 1];
std::copy(recovered.begin(), recovered.end(), writable);
writable[recovered.size()] = '\0'; // don't forget the terminating 0
// don't forget to free the string after finished using it
//delete[] writable;
return writable;
}
The result is:
When I read 1MB of text to encrypt() function, write the encrypted string "cipher" to "ss1.txt", its 1MB too. But to "writable", its only a part of "cipher" (about 1KB), and decrypted result is a part of original text too. Seem like '\0' was met somewhere and its terminated my char array?
I feel like going around now. Is there a way to using Crypto++ with (any) large byte (binary) array?
Additionally, I want to avoid using FileSource (Crypto++), because it doesn't allow me to save the encrypted value to variable.
Related
I'm stuck with the problem of decrypting AES-CBC ecrypted string.
I have JS code which decrypt that string but I need do that in C++.
Key is string of SHA512 hash, and message is string of Base64.
The JS code for decrypt:
CryptoJS.algo.AES.keySize = 32,
CryptoJS.algo.EvpKDF.cfg.iterations = 10000,
CryptoJS.algo.EvpKDF.cfg.keySize = 32;
var r = CryptoJS.AES.decrypt(message, key.toString());
My C++ code doesn't work
std::string generateIV(std::string key)
{
std::string iv(CryptoPP::AES::BLOCKSIZE, 0);
CryptoPP::SHA1().CalculateDigest((byte*)iv.data(), (byte*)key.data(), key.size());
return iv;
}
std::string decrypt(std::string &message, std::string &key) {
std::string decrypted;
std::string iv(generateIV(key));
// Create the AES decryption object
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption aesDecryption;
aesDecryption.SetKeyWithIV((byte*)key.data(), key.size(), (byte*)iv.data(), iv.size());
// Decrypt the message
CryptoPP::StringSource ss(message, true,
new CryptoPP::StreamTransformationFilter(aesDecryption,
new CryptoPP::StringSink(decrypted)
)
);
return decrypted;
}
Maybe I should use OpenSSL?
The ciphertext generated by the posted CryptoJS code cannot be decrypted by any AES compliant library. This is due to the line
CryptoJS.algo.AES.keySize = 32
which defines a keysize of 32 words = 32 * 4 = 128 bytes for key derivation. This is not a valid AES keysize and the derived number of rounds is not defined for AES at all (38 rounds for 128 bytes, see here; AES defines only 10, 12 and 14 rounds depending on the key size). The ciphertext is therefore not AES compliant. It can be decrypted with CryptoJS, but not by any AES compliant library, see also this CryptoJS issue #293.
For the generated ciphertext to be AES compatible, one of the allowed AES key sizes must be used, e.g. a keysize of 8 words = 32 bytes:
CryptoJS.algo.AES.keySize = 8
Furthermore, note that line
CryptoJS.algo.EvpKDF.cfg.iterations = 10000
leads to incomapatability with the OpenSSL CLI, which by default uses an iteration count of 1 in key derivation (which is one of the reasons why this key derivation is weak, see here).
By the way, the line
CryptoJS.algo.EvpKDF.cfg.keySize = 32
is completely ignored by the processing and can also be omitted.
If a valid AES key size is used, e.g. 8 words = 32 bytes:
CryptoJS.algo.AES.keySize = 8, // 8 words = 32 bytes
CryptoJS.algo.EvpKDF.cfg.iterations = 10000,
CryptoJS.algo.EvpKDF.cfg.keySize = 32;
var r = CryptoJS.AES.decrypt(message, key.toString());
the ciphertext can be decrypted programmatically. As already mentioned in the comments, CryptoJS uses the OpenSSL propritary key derivation function EVP_BytesToKey() if the key material is passed as string. This generates an 8 bytes salt during encryption and uses the salt and password to derive key and IV. These are used to encrypt in CBC mode with PKCS#7 padding by default. OpenSSL formats the result of the encryption as a concatenation of the ASCII encoding of Salted__, followed by the 8 bytes salt and finally by the actual ciphertext, usually Base64 encoded.
For decryption, the salt and ciphertext must be separated. Then, based on salt and password, key and IV are to be determined, with which finally the ciphertext is decrypted.
Thus, for decryption an implementation for EVP_BytesToKey() is needed. Such an implementation can be found for Crypto++ here in the Crypto++ docs, and a code with which the ciphertext of the CryptoJS code can be decrypted (after fixing the keysize issue) is e.g.:
#include "aes.h"
#include "modes.h"
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include "md5.h"
#include "base64.h"
#include "secblock.h"
static int OPENSSL_PKCS5_SALT_LEN = 8;
int OPENSSL_EVP_BytesToKey(CryptoPP::HashTransformation& hash, const unsigned char* salt, const unsigned char* data, int dlen, unsigned int count, unsigned char* key, unsigned int ksize, unsigned char* iv, unsigned int vsize);
int main(int, char**) {
// Pass data and parameter
std::string passphrase = "my passphrase";
std::string encryptedB64 = "U2FsdGVkX18AuE7abdK11z8Cgn3Nc+2cELB1sWIPhAJXBZGhnw45P4l58o33IEiJ8fV4oEid2L8wKXpAntPrAQ=="; // CryptoJS ciphertext for a 32 bytes keysize
std::string encrypted;
int iterationCount = 10000;
int keySize = 32;
// Base64 decode
CryptoPP::StringSource ssB64decodeCt(encryptedB64, true,
new CryptoPP::Base64Decoder(
new CryptoPP::StringSink(encrypted)
)
);
// Separate
std::string salt(encrypted.substr(8, 8));
std::string ciphertext(encrypted.substr(16));
// Derive key
CryptoPP::SecByteBlock key(keySize), iv(16);
CryptoPP::Weak::MD5 md5;
OPENSSL_EVP_BytesToKey(md5, (const unsigned char*)salt.data(), (const unsigned char*)passphrase.data(), passphrase.size(), iterationCount, key.data(), key.size(), iv.data(), iv.size());
// Decryption
std::string decryptedText;
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryption(key.data(), key.size(), iv.data());
CryptoPP::StringSource ssDecryptCt(
ciphertext,
true,
new CryptoPP::StreamTransformationFilter(
decryption,
new CryptoPP::StringSink(decryptedText),
CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme::PKCS_PADDING
)
);
// Output
std::cout << decryptedText << std::endl; // The quick brown fox jumps over the lazy dog
return 0;
}
// from: https://www.cryptopp.com/wiki/OPENSSL_EVP_BytesToKey
int OPENSSL_EVP_BytesToKey(CryptoPP::HashTransformation& hash, const unsigned char* salt, const unsigned char* data, int dlen, unsigned int count, unsigned char* key, unsigned int ksize, unsigned char* iv, unsigned int vsize)
{
if (data == NULL) return (0);
unsigned int nkey = ksize;
unsigned int niv = vsize;
unsigned int nhash = hash.DigestSize();
CryptoPP::SecByteBlock digest(nhash);
unsigned int addmd = 0, i;
for (;;)
{
hash.Restart();
if (addmd++)
hash.Update(digest.data(), digest.size());
hash.Update(data, dlen);
if (salt != NULL)
hash.Update(salt, OPENSSL_PKCS5_SALT_LEN);
hash.TruncatedFinal(digest.data(), digest.size());
for (i = 1; i < count; i++)
{
hash.Restart();
hash.Update(digest.data(), digest.size());
hash.TruncatedFinal(digest.data(), digest.size());
}
i = 0;
if (nkey)
{
for (;;)
{
if (nkey == 0) break;
if (i == nhash) break;
if (key != NULL)
*(key++) = digest[i];
nkey--;
i++;
}
}
if (niv && (i != nhash))
{
for (;;)
{
if (niv == 0) break;
if (i == nhash) break;
if (iv != NULL)
*(iv++) = digest[i];
niv--;
i++;
}
}
if ((nkey == 0) && (niv == 0)) break;
}
return ksize;
}
I am trying to decrypt ciphertext provided by a user using a Qt QLineEdit.
If I encrypt the string and then decrypt it using the code below, everything works as expected.
crypto::aes_pbkdf2_decrypt(crypto::aes_pbkdf2_encrypt("ciphertxt", "keystr"), "keystr");
When the string is encrypted, it gets an output to a QtLineEdit. When I attempt to decrypt that ciphertext output, it throws StreamTransformationFilter: invalid PKCS #7 block padding found.
This doesn't make much sense to me since it works perfectly when I directly decrypt the output from the encryption function as shows above.
Here are the functions I am using for encryption/decryption:
Encrypt
std::string crypto::aes_pbkdf2_encrypt(std::string pPlainText, std::string pKey)
{
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::SecByteBlock iv (CryptoPP::AES::BLOCKSIZE);
CryptoPP::SecByteBlock salt (CryptoPP::AES::DEFAULT_KEYLENGTH / 2);
CryptoPP::SecByteBlock key (CryptoPP::AES::DEFAULT_KEYLENGTH);
std::string cipher, output, cipher_str, iv_str, salt_str;
prng.GenerateBlock(salt, salt.size());
prng.GenerateBlock(iv, iv.size());
key = pbkdf2_salt_hash(pKey, salt);
try {
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt;
encrypt.SetKeyWithIV(key, key.size(), iv);
CryptoPP::StringSource stringsource(pPlainText, true,
new CryptoPP::StreamTransformationFilter(encrypt,
new CryptoPP::StringSink(cipher)
)
);
} catch (const CryptoPP::Exception& e) {
std::cerr << e.what() << std::endl;
return "";
}
CryptoPP::HexEncoder encoder (new CryptoPP::FileSink(std::cout));
encoder.Detach(new CryptoPP::StringSink(salt_str));
encoder.Put(salt, salt.size());
encoder.MessageEnd();
encoder.Detach(new CryptoPP::StringSink(iv_str));
encoder.Put(iv, iv.size());
encoder.MessageEnd();
encoder.Detach(new CryptoPP::StringSink(cipher_str));
encoder.Put((const CryptoPP::byte*) &cipher[0], cipher.size());
encoder.MessageEnd();
output = iv_str + cipher_str + salt_str;
std::cout << "Cipher: " << output << std::endl;
return output;
}
Decrypt
std::string crypto::aes_pbkdf2_decrypt(std::string pCipherText, std::string pKey)
{
std::string iv_str = pCipherText.substr(0, 32);
std::string salt_str = pCipherText.substr(pCipherText.size() - 16, pCipherText.size());
std::string cipher_str = pCipherText.substr(iv_str.size(), pCipherText.size() - (iv_str.size() + salt_str.size()));
std::string iv, salt, cipher, output;
CryptoPP::HexDecoder decoder(new CryptoPP::FileSink(std::cout));
decoder.Attach(new CryptoPP::StringSink(iv));
decoder.Put((CryptoPP::byte*) iv_str.data(), iv_str.size());
decoder.MessageEnd();
decoder.Attach(new CryptoPP::StringSink(salt));
decoder.Put((CryptoPP::byte*) salt_str.data(), salt_str.size());
decoder.MessageEnd();
decoder.Attach(new CryptoPP::StringSink(cipher));
decoder.Put((CryptoPP::byte*) cipher_str.data(), cipher_str.size());
decoder.MessageEnd();
try {
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
CryptoPP::SecByteBlock salt_blk ((CryptoPP::byte*) salt.data(), salt.size());
CryptoPP::SecByteBlock iv_blk ((CryptoPP::byte*) iv.data(), iv.size());
CryptoPP::SecByteBlock key = pbkdf2_salt_hash(pKey, salt_blk);
decryptor.SetKeyWithIV(key.data(), key.size(), iv_blk);
CryptoPP::StringSource stringsource (cipher, true,
new CryptoPP::StreamTransformationFilter(decryptor,
new CryptoPP::StringSink(output)
)
);
} catch (const CryptoPP::Exception& e) {
std::cerr << e.what() << std::endl;
return "";
}
return output;
}
PBKDF2 Hashing
CryptoPP::SecByteBlock crypto::pbkdf2_salt_hash(std::string pPlainText, CryptoPP::SecByteBlock pSalt)
{
CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA256> pbkdf;
CryptoPP::SecByteBlock output(CryptoPP::AES::DEFAULT_KEYLENGTH);
CryptoPP::byte text_bytes[pPlainText.size()];
std::memcpy(text_bytes, pPlainText.data(), pPlainText.size());
size_t plaintext_length = strlen((const char*)text_bytes);
size_t salt_length = pSalt.size();
pbkdf.DeriveKey(output, output.size(), 0, text_bytes, plaintext_length, pSalt, salt_length, 1024, 0.0f);
return output;
}
What I have already tried:
I tried each padding type in CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme to CryptoPP::StringSink in the decryption function. Only creates odd 'OpenType' errors and strangely formatted outputs.
I am trying to encrypt some sequential data using AES in Crypto++. However, I noticed that randomly choosing an encrypted item and decrypt it causes a StreamTransformationFilter: invalid PKCS #7 block padding found
A simple code that causes the same problem is the following:
AESUtil aes;
std::vector<std::string> t = std::vector<std::string>();
for (int i = 0; i < 100; ++i) {
std::string p = "a";
auto c = aes.encrypt(p);
t.push_back(c);
}
auto d = aes.decrypt(t[78]);
where AESUtil is as follow:
AESUtil::AESUtil() {
this->key = CryptoPP::SecByteBlock(0x00, CryptoPP::AES::DEFAULT_KEYLENGTH);
rnd.GenerateBlock(key, key.size());
// Generate a random IV
this->iv = CryptoPP::SecByteBlock(CryptoPP::AES::BLOCKSIZE);
rnd.GenerateBlock(iv, iv.size());
this->aesEncryption = CryptoPP::AES::Encryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
this->cbcEncryption = CryptoPP::CBC_Mode_ExternalCipher::Encryption(aesEncryption, iv);
this->aesDecryption = CryptoPP::AES::Decryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH);
this->cbcDecryption = CryptoPP::CBC_Mode_ExternalCipher::Decryption(aesDecryption, iv);
}
void AESUtil::encrypt(std::string &ciphertext, std::string &plaintext) {
CryptoPP::StreamTransformationFilter stfEncryptor(this->cbcEncryption, new CryptoPP::StringSink(ciphertext));
stfEncryptor.Put(reinterpret_cast<const unsigned char *>( plaintext.c_str()), plaintext.size());
stfEncryptor.MessageEnd();
}
void AESUtil::decrypt(std::string &plaintext, std::string &ciphertext) {
CryptoPP::StreamTransformationFilter stfDecryptor(this->cbcDecryption, new CryptoPP::StringSink(plaintext));
stfDecryptor.Put(reinterpret_cast<const unsigned char *>( ciphertext.c_str()), ciphertext.size());
stfDecryptor.MessageEnd();
}
std::string AESUtil::encrypt(std::string plaintext) {
std::string ciphertext;
//ciphertext.reserve(plaintext.size()+16);
encrypt(ciphertext, plaintext);
return ciphertext;
}
std::string AESUtil::decrypt(std::string ciphertext) {
std::string plaintext;
//plaintext.reserve(ciphertext.size()+16);
decrypt(plaintext, ciphertext);
return plaintext;
}
I thought the problem was linked to \0 null terminating character in strings, so I changed the code to use hex (not sure it is actually correct). However, the issue persists.
void AESUtil::encrypt(std::string &ciphertext, std::string &plaintext) {
CryptoPP::ByteQueue encrypted;
CryptoPP::StreamTransformationFilter f1(this->cbcEncryption, new CryptoPP::Redirector(encrypted));
//f1.PutWord32((uint32_t)v1.size(), BIG_ENDIAN_ORDER);
f1.Put((const unsigned char *) plaintext.c_str(), plaintext.size());
f1.MessageEnd();
CryptoPP::HexEncoder encoder(new CryptoPP::StringSink(ciphertext));
encrypted.CopyTo(encoder);
encoder.MessageEnd();
}
void AESUtil::decrypt(std::string &plaintext, std::string &ciphertext) {
CryptoPP::ByteQueue decrypted;
CryptoPP::HexDecoder decoder(new CryptoPP::Redirector(decrypted));
decoder.Put(reinterpret_cast<const unsigned char *>( ciphertext.data()), ciphertext.size());
decoder.MessageEnd();
CryptoPP::StreamTransformationFilter f2(this->cbcDecryption, new CryptoPP::StringSink(plaintext));
decrypted.CopyTo(f2);
f2.MessageEnd();
}
With the following code, both the AESUtil versions work without any problem:
for (int i = 0; i < 100; ++i) {
std::string p = "a";
auto c = aes.encrypt(p);
auto d = aes.decrypt(c);
}
Any idea what is the problem and how I could solve it? Any help would be very appreciated
I have found the problem.
The IV was changing even if MessageEnd was called, keeping a reference to the state of AESUtil.
I solved the problem by randomly generating the IV during the encryption process and storing it together with the ciphertext.
If anyone is interested, here is the new code:
void AESUtil::encrypt(std::string &ciphertext, std::string &plaintext) {
// Generate a random IV
CryptoPP::SecByteBlock iv(CryptoPP::AES::BLOCKSIZE);
this->rnd.GenerateBlock(iv, iv.size());
//memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE );
CryptoPP::StringSink stringSink(ciphertext);
CryptoPP::ArraySource as(iv, iv.size(), true,
new CryptoPP::Redirector(stringSink));
CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(this->aesEncryption, iv);
CryptoPP::ByteQueue encrypted;
CryptoPP::StreamTransformationFilter f1(cbcEncryption, new CryptoPP::Redirector(encrypted));
f1.Put(reinterpret_cast<const unsigned char *>(plaintext.c_str()), plaintext.size());
f1.MessageEnd();
CryptoPP::HexEncoder encoder(new CryptoPP::Redirector(stringSink));
encrypted.CopyTo(encoder);
encoder.MessageEnd();
std::string recovered;
CryptoPP::StringSink sink(recovered);
encrypted.CopyTo(sink);
}
void AESUtil::decrypt(std::string &plaintext, std::string &ciphertext) {
CryptoPP::SecByteBlock iv(CryptoPP::AES::BLOCKSIZE);
CryptoPP::StringSource ss(ciphertext, false /* DO NOT Pump All */);
// Attach new filter
CryptoPP::ArraySink as(iv, iv.size());
ss.Attach(new CryptoPP::Redirector(as));
ss.Pump(CryptoPP::AES::BLOCKSIZE); // Pump first 16 bytes
CryptoPP::StringSink stringSink(plaintext);
CryptoPP::ByteQueue decrypted;
CryptoPP::HexDecoder decoder(new CryptoPP::Redirector(decrypted));
ss.Detach(new CryptoPP::Redirector(decoder));
ss.PumpAll();
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(this->aesDecryption, iv);
CryptoPP::StreamTransformationFilter f2(cbcDecryption, new CryptoPP::Redirector(stringSink));
decrypted.CopyTo(f2);
f2.MessageEnd();
}
We are using cryptopp library. We are using the below coding. Encryption is working file without any issue and we are able to get the cipher text. But getting an error while decrypting as "Block padding found". What could be the issue...?
#include <iostream>
#include <string>
using namespace std;
#include "cryptlib.h"
#include "filters.h"
#include "files.h"
#include "modes.h"
#include "hex.h"
#include "aes.h"
#include "osrng.h"
using namespace CryptoPP;
using CryptoPP::AutoSeededRandomPool;
class cspl_crypto{
public:
cspl_crypto();
byte* generate_block(int size);
char* encrypt_rijndael(byte[], byte[], int, char*, int);
char* decrypt_rijndael(byte[], byte[], int, char*, int);
string readFile();
void writeFile(string);
};
cspl_crypto::cspl_crypto()
{
}
int main(int argc, char* argv[])
{
vector<byte> plain;
cspl_crypto ccrypto;
AutoSeededRandomPool prng;
byte key[AES::DEFAULT_KEYLENGTH];
prng.GenerateBlock(key, sizeof(key));
byte iv[AES::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));
Converting string to char *
string str("testing"); //ccrypto.readFile()
char plainArray[str.size()];
strcpy(plainArray, str.c_str());
char* cipher = ccrypto.encrypt_rijndael(key, iv, sizeof(key), plainArray,
sizeof(plainArray));
//char cipherCharArray[cipherText.size()];
// strcpy(cipherCharArray, cipherText.c_str());
char* recover = ccrypto.decrypt_rijndael(key, iv, sizeof(key), cipher,
sizeof(cipher));
// cout << "Recovered text: " << recoverText << endl;
return 0;
}
Encryption Block:
char* cspl_crypto::encrypt_rijndael(byte key[], byte iv[], int keysize, char
plainText[], int plainTextSize){
vector<byte> cipher;
std::vector<byte> plain(plainText, plainText + plainTextSize);
CBC_Mode<AES>::Encryption enc;
enc.SetKeyWithIV(key, keysize, iv, keysize);
// Make room for padding
cipher.resize(plain.size()+AES::BLOCKSIZE);
ArraySink cs(&cipher[0], cipher.size());
ArraySource(plain.data(), plain.size(), true,
new StreamTransformationFilter(enc, new Redirector(cs)));
// Set cipher text length now that its known
cipher.resize(cs.TotalPutLength());
char returnValue[cipher.size()];
copy(cipher.begin(), cipher.end(), returnValue);
return returnValue;
}
Decyption Block:
char* cspl_crypto::decrypt_rijndael(byte key[], byte iv[], int keysize, char
cipher[], int size ){
std::vector<byte> v(cipher, cipher + size);
vector<byte> recover;
CBC_Mode<AES>::Decryption dec;
dec.SetKeyWithIV(key, keysize, iv, keysize);
// Recovered text will be less than cipher text
recover.resize(v.size());
ArraySink rs(&recover[0], recover.size());
ArraySource(v.data(), v.size(), true,
new StreamTransformationFilter(dec, new Redirector(rs)));
// Set recovered text length now that its known
recover.resize(rs.TotalPutLength());
char returnValue[recover.size()];
copy(recover.begin(), recover.end(), returnValue);
return returnValue;
}
Library:
string cspl_crypto::readFile(){
string line;
string returnValue = "";
ifstream myfile ("N07.txt");
if (myfile.is_open())
{
while ( getline (myfile,line) )
{
returnValue += line + '\n';
}
myfile.close();
}
else returnValue = "Unable to open file";
return returnValue;
}
I am using Crypto++, CTR mode, to encrypt and decrypt text in C++. Everything seem to worked 99%. Ecrypting success, decrypting is give back the original text too, but I given some extra random garbage redundancy text like 'ð', at the end of the decrypted text. This extra part is random generated each time I run the code. Is there something wrong in my code?
Encrypt a string to a string
string encryptString(string plain, byte key[], int sizeKey, byte iv[], int sizeIV){
string cipher;
try{
CTR_Mode< AES >::Encryption e;
e.SetKeyWithIV(key, sizeKey, iv, sizeIV);
// The StreamTransformationFilter removes
// padding as required.
StringSource s(plain, true,
new StreamTransformationFilter(e,
new StringSink(cipher)
)
);
#if 0
StreamTransformationFilter filter(e);
filter.Put((const byte*)plain.data(), plain.size());
filter.MessageEnd();
const size_t ret = filter.MaxRetrievable();
cipher.resize(ret);
filter.Get((byte*)cipher.data(), cipher.size());
#endif
return cipher;
}
catch (const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
return NULL;
}
}
Decrypt a ciphered string to a string
string decryptString(string cipher, byte key[], int sizeKey, byte iv[], int sizeIV){
string reco ="";
try{
CTR_Mode< AES >::Decryption d;
d.SetKeyWithIV(key, sizeKey, iv, sizeIV);
StringSource s(cipher, true,
new StreamTransformationFilter(d,
new StringSink(reco)
)
);
}
catch (const CryptoPP::Exception& e)
{
cerr << e.what() << endl;
}
return reco;
}
Wrap encryptString function above.
char* encrypt(char * plainText, byte key[], int sizeKey, byte iv[], int sizeIV, long &len){
string cipher = encryptString(plainText, key, sizeKey, iv, sizeIV);
len = cipher.size() + 1;
char * writable = new char[len];
std::copy(cipher.begin(), cipher.end(), writable);
writable[len] = '\0'; // don't forget the terminating 0
return writable;
}
Wrap decryptString function above.
char* decrypt(char * cipher, byte key[], int sizeKey, byte iv[], int sizeIV, long len){
string ss(cipher, len);
long lengSS = ss.length();
string recovered = decryptString(ss, key, sizeKey, iv, sizeIV);
char * writable = new char[recovered.size() + 1];
std::copy(recovered.begin(), recovered.end(), writable);
writable[recovered.size()] = '\0'; // don't forget the terminating 0
return writable;
}
My test script is simple. Read the some.txt content ("I love you"), write it to s1.txt to check if the reading is right. Encrypt, decrypt, then write the recovered text to another file (d1.txt).
int main(int argc, char* argv[])
{
AutoSeededRandomPool prng;
byte key[AES::DEFAULT_KEYLENGTH] = { '1', '2', '3', '4', '5', '6', '7', '8', '1', '2', '3', '4', '5', '6', '7', '8' };
//prng.GenerateBlock(key, sizeof(key));
byte iv[AES::BLOCKSIZE] = { '8', '7', '6', '5', '4', '3', '2', '1', '8', '7', '6', '5', '4', '3', '2', '1' };
prng.GenerateBlock(iv, sizeof(iv));
long size = 0;
char * s1 = FileUtil::readAllByte("some.txt");
//Result: s1.txt content is "I love you"
long len = 0;
char* result1 = encrypt(s1, key, sizeof(key), iv, sizeof(iv), len);
//Result: result1 is a bunch of ciphered characters
cout << "desc" << endl;
char* recovered1 = decrypt(result1, key, sizeof(key), iv, sizeof(iv), len);
//Result: recovered1="I love youð". Generally, it has form of "I love youX"
//X can be any garbage chatacter, and each time I run the code, X is one different
//character.
}
According to the accept answer, Solution is: updated my encrypt() like this:
char* encrypt(char * plainText, byte key[], int sizeKey, byte iv[], int sizeIV, long &len){
string cipher = encryptString(plainText, key, sizeKey, iv, sizeIV);
FileUtil::writeFile("ss1.txt", cipher, cipher.length());
len = cipher.size() ;
char * writable = new char[len];
std::copy(cipher.begin(), cipher.end(), writable);
writable[len] = '\0'; // don't forget the terminating 0
FileUtil::writeFile("w1.txt",writable, len);
return writable;
}
Just allocate writeable's length = cipher's length. Set the terminator at writeble[len]
That tends to happen when you have things like buffer overruns and unterminated strings. If we look at your encrypt function we see a buffer overrun:
len = cipher.size() + 1;
char * writable = new char[len];
std::copy(cipher.begin(), cipher.end(), writable);
writable[len] = '\0';
See here you allocated len bytes, where len is one larger than cipher. But when you terminate the string, you are using len to index which is out-of-bounds.
You should either use len-1 or cipher.size() for the terminator index.
char* encrypt(char * plainText, ... );
char* decrypt(char * cipher, ... );
You can also avoid encryptString and decryptString and the extra copy. I'll show you the encrypt, the decrypt is similar.
char* encrypt(char * plainText, byte key[], int sizeKey, byte iv[], int sizeIV, long &len)
{
const unsigned long plainTextLen = len; len = 0;
const unsigned long extraLen = plainTextLen+16;
ArraySource source(plainText, plainTextLen, false);
unique_ptr<char[]> writable(new char[extraLen]);
ArraySink sink(writable, extraLen);
CTR_Mode< AES >::Encryption enc;
enc.SetKeyWithIV(key, sizeKey, iv, sizeIV);
source.Detach(new StreamTransformationFilter(enc, new Redirector(sink)));
source.PumpAll();
len = sink.TotalPutLength();
return writable.release();
}
I did not compile and run it, so you will have to clear the compiler issues in the code above. They should all be minor, like conversions and casts.
You usually don't need to worry about the NULL; just use the ptr and len. You can create a std::string from the decrypted ciphertext with string recovered = string(ptr, len);. std::string will produce a NULL when needed, but its usually not needed.
Detach is not a typo. You use it to Attach a new filter and delete a previous filter. You use it to avoid memory leaks.