redact_crypto/
entry.rs

1use crate::{
2    Algorithm, ByteAlgorithm, ByteSource, CryptoError, Data, DataBuilder, HasByteSource, HasIndex,
3    Key, KeyBuilder, Storer, ToPublicAsymmetricByteAlgorithm, ToSecretAsymmetricByteAlgorithm,
4    ToSymmetricByteAlgorithm, TypeStorer,
5};
6use async_recursion::async_recursion;
7use async_trait::async_trait;
8use mongodb::bson::Document;
9use once_cell::sync::OnceCell;
10use serde::{Deserialize, Serialize};
11use std::convert::TryFrom;
12
13pub type EntryPath = String;
14
15#[derive(Serialize, Deserialize, Debug)]
16#[serde(bound = "T: StorableType")]
17pub struct Entry<T> {
18    pub path: EntryPath,
19    pub builder: TypeBuilder,
20    pub value: State,
21    #[serde(skip)]
22    resolved_value: OnceCell<T>,
23}
24
25pub trait StorableType:
26    HasByteSource
27    + HasBuilder
28    + HasIndex<Index = Document>
29    + Unpin
30    + Send
31    + std::fmt::Debug
32    + 'static
33{
34}
35
36impl<T: ToSymmetricByteAlgorithm + StorableType> Entry<T> {
37    pub async fn to_symmetric_byte_algorithm(
38        self,
39        nonce: Option<<T as ToSymmetricByteAlgorithm>::Nonce>,
40    ) -> Result<ByteAlgorithm, CryptoError> {
41        let (key, entry_path, state) = self.take_resolve_all().await?;
42        key.to_byte_algorithm(nonce, |key| async move {
43            match state {
44                State::Referenced { path, storer } => key.to_ref_entry(path, storer),
45                State::Sealed { algorithm, .. } => key.to_sealed_entry(entry_path, algorithm).await,
46                State::Unsealed { .. } => key.to_unsealed_entry(entry_path),
47            }
48        })
49        .await
50    }
51}
52
53impl<T: ToSecretAsymmetricByteAlgorithm + StorableType> Entry<T> {
54    pub async fn to_secret_asymmetric_byte_algorithm(
55        self,
56        public_key: Option<Entry<<T as ToSecretAsymmetricByteAlgorithm>::PublicKey>>,
57        nonce: Option<<T as ToSecretAsymmetricByteAlgorithm>::Nonce>,
58    ) -> Result<ByteAlgorithm, CryptoError> {
59        let (secret_key, entry_path, state) = self.take_resolve_all().await?;
60        secret_key
61            .to_byte_algorithm(public_key, nonce, |key| async move {
62                match state {
63                    State::Referenced { path, storer } => key.to_ref_entry(path, storer),
64                    State::Sealed { algorithm, .. } => {
65                        key.to_sealed_entry(entry_path, algorithm).await
66                    }
67                    State::Unsealed { .. } => key.to_unsealed_entry(entry_path),
68                }
69            })
70            .await
71    }
72}
73
74impl<T: ToPublicAsymmetricByteAlgorithm + StorableType> Entry<T> {
75    pub async fn to_public_asymmetric_byte_algorithm(
76        self,
77        secret_key: Entry<<T as ToPublicAsymmetricByteAlgorithm>::SecretKey>,
78        nonce: Option<<T as ToPublicAsymmetricByteAlgorithm>::Nonce>,
79    ) -> Result<ByteAlgorithm, CryptoError> {
80        let (public_key, entry_path, state) = self.take_resolve_all().await?;
81        public_key
82            .to_byte_algorithm(secret_key, nonce, |key| async move {
83                match state {
84                    State::Referenced { path, storer } => key.to_ref_entry(path, storer),
85                    State::Sealed { algorithm, .. } => {
86                        key.to_sealed_entry(entry_path, algorithm).await
87                    }
88                    State::Unsealed { .. } => key.to_unsealed_entry(entry_path),
89                }
90            })
91            .await
92    }
93}
94
95impl<T: StorableType> Entry<T> {
96    pub fn cast<U: StorableType>(self) -> Result<Entry<U>, CryptoError> {
97        let builder =
98            <U as HasBuilder>::Builder::try_from(TypeBuilderContainer(self.builder))?.into();
99        Ok(Entry::new(self.path, builder, self.value))
100    }
101
102    pub fn new(path: EntryPath, builder: TypeBuilder, value: State) -> Self {
103        Entry {
104            path,
105            builder,
106            value,
107            resolved_value: OnceCell::new(),
108        }
109    }
110
111    #[async_recursion]
112    pub async fn dereference(self) -> Result<Entry<T>, CryptoError> {
113        match self.value {
114            State::Referenced {
115                ref path,
116                ref storer,
117            } => {
118                let entry = storer.get::<T>(path).await?;
119                Ok(entry.dereference().await?)
120            }
121            _ => Ok(self),
122        }
123    }
124
125    #[async_recursion]
126    pub async fn take_resolve(mut self) -> Result<T, CryptoError> {
127        match self.resolved_value.take() {
128            None => match self.value {
129                State::Referenced {
130                    ref path,
131                    ref storer,
132                } => {
133                    let entry = storer.get::<T>(path).await?;
134                    Ok(entry.take_resolve().await?)
135                }
136                State::Sealed {
137                    ref ciphertext,
138                    ref algorithm,
139                } => {
140                    let builder =
141                        <T as HasBuilder>::Builder::try_from(TypeBuilderContainer(self.builder))?;
142                    let plaintext = algorithm.unseal(ciphertext).await?;
143                    builder.build(Some(plaintext.get()?))
144                }
145                State::Unsealed { bytes, .. } => {
146                    let builder =
147                        <T as HasBuilder>::Builder::try_from(TypeBuilderContainer(self.builder))?;
148                    builder.build(Some(bytes.get()?))
149                }
150            },
151            Some(value) => Ok(value),
152        }
153    }
154
155    #[async_recursion]
156    pub async fn take_resolve_all(mut self) -> Result<(T, EntryPath, State), CryptoError> {
157        match self.resolved_value.take() {
158            None => match self.value {
159                State::Referenced {
160                    ref path,
161                    ref storer,
162                } => {
163                    let entry = storer.get::<T>(path).await?;
164                    entry.take_resolve_all().await
165                }
166                State::Sealed {
167                    ref ciphertext,
168                    ref algorithm,
169                } => {
170                    let builder =
171                        <T as HasBuilder>::Builder::try_from(TypeBuilderContainer(self.builder))?;
172                    let plaintext = algorithm.unseal(ciphertext).await?;
173                    Ok((
174                        builder.build(Some(plaintext.get()?))?,
175                        self.path,
176                        self.value,
177                    ))
178                }
179                State::Unsealed { ref bytes, .. } => {
180                    let builder =
181                        <T as HasBuilder>::Builder::try_from(TypeBuilderContainer(self.builder))?;
182                    Ok((builder.build(Some(bytes.get()?))?, self.path, self.value))
183                }
184            },
185            Some(value) => Ok((value, self.path, self.value)),
186        }
187    }
188
189    pub async fn resolve(&self) -> Result<&T, CryptoError> {
190        match self.resolved_value.get() {
191            None => match self.value {
192                State::Referenced {
193                    ref path,
194                    ref storer,
195                } => {
196                    let entry = storer.get::<T>(path).await?;
197                    let value = entry.take_resolve().await?;
198                    Ok(self.resolved_value.get_or_init(|| value))
199                }
200                State::Sealed {
201                    ref ciphertext,
202                    ref algorithm,
203                } => {
204                    let builder =
205                        <T as HasBuilder>::Builder::try_from(TypeBuilderContainer(self.builder))?;
206                    let plaintext = algorithm.unseal(ciphertext).await?;
207                    self.resolved_value
208                        .get_or_try_init(|| builder.build(Some(plaintext.get()?)))
209                }
210                State::Unsealed { ref bytes, .. } => {
211                    let builder =
212                        <T as HasBuilder>::Builder::try_from(TypeBuilderContainer(self.builder))?;
213                    self.resolved_value
214                        .get_or_try_init(|| builder.build(Some(bytes.get()?)))
215                }
216            },
217            Some(value) => Ok(value),
218        }
219    }
220}
221
222#[derive(Serialize, Deserialize, Debug)]
223#[serde(tag = "t", content = "c")]
224pub enum State {
225    Referenced {
226        path: EntryPath,
227        storer: TypeStorer,
228    },
229    Sealed {
230        ciphertext: ByteSource,
231        algorithm: ByteAlgorithm,
232    },
233    Unsealed {
234        bytes: ByteSource,
235    },
236}
237
238pub trait HasBuilder {
239    type Builder: Builder<Output = Self>;
240
241    fn builder(&self) -> Self::Builder;
242}
243
244pub trait Builder:
245    TryFrom<TypeBuilderContainer, Error = CryptoError> + Into<TypeBuilder> + Send
246{
247    type Output;
248
249    fn build(&self, bytes: Option<&[u8]>) -> Result<Self::Output, CryptoError>;
250}
251
252#[async_trait]
253pub trait ToEntry: StorableType + Sized {
254    fn to_ref_entry<S: Storer + Into<TypeStorer>>(
255        self,
256        path: EntryPath,
257        storer: S,
258    ) -> Result<Entry<Self>, CryptoError> {
259        Ok(Entry::new(
260            path.clone(),
261            self.builder().into(),
262            State::Referenced {
263                storer: storer.into(),
264                path,
265            },
266        ))
267    }
268
269    async fn to_sealed_entry(
270        self,
271        path: EntryPath,
272        algorithm: ByteAlgorithm,
273    ) -> Result<Entry<Self>, CryptoError> {
274        let byte_source = self.byte_source();
275        let ciphertext = algorithm.seal(&byte_source).await?;
276        Ok(Entry::new(
277            path,
278            self.builder().into(),
279            State::Sealed {
280                ciphertext,
281                algorithm,
282            },
283        ))
284    }
285
286    fn to_unsealed_entry(self, path: EntryPath) -> Result<Entry<Self>, CryptoError> {
287        Ok(Entry::new(
288            path,
289            self.builder().into(),
290            State::Unsealed {
291                bytes: self.byte_source(),
292            },
293        ))
294    }
295}
296
297impl<T: StorableType> ToEntry for T {}
298
299/// Need this to provide a level an indirection for TryFrom
300#[derive(Serialize, Deserialize, Copy, Clone)]
301pub struct TypeBuilderContainer(pub TypeBuilder);
302
303#[derive(Debug)]
304pub enum Type {
305    Key(Key),
306    Data(Data),
307}
308
309impl StorableType for Type {}
310
311impl HasIndex for Type {
312    type Index = Document;
313
314    fn get_index() -> Option<Self::Index> {
315        None
316    }
317}
318
319impl HasBuilder for Type {
320    type Builder = TypeBuilder;
321
322    fn builder(&self) -> Self::Builder {
323        match self {
324            Self::Key(kb) => TypeBuilder::Key(kb.builder()),
325            Self::Data(db) => TypeBuilder::Data(db.builder()),
326        }
327    }
328}
329
330impl HasByteSource for Type {
331    fn byte_source(&self) -> ByteSource {
332        match self {
333            Self::Key(kb) => kb.byte_source(),
334            Self::Data(db) => db.byte_source(),
335        }
336    }
337}
338
339#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
340#[serde(tag = "t", content = "c")]
341pub enum TypeBuilder {
342    Data(DataBuilder),
343    Key(KeyBuilder),
344}
345
346impl TryFrom<TypeBuilderContainer> for TypeBuilder {
347    type Error = CryptoError;
348
349    fn try_from(builder: TypeBuilderContainer) -> Result<Self, Self::Error> {
350        Ok(builder.0)
351    }
352}
353
354impl Builder for TypeBuilder {
355    type Output = Type;
356
357    fn build(&self, bytes: Option<&[u8]>) -> Result<Self::Output, CryptoError> {
358        match self {
359            Self::Key(k) => Ok(Type::Key(k.build(bytes)?)),
360            Self::Data(d) => Ok(Type::Data(d.build(bytes)?)),
361        }
362    }
363}
364
365#[cfg(test)]
366mod tests {
367    use super::{Type, TypeBuilder, TypeBuilderContainer};
368    use crate::{
369        BoolDataBuilder, Builder, Data, DataBuilder, HasBuilder, HasIndex, StringDataBuilder,
370    };
371    use std::convert::TryInto;
372
373    #[test]
374    fn test_type_to_index() {
375        assert_eq!(Type::get_index(), None);
376    }
377
378    #[test]
379    fn test_type_to_builder() {
380        let t = Type::Data(Data::String("hello, world!".to_owned()));
381        let tb = t.builder();
382        match tb {
383            TypeBuilder::Data(DataBuilder::String(_)) => (),
384            _ => panic!("Outputted builder should have been a StringDataBuilder"),
385        }
386    }
387
388    #[test]
389    fn test_typebuilder_build_valid() {
390        let tb = TypeBuilder::Data(DataBuilder::String(StringDataBuilder {}));
391        let t = tb.build(Some(b"hello, world!")).unwrap();
392        match t {
393            Type::Data(Data::String(s)) => assert_eq!(s, "hello, world!".to_owned()),
394            _ => panic!("Extracted type should have been a data string-type"),
395        }
396    }
397
398    #[test]
399    #[should_panic]
400    fn test_typebuilder_build_invalid() {
401        let tb = TypeBuilder::Data(DataBuilder::Bool(BoolDataBuilder {}));
402        tb.build(Some(b"not a bool")).unwrap();
403    }
404
405    #[test]
406    fn test_typebuilder_from_typebuildercontainer_valid() {
407        let tbc = TypeBuilderContainer(TypeBuilder::Data(DataBuilder::Bool(BoolDataBuilder {})));
408        let tb: TypeBuilder = tbc.try_into().unwrap();
409        let t = tb.build(Some(b"true")).unwrap();
410        match t {
411            Type::Data(Data::Bool(b)) => assert_eq!(b, true),
412            _ => panic!("Extracted data should have been a bool-type"),
413        }
414    }
415}