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 cipher = match key.len() {
0 => Err(QmcCryptoError::QMCV2MapKeyEmpty)?,
..=300 => QMCv2Cipher::MapL(QMC2Map::new(key)?),
1..=300 => QMCv2Cipher::MapL(QMC2Map::new(key)?),
_ => QMCv2Cipher::RC4(QMC2RC4::new(key)),
};
Ok(cipher)

View File

@ -11,14 +11,14 @@ const RC4_STREAM_CACHE_SIZE: usize = OTHER_SEGMENT_SIZE + 512;
pub struct QMC2RC4 {
hash: f64,
key: Box<[u8]>,
key_stream: [u8; RC4_STREAM_CACHE_SIZE],
key_stream: Box<[u8; RC4_STREAM_CACHE_SIZE]>,
}
impl QMC2RC4 {
pub fn new(key: &[u8]) -> Self {
let mut rc4 = RC4::new(key);
let mut key_stream = [0u8; RC4_STREAM_CACHE_SIZE];
rc4.derive(&mut key_stream);
let mut key_stream = Box::new([0u8; RC4_STREAM_CACHE_SIZE]);
rc4.derive(&mut key_stream[..]);
Self {
hash: hash(key),
@ -31,8 +31,9 @@ impl QMC2RC4 {
let n = self.key.len();
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;
*datum ^= self.key[idx % n];
let idx = get_segment_key(offset as u64, self.key[offset % n], self.hash);
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 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);
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) {
*datum ^= key;
}

View File

@ -42,8 +42,11 @@ impl RC4 {
self.state[index]
}
pub fn derive(&mut self, buffer: &mut [u8]) {
for item in buffer.iter_mut() {
pub fn derive<T>(&mut self, buffer: &mut T)
where
T: AsMut<[u8]> + ?Sized,
{
for item in buffer.as_mut().iter_mut() {
*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 {
0 => 0,
seed => {
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() {
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.
#[wasm_bindgen(constructor)]
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))
}

View File

@ -62,7 +62,8 @@ async function main() {
const wasmDistDir = path.resolve(__dirname, 'dist');
await rm(wasmOutDir, { recursive: true, force: true });
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'),
});

View File

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