rcrypto 0.2.0

A rust cryptography library
Documentation
use std::cell::Cell;
use crate::sm4::sm4_const_tables::{SBOX, FK, CK};
use crate::{CryptoError, CryptoErrorKind, Cipher};

const SM4_BLOCK_SIZE: usize = 16;

pub struct SM4 {
    rk: Cell<[u32; 32]>,
}

impl Clone for SM4 {
    fn clone(&self) -> Self {
        SM4 {
            rk: Cell::new(self.get_rk_ref().clone()),
        }
    }
}

impl SM4 {
    #[inline]
    fn f_tau(x: u32) -> u32 {
        let y = x.to_be_bytes();
        let s = [SBOX[y[0] as usize], SBOX[y[1] as usize], SBOX[y[2] as usize], SBOX[y[3] as usize]];
        u32::from_be_bytes(s)
    }
    
    #[inline]
    fn f_l(x: u32) -> u32 {
        x ^ x.rotate_left(2) ^ x.rotate_left(10) ^ x.rotate_left(18) ^ x.rotate_left(24)
    }
    
    #[inline]
    fn round_f(x0: u32, x1: u32, x2: u32, x3: u32, rk: u32) -> u32 {
        x0 ^ Self::f_l(Self::f_tau(x1 ^ x2 ^ x3 ^ rk))
    }
    
    #[inline]
    fn f_lb(x: u32) -> u32 {
        x ^ x.rotate_left(13) ^ x.rotate_left(23)
    }
    
    fn key_schedule(mk: &[u32]) -> SM4 {
        let mut k = [0u32; 36];
        mk.iter().zip(k.iter_mut()).enumerate().for_each(|(i, (&x, y))| {
            *y = x ^ FK[i]
        });
        
        let mut rk = [0u32; 32];
        for i in 0..32 {
            k[i + 4] = k[i] ^ Self::f_lb(Self::f_tau(k[i+1] ^ k[i+2] ^ k[i+3] ^ CK[i]));
            rk[i] = k[i+4];
        }
        
        SM4 {
            rk: Cell::new(rk)
        }
    }
    
    #[inline]
    fn u8_to_u32(k0: u8, k1: u8, k2: u8, k3: u8) -> u32 {
        ((k0 as u32) << 24) | ((k1 as u32) << 16) | ((k2 as u32) << 8) | (k3 as u32)
    }
    
    pub fn from_slice(key: &[u8]) -> Result<SM4, CryptoError> {
        if key.len() != SM4_BLOCK_SIZE {
            Err(CryptoError::new(CryptoErrorKind::InvalidParameter, 
                format!("The length of key must be 16 in bytes")))
        } else {
            let mk = [
                Self::u8_to_u32(key[0], key[1], key[2], key[3]),
                Self::u8_to_u32(key[4], key[5], key[6], key[7]),
                Self::u8_to_u32(key[8], key[9], key[10], key[11]),
                Self::u8_to_u32(key[12], key[13], key[14], key[15]),
            ];
            Ok(Self::key_schedule(mk.as_ref()))
        }
    }
    
    pub fn new(key: [u8; 16]) -> SM4 {
        Self::from_slice(key.as_ref()).unwrap()
    }
    
    fn get_rk_ref(&self) -> &[u32; 32] {
        unsafe {
            & (*self.rk.as_ptr())
        }
    }
    
    fn ed_inner(&self, dst: &mut Vec<u8>, data: &[u8], rk: fn(&[u32; 32], usize) -> u32) -> Result<usize, CryptoError> {
        if data.len() != SM4_BLOCK_SIZE {
            return Err(CryptoError::new(CryptoErrorKind::InvalidParameter,
                                        format!("The length of data block must be 16 in bytes")));
        }

        let mut x = [0u32; 36];
        x[0] = Self::u8_to_u32(data[0], data[1], data[2], data[3]);
        x[1] = Self::u8_to_u32(data[4], data[5], data[6], data[7]);
        x[2] = Self::u8_to_u32(data[8], data[9], data[10], data[11]);
        x[3] = Self::u8_to_u32(data[12], data[13], data[14], data[15]);

        for i in 0..32 {
            x[i + 4] = Self::round_f(x[i], x[i+1], x[i+2], x[i+3], rk(self.get_rk_ref(), i));
        }
        dst.clear();
        for i in (32..=35).rev() {
            dst.extend(x[i].to_be_bytes().iter());
        }

        Ok(dst.len())
    }
}

impl Cipher for SM4 {
    type Output = usize;
    fn block_size(&self) -> Option<usize> {
        Some(SM4_BLOCK_SIZE)
    }

    fn encrypt(&self, dst: &mut Vec<u8>, plaintext_block: &[u8]) -> Result<usize, CryptoError> {
        self.ed_inner(dst, plaintext_block, |rk: &[u32; 32], idx: usize| -> u32 {rk[idx]})
    }

    fn decrypt(&self, dst: &mut Vec<u8>, cipher_block: &[u8]) -> Result<usize, CryptoError> {
        self.ed_inner(dst, cipher_block, |rk: &[u32; 32], idx: usize| -> u32 {rk[31-idx]})
    }
}