I've implemented a C++ wrapper library for Crypto++ v5.6.2 and have a question about combinations of symmetric algorithms (e. g. Blowfish) and block modes (e. g. GCM).
I am able to encrypt and decrypt data via Blowfish/EAX, but I can't achieve the same by using Blowfish/GCM. AES/EAX and AES/GCM both work.
The following simple application demonstrates my problem:
#include <iostream>
#include <string>
#include "cryptopp/blowfish.h"
#include "cryptopp/filters.h"
#include "cryptopp/eax.h"
#include "cryptopp/gcm.h"
#include "cryptopp/osrng.h"
#include "cryptopp/hex.h"
std::string encrypt(
CryptoPP::AuthenticatedSymmetricCipher &encryption,
std::string const kPlainText,
CryptoPP::SecByteBlock const kKey,
unsigned const char * kIV) {
std::string cipher_text;
// TODO Is this the source of the problem?
// BlockSize always returns 0 which leads to an exception if GCM block mode is used!
std::cout << encryption.BlockSize() << " bytes" << std::endl;
encryption.SetKeyWithIV(
kKey,
kKey.size(),
kIV
);
CryptoPP::StringSink *string_sink = new CryptoPP::StringSink(cipher_text);
CryptoPP::BufferedTransformation *transformator = NULL;
// The AuthenticatedEncryptionFilter adds padding as required.
transformator = new CryptoPP::AuthenticatedEncryptionFilter(
encryption,
string_sink);
bool const kPumpAll = true;
CryptoPP::StringSource(
kPlainText,
kPumpAll,
transformator);
return cipher_text;
}
std::string decrypt(
CryptoPP::AuthenticatedSymmetricCipher &decryption,
std::string const kCipherText,
CryptoPP::SecByteBlock const kKey,
unsigned const char * kIV) {
std::string recovered_plain_text;
decryption.SetKeyWithIV(
kKey,
kKey.size(),
kIV);
CryptoPP::StringSink *string_sink = new CryptoPP::StringSink(
recovered_plain_text);
CryptoPP::BufferedTransformation *transformator = NULL;
CryptoPP::AuthenticatedDecryptionFilter *decryption_filter = NULL;
decryption_filter = new CryptoPP::AuthenticatedDecryptionFilter(
decryption,
string_sink);
transformator = new CryptoPP::Redirector(*decryption_filter);
bool const kPumpAll = true;
CryptoPP::StringSource(
kCipherText,
kPumpAll,
transformator);
return recovered_plain_text;
}
int main() {
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::SecByteBlock key(CryptoPP::Blowfish::DEFAULT_KEYLENGTH);
prng.GenerateBlock(key, key.size());
byte iv[CryptoPP::Blowfish::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));
// Creates templated mode objects of block ciphers.
// This works...
// CryptoPP::EAX<CryptoPP::Blowfish>::Encryption encryption;
// CryptoPP::EAX<CryptoPP::Blowfish>::Decryption decryption;
// This does NOT work...
CryptoPP::GCM<CryptoPP::Blowfish>::Encryption encryption;
CryptoPP::GCM<CryptoPP::Blowfish>::Decryption decryption;
std::string plain_text = "Block Mode Test";
std::string cipher_text = encrypt(encryption, plain_text, key, iv);
// terminate called after throwing an instance of 'CryptoPP::InvalidArgument'
// what(): Blowfish/GCM: block size of underlying block cipher is not 16
std::cout << "cipher text: " << std::hex << cipher_text << std::endl;
std::cout << "recovered plain text: " << decrypt(decryption, cipher_text, key, iv) << std::endl;
}
A CryptoPP::InvalidArgument exception is thrown if running the code above with the following text:
Blowfish/GCM: block size of underlying block cipher is not 16
But when running the code instead with the block mode EAX, no exception is thrown. So my questions are:
Does GCM only work with AES? Can GCM also be used with Blowfish or 3DES?
Is there a matrix available which lists all possible combinations of symmetric algorithms with block modes?
Or is this a bug in Crypto++? Because the method BlockSize() always returns 0 but the exception is only raised if using Blowfish (or 3DES) instead of AES. This seems to raise the exception mentioned.
GCM has been designed to work with 128-bit (=16 byte) block size only. You can find this in the original paper in Section 5.1.
Blowfish is a 64-bit block size algorithm, so the two are not compatible as an "out-of-the-box" authenticated encryption combination. The same is true for 3DES. The exception is not a bug in Crypto++.
GCM will work with other Crypto++ objects that have 128-bit block sizes. They include AES, Cast-256, Rijndael Cameilla, MARS, Serpent and Twofish. A table of the block sizes is available at Applied Crypto++: Block Ciphers.
GCM will not work with larger block sizes either. For example, Rijndael (the parent of AES) offers 192-bit and 256-bit block sizes (AES only specifies the 128-bit block size). GCM will not work with the larger block sizes. And the same is true for SHACAL-2, with a 256-bit block size.
Crypto++'s BlockSize() sometimes returns 0 (it has to do with the template parameters). Instead, use the compile time constants like AES::BLOCKSIZE, Camellia::BLOCKSIZE and Rijndael::BLOCKSIZE. This could be considered a bug.
Related
I'm trying to encrypt 320 bytes of binary data using AES-128 in CBC mode and store the cipher into a file. The output file should have been of 320 bytes, but I got 336 bytes. Here is my code:
#include <iostream>
#include <fstream>
#include <crypto++/aes.h>
#include <crypto++/modes.h>
#include <crypto++/base64.h>
#include <crypto++/sha.h>
#include <cryptopp/osrng.h>
#include <crypto++/filters.h>
#include <crypto++/files.h>
namespace CryptoPP
{
using byte = unsigned char;
}
void myAESTest()
{
std::string password = "testPassWord";
// hash the password string
// -------------------------------
CryptoPP::byte key[CryptoPP::AES::DEFAULT_KEYLENGTH], iv[CryptoPP::AES::BLOCKSIZE];
CryptoPP::byte passHash[CryptoPP::SHA256::DIGESTSIZE];
CryptoPP::SHA256().CalculateDigest(passHash, (CryptoPP::byte*) password.data(), password.size());
std::memcpy(key, passHash, CryptoPP::AES::DEFAULT_KEYLENGTH);
std::memcpy(iv, passHash+CryptoPP::AES::DEFAULT_KEYLENGTH, CryptoPP::AES::BLOCKSIZE);
// encrypt
// ---------------------------------
int chunkSize = 20*CryptoPP::AES::BLOCKSIZE;
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor;
encryptor.SetKeyWithIV(key, sizeof(key), iv);
std::ofstream testOut("./test.enc", std::ios::binary);
CryptoPP::FileSink outSink(testOut);
CryptoPP::byte message[chunkSize];
CryptoPP::StreamTransformationFilter stfenc(encryptor, new CryptoPP::Redirector(outSink));
for(int i = 0; i < chunkSize; i ++)
{
message[i] = (CryptoPP::byte)i;
}
stfenc.Put(message, chunkSize);
stfenc.MessageEnd();
testOut.close();
// decrypt
// ------------------------------------
// Because of some unknown reason increase chuksize by 1 block
// chunkSize+=16;
CryptoPP::byte cipher[chunkSize], decrypted[chunkSize];
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
decryptor.SetKeyWithIV(key, sizeof(key), iv);
std::ifstream inFile("./test.enc", std::ios::binary);
inFile.read((char *)cipher, chunkSize);
CryptoPP::ArraySink decSink(decrypted, chunkSize);
CryptoPP::StreamTransformationFilter stfdec(decryptor, new CryptoPP::Redirector(decSink));
stfdec.Put(cipher, chunkSize);
stfdec.MessageEnd();
inFile.close();
for(int i = 0; i < chunkSize; i++)
{
std::cout << (int)decrypted[i] << ' ';
}
std::cout << std::endl;
}
int main(int argc, char* argv[])
{
myAESTest();
return 0;
}
I'm not able to understand how the last 16 bytes are generated. If I choose to ignore the last 16 bytes in the decryption, CryptoPP throws CryptoPP::InvalidCiphertext error:
terminate called after throwing an instance of 'CryptoPP::InvalidCiphertext'
what(): StreamTransformationFilter: invalid PKCS #7 block padding found
I'm not able to understand how the last 16 bytes are generated. If I choose to ignore the last 16 bytes in the decryption, Crypto++ throws InvalidCiphertext error
The last 16 bytes are padding. Padding is added by the StreamTransformationFilter filter; see StreamTransformationFilter Class Reference in the manual. Though not obvious, DEFAULT_PADDING is PKCS_PADDING for ECB_Mode and CBC_Mode. It is NO_PADDING for other modes like OFB_Mode and CTR_Mode.
You only need to specify NO_PADDING for both the encryption and decryption filters. However, you have to ensure the plaintext and ciphertext are a multiple of the blocksize, which is 16 for AES.
You can sidestep the blocksize restriction by switching to another mode like CTR_Mode. But then you have to be very careful of key or IV reuse, which may be difficult due to the password derivation scheme you are using.
So instead of:
CBC_Mode<AES>::Encryption encryptor;
...
StreamTransformationFilter stfenc(encryptor, new Redirector(outSink));
Use:
CBC_Mode<AES>::Encryption encryptor;
...
StreamTransformationFilter stfenc(encryptor, new Redirector(outSink), NO_PADDING);
Also see CBC_Mode on the Crypto++ wiki. You might also be interested in Authenticated Encryption on the wiki.
For this, you can also:
#ifndef CRYPTOPP_NO_GLOBAL_BYTE
namespace CryptoPP
{
using byte = unsigned char;
}
#endif
CRYPTOPP_NO_GLOBAL_BYTE is defined after the C++17 std::byte fixes. If CRYPTOPP_NO_GLOBAL_BYTE is not defined, then byte is in the global namespace (Crypto++ 5.6.5 and earlier). If CRYPTOPP_NO_GLOBAL_BYTE is defined, then byte is in the CryptoPP namespace (Crypto++ 6.0 and later).
For this:
std::ofstream testOut("./test.enc", std::ios::binary);
FileSink outSink(testOut);
You can also do:
FileSink outSink("./test.enc");
For this:
SHA256().CalculateDigest(passHash, (byte*) password.data(), password.size());
std::memcpy(key, passHash, AES::DEFAULT_KEYLENGTH);
std::memcpy(iv, passHash+AES::DEFAULT_KEYLENGTH, AES::BLOCKSIZE);
You might consider using HKDF as a derivation function. Use the one password but two different labels to ensure independent derivation. One label might be the string "AES key derivation version 1" and the other label might be "AES iv derivation version 1".
The label would be used as the info parameter for DeriveKey. You just need to call it twice, once for the key and once for the iv.
unsigned int DeriveKey (byte *derived, size_t derivedLen,
const byte *secret, size_t secretLen,
const byte *salt, size_t saltLen,
const byte *info, size_t infoLen) const
secret is the password. If you have salt then use it. Otherwise HKDF uses a default salt.
Also see HKDF on the Crypto++ wiki.
Finally, regarding this:
You can sidestep the blocksize restriction by switching to another mode like CTR_Mode. But then you have to be very careful of key or IV reuse, which may be difficult due to the password derivation scheme you are using.
You might also consider an Integrated Encryption Scheme, like Elliptic Curve Integrated Encryption Scheme. It is IND-CCA2, which is a strong notion of security. Everything you need is packaged into the encryption scheme.
Under ECIES each user gets a public/private keypair. Then, a large random secret is used as a seed for the AES key, iv and mac key. The the plaintext is encrypted and authenticated. Finally, the seed is encrypted under the user's public key. The password is still used, but it is used to decrypt the private key.
What if I want to encrypt data, using the Crypto++ library and having a user-defined password that is shorter than 32 Byte?
Right now I have the following code:
byte passwordBytes[AES::MAX_KEYLENGTH];
byte ivBytes[AES::BLOCKSIZE];
std::string textToEncrypt("encryptMe");
std::string aesKey("passwordFromUser");
std::string ivText("Iv16BytesOfText...");
memset(passwordBytes, 0, sizeof(passwordBytes)); //fill with zeroes first
memcpy(passwordBytes, aesKey.data(), aesKey.size()); //fill with key data
memcpy(ivBytes, ivText.data(), CryptoPP::AES::BLOCKSIZE); //fill iv bytes
CTR_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV(passwordBytes, sizeof(passwordBytes), ivBytes);
StringSource encryptor(textToEncrypt, true,
new StreamTransformationFilter(encryption,
new StringSink(verschluesselterText)
,StreamTransformationFilter::NO_PADDING
)
);
As you can see, aesKey is shorter than 32 Bytes.
To apply the full 32 Bytes to the encrypting function, I just fill out the unused space with zeroes, but that doesn't seem to be the best solution to me.
Am I missing something regarding creating an AES Key? With a user-defined password?
My second question, what if the user chooses a password that is longer then 32 Byte? In My case, the password would be truncated, which doesn't sound right to me.
Thanks for your help!
What if i want to encrypt data, using the Crypto++ library and having a user defined password that is shorter then 32 Byte?
Use a key derivation function (KDF) to digest the password. The modern one is Krawczyk and Eronen's HKDF using the Extract-then-Expand model. The paper is located at Cryptographic Extraction and Key Derivation: The HKDF Scheme.
You should consider using it for the IV, too. Rather than deriving 32 bytes (AES::MAX_KEYLENGTH), derive 48 bytes (AES::MAX_KEYLENGTH+AES::BLOCKSIZE) instead. The IV in your design can then be used for the salt parameter to the KDF.
Maybe something like:
#include <iostream>
#include <string>
using namespace std;
#include "cryptlib.h"
#include "aes.h"
#include "sha.h"
#include "hkdf.h"
#include "modes.h"
#include "filters.h"
using namespace CryptoPP;
int main(int argc, char* argv[])
{
SecByteBlock key(AES::MAX_KEYLENGTH+AES::BLOCKSIZE);
string password("passwordFromUser"), iv("<random value>"), message("encryptMe");
string encrypted, recovered;
try
{
HKDF<SHA256> hkdf;
hkdf.DeriveKey(key, key.size(), (const byte*)password.data(), password.size(), (const byte*)iv.data(), iv.size(), NULL, 0);
///////////////////////////////////////////////////////////////////////
CTR_Mode<AES>::Encryption encryption;
encryption.SetKeyWithIV(key, AES::MAX_KEYLENGTH, key+AES::MAX_KEYLENGTH);
StringSource encryptor(message, true,
new StreamTransformationFilter(encryption,
new StringSink(encrypted))
);
///////////////////////////////////////////////////////////////////////
CTR_Mode<AES>::Decryption decryption;
decryption.SetKeyWithIV(key, AES::MAX_KEYLENGTH, key+AES::MAX_KEYLENGTH);
StringSource decryptor(encrypted, true,
new StreamTransformationFilter(decryption,
new StringSink(recovered))
);
cout << "Message: " << message << endl;
cout << "Recovered: " << recovered << endl;
}
catch(const Exception& ex)
{
cerr << ex.what() << endl;
return 1;
}
return 0;
}
When using the encryption method above, you have to track the {iv,message} pair. The IV is needed to ensure uniqueness per-message since the password effectively fixes the AES key.
What if the user chooses a password that is longer then 32 Byte? In My case, the password would by truncated, which doesn't sound right to me.
A KDF handles it for you. It extracts the entropy regardless of how little or how much.
StringSource encryptor(textToEncrypt, true,
new StreamTransformationFilter(encryption,
new StringSink(verschluesselterText),
StreamTransformationFilter::NO_PADDING
)
There's no need to specify the padding mode. Also see the documentation for BlockPaddingScheme.
You should be very careful with modes like CTR. CTR mode xor's the keystream with the plain text. If someone reuses their password on different messages, then its possible to recover the keystream which leads to plaintext recovery.
If ivText is unique for each message, then you should add it to your KDF to ensure a unique keystream for each message. Add the IV as the salt parameter for HKDF. Here, "unique" means if I have a message "Hello World", then the IV is different each time I encrypt the message.
If the IV is truly just "Iv16BytesOfText..." (i.e., its fixed), then there's nothing unique about it. Just derive an additional 16 bytes from the user's password. Then, to avoid the keystream xor attack, switch to a mode like CBC.
Finally, you should probably use CCM, EAX or GCM mode. Right now, you only have confidentiality. Usually you want authenticity, too. To gain authenticity, you often select an Authenticated Encryption mode of operation.
I am using Crypto++ library for cryptography related works. And sub-part of task is to encrypt and decrypt a text. The message can be up to 256 character long containing alphanumeric number spaces dot and special characters.
This piece of code is working for text length is less than or equal to 8. But after that it fails to decrypt the encrypted text.
// g++ -std=c++1y crypto.cpp -I /home/shravan40/cryptopp/build -lcryptopp
#include <iostream>
#include <cryptopp/rsa.h>
#include <cryptopp/integer.h>
#include <cryptopp/osrng.h>
int main(){
// Keys
CryptoPP::Integer n("0xbeaadb3d839f3b5f"), e("0x11"), d("0x21a5ae37b9959db9");
CryptoPP::RSA::PrivateKey privKey;
privKey.Initialize(n, e, d);
CryptoPP::RSA::PublicKey pubKey;
pubKey.Initialize(n, e);
// Encryption
std::string message = "Shravan Kumar";
CryptoPP::Integer m((const byte *)message.data(), message.size());
std::cout << "m: " << m << std::endl;
// m: 126879297332596.
CryptoPP::Integer c = pubKey.ApplyFunction(m);
std::cout << "c: " << std::hex << c << std::endl;
// c: 3f47c32e8e17e291h
// Decryption
CryptoPP::AutoSeededRandomPool prng;
CryptoPP::Integer r = privKey.CalculateInverse(prng, c);
std::cout << "r: " << std::hex << r << std::endl;
// r: 736563726574h
std::string recovered;
recovered.resize(r.MinEncodedSize());
r.Encode((byte *)recovered.data(), recovered.size());
std::cout << "recovered: " << recovered << std::endl;
// recovered: Expected : (Shravan Kumar), Received -> y5��dqm0
return 0;
}
Richard Critten is correct in his comment that usually hybrid encryption is used (an asymmetric cipher such as RSA with a symmetric cipher such as AES).
For these kind of insecure examples though you are usually simply required to split up the plaintext into parts the same size as the modulus n. So in your case just put every 8 bytes / characters together and use it for a (big endian) number. As the input seems to be ASCII the highest bit of those 8 bytes will always be set to zero, so you should not have any problems encrypting/decrypting. The input for RSA must of course always be less than n.
You may of course have to think up a smart way of handling the last part of the string.
Notes:
In case it hasn't been told (yet): raw RSA without padding is not secure either. So it is not just the key size that would be a problem if this example would be implemented in the field.
I haven't got a clue what you're doing with regard to decryption. You should have a look at your textbook again I suppose.
Integer m((const byte *)message.data(), message.size());
If you use message.size()+1, then the message will include the trailing NULL. You can use it during decryption to determine where the recovered string ends. Otherwise, you are going to need to track length of the message.
You might also be interested in Raw RSA from the Crypto++ wiki. But as Maarten cautined, its tricky to get right and build into a scheme.
You might consider something using RSA encryption with OAEP or PKCS v1.5 padding. Also see RSA Encryption Schemes on the Crypto++ wiki.
I believe this is undefined behavior:
std::string recovered;
recovered.resize(r.MinEncodedSize());
r.Encode((byte *)recovered.data(), recovered.size());
I think you need to use &recovered[0] to get the non-const pointer. It may be causing your problem.
I am new to cryptopp and have been struggling for a while with the creation of private keys for ECDSA signing.
I have a hex encoded private exponent E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB. This is stored as a string.
I want to use this to sign a text block using ECDSA. My code looks a bit like this
string Sig::genSignature(const string& privKeyIn, const string& messageIn)
{
AutoSeededRandomPool prng;
ECDSA<ECP, SHA256>::PrivateKey privateKey;
privateKey.AccessGroupParameters().Initialize(ASN1::secp256r1());
privateKey.Load(StringSource(privKeyIn, true, NULL).Ref());
ECDSA<ECP, SHA256>::Signer signer(privateKey);
// Determine maximum size, allocate a string with that size
size_t siglen = signer.MaxSignatureLength();
string signature(siglen, 0x00);
// Sign, and trim signature to actual size
siglen = signer.SignMessage(prng, (const byte *) messageIn.data(), (size_t) messageIn.length(), (byte*)signature.data());
signature.resize(siglen);
cout << signature.data() << endl;
return signature;
}
This code generates the following error in Visual studio on the when I try to do privateKey.load(...)
First-chance exception at 0x7693C42D in DLLTest.exe: Microsoft C++ exception: CryptoPP::BERDecodeErr at memory location 0x0033EEA8.
Unhandled exception at 0x7693C42D in DLLTest.exe: Microsoft C++ exception: CryptoPP::BERDecodeErr at memory location 0x0033EEA8.
I am guessing I am doing something a bit stupid... any help would be great???
PS I had a similar issue using ECDH for GMAC generation but got round this by saving the key as a SECByteBlock but this 'trick' doesnt seem to work in this case.
DLLTest.exe: Microsoft C++ exception: CryptoPP::BERDecodeErr ...
You have a private exponent, and not a private key. So you should not call Load on it. That's causing the Crypto++ BERDecodeErr exception.
The answer is detailed on the ECDSA wiki page, but its not readily apparent. You need to perform the following to initialize the privateKey given the curve and exponent::
string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";
exp.insert(0, "0x");
Integer x(exp.c_str());
privateKey.Initialize(ASN1::secp256r1(), x);
Prepending the "0x" ensures the Integer class will parse the ASCII string correctly. You can also append a "h" character to the string. You can see the parsing code for Integer class at Integer.cpp around line 2960 in the StringToInteger function.
Here's another way to do the same thing:
string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";
HexDecoder decoder;
decoder.Put((byte*)exp.data(), exp.size());
decoder.MessageEnd();
Integer x;
x.Decode(decoder, decoder.MaxRetrievable());
privateKey.Initialize(ASN1::secp256r1(), x);
The HexDecoder will perform the ASCII to binary conversion for you. The buffer held by the HexDecoder will then be consumed by the Integer using its Decode (BufferedTransformation &bt, size_t inputLen, Signedness=UNSIGNED) method.
And here is another way using HexDecoder (Crypto++ is as bad as scripting languages at times :)...
string exp = "E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB";
StringSource ss(exp, true /*punpAll*/, new HexDecoder);
Integer x;
x.Decode(ss, ss.MaxRetrievable());
privateKey.Initialize(ASN1::secp256r1(), x);
After initializing the key, you should validate it:
bool result = privateKey.Validate( prng, 3 );
if( !result ) { /* Handle error */ }
This will output binary data:
cout << signature.data() << endl;
If you want something printable/readable, run it though a Crypto++ HexEncoder.
for others looking for this later
string genSignature(const string& privKeyIn, const string& messageIn)
{
CryptoPP::Integer secretNumber(genSecretNumber(privKeyIn, messageIn));
AutoSeededRandomPool secretNumberGenerator;
if (encryptBase::debug)
{
cout << "secret number: " << secretNumber << endl;
}
SecByteBlock message(convertHexStrToSecByteBlock(messageIn));
ECDSA<ECP, SHA256>::PrivateKey privateKey;
string exp(privKeyIn);
exp.insert(0, "0x");
Integer x(exp.c_str());
privateKey.Initialize(ASN1::secp256r1(), x);
AutoSeededRandomPool prng;
if (!privateKey.Validate(prng, 3))
{
cout << "unable to verify key" << endl;
return "failed to verify key";
}
ECDSA<ECP, SHA256>::Signer signer(privateKey);
size_t siglen = signer.MaxSignatureLength();
string signature(siglen, 0x00);
siglen = signer.SignMessage(secretNumberGenerator, message.BytePtr(), message.size(), (byte*)signature.data());
signature.resize(siglen);
string encoded;
HexEncoder encoder;
encoder.Put((byte *) signature.data(), signature.size());
encoder.MessageEnd();
word64 size = encoder.MaxRetrievable();
if (size)
{
encoded.resize(size);
encoder.Get((byte*)encoded.data(), encoded.size());
}
return encoded;
}
Is it explicitly allowed to use the same buffer for plaintext/ciphertext when performing AES encryption/decryption in CBC and ECB modes using Crypto++ (assuming the buffer size is sufficient to accomodate the encrypted data) as in the following code:
#include <cstdio>
#include <cassert>
#include "cryptopp\rsa.h"
#include "cryptopp\rijndael.h"
#include "cryptopp\modes.h"
int main()
{
using namespace CryptoPP;
byte key[32], iv[Rijndael::BLOCKSIZE];
char testdata[] = "Crypto++ Test"; // any data can be here
size_t buffer_size = (sizeof(testdata) + Rijndael::BLOCKSIZE) & ~(Rijndael::BLOCKSIZE - 1);
byte* buffer = new byte[buffer_size];
memcpy(buffer, testdata, sizeof(testdata));
// encrypt data inplace
CBC_Mode<Rijndael>::Encryption enc(key, sizeof(key), iv);
MeterFilter meter(new ArraySink(buffer, buffer_size));
ArraySource(buffer, sizeof(testdata), true, new StreamTransformationFilter(enc, new Redirector(meter), BlockPaddingSchemeDef::PKCS_PADDING));
assert(meter.GetTotalBytes() == buffer_size);
// decrypt data inplace
CBC_Mode<Rijndael>::Decryption dec(key, sizeof(key), iv);
MeterFilter meter2(new ArraySink(buffer, buffer_size));
ArraySource(buffer, buffer_size, true, new StreamTransformationFilter(dec, new Redirector(meter2), BlockPaddingSchemeDef::PKCS_PADDING));
assert(meter2.GetTotalBytes() == sizeof(testdata));
printf("%s\n", static_cast<char*>(buffer));
delete buffer;
}
In general, Crypto++ buffers can be the same or they can be distinct. I can't think of a situation where they are not allowed to be the same for in-place or in-situ processing of plain text or cipher text data. The only caveat is the buffer has to be larger enough for cipher text expansion.
You also have to be careful about overlap, but how you can get into trouble depends on the cipher. For example, AES in CBC_Mode operates on 16 byte blocks (the functions of interest are ProcessBlock, ProcessXorBlock, and friends). You can use an overlapped buffer as long as the difference between pointers is 17 bytes (or more). In the RSA case, you would likely need a difference of MaxPreImage size, which is based on the size of the modulus.
Finally, the old Crypto++ FAQ discusses it briefly as "in-line processing". See How do I use a block cipher in Crypto++ 4.x?