I have the following code, basically grabbed right from the Crypto++ wiki at http://www.cryptopp.com/wiki/AuthenticatedDecryptionFilter
The problem is, with the output I get "plaintext: abc1230000000077C8E390" instead of what I'd expect, just "plaintext: abc123"
What is the extra data at the end?
Also- two more small questions that are based on the same codebase:
would this method serve as a drop-in replacement for using actual random byte data for pdata- or since it's string must it actually be like usual text?
I assume adata is meant to be transferred in plaintext, so when storing the ciphertext, am I correct that both the iv and adata are in plaintext, and the user only provides the key (to decrypt the ciphertext with those 4 elements: key (provided by user), ciphertext (made available), iv (made available), and adata (made available)?
Thank you!
AutoSeededRandomPool prng;
byte key[ AES::DEFAULT_KEYLENGTH ];
prng.GenerateBlock( key, sizeof(key) );
byte iv[ AES::BLOCKSIZE ];
prng.GenerateBlock( iv, sizeof(iv) );
string adata( 16, (char)0x00 );
string cipherText = encryptData("abc123", adata, key, iv);
string plainText = decryptData(cipherText, adata, key, iv);
cout << "plaintext: " << plainText << endl;
//Utilities
string MainWindow::encryptData(string pdata, string adata, const byte *key, const byte *iv) {
const int TAG_SIZE = 16;
string cipher;
try
{
GCM< AES >::Encryption e;
e.SetKeyWithIV( key, AES::DEFAULT_KEYLENGTH, iv, AES::BLOCKSIZE );
AuthenticatedEncryptionFilter ef( e,
new StringSink( cipher ), false, TAG_SIZE
); // AuthenticatedEncryptionFilter
ef.ChannelPut( "AAD", (const byte*)adata.data(), adata.size() );
ef.ChannelMessageEnd("AAD");
ef.ChannelPut( "", (const byte*)pdata.data(), pdata.size() );
ef.ChannelMessageEnd("");
}
catch( CryptoPP::BufferedTransformation::NoChannelSupport& e )
{
cerr << "Caught NoChannelSupport..." << endl;
cerr << e.what() << endl;
cerr << endl;
}
catch( CryptoPP::AuthenticatedSymmetricCipher::BadState& e )
{
cerr << "Caught BadState..." << endl;
cerr << e.what() << endl;
cerr << endl;
}
catch( CryptoPP::InvalidArgument& e )
{
cerr << "Caught InvalidArgument..." << endl;
cerr << e.what() << endl;
cerr << endl;
}
return(cipher);
}
string MainWindow::decryptData(string cipher, string adata, const byte *key, const byte *iv) {
const int TAG_SIZE = 16;
string radata, rpdata;
try
{
GCM< AES >::Decryption d;
d.SetKeyWithIV( key, AES::DEFAULT_KEYLENGTH, iv, AES::BLOCKSIZE );
string enc = cipher.substr( 0, cipher.length()-TAG_SIZE );
string mac = cipher.substr( cipher.length()-TAG_SIZE );
assert( cipher.size() == enc.size() + mac.size() );
assert( enc.size() == pdata.size() );
assert( TAG_SIZE == mac.size() );
radata = adata;
AuthenticatedDecryptionFilter df( d, NULL,
AuthenticatedDecryptionFilter::MAC_AT_BEGIN |
AuthenticatedDecryptionFilter::THROW_EXCEPTION, TAG_SIZE );
df.ChannelPut( "", (const byte*)mac.data(), mac.size() );
df.ChannelPut( "AAD", (const byte*)adata.data(), adata.size() );
df.ChannelPut( "", (const byte*)enc.data(), enc.size() );
df.ChannelMessageEnd( "AAD" );
df.ChannelMessageEnd( "" );
string retrieved;
size_t n = (size_t)df.MaxRetrievable();
retrieved.resize( n );
if( n > 0 ) { df.Get( (byte*)retrieved.data(), n ); }
rpdata = retrieved;
assert( rpdata == pdata );
}
catch( CryptoPP::InvalidArgument& e )
{
cerr << "Caught InvalidArgument..." << endl;
cerr << e.what() << endl;
cerr << endl;
}
catch( CryptoPP::AuthenticatedSymmetricCipher::BadState& e )
{
cerr << "Caught BadState..." << endl;
cerr << e.what() << endl;
cerr << endl;
}
catch( CryptoPP::HashVerificationFilter::HashVerificationFailed& e )
{
cerr << "Caught HashVerificationFailed..." << endl;
cerr << e.what() << endl;
cerr << endl;
}
return rpdata;
}
My guess here is you reused a string. Crypto++ will append, and not overwrite, in a
StringSink
. So you gotabc123
, and then you managed to append0000000077C8E390
somehow.The following works for me. Its a mildly modified version of your program. It makes
adata
andpdata
global, renames them to avoid name hiding, and passes the message throughpdata
.Because you are using
THROW_EXCEPTION
, you don't need this:Yes. It would be something like an IP address, a disk sector or counter (among others).
More correctly, its data that is authenticated only. If its tampered with, it will be detected.
The IV can be a public or private parameter.
If its a public parameter, then it does not need to be authenticated. Its a little non-intuitive, but that's because tampering with the IV will tamper with the cipher's state, so decryption will fail as if you used the wrong key.
The
pdata
gets confidentiality and authenticity assurances; andadata
gets authenticity assurances only.The key and IV are customary. Handle them like you always do (or should do :).