Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
[package]
name = "dlc-decrypter"
version = "0.2.1"
edition = "2024"
authors = ["Tobias Matt <t.matt81@gmail.com>"]
license = "MIT"
repository = "https://github.com/Bubblepoint/dlc-decoder"
description = "A decoder library for dlc files"

[dependencies]
error-chain = "0.11.0"
base64 = "0.9.0"
reqwest = "0.8.5"
error-chain = "0.12.4"
base64 = "0.22.1"
reqwest = "0.13.0"
rust-crypto = "0.2.36"
regex = "1.0.0"
tokio = { version = "1", features = ["full"] }

[lib]
name = "dlc_decrypter"
5 changes: 3 additions & 2 deletions examples/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ extern crate dlc_decrypter;
use std::env;
use dlc_decrypter::DlcDecoder;

fn main() {
#[tokio::main]
async fn main() {
// Create the DlcDecoder
let dd = DlcDecoder::new();

Expand All @@ -12,7 +13,7 @@ fn main() {
// own name
for arg in env::args().skip(1) {
// hand over the file path
let dlc = dd.from_file(arg);
let dlc = dd.from_file(arg).await;

// print the result
println!("DLC: {:?}", dlc);
Expand Down
50 changes: 25 additions & 25 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ use std::ops::Deref;
use std::fs::File;
use std::str;
use reqwest::Client;
use reqwest::header::{Connection, UserAgent};
use reqwest::header;
use crypto::{buffer, aes, blockmodes };
use crypto::buffer::{ ReadBuffer, WriteBuffer };
use regex::Regex;
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::Engine as _;



Expand Down Expand Up @@ -146,20 +148,20 @@ impl DlcDecoder {
}

/// Decrypt a specified .dlc file
pub fn from_file<P: Into<String>>(&self, path: P) -> Result<DlcPackage> {
pub async fn from_file<P: Into<String>>(&self, path: P) -> Result<DlcPackage> {
// read the file
let mut file = File::open(path.into())?;
let mut data = Vec::new();
file.read_to_end(&mut data)?;

// return the decrypted dlc package
self.from_data(&data)
self.from_data(&data).await
}

/// Decrypt the contet of a .dlc file.
pub fn from_data(&self, data: &[u8]) -> Result<DlcPackage> {
pub async fn from_data(&self, data: &[u8]) -> Result<DlcPackage> {
// decrypt the .dlc data
let data = self.decrypt_dlc(data)?;
let data = self.decrypt_dlc(data).await?;

// parse the dlc header data
let mut dlc = self.parse_header(&data)?;
Expand All @@ -172,7 +174,7 @@ impl DlcDecoder {

/******************* Private Functions *****************/
/// Decrypt the .dlc data to a plain String
fn decrypt_dlc(&self, data: &[u8]) -> Result<String> {
async fn decrypt_dlc(&self, data: &[u8]) -> Result<String> {
// check if the file is to short to get the key out of it
if data.len() <= 88 {
bail!("Corrupted data");
Expand All @@ -182,17 +184,17 @@ impl DlcDecoder {
let (data, key) = data.split_at(data.len() - 88);

// get decrypten key
let key = self.get_jd_decryption_key(key)?;
let key = self.get_jd_decryption_key(key).await?;

// decrypt the key
let key = DlcDecoder::decrypt_raw_data(&key, &self.jd_decryption_key, &self.jd_decryption_iv)?;

// decrypt the content
let data = base64::decode(data)?;
let data = BASE64.decode(data)?;
let data = DlcDecoder::decrypt_raw_data(&data, key.deref(), key.deref())?;

// format to text
let data = base64::decode(&data)?;
let data = BASE64.decode(&data)?;
let data = String::from_utf8(data)?;

return Ok(data);
Expand Down Expand Up @@ -236,27 +238,25 @@ impl DlcDecoder {
}

/// Download the decryption key for the .dlc container
fn get_jd_decryption_key(&self, key: &[u8]) -> Result<Vec<u8>> {
async fn get_jd_decryption_key(&self, key: &[u8]) -> Result<Vec<u8>> {
// build the request url
let url = format!("http://service.jdownloader.org/dlcrypt/service.php?srcType=dlc&destType={}&data={}", &self.jd_app_name, str::from_utf8(key)?);

// build up the request
let mut res = Client::new().get(&url)
.header(Connection::close())
.header(UserAgent::new("Mozilla/5.3 (Windows; U; Windows NT 5.1; de; rv:1.8.1.6) Gecko/2232 Firefox/3.0.0.R"))
.send()?;
let res = Client::new().get(&url)
.header(header::CONNECTION, "close")
.header(header::USER_AGENT, "Mozilla/5.3 (Windows; U; Windows NT 5.1; de; rv:1.8.1.6) Gecko/2232 Firefox/3.0.0.R")
.send().await?;

// read the response
let mut key = Vec::new();
res.read_to_end(&mut key)?;
let key = res.bytes().await?.to_vec();

// check if response is long enough
if key.len() != 33 {
bail!("Unexpected Error");
};

// remove <rc> and </rc>
let key = base64::decode(&key[4..28])?;
let key = BASE64.decode(&key[4..28])?;

Ok(key)
}
Expand All @@ -272,12 +272,12 @@ impl DlcDecoder {
// extract the name
let re = Regex::new(r#"name="([^"]*)"#)?;
let t = re.find(&pck).ok_or("Can't find name in data")?;
dlc.name = String::from_utf8(base64::decode(&pck[t.start()+6..t.end()])?)?;
dlc.name = String::from_utf8(BASE64.decode(&pck[t.start()+6..t.end()])?)?;

// extract the password - optional
let re = Regex::new(r#"passwords="([^"]*)"#)?;
if let Some(t) = re.find(&pck) {
dlc.password = String::from_utf8(base64::decode(&pck[t.start()+11..t.end()])?)?;
dlc.password = String::from_utf8(BASE64.decode(&pck[t.start()+11..t.end()])?)?;
}

Ok(dlc)
Expand Down Expand Up @@ -315,7 +315,7 @@ impl DlcDecoder {
/// Get the files details
fn file_details(&self, data: String, pos: usize) -> String {
// try to decode the data
let buf: String = match base64::decode(&data[pos..]) {
let buf: String = match BASE64.decode(&data[pos..]) {
Ok(x) => {
match String::from_utf8(x) {
Ok(x) => x,
Expand Down Expand Up @@ -345,22 +345,22 @@ fn aes_cbc_decryptor<X: PaddingProcessor + Send + 'static>(
key_size: KeySize,
key: &[u8],
iv: &[u8],
padding: X) -> Box<Decryptor + 'static> {
padding: X) -> Box<dyn Decryptor + 'static> {
match key_size {
KeySize::KeySize128 => {
let aes_dec = aessafe::AesSafe128Decryptor::new(key);
let dec = Box::new(CbcDecryptor::new(aes_dec, padding, iv.to_vec()));
dec as Box<Decryptor + 'static>
dec as Box<dyn Decryptor + 'static>
}
KeySize::KeySize192 => {
let aes_dec = aessafe::AesSafe192Decryptor::new(key);
let dec = Box::new(CbcDecryptor::new(aes_dec, padding, iv.to_vec()));
dec as Box<Decryptor + 'static>
dec as Box<dyn Decryptor + 'static>
}
KeySize::KeySize256 => {
let aes_dec = aessafe::AesSafe256Decryptor::new(key);
let dec = Box::new(CbcDecryptor::new(aes_dec, padding, iv.to_vec()));
dec as Box<Decryptor + 'static>
dec as Box<dyn Decryptor + 'static>
}
}
}