ng_repo/
block.rs

1// Copyright (c) 2022-2025 Niko Bonnieure, Par le Peuple, NextGraph.org developers
2// All rights reserved.
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
5// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10//! Immutable Block, used to store and exchange File and Commit
11
12use chacha20::cipher::{KeyIvInit, StreamCipher};
13use chacha20::ChaCha20;
14
15use crate::errors::*;
16use crate::log::*;
17use crate::types::*;
18
19impl BlockV0 {
20    pub fn new(
21        children: Vec<BlockId>,
22        mut header_ref: Option<CommitHeaderRef>,
23        content: Vec<u8>,
24        key: Option<SymKey>,
25    ) -> BlockV0 {
26        let (commit_header, commit_header_key) = header_ref
27            .take()
28            .map_or((CommitHeaderObject::None, None), |obj_ref| {
29                (obj_ref.obj, Some(obj_ref.key))
30            });
31        let bc = BlockContentV0 {
32            children,
33            commit_header: commit_header,
34            encrypted_content: content,
35        };
36        let mut b = BlockV0 {
37            id: None,
38            key,
39            content: BlockContent::V0(bc),
40            commit_header_key,
41        };
42        b.id = Some(b.compute_id());
43        b
44    }
45
46    pub fn dummy() -> BlockV0 {
47        BlockV0::new(vec![], None, vec![], None)
48    }
49
50    pub fn new_random_access(
51        children: Vec<BlockId>,
52        content: Vec<u8>,
53        key: Option<SymKey>,
54    ) -> BlockV0 {
55        let bc = BlockContentV0 {
56            children,
57            commit_header: CommitHeaderObject::RandomAccess,
58            encrypted_content: content,
59        };
60        let mut b = BlockV0 {
61            id: None,
62            key,
63            content: BlockContent::V0(bc),
64            commit_header_key: None,
65        };
66        b.id = Some(b.compute_id());
67        b
68    }
69
70    /// Compute the ID
71    pub fn compute_id(&self) -> BlockId {
72        let ser = serde_bare::to_vec(&self.content).unwrap();
73        let hash = blake3::hash(ser.as_slice());
74        Digest::Blake3Digest32(hash.as_bytes().clone())
75    }
76
77    pub fn children(&self) -> &Vec<BlockId> {
78        self.content.children()
79    }
80}
81
82impl From<&Digest> for String {
83    fn from(id: &Digest) -> Self {
84        let mut ser = serde_bare::to_vec(id).unwrap();
85        ser.reverse();
86        base64_url::encode(&ser)
87    }
88}
89
90impl BlockContent {
91    /// Get the encrypted content
92    pub fn encrypted_content(&self) -> &Vec<u8> {
93        match self {
94            BlockContent::V0(bc) => &bc.encrypted_content,
95        }
96    }
97
98    // /// Get the header id
99    // pub fn header_id(&self) -> &Option<ObjectId> {
100    //     match self {
101    //         BlockContent::V0(bc) => &bc.commit_header_id,
102    //     }
103    // }
104
105    /// Get the children
106    pub fn children(&self) -> &Vec<BlockId> {
107        match self {
108            BlockContent::V0(b) => &b.children,
109        }
110    }
111}
112
113impl Block {
114    pub fn new(
115        children: Vec<BlockId>,
116        header_ref: Option<CommitHeaderRef>,
117        content: Vec<u8>,
118        key: Option<SymKey>,
119    ) -> Block {
120        Block::V0(BlockV0::new(children, header_ref, content, key))
121    }
122
123    pub fn dummy() -> Block {
124        Block::V0(BlockV0::dummy())
125    }
126
127    pub fn new_random_access(
128        children: Vec<BlockId>,
129        content: Vec<u8>,
130        key: Option<SymKey>,
131    ) -> Block {
132        Block::V0(BlockV0::new_random_access(children, content, key))
133    }
134
135    pub fn new_with_encrypted_content(content: Vec<u8>, key: Option<SymKey>) -> Block {
136        Block::V0(BlockV0::new(vec![], None, content, key))
137    }
138
139    pub fn size(&self) -> usize {
140        serde_bare::to_vec(&self).unwrap().len()
141    }
142
143    /// Compute the ID
144    pub fn compute_id(&self) -> BlockId {
145        match self {
146            Block::V0(v0) => v0.compute_id(),
147        }
148    }
149
150    /// Get the already computed ID or computes it, saves it, and returns it
151    pub fn get_and_save_id(&mut self) -> BlockId {
152        match &self {
153            Block::V0(b) => match b.id {
154                Some(id) => id,
155                None => {
156                    let id = self.compute_id();
157                    let Block::V0(c) = self;
158                    c.id = Some(id);
159                    id
160                }
161            },
162        }
163    }
164
165    /// Get the already computed ID or computes it
166    pub fn id(&self) -> BlockId {
167        match self {
168            Block::V0(b) => match b.id {
169                Some(id) => id,
170                None => self.compute_id(),
171            },
172        }
173    }
174
175    /// Get the content
176    pub fn content(&self) -> &BlockContent {
177        match self {
178            Block::V0(b) => &b.content,
179        }
180    }
181
182    /// Get the encrypted content
183    pub fn encrypted_content(&self) -> &Vec<u8> {
184        match self {
185            Block::V0(b) => &b.content.encrypted_content(),
186        }
187    }
188
189    /// Get the children
190    pub fn children(&self) -> &Vec<BlockId> {
191        match self {
192            Block::V0(b) => &b.content.children(),
193        }
194    }
195
196    pub fn destroy_header(&mut self) {
197        match self {
198            Block::V0(b) => b.commit_header_key = None,
199        }
200    }
201
202    /// Get the header reference
203    pub fn header_ref(&self) -> Option<CommitHeaderRef> {
204        match self {
205            Block::V0(b) => match b.commit_header_key.as_ref() {
206                Some(key) => match b.content.commit_header_obj() {
207                    CommitHeaderObject::None => None,
208                    CommitHeaderObject::RandomAccess => None,
209                    _ => Some(CommitHeaderRef {
210                        obj: b.content.commit_header_obj().clone(),
211                        key: key.clone(),
212                    }),
213                },
214
215                None => None,
216            },
217        }
218    }
219
220    /// Get the key
221    pub fn key(&self) -> Option<SymKey> {
222        match self {
223            Block::V0(b) => b.key.clone(),
224        }
225    }
226
227    /// Set the key
228    pub fn set_key(&mut self, key: Option<SymKey>) {
229        match self {
230            Block::V0(b) => b.key = key,
231        }
232    }
233
234    pub fn read(
235        &self,
236        key: &SymKey,
237    ) -> Result<(Vec<(BlockId, BlockKey)>, Vec<u8>), ObjectParseError> {
238        match self {
239            Block::V0(b) => {
240                // decrypt content in place (this is why we have to clone first)
241                let mut content_dec = b.content.encrypted_content().clone();
242                match key {
243                    SymKey::ChaCha20Key(key) => {
244                        let nonce = [0u8; 12];
245                        let mut cipher = ChaCha20::new(key.into(), &nonce.into());
246                        let mut content_dec_slice = &mut content_dec.as_mut_slice();
247                        cipher.apply_keystream(&mut content_dec_slice);
248                    }
249                }
250
251                // deserialize content
252                let content: ChunkContentV0;
253                match serde_bare::from_slice(content_dec.as_slice()) {
254                    Ok(c) => content = c,
255                    Err(_e) => {
256                        //log_debug!("Block deserialize error: {}", _e);
257                        return Err(ObjectParseError::BlockDeserializeError);
258                    }
259                }
260                // parse content
261                match content {
262                    ChunkContentV0::InternalNode(keys) => {
263                        let b_children = b.children();
264                        if keys.len() != b_children.len() {
265                            log_debug!(
266                                "Invalid keys length: got {}, expected {}",
267                                keys.len(),
268                                b_children.len()
269                            );
270                            log_debug!("!!! children: {:?}", b_children);
271                            log_debug!("!!! keys: {:?}", keys);
272                            return Err(ObjectParseError::InvalidKeys);
273                        }
274                        let mut children = Vec::with_capacity(b_children.len());
275                        for (id, key) in b_children.iter().zip(keys.iter()) {
276                            children.push((id.clone(), key.clone()));
277                        }
278                        Ok((children, vec![]))
279                    }
280                    ChunkContentV0::DataChunk(chunk) => Ok((vec![], chunk)),
281                }
282            }
283        }
284    }
285}