1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use srd_errors::SrdError;

use chacha::{ChaCha, KeyStream};

cfg_if! {
    if #[cfg(feature = "aes")]{
        use aes::Aes256;
        use block_modes::{BlockMode, Cbc, block_padding::NoPadding};
    }
}

use Result;

const AES256_FLAG: u32 = 0x00000001;
const CHACHA20_FLAG: u32 = 0x00000100;
const XCHACHA20_FLAG: u32 = 0x00000200;

#[derive(Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "ser", derive(Serialize, Deserialize))]
pub enum Cipher {
    AES256,
    ChaCha20,
    XChaCha20,
}

impl Cipher {
    pub fn encrypt_data(&self, data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>> {
        match self {
            &Cipher::AES256 => encrypt_data_aes(data, key, iv),
            &Cipher::ChaCha20 => encrypt_data_chacha(data, key, iv),
            &Cipher::XChaCha20 => encrypt_data_xchacha(data, key, iv),
        }
    }

    pub fn decrypt_data(&self, data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>> {
        match self {
            &Cipher::AES256 => decrypt_data_aes(data, key, iv),
            &Cipher::ChaCha20 => encrypt_data_chacha(data, key, iv),
            &Cipher::XChaCha20 => encrypt_data_xchacha(data, key, iv),
        }
    }

    pub fn flag(&self) -> u32 {
        match self {
            &Cipher::AES256 => AES256_FLAG,
            &Cipher::ChaCha20 => CHACHA20_FLAG,
            &Cipher::XChaCha20 => XCHACHA20_FLAG,
        }
    }

    pub fn from_flags(flags: u32) -> Vec<Self> {
        let mut ciphers = Vec::new();
        if flags & AES256_FLAG != 0 {
            ciphers.push(Cipher::AES256)
        };
        if flags & CHACHA20_FLAG != 0 {
            ciphers.push(Cipher::ChaCha20)
        };
        if flags & XCHACHA20_FLAG != 0 {
            ciphers.push(Cipher::XChaCha20)
        };
        ciphers
    }

    pub fn best_cipher(ciphers: &[Cipher]) -> Result<Cipher> {
        if ciphers.contains(&Cipher::XChaCha20) {
            return Ok(Cipher::XChaCha20);
        };
        if ciphers.contains(&Cipher::ChaCha20) {
            return Ok(Cipher::ChaCha20);
        };
        if ciphers.contains(&Cipher::AES256) {
            return Ok(Cipher::AES256);
        };
        Err(SrdError::Cipher)
    }
}

#[cfg(feature = "aes")]
fn encrypt_data_aes(data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>> {
    if data.len() % 16 != 0 {
        return Err(SrdError::InvalidDataLength);
    }

    let cipher = Cbc::<Aes256, NoPadding>::new_var(key, &iv[0..16])?;
    let ciphertext = cipher.encrypt_vec(data);

    Ok(ciphertext)
}

#[cfg(feature = "aes")]
fn decrypt_data_aes(data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>> {
    if data.len() % 16 != 0 {
        return Err(SrdError::InvalidDataLength);
    }

    let cipher = Cbc::<Aes256, NoPadding>::new_var(key, &iv[0..16])?;
    let plaintext = cipher.decrypt_vec(data)?;

    Ok(plaintext)
}

#[cfg(not(feature = "aes"))]
fn encrypt_data_aes(_: &[u8], _: &[u8], _: &[u8]) -> Result<Vec<u8>> {
    unreachable!();
}

#[cfg(not(feature = "aes"))]
fn decrypt_data_aes(_: &[u8], _: &[u8], _: &[u8]) -> Result<Vec<u8>> {
    unreachable!();
}

fn encrypt_data_chacha(data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>> {
    let mut key_ref = [0u8; 32];
    key_ref.copy_from_slice(key);

    let mut iv_ref = [0u8; 8];
    iv_ref.copy_from_slice(&iv[0..8]);

    let mut stream = ChaCha::new_chacha20(&key_ref, &iv_ref);
    let mut buffer = data.to_vec();

    stream.xor_read(&mut buffer)?;
    Ok(buffer)
}

fn encrypt_data_xchacha(data: &[u8], key: &[u8], iv: &[u8]) -> Result<Vec<u8>> {
    let mut key_ref = [0u8; 32];
    key_ref.copy_from_slice(key);

    let mut iv_ref = [0u8; 24];
    iv_ref.copy_from_slice(&iv[0..24]);

    let mut stream = ChaCha::new_xchacha20(&key_ref, &iv_ref);
    let mut buffer = data.to_vec();

    stream.xor_read(&mut buffer)?;
    Ok(buffer)
}