dexios_core/
cipher.rs

1//! This module is used for standard, typical encryption and decryption.
2//!
3//! The data is fully loaded into memory before encryption/decryption, and it is processed within the same "block"
4//!
5//! # Examples
6//! ```rust,ignore
7//! // obviously the key should contain data, not be an empty vec
8//! let raw_key = Protected::new(vec![0u8; 128]);
9//! let salt = gen_salt();
10//! let key = balloon_hash(raw_key, &salt, &HeaderVersion::V4).unwrap();
11//! let cipher = Ciphers::initialize(key, &Algorithm::XChaCha20Poly1305).unwrap();
12//!
13//! let secret = "super secret information";
14//!
15//! let nonce = gen_nonce(&Algorithm::XChaCha20Poly1305, &Mode::MemoryMode);
16//! let encrypted_data = cipher.encrypt(&nonce, secret.as_bytes()).unwrap();
17//!
18//! let decrypted_data = cipher.decrypt(&nonce, encrypted_data.as_slice()).unwrap();
19//!
20//! assert_eq!(secret, decrypted_data);
21//! ```
22
23use aead::{Aead, AeadInPlace, KeyInit, Payload};
24use aes_gcm::Aes256Gcm;
25use chacha20poly1305::XChaCha20Poly1305;
26use deoxys::DeoxysII256;
27
28use crate::primitives::Algorithm;
29use crate::protected::Protected;
30
31/// This `enum` defines all possible cipher types, for each AEAD that is supported by `dexios-core`
32pub enum Ciphers {
33    Aes256Gcm(Box<Aes256Gcm>),
34    XChaCha(Box<XChaCha20Poly1305>),
35    DeoxysII(Box<DeoxysII256>),
36}
37
38impl Ciphers {
39    /// This can be used to quickly initialise a `Cipher`
40    ///
41    /// The returned `Cipher` can be used for both encryption and decryption
42    ///
43    /// You just need to provide the `argon2id`/`balloon` hashed key, and the algorithm to use
44    ///
45    /// # Examples
46    /// ```rust,ignore
47    /// // obviously the key should contain data, not be an empty vec
48    /// let raw_key = Protected::new(vec![0u8; 128]);
49    /// let salt = gen_salt();
50    /// let key = balloon_hash(raw_key, &salt, &HeaderVersion::V4).unwrap();
51    /// let cipher = Ciphers::initialize(key, &Algorithm::XChaCha20Poly1305).unwrap();
52    /// ```
53    ///
54    pub fn initialize(key: Protected<[u8; 32]>, algorithm: &Algorithm) -> anyhow::Result<Self> {
55        let cipher = match algorithm {
56            Algorithm::Aes256Gcm => {
57                let cipher = Aes256Gcm::new_from_slice(key.expose())
58                    .map_err(|_| anyhow::anyhow!("Unable to create cipher with hashed key."))?;
59
60                Ciphers::Aes256Gcm(Box::new(cipher))
61            }
62            Algorithm::XChaCha20Poly1305 => {
63                let cipher = XChaCha20Poly1305::new_from_slice(key.expose())
64                    .map_err(|_| anyhow::anyhow!("Unable to create cipher with hashed key."))?;
65
66                Ciphers::XChaCha(Box::new(cipher))
67            }
68            Algorithm::DeoxysII256 => {
69                let cipher = DeoxysII256::new_from_slice(key.expose())
70                    .map_err(|_| anyhow::anyhow!("Unable to create cipher with hashed key."))?;
71
72                Ciphers::DeoxysII(Box::new(cipher))
73            }
74        };
75
76        drop(key);
77        Ok(cipher)
78    }
79
80    /// This can be used to encrypt data with a given `Ciphers` object
81    ///
82    /// It requires the nonce, and either some plaintext, or an `aead::Payload` (that contains the plaintext and the AAD)
83    pub fn encrypt<'msg, 'aad>(
84        &self,
85        nonce: &[u8],
86        plaintext: impl Into<Payload<'msg, 'aad>>,
87    ) -> aead::Result<Vec<u8>> {
88        match self {
89            Ciphers::Aes256Gcm(c) => c.encrypt(nonce.as_ref().into(), plaintext),
90            Ciphers::XChaCha(c) => c.encrypt(nonce.as_ref().into(), plaintext),
91            Ciphers::DeoxysII(c) => c.encrypt(nonce.as_ref().into(), plaintext),
92        }
93    }
94
95    pub fn encrypt_in_place(
96        &self,
97        nonce: &[u8],
98        aad: &[u8],
99        buffer: &mut dyn aead::Buffer,
100    ) -> Result<(), aead::Error> {
101        match self {
102            Ciphers::Aes256Gcm(c) => c.encrypt_in_place(nonce.as_ref().into(), aad, buffer),
103            Ciphers::XChaCha(c) => c.encrypt_in_place(nonce.as_ref().into(), aad, buffer),
104            Ciphers::DeoxysII(c) => c.encrypt_in_place(nonce.as_ref().into(), aad, buffer),
105        }
106    }
107
108    /// This can be used to decrypt data with a given `Ciphers` object
109    ///
110    /// It requires the nonce used for encryption, and either some plaintext, or an `aead::Payload` (that contains the plaintext and the AAD)
111    ///
112    /// NOTE: The data will not decrypt successfully if an AAD was provided for encryption, but is not present/has been modified while decrypting
113    pub fn decrypt<'msg, 'aad>(
114        &self,
115        nonce: &[u8],
116        ciphertext: impl Into<Payload<'msg, 'aad>>,
117    ) -> aead::Result<Vec<u8>> {
118        match self {
119            Ciphers::Aes256Gcm(c) => c.decrypt(nonce.as_ref().into(), ciphertext),
120            Ciphers::XChaCha(c) => c.decrypt(nonce.as_ref().into(), ciphertext),
121            Ciphers::DeoxysII(c) => c.decrypt(nonce.as_ref().into(), ciphertext),
122        }
123    }
124}