decrypt_cookies/chromium/
mod.rs

1pub mod builder;
2pub(crate) mod items;
3use std::marker::PhantomData;
4
5use chromium_crypto::Decrypter;
6use chrono::prelude::Utc;
7use items::cookie::cookie_entities::cookies;
8pub use items::{
9    cookie::{
10        cookie_entities::cookies::{
11            Column as ChromiumCookieCol, ColumnIter as ChromiumCookieColIter,
12        },
13        ChromiumCookie,
14    },
15    passwd::{
16        login_data_entities::logins::{Column as ChromiumLoginCol, Column as ChromiumLoginColIter},
17        LoginData,
18    },
19};
20use rayon::prelude::*;
21use sea_orm::{sea_query::IntoCondition, ColumnTrait, DbErr};
22use tokio::task::{self, JoinError};
23
24use crate::{
25    browser::cookies::LeetCodeCookies,
26    chromium::items::{
27        cookie::cookie_dao::CookiesQuery,
28        passwd::{login_data_dao::LoginDataQuery, login_data_entities::logins},
29        I64ToChromiumDateTime,
30    },
31};
32
33#[derive(Debug)]
34#[derive(thiserror::Error)]
35pub enum ChromiumError {
36    #[error(transparent)]
37    Task(#[from] JoinError),
38    #[error(transparent)]
39    Db(#[from] DbErr),
40    #[error(transparent)]
41    Decrypt(#[from] chromium_crypto::error::CryptoError),
42}
43
44type Result<T> = std::result::Result<T, ChromiumError>;
45
46/// Chromium based, get cookies, etc. and decrypt
47///
48/// Initialize it with `ChromiumBuilder`
49///
50/// # Example
51/// ```rust, ignore
52/// let getter = ChromiumBuilder::new(Chromium::new())
53///     .build()
54///     .await?;
55/// getter
56///     .get_cookies_session_csrf(host)
57///     .await?
58/// ```
59#[derive(Clone)]
60#[derive(Debug)]
61#[derive(Default)]
62pub struct ChromiumGetter<T> {
63    pub(crate) cookies_query: CookiesQuery,
64    pub(crate) login_data_query: LoginDataQuery,
65    pub(crate) login_data_for_account_query: Option<LoginDataQuery>,
66    pub(crate) crypto: Decrypter,
67    pub(crate) __browser: PhantomData<T>,
68}
69
70impl<T: Send + Sync> ChromiumGetter<T> {
71    async fn par_decrypt_logins(&self, raw: Vec<logins::Model>) -> Result<Vec<LoginData>> {
72        let crypto = self.crypto.clone();
73
74        let login_data = task::spawn_blocking(move || {
75            raw.into_par_iter()
76                .map(|mut v| {
77                    let res = v
78                        .password_value
79                        .as_mut()
80                        .map_or_else(String::new, |passwd| {
81                            crypto
82                                .decrypt(passwd)
83                                .unwrap_or_default()
84                        });
85                    let mut cookies = LoginData::from(v);
86                    cookies.set_password_value(res);
87                    cookies
88                })
89                .collect()
90        })
91        .await;
92        Ok(login_data?)
93    }
94    /// contains passwords
95    ///
96    /// # Example:
97    ///
98    /// ```rust
99    /// use decrypt_cookies::prelude::*;
100    ///
101    /// #[tokio::main]
102    /// async fn main() {
103    ///     let edge_getter = ChromiumBuilder::<Chrome>::new()
104    ///         .build()
105    ///         .await
106    ///         .unwrap();
107    ///     let res = edge_getter
108    ///         .logins_filter(ChromiumLoginCol::OriginUrl.contains("google.com"))
109    ///         .await
110    ///         .unwrap_or_default();
111    ///     dbg!(res);
112    /// }
113    /// ```
114    pub async fn logins_filter<F>(&self, filter: F) -> Result<Vec<LoginData>>
115    where
116        F: IntoCondition + Send + Clone,
117    {
118        let mut raw_login = self
119            .login_data_query
120            .query_login_dt_filter(filter.clone())
121            .await?;
122        if raw_login.is_empty() {
123            if let Some(query) = &self.login_data_for_account_query {
124                raw_login = query
125                    .query_login_dt_filter(filter)
126                    .await?;
127            }
128        }
129        self.par_decrypt_logins(raw_login)
130            .await
131    }
132    pub async fn logins_by_host<F>(&self, host: F) -> Result<Vec<LoginData>>
133    where
134        F: AsRef<str> + Send,
135    {
136        let mut raw_login = self
137            .login_data_query
138            .query_login_dt_filter(ChromiumLoginCol::OriginUrl.contains(host.as_ref()))
139            .await?;
140        if raw_login.is_empty() {
141            if let Some(query) = &self.login_data_for_account_query {
142                raw_login = query
143                    .query_login_dt_filter(ChromiumLoginCol::OriginUrl.contains(host.as_ref()))
144                    .await?;
145            }
146        }
147        self.par_decrypt_logins(raw_login)
148            .await
149    }
150    /// contains passwords
151    pub async fn all_logins(&self) -> Result<Vec<LoginData>> {
152        let mut raw_login = self
153            .login_data_query
154            .query_all_login_dt()
155            .await?;
156        if raw_login.is_empty() {
157            if let Some(query) = &self.login_data_for_account_query {
158                raw_login = query.query_all_login_dt().await?;
159            }
160        }
161        self.par_decrypt_logins(raw_login)
162            .await
163    }
164}
165
166impl<T: Send + Sync> ChromiumGetter<T> {
167    /// filter cookies
168    ///
169    /// # Example:
170    ///
171    /// ```rust
172    /// use decrypt_cookies::prelude::*;
173    ///
174    /// #[tokio::main]
175    /// async fn main() {
176    ///     let edge_getter = ChromiumBuilder::<Chrome>::new()
177    ///         .build()
178    ///         .await
179    ///         .unwrap();
180    ///     let res = edge_getter
181    ///         .cookies_filter(ChromiumCookieCol::HostKey.contains("google.com"))
182    ///         .await
183    ///         .unwrap_or_default();
184    ///     dbg!(res);
185    /// }
186    /// ```
187    pub async fn cookies_filter<F>(&self, filter: F) -> Result<Vec<ChromiumCookie>>
188    where
189        F: IntoCondition + Send,
190    {
191        let raw_ck = self
192            .cookies_query
193            .cookie_filter(filter)
194            .await?;
195        self.par_decrypt_ck(raw_ck).await
196    }
197    /// decrypt Cookies
198    pub async fn cookies_by_host<A: AsRef<str> + Send>(
199        &self,
200        host: A,
201    ) -> Result<Vec<ChromiumCookie>> {
202        let raw_ck = self
203            .cookies_query
204            .cookie_by_host(host.as_ref())
205            .await?;
206        self.par_decrypt_ck(raw_ck).await
207    }
208
209    /// return all cookies
210    pub async fn all_cookies(&self) -> Result<Vec<ChromiumCookie>> {
211        let raw_ck = self
212            .cookies_query
213            .all_cookie()
214            .await?;
215        self.par_decrypt_ck(raw_ck).await
216    }
217
218    /// parallel decrypt cookies
219    /// and not blocking scheduling
220    async fn par_decrypt_ck(&self, raw: Vec<cookies::Model>) -> Result<Vec<ChromiumCookie>> {
221        let crypto = self.crypto.clone();
222
223        let decrypted_ck = task::spawn_blocking(move || {
224            raw.into_par_iter()
225                .map(|mut v| {
226                    let res = crypto
227                        .decrypt(&mut v.encrypted_value)
228                        .unwrap_or_default();
229                    let mut cookies = ChromiumCookie::from(v);
230                    cookies.set_encrypted_value(res);
231                    cookies
232                })
233                .collect()
234        })
235        .await?;
236        Ok(decrypted_ck)
237    }
238
239    /// get `LEETCODE_SESSION` and `csrftoken` for leetcode
240    pub async fn get_session_csrf<A: AsRef<str> + Send>(&self, host: A) -> Result<LeetCodeCookies> {
241        let cookies = self
242            .cookies_query
243            .cookie_filter(
244                ChromiumCookieCol::HostKey
245                    .contains(host.as_ref())
246                    .and(
247                        ChromiumCookieCol::Name
248                            .eq("csrftoken")
249                            .or(ChromiumCookieCol::Name.eq("LEETCODE_SESSION")),
250                    ),
251            )
252            .await?;
253
254        let mut csrf_token = LeetCodeCookies::default();
255        let mut hds = Vec::with_capacity(2);
256
257        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
258        enum CsrfSession {
259            Csrf,
260            Session,
261        }
262        for mut cookie in cookies {
263            if cookie.name == "csrftoken" {
264                let expir = cookie
265                    .expires_utc
266                    .micros_to_chromium_utc();
267                if let Some(expir) = expir {
268                    if Utc::now() > expir {
269                        csrf_token.expiry = true;
270                        break;
271                    }
272                }
273
274                let cy = self.crypto.clone();
275                let csrf_hd =
276                    task::spawn_blocking(move || match cy.decrypt(&mut cookie.encrypted_value) {
277                        Ok(it) => it,
278                        Err(err) => {
279                            tracing::warn!("decrypt csrf failed: {err}");
280                            String::new()
281                        },
282                    });
283                hds.push((csrf_hd, CsrfSession::Csrf));
284            }
285            else if cookie.name == "LEETCODE_SESSION" {
286                let expir = cookie
287                    .expires_utc
288                    .micros_to_chromium_utc();
289                if let Some(expir) = expir {
290                    if Utc::now() > expir {
291                        csrf_token.expiry = true;
292                        break;
293                    }
294                }
295
296                let cy = self.crypto.clone();
297                let session_hd =
298                    task::spawn_blocking(move || match cy.decrypt(&mut cookie.encrypted_value) {
299                        Ok(it) => it,
300                        Err(err) => {
301                            tracing::warn!("decrypt session failed: {err}");
302                            String::new()
303                        },
304                    });
305                hds.push((session_hd, CsrfSession::Session));
306            }
307        }
308        for (handle, flag) in hds {
309            let res = handle.await?;
310            match flag {
311                CsrfSession::Csrf => csrf_token.csrf = res,
312                CsrfSession::Session => csrf_token.session = res,
313            }
314        }
315        Ok(csrf_token)
316    }
317}
318
319impl<T> ChromiumGetter<T> {
320    /// the browser's decrypt
321    pub fn decrypt(&self, ciphertext: &mut [u8]) -> Result<String> {
322        Ok(self.crypto.decrypt(ciphertext)?)
323    }
324}