baesd ruste crypto?!!?1'1??!?!!?!?
This commit is contained in:
parent
6994f35062
commit
30153cfb8a
|
@ -1,4 +1,10 @@
|
|||
node_modules/
|
||||
/target
|
||||
/dist
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
bin/
|
||||
pkg/
|
||||
wasm-pack.log
|
||||
worker/
|
||||
.cargo-ok
|
||||
package-lock.json
|
||||
node_modules/
|
||||
.cargo-ok
|
|
@ -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
File diff suppressed because one or more lines are too long
70
index.js
70
index.js
|
@ -1,58 +1,10 @@
|
|||
const Router = require('./router')
|
||||
const aesjs = require('aes-js');
|
||||
const ID3Writer = require('browser-id3-writer');
|
||||
const Blowfish = require('./blowfish');
|
||||
|
||||
addEventListener('fetch', event => {
|
||||
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 = {
|
||||
'64': '10',
|
||||
'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" } })
|
||||
}
|
||||
|
||||
result = await track_url(json, format)
|
||||
const wasm = await import('./pkg')
|
||||
|
||||
result = await track_url(json, format, wasm.stream_url)
|
||||
if (typeof result === 'object') {
|
||||
return result
|
||||
}
|
||||
|
@ -189,18 +143,16 @@ async function track(id, format, access_token, tagging) {
|
|||
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'))
|
||||
|
||||
pipeDecryptedStream(writer, track.body, length, bfKey)
|
||||
pipeDecryptedStream(writer, track.body, length, cipher)
|
||||
|
||||
return new Response(readable, { status: 200, headers: { 'content-type': 'audio/mpeg' } })
|
||||
}
|
||||
|
||||
async function pipeDecryptedStream(writer, body, length, bfKey) {
|
||||
const cipher = new Blowfish(bfKey, Blowfish.MODE.CBC, Blowfish.PADDING.NULL)
|
||||
cipher.setIv('\x00\x01\x02\x03\x04\x05\x06\x07')
|
||||
|
||||
async function pipeDecryptedStream(writer, body, length, cipher) {
|
||||
const reader = body.getReader({ mode: 'byob' })
|
||||
let byteCount = 0
|
||||
let end = false
|
||||
|
@ -224,10 +176,10 @@ async function pipeDecryptedStream(writer, body, length, bfKey) {
|
|||
|
||||
if (byteCount % 6144 === 0 && !end) {
|
||||
// encrypted chunk
|
||||
chunk = cipher.decode(chunk, Blowfish.TYPE.UINT8_ARRAY)
|
||||
chunk = cipher.decrypt_chunk(chunk)
|
||||
}
|
||||
|
||||
writer.write(chunk)
|
||||
await writer.write(chunk)
|
||||
byteCount += 2048
|
||||
}
|
||||
|
||||
|
@ -235,7 +187,7 @@ async function pipeDecryptedStream(writer, body, length, bfKey) {
|
|||
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
|
||||
if (json.alternative) {
|
||||
json = json.alternative
|
||||
|
@ -255,7 +207,7 @@ async function track_url(json, 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) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,10 +10,10 @@
|
|||
"author": "uh_wot <uhwot@protonmail.com>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@wasm-tool/wasm-pack-plugin": "^1.5.0",
|
||||
"prettier": "^1.17.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"aes-js": "^3.1.2",
|
||||
"browser-id3-writer": "^4.4.0",
|
||||
"serverless-cloudflare-workers": "^1.2.0"
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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,
|
||||
}),
|
||||
]
|
||||
};
|
|
@ -1,5 +1,6 @@
|
|||
name = "dz"
|
||||
type = "webpack"
|
||||
webpack_config = "webpack.config.js"
|
||||
account_id = "03479b0523a52b140e0dabac40cb0fc8"
|
||||
workers_dev = true
|
||||
route = ""
|
||||
|
|
Loading…
Reference in New Issue