mirror of
https://git.um-react.app/um/lib_um_crypto_rust.git
synced 2026-03-07 20:19:51 +00:00
feat: added glue exports for kwm/qmc
This commit is contained in:
@@ -16,6 +16,7 @@ default = ["console_error_panic_hook"]
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2.84"
|
||||
anyhow = "1.0.86"
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
|
||||
@@ -1,9 +1,70 @@
|
||||
use crate::errors::map_js_error;
|
||||
use crate::exports::qmc::JsQMC2;
|
||||
use umc_kuwo::kwm_v1::CipherV1;
|
||||
use umc_kuwo::{Cipher, Header};
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::JsError;
|
||||
|
||||
#[wasm_bindgen(js_name=kuwoDecodeEKey)]
|
||||
pub fn decode_ekey(ekey: &str) -> Result<String, JsError> {
|
||||
let ekey = umc_kuwo::des::decode_ekey(ekey, &umc_kuwo::SECRET_KEY).map_err(map_js_error)?;
|
||||
Ok(ekey)
|
||||
/// Kuwo KWM file header.
|
||||
#[wasm_bindgen(js_name=KuwoHeader)]
|
||||
pub struct JsKuwoHeader(Header);
|
||||
|
||||
#[wasm_bindgen(js_class = KuwoHeader)]
|
||||
impl JsKuwoHeader {
|
||||
/// Parse the KuWo header (0x400 bytes)
|
||||
pub fn parse(header: &[u8]) -> Result<JsKuwoHeader, JsError> {
|
||||
let hdr = Header::from_bytes(header).map_err(map_js_error)?;
|
||||
Ok(JsKuwoHeader(hdr))
|
||||
}
|
||||
|
||||
/// Get quality id (used for Android Kuwo APP),
|
||||
/// that can be then used to extract ekey from mmkv db.
|
||||
#[wasm_bindgen(getter, js_name=qualityId)]
|
||||
pub fn quality_id(&self) -> u32 {
|
||||
self.0.get_quality_id()
|
||||
}
|
||||
|
||||
/// Create an instance of cipher (decipher) for decryption
|
||||
#[wasm_bindgen(js_name=makeCipher)]
|
||||
pub fn make_cipher(&self, ekey: Option<String>) -> Result<JsCipher, JsError> {
|
||||
let cipher = self.0.get_cipher(ekey).map_err(map_js_error)?;
|
||||
Ok(JsCipher(cipher))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a decipher instance for "BoDian Music".
|
||||
#[wasm_bindgen(js_name=kuwoBodianCipherFactory)]
|
||||
pub fn js_kuwo_bodian_cipher_factory(ekey: &str) -> Result<JsQMC2, JsError> {
|
||||
let ekey = umc_kuwo::des::decode_ekey(ekey, &umc_kuwo::SECRET_KEY).map_err(map_js_error)?;
|
||||
JsQMC2::new(ekey.as_str())
|
||||
}
|
||||
|
||||
/// Kuwo KWM v1
|
||||
#[wasm_bindgen(js_name=KWMCipherV1)]
|
||||
pub struct JsCipherV1(CipherV1);
|
||||
|
||||
#[wasm_bindgen(js_class=KWMCipherV1)]
|
||||
impl JsCipherV1 {
|
||||
/// Create a decipher instance for "Kuwo KWM v1".
|
||||
pub fn decrypt(&self, buffer: &mut [u8], offset: usize) {
|
||||
self.0.decrypt(buffer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a decipher instance for "Kuwo KWM v2".
|
||||
#[wasm_bindgen(js_name=kuwoV2CipherFactory)]
|
||||
pub fn js_kuwo_v2_cipher_factory(ekey: &str) -> Result<JsQMC2, JsError> {
|
||||
JsQMC2::new(ekey)
|
||||
}
|
||||
|
||||
/// Common V1/V2 wrapper interface, derived from `KuwoHeader.makeCipher`
|
||||
#[wasm_bindgen(js_name=KWMCipher)]
|
||||
pub struct JsCipher(Cipher);
|
||||
|
||||
#[wasm_bindgen(js_class=KWMCipher)]
|
||||
impl JsCipher {
|
||||
/// Decrypt buffer at given offset.
|
||||
pub fn decrypt(&self, buffer: &mut [u8], offset: usize) {
|
||||
self.0.decrypt(buffer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod kuwo;
|
||||
pub mod qmc;
|
||||
|
||||
60
um_wasm/src/exports/qmc.rs
Normal file
60
um_wasm/src/exports/qmc.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use crate::errors::map_js_error;
|
||||
use umc_qmc::footer::FooterParseError;
|
||||
use umc_qmc::QMCv2Cipher;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen::JsError;
|
||||
|
||||
/// QMC1 (qmcflac) decipher, decrypt buffer at given offset.
|
||||
#[wasm_bindgen(js_name=decryptQMC1)]
|
||||
pub fn js_decrypt_qmc1(buffer: &mut [u8], offset: usize) {
|
||||
umc_qmc::v1::decrypt(buffer, offset)
|
||||
}
|
||||
|
||||
/// QMC2 (mgg/mflac) cipher
|
||||
#[wasm_bindgen(js_name=QMC2)]
|
||||
pub struct JsQMC2(QMCv2Cipher);
|
||||
|
||||
#[wasm_bindgen(js_class=QMC2)]
|
||||
impl JsQMC2 {
|
||||
/// Create a new QMC2 (mgg/mflac) cipher instance.
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(ekey: &str) -> Result<JsQMC2, JsError> {
|
||||
let cipher = QMCv2Cipher::new(ekey.as_bytes()).map_err(map_js_error)?;
|
||||
Ok(JsQMC2(cipher))
|
||||
}
|
||||
|
||||
/// Decrypt buffer at given offset.
|
||||
pub fn decrypt(&self, buffer: &mut [u8], offset: usize) {
|
||||
self.0.decrypt(buffer, offset)
|
||||
}
|
||||
}
|
||||
|
||||
/// QMC Footer.
|
||||
#[wasm_bindgen(js_name=QMCFooter)]
|
||||
pub struct JsQMCFooter(umc_qmc::footer::Metadata);
|
||||
|
||||
#[wasm_bindgen(js_class=QMCFooter)]
|
||||
impl JsQMCFooter {
|
||||
/// Parse QMC Footer from byte slice.
|
||||
/// Recommended to slice the last 1024 bytes of the file.
|
||||
pub fn parse(footer: &[u8]) -> Result<Option<JsQMCFooter>, JsError> {
|
||||
match umc_qmc::footer::from_byte_slice(footer) {
|
||||
Ok(Some(metadata)) => Ok(Some(JsQMCFooter(metadata))),
|
||||
Ok(None) => Ok(None),
|
||||
Err(FooterParseError::PCv1EKeyTooLarge(_)) => Ok(None),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get eKey (if embedded)
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn ekey(&self) -> Option<String> {
|
||||
self.0.ekey.clone()
|
||||
}
|
||||
|
||||
/// Get size of footer
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn size(&self) -> usize {
|
||||
self.0.size
|
||||
}
|
||||
}
|
||||
@@ -5,17 +5,8 @@ mod utils;
|
||||
use utils::set_panic_hook;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
fn alert(s: &str);
|
||||
}
|
||||
|
||||
/// Init panic hook
|
||||
#[wasm_bindgen]
|
||||
pub fn init() {
|
||||
set_panic_hook();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn greet() {
|
||||
alert("hello world!");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user