How to encrypt a byte array with Crypto++ - c++

How can I encrypt a byte array with Crypto++'s RSA implementation? I already found an example for strings. But I can't find a good example how to do the same for a byte array.
This is my first attempt:
//dataSize: Size of data that is going to be send
//dataToSend Bytes to send to the user
//seedPool is an AutoSeededRandomPool
CryptoPP::RSAES_OAEP_SHA_Encryptor encryptor(publicKey);
int size = 64000;
byte * cipher = new byte(size);
CryptoPP::ArraySink* test = new CryptoPP::ArraySink(cipher, size);
CryptoPP::ArraySource as((byte*)dataToSend, dataSize, true, new CryptoPP::PK_EncryptorFilter(seedPool, encryptor, test));
int newDataSize = test->TotalPutLength();
unsigned int bytesSend = ::send(socketLink, (char *)(cipher), (int)newDataSize, 0);
delete[] cipher;
This doesn't work. TotalPutLength will always return 0 but there is data put in cipher.
What is a safe way to implement this? I don't want to be vulnerable for buffer overflows or any other attack.

byte * cipher = new byte(size);
I believe this should be:
byte * cipher = new byte[size];
Otherwise, I think you get one byte initialized to 6400 (which is truncated to 0x00).
CryptoPP::ArraySink * test = new CryptoPP::ArraySink(cipher, size);
This is kind of different. You can stay out of the memory manager if you'd like:
CryptoPP::ArraySink test(cipher, size);
int newDataSize = test->TotalPutLength();
I've never used TotalPutLength, and I did not see it documented on a BufferedTransformation or Sink. So I don't really have any advice on what its returning.
TotalPutLength is OK to use. An ArraySink could return the wrong value if the sink was full. It would happen if the array was fixed and too small for all the data. We cleared that issue at Crypto++ 5.6.3 or 5.6.4.
If you want to count the number of bytes processed (even if the sink cannot store they bytes), then you can also use a MeterFilter:
byte data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
string encoded;
MeterFilter meter( new StringSink( encoded ) );
ArraySource( data, sizeof( data ), true,
new HexEncoder(
new Redirector( meter ),
true /*UCase*/, 2 /*Group*/,
" " /*Separator*/
)
);
cout << "processed " << meter.GetTotalBytes() << " bytes" << endl;
cout << encoded << endl;
Output:
Processed 23 bytes
00 01 02 03 04 05 06 07
How can you encrypt a byte array with Cryptopp RSA implementation
Now we're talking ;) Try this from the Crypto++ wiki on RSA Encryption.
////////////////////////////////////////////////
// Generate keys
AutoSeededRandomPool rng;
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize( rng, 1536 );
RSA::PrivateKey privateKey( params );
RSA::PublicKey publicKey( params );
string plain="RSA Encryption", cipher, recovered;
////////////////////////////////////////////////
// Encryption
RSAES_OAEP_SHA_Encryptor e( publicKey );
StringSource ss1( plain, true,
new PK_EncryptorFilter( rng, e,
new StringSink( cipher )
) // PK_EncryptorFilter
); // StringSource
////////////////////////////////////////////////
// Decryption
RSAES_OAEP_SHA_Decryptor d( privateKey );
StringSource ss2( cipher, true,
new PK_DecryptorFilter( rng, d,
new StringSink( recovered )
) // PK_DecryptorFilter
); // StringSource
assert( plain == recovered );

Related

How to implement AES in crypto++ which is applied multiple times to an input? which AES mode?

For my target application I want to use AES multiple times in a row for a given input.
After encrypting it $i$ times in a row it should result in same starting input if decrypting it $i$ times backwards.
Given some example code I already implemented some working solution:
//g++ -o aestest aestest.cpp -lcryptopp
#include <iostream>
#include <iomanip>
#include "cryptopp/modes.h"
#include "cryptopp/aes.h"
#include "cryptopp/filters.h"
#include <cassert>
void dispBA(std::vector<byte> &bav, std::string text =""){
std::cout << text << " : size "<< bav.size() <<" byte"<< std::endl;
for( long unsigned int i = 0; i < bav.size(); i++ ) {
std::cout << std::dec<< (int)((static_cast<byte>(bav[i]))) << ", ";
}
std::cout << std::endl << std::endl;
}
void encryptBV(CryptoPP::CBC_Mode_ExternalCipher::Encryption &cbcEncryption, std::vector<byte> &bytesIn, std::vector<byte> &bytesCrypOut, bool disp=false){
CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::ArraySink( &bytesCrypOut[0],bytesCrypOut.size() ), CryptoPP::StreamTransformationFilter::NO_PADDING );
stfEncryptor.Put( &bytesIn[0], bytesIn.size() );
stfEncryptor.MessageEnd();
if(disp)dispBA(bytesCrypOut, "Cipher numbers");
}
void decryptBV(CryptoPP::CBC_Mode_ExternalCipher::Decryption &cbcDecryption, std::vector<byte> &bytesCrypIn, std::vector<byte> &bytesDecrOut, bool disp=false){
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::ArraySink( &bytesDecrOut[0], bytesDecrOut.size() ), CryptoPP::StreamTransformationFilter::NO_PADDING );
stfDecryptor.Put( &bytesCrypIn[0], bytesCrypIn.size() );
stfDecryptor.MessageEnd();
if(disp)dispBA(bytesDecrOut, "Decrypted numbers");
}
int main(int argc, char* argv[]) {
//Key 32 bytes -> aes256
//block size always 16
const int myKeysize = 32;
byte key[ myKeysize ], iv[ CryptoPP::AES::BLOCKSIZE ];
memset( key, 0x00, myKeysize ); // no key set yet
memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE );
//encryption
std::vector<byte> bytesCryp(myKeysize);
CryptoPP::AES::Encryption aesEncryption(key, myKeysize);
CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( aesEncryption, iv );
//decryption
std::vector<byte> bytesDecr(myKeysize);
CryptoPP::AES::Decryption aesDecryption(key, myKeysize);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, iv );
std::vector<byte> bytesIn = {0 ,1, 2, 3, 4, 5 ,6 ,7 ,8 ,9 ,10 ,11, 12, 13 ,14 ,15 ,16, 17, 18 ,19 ,20 ,21 ,22 ,23 ,24 ,25 ,26 ,27 ,28 ,29 ,30,31};
assert(bytesIn.size()== myKeysize);
dispBA(bytesIn, "Starting number");
//-----does work for single en/decryption
// Create Cipher numbers
//encryptBV(cbcEncryption, bytesIn, bytesCryp, true);
// Decrypt
//decryptBV(cbcDecryption, bytesCryp, bytesDecr, true);
int count = 42;
for(int i=0; i < count; i++){
cbcEncryption.SetKeyWithIV(key, myKeysize, iv ); // ++
encryptBV(cbcEncryption, bytesIn, bytesCryp);
cbcEncryption.SetKeyWithIV(key, myKeysize, iv ); // ++
encryptBV(cbcEncryption, bytesCryp, bytesIn);
}
for(int i=0; i<count; i++){
cbcDecryption.SetKeyWithIV(key, myKeysize, iv ); // ++
decryptBV(cbcDecryption, bytesIn, bytesCryp);
cbcDecryption.SetKeyWithIV(key, myKeysize, iv ); // ++
decryptBV(cbcDecryption, bytesCryp, bytesIn);
}
dispBA(bytesIn, "end"); // this does only return the correct value if lines marked with ++ are added
//-----but it does also work if I repeat them without changing the inputs--- ["W2"]
// Create Cipher numbers
//encryptBV(cbcEncryption, bytesIn, bytesCryp, true);
//encryptBV(cbcEncryption, bytesIn, bytesCryp, true);
// Decrypt
//decryptBV(cbcDecryption, bytesCryp, bytesDecr, true);
//decryptBV(cbcDecryption, bytesCryp, bytesDecr, true);
return 0;
}
However currently this only works if I reset the key after every message (or generate new ExternalCipher). I though the key is only changed during the rounds at a single (128,196,256-bit) message (or inside a long) and not continue through messages.
Is there a better way to do this? Or why this happen and does not happen if I don't change the input (see ["W2"])?
Here I found different mode of operation. I guess those are different implementation of AES. Should I use a certain? (edit: ECB? its not supported afaik)
There is no ECB mode listed at crypto++ Modes of Operation but! crypto++ has some shown here with this I can do some small rewrite:
//ecb
void encryptBV(CryptoPP::ECB_Mode< CryptoPP::AES >::Encryption &ecbEncryption, std::vector<byte> &bytesIn, std::vector<byte> &bytesCrypOut, bool disp=false){
CryptoPP::StreamTransformationFilter stfEncryptor(ecbEncryption, new CryptoPP::ArraySink( &bytesCrypOut[0],bytesCrypOut.size() ), CryptoPP::StreamTransformationFilter::NO_PADDING );
stfEncryptor.Put( &bytesIn[0], bytesIn.size() );
stfEncryptor.MessageEnd();
if(disp)dispBA(bytesCrypOut, "Cipher number");
}
void decryptBV(CryptoPP::ECB_Mode< CryptoPP::AES >::Decryption &ecbDecryption, std::vector<byte> &bytesCrypIn, std::vector<byte> &bytesDecrOut, bool disp=false){
CryptoPP::StreamTransformationFilter stfDecryptor(ecbDecryption, new CryptoPP::ArraySink( &bytesDecrOut[0], bytesDecrOut.size() ), CryptoPP::StreamTransformationFilter::NO_PADDING );
stfDecryptor.Put( &bytesCrypIn[0], bytesCrypIn.size() );
stfDecryptor.MessageEnd();
if(disp)dispBA(bytesDecrOut, "Decrypted number");
}
int main(int argc, char* argv[]) {
//Key 32 bytes -> aes256
//block size always 16
const int myKeysize = 32;
byte key[ myKeysize ], iv[ CryptoPP::AES::BLOCKSIZE ];
memset( key, 0x00, myKeysize ); // no key set yet
memset( iv, 0x00, CryptoPP::AES::BLOCKSIZE );
CryptoPP::ECB_Mode< CryptoPP::AES >::Encryption ecbEncryption;
ecbEncryption.SetKey( key, myKeysize);
CryptoPP::ECB_Mode< CryptoPP::AES >::Decryption ecbDecryption;
ecbDecryption.SetKey( key, myKeysize);
std::vector<byte> bytesCryp(myKeysize);
std::vector<byte> bytesDecr(myKeysize);
std::vector<byte> bytesIn = {0 ,1, 2, 3, 4, 5 ,6 ,7 ,8 ,9 ,10 ,11, 12, 13 ,14 ,15 ,16, 17, 18 ,19 ,20 ,21 ,22 ,23 ,24 ,25 ,26 ,27 ,28 ,29 ,30,31};
dispBA(bytesIn, "Starting number");
//test single encrypt, decrypt
encryptBV(ecbEncryption, bytesIn, bytesCryp, true);
decryptBV(ecbDecryption, bytesCryp, bytesDecr, true);
int count = 42;
for(int i=0; i<count; i++){
encryptBV(ecbEncryption, bytesIn, bytesCryp);
encryptBV(ecbEncryption, bytesCryp, bytesIn);
}
for(int i=0; i<count; i++){
decryptBV(ecbDecryption, bytesIn, bytesCryp);
decryptBV(ecbDecryption, bytesCryp, bytesIn);
}
dispBA(bytesIn, "end");
}
It also runs faster (about 60%).
Warning: this is not secure for normal AES usage.
(if someone else found some better way I can change the correct answer)

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.

Decrypted image is not same as original image

I just started working on cryptopp library. I have a image buffer and i want to encrypt with some key and then decrypt later but facing issue, decrypted and original images are not same.
I am not sure weather issue in encryption or not could some one help me out of this.
using qt creator
Code:
AutoSeededRandomPool prng;
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
prng.GenerateBlock( key, key.size() );
byte ctr[ AES::BLOCKSIZE ];
prng.GenerateBlock( ctr, sizeof(ctr) );
string cipher, encoded, recovered;
QFile file("original.png");
if(!file.open(QIODevice::ReadOnly)){
cout << "could not open the file"<< endl;
}
QByteArray buffer = file.readAll();
qDebug()<<"buffer length"<<buffer.length();
file.close();
try
{
CTR_Mode< AES >::Encryption e;
e.SetKeyWithIV( (byte*)key.data(), key.size(), ctr );
StringSource ss1( buffer, true,
new StreamTransformationFilter( e,
new StringSink( cipher )
)
);
}
catch( CryptoPP::Exception& e )
{
cerr << e.what() << endl;
exit(1);
}
qDebug()<<"cipher length "<<cipher.length();
try
{
CTR_Mode< AES >::Decryption d;
d.SetKeyWithIV( (byte*)key.data(), key.size(), ctr );
StringSource ss3( cipher, true,
new StreamTransformationFilter( d,
new StringSink( recovered )
)
);
}
catch( CryptoPP::Exception& e )
{
cerr << e.what() << endl;
exit(1);
}
qDebug()<<"recovered length "<<recovered.length();
QFile ouput("recovered.png");
if(ouput.open(QIODevice::WriteOnly)){
ouput.write(recovered.data(), recovered.size());
ouput.close();
}
response:
buffer length 538770
cipher length 8
recovered length 8
why my cipher length is 8 only.
QFile ouput("recovered.png");
if(ouput.open(QIODevice::WriteOnly)){
ouput.write(recovered.c_str());
ouput.close();
}
Let me throw mine in the pot.... You are treating binary data as a C-String, which means reading/writing stops at the first NULL character. You should use an overload that takes a pointer and size. Maybe something like:
ouput.write(recovered.data(), recovered.size());
(After code edits)
QByteArray buffer = file.readAll();
...
StringSource ss1( buffer, true, ...);
That's probably not producing expected results. Maybe you should try:
QByteArray buffer = file.readAll();
...
StringSource ss1( buffer.data(), buffer.size(), true, ...);
The above StringSource overload, with a pointer and size, is the one you should prefer in this case. Its the exact case it was designed for, and it saves the extra copy of buffer.
You could even use a FileSource and FileSink` to integrate with Crypto++::
FileSource ifile("original.png", true,
new StreamTransformationFilter(e,
new StringSink( cipher )
)
);
Also, Barmak is correct about:
In your case you call it ctr but it seems to be uninitialized...
Though it is uninitialized, the same [unknown] value is used for encryption and decryption, so the problem did not show its head. It will show up later, like when you encrypt on one machine and decrypt on another.
You should follow Barmak advice an initialize it. Maybe something like:
byte ctr[16];
OS_GenerateRandomBlock(false, ctr, sizeof(ctr));
OS_GenerateRandomBlock is discussed on Crypto++ wiki at RandomNumberGenerator.
You can usually send the counter in the plaintext with the message because its usually considered a public value. But it really depends on your security model.
Be sure to never reuse a security context in counter mode. Each message must be encrypted under a unique security context. The security context is the {key,ctr} pair.
You can also print the counter with:
byte ctr[16];
OS_GenerateRandomBlock(false, ctr, sizeof(ctr));
HexEncoder encoder(new FileSink(cout));
cout << "Counter: ";
encoder.Put(ctr, sizeof(ctr));
encoder.MessageEnd();
cout << endl;
The code above simply hex-encodes the raw byte array nd then prints it to stdout.
(comment) string key = "7D9BB722DA2DC8674E08C3D44AAE976F"; - You probably want a binary string; not an ASCII string. For Crypto++, see HexDecoder on the Crypto++ wiki. I'm not sure what QT offers for the service.
Since there's more space here... this is one of the things you could do:
string key, encodedKey = "7D9BB722DA2DC8674E08C3D44AAE976F";
StringSource ss(encodedKey, true, new HexDecoder(key));
After the statements execute, the string key will be binary data.
In your code you are converting the file content to Base64,
encrypting it, decrypting it and saving it to a file (thus saving a the png file as Base64)
You should encrypt the raw file data (and not the Base64 encoded one).
Edit: After your edit I don't know what fails. Please try the following function, which works for me. Uses FileSource and FileSink, but should work with StringSource and StringSink accordingly.
bool encryptdecrypt(const std::string& filename)
{
std::string key = "7D9BB722DA2DC8674E08C3D44AAE976F";
byte ctr[ CryptoPP::AES::BLOCKSIZE ];
std::string cipher;
try
{
CryptoPP::CTR_Mode< CryptoPP::AES >::Encryption e;
e.SetKeyWithIV( (byte*)key.data(), key.size(), ctr );
CryptoPP::FileSource( filename.c_str(), true,
new CryptoPP::StreamTransformationFilter( e,
new CryptoPP::StringSink( cipher )
)
);
}
catch( CryptoPP::Exception& e )
{
std::cerr << e.what() << std::endl;
return false;
}
try
{
CryptoPP::CTR_Mode< CryptoPP::AES >::Decryption d;
d.SetKeyWithIV( (byte*)key.data(), key.size(), ctr );
CryptoPP::StringSource( cipher, true,
new CryptoPP::StreamTransformationFilter( d,
new CryptoPP::FileSink( ( "decrypted_" + filename ).c_str() )
)
);
}
catch( CryptoPP::Exception& e )
{
std::cerr << e.what() << std::endl;
return false;
}
return true;
}
I found the issue with QByteArray buffer. i just converted to std::string its working.
QByteArray buffer = file.readAll();
// string
std::string stdString(buffer.data(), buffer.length());
//used stdString instead of buffer in pipeline
StringSource ss1(stdString, true,
new StreamTransformationFilter( e,
new StringSink( cipher )
)
);

Using the CryptoPP vectors in the GCM sample code

I have downloaded the CryptoPP library and I am able to run the sample codes and get results (for CCM and GCM modes).
The next step for me is to try out the test vectors for each of these modes. From my understanding im suppose to try out the different keys, IVs and plaintexts as specified in the test vectors. Then I have to verify that the expected results are also specified in each vector.
What I can seem to understand is how to input these keys and IVs for the vectors. From the code as shown below, it seems to be using a random key.
Preferably I would like to input the keys and IVs from command prompt and then run the test code. Just setting the vectors from the code in Visual Studio would do though.
Please find the sample code and one of the vectors below:
Sample Code:
AutoSeededRandomPool prng;
SecByteBlock key( AES::DEFAULT_KEYLENGTH );
prng.GenerateBlock( key, key.size() );
byte iv[ AES::BLOCKSIZE * 16 ];
prng.GenerateBlock( iv, sizeof(iv) );
const int TAG_SIZE = 12;
// Plain text
string pdata="Authenticated Encryption";
// Encrypted, with Tag
string cipher, encoded;
// Recovered plain text
string rpdata;
/*********************************\
\*********************************/
try
{
GCM< AES >::Encryption e;
e.SetKeyWithIV( key, key.size(), iv, sizeof(iv) );
StringSource( pdata, true,
new AuthenticatedEncryptionFilter( e,
new StringSink( cipher ), false, TAG_SIZE
) // AuthenticatedEncryptionFilter
); // StringSource
}
catch( CryptoPP::Exception& e )
{
cerr << e.what() << endl;
exit(1);
}
/*********************************\
\*********************************/
try
{
GCM< AES >::Decryption d;
d.SetKeyWithIV( key, key.size(), iv, sizeof(iv) );
AuthenticatedDecryptionFilter df( d,
new StringSink( rpdata ),
DEFAULT_FLAGS, TAG_SIZE
); // AuthenticatedDecryptionFilter
// The StringSource dtor will be called immediately
// after construction below. This will cause the
// destruction of objects it owns. To stop the
// behavior so we can get the decoding result from
// the DecryptionFilter, we must use a redirector
// or manually Put(...) into the filter without
// using a StringSource.
StringSource( cipher, true,
new Redirector( df /*, PASS_EVERYTHING */ )
); // StringSource
// If the object does not throw, here's the only
// opportunity to check the data's integrity
if( true == df.GetLastResult() ) {
cout << "recovered text: " << rpdata << endl;
}
}
catch( CryptoPP::Exception& e )
{
cerr << e.what() << endl;
exit(1);
}
One of the vectors:
GCM Test Case #14 (AES-256)
Variable Value
-------------------------------------------------
K : 00000000000000000000000000000000
: 00000000000000000000000000000000
P : 00000000000000000000000000000000
IV : 000000000000000000000000
H : dc95c078a2408989ad48a21492842087
Y_0 : 00000000000000000000000000000001
E(K,Y_0) : 530f8afbc74536b9a963b4f1c4cb738b
Y_1 : 00000000000000000000000000000002
E(K,Y_1) : cea7403d4d606b6e074ec5d3baf39d18
X_1 : fd6ab7586e556dba06d69cfe6223b262
len(A)||len(C) : 00000000000000000000000000000080
GHASH(H,A,C) : 83de425c5edc5d498f382c441041ca92
C : cea7403d4d606b6e074ec5d3baf39d18
T : d0d1c8a799996bf0265b98b5d48ab919
I have downloaded the CryptoPP library and I am able to run the sample codes and get results (for CCM and GCM modes).
Crypto++ does not use the examples from its wiki when running its self tests. The self test code is much more hairier.
What I can seem to understand is how to input these keys and IVs for the vectors. From the code as shown below, it seems to be using a random key.
The Crypto++ test vectors are located in <crypto++ dir>/TestVectors. I don't believe the vector you show in your question is from Crypto++. For example, here's from <crypto++ dir>/TestVectors/gcm.txt:
AlgorithmType: AuthenticatedSymmetricCipher
Name: AES/GCM
Source: aes-modes-src-07-10-08/Testvals/gcm.1, Basic Tests for GCM (compiled by B. R. Gladman)
Key: 00000000000000000000000000000000
IV: 000000000000000000000000
MAC: 00000000000000000000000000000000
Test: NotVerify
Key: 00000000000000000000000000000000
IV: 000000000000000000000000
MAC: 58e2fccefa7e3061367f1d57a4e7455a
Test: Encrypt
Key: 00000000000000000000000000000000
IV: 000000000000000000000000
Plaintext: 00000000000000000000000000000000
Ciphertext: 0388dace60b6a392f328c2b971b2fe78
MAC: ab6e47d42cec13bdf53a67b21257bddf
Test: Encrypt
...
You can see how the Crypto++ test suite consumes it when you use the cryptest.exe v command. The source files that execute the self tests are validat1.cpp, validat2.cpp and validat3.cpp. The GCM testing starts in validat1.cpp on line 95:
pass=ValidateGCM() && pass;
Here's ValidateGCM around line 1395:
bool ValidateGCM()
{
cout << "\nAES/GCM validation suite running...\n";
cout << "\n2K tables:";
bool pass = RunTestDataFile("TestVectors/gcm.txt", MakeParameters(Name::TableSize(), (int)2048));
cout << "\n64K tables:";
return RunTestDataFile("TestVectors/gcm.txt", MakeParameters(Name::TableSize(), (int)64*1024)) && pass;
}
Its a real pain to untangle RunTestDataFile, TestDataFile and TestAuthenticatedSymmetricCipher (and friends). They are implemented in datatest.cpp. The pain point is TestAuthenticatedSymmetricCipher around line 450 of datatest.cpp.
I usually go to the applicable standard, pull the test vectors, and then write my own self tests. In the case of deterministic encryption like AES/GCM, you can write a Known Answer Test (KAT). For non-deterministic tests, you will need to write a Pairwise Consistency Test (PCT). Essentially, you verify you can round trip data from a public/private key pair operation, like a DH or RSA key.

AES CBC decryption works, CTR does not

I am trying to decrypt data using AES CBC and CTR mode. The ciphertext has been prepended with the 16 byte IV.
I have my ciphertext data in the following format:
vector<vector<byte>> CBCMessages;
vector<vector<byte>> CBCKeys;
vector<vector<byte>> CTRMessages;
vector<vector<byte>> CTRKeys;
I am using Crypto++ to decrypt the data. This is my code:
for (int i = 0; i < CBCMessages.size(); i++)
{
std::string decryptedtext;
// split IV from ciphertext
byte iv[16];
std::copy(CBCMessages[i].begin(), CBCMessages[i].begin()+16, iv);
CBCMessages[i].erase(CBCMessages[i].begin(), CBCMessages[i].begin()+16);
// this block works fine
AES::Decryption aesDecryption(&(CBCKeys[i][0]), CBCKeys[i].size());
CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, iv );
StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink( decryptedtext ) );
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( &(CBCMessages[i][0]) ), CBCMessages[i].size() );
stfDecryptor.MessageEnd();
std::cout << decryptedtext << std::endl;
}
for (int i = 0; i < CTRMessages.size(); i++)
{
std::string decryptedtext;
// split IV from ciphertext
byte iv[16];
std::copy(CTRMessages[i].begin(), CTRMessages[i].begin()+16, iv);
CTRMessages[i].erase(CTRMessages[i].begin(), CTRMessages[i].begin()+16);
// this block produces junk
AES::Decryption aesDecryption(&(CTRKeys[i][0]), CTRKeys[i].size());
CTR_Mode_ExternalCipher::Decryption ctrDecryption( aesDecryption, iv );
StreamTransformationFilter stfDecryptor(ctrDecryption, new CryptoPP::StringSink( decryptedtext ) );
stfDecryptor.Put( reinterpret_cast<const unsigned char*>( &(CTRMessages[i][0]) ), CTRMessages[i].size() );
stfDecryptor.MessageEnd();
std::cout << decryptedtext << std::endl;
// try again with different method - this works fine
decryptedtext.clear();
CTR_Mode< AES >::Decryption d;
d.SetKeyWithIV( &(CTRKeys[i][0]), CTRKeys[i].size(), iv, 16 );
StringSource( reinterpret_cast<const unsigned char*>( &(CTRMessages[i][0]) ), CTRMessages[i].size(), true,
new StreamTransformationFilter( d,
new StringSink( decryptedtext )
)
);
std::cout << decryptedtext << std::endl;
}
As you can see, the middle block (first block for CTR decryption) produces junk output. Note that this block should actually be pretty identical to the block used for CBC decryption.
The block used for CBC decryption is basically copied from this FAQ entry (answer by 2005-Oct-21 10:38am jeffrey). I have then altered this block to use it for CTR decryption which is when it failed to work. The second CTR block is inspired by the "Sample Program" section here.
What seems to be the problem in the first CTR code block?
It is probably because
AES::Decryption aesDecryption(&(CTRKeys[i][0]), CTRKeys[i].size());
https://upload.wikimedia.org/wikipedia/commons/3/3c/CTR_decryption_2.svg
CTR Mode needs AES::Encryption to decrypt the ciphertest
https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation