-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Closed
Labels
Description
The code below is essentially the same as the example at https://www.cryptopp.com/wiki/GCM_Mode#AEAD except that the tag size is configurable using a compiler define.
The following anomalies were observed with fuzz testing:
- Compile with
-DTAG_SIZE=32
to observe that encryption succeeds, but subsequent decryption does not - Compile with
-DTAG_SIZE=200
to observe out-of-bounds reads (use Valgrind or AddressSanitizer)
#include <cassert>
#include <gcm.h>
#include <aes.h>
#include <iostream>
#include <filters.h>
using namespace std;
using namespace CryptoPP;
int main(void)
{
byte key[32]; memset( key, 0, sizeof(key) );
byte iv[12]; memset( iv, 0, sizeof(iv) );
string adata( 16, (char)0x00 );
string pdata( 16, (char)0x00 );
// Encrypted, with Tag
string cipher, encoded;
// Recovered (decrypted)
string radata, rpdata;
/*********************************\
\*********************************/
try
{
GCM< AES >::Encryption e;
e.SetKeyWithIV( key, sizeof(key), iv, sizeof(iv) );
// AuthenticatedEncryptionFilter defines two
// channels: DEFAULT_CHANNEL and AAD_CHANNEL
// DEFAULT_CHANNEL is encrypted and authenticated
// AAD_CHANNEL is authenticated
AuthenticatedEncryptionFilter ef( e,
new StringSink( cipher ), false,
TAG_SIZE /* MAC_AT_END */
); // AuthenticatedEncryptionFilter
// Authenticated data *must* be pushed before
// Confidential/Authenticated data. Otherwise
// we must catch the BadState exception
ef.ChannelPut( AAD_CHANNEL, (const unsigned char*)adata.data(), adata.size() );
ef.ChannelMessageEnd(AAD_CHANNEL);
// Confidential data comes after authenticated data.
// This is a limitation due to CCM mode, not GCM mode.
ef.ChannelPut( DEFAULT_CHANNEL, (const unsigned char*)pdata.data(), pdata.size() );
ef.ChannelMessageEnd(DEFAULT_CHANNEL);
printf("Encryption succeeded\n");
}
catch( CryptoPP::Exception& e )
{
cerr << "Caught Exception..." << endl;
cerr << e.what() << endl;
cerr << endl;
return 0;
}
try
{
GCM< AES >::Decryption d;
d.SetKeyWithIV( key, sizeof(key), iv, sizeof(iv) );
// Break the cipher text out into it's
// components: Encrypted and MAC
string enc = cipher.substr( 0, cipher.length()-TAG_SIZE );
string mac = cipher.substr( cipher.length()-TAG_SIZE );
// Sanity checks
assert( cipher.size() == enc.size() + mac.size() );
assert( enc.size() == pdata.size() );
assert( TAG_SIZE == mac.size() );
// Not recovered - sent via clear channel
radata = adata;
// Object *will* throw an exception
// during decryption\verification _if_
// verification fails.
AuthenticatedDecryptionFilter df( d, NULL,
AuthenticatedDecryptionFilter::MAC_AT_BEGIN | AuthenticatedDecryptionFilter::THROW_EXCEPTION, TAG_SIZE );
// The order of the following calls are important
df.ChannelPut( DEFAULT_CHANNEL, (const unsigned char*)mac.data(), mac.size() );
df.ChannelPut( AAD_CHANNEL, (const unsigned char*)adata.data(), adata.size() );
df.ChannelPut( DEFAULT_CHANNEL, (const unsigned char*)enc.data(), enc.size() );
// If the object throws, it will most likely occur
// during ChannelMessageEnd()
df.ChannelMessageEnd( AAD_CHANNEL );
df.ChannelMessageEnd( DEFAULT_CHANNEL );
// If the object does not throw, here's the only
// opportunity to check the data's integrity
bool b = false;
b = df.GetLastResult();
assert( true == b );
// Remove data from channel
string retrieved;
size_t n = (size_t)-1;
// Plain text recovered from enc.data()
df.SetRetrievalChannel( DEFAULT_CHANNEL );
n = (size_t)df.MaxRetrievable();
retrieved.resize( n );
if( n > 0 ) { df.Get( (byte*)retrieved.data(), n ); }
rpdata = retrieved;
assert( rpdata == pdata );
// All is well - work with data
cout << "Decrypted and Verified data. Ready for use." << endl;
cout << endl;
cout << "adata length: " << adata.size() << endl;
cout << "pdata length: " << pdata.size() << endl;
cout << endl;
cout << "recovered adata length: " << radata.size() << endl;
cout << "recovered pdata length: " << rpdata.size() << endl;
cout << endl;
}
catch( CryptoPP::Exception& e )
{
cerr << "Caught Exception..." << endl;
cerr << e.what() << endl;
cerr << endl;
return 0;
}
return 0;
}