unkocrypto_rs/
lib.rs

1// unkocrypto
2// author: Leonardone @ NEETSDKASU
3// MIT License
4
5use std::convert::TryFrom;
6use std::error;
7use std::fmt;
8use std::io;
9use std::mem::size_of;
10use std::result;
11use std::slice;
12
13pub const MIN_BLOCK_SIZE: usize = 32;
14pub const MAX_BLOCK_SIZE: usize = 1 << 20;
15pub const META_SIZE: usize = size_of::<i32>() + size_of::<i64>();
16
17const BYTE: i32 = u8::MAX as i32 + 1;
18
19pub trait IntRng {
20    fn next_int(&mut self) -> i32;
21}
22
23pub trait Checksum {
24    fn reset(&mut self);
25    fn update(&mut self, v: u8);
26    fn get_value(&self) -> i64;
27}
28
29pub type Result<T> = result::Result<T, Error>;
30
31#[derive(Debug)]
32pub enum Error {
33    IoError(io::Error),
34    UnkocryptoError(Cause),
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum Cause {
39    InvalidBlockSize,
40    InvalidSourceSize,
41    InvalidDataCount,
42    InvalidData,
43    InvalidChecksum,
44}
45
46impl From<io::Error> for Error {
47    fn from(error: io::Error) -> Self {
48        Error::IoError(error)
49    }
50}
51
52impl fmt::Display for Error {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        use Error::*;
55        match self {
56            IoError(ref e) => fmt::Display::fmt(e, f),
57            UnkocryptoError(ref c) => write!(f, "UnkocryptoError({})", c),
58        }
59    }
60}
61
62impl error::Error for Error {
63    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
64        use Error::*;
65        match self {
66            IoError(ref e) => Some(e),
67            UnkocryptoError(_) => None,
68        }
69    }
70}
71
72impl fmt::Display for Cause {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        write!(f, "{:?}", self)
75    }
76}
77
78// bound [1, 2^31)
79fn next_int<T: IntRng>(rng: &mut T, bound: i32) -> i32 {
80    if (bound & -bound) == bound {
81        return ((bound as u64 * (rng.next_int() as u32 >> 1) as u64) >> 31) as i32;
82    }
83    let mut val: i32;
84    loop {
85        let bits = ((rng.next_int() as u32) >> 1) as i32;
86        val = bits % bound;
87        if (bits - val).wrapping_add(bound - 1) >= 0 {
88            break;
89        }
90    }
91    val
92}
93
94fn read<R: io::Read>(src: &mut R, one_byte: &mut u8) -> Result<usize> {
95    let size = src.read(slice::from_mut(one_byte))?;
96    Ok(size)
97}
98
99fn read_int(src: &[u8], pos: usize) -> i32 {
100    let src = &src[pos..pos + size_of::<i32>()];
101    i32::from_be_bytes(TryFrom::try_from(src).unwrap())
102}
103
104fn read_long(src: &[u8], pos: usize) -> i64 {
105    let src = &src[pos..pos + size_of::<i64>()];
106    i64::from_be_bytes(TryFrom::try_from(src).unwrap())
107}
108
109fn write_int(dst: &mut [u8], pos: usize, v: i32) {
110    dst[pos..pos + size_of::<i32>()].copy_from_slice(&v.to_be_bytes());
111}
112
113fn write_long(dst: &mut [u8], pos: usize, v: i64) {
114    dst[pos..pos + size_of::<i64>()].copy_from_slice(&v.to_be_bytes());
115}
116
117pub fn decrypt<C, T, R, W>(
118    block_size: usize,
119    mut checksum: C,
120    rng: &mut T,
121    src: &mut R,
122    dst: &mut W,
123) -> Result<(u64, u64)>
124where
125    C: Checksum,
126    T: IntRng,
127    R: io::Read,
128    W: io::Write,
129{
130    if !(MIN_BLOCK_SIZE..=MAX_BLOCK_SIZE).contains(&block_size) {
131        return Err(Error::UnkocryptoError(Cause::InvalidBlockSize));
132    }
133    let data_size: usize = block_size - META_SIZE;
134    let mut mask: Vec<u8> = vec![0; block_size];
135    let mut indexes: Vec<usize> = vec![0; block_size];
136    let mut data: Vec<u8> = vec![0; block_size];
137    let mut src_size: u64 = 0;
138    let mut dst_size: u64 = 0;
139    let mut one_byte: u8 = 0;
140    let mut read_size: usize = read(src, &mut one_byte)?;
141    if read_size == 0 {
142        return Err(Error::UnkocryptoError(Cause::InvalidSourceSize));
143    }
144    while read_size > 0 {
145        for i in 0..block_size {
146            mask[i] = next_int(rng, BYTE) as u8;
147            indexes[i] = i;
148        }
149        for i in 0..block_size {
150            let j = next_int(rng, (block_size - i) as i32) as usize + i;
151            indexes.swap(i, j);
152        }
153        for j in indexes.iter() {
154            if read_size == 0 {
155                return Err(Error::UnkocryptoError(Cause::InvalidSourceSize));
156            }
157            data[*j] = one_byte ^ mask[*j];
158            read_size = read(src, &mut one_byte)?;
159        }
160        src_size += block_size as u64;
161        let count = read_int(&data, data_size);
162        let code = read_long(&data, data_size + size_of::<i32>());
163        if count < 0 || (count == 0 && read_size > 0) || data_size < count as usize {
164            return Err(Error::UnkocryptoError(Cause::InvalidDataCount));
165        }
166        let count = count as usize;
167        dst_size += count as u64;
168        checksum.reset();
169        for (i, d) in data.iter().enumerate().take(data_size) {
170            if i < count {
171                dst.write_all(slice::from_ref(d))?;
172                checksum.update(*d);
173            } else if *d != 0 {
174                return Err(Error::UnkocryptoError(Cause::InvalidData));
175            }
176        }
177        if code != checksum.get_value() {
178            return Err(Error::UnkocryptoError(Cause::InvalidChecksum));
179        }
180    }
181    Ok((src_size, dst_size))
182}
183
184pub fn encrypt<C, T, R, W>(
185    block_size: usize,
186    mut checksum: C,
187    rng: &mut T,
188    src: &mut R,
189    dst: &mut W,
190) -> Result<(u64, u64)>
191where
192    C: Checksum,
193    T: IntRng,
194    R: io::Read,
195    W: io::Write,
196{
197    if !(MIN_BLOCK_SIZE..=MAX_BLOCK_SIZE).contains(&block_size) {
198        return Err(Error::UnkocryptoError(Cause::InvalidBlockSize));
199    }
200    let data_size: usize = block_size - META_SIZE;
201    let mut data: Vec<u8> = vec![0; block_size];
202    let mut src_size: u64 = 0;
203    let mut dst_size: u64 = 0;
204    let mut one_byte: u8 = 0;
205    let mut read_size: usize = read(src, &mut one_byte)?;
206    loop {
207        dst_size += block_size as u64;
208        checksum.reset();
209        let mut count: usize = 0;
210        while count < data_size {
211            if read_size == 0 {
212                break;
213            }
214            checksum.update(one_byte);
215            data[count] = one_byte ^ next_int(rng, BYTE) as u8;
216            count += 1;
217            read_size = read(src, &mut one_byte)?;
218        }
219        src_size += count as u64;
220        for d in data.iter_mut().take(data_size).skip(count) {
221            *d = next_int(rng, BYTE) as u8;
222        }
223        write_int(&mut data, data_size, count as i32);
224        write_long(
225            &mut data,
226            data_size + size_of::<i32>(),
227            checksum.get_value(),
228        );
229        for d in data.iter_mut().skip(data_size) {
230            *d ^= next_int(rng, BYTE) as u8;
231        }
232        for i in 0..data.len() {
233            let j = next_int(rng, (data.len() - i) as i32) as usize + i;
234            data.swap(i, j);
235        }
236        dst.write_all(&data)?;
237        if read_size == 0 {
238            break;
239        }
240    }
241    Ok((src_size, dst_size))
242}
243
244pub fn calc_block_count(block_size: usize, src_len: u64) -> Result<u64> {
245    if !(MIN_BLOCK_SIZE..=MAX_BLOCK_SIZE).contains(&block_size) {
246        return Err(Error::UnkocryptoError(Cause::InvalidBlockSize));
247    }
248    let data_size: u64 = (block_size - META_SIZE) as u64;
249    let block_count: u64 = (src_len + data_size - 1) / data_size;
250    Ok(block_count)
251}
252
253pub fn calc_encrypted_size(block_size: usize, src_len: u64) -> Result<u64> {
254    calc_block_count(block_size, src_len)?
255        .checked_mul(block_size as u64)
256        .ok_or_else(|| Error::UnkocryptoError(Cause::InvalidSourceSize))
257}
258
259pub fn consume<T: IntRng>(rng: &mut T, block_size: usize, block_count: u64) -> Result<()> {
260    if !(MIN_BLOCK_SIZE..=MAX_BLOCK_SIZE).contains(&block_size) {
261        return Err(Error::UnkocryptoError(Cause::InvalidBlockSize));
262    }
263    let _ = block_count
264        .checked_mul(block_size as u64)
265        .ok_or_else(|| Error::UnkocryptoError(Cause::InvalidSourceSize))?;
266    for _ in 0..block_count {
267        for _ in 0..block_size {
268            let _ = next_int(rng, BYTE);
269        }
270        for i in 0..block_size {
271            let _ = next_int(rng, (block_size - i) as i32);
272        }
273    }
274    Ok(())
275}
276
277#[cfg(test)]
278mod tests {
279    struct JavaRandom {
280        seed: i64,
281    }
282
283    impl JavaRandom {
284        fn new(seed: i64) -> JavaRandom {
285            let mut jr = JavaRandom { seed: 0 };
286            jr.set_seed(seed);
287            jr
288        }
289
290        fn set_seed(&mut self, seed: i64) {
291            self.seed = (seed ^ 0x5DEECE66D) & ((1 << 48) - 1);
292        }
293
294        fn next(&mut self, bits: i32) -> i32 {
295            self.seed = self.seed.wrapping_mul(0x5DEECE66D).wrapping_add(0xB) & ((1 << 48) - 1);
296            (self.seed as u64 >> (48 - bits)) as i32
297        }
298    }
299
300    impl crate::IntRng for JavaRandom {
301        fn next_int(&mut self) -> i32 {
302            self.next(32)
303        }
304    }
305
306    static CRC32_TABLE: [i64; 256] = {
307        const fn f(c: i64) -> i64 {
308            (c >> 1) ^ (0xedb88320 * (c & 1))
309        }
310        const fn calc(v: i64) -> i64 {
311            f(f(f(f(f(f(f(f(v))))))))
312        }
313        let mut table = [0_i64; 256];
314        macro_rules! m {
315            ($e:expr) => {
316                table[$e] = calc($e);
317            };
318            ($e:expr, $a:expr $(,$b:expr)*) => {
319                m!(($e<<1) $(,$b)*);
320                m!((($e<<1)|1) $(,$b)*);
321            };
322        }
323        m!(0, 1, 2, 3, 4, 5, 6, 7, 8);
324        table
325    };
326
327    struct Crc32 {
328        value: i64,
329    }
330
331    impl Crc32 {
332        fn new() -> Self {
333            Self { value: 0xffffffff }
334        }
335    }
336
337    impl crate::Checksum for Crc32 {
338        fn reset(&mut self) {
339            self.value = 0xffffffff;
340        }
341
342        fn update(&mut self, v: u8) {
343            let b = CRC32_TABLE[(self.value & 0xff) as usize ^ v as usize];
344            let c = self.value >> 8;
345            self.value = b ^ c;
346        }
347
348        fn get_value(&self) -> i64 {
349            self.value ^ 0xffffffff
350        }
351    }
352
353    #[test]
354    fn it_works() {
355        let block_size: usize = crate::MIN_BLOCK_SIZE;
356
357        let seed: i64 = 123456789;
358
359        let data_src: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
360
361        let secret_src: [u8; 32] = [
362            0xa2, 0xfc, 0x45, 0x90, 0x64, 0x80, 0x77, 0x46, 0x3f, 0x7e, 0x1d, 0x7c, 0x64, 0xfe,
363            0x5c, 0x98, 0x7a, 0x00, 0x79, 0xa8, 0x64, 0xf2, 0x7d, 0xc1, 0xe3, 0x66, 0x31, 0x31,
364            0x1e, 0x62, 0xb6, 0x04,
365        ];
366
367        // decrypt
368        {
369            let crc = Crc32::new();
370
371            let mut rng = JavaRandom::new(seed);
372
373            let mut cur = secret_src.as_slice();
374
375            let mut dst = Vec::new();
376
377            let (src_len, dst_len) =
378                crate::decrypt(block_size, crc, &mut rng, &mut cur, &mut dst).unwrap();
379
380            assert_eq!(secret_src.len() as u64, src_len);
381            assert_eq!(data_src.len() as u64, dst_len);
382            assert_eq!(data_src, dst.as_ref());
383        }
384
385        // encrypt
386        {
387            let crc = Crc32::new();
388
389            let mut rng = JavaRandom::new(seed);
390
391            let mut cur = data_src.as_slice();
392
393            let mut dst = Vec::new();
394
395            let (src_len, dst_len) =
396                crate::encrypt(block_size, crc, &mut rng, &mut cur, &mut dst).unwrap();
397
398            assert_eq!(data_src.len() as u64, src_len);
399            assert_eq!(secret_src.len() as u64, dst_len);
400            assert_eq!(secret_src, dst.as_ref());
401        }
402
403        // calc_encrypted_size
404        {
405            const LARGE_SRC_LEN: u64 = 12345;
406
407            let crc = Crc32::new();
408
409            let mut rng = JavaRandom::new(seed);
410
411            let mut cur = std::io::Read::take(std::io::repeat(123), LARGE_SRC_LEN);
412
413            let mut dst = std::io::sink();
414
415            let (_, len) = crate::encrypt(block_size, crc, &mut rng, &mut cur, &mut dst).unwrap();
416
417            let guess_size = crate::calc_encrypted_size(block_size, LARGE_SRC_LEN).unwrap();
418
419            assert_eq!(len, guess_size);
420        }
421
422        // consume
423        {
424            let multi_src: Vec<Vec<u8>> = vec![data_src.as_slice(); 5]
425                .iter()
426                .enumerate()
427                .map(|(i, a)| {
428                    a.iter()
429                        .map(|e| i as u8 + *e)
430                        .cycle()
431                        .take(10 * (i + 10))
432                        .collect()
433                })
434                .collect();
435            let encrypted = {
436                let mut rng = JavaRandom::new(seed);
437                let mut dst = Vec::new();
438                for mut src in multi_src.iter().map(|a| a.as_slice()) {
439                    let block_count =
440                        crate::calc_block_count(block_size, src.len() as u64).unwrap();
441                    dst.push(block_count as u8);
442                    let crc = Crc32::new();
443                    crate::encrypt(block_size, crc, &mut rng, &mut src, &mut dst).unwrap();
444                }
445                dst
446            };
447            {
448                let mut rng = JavaRandom::new(seed);
449                let mut cur = encrypted.as_slice();
450                for (i, src) in multi_src.iter().enumerate() {
451                    let block_count = cur[0] as u64;
452                    let size = block_size * block_count as usize;
453                    cur = &cur[1..];
454                    if i % 2 == 1 {
455                        crate::consume(&mut rng, block_size, block_count).unwrap();
456                    } else {
457                        let crc = Crc32::new();
458                        let mut chunk = &cur[..size];
459                        let mut dst = Vec::new();
460                        crate::decrypt(block_size, crc, &mut rng, &mut chunk, &mut dst).unwrap();
461                        assert_eq!(src, &dst);
462                    }
463                    cur = &cur[size..];
464                }
465            }
466        }
467    }
468}