devolutions_crypto/online_ciphertext/
mod.rs1mod 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 ) -> OnlineCiphertextEncryptor {
117 let mut 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 header.version = OnlineCiphertextVersion::V1;
128
129 let cipher = OnlineCiphertextV1Encryptor::new(key, full_aad, chunk_size);
130
131 OnlineCiphertextEncryptor::V1(cipher)
132 }
133 }
134 }
135
136 pub fn new_asymmetric(
137 public_key: &PublicKey,
138 aad: &[u8],
139 chunk_size: u32,
140 version: OnlineCiphertextVersion,
141 ) -> OnlineCiphertextEncryptor {
142 let mut header = Header::<OnlineCiphertextHeader> {
143 data_subtype: CiphertextSubtype::Asymmetric,
144 ..Default::default()
145 };
146
147 let mut full_aad: Vec<u8> = header.borrow().into();
148 full_aad.extend_from_slice(aad);
149
150 match version {
151 OnlineCiphertextVersion::V1 | OnlineCiphertextVersion::Latest => {
152 header.version = OnlineCiphertextVersion::V1;
153
154 let cipher =
155 OnlineCiphertextV1Encryptor::new_asymmetric(public_key, full_aad, chunk_size);
156
157 OnlineCiphertextEncryptor::V1(cipher)
158 }
159 }
160 }
161}
162
163macro_rules! online_ciphertext_header_impl {
164 ($($version_name:ident),+) => {
165 paste! {
166 #[derive(Clone, Debug)]
168 pub struct OnlineCiphertextHeader {
169 pub(crate) header: Header<OnlineCiphertextHeader>,
170 payload: OnlineCiphertextHeaderPayload,
171 }
172
173 impl HeaderType for OnlineCiphertextHeader {
174 type Version = OnlineCiphertextVersion;
175 type Subtype = CiphertextSubtype;
176
177 fn data_type() -> DataType {
178 DataType::OnlineCiphertext
179 }
180 }
181
182 #[derive(Clone, Debug)]
183 enum OnlineCiphertextHeaderPayload {
184 $(
185 $version_name([<OnlineCiphertext $version_name Header>]),
186 ),+
187 }
188
189 impl OnlineCiphertextHeaderPayload {
190 pub fn get_serialized_size(&self) -> usize {
191 match &self {
192 $(
193 Self::$version_name(p) => p.get_serialized_size(),
194 ),+
195 }
196 }
197
198 pub fn get_chunk_size(&self) -> u32 {
199 match &self {
200 $(
201 Self::$version_name(p) => p.get_chunk_size(),
202 ),+
203 }
204 }
205 }
206
207 impl From<&OnlineCiphertextHeader> for Vec<u8> {
208 fn from(value: &OnlineCiphertextHeader) -> Self {
209 let mut output: Vec<u8> = value.header.borrow().into();
210 let mut payload: Vec<u8> = match &value.payload {
211 $(
212 OnlineCiphertextHeaderPayload::$version_name(p) => {
213 p.into()
214 },
215 ),+
216 };
217
218 output.append(&mut payload);
219 output
220 }
221 }
222
223 impl TryFrom<&[u8]> for OnlineCiphertextHeader {
224 type Error = Error;
225
226 fn try_from(value: &[u8]) -> Result<Self> {
227 let header = Header::<OnlineCiphertextHeader>::try_from(&value[..8])?;
228
229 let version = if header.version == OnlineCiphertextVersion::Latest { OnlineCiphertextVersion::V1 } else { header.version };
230 match version {
231 $(
232 OnlineCiphertextVersion::$version_name => {
233 match header.data_subtype {
234 CiphertextSubtype::Symmetric => {
235 Ok(Self {
236 header,
237 payload:
238 OnlineCiphertextHeaderPayload::$version_name
239 ([<OnlineCiphertext $version_name Header>]::Symmetric
240 ([<OnlineCiphertext $version_name HeaderSymmetric>]::try_from(&value[8..])?))
241 })
242 }
243 CiphertextSubtype::Asymmetric => {
244 Ok(Self {
245 header,
246 payload:
247 OnlineCiphertextHeaderPayload::$version_name
248 ([<OnlineCiphertext $version_name Header>]::Asymmetric
249 ([<OnlineCiphertext $version_name HeaderAsymmetric>]::try_from(&value[8..])?))
250 })
251 }
252 CiphertextSubtype::None => Err(Error::UnknownSubtype)
253 }
254 }
255 ),+
256 OnlineCiphertextVersion::Latest => unreachable!("Latest is checked before the match arm")
257 }
258 }
259 }
260 }
261 };
262}
263
264macro_rules! online_ciphertext_impl {
265 ($name:ident, $func:ident, $($version_name:ident),+) => {
266 paste! {
267 pub enum [<OnlineCiphertext $name>] {
268 $(
269 $version_name([<OnlineCiphertext $version_name $name>]),
270 ),+
271 }
272
273 impl [<OnlineCiphertext $name>] {
274 pub fn get_chunk_size(&self) -> u32 {
275 match &self {
276 $(
277 [<OnlineCiphertext $name>]::$version_name(cipher) => {
278 cipher.get_chunk_size()
279 }
280 ),+
281 }
282 }
283
284 pub fn get_tag_size(&self) -> usize {
285 match &self {
286 $(
287 [<OnlineCiphertext $name>]::$version_name(cipher) => {
288 cipher.get_tag_size()
289 }
290 ),+
291 }
292 }
293
294 pub fn get_header(&self) -> OnlineCiphertextHeader {
295 match self {
296 $(
297 Self::$version_name(encryptor) => {
298 let data_subtype = encryptor.get_header().get_subtype();
299 OnlineCiphertextHeader {
300 header: Header::<OnlineCiphertextHeader> {
301 data_subtype,
302 ..Default::default()
303 },
304 payload: OnlineCiphertextHeaderPayload::$version_name(encryptor.get_header().clone()),
305 }
306 }
307 ),+
308 }
309 }
310
311 pub fn [<$func _next_chunk>](
312 &mut self,
313 data: &[u8],
314 aad: &[u8],
315 ) -> Result<Vec<u8>> {
316 match self {
317 $(
318 [<OnlineCiphertext $name>]::$version_name(cipher) => {
319 cipher.[<$func _next_chunk>](data, aad)
320 }
321 ),+
322 }
323 }
324
325 pub fn [<$func _next_chunk_in_place>](
326 &mut self,
327 data: &mut Vec<u8>,
328 aad: &[u8],
329 ) -> Result<()> {
330 match self {
331 $(
332 [<OnlineCiphertext $name>]::$version_name(cipher) => {
333 cipher.[<$func _next_chunk_in_place>](data, aad)
334 }
335 ),+
336 }
337 }
338
339 pub fn [<$func _last_chunk>](
340 self,
341 data: &[u8],
342 aad: &[u8],
343 ) -> Result<Vec<u8>> {
344 match self {
345 $(
346 [<OnlineCiphertext $name>]::$version_name(cipher) => {
347 cipher.[<$func _last_chunk>](data, aad)
348 }
349 ),+
350 }
351 }
352
353 pub fn [<$func _last_chunk_in_place>](
354 self,
355 data: &mut Vec<u8>,
356 aad: &[u8],
357 ) -> Result<()> {
358 match self {
359 $(
360 [<OnlineCiphertext $name>]::$version_name(cipher) => {
361 cipher.[<$func _last_chunk_in_place>](data, aad)
362 }
363 ),+
364 }
365 }
366 }
367 }
368 };
369}
370
371online_ciphertext_impl!(Encryptor, encrypt, V1);
372online_ciphertext_impl!(Decryptor, decrypt, V1);
373
374online_ciphertext_header_impl!(V1);