Compare commits

..

6 Commits

7 changed files with 32 additions and 15 deletions

View File

@ -29,7 +29,7 @@ impl QMCv2Cipher {
let key = key.as_ref(); let key = key.as_ref();
let cipher = match key.len() { let cipher = match key.len() {
0 => Err(QmcCryptoError::QMCV2MapKeyEmpty)?, 0 => Err(QmcCryptoError::QMCV2MapKeyEmpty)?,
..=300 => QMCv2Cipher::MapL(QMC2Map::new(key)?), 1..=300 => QMCv2Cipher::MapL(QMC2Map::new(key)?),
_ => QMCv2Cipher::RC4(QMC2RC4::new(key)), _ => QMCv2Cipher::RC4(QMC2RC4::new(key)),
}; };
Ok(cipher) Ok(cipher)

View File

@ -11,14 +11,14 @@ const RC4_STREAM_CACHE_SIZE: usize = OTHER_SEGMENT_SIZE + 512;
pub struct QMC2RC4 { pub struct QMC2RC4 {
hash: f64, hash: f64,
key: Box<[u8]>, key: Box<[u8]>,
key_stream: [u8; RC4_STREAM_CACHE_SIZE], key_stream: Box<[u8; RC4_STREAM_CACHE_SIZE]>,
} }
impl QMC2RC4 { impl QMC2RC4 {
pub fn new(key: &[u8]) -> Self { pub fn new(key: &[u8]) -> Self {
let mut rc4 = RC4::new(key); let mut rc4 = RC4::new(key);
let mut key_stream = [0u8; RC4_STREAM_CACHE_SIZE]; let mut key_stream = Box::new([0u8; RC4_STREAM_CACHE_SIZE]);
rc4.derive(&mut key_stream); rc4.derive(&mut key_stream[..]);
Self { Self {
hash: hash(key), hash: hash(key),
@ -31,8 +31,9 @@ impl QMC2RC4 {
let n = self.key.len(); let n = self.key.len();
for (datum, offset) in data.iter_mut().zip(offset..) { for (datum, offset) in data.iter_mut().zip(offset..) {
let idx = get_segment_key(offset as u64, self.key[offset % n], self.hash) as usize; let idx = get_segment_key(offset as u64, self.key[offset % n], self.hash);
*datum ^= self.key[idx % n]; let idx = idx % (n as u64);
*datum ^= self.key[idx as usize];
} }
} }
@ -43,10 +44,11 @@ impl QMC2RC4 {
let block_offset = offset % OTHER_SEGMENT_SIZE; let block_offset = offset % OTHER_SEGMENT_SIZE;
let seed = self.key[id % n]; let seed = self.key[id % n];
let skip = get_segment_key(id as u64, seed, self.hash) % 512; let skip = get_segment_key(id as u64, seed, self.hash);
let skip = (skip & 0x1FF) as usize;
debug_assert!(data.len() <= OTHER_SEGMENT_SIZE - block_offset); debug_assert!(data.len() <= OTHER_SEGMENT_SIZE - block_offset);
let key_stream = self.key_stream.iter().skip(skip as usize + block_offset); let key_stream = self.key_stream.iter().skip(skip + block_offset);
for (datum, &key) in data.iter_mut().zip(key_stream) { for (datum, &key) in data.iter_mut().zip(key_stream) {
*datum ^= key; *datum ^= key;
} }

View File

@ -42,8 +42,11 @@ impl RC4 {
self.state[index] self.state[index]
} }
pub fn derive(&mut self, buffer: &mut [u8]) { pub fn derive<T>(&mut self, buffer: &mut T)
for item in buffer.iter_mut() { where
T: AsMut<[u8]> + ?Sized,
{
for item in buffer.as_mut().iter_mut() {
*item ^= self.generate(); *item ^= self.generate();
} }
} }

View File

@ -1,9 +1,9 @@
pub fn get_segment_key(id: u64, seed: u8, hash: f64) -> u32 { pub fn get_segment_key(id: u64, seed: u8, hash: f64) -> u64 {
match seed { match seed {
0 => 0, 0 => 0,
seed => { seed => {
let result = hash / ((id + 1).wrapping_mul(seed.into()) as f64) * 100.0; let result = hash / ((id + 1).wrapping_mul(seed.into()) as f64) * 100.0;
result as u32 result as u64
} }
} }
} }
@ -21,4 +21,14 @@ mod tests {
fn test_segment_key_123() { fn test_segment_key_123() {
assert_eq!(get_segment_key(1, 123, 12345.0), 5018); assert_eq!(get_segment_key(1, 123, 12345.0), 5018);
} }
#[test]
fn test_segment_key_large_1() {
assert_eq!(get_segment_key(51, 35, 516402887.0), 28373784);
}
#[test]
fn test_segment_key_large_2() {
assert_eq!(get_segment_key(0, 66, 3908240000.0), 5921575757);
}
} }

View File

@ -19,7 +19,8 @@ impl JsQMC2 {
/// Create a new QMC2 (mgg/mflac) cipher instance. /// Create a new QMC2 (mgg/mflac) cipher instance.
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new(ekey: &str) -> Result<JsQMC2, JsError> { pub fn new(ekey: &str) -> Result<JsQMC2, JsError> {
let cipher = QMCv2Cipher::new(ekey.as_bytes()).map_err(map_js_error)?; let key = umc_qmc::ekey::decrypt(ekey).map_err(map_js_error)?;
let cipher = QMCv2Cipher::new(key).map_err(map_js_error)?;
Ok(JsQMC2(cipher)) Ok(JsQMC2(cipher))
} }

View File

@ -62,7 +62,8 @@ async function main() {
const wasmDistDir = path.resolve(__dirname, 'dist'); const wasmDistDir = path.resolve(__dirname, 'dist');
await rm(wasmOutDir, { recursive: true, force: true }); await rm(wasmOutDir, { recursive: true, force: true });
const wasmRelOutDir = path.relative(wasmSourceDir, wasmOutDir); const wasmRelOutDir = path.relative(wasmSourceDir, wasmOutDir);
await run(['wasm-pack', 'build', '--target', 'web', '--out-dir', wasmRelOutDir], { const profileFlag = process.env.BUILD_RELEASE ? '--release' : '--dev';
await run(['wasm-pack', 'build', profileFlag, '--target', 'web', '--out-dir', wasmRelOutDir], {
cwd: path.resolve(__dirname, '..', 'um_wasm'), cwd: path.resolve(__dirname, '..', 'um_wasm'),
}); });

View File

@ -11,7 +11,7 @@ function loader() {
const url = new URL('um_wasm_bg.wasm', import.meta.url); const url = new URL('um_wasm_bg.wasm', import.meta.url);
const wasm = const wasm =
url.protocol === 'file:' url.protocol === 'file:'
? import('node:f' + 's/promises') ? import(/* @vite-ignore */ 'node:f' + 's/promises')
.then((fs) => fs.readFile(url)) .then((fs) => fs.readFile(url))
.catch((err) => { .catch((err) => {
console.log('read wasm failed', err); console.log('read wasm failed', err);