Skip to main content

devolutions_crypto/online_ciphertext/
mod.rs

1//! Module for symmetric/asymmetric encryption/decryption.
2//!
3//! This module contains everything related to encryption. You can use it to encrypt and decrypt data using either a shared key of a keypair.
4//! Either way, the encryption will give you a `Ciphertext`, which has a method to decrypt it.
5//!
6//! ### Symmetric
7//!
8//! ```rust
9//! use devolutions_crypto::utils::generate_key;
10//! use devolutions_crypto::ciphertext::{ encrypt, CiphertextVersion, Ciphertext };
11//!
12//! let key: Vec<u8> = generate_key(32).expect("generate key shouldn't fail");
13//!
14//! let data = b"somesecretdata";
15//!
16//! let encrypted_data: Ciphertext = encrypt(data, &key, CiphertextVersion::Latest).expect("encryption shouldn't fail");
17//!
18//! let decrypted_data = encrypted_data.decrypt(&key).expect("The decryption shouldn't fail");
19//!
20//! assert_eq!(decrypted_data, data);
21//! ```
22//!
23//! ### Asymmetric
24//! Here, you will need a `PublicKey` to encrypt data and the corresponding
25//! `PrivateKey` to decrypt it. You can generate them by using `generate_keypair`
26//! in the [Key module](#key).
27//!
28//! ```rust
29//! use devolutions_crypto::key::{generate_keypair, KeyVersion, KeyPair};
30//! use devolutions_crypto::ciphertext::{ encrypt_asymmetric, CiphertextVersion, Ciphertext };
31//!
32//! let keypair: KeyPair = generate_keypair(KeyVersion::Latest);
33//!
34//! let data = b"somesecretdata";
35//!
36//! let encrypted_data: Ciphertext = encrypt_asymmetric(data, &keypair.public_key, CiphertextVersion::Latest).expect("encryption shouldn't fail");
37//!
38//! let decrypted_data = encrypted_data.decrypt_asymmetric(&keypair.private_key).expect("The decryption shouldn't fail");
39//!
40//! assert_eq!(decrypted_data, data);
41//! ```
42
43mod online_ciphertext_v1;
44
45use std::borrow::Borrow;
46
47use super::CiphertextSubtype;
48use super::DataType;
49use super::Error;
50use super::Header;
51use super::HeaderType;
52pub use super::OnlineCiphertextVersion;
53use super::Result;
54
55use super::key::{PrivateKey, PublicKey};
56
57use online_ciphertext_v1::{OnlineCiphertextV1Decryptor, OnlineCiphertextV1Encryptor};
58use online_ciphertext_v1::{
59    OnlineCiphertextV1Header, OnlineCiphertextV1HeaderAsymmetric, OnlineCiphertextV1HeaderSymmetric,
60};
61
62use paste::paste;
63
64impl OnlineCiphertextHeader {
65    pub fn into_decryptor(self, key: &[u8], aad: &[u8]) -> Result<OnlineCiphertextDecryptor> {
66        let mut full_aad: Vec<u8> = self.header.borrow().into();
67        full_aad.extend_from_slice(aad);
68
69        match self.payload {
70            OnlineCiphertextHeaderPayload::V1(header) => match header {
71                OnlineCiphertextV1Header::Symmetric(header) => {
72                    let cipher = OnlineCiphertextV1Decryptor::new(key, full_aad, header);
73
74                    Ok(OnlineCiphertextDecryptor::V1(cipher))
75                }
76                _ => Err(Error::InvalidDataType),
77            },
78        }
79    }
80
81    pub fn into_decryptor_asymmetric(
82        self,
83        key: &PrivateKey,
84        aad: &[u8],
85    ) -> Result<OnlineCiphertextDecryptor> {
86        let mut full_aad: Vec<u8> = self.header.borrow().into();
87        full_aad.extend_from_slice(aad);
88
89        match self.payload {
90            OnlineCiphertextHeaderPayload::V1(header) => match header {
91                OnlineCiphertextV1Header::Asymmetric(header) => {
92                    let cipher = OnlineCiphertextV1Decryptor::new_asymmetric(key, full_aad, header);
93
94                    Ok(OnlineCiphertextDecryptor::V1(cipher))
95                }
96                _ => Err(Error::InvalidDataType),
97            },
98        }
99    }
100
101    pub fn get_serialized_size(&self) -> usize {
102        self.payload.get_serialized_size() + 8
103    }
104
105    pub fn get_chunk_size(&self) -> u32 {
106        self.payload.get_chunk_size()
107    }
108}
109
110impl OnlineCiphertextEncryptor {
111    pub fn new(
112        key: &[u8],
113        aad: &[u8],
114        chunk_size: u32,
115        version: OnlineCiphertextVersion,
116    ) -> Result<OnlineCiphertextEncryptor> {
117        let header = Header::<OnlineCiphertextHeader> {
118            data_subtype: CiphertextSubtype::Symmetric,
119            ..Default::default()
120        };
121
122        let mut full_aad: Vec<u8> = header.borrow().into();
123        full_aad.extend_from_slice(aad);
124
125        match version {
126            OnlineCiphertextVersion::V1 | OnlineCiphertextVersion::Latest => {
127                let cipher = OnlineCiphertextV1Encryptor::new(key, full_aad, chunk_size)?;
128
129                Ok(OnlineCiphertextEncryptor::V1(cipher))
130            }
131        }
132    }
133
134    pub fn new_asymmetric(
135        public_key: &PublicKey,
136        aad: &[u8],
137        chunk_size: u32,
138        version: OnlineCiphertextVersion,
139    ) -> Result<OnlineCiphertextEncryptor> {
140        let header = Header::<OnlineCiphertextHeader> {
141            data_subtype: CiphertextSubtype::Asymmetric,
142            ..Default::default()
143        };
144
145        let mut full_aad: Vec<u8> = header.borrow().into();
146        full_aad.extend_from_slice(aad);
147
148        match version {
149            OnlineCiphertextVersion::V1 | OnlineCiphertextVersion::Latest => {
150                let cipher =
151                    OnlineCiphertextV1Encryptor::new_asymmetric(public_key, full_aad, chunk_size)?;
152
153                Ok(OnlineCiphertextEncryptor::V1(cipher))
154            }
155        }
156    }
157}
158
159macro_rules! online_ciphertext_header_impl {
160    ($($version_name:ident),+) => {
161        paste! {
162            /// A versionned online ciphertext.
163            #[derive(Clone, Debug)]
164            pub struct OnlineCiphertextHeader {
165                pub(crate) header: Header<OnlineCiphertextHeader>,
166                payload: OnlineCiphertextHeaderPayload,
167            }
168
169            impl HeaderType for OnlineCiphertextHeader {
170                type Version = OnlineCiphertextVersion;
171                type Subtype = CiphertextSubtype;
172
173                fn data_type() -> DataType {
174                    DataType::OnlineCiphertext
175                }
176            }
177
178            #[derive(Clone, Debug)]
179            enum OnlineCiphertextHeaderPayload {
180                $(
181                    $version_name([<OnlineCiphertext $version_name Header>]),
182                ),+
183            }
184
185            impl OnlineCiphertextHeaderPayload {
186                pub fn get_serialized_size(&self) -> usize {
187                    match &self {
188                        $(
189                            Self::$version_name(p) => p.get_serialized_size(),
190                        ),+
191                    }
192                }
193
194                pub fn get_chunk_size(&self) -> u32 {
195                    match &self {
196                        $(
197                            Self::$version_name(p) => p.get_chunk_size(),
198                        ),+
199                    }
200                }
201            }
202
203            impl From<&OnlineCiphertextHeader> for Vec<u8> {
204                fn from(value: &OnlineCiphertextHeader) -> Self {
205                    let mut output: Vec<u8> = value.header.borrow().into();
206                    let mut payload: Vec<u8> = match &value.payload {
207                        $(
208                            OnlineCiphertextHeaderPayload::$version_name(p) => {
209                                p.into()
210                            },
211                        ),+
212                    };
213
214                    output.append(&mut payload);
215                    output
216                }
217            }
218
219            impl TryFrom<&[u8]> for OnlineCiphertextHeader {
220                type Error = Error;
221
222                fn try_from(value: &[u8]) -> Result<Self> {
223                    if value.len() < 8 {
224                        return Err(Error::InvalidLength);
225                    }
226                    let header = Header::<OnlineCiphertextHeader>::try_from(&value[..8])?;
227
228                    let version = if header.version == OnlineCiphertextVersion::Latest { OnlineCiphertextVersion::V1 } else { header.version };
229                    match version {
230                        $(
231                            OnlineCiphertextVersion::$version_name => {
232                                match header.data_subtype {
233                                    CiphertextSubtype::Symmetric => {
234                                        Ok(Self {
235                                            header,
236                                            payload:
237                                                OnlineCiphertextHeaderPayload::$version_name
238                                                    ([<OnlineCiphertext $version_name Header>]::Symmetric
239                                                        ([<OnlineCiphertext $version_name HeaderSymmetric>]::try_from(&value[8..])?))
240                                        })
241                                    }
242                                    CiphertextSubtype::Asymmetric => {
243                                        Ok(Self {
244                                            header,
245                                            payload:
246                                                OnlineCiphertextHeaderPayload::$version_name
247                                                    ([<OnlineCiphertext $version_name Header>]::Asymmetric
248                                                        ([<OnlineCiphertext $version_name HeaderAsymmetric>]::try_from(&value[8..])?))
249                                        })
250                                    }
251                                    CiphertextSubtype::None => Err(Error::UnknownSubtype)
252                                }
253                            }
254                        ),+
255                        OnlineCiphertextVersion::Latest => unreachable!("Latest is checked before the match arm")
256                    }
257                }
258            }
259        }
260    };
261}
262
263macro_rules! online_ciphertext_impl {
264    ($name:ident, $func:ident, $($version_name:ident),+) => {
265        paste! {
266            pub enum [<OnlineCiphertext $name>] {
267            $(
268                $version_name([<OnlineCiphertext $version_name $name>]),
269            ),+
270            }
271
272            impl [<OnlineCiphertext $name>] {
273                pub fn get_chunk_size(&self) -> u32 {
274                    match &self {
275                    $(
276                        [<OnlineCiphertext $name>]::$version_name(cipher) => {
277                            cipher.get_chunk_size()
278                        }
279                    ),+
280                    }
281                }
282
283                pub fn get_tag_size(&self) -> usize {
284                    match &self {
285                    $(
286                        [<OnlineCiphertext $name>]::$version_name(cipher) => {
287                            cipher.get_tag_size()
288                        }
289                    ),+
290                    }
291                }
292
293                pub fn get_header(&self) -> OnlineCiphertextHeader {
294                    match self {
295                    $(
296                        Self::$version_name(encryptor) => {
297                            let data_subtype = encryptor.get_header().get_subtype();
298                            OnlineCiphertextHeader {
299                                header: Header::<OnlineCiphertextHeader> {
300                                    data_subtype,
301                                    ..Default::default()
302                                },
303                                payload: OnlineCiphertextHeaderPayload::$version_name(encryptor.get_header().clone()),
304                            }
305                        }
306                    ),+
307                    }
308                }
309
310                pub fn [<$func _next_chunk>](
311                    &mut self,
312                    data: &[u8],
313                    aad: &[u8],
314                ) -> Result<Vec<u8>> {
315                    match self {
316                    $(
317                        [<OnlineCiphertext $name>]::$version_name(cipher) => {
318                            cipher.[<$func _next_chunk>](data, aad)
319                        }
320                    ),+
321                    }
322                }
323
324                pub fn [<$func _next_chunk_in_place>](
325                    &mut self,
326                    data: &mut Vec<u8>,
327                    aad: &[u8],
328                ) -> Result<()> {
329                    match self {
330                    $(
331                        [<OnlineCiphertext $name>]::$version_name(cipher) => {
332                            cipher.[<$func _next_chunk_in_place>](data, aad)
333                        }
334                    ),+
335                    }
336                }
337
338                pub fn [<$func _last_chunk>](
339                    self,
340                    data: &[u8],
341                    aad: &[u8],
342                ) -> Result<Vec<u8>> {
343                    match self {
344                    $(
345                        [<OnlineCiphertext $name>]::$version_name(cipher) => {
346                            cipher.[<$func _last_chunk>](data, aad)
347                        }
348                    ),+
349                    }
350                }
351
352                pub fn [<$func _last_chunk_in_place>](
353                    self,
354                    data: &mut Vec<u8>,
355                    aad: &[u8],
356                ) -> Result<()> {
357                    match self {
358                    $(
359                        [<OnlineCiphertext $name>]::$version_name(cipher) => {
360                            cipher.[<$func _last_chunk_in_place>](data, aad)
361                        }
362                    ),+
363                    }
364                }
365            }
366        }
367    };
368}
369
370online_ciphertext_impl!(Encryptor, encrypt, V1);
371online_ciphertext_impl!(Decryptor, decrypt, V1);
372
373online_ciphertext_header_impl!(V1);