feat: qmc v2 map cipher
This commit is contained in:
parent
aa4c650ff0
commit
1a282c0912
@ -1,7 +1,11 @@
|
||||
use thiserror::Error;
|
||||
|
||||
pub mod v1;
|
||||
pub mod v2_map;
|
||||
pub mod v2_rc4;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum QmcCryptoError {}
|
||||
pub enum QmcCryptoError {
|
||||
#[error("QMC V2/Map Cipher: Key is empty")]
|
||||
QMCV2MapKeyEmpty,
|
||||
}
|
||||
|
57
um_crypto/qmc/src/v2_map/key.rs
Normal file
57
um_crypto/qmc/src/v2_map/key.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use crate::v1::cipher::V1_KEY_SIZE;
|
||||
use crate::QmcCryptoError;
|
||||
|
||||
const INDEX_OFFSET: usize = 71214;
|
||||
|
||||
pub fn key_compress<T: AsRef<[u8]>>(long_key: T) -> anyhow::Result<[u8; V1_KEY_SIZE]> {
|
||||
let long_key = long_key.as_ref();
|
||||
if long_key.is_empty() {
|
||||
Err(QmcCryptoError::QMCV2MapKeyEmpty)?;
|
||||
}
|
||||
|
||||
let n = long_key.len();
|
||||
let mut result = [0u8; V1_KEY_SIZE];
|
||||
|
||||
let key_stream = (0..V1_KEY_SIZE).map(|i| {
|
||||
let i = (i * i + INDEX_OFFSET) % n;
|
||||
let key = long_key[i];
|
||||
let shift = ((i + 4) % 8) as u32;
|
||||
key.wrapping_shl(shift) | key.wrapping_shr(shift)
|
||||
});
|
||||
|
||||
for (key, value) in result.iter_mut().zip(key_stream) {
|
||||
*key = value;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_compress() {
|
||||
let expected = [
|
||||
0x79, 0xf4, 0x00, 0x75, 0x9e, 0x36, 0x00, 0x14, 0x8a, 0x63, 0x00, 0xb4, 0xbe, 0x77,
|
||||
0x00, 0x17, 0xba, 0x00, 0x37, 0x00, 0x00, 0x00, 0xbf, 0x80, 0x41, 0xbf, 0x83, 0xdd,
|
||||
0xbc, 0x5c, 0x02, 0x43, 0x14, 0x82, 0x49, 0x02, 0x00, 0x55, 0xbe, 0x6d, 0xbf, 0x49,
|
||||
0x80, 0x8e, 0x43, 0x00, 0xfa, 0x41, 0x67, 0xa8, 0x17, 0xf4, 0xae, 0x16, 0x15, 0x00,
|
||||
0xc1, 0x37, 0x82, 0xdd, 0x36, 0x21, 0x38, 0x55, 0x00, 0x79, 0x41, 0x9e, 0x42, 0xc1,
|
||||
0x36, 0xfa, 0xcf, 0x35, 0x00, 0x00, 0x41, 0xdd, 0x43, 0x42, 0x17, 0x4d, 0x8e, 0x8a,
|
||||
0xdd, 0x00, 0xbe, 0xf5, 0x38, 0xb4, 0xbf, 0x00, 0x7a, 0xcc, 0x4d, 0x02, 0x00, 0xcf,
|
||||
0xc1, 0xc1, 0x02, 0xa8, 0x00, 0x16, 0xc1, 0xbf, 0xc2, 0x42, 0x00, 0x49, 0x00, 0xc1,
|
||||
0xc2, 0xf5, 0x00, 0x17, 0x41, 0xdc, 0x83, 0xc2, 0x00, 0x9e, 0x41, 0xc1, 0x71, 0x36,
|
||||
0x00, 0x80,
|
||||
];
|
||||
let key = (b'a'..=b'z')
|
||||
.chain(b'A'..=b'Z')
|
||||
.chain(b'0'..=b'9')
|
||||
.cycle()
|
||||
.take(325)
|
||||
.collect::<Vec<u8>>();
|
||||
|
||||
let actual = key_compress(&key).expect("should compress ok");
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
40
um_crypto/qmc/src/v2_map/mod.rs
Normal file
40
um_crypto/qmc/src/v2_map/mod.rs
Normal file
@ -0,0 +1,40 @@
|
||||
mod key;
|
||||
|
||||
use crate::v1::cipher::{qmc1_transform, V1_KEY_SIZE};
|
||||
use crate::v2_map::key::key_compress;
|
||||
use anyhow::Result;
|
||||
|
||||
pub struct QMC2Map {
|
||||
key: [u8; V1_KEY_SIZE],
|
||||
}
|
||||
|
||||
impl QMC2Map {
|
||||
pub fn new<T: AsRef<[u8]>>(key: T) -> Result<Self> {
|
||||
let key = key_compress(key)?;
|
||||
Ok(Self { key })
|
||||
}
|
||||
|
||||
pub fn decrypt<T: AsMut<[u8]>>(&self, data: &mut T, offset: usize) {
|
||||
for (i, datum) in data.as_mut().iter_mut().enumerate() {
|
||||
*datum = qmc1_transform(&self.key, *datum, offset + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decrypt() {
|
||||
let key = (b'a'..=b'z')
|
||||
.chain(b'A'..=b'Z')
|
||||
.chain(b'0'..=b'9')
|
||||
.cycle()
|
||||
.take(325)
|
||||
.collect::<Vec<u8>>();
|
||||
|
||||
let cipher = QMC2Map::new(key).expect("should not fail");
|
||||
let mut actual = [
|
||||
0x00u8, 0x9e, 0x41, 0xc1, 0x71, 0x36, 0x00, 0x80, 0xf4, 0x00, 0x75, 0x9e, 0x36, 0x00, 0x14,
|
||||
0x8a,
|
||||
];
|
||||
cipher.decrypt(&mut actual, 32760);
|
||||
assert_eq!(actual, [0u8; 0x10]);
|
||||
}
|
Loading…
Reference in New Issue
Block a user