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 ) -> 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 #[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);