简体   繁体   中英

'message hash or MAC not valid' AES GCM when decrypting

I am trying to convert a snippet of Python code to C++. The Python code reads ciphertext and a key from a file and uses PyCryptoDome to decrypt. I have tried to replicate this behavior in C++, however, it throws HashVerificationFilter: message hash or MAC not valid

I have read this question and this question . I'm already getting the size of a string for my key and IV length and I'm pretty sure that's not the reason why my code is failing.

My code is based on the GCM AE example in the Crypto++ wiki, here . I believe my code is failing due to how my ciphertext is formatted (salt, nonce, and cipher). I'm pretty new to cryptography in general but I doubt that's the correct way it's supposed to be formatted. Let me know if I'm wrong.

I expect my program to at least run without an exception, albeit the decrypted text might be wrong. However, it throws the HashVerificationFilter error instead. I've tried logging the sizes of the IV, the sizes of the key, and the ciphertext and I made sure they were being read correctly. I think I am passing a value wrong somewhere or my ciphertext is formatted incorrectly.


Python Code:

from Crypto.Cipher import AES

def decrypt(buffer, key):
    aes_obj = AES.new(key, AES.MODE_GCM, buffer[3:15])
    return aes_obj.decrypt(buffer[15:])[:-16].decode()

if __name__ == "__main__":
    key = open("key.txt", "rb").read()
    cipher =  open("cipher.txt", "rb").read()
    
    output = decrypt(cipher, key)
    print(output)

C++ Code:

#include <cryptopp/cryptlib.h>
#include <cryptopp/filters.h>
#include <cryptopp/aes.h>
#include <cryptopp/gcm.h>

#include <fstream>
#include <string>
#include <iostream>
#include <cassert>
#include <sstream>

using CryptoPP::BufferedTransformation;
using CryptoPP::AuthenticatedSymmetricCipher;

using CryptoPP::Redirector;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::AuthenticatedDecryptionFilter;

using CryptoPP::AES;
using CryptoPP::GCM;

const int TAG_SIZE = 12;

std::string decrypt(std::string buffer, std::string key)
{
    GCM<AES>::Decryption d;

    std::string plain;
    std::string nonce = buffer.substr(0, 15).substr(3);

    const CryptoPP::byte* aes_key = reinterpret_cast<const CryptoPP::byte*>(key.c_str());  
    const CryptoPP::byte* nonce_byte = reinterpret_cast<const CryptoPP::byte*>(nonce.c_str());

    d.SetKeyWithIV(aes_key, key.size(), nonce_byte, nonce.size());

    AuthenticatedDecryptionFilter df( d, 
        new StringSink( plain ),
        AuthenticatedDecryptionFilter::DEFAULT_FLAGS,
        TAG_SIZE
    );

    StringSource ss( buffer.substr(15), true,
        new Redirector(df)
    );

    bool b = df.GetLastResult();
    assert( true == b );
    
    return plain;
}

int main()
{
    std::ifstream key_f("key.txt", std::ios::binary | std::ios::out);
    std::ifstream cipher_f("cipher.txt", std::ios::binary | std::ios::out);

    std::stringstream key;
    std::stringstream cipher;

    key << key_f.rdbuf();
    cipher << cipher_f.rdbuf();

    try {
        std::string output = decrypt(cipher.str(), key.str());
        std::cout << "Decrypted: " << output << std::endl;
    } catch (CryptoPP::Exception& e)
    {
        std::cerr << "Caught Exception: " << e.what() << "\n"; 
    }
}

As Maarten Bodewes had commented, my tag size was incorrect. The correct TAG_SIZE should be 16 bytes. Looking through the PyCryptoDome documentation, I found he was correct as I had missed the default tag size in PyCryptoDome AES objects was 16 bytes.

Referenced from PyCryptoDome docs :

It must be even and in the range [4..16]. The recommended value (and the default, if not specified) is 16.

TLDR; Changing the tag size to 16 bytes fixes the issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM