3

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Elf
  • 153
  • 2
  • 12
  • 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 Jun 01 '20 at 19:25
  • Actually, the data isn't encrypted using Rust code. The program just decrypt already encrypted messages coming from elsewhere. – Elf Jun 01 '20 at 19:29
  • 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 Jun 01 '20 at 19:31
  • I think I understand what you mean, but what could I do to fix this? – Elf Jun 01 '20 at 19:33
  • 1
    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 Jun 01 '20 at 19:37
  • 1
    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 Jun 01 '20 at 19:38
  • I removed the tag for convenience, because it was not necessary, but in fact, I have the associated tag for this encrypted message as well. I used `decrypt_and_verify()` instead of `verify()` in the python code to check it, and it matched. In the Rust version, no matter if I use `decrypt` or `decrypt_in_place_detached` (which check the tag), it does the same and panic. – Elf Jun 01 '20 at 19:38
  • @rossum what should I set exactly explicitely and how can I do it? In the program, I have no idea of where the message have been encrypted and I don't have any informations on it. – Elf Jun 01 '20 at 19:43

1 Answers1

4

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366