1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
mod dao;
mod entities;

use std::path::PathBuf;

use miette::{IntoDiagnostic, Result};
use secret_service::EncryptionType;
use secret_service::SecretService;
use tracing::debug;

use crate::{Browser, Cookies};

const CHROME_STORAGE_NAME: &str = "Chrome Safe Storage";
const EDGE_STORAGE_NAME: &str = "Chrome Safe Storage";
// const CHROMIUM_STORAGE_NAME: &str = "Chromium Safe Storage";

pub const CHROME_LINUX: &str = "google-chrome/Default/Cookies";
pub const CHROME_LINUX1: &str = "google-chrome/Profile 1/Cookies";
// pub const CHROME_LINUX: &str = "google-chrome/Guest Profile/Cookies";
// pub const CHROME_LINUX: &str = "google-chrome/System Profile/Cookies";

pub const CHROME_MAC: &str = "Google/Chrome/Default/Cookies";
pub const CHROME_WIN: &str = "Local\\Google\\Chrome\\User Data\\Default\\Cookies";

pub const EDGE_LINUX: &str = "microsoft-edge/Default/Cookies";
pub const EDGE_MAC: &str = "Microsoft Edge/Default/Cookies";
pub const EDGE_WIN: &str = "Microsoft\\Edge\\User Data\\Default\\Cookies";

pub fn get_browser_cookies_path(browser: Browser) -> PathBuf {
    #[cfg(target_os = "linux")]
    let v = match browser {
       Browser::Edge => EDGE_LINUX,
       Browser::Chrome => CHROME_LINUX1,
        _ => EDGE_LINUX,
    };
    #[cfg(target_os = "macos")]
    let v = match browser {
        Browser::Edge => EDGE_MAC,
        Browser::Chrome => CHROME_MAC,
        _ => EDGE_MAC,
    };
    #[cfg(target_os = "windows")]
    let v = match browser {
        Browser::Edge => EDGE_WIN,
        Browser::Chrome => CHROME_WIN,
        _ => EDGE_WIN,
    };
    let mut cookie_dir = dirs::config_dir().expect("get config dir failed");
    cookie_dir.push(v);

    if browser == Browser::Chrome && !cookie_dir.exists() {
        cookie_dir = dirs::config_dir().expect("get config dir failed");
        cookie_dir.push(CHROME_LINUX);
    }
    cookie_dir
}

/// get `LEETCODE_SESSION` and `csrftoken`
///
/// * `browser`: `"chrome"`, `"edge"`
// , brave, opera, vivaldi, chromium
pub async fn get_session_csrf(browser: Browser, host: &str) -> Result<Cookies> {
    let cookies = dao::query_cookie(browser, host).await?;

    let mut res = Cookies::default();
    for cookie in cookies {
        if cookie.name == "csrftoken" {
            res.csrf = decrypt_cookies(&cookie.encrypted_value, browser).await?;
        } else if cookie.name == "LEETCODE_SESSION" {
            res.session = decrypt_cookies(&cookie.encrypted_value, browser).await?;
        }
    }

    Ok(res)
}

/// from `secret_service` get pass
async fn get_pass(browser: Browser) -> Result<Vec<u8>> {
    let default_pass = Ok(b"peanuts".to_vec());
    // initialize secret service (dbus connection and encryption session)
    let Ok(ss) = SecretService::connect(EncryptionType::Dh).await else {
        return default_pass;
    };
    // get default collection
    let Ok(collection) = ss.get_default_collection().await else {
        return default_pass;
    };
    let coll = collection
        .get_all_items()
        .await
        .into_diagnostic()?;
    let label = match browser {
        Browser::Edge => EDGE_STORAGE_NAME,
        Browser::Chrome => CHROME_STORAGE_NAME,
        _ => "",
    };
    let mut res = vec![];
    for i in coll {
        if i.get_label()
            .await
            .into_diagnostic()?
            == label
        {
            res = i
                .get_secret()
                .await
                .into_diagnostic()?;
        }
    }
    debug!("res: {}", String::from_utf8_lossy(&res).to_string());
    if res.is_empty() {
        res = b"peanuts".to_vec();
    }
    debug!("done res: {}", String::from_utf8_lossy(&res).to_string());

    Ok(res)
}

pub async fn decrypt_cookies(be_decrypte: &Vec<u8>, browser: Browser) -> Result<String> {
    use openssl::{hash::MessageDigest, pkcs5::pbkdf2_hmac, symm};

    let mut key = [32_u8; 16];
    let pass = get_pass(browser).await?;

    pbkdf2_hmac(&pass, b"saltysalt", 1, MessageDigest::sha1(), &mut key)
        .into_diagnostic()?;

    let iv = vec![32_u8; 16];

    let mut decrypter = symm::Crypter::new(
        symm::Cipher::aes_128_cbc(),
        symm::Mode::Decrypt,
        &key,
        Some(&iv),
    )
    .into_diagnostic()?;

    let data_len = be_decrypte.len() - 3;
    let block_size = symm::Cipher::aes_128_cbc().block_size();
    let mut res = vec![0; data_len + block_size];

    decrypter.pad(false);
    let _num = decrypter
        .update(
            be_decrypte
                .get(3..)
                .expect("crypto error"),
            &mut res,
        )
        .into_diagnostic()?;
    decrypter
        .finalize(&mut res)
        .into_diagnostic()?;

    res.retain(|v| v >= &20);

    Ok(String::from_utf8_lossy(&res).to_string())
}