async_eris/
lib.rs

1// SPDX-FileCopyrightText: 2022 Yureka Lilian <yuka@yuka.dev>
2// SPDX-FileCopyrightText: 2022 Katharina Fey <kookie@spacekookie.de>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later WITH LicenseRef-AppStore
5
6mod dec;
7mod enc;
8mod serde_util;
9
10pub use dec::{decode, decode_const, Error, Result};
11pub use enc::{encode, encode_const, BlockSize, Encoder};
12
13#[cfg(test)]
14mod tests;
15
16/// The target specification this implementation supports
17///
18/// Blocks encoded with a later specification MAY still correctly
19/// decode, but no guarantees can be made.  If you find that some
20/// content encoded with a compatible library does NOT correctly
21/// decode, please get in touch!
22pub const TARGET_SPECIFICATION: &str = "1.0.0";
23
24use async_trait::async_trait;
25use blake2::{digest::consts::U32, Blake2b, Digest};
26use chacha20::cipher::{KeyIvInit, StreamCipher};
27use chacha20::ChaCha20;
28use derive_more::{DebugCustom, Deref, DerefMut, Display, From};
29use serde::{Deserialize, Serialize};
30use std::{collections::HashMap, sync::RwLock};
31
32fn display_base32(bytes: &[u8]) -> String {
33    base32::encode(base32::Alphabet::RFC4648 { padding: false }, bytes)
34}
35
36// Really only useful for decoding variable length test data
37#[cfg(test)]
38fn vardecode_base32(s: &str) -> Result<Vec<u8>> {
39    let buf = base32::decode(base32::Alphabet::RFC4648 { padding: false }, s)
40        .ok_or(Error::InvalidBase32)?;
41    Ok(buf)
42}
43
44fn decode_base32<const BS: usize>(s: &str) -> Result<[u8; BS]> {
45    match base32::decode(base32::Alphabet::RFC4648 { padding: false }, s)
46        .ok_or(Error::InvalidBase32)
47    {
48        Ok(v) if v.len() == BS => Ok(v.try_into().map_err(|_| Error::InvalidBase32)?),
49        Ok(_) => Err(Error::UnexpectedBlockSize),
50        Err(e) => Err(e),
51    }
52}
53
54/// A 32 byte block reference identifier
55#[derive(
56    Clone,
57    Copy,
58    PartialEq,
59    Eq,
60    Hash,
61    Deref,
62    PartialOrd,
63    Ord,
64    From,
65    Display,
66    DebugCustom,
67    Serialize,
68    Deserialize,
69)]
70#[display(fmt = "{}", "display_base32(&self.0)")]
71#[debug(fmt = "{}", "self")]
72pub struct BlockReference(pub [u8; 32]);
73
74impl BlockReference {
75    pub fn as_slice(&self) -> &[u8] {
76        &self.0
77    }
78    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
79        bytes
80            .try_into()
81            .map(|inner| Self(inner))
82            .map_err(|_| crate::Error::UnexpectedBlockSize)
83    }
84}
85
86impl TryFrom<&String> for BlockReference {
87    type Error = Error;
88    fn try_from(s: &String) -> Result<BlockReference> {
89        let buf = decode_base32(s.as_str())?;
90        Ok(BlockReference(buf))
91    }
92}
93
94/// Represents a 32 byte ChaCha20 key for encryption
95#[derive(
96    Clone,
97    Copy,
98    PartialEq,
99    Eq,
100    Hash,
101    Deref,
102    PartialOrd,
103    Ord,
104    From,
105    Display,
106    DebugCustom,
107    Serialize,
108    Deserialize,
109)]
110#[display(fmt = "{}", "display_base32(&self.0)")]
111#[debug(fmt = "{}", "self")]
112pub struct BlockKey(pub [u8; 32]);
113
114impl BlockKey {
115    pub fn as_slice(&self) -> &[u8] {
116        &self.0
117    }
118
119    pub fn from_bytes(input: &[u8]) -> Result<Self> {
120        input
121            .try_into()
122            .map(|inner| Self(inner))
123            .map_err(|_| crate::Error::UnexpectedKeySize)
124    }
125}
126
127impl TryFrom<&String> for BlockKey {
128    type Error = Error;
129    fn try_from(s: &String) -> Result<BlockKey> {
130        let buf = decode_base32(s.as_str())?;
131        Ok(BlockKey(buf))
132    }
133}
134
135#[doc(hidden)] // Only exposed for tests
136pub type RKPair = (BlockReference, BlockKey);
137
138/// An encoded data block of size `BLOCKSIZE`
139#[derive(Clone, Deref, DerefMut, From, Display, DebugCustom, Serialize, Deserialize)]
140#[display(fmt = "{}", "display_base32(&self.0)")]
141#[debug(fmt = "{}", "self")]
142pub struct Block<const BS: usize>(#[serde(with = "serde_util")] [u8; BS]);
143
144impl<const BS: usize> Block<BS> {
145    pub fn as_slice(&self) -> &[u8; BS] {
146        &self.0
147    }
148
149    /// Take a `Vec<u8>` and move its contents into an array
150    pub fn copy_from_vec(vec: Vec<u8>) -> Self {
151        let mut buf = [0; BS];
152        buf.copy_from_slice(vec.as_slice());
153        Self(buf)
154    }
155
156    pub fn to_vec(self) -> Vec<u8> {
157        self.0.to_vec()
158    }
159}
160
161impl<const BS: usize> TryFrom<&String> for Block<BS> {
162    type Error = Error;
163    fn try_from(s: &String) -> Result<Block<BS>> {
164        let buf = decode_base32(s.as_str())?;
165        Ok(Block(buf))
166    }
167}
168
169#[async_trait]
170pub trait BlockStorage<const BS: usize> {
171    async fn store(&self, block: &Block<BS>) -> std::io::Result<()>;
172    async fn fetch(&self, reference: &BlockReference) -> std::io::Result<Option<Block<BS>>>;
173}
174
175pub type MemoryStorage = RwLock<HashMap<BlockReference, Vec<u8>>>;
176
177#[async_trait]
178impl<const BS: usize> BlockStorage<BS> for MemoryStorage {
179    async fn store(&self, block: &Block<BS>) -> std::io::Result<()> {
180        self.write()
181            .unwrap()
182            .insert(block.reference(), block.0.to_vec());
183        Ok(())
184    }
185
186    async fn fetch(&self, reference: &BlockReference) -> std::io::Result<Option<Block<BS>>> {
187        self.read()
188            .unwrap()
189            .get(reference)
190            .map(|x| -> std::io::Result<_> {
191                let arr: [u8; BS] = x.clone().try_into().map_err(|_| {
192                    std::io::Error::new(std::io::ErrorKind::Other, "Block has unexpected size")
193                })?;
194                Ok(arr.into())
195            })
196            .transpose()
197    }
198}
199
200const fn num_bits<T>() -> usize {
201    std::mem::size_of::<T>() * 8
202}
203
204// replace with usize::log2 once its stable
205fn log_2(x: usize) -> u32 {
206    assert!(x > 0);
207    num_bits::<usize>() as u32 - x.leading_zeros() - 1
208}
209
210#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
211pub struct ReadCapability {
212    pub root_reference: BlockReference,
213    pub root_key: BlockKey,
214    pub level: u8,
215    pub block_size: usize,
216}
217
218impl ReadCapability {
219    pub fn from_rk_pair(rk_pair: RKPair, level: u8, block_size: usize) -> ReadCapability {
220        ReadCapability {
221            root_reference: rk_pair.0,
222            root_key: rk_pair.1,
223            level,
224            block_size,
225        }
226    }
227
228    pub fn binary(&self) -> Vec<u8> {
229        let mut out = vec![];
230        out.push(log_2(self.block_size) as u8);
231        out.push(self.level);
232        out.extend_from_slice(&*self.root_reference);
233        out.extend_from_slice(&*self.root_key);
234        out
235    }
236
237    pub fn from_binary(buf: &[u8]) -> Option<ReadCapability> {
238        if buf.len() != 1 + 1 + 32 + 32 {
239            return None;
240        }
241        let block_size = 2usize.pow(buf[0].into());
242        let level = buf[1];
243        let root_reference_bytes: [u8; 32] = buf[2..34].try_into().unwrap();
244        let root_key_bytes: [u8; 32] = buf[34..66].try_into().unwrap();
245        let root_reference = BlockReference::from(root_reference_bytes);
246        let root_key = BlockKey::from(root_key_bytes);
247        Some(Self {
248            block_size,
249            level,
250            root_reference,
251            root_key,
252        })
253    }
254
255    pub fn urn(&self) -> String {
256        format!("urn:erisx2:{}", &display_base32(&self.binary()))
257    }
258}
259
260impl<const BS: usize> Block<BS> {
261    pub fn reference(&self) -> BlockReference {
262        let mut hasher = Blake2b::<U32>::new();
263        Digest::update(&mut hasher, &**self);
264        BlockReference(hasher.finalize().into())
265    }
266
267    pub fn chacha20(&mut self, key: &BlockKey) {
268        // audit(security): is this correct?  This doesn't seem correct
269        let nonce = [0; 12];
270        let mut cipher = ChaCha20::new(&(**key).into(), &nonce.into());
271        cipher.apply_keystream(&mut **self);
272    }
273}