summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/backend/kernel/symmetric.rs
blob: d3e7f3dc3ff563b090fdc5554720ca50c9e5d02f (plain)
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
use crate::crypto::symmetric::Mode;

use crate::types::SymmetricAlgorithm;
use crate::{Error, Result};
use unimpl::unimpl;

use kcapi::{INIT_AIO, ACCESS_HEURISTIC};
use kcapi::skcipher::KcapiSKCipher;
use std::sync::Mutex;

fn symmetric_algo_to_skcipher(sk_algo: SymmetricAlgorithm) -> Result<&'static str> {
    Ok(match sk_algo {
        SymmetricAlgorithm::TripleDES => "des3",
        SymmetricAlgorithm::AES128 | SymmetricAlgorithm::AES192 | SymmetricAlgorithm::AES256 => "aes",
        _ => return Err(Error::UnsupportedSymmetricAlgorithm(sk_algo).into()),
    })
}

struct KernelCipher {
    cipher: Option<Mutex<KcapiSKCipher>>,
    block_size: usize,
    iv: Vec<u8>,
    key: Vec<u8>,
    mode: String,
    sk_algo: SymmetricAlgorithm,
}

impl KernelCipher {
    fn new(mode: &str, sk_algo: SymmetricAlgorithm) -> Result<Self> {
        Ok(Self {
            cipher: None,
            block_size: sk_algo.block_size()?,
            sk_algo,
            mode: mode.into(),
            iv: vec![0; sk_algo.block_size()?],
            key: vec![0; sk_algo.block_size()?],
        })
    }

    fn set_key(&mut self, key: &[u8]) {
        self.key = key.into();
    }

    fn set_iv(&mut self, iv: Vec<u8>) {
        self.iv = iv;
    }
}

impl Mode for KernelCipher {
    fn block_size(&self) -> usize {
        self.block_size
    }

    fn encrypt(&mut self, dst: &mut [u8], src: &[u8]) -> Result<()> {
        if let Some(cipher) = &self.cipher {
            let mut cipher = cipher.lock().expect("not to be poisoned");
            if src.len() < self.block_size {
                cipher.stream_update_last(vec![src.into()])?;
            } else {
                cipher.stream_update(vec![src.into()])?;
            }
            dst.copy_from_slice(&cipher.stream_op()?[0]);
        } else {
            let mut cipher = KcapiSKCipher::new_enc_stream(&format!("{}({})", self.mode, symmetric_algo_to_skcipher(self.sk_algo)?), self.key.clone(), self.iv.clone(), vec![src.into()])?;
            dst.copy_from_slice(&cipher.stream_op()?[0]);
            self.cipher = Some(Mutex::new(cipher));
        }
        Ok(())
    }

    fn decrypt(&mut self, dst: &mut [u8], src: &[u8]) -> Result<()> {
        if let Some(cipher) = &self.cipher {
            let mut cipher = cipher.lock().expect("not to be poisoned");
            if src.len() < self.block_size {
                cipher.stream_update_last(vec![src.into()])?;
            } else {
                cipher.stream_update(vec![src.into()])?;
            }
            dst.copy_from_slice(&cipher.stream_op()?[0]);
        } else {
            let mut cipher = KcapiSKCipher::new_dec_stream(&format!("{}({})", self.mode, symmetric_algo_to_skcipher(self.sk_algo)?), self.key.clone(), self.iv.clone(), vec![src.into()])?;
            dst.copy_from_slice(&cipher.stream_op()?[0]);
            self.cipher = Some(Mutex::new(cipher));
        }
        Ok(())
    }
}

impl SymmetricAlgorithm {
    /// Returns whether this algorithm is supported by the crypto backend.
    pub(crate) fn is_supported_by_backend(&self) -> bool {
        KernelCipher::new("ecb", *self).is_ok()
    }

    /// Creates a OpenSSL context for encrypting in CFB mode.
    pub(crate) fn make_encrypt_cfb(self, key: &[u8], iv: Vec<u8>) -> Result<Box<dyn Mode>> {
        let mut cipher = KernelCipher::new("cfb", self)?;
        cipher.set_key(key);
        cipher.set_iv(iv);
        Ok(Box::new(cipher))
    }

    /// Creates a OpenSSL context for decrypting in CFB mode.
    pub(crate) fn make_decrypt_cfb(self, key: &[u8], iv: Vec<u8>) -> Result<Box<dyn Mode>> {
        self.make_encrypt_cfb(key, iv)
    }

    /// Creates a OpenSSL context for encrypting in ECB mode.
    pub(crate) fn make_encrypt_ecb(self, key: &[u8]) -> Result<Box<dyn Mode>> {
        let mut cipher = KernelCipher::new("ecb", self)?;
        cipher.set_key(key);
        Ok(Box::new(cipher))
    }

    /// Creates a OpenSSL context for decrypting in ECB mode.
    pub(crate) fn make_decrypt_ecb(self, key: &[u8]) -> Result<Box<dyn Mode>> {
        self.make_encrypt_ecb(key)
    }
}