Skip to main content

jd_decrypter/
lib.rs

1//! A simple library to decode JDownloader .ejs files.
2//!
3//! ## Usage
4//! Add `jd_decrypter` as a dependency in `Cargo.toml`:
5//!
6//! ```toml
7//! [dependencies]
8//! jd-decrypter = "0.1.0"
9//! ```
10//!
11//! Use the `jd_decrypter::Decryptor` to decrypt a .ejs file:
12//!
13//! ```rust
14//! extern crate jd_decrypter;
15//! 
16//! use std::env;
17//! use jd_decrypter::JdAccountList;
18//! 
19//! fn main() {
20//!     // loop over all arguments for the programm
21//!     // skip the first one because it's the programm
22//!     // own name
23//!     for arg in env::args().skip(1) {
24//!         // hand over the file path
25//!         let dlc = JdAccountList::from_file(arg);
26//! 
27//!         // print the result
28//!         println!("Accounts: {:?}", dlc);
29//!     }
30//! }
31//! ```
32//!
33//! ## License
34//! Distributed under the MIT License.
35
36#![allow(renamed_and_removed_lints)]
37
38#[macro_use]
39extern crate error_chain;
40#[macro_use]
41extern crate serde_derive;
42extern crate crypto;
43extern crate serde;
44extern crate serde_json;
45
46use crypto::buffer::{ReadBuffer, WriteBuffer};
47use crypto::{aes, blockmodes, buffer};
48use std::fs::File;
49use std::io::Read;
50use std::collections::HashMap;
51
52use serde_json::{Value};
53
54const ACCOUNT_KEY: [u8; 16] = [1, 6, 4, 5, 2, 7, 4, 3, 12, 61, 14, 75, 254, 249, 212, 33]; // AccountSettings.java
55
56//const auth_key: [u8; 16] = [2, 4, 4, 5, 2, 7, 4, 3, 12, 61, 14, 75, 254, 249, 212, 33];	    // AuthenticationControllerSettings.java
57//const proxy_key: [u8;16] = [1, 3, 17, 1, 1, 84, 1, 1, 1, 2, 193, 1, 17, 1, 34, 244];		// ProxySelectorImpl.java
58//const crawler_key: [u8;16] = [1, 3, 17, 1, 1, 84, 1, 1, 1, 1, 18, 1, 1, 1, 34, 1];  		// CrawlerPluginController.java
59//const config_key: [u8;16 ] = [1, 2, 17, 1, 1, 84, 1, 1, 1, 1, 18, 1, 1, 1, 34, 1];			// JSonStorage.java, SubConfiguration.java
60//const file_key: [u8;16] = [0, 2, 17, 1, 1, 84, 2, 1, 1, 1, 18, 1, 1, 1, 18, 1];    		    // ExtFileChooseIdConfig.java
61//const dload_key: [u8;16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];		    // DownloadLinkStorable.java
62
63/// JdAccountlist has all accounts grouped by name
64#[derive(Debug, Default, Serialize, Deserialize)]
65pub struct JdAccountList(HashMap<String, Vec<JdAccount>>);
66
67/// Struct to decode the .dlc file or data into an readable format.
68#[derive(Debug, Default, Serialize, Deserialize)]
69pub struct JdAccount {
70    pub properties: Value,
71    pub hoster: String,
72    #[serde(rename = "maxSimultanDownloads")]
73    pub max_simultan_downloads: isize,
74    pub password : String,
75    #[serde(rename = "infoProperties")]
76    pub info_properties: Value,
77    #[serde(rename = "createTime")]
78    pub create_time: isize,
79    #[serde(rename = "trafficLeft")]
80    pub traffic_left: isize,
81    #[serde(rename = "trafficMax")]
82    pub traffic_max: isize,
83    #[serde(rename = "validUntil")]
84    pub valid_until: isize,
85    pub active: bool,
86    pub enabled: bool,
87    #[serde(rename = "trafficUnlimited")]
88    pub traffic_unlimited: bool,
89    pub specialtraffic: bool,
90    pub user: String,
91    #[serde(rename = "concurrentUsePossible")]
92    pub concurrent_use_possible: bool,
93    pub id: usize,
94    #[serde(rename = "errorType")]
95    pub error_type: Option<String>,
96    #[serde(rename = "errorString")]
97    pub error_string: Option<String>,
98}
99
100impl JdAccountList {
101    /// Decrypt a specified .dlc file
102    pub fn from_file<P: Into<String>>(path: P) -> Result<JdAccountList> {
103        // read the file
104        let mut file = File::open(path.into())?;
105        let mut data = Vec::new();
106        file.read_to_end(&mut data)?;
107
108        // return the decrypted dlc package
109        JdAccountList::from_data(&data)
110    }
111
112    /// Decrypt the contet of a .dlc file.
113    pub fn from_data(data: &[u8]) -> Result<JdAccountList> {
114        let mut data = decrypt_raw_data(data, &ACCOUNT_KEY, &ACCOUNT_KEY)?;
115
116        // remove all data from the end of the string until we reach the json data
117        while data.last().ok_or("No decrypted data")? != &0x7Du8 {
118            data.pop().ok_or("No decrypted data in loop")?;
119        }
120
121        // get the string
122        let data = std::str::from_utf8(&data)?;
123        let al: JdAccountList = serde_json::from_str(data)?;
124        Ok(al)
125    }
126
127    /// Get a reference to the internal HashMap with all accounts
128    pub fn as_ref(&self) -> &HashMap<String, Vec<JdAccount>> {
129        &self.0
130    }
131}
132
133/// Decrypt data by the given key and iv.
134fn decrypt_raw_data(data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>> {
135    // create decryptor and set keys & values
136    let mut decryptor = aes_cbc_decryptor(aes::KeySize::KeySize128, key, iv, blockmodes::NoPadding);
137
138    // create the buffer objects
139    let mut buffer = [0; 4096];
140    let mut read_buffer = buffer::RefReadBuffer::new(data);
141    let mut writ_buffer = buffer::RefWriteBuffer::new(&mut buffer);
142    let mut result = Vec::new();
143
144    loop {
145        // decrypt the buffer
146        if decryptor
147            .decrypt(&mut read_buffer, &mut writ_buffer, true)
148            .is_err()
149        {
150            bail!("Can't decrypt");
151        }
152
153        // when the write_buffer is empty, the decryption is finished
154        if writ_buffer.is_empty() {
155            break;
156        }
157
158        // add the encrypted data to the result
159        result.extend_from_slice(writ_buffer.take_read_buffer().take_remaining());
160    }
161
162    // remove tailing zeros
163    result.retain(|x| *x != 0 as u8);
164
165    return Ok(result);
166}
167
168// use only for crypto
169use crypto::aes::KeySize;
170use crypto::aessafe;
171use crypto::blockmodes::{CbcDecryptor, PaddingProcessor};
172use crypto::symmetriccipher::Decryptor;
173
174/// Reimplementation of the aes cbc decryptor function from Rust-Crypto.
175///
176/// This function always use the software decryption insted of the hardware one.
177/// This can have a samll speed impact. But the hardware decryption fails for
178/// musl-docker builds and is shutting down any programm without a warning.
179///
180/// To garuntee the stability of the dlc-decryptor, we use the software decryption.
181fn aes_cbc_decryptor<X: PaddingProcessor + Send + 'static>(
182    key_size: KeySize,
183    key: &[u8],
184    iv: &[u8],
185    padding: X,
186) -> Box<Decryptor + 'static> {
187    match key_size {
188        KeySize::KeySize128 => {
189            let aes_dec = aessafe::AesSafe128Decryptor::new(key);
190            let dec = Box::new(CbcDecryptor::new(aes_dec, padding, iv.to_vec()));
191            dec as Box<Decryptor + 'static>
192        }
193        KeySize::KeySize192 => {
194            let aes_dec = aessafe::AesSafe192Decryptor::new(key);
195            let dec = Box::new(CbcDecryptor::new(aes_dec, padding, iv.to_vec()));
196            dec as Box<Decryptor + 'static>
197        }
198        KeySize::KeySize256 => {
199            let aes_dec = aessafe::AesSafe256Decryptor::new(key);
200            let dec = Box::new(CbcDecryptor::new(aes_dec, padding, iv.to_vec()));
201            dec as Box<Decryptor + 'static>
202        }
203    }
204}
205
206// Error_Chain error handling
207error_chain!{
208
209    types {
210        Error, ErrorKind, ResultExt, Result;
211    }
212
213    foreign_links {
214        Fmt(::std::fmt::Error);
215        Io(::std::io::Error);
216        Utf8(::std::str::Utf8Error);
217        FromUtf8(::std::string::FromUtf8Error);
218        SerdeJson(::serde_json::Error);
219    }
220}