3
votes

I have a given encrypted message (decrypted, it is "encrypted secret message") and I'm trying to retrieve this original string from the AES-GCM 256 encrypted one. I use the aes-gcm crate to do this:

use aes_gcm::Aes256Gcm;
use aead::{Aead, NewAead, generic_array::GenericArray};

fn main() {
    let key_bytes = hex::decode("ce265dbc38bb25ef29fade77a4b88fe07b6063215f6526a4623cf810a3d611c9").unwrap();
    let nonce_bytes = hex::decode("ce77357fe7b2401400408f44").unwrap();
    let ciphertext_bytes = hex::decode("fd77fae68fa27ea00afbe474f4fcd47248a19b3cbf2a6d7e").unwrap();

    let key = GenericArray::clone_from_slice(key_bytes.as_slice());
    let nonce = GenericArray::from_slice(nonce_bytes.as_slice());

    let cipher = Aes256Gcm::new(key);
    let plaintext = cipher.decrypt(nonce, ciphertext_bytes.as_slice()).unwrap(); // panic on a decryption failure

    println!("{:?}", plaintext);
}

This is the dependencies section of my Cargo.toml:

[dependencies]
aes-gcm = "0.5.0"
aead = "0.2.0"
hex = "0.4.2"

The problem is that the program always panics at the unwrap call on line 13, even though the encrypted message, the key and the nonce are good:

thread 'main' panicked at 'called Result::unwrap() on an Err value: Error', src\main.rs:13:21

It looks like it's an error from the encrypted message being invalid, from the encrypted message, the key or the nonce being invalid, but it's not. I wrote a Python program that does exactly the same thing and it works; the output is indeed encrypted secret message!

from Crypto.Cipher import AES

key = bytes.fromhex("ce265dbc38bb25ef29fade77a4b88fe07b6063215f6526a4623cf810a3d611c9")

nonce = bytes.fromhex("ce77357fe7b2401400408f44")
cipher_text = bytes.fromhex("fd77fae68fa27ea00afbe474f4fcd47248a19b3cbf2a6d7e")

cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
plaintext = cipher.decrypt(cipher_text)

print(plaintext.decode("utf-8"))

I want to be able to decrypt these kind of encrypted messages in Rust, not in Python. I don't know why I get an error at all. Did I miss something?

1
If you encrypt the data using Rust, it includes a number of extra bytes (in hex: 2ef2a9d27909df90bcb45606067148a6). This seems like you aren't actually using the same cyphers on both sides. - Shepmaster
Actually, the data isn't encrypted using Rust code. The program just decrypt already encrypted messages coming from elsewhere. - CypElf
That's my point. If you encrypt it with the Rust code, the cyphertext differs, but matches at the beginning. This leads me to think that you aren't actually using the same cypher. Especially something around padding. - Shepmaster
GCM is an authenticated cipher mode, so it should have an MAC tag. I suspect the lack of it is what is causing the error. It looks like the decrypt method is expecting it concatenated to the ciphertext. - matt
Your cyphertext was encrypted on a different system. That means you do not rely on any system defaults anywhere, because the defaults on the other system may be different. Set everything explicitly in your code to match the settings used to encrypt, including all the default settings at the encryption end. - rossum

1 Answers

4
votes

From encrypt, emphasis mine:

The default implementation assumes a postfix tag (ala AES-GCM, AES-GCM-SIV, ChaCha20Poly1305). Aead implementations which do not use a postfix tag will need to override this to correctly assemble the ciphertext message.

Since you say you have the tag:

I have the associated tag for this encrypted message as well

You can follow the same pattern:

use aead::{generic_array::GenericArray, Aead, NewAead};
use aes_gcm::Aes256Gcm;

fn main() {
    let key_hex = "ce265dbc38bb25ef29fade77a4b88fe07b6063215f6526a4623cf810a3d611c9";
    let nonce_hex = "ce77357fe7b2401400408f44";
    let ciphertext_hex = "fd77fae68fa27ea00afbe474f4fcd47248a19b3cbf2a6d7e";

    // Append the tag data to the encrypted data
    let tag_hex = "2ef2a9d27909df90bcb45606067148a6";
    let ciphertext_and_tag_hex = format!("{}{}", ciphertext_hex, tag_hex);
    let ciphertext_bytes = hex::decode(ciphertext_and_tag_hex).unwrap();

    let key_bytes = hex::decode(key_hex).unwrap();
    let nonce_bytes = hex::decode(nonce_hex).unwrap();

    let key = GenericArray::clone_from_slice(&key_bytes);
    let nonce = GenericArray::from_slice(&nonce_bytes);

    let cipher = Aes256Gcm::new(key);

    let plaintext = cipher.decrypt(nonce, &*ciphertext_bytes).unwrap();
    println!("{}", String::from_utf8_lossy(&plaintext));
}

You can also investigate the decrypt_in_place_detached method, which is more complicated but allows specifying the tag separately.