msg_tool\scripts\kirikiri\archive\xp3\crypt/
cz.rs

1use super::*;
2use aes::Aes128Dec;
3use aes::cipher::{BlockModeDecrypt, KeyIvInit};
4use cbc::Decryptor;
5
6type Aes128CbcDec = Decryptor<Aes128Dec>;
7
8const CZ_MAGIC: &[u8; 4] = b"\xFD\xD7\x90\xA5";
9const CZ_IV_SEED: u32 = 0xBFBFBFBF;
10const CZ_HEADER_KEY: &[u8; 4] = b"\x9D\x1D\x9A\xF2";
11const CZ_DEFAULT_KEY: &[u8; 16] = b"\x91\x10\xfcuE\x8f\xb5\xe6\xfe\xac\xbaDvX\xc2\x1a";
12
13fn cz_decrypt_int(data: &[u8], offset: usize, key: u8) -> u32 {
14    let mut v: u32 = (data[offset] ^ key ^ CZ_HEADER_KEY[0]) as u32;
15    v |= ((data[offset + 1] ^ key ^ CZ_HEADER_KEY[1]) as u32) << 8;
16    v |= ((data[offset + 2] ^ key ^ CZ_HEADER_KEY[2]) as u32) << 16;
17    v |= ((data[offset + 3] ^ key ^ CZ_HEADER_KEY[3]) as u32) << 24;
18    v
19}
20
21fn cz_create_iv(seed: u32) -> [u8; 16] {
22    let mut state = [0u32; 4];
23    state[0] = 123456789;
24    state[1] = 972436830;
25    state[2] = 524018621;
26    state[3] = seed;
27    let mut iv = [0u8; 16];
28    for i in 0..16 {
29        let a = state[3];
30        let b = state[0] ^ (state[0] << 11);
31        state[0] = state[1];
32        state[1] = state[2];
33        state[2] = a;
34        state[3] = b ^ a ^ ((b ^ (a >> 11)) >> 8);
35        iv[i] = state[3] as u8;
36    }
37    iv
38}
39
40#[derive(Debug)]
41struct AesDecryptor<'a> {
42    aes: Aes128CbcDec,
43    entry: StreamRegion<Entry<'a>>,
44    pos: u64,
45    original_size: u64,
46}
47
48impl<'a> AesDecryptor<'a> {
49    fn new(
50        aes: Aes128CbcDec,
51        entry: StreamRegion<Entry<'a>>,
52        original_size: u64,
53    ) -> AlignedReader<16, Self> {
54        AlignedReader::new(Self {
55            aes,
56            entry,
57            pos: 0,
58            original_size,
59        })
60    }
61}
62
63impl<'a> Read for AesDecryptor<'a> {
64    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
65        let readed = self.entry.read_most(buf)?;
66        if readed % 16 != 0 {
67            return Err(std::io::Error::new(
68                std::io::ErrorKind::UnexpectedEof,
69                "Not enough data to decrypt",
70            ));
71        }
72        // NoPadding
73        for i in (0..readed).step_by(16) {
74            let block: &mut [u8; 16] = (&mut buf[i..i + 16]).try_into().unwrap();
75            self.aes.decrypt_block(block.into());
76        }
77        let remaining = self.original_size - self.pos;
78        let readed = readed.min(remaining as usize);
79        self.pos += readed as u64;
80        Ok(readed)
81    }
82}
83
84#[derive(Debug)]
85pub struct KissCrypt {
86    base: BaseSchema,
87}
88
89impl KissCrypt {
90    pub fn new(base: BaseSchema) -> Self {
91        Self { base }
92    }
93}
94
95impl Crypt for KissCrypt {
96    fn hash_after_crypt(&self) -> bool {
97        self.base.hash_after_crypt
98    }
99    fn startup_tjs_not_encrypted(&self) -> bool {
100        self.base.startup_tjs_not_encrypted
101    }
102    fn obfuscated_index(&self) -> bool {
103        self.base.obfuscated_index
104    }
105    fn need_filter(&self, _filename: &str, buf: &[u8], buf_len: usize) -> bool {
106        buf_len >= 4 && buf.starts_with(CZ_MAGIC)
107    }
108    fn filter<'a>(&self, mut entry: Entry<'a>) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
109        let mut header = [0u8; 15];
110        entry.read_exact(&mut header)?;
111        let typ = [header[4] ^ 0x11, header[5] ^ 0x7F, header[6] ^ 0x9A];
112        let key = typ[0];
113        let _unpacked_size = cz_decrypt_int(&header, 7, key);
114        let packed_size = cz_decrypt_int(&header, 11, key);
115        if (packed_size as u64) < entry.index.original_size && (packed_size - 5) & 0xF == 0 {
116            let padded_size = packed_size - 5;
117            let original_size = padded_size
118                - (entry.peek_u8_at(15 + padded_size as u64 + 1)?
119                    ^ entry.peek_u8_at(15 + padded_size as u64)?) as u32;
120            let iv_seed = entry.peek_u32_at(15 + padded_size as u64 + 1)? ^ CZ_IV_SEED;
121            let aes = Aes128CbcDec::new(CZ_DEFAULT_KEY.into(), &cz_create_iv(iv_seed).into());
122            let entry = StreamRegion::with_size(entry, padded_size as u64)?;
123            let stream = AesDecryptor::new(aes, entry, original_size as u64);
124            if typ[0] == b'C' {
125                let stream = flate2::read::ZlibDecoder::new(stream);
126                return Ok(Box::new(stream));
127            }
128            Ok(Box::new(stream))
129        } else {
130            Ok(Box::new(entry))
131        }
132    }
133    fn decrypt_supported(&self) -> bool {
134        true
135    }
136    fn decrypt_seek_supported(&self) -> bool {
137        true
138    }
139    fn decrypt<'a>(
140        &self,
141        entry: &Xp3Entry,
142        cur_seg: &Segment,
143        stream: Box<dyn Read + Send + Sync + 'a>,
144    ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
145        let key = entry.file_hash ^ (entry.file_hash >> 19) ^ 0x4A9EEFF0;
146        Ok(Box::new(KissCryptReader::new(stream, cur_seg, key)))
147    }
148    fn decrypt_with_seek<'a>(
149        &self,
150        entry: &Xp3Entry,
151        cur_seg: &Segment,
152        stream: Box<dyn ReadSeek + Send + Sync + 'a>,
153    ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
154        let key = entry.file_hash ^ (entry.file_hash >> 19) ^ 0x4A9EEFF0;
155        Ok(Box::new(KissCryptReader::new(stream, cur_seg, key)))
156    }
157}
158
159impl<R: Read> Read for KissCryptReader<R> {
160    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
161        let readed = self.inner.read(buf)?;
162        let offset = self.seg_start + self.pos;
163        let mut i = 0usize;
164        while (i as u64 + offset) & 0xF != 0 {
165            i += 1;
166        }
167        while i < readed {
168            buf[i] ^= (self.key ^ (offset as u32 + i as u32)) as u8;
169            i += 0x10;
170        }
171        self.pos += readed as u64;
172        Ok(readed)
173    }
174}