Extra data in result for AES/GCM decryption

871 views Asked by At

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:

  1. 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?

  2. 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;    
}
1

There are 1 answers

2
jww On BEST ANSWER

The problem is, with the output I get "plaintext: abc1230000000077C8E390"

My guess here is you reused a string. Crypto++ will append, and not overwrite, in a StringSink. So you got abc123, and then you managed to append 0000000077C8E390 somehow.


The following works for me. Its a mildly modified version of your program. It makes adata and pdata global, renames them to avoid name hiding, and passes the message through pdata.

$ ./cryptopp-test.exe
plaintext: Now is the time for all good men to come to the aide of their country

$ ./cryptopp-test.exe wxyz
plaintext: wxyz

Because you are using THROW_EXCEPTION, you don't need this:

b = df.GetLastResult();
assert( true == b );

I assume adata is meant to be transferred in plaintext...

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.


... am I correct that both the iv and adata are in plaintext

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 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)?

The pdata gets confidentiality and authenticity assurances; and adata gets authenticity assurances only.

The key and IV are customary. Handle them like you always do (or should do :).


using CryptoPP::DEFAULT_CHANNEL;
using CryptoPP::AAD_CHANNEL;

string encryptData(const string& pdata, const string& adata, const byte *key, unsigned int ksize, const byte *iv, unsigned int vsize);
string decryptData(const string& cipher, const string& adata, const byte *key, unsigned int ksize, const byte *iv, unsigned int vsize);

string xadata = "172.16.1.10";
string xpdata = "Now is the time for all good men to come to the aide of their country";

static const unsigned int TAG_SIZE = 16;

int main(int argc, char* argv[])
{
    (void)argc; (void)argv;

    if(argc >= 2)
        xpdata = argv[1];

    try {

        AutoSeededRandomPool prng;

        byte key[ AES::DEFAULT_KEYLENGTH ];
        prng.GenerateBlock( key, sizeof(key) );

        byte iv[ AES::BLOCKSIZE ];
        prng.GenerateBlock( iv, sizeof(iv) );

        string cipherText = encryptData(xpdata, xadata, key, sizeof(key), iv, sizeof(iv));
        string plainText = decryptData(cipherText, xadata, key, sizeof(key), iv, sizeof(iv));

        cout << "plaintext: " << plainText << endl;
    }
    catch(CryptoPP::Exception& ex)
    {
        cerr << ex.what() << endl;
    }

    return 0;
}

//Utilities
string encryptData(const string& pdata, const string& adata, const byte *key, unsigned int ksize, const byte *iv, unsigned int vsize) {

    string cipher;

    GCM< AES >::Encryption e;
    e.SetKeyWithIV( key, ksize, iv, vsize );

    AuthenticatedEncryptionFilter ef( e,
                                     new StringSink( cipher ), false, TAG_SIZE
                                     ); // AuthenticatedEncryptionFilter

    ef.ChannelPut(AAD_CHANNEL, (const byte*)adata.data(), adata.size() );
    ef.ChannelMessageEnd(AAD_CHANNEL);

    ef.ChannelPut(DEFAULT_CHANNEL, (const byte*)pdata.data(), pdata.size() );
    ef.ChannelMessageEnd(DEFAULT_CHANNEL);

    return(cipher);
}

string decryptData(const string& cipher, const string& adata, const byte *key, unsigned int ksize, const byte *iv, unsigned int vsize) {

    string recovered;

    GCM< AES >::Decryption d;
    d.SetKeyWithIV( key, ksize, iv, vsize );

    const string& enc = cipher.substr( 0, cipher.length()-TAG_SIZE );
    const string& mac = cipher.substr( cipher.length()-TAG_SIZE );

    assert( cipher.size() == enc.size() + mac.size() );
    assert( enc.size() == xpdata.size() );
    assert( TAG_SIZE == mac.size() );

    AuthenticatedDecryptionFilter df( d, new StringSink(recovered),
                                     AuthenticatedDecryptionFilter::MAC_AT_BEGIN |
                                     AuthenticatedDecryptionFilter::THROW_EXCEPTION, TAG_SIZE );

    df.ChannelPut(DEFAULT_CHANNEL, (const byte*)mac.data(), mac.size() );
    df.ChannelPut(AAD_CHANNEL, (const byte*)adata.data(), adata.size() );
    df.ChannelPut(DEFAULT_CHANNEL, (const byte*)enc.data(), enc.size() );

    df.ChannelMessageEnd(AAD_CHANNEL);
    df.ChannelMessageEnd(DEFAULT_CHANNEL);

    assert( recovered == xpdata );        
    return(recovered);
}