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 append0000000077C8E390somehow.The following works for me. Its a mildly modified version of your program. It makes
adataandpdataglobal, 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
pdatagets confidentiality and authenticity assurances; andadatagets authenticity assurances only.The key and IV are customary. Handle them like you always do (or should do :).