baesd ruste crypto?!!?1'1??!?!!?!?

This commit is contained in:
uh wot 2021-08-02 17:26:47 +02:00
parent 6994f35062
commit 30153cfb8a
Signed by: uhwot
GPG Key ID: CB2454984587B781
10 changed files with 7234 additions and 69 deletions

12
.gitignore vendored
View File

@ -1,4 +1,10 @@
node_modules/ /target
/dist
**/*.rs.bk
Cargo.lock
bin/
pkg/
wasm-pack.log
worker/ worker/
.cargo-ok node_modules/
package-lock.json .cargo-ok

26
Cargo.toml Normal file
View File

@ -0,0 +1,26 @@
[package]
name = "dzserver"
version = "0.1.0"
authors = ["uh wot <uhwot@protonmail.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
block-modes = "0.8"
blowfish = "0.8"
aes = "0.7"
md-5 = "0.9"
hex = "0.4"
js-sys = "0.3"
wasm-bindgen = "=0.2.74"
[dev-dependencies]
wasm-bindgen-test = "0.3"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
lto = true

File diff suppressed because one or more lines are too long

8
dist/worker.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,58 +1,10 @@
const Router = require('./router') const Router = require('./router')
const aesjs = require('aes-js');
const ID3Writer = require('browser-id3-writer'); const ID3Writer = require('browser-id3-writer');
const Blowfish = require('./blowfish');
addEventListener('fetch', event => { addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request)) event.respondWith(handleRequest(event.request))
}) })
const trackCDNKey = [106, 111, 54, 97, 101, 121, 54, 104, 97, 105, 100, 50, 84, 101, 105, 104]
const bfSecret = [103, 52, 101, 108, 53, 56, 119, 99, 48, 122, 118, 102, 57, 110, 97, 49]
function str2bin(str) {
return Array.from(str).map(function (item) {
return item.charCodeAt(0);
})
}
function bin2str(bin) {
return String.fromCharCode.apply(String, bin);
}
async function bf_key(id) {
let id_md5 = await crypto.subtle.digest('MD5', new Uint8Array(str2bin(id)))
id_md5 = aesjs.utils.hex.fromBytes(new Uint8Array(id_md5))
key = []
for (let i = 0; i < 16; i++) {
key.push(id_md5[i].charCodeAt(0) ^ id_md5[i + 16].charCodeAt(0) ^ bfSecret[i])
}
return bin2str(key)
}
async function url_gen(md5_origin, format, id, media_version) {
const cipher = new aesjs.ModeOfOperation.ecb(trackCDNKey)
let result = [md5_origin, format, id, media_version].join('\xa4')
let result_md5 = await crypto.subtle.digest('MD5', new Uint8Array(str2bin(result)))
result_md5 = aesjs.utils.hex.fromBytes(new Uint8Array(result_md5))
result = result_md5 + '\xa4' + result + '\xa4'
// zero-padding
if (result.length % 16) {
result += '\x00'.repeat(16 - result.length % 16)
}
result = aesjs.utils.hex.fromBytes(cipher.encrypt(str2bin(result)))
// cdn template with first character of md5 string + hash
return `https://cdns-proxy-${md5_origin[0]}.dzcdn.net/mobile/1/${result}`
}
const format_string_to_num = { const format_string_to_num = {
'64': '10', '64': '10',
'128': '1', '128': '1',
@ -113,7 +65,9 @@ async function track(id, format, access_token, tagging) {
return new Response(JSON.stringify(json.error), { status: 403, headers: { 'content-type': "application/json" } }) return new Response(JSON.stringify(json.error), { status: 403, headers: { 'content-type': "application/json" } })
} }
result = await track_url(json, format) const wasm = await import('./pkg')
result = await track_url(json, format, wasm.stream_url)
if (typeof result === 'object') { if (typeof result === 'object') {
return result return result
} }
@ -189,18 +143,16 @@ async function track(id, format, access_token, tagging) {
id = json.alternative.id.toString() id = json.alternative.id.toString()
} }
const bfKey = await bf_key(id) const cipher = new wasm.Cipher(id)
const length = Number(track.headers.get('Content-Length')) const length = Number(track.headers.get('Content-Length'))
pipeDecryptedStream(writer, track.body, length, bfKey) pipeDecryptedStream(writer, track.body, length, cipher)
return new Response(readable, { status: 200, headers: { 'content-type': 'audio/mpeg' } }) return new Response(readable, { status: 200, headers: { 'content-type': 'audio/mpeg' } })
} }
async function pipeDecryptedStream(writer, body, length, bfKey) { async function pipeDecryptedStream(writer, body, length, cipher) {
const cipher = new Blowfish(bfKey, Blowfish.MODE.CBC, Blowfish.PADDING.NULL)
cipher.setIv('\x00\x01\x02\x03\x04\x05\x06\x07')
const reader = body.getReader({ mode: 'byob' }) const reader = body.getReader({ mode: 'byob' })
let byteCount = 0 let byteCount = 0
let end = false let end = false
@ -224,10 +176,10 @@ async function pipeDecryptedStream(writer, body, length, bfKey) {
if (byteCount % 6144 === 0 && !end) { if (byteCount % 6144 === 0 && !end) {
// encrypted chunk // encrypted chunk
chunk = cipher.decode(chunk, Blowfish.TYPE.UINT8_ARRAY) chunk = cipher.decrypt_chunk(chunk)
} }
writer.write(chunk) await writer.write(chunk)
byteCount += 2048 byteCount += 2048
} }
@ -235,7 +187,7 @@ async function pipeDecryptedStream(writer, body, length, bfKey) {
await writer.close() await writer.close()
} }
async function track_url(json, format) { function track_url(json, format, url_func) {
// needed if track has fallback, like https://www.deezer.com/track/11835714 // needed if track has fallback, like https://www.deezer.com/track/11835714
if (json.alternative) { if (json.alternative) {
json = json.alternative json = json.alternative
@ -255,7 +207,7 @@ async function track_url(json, format) {
format = format_string_to_num[format] format = format_string_to_num[format]
return await url_gen(md5_origin, format, id, media_version) return url_func(md5_origin, format, id, media_version)
} }
async function m3u8(type, id, format, access_token, tagging) { async function m3u8(type, id, format, access_token, tagging) {

7102
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,10 @@
"author": "uh_wot <uhwot@protonmail.com>", "author": "uh_wot <uhwot@protonmail.com>",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@wasm-tool/wasm-pack-plugin": "^1.5.0",
"prettier": "^1.17.0" "prettier": "^1.17.0"
}, },
"dependencies": { "dependencies": {
"aes-js": "^3.1.2",
"browser-id3-writer": "^4.4.0", "browser-id3-writer": "^4.4.0",
"serverless-cloudflare-workers": "^1.2.0" "serverless-cloudflare-workers": "^1.2.0"
} }

68
src/lib.rs Normal file
View File

@ -0,0 +1,68 @@
use wasm_bindgen::prelude::*;
use js_sys::Uint8Array;
use blowfish::Blowfish;
use aes::Aes128;
use block_modes::{BlockMode, Cbc, Ecb};
use block_modes::block_padding::{NoPadding, ZeroPadding};
use md5::{Md5, Digest};
type BfCbc = Cbc<Blowfish, NoPadding>;
type Aes128Ecb = Ecb<Aes128, ZeroPadding>;
const TRACK_CDN_KEY: [u8; 16] = [106, 111, 54, 97, 101, 121, 54, 104, 97, 105, 100, 50, 84, 101, 105, 104];
const BF_SECRET: [u8; 16] = [103, 52, 101, 108, 53, 56, 119, 99, 48, 122, 118, 102, 57, 110, 97, 49];
#[wasm_bindgen]
pub struct Cipher {
cipher: BfCbc
}
#[wasm_bindgen]
impl Cipher {
#[wasm_bindgen(constructor)]
pub fn new(id: &str) -> Self {
let id_md5 = format!("{:x}", Md5::digest(id.as_bytes()));
let id_md5 = id_md5.as_bytes();
let mut key = [0u8; 16];
for i in 0..16 {
key[i] = id_md5[i] ^ id_md5[i+16] ^ BF_SECRET[i]
};
Self { cipher: BfCbc::new_from_slices(&key, &[0, 1, 2, 3, 4, 5, 6, 7]).unwrap() }
}
pub fn decrypt_chunk(&self, chunk: &mut [u8]) -> Uint8Array {
let chunk = self.cipher.clone().decrypt(chunk).unwrap();
unsafe { Uint8Array::view(&chunk) }
}
}
#[wasm_bindgen]
pub fn stream_url(md5_origin: &str, format: &str, id: i32, media_version: u8) -> String {
// md5 origin + format num + id + media version
let metadata = [
md5_origin.as_bytes(),
&[b'\xa4'],
format.as_bytes(),
&[b'\xa4'],
id.to_string().as_bytes(),
&[b'\xa4'],
media_version.to_string().as_bytes(),
].concat();
let hash = format!("{:x}", Md5::digest(&metadata));
// md5 hash + previous metadata
let metadata_hash = [
hash.as_bytes(),
&[b'\xa4'],
&metadata,
&[b'\xa4'],
].concat();
let cipher = Aes128Ecb::new_from_slices(&TRACK_CDN_KEY, Default::default()).unwrap();
let ciphertext = cipher.encrypt_vec(&metadata_hash);
format!("https://cdns-proxy-{}.dzcdn.net/mobile/1/{}", md5_origin.chars().next().unwrap(), hex::encode(ciphertext))
}

13
webpack.config.js Normal file
View File

@ -0,0 +1,13 @@
const path = require("path");
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
module.exports = {
entry: {
index: "./index.js"
},
plugins: [
new WasmPackPlugin({
crateDirectory: __dirname,
}),
]
};

View File

@ -1,5 +1,6 @@
name = "dz" name = "dz"
type = "webpack" type = "webpack"
webpack_config = "webpack.config.js"
account_id = "03479b0523a52b140e0dabac40cb0fc8" account_id = "03479b0523a52b140e0dabac40cb0fc8"
workers_dev = true workers_dev = true
route = "" route = ""