AES/GCM 解密结果中的额外数据

Extra data in result for AES/GCM decryption

我有以下代码,基本上是从 http://www.cryptopp.com/wiki/AuthenticatedDecryptionFilter

的 Crypto++ wiki 上抓取的

问题是,我得到的输出是 "plaintext: abc1230000000077C8E390" 而不是我所期望的,只是 "plaintext: abc123"

最后的额外数据是什么?

还有两个基于相同代码库的小问题:

  1. 此方法是否可以作为对 pdata 使用实际随机字节数据的直接替代方法 - 或者因为它是字符串,所以它实际上必须像普通文本一样吗?

  2. 我假设adata是明文传输的,所以在存储密文时,我正确的是iv和adata都是明文的,用户只提供密钥(解密包含这 4 个元素的密文:key(由用户提供)、ciphertext(可用)、iv(可用)和 adata(可用)?

谢谢!

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;    
}

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

我猜你重复使用了一个字符串。 Crypto++ 将在 StringSink 中附加,而不是覆盖。所以你得到了 abc123,然后你设法以某种方式附加了 0000000077C8E390


以下适合我。它是您的程序的轻微修改版本。它使 adatapdata 全局化,重命名它们以避免名称隐藏,并通过 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

因为你正在使用 THROW_EXCEPTION,你不需要这个:

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

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

是的。它将类似于 IP 地址、磁盘扇区或计数器(以及其他)。

更准确地说,它的数据仅经过身份验证。如果它被篡改,它会被检测到。


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

IV 可以是 public 或私有参数。

如果是public参数,则不需要认证。它有点不直观,但那是因为篡改IV会篡改密码的状态,所以解密会失败,就像你使用了错误的密钥一样。


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

pdata 获得保密性和真实性保证; adata 仅获得真实性保证。

密钥和IV是习惯的。像往常一样处理它们(或应该这样做 :)。


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);
}