async_eris/
dec.rs

1// SPDX-FileCopyrightText: 2022 Yureka Lilian <yuka@yuka.dev>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later WITH LicenseRef-AppStore
4
5use crate::{BlockKey, BlockReference, BlockStorage, ReadCapability};
6use futures_lite::io::{AsyncWrite, AsyncWriteExt};
7use std::collections::VecDeque;
8use thiserror::Error as ThisError;
9
10#[derive(ThisError, Debug)]
11pub enum Error {
12    #[error("invalid padding")]
13    Padding,
14    #[error("i/o error: {0}")]
15    Io(#[from] std::io::Error),
16    #[error("a block was not found in storage")]
17    BlockNotFound,
18    #[error("non-standard block size")]
19    NonstandardBlockSize,
20    #[error("unexpected block size")]
21    UnexpectedBlockSize,
22    #[error("unexpected key size")]
23    UnexpectedKeySize,
24    /// Mostly used for testing
25    #[error("invalid base32 encoding")]
26    InvalidBase32,
27}
28
29pub type Result<T = ()> = std::result::Result<T, Error>;
30
31fn unpad(input: &mut &[u8]) -> Result {
32    loop {
33        if input.len() == 0 {
34            return Err(Error::Padding);
35        }
36        let next = input[input.len() - 1];
37        *input = &mut &input[..input.len() - 1];
38        match next {
39            0 => (),
40            0x80 => return Ok(()),
41            _ => return Err(Error::Padding),
42        }
43    }
44}
45
46/// Decode a message from its `ReadCapability`, a block storage
47/// medium, and an async write medium.
48pub async fn decode<S: BlockStorage<1024> + BlockStorage<{ 32 * 1024 }>, W: AsyncWrite + Unpin>(
49    target: &mut W,
50    read_capability: &ReadCapability,
51    block_storage: &S,
52) -> Result<()> {
53    match read_capability.block_size {
54        1024 => decode_const::<_, _, 1024>(target, read_capability, block_storage).await,
55        32768 => decode_const::<_, _, 32768>(target, read_capability, block_storage).await,
56        _ => Err(Error::NonstandardBlockSize),
57    }
58}
59
60pub async fn decode_const<S: BlockStorage<BS>, W: AsyncWrite + Unpin, const BS: usize>(
61    target: &mut W,
62    read_capability: &ReadCapability,
63    block_storage: &S,
64) -> Result<()> {
65    if read_capability.block_size != BS {
66        return Err(Error::UnexpectedBlockSize);
67    }
68
69    let mut subtrees = VecDeque::new();
70    subtrees.push_back(read_capability.clone());
71
72    while let Some(tree) = subtrees.pop_front() {
73        let mut block = block_storage
74            .fetch(&tree.root_reference)
75            .await?
76            .ok_or(Error::BlockNotFound)?;
77        block.chacha20(&tree.root_key);
78
79        if tree.level == 0 {
80            let mut block = (*block).as_slice();
81            if subtrees.len() == 0 {
82                // this is the last block, unpad
83                unpad(&mut block)?;
84            }
85            target.write_all(block).await?;
86        } else {
87            for rk_pair_raw in block.chunks_exact(64) {
88                let has_content = rk_pair_raw.iter().any(|x| *x != 0);
89                if !has_content {
90                    break;
91                }
92
93                let rk_pair = (
94                    BlockReference(rk_pair_raw[..32].try_into().unwrap()),
95                    BlockKey(rk_pair_raw[32..].try_into().unwrap()),
96                );
97                subtrees.push_back(ReadCapability::from_rk_pair(
98                    rk_pair,
99                    tree.level - 1,
100                    read_capability.block_size,
101                ));
102            }
103        }
104    }
105
106    Ok(())
107}