Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IV derivation seems to be wrong (?) #1

Open
x3ro opened this issue Nov 22, 2024 · 2 comments
Open

IV derivation seems to be wrong (?) #1

x3ro opened this issue Nov 22, 2024 · 2 comments

Comments

@x3ro
Copy link

x3ro commented Nov 22, 2024

Hi and thanks for writing this, it was very helpful that someone already did most of the reserach 😅

I gave your code a try and found that it does indeed return a decrypted key, but this key did not successfully decrypt the test database. I played around with it a little bit and eventually noticed that the key does not have the right length: 48 bytes as opposed to the expected 64 bytes.

I eventually narrowed this down to decrypt_string, where you take the ciphertext and treat the first 16 bytes as the IV. However, looking at the OSCrypt code from Chromium, I noticed that it just uses an IV made up of 0x20 instead.

My resulting decrypt_string function looks like this, and works at least on my machine:

    fn decrypt_string(&self, encrypted_hex: &str) -> Result<String, Box<dyn std::error::Error>> {
        let encrypted_data = decode(encrypted_hex)?; // Decode the hex string
        if !encrypted_data.starts_with(ENCRYPTION_VERSION_PREFIX.as_bytes()) {
            return Err("Invalid encryption version prefix".into()); // Validate encryption version prefix
        }

        let encrypted_text = &encrypted_data[ENCRYPTION_VERSION_PREFIX.len()..]; // Extract encrypted text
        // Create AES-CBC cipher with empty IV
        // https://chromium.googlesource.com/chromium/src/+/refs/tags/130.0.6686.2/components/os_crypt/sync/os_crypt_mac.mm#208
        let cipher = Aes128Cbc::new_from_slices(&self.aes_key, &[b' '; KEY_LENGTH])?; 
        let decrypted = cipher.decrypt_vec(encrypted_text)?; // Decrypt the text
        String::from_utf8(decrypted).map_err(|e| e.into()) // Convert decrypted bytes to a UTF-8 string
    }
@fjh658
Copy link
Owner

fjh658 commented Nov 25, 2024

Hi and thanks for writing this, it was very helpful that someone already did most of the reserach 😅

I gave your code a try and found that it does indeed return a decrypted key, but this key did not successfully decrypt the test database. I played around with it a little bit and eventually noticed that the key does not have the right length: 48 bytes as opposed to the expected 64 bytes.

I eventually narrowed this down to decrypt_string, where you take the ciphertext and treat the first 16 bytes as the IV. However, looking at the OSCrypt code from Chromium, I noticed that it just uses an IV made up of 0x20 instead.

My resulting decrypt_string function looks like this, and works at least on my machine:

    fn decrypt_string(&self, encrypted_hex: &str) -> Result<String, Box<dyn std::error::Error>> {
        let encrypted_data = decode(encrypted_hex)?; // Decode the hex string
        if !encrypted_data.starts_with(ENCRYPTION_VERSION_PREFIX.as_bytes()) {
            return Err("Invalid encryption version prefix".into()); // Validate encryption version prefix
        }

        let encrypted_text = &encrypted_data[ENCRYPTION_VERSION_PREFIX.len()..]; // Extract encrypted text
        // Create AES-CBC cipher with empty IV
        // https://chromium.googlesource.com/chromium/src/+/refs/tags/130.0.6686.2/components/os_crypt/sync/os_crypt_mac.mm#208
        let cipher = Aes128Cbc::new_from_slices(&self.aes_key, &[b' '; KEY_LENGTH])?; 
        let decrypted = cipher.decrypt_vec(encrypted_text)?; // Decrypt the text
        String::from_utf8(decrypted).map_err(|e| e.into()) // Convert decrypted bytes to a UTF-8 string
    }

The decryption key is x'decrypt_key', including x and the quotation marks on both sides. For example, if decrypt_key=1234, then the complete key would be x'1234'.

SignalDecryption
Config file: /Users/hacker/Library/Application Support/Signal/config.json
Encrypted key: 7631307d7b10316d9f44d3381af53523b696324dcbb7f6dab87df980b18392d9d3e1cd435c85d3a032b9dfdcf55219cba6ae81c3ec2d3972d1408f6ddeaddfa3fef3bf66ba1f8bdc6f30486abe8a7a5e57fd6b
Decrypted key: e44131c698c3623fc526e95410f6c520eaef7cd7987e426a03fb7a50988f6b83

1. Open db file

db file: '~/Library/Application Support/Signal/sql/db.sqlite'
image

2. Input decrypted key

3. Show result

image

@fjh658 fjh658 closed this as completed Nov 25, 2024
@fjh658 fjh658 reopened this Nov 25, 2024
@x3ro
Copy link
Author

x3ro commented Nov 25, 2024

Hi and thanks for the overview :) I'm aware of how to use the decrypted key, and I already have a working implementation of this. Maybe there are differences between Signal versions, but in your example the decrypted key is 64 bytes long. This was not the case for me, when I ran your code without modification, where I only got a 48 byte key, which naturally didn't work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants