1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
use anyhow::Result;
use base64::{engine::general_purpose, Engine as _};
/**
 * encrypt.rs
 * Copyright (C) 2019 gmg137 <gmg137@live.com>
 * Distributed under terms of the GPLv3 license.
 */
use lazy_static::lazy_static;
use libaes::Cipher;
use md5::compute;
use num_bigint::BigUint;
use rand::{rngs::OsRng, Rng, RngCore};
use std::convert::TryFrom;

// below imports are left for debug
// use std::io::Write;
// use openssl::rsa;

lazy_static! {
    static ref IV: Vec<u8> = b"0102030405060708".to_vec();
    static ref PRESET_KEY: Vec<u8> = b"0CoJUm6Qyw8W8jud".to_vec();
    static ref LINUX_API_KEY: Vec<u8> = b"rFgB&h#%2?^eDg:Q".to_vec();
    static ref BASE62: Vec<u8> = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".as_bytes().to_vec();
    static ref RSA_PUBLIC_KEY: Vec<u8> = b"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7clFSs6sXqHauqKWqdtLkF2KexO40H1YTX8z2lSgBBOAxLsvaklV8k4cBFK9snQXE9/DDaFt6Rr7iVZMldczhC0JNgTz+SHXT6CBHuX3e9SdB1Ua44oncaTWz7OBGLbCiK45wIDAQAB\n-----END PUBLIC KEY-----".to_vec();
    static ref EAPIKEY: Vec<u8> = b"e82ckenh8dichen8".to_vec();
}
static MODULUS: &str = "e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7";
// static MODULUS:&str =
// "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7"
// ;
static PUBKEY: &str = "010001";

#[allow(non_snake_case)]
pub struct Crypto;

#[allow(dead_code)]
impl Crypto {
    pub fn hex_random_bytes(n: usize) -> String {
        let mut data: Vec<u8> = Vec::with_capacity(n);
        OsRng.fill_bytes(&mut data);
        hex::encode(data)
    }

    pub fn alpha_lowercase_random_bytes(n: usize) -> String {
        const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
        let mut rng = rand::thread_rng();

        let rand_string: String = (0..n)
            .map(|_| {
                let idx = rng.gen_range(0..CHARSET.len());
                CHARSET[idx] as char
            })
            .collect();

        rand_string
    }

    pub fn eapi(url: &str, text: &str) -> Result<String> {
        let message = format!("nobody{url}use{text}md5forencrypt");
        let hash = compute(message.as_bytes());
        let digest = hex::encode(hash.as_ref());

        let data = format!("{url}-36cd479b6b5-{text}-36cd479b6b5-{digest}");
        let params = Self::aes_encrypt(&data, &EAPIKEY, Some(&*IV))?;
        let params = hex::encode_upper(params);

        let p_value = Self::escape(&params);
        let result = format!("params={p_value}&");
        Ok(result)
    }

    pub fn weapi(text: &str) -> Result<String> {
        let mut secret_key = [0_u8; 16];
        OsRng.fill_bytes(&mut secret_key);
        let key: Vec<u8> = secret_key
            .iter()
            .map(|i| BASE62[(i % 62) as usize])
            .collect();

        // let b64 = general_purpose::STANDARD.encode(b"hello world~");
        // println!("{}", b64);

        // const CUSTOM_ENGINE: engine::GeneralPurpose =
        // engine::GeneralPurpose::new(&alphabet::URL_SAFE, general_purpose::NO_PAD);

        // let b64_url = CUSTOM_ENGINE.encode(b"hello internet~");

        let params1 = Self::aes_encrypt(text, &PRESET_KEY, Some(&*IV))?;

        let params = Self::aes_encrypt(&params1, &key, Some(&*IV))?;

        let key_string = key.iter().map(|&c| c as char).collect::<String>();
        let enc_sec_key = Self::rsa(&key_string);

        // code here is left for debug, as probably the public key could be banned from netease
        // server, thus we should replace the exp and modulus.

        // if let Ok(key) = rsa::Rsa::public_key_from_pem(&RSA_PUBLIC_KEY) {
        //     let modulus = key.n();
        //     let exp = key.e();
        //     let mut file = std::fs::File::create("data.txt").expect("create failed");
        //     let _drop = writeln!(
        //         &mut file,
        //         "modulus is:{} and exp is:{}",
        //         modulus.to_hex_str().unwrap().to_lowercase(),
        //         exp.to_hex_str().unwrap(),
        //     );
        // }

        let p_value = Self::escape(&params);
        let enc_value = Self::escape(&enc_sec_key);

        Ok(format!("params={p_value}&encSecKey={enc_value}&"))
    }

    pub fn linuxapi(text: &str) -> Result<String> {
        let params = Self::aes_encrypt(text, &LINUX_API_KEY, None)?;
        let params = hex::encode(params).to_uppercase();

        let e_value = Self::escape(&params);
        Ok(format!("eparams={e_value}&"))
    }

    pub fn aes_encrypt(data: &str, key: &[u8], iv: Option<&[u8]>) -> Result<String> {
        let mut iv_real: Vec<u8> = vec![0_u8; 16];
        if let Some(i) = iv {
            iv_real = i.to_vec();
        }
        // Create a new 128-bit cipher
        let key_16 = <&[u8; 16]>::try_from(key)?;
        let cipher = Cipher::new_128(key_16);

        // Encryption
        let encrypted = cipher.cbc_encrypt(&iv_real, data.as_bytes());

        Ok(general_purpose::URL_SAFE.encode(encrypted))
        // encode(encrypted)
    }

    fn rsa(text: &str) -> String {
        let text = text.chars().rev().collect::<String>();
        let text = BigUint::parse_bytes(hex::encode(text).as_bytes(), 16).unwrap();
        let pubkey = BigUint::parse_bytes(PUBKEY.as_bytes(), 16).unwrap();
        let modulus = BigUint::parse_bytes(MODULUS.as_bytes(), 16).unwrap();
        let pow = text.modpow(&pubkey, &modulus);
        pow.to_str_radix(16)
    }

    // This is for getting url of picture.
    pub fn encrypt_id(id: &str) -> String {
        let magic = b"3go8&$8*3*3h0k(2)2";
        let magic_len = magic.len();
        let id = id;
        let mut song_id = id.to_string().into_bytes();
        id.as_bytes().iter().enumerate().for_each(|(i, sid)| {
            song_id[i] = *sid ^ magic[i % magic_len];
        });

        general_purpose::URL_SAFE
            .encode(compute(&song_id).as_ref())
            .replace('/', "_")
            .replace('+', "-")
    }

    fn escape(str: &str) -> String {
        let mut enc = Vec::<u8>::new();
        for ch in str.as_bytes() {
            if Self::keep_as(*ch) {
                enc.push(*ch);
            } else {
                enc.push(0x25);
                let n1 = (*ch >> 4) & 0xf;
                let n2 = *ch & 0xf;
                enc.push(Self::to_dec_ascii(n1));
                enc.push(Self::to_dec_ascii(n2));
            }
        }
        String::from_utf8(enc).unwrap()
    }

    const fn keep_as(n: u8) -> bool {
        n.is_ascii_alphanumeric()
            || n == b'*'
            || n == b'-'
            || n == b'.'
            || n == b'_'
            || n == b'\''
            || n == b'~'
            || n == b'!'
            || n == b'('
            || n == b')'
    }

    const fn to_dec_ascii(n: u8) -> u8 {
        match n {
            0 => 48,
            1 => 49,
            2 => 50,
            3 => 51,
            4 => 52,
            5 => 53,
            6 => 54,
            7 => 55,
            8 => 56,
            9 => 57,
            10 => b'A',
            11 => b'B',
            12 => b'C',
            13 => b'D',
            14 => b'E',
            15 => b'F',
            _ => 127,
        }
    }
}