1use core::convert::Infallible;
18use std::collections::HashSet;
19
20use vitaminc_protected::{Controlled, Protected};
21
22use crate::leaf::{self, Item};
23use crate::plaintext::{Error, MapSink, SeqSink, Sink, SourceError};
24use crate::seal::{open_all, open_all_async, AsyncCipher, Cipher, Node, OpenError, Opened};
25use crate::{CipherText, IntoAad};
26
27#[derive(Debug, thiserror::Error)]
35pub enum DecryptError<E, C = Infallible> {
36 #[error("cipher failed: {0}")]
40 Cipher(C),
41 #[error("an authenticated empty marker carried a payload")]
44 Auth,
45 #[error("malformed ciphertext")]
48 Malformed,
49 #[error("sink refused the value: {0}")]
52 Sink(E),
53}
54
55impl<E, C> From<E> for DecryptError<E, C> {
56 fn from(e: E) -> Self {
57 Self::Sink(e)
58 }
59}
60
61impl<E> DecryptError<E> {
62 pub fn with_cipher<C>(self) -> DecryptError<E, C> {
65 match self {
66 Self::Cipher(never) => match never {},
67 Self::Auth => DecryptError::Auth,
68 Self::Malformed => DecryptError::Malformed,
69 Self::Sink(e) => DecryptError::Sink(e),
70 }
71 }
72}
73
74fn opening<E, C>(e: OpenError<C>) -> DecryptError<E, C> {
75 match e {
76 OpenError::Cipher(c) => DecryptError::Cipher(c),
77 OpenError::Malformed | OpenError::Count(_) => DecryptError::Malformed,
78 }
79}
80
81impl<E: Error, C> Error for DecryptError<E, C> {
83 fn source(error: SourceError) -> Self {
84 Self::Sink(E::source(error))
85 }
86}
87
88pub fn decrypt_into<'a, C, S, A>(
90 cipher: &C,
91 node: Node<C::Leaf>,
92 aad: A,
93 sink: S,
94) -> Result<S::Ok, DecryptError<S::Error, C::Error>>
95where
96 C: Cipher,
97 S: Sink,
98 A: IntoAad<'a>,
99{
100 walk(
101 open_all(cipher, node, &aad.into_aad()).map_err(opening)?,
102 sink,
103 )
104 .map_err(DecryptError::with_cipher)
105}
106
107pub async fn decrypt_into_async<'a, C, S, A>(
114 cipher: &C,
115 node: Node<C::Leaf>,
116 aad: A,
117 sink: S,
118) -> Result<S::Ok, DecryptError<S::Error, C::Error>>
119where
120 C: AsyncCipher,
121 S: Sink,
122 A: IntoAad<'a>,
123{
124 let opened = open_all_async(cipher, node, &aad.into_aad())
125 .await
126 .map_err(opening)?;
127 walk(opened, sink).map_err(DecryptError::with_cipher)
128}
129
130fn sealed_any<'n, L: 'n>(mut nodes: impl Iterator<Item = &'n Node<L>>) -> bool {
131 nodes.any(|n| !matches!(n, CipherText::Passthrough(_)))
132}
133
134pub fn walk<S: Sink>(node: Node<Opened>, sink: S) -> Result<S::Ok, DecryptError<S::Error>> {
136 let empty_marker = |plaintext: Opened| match plaintext.risky_ref().is_empty() {
137 true => Ok(()),
138 false => Err(DecryptError::Auth),
139 };
140 match node {
141 CipherText::Single(plaintext) => {
142 let item =
143 leaf::read_leaf_item(plaintext.risky_ref()).map_err(|_| DecryptError::Malformed)?;
144 Ok(match item {
145 Item::Scalar(ty, v) => sink.scalar(ty, v)?,
146 Item::TypedNull(ty) => sink.typed_null(ty)?,
147 })
148 }
150 CipherText::Sequence(items) => walk_seq(items, sink),
151 CipherText::EmptySequence(plaintext) => {
152 empty_marker(plaintext)?;
153 Ok(sink.seq(Some(0))?.end()?)
154 }
155 CipherText::Map(entries) => walk_map(entries, sink),
156 CipherText::EmptyMap(plaintext) => {
157 empty_marker(plaintext)?;
158 Ok(sink.map(Some(0))?.end()?)
159 }
160 CipherText::Passthrough(frame) => {
161 let frame = frame.parse().map_err(|_| DecryptError::Malformed)?;
162 Ok(sink.passthrough(frame)?)
163 }
164 }
165}
166
167fn walk_seq<S: Sink>(items: Vec<Node<Opened>>, sink: S) -> Result<S::Ok, DecryptError<S::Error>> {
168 if !sealed_any(items.iter()) {
169 return Err(DecryptError::Malformed);
170 }
171 let mut seq = sink.seq(Some(items.len()))?;
172 for item in items {
173 walk(item, seq.element()?)?;
174 }
175 Ok(seq.end()?)
176}
177
178fn walk_map<S: Sink>(
179 entries: Vec<(String, Node<Opened>)>,
180 sink: S,
181) -> Result<S::Ok, DecryptError<S::Error>> {
182 let mut seen = HashSet::with_capacity(entries.len());
183 if !entries.iter().all(|(k, _)| seen.insert(k.as_str()))
184 || !sealed_any(entries.iter().map(|(_, v)| v))
185 {
186 return Err(DecryptError::Malformed);
187 }
188 let mut map = sink.map(Some(entries.len()))?;
189 for (key, value) in entries {
190 walk(value, map.entry(&key)?)?;
191 }
192 Ok(map.end()?)
193}
194
195pub fn decrypt_to_frame<'a, C, A>(
200 cipher: &C,
201 node: Node<C::Leaf>,
202 aad: A,
203) -> Result<Protected<Vec<u8>>, DecryptError<crate::frame::FrameWriteError, C::Error>>
204where
205 C: Cipher,
206 A: IntoAad<'a>,
207{
208 let mut out = Protected::new(Vec::new());
209 decrypt_into(
210 cipher,
211 node,
212 aad,
213 crate::frame::FrameWriter::new(out.risky_inner_mut()),
214 )?;
215 Ok(out)
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use crate::plaintext::record::{record, Event, Owned, Record};
222 use crate::seal::leaves_with_aad;
223 use crate::seal::mock::{Leaf, Mock};
224 use crate::{encrypt, Encrypt, Int};
225 use std::collections::BTreeMap;
226 use vitaminc_protected::Protected;
227
228 fn sample() -> BTreeMap<&'static str, Vec<Option<u32>>> {
229 let mut m = BTreeMap::new();
230 m.insert("a", vec![Some(1), None]);
231 m.insert("b", vec![]);
232 m
233 }
234
235 fn round_trip<P: Encrypt + Clone>(p: P) -> Vec<Event> {
236 let sealed = encrypt(p, &Mock, "ctx").expect("encrypts");
237 let mut log = Vec::new();
238 decrypt_into(&Mock, sealed, "ctx", Record(&mut log)).expect("decrypts");
239 log
240 }
241
242 #[test]
243 fn what_goes_in_comes_out() {
244 assert_eq!(round_trip(sample()), record(sample()).expect("records"));
245 assert_eq!(
246 round_trip(34u32),
247 [Event::Scalar(None, Owned::Int(Int::U32(34)))]
248 );
249 assert_eq!(
250 round_trip(Vec::<u32>::new()),
251 [Event::Seq(Some(0)), Event::EndSeq]
252 );
253 assert_eq!(
254 round_trip(BTreeMap::<&str, u32>::new()),
255 [Event::Map(Some(0)), Event::EndMap]
256 );
257 }
258
259 #[test]
260 fn passthrough_comes_back_as_a_walk() {
261 #[derive(Clone)]
262 struct Doc;
263 impl Encrypt for Doc {
264 fn visit<S: Sink>(self, sink: S) -> Result<S::Ok, S::Error> {
265 let mut map = sink.map(Some(2))?;
266 map.entry("id")?.passthrough(vec![7u16])?;
267 "secret".visit(map.entry("name")?)?;
268 map.end()
269 }
270 }
271 assert_eq!(
272 round_trip(Doc),
273 [
274 Event::Map(Some(2)),
275 Event::Entry("id".into()),
276 Event::Passthrough,
277 Event::Seq(Some(1)),
278 Event::Element,
279 Event::Scalar(None, Owned::Int(Int::U16(7))),
280 Event::EndSeq,
281 Event::Entry("name".into()),
282 Event::Scalar(None, Owned::Text("secret".into())),
283 Event::EndMap,
284 ]
285 );
286 }
287
288 #[test]
289 fn a_walk_error_keeps_its_kind_under_any_cipher_error() {
290 let widen = |e: DecryptError<u8>| e.with_cipher::<&str>();
291 assert!(matches!(widen(DecryptError::Auth), DecryptError::Auth));
292 assert!(matches!(
293 widen(DecryptError::Malformed),
294 DecryptError::Malformed
295 ));
296 assert!(matches!(
297 widen(DecryptError::Sink(7)),
298 DecryptError::Sink(7)
299 ));
300 }
301
302 #[test]
303 fn every_marker_that_is_not_empty_is_an_auth_failure() {
304 let tampered = || Protected::new(vec![1]);
305 let nodes: [Node<Opened>; 2] = [
306 CipherText::EmptySequence(tampered()),
307 CipherText::EmptyMap(tampered()),
308 ];
309 for node in nodes {
310 assert!(matches!(
311 walk(node, Record(&mut Vec::new())),
312 Err(DecryptError::Auth)
313 ));
314 }
315 }
316
317 #[test]
318 fn decrypt_errors_say_what_failed() {
319 type E = DecryptError<&'static str, &'static str>;
320 assert_eq!(E::Cipher("down").to_string(), "cipher failed: down");
321 assert_eq!(
322 E::Auth.to_string(),
323 "an authenticated empty marker carried a payload"
324 );
325 assert_eq!(E::Malformed.to_string(), "malformed ciphertext");
326 assert_eq!(
327 E::Sink("expected u32").to_string(),
328 "sink refused the value: expected u32"
329 );
330 fn is_error<E: core::error::Error>(_: &E) {}
331 is_error(&DecryptError::<&str>::Malformed);
332 }
333
334 #[test]
335 fn a_malformed_tree_is_refused() {
336 let leaf = |bytes: &[u8]| CipherText::Single(Protected::new(bytes.to_vec()));
337 assert!(matches!(
339 walk(leaf(&[0xFF]), Record(&mut Vec::new())),
340 Err(DecryptError::Malformed)
341 ));
342 let node: Node<Opened> = CipherText::Map(vec![
344 ("k".into(), leaf(&[0x00])),
345 ("k".into(), leaf(&[0x00])),
346 ]);
347 assert!(matches!(
348 walk(node, Record(&mut Vec::new())),
349 Err(DecryptError::Malformed)
350 ));
351 let node: Node<Opened> =
353 CipherText::Sequence(vec![CipherText::Passthrough(PassthroughFrame(vec![0x00]))]);
354 assert!(matches!(
355 walk(node, Record(&mut Vec::new())),
356 Err(DecryptError::Malformed)
357 ));
358 let node: Node<Opened> = CipherText::Passthrough(PassthroughFrame(vec![0xF0]));
360 assert!(matches!(
361 walk(node, Record(&mut Vec::new())),
362 Err(DecryptError::Malformed)
363 ));
364 }
365
366 #[test]
367 fn a_leaf_moved_to_another_position_fails_to_open() {
368 let sealed = encrypt(vec![1u32], &Mock, "ctx").expect("encrypts");
370 let (leaf, _) = leaves_with_aad(sealed, &"ctx".into_aad()).remove(0);
371 let moved: Node<Leaf> = CipherText::Single(leaf);
372 assert!(matches!(
373 decrypt_into(&Mock, moved, "ctx", Record(&mut Vec::new())),
374 Err(DecryptError::Cipher(_))
375 ));
376 }
377
378 #[test]
379 fn decrypt_to_frame_is_the_frame_of_the_source() {
380 let sealed = encrypt(sample(), &Mock, "ctx").expect("encrypts");
381 let frame = decrypt_to_frame(&Mock, sealed, "ctx").expect("decrypts");
382 assert_eq!(
383 frame.risky_ref(),
384 crate::to_frame(sample()).expect("frames").risky_ref()
385 );
386 }
387
388 use crate::frame::PassthroughFrame;
389}