Skip to main content

seher/crypto/
linux.rs

1use crate::crypto::{CryptoError, Result};
2use aes::cipher::{BlockDecryptMut, KeyIvInit};
3
4type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
5
6const SALT: &[u8] = b"saltysalt";
7const IV: &[u8] = b"                "; // 16 spaces
8const KEY_LENGTH: usize = 16;
9const ITERATIONS: u32 = 1;
10
11/// # Errors
12///
13/// Returns an error if decryption fails or if the value is not valid UTF-8.
14pub fn decrypt(encrypted_value: &[u8]) -> Result<String> {
15    if encrypted_value.len() < 3 {
16        return Ok(String::new());
17    }
18
19    let version = &encrypted_value[..3];
20    match version {
21        b"v10" | b"v11" => {
22            let encrypted = &encrypted_value[3..];
23            let key = get_encryption_key();
24            decrypt_aes_cbc(&key, encrypted)
25        }
26        _ => String::from_utf8(encrypted_value.to_vec())
27            .map_err(|e| CryptoError::DecryptionFailed(e.to_string())),
28    }
29}
30
31fn get_encryption_key() -> Vec<u8> {
32    if let Ok(key) = get_key_from_secret_service() {
33        key
34    } else {
35        // Fallback to default password "peanuts"
36        let mut key = vec![0u8; KEY_LENGTH];
37        pbkdf2::pbkdf2_hmac::<sha1::Sha1>(b"peanuts", SALT, ITERATIONS, &mut key);
38        key
39    }
40}
41
42fn get_key_from_secret_service() -> Result<Vec<u8>> {
43    use secret_service::blocking::SecretService;
44
45    let service = SecretService::connect(secret_service::EncryptionType::Dh)
46        .map_err(|e| CryptoError::SecretServiceError(format!("Failed to connect: {e}")))?;
47
48    let collection = service
49        .get_default_collection()
50        .map_err(|e| CryptoError::SecretServiceError(format!("Failed to get collection: {e}")))?;
51
52    let items = collection
53        .search_items(std::collections::HashMap::from([("application", "chrome")]))
54        .map_err(|e| CryptoError::SecretServiceError(format!("Failed to search items: {e}")))?;
55
56    if let Some(item) = items.first() {
57        let password = item
58            .get_secret()
59            .map_err(|e| CryptoError::SecretServiceError(format!("Failed to get secret: {e}")))?;
60
61        let mut key = vec![0u8; KEY_LENGTH];
62        pbkdf2::pbkdf2_hmac::<sha1::Sha1>(&password, SALT, ITERATIONS, &mut key);
63        return Ok(key);
64    }
65
66    Err(CryptoError::SecretServiceError(
67        "Chrome password not found".to_string(),
68    ))
69}
70
71fn decrypt_aes_cbc(key: &[u8], encrypted: &[u8]) -> Result<String> {
72    let cipher = Aes128CbcDec::new(key.into(), IV.into());
73
74    let mut buffer = encrypted.to_vec();
75    let decrypted = cipher
76        .decrypt_padded_mut::<aes::cipher::block_padding::Pkcs7>(&mut buffer)
77        .map_err(|e| CryptoError::DecryptionFailed(format!("AES-CBC decryption failed: {e:?}")))?;
78
79    String::from_utf8(decrypted.to_vec())
80        .map_err(|e| CryptoError::DecryptionFailed(format!("UTF-8 conversion failed: {e}")))
81}