eris_rs/
encode.rs

1use crate::types::BlockSize;
2use crate::types::BlockStorageError;
3use crate::types::ReadCapability;
4use std::io::Read;
5
6use chacha20::cipher::{KeyIvInit, StreamCipher};
7use chacha20::{ChaCha20, Key as ChaChaKey, Nonce};
8
9use crate::arity;
10use crate::blake2b256_hash;
11use crate::constants::{KEY_SIZE_BYTES, REF_SIZE_BYTES};
12use crate::types::{
13    BlockWithReference, BlockWithReferenceWriteFn, Key, Reference, ReferenceKeyPair,
14};
15
16struct EncryptBlockReturn {
17    encrypted_block: Vec<u8>,
18    reference: Reference,
19    key: Key,
20}
21
22fn encrypt_node(input: &[u8], level: u8, convergence_secret: Option<&[u8]>) -> EncryptBlockReturn {
23    let key_slice = blake2b256_hash(input, convergence_secret);
24    let key = ChaChaKey::from_slice(&key_slice);
25    let nonce_slice = &[level, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
26    let nonce = Nonce::from_slice(nonce_slice);
27
28    let mut encrypted_block = Vec::from(input);
29    let mut cipher = ChaCha20::new(key, nonce);
30    cipher.apply_keystream(&mut encrypted_block);
31    let reference = blake2b256_hash(&encrypted_block, None);
32
33    EncryptBlockReturn {
34        encrypted_block,
35        reference,
36        key: key_slice,
37    }
38}
39
40fn encrypt_internal_node(input: &[u8], level: u8) -> EncryptBlockReturn {
41    encrypt_node(input, level, None)
42}
43
44fn encrypt_leaf_node(input: &[u8], convergence_secret: &[u8]) -> EncryptBlockReturn {
45    encrypt_node(input, 0, Some(convergence_secret))
46}
47
48struct SplitContentReturn {
49    rk_pairs: Vec<ReferenceKeyPair>,
50}
51
52fn pad(input: &mut Vec<u8>, start_index: usize) -> Option<Vec<u8>> {
53    if input.len() - start_index == 0 {
54        let mut vec = Vec::with_capacity(input.len());
55        vec.fill(0);
56        vec[0] = 0x80;
57        Some(vec)
58    } else {
59        input[start_index] = 0x80;
60        for i in input.iter_mut().skip(start_index + 1) {
61            *i = 0x00;
62        }
63        None
64    }
65}
66
67fn split_content(
68    content: &mut dyn std::io::Read,
69    convergence_secret: &[u8],
70    block_size_bytes: usize,
71    block_write_fn: &BlockWithReferenceWriteFn,
72) -> Result<SplitContentReturn, BlockStorageError> {
73    let mut rk_pairs: Vec<ReferenceKeyPair> = Vec::new();
74    // we might produce an additional block due to padding
75    let mut last_buffer: Vec<u8> = Vec::new();
76
77    loop {
78        let mut bytes_read = 0;
79        let mut last_block = false;
80        let mut blocks_to_write: Vec<&Vec<u8>> = Vec::new();
81        let mut buffer: Vec<u8> = vec![0; block_size_bytes];
82
83        while bytes_read < block_size_bytes {
84            let n = match content.read(&mut buffer[bytes_read..]) {
85                Ok(v) => v,
86                Err(e) => return Err(e),
87            };
88            if n == 0 {
89                last_block = true;
90                break;
91            }
92            bytes_read += n;
93        }
94        if last_block {
95            if let Some(last_block) = pad(&mut buffer, bytes_read) {
96                last_buffer.copy_from_slice(&last_block);
97                blocks_to_write.push(&last_buffer);
98            }
99        }
100        blocks_to_write.push(&buffer);
101        for block in blocks_to_write {
102            let EncryptBlockReturn {
103                encrypted_block,
104                reference,
105                key,
106            } = encrypt_leaf_node(block, convergence_secret);
107            {
108                let rk_pair = ReferenceKeyPair { reference, key };
109                match block_write_fn(BlockWithReference {
110                    block: encrypted_block,
111                    reference: rk_pair.reference,
112                }) {
113                    Ok(_) => rk_pairs.push(rk_pair),
114                    Err(e) => return Err(e),
115                }
116            }
117        }
118        if last_block {
119            break;
120        }
121    }
122    Ok(SplitContentReturn { rk_pairs })
123}
124
125struct RKPairPacker {
126    pairs: Vec<ReferenceKeyPair>,
127    arity: usize,
128    cycle: usize,
129}
130
131impl RKPairPacker {
132    fn new(pairs: Vec<ReferenceKeyPair>, arity: usize) -> RKPairPacker {
133        RKPairPacker {
134            pairs,
135            arity,
136            cycle: 0,
137        }
138    }
139}
140
141impl Iterator for RKPairPacker {
142    type Item = Vec<u8>;
143
144    fn next(&mut self) -> Option<Vec<u8>> {
145        let mut result: Vec<u8> = Vec::new();
146        let cycles_needed = match self.pairs.len() % self.arity {
147            0 => self.pairs.len() / self.arity,
148            _ => self.pairs.len() / self.arity + 1,
149        };
150        if self.cycle == cycles_needed {
151            return None;
152        }
153        for count in 0..self.arity {
154            match self.pairs.get(self.cycle * self.arity + count) {
155                Some(pair) => {
156                    let mut refkey = [pair.reference, pair.key].concat();
157                    result.append(&mut refkey)
158                }
159                None => {
160                    let mut empty_block: Vec<u8> = vec![0; REF_SIZE_BYTES + KEY_SIZE_BYTES];
161                    result.append(&mut empty_block)
162                }
163            }
164        }
165        self.cycle += 1;
166        Some(result)
167    }
168}
169
170fn collect_rk_pairs(
171    input_pairs: Vec<ReferenceKeyPair>,
172    level: u8,
173    block_size_bytes: usize,
174    block_write_fn: &BlockWithReferenceWriteFn,
175) -> Result<Vec<ReferenceKeyPair>, BlockStorageError> {
176    let mut output_rk_pairs: Vec<ReferenceKeyPair> = Vec::new();
177    for node in RKPairPacker::new(input_pairs, arity(block_size_bytes)) {
178        let EncryptBlockReturn {
179            encrypted_block,
180            reference,
181            key,
182        } = encrypt_internal_node(&node, level);
183        {
184            let rk_pair = ReferenceKeyPair { reference, key };
185            match block_write_fn(BlockWithReference {
186                block: encrypted_block,
187                reference: rk_pair.reference,
188            }) {
189                Ok(_) => output_rk_pairs.push(rk_pair),
190                Err(e) => return Err(e),
191            }
192        }
193    }
194    Ok(output_rk_pairs)
195}
196
197pub fn encode(
198    content: &mut dyn Read,
199    convergence_secret: &[u8],
200    block_size: BlockSize,
201    block_write_fn: &BlockWithReferenceWriteFn,
202) -> Result<ReadCapability, BlockStorageError> {
203    let mut level = 0;
204
205    let block_size_bytes = match block_size {
206        BlockSize::Size1KiB => 1024,
207        BlockSize::Size32KiB => 32 * 1024,
208    };
209
210    match split_content(
211        content,
212        convergence_secret,
213        block_size_bytes,
214        block_write_fn,
215    ) {
216        Ok(SplitContentReturn { rk_pairs }) => {
217            let mut rk_pairs: Vec<ReferenceKeyPair> = rk_pairs;
218            while rk_pairs.len() > 1 {
219                level += 1;
220                match collect_rk_pairs(rk_pairs, level, block_size_bytes, block_write_fn) {
221                    Ok(pairs) => rk_pairs = pairs,
222                    Err(e) => return Err(e),
223                }
224            }
225            Ok(ReadCapability {
226                level,
227                root: rk_pairs[0],
228                block_size,
229            })
230        }
231        Err(e) => Err(e),
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238    use crate::constants::REFKEY_SIZE_BYTES;
239    use crate::tests::vectors::read_positive_test_vectors;
240    use crate::types::BlockStorageError;
241    use hex_literal::hex;
242    use std::collections::HashMap;
243    use std::io::Cursor;
244    use std::sync::mpsc;
245    use std::sync::mpsc::{Receiver, Sender};
246    use std::{thread, vec};
247
248    #[test]
249    fn test_encrypt() {
250        let test_data = hex!("5dc2de4e32d5a8855d24fb452a83a035c73bd295b766cd4b9a07060917ebe38b");
251        let convergence_secret =
252            hex!("d8ae7bfb1dd49edbe5fdefbf1eebe911a22e06d66a1091519e61a8650ca7a8a1");
253        let result = encrypt_leaf_node(&test_data, &convergence_secret);
254        assert_ne!(result.encrypted_block, test_data);
255    }
256
257    // Streams a certain amount of bytes
258    struct ChaCha20ZeroReader {
259        read_total: usize,
260        max: usize,
261        cipher: ChaCha20,
262        count: u64,
263    }
264
265    impl ChaCha20ZeroReader {
266        fn new(max: usize, key: &[u8; REFKEY_SIZE_BYTES]) -> ChaCha20ZeroReader {
267            let nonce = Nonce::from_slice(&[0; 12]);
268            let chacha_key = ChaChaKey::from_slice(key);
269            ChaCha20ZeroReader {
270                max,
271                read_total: 0,
272                cipher: ChaCha20::new(chacha_key, nonce),
273                count: 0,
274            }
275        }
276    }
277
278    impl Read for ChaCha20ZeroReader {
279        fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
280            let to_read = (self.max - self.read_total).clamp(0, buf.len());
281            self.cipher.apply_keystream(&mut buf[0..to_read]);
282            self.read_total += to_read;
283            self.count += 1;
284            Ok(to_read)
285        }
286    }
287
288    #[test]
289    fn test_hello_world() {
290        let payload = "Hello world!";
291        let block_size = BlockSize::Size1KiB;
292        let convergence_secret: [u8; 32] = Default::default();
293        let write_fn =
294            move |block_with_reference: BlockWithReference| -> Result<usize, BlockStorageError> {
295                let size = block_with_reference.block.len();
296                Ok(size)
297            };
298
299        let read_capability = encode(
300            &mut payload.as_bytes(),
301            &convergence_secret,
302            block_size,
303            &write_fn,
304        )
305        .unwrap();
306        let encoded_urn = read_capability.to_urn();
307        assert_eq!(encoded_urn, "urn:eris:BIAD77QDJMFAKZYH2DXBUZYAP3MXZ3DJZVFYQ5DFWC6T65WSFCU5S2IT4YZGJ7AC4SYQMP2DM2ANS2ZTCP3DJJIRV733CRAAHOSWIYZM3M");
308    }
309
310    #[test]
311    #[allow(non_snake_case)]
312    fn test_chacha20_reader_100MiB() {
313        let name = "chachacha";
314        let key = blake2b256_hash(name.as_bytes(), None);
315        let size = 100 * 1024 * 1024;
316        let mut reader = ChaCha20ZeroReader::new(size, &key);
317        let mut read_total = 0;
318        let mut first_block = Vec::new();
319        let mut last_block = Vec::new();
320
321        loop {
322            let mut buf = [0; 1024];
323            match reader.read(&mut buf) {
324                Ok(0) => break,
325                Ok(s) => {
326                    if first_block.is_empty() {
327                        first_block.extend_from_slice(&buf[..s])
328                    }
329                    last_block.extend_from_slice(&buf[..s]);
330                    read_total += s;
331                }
332                Err(_) => {
333                    panic!("unexpected error")
334                }
335            }
336        }
337        assert_eq!(read_total, size);
338        let exp_first_ten_b32 = "OQTYRF453ZMTSPSY";
339        let exp_last_ten_b32 = "6SEHZ3OIUBEYPXMT";
340
341        let base32_alphabet = base32::Alphabet::RFC4648 { padding: false };
342        let first_ten_b32 = base32::encode(base32_alphabet, &first_block[0..10]);
343        let last_ten_b32 = base32::encode(base32_alphabet, &last_block[last_block.len() - 10..]);
344        assert_eq!(exp_first_ten_b32, first_ten_b32);
345        assert_eq!(exp_last_ten_b32, last_ten_b32);
346    }
347
348    #[test]
349    fn test_large_payloads() {
350        struct LargePayload {
351            name: String,
352            size: usize,
353            urn: String,
354            block_size: BlockSize,
355        }
356
357        let payloads: Vec<LargePayload> = vec![
358            LargePayload{
359                name: "100MiB (block size 1KiB)".to_owned(),
360                size: 100*1024*1024, // 100MiB
361                urn: "urn:eris:BIC6F5EKY2PMXS2VNOKPD3AJGKTQBD3EXSCSLZIENXAXBM7PCTH2TCMF5OKJWAN36N4DFO6JPFZBR3MS7ECOGDYDERIJJ4N5KAQSZS67YY".to_owned(),
362                block_size: BlockSize::Size1KiB
363            },
364            LargePayload{
365                name: "1GiB (block size 32KiB)".to_owned(),
366                size: 1024*1024*1024, // 1GiB
367                urn: "urn:eris:B4BL4DKSEOPGMYS2CU2OFNYCH4BGQT774GXKGURLFO5FDXAQQPJGJ35AZR3PEK6CVCV74FVTAXHRSWLUUNYYA46ZPOPDOV2M5NVLBETWVI".to_owned(),
368                block_size: BlockSize::Size32KiB
369            },
370            //// Takes a long time to complete
371            //LargePayload{
372            //    name: "256GiB (block size 32KiB)".to_owned(),
373            //    size: 256*1024*1024*1024, // 1GiB
374            //    urn: "urn:eris:B4B5DNZVGU4QDCN7TAYWQZE5IJ6ESAOESEVYB5PPWFWHE252OY4X5XXJMNL4JMMFMO5LNITC7OGCLU4IOSZ7G6SA5F2VTZG2GZ5UCYFD5E".to_owned(),
375            //    block_size: BlockSize::Size32KiB
376            //}
377        ];
378
379        for payload in payloads {
380            let key = blake2b256_hash(payload.name.as_bytes(), None);
381
382            let cha_cha_reader = ChaCha20ZeroReader::new(payload.size, &key);
383            let mut reader = cha_cha_reader;
384
385            let write_fn = move |block_with_reference: BlockWithReference| -> Result<usize, BlockStorageError> {
386                let size = block_with_reference.block.len();
387                Ok(size)
388            };
389            let convergence_secret: [u8; 32] = Default::default();
390            let res = encode(
391                &mut reader,
392                &convergence_secret,
393                payload.block_size,
394                &write_fn,
395            )
396            .unwrap();
397            let encoded_urn = res.to_urn();
398            assert_eq!(payload.urn, encoded_urn);
399        }
400    }
401
402    #[test]
403    fn test_hello_world_cha_cha() {
404        let name = "Hello World!";
405        let key = blake2b256_hash(name.as_bytes(), None);
406        let size = 1024; // 1 kb
407        let urn = "urn:eris:BIAVWP2IZZ2A3WNGPMDSGFE2FJUHFYSEOKKM76DOA2J2XYLOUBSINJEVFKDT2VKH4BVNQPGZVZZTFSK5JFUZ3FTKXVX6TYCZY762UYZYG4";
408        let block_size = BlockSize::Size1KiB;
409
410        let mut reader = ChaCha20ZeroReader::new(size, &key);
411        let write_fn =
412            move |block_with_reference: BlockWithReference| -> Result<usize, BlockStorageError> {
413                let size = block_with_reference.block.len();
414                Ok(size)
415            };
416        let convergence_secret: [u8; 32] = Default::default();
417        let res = encode(&mut reader, &convergence_secret, block_size, &write_fn).unwrap();
418        let encoded_urn = res.to_urn();
419        assert_eq!(urn, encoded_urn);
420    }
421
422    // Tests whether encode() is reading into a zero'ed buffer,
423    // not doing that will XOR old data and thus produce a different
424    // cipher stream
425    #[test]
426    fn test_chacha_reader() {
427        let name = "ChaCha!";
428        let key = blake2b256_hash(name.as_bytes(), None);
429        let size = 10 * 1024; // 10 kb
430        let block_size = BlockSize::Size1KiB;
431
432        let mut reader = ChaCha20ZeroReader::new(size, &key);
433        let write_fn =
434            move |block_with_reference: BlockWithReference| -> Result<usize, BlockStorageError> {
435                let size = block_with_reference.block.len();
436                Ok(size)
437            };
438        let convergence_secret: [u8; 32] = Default::default();
439        let res_direct = encode(&mut reader, &convergence_secret, block_size, &write_fn).unwrap();
440        let urn_direct = res_direct.to_urn();
441
442        let mut chacha2 = ChaCha20ZeroReader::new(size, &key);
443        let mut cha_cha_content = Vec::<u8>::new();
444        loop {
445            let mut buf = [0; 1024];
446            match chacha2.read(&mut buf) {
447                Ok(0) => break,
448                Ok(s) => {
449                    cha_cha_content.extend_from_slice(&buf[..s]);
450                }
451                Err(_) => {
452                    panic!("unexpected error")
453                }
454            }
455        }
456        let mut reader2 = Cursor::new(cha_cha_content);
457        let res_indirect =
458            encode(&mut reader2, &convergence_secret, block_size, &write_fn).unwrap();
459        let urn_indirect = res_indirect.to_urn();
460        assert_eq!(urn_direct, urn_indirect);
461    }
462
463    #[test]
464    fn test_encode() {
465        let test_vectors = read_positive_test_vectors();
466        for vector in test_vectors {
467            println!("Running vector {}", vector.file_name);
468
469            let base32_alphabet = base32::Alphabet::RFC4648 { padding: false };
470            let content = base32::decode(base32_alphabet, &vector.data.content).unwrap();
471
472            let mut cursor = Cursor::new(content);
473            let convergence_secret =
474                base32::decode(base32_alphabet, &vector.data.convergence_secret).unwrap();
475
476            let block_size = match vector.data.block_size {
477                1024 => BlockSize::Size1KiB,
478                32768 => BlockSize::Size32KiB,
479                _ => panic!("unsupported blocksize encountered"),
480            };
481
482            let mut generated_blocks: HashMap<String, Vec<u8>> = HashMap::new();
483
484            let (block_tx, block_rx): (Sender<BlockWithReference>, Receiver<BlockWithReference>) =
485                mpsc::channel();
486            let (return_tx, return_rx): (Sender<ReadCapability>, Receiver<ReadCapability>) =
487                mpsc::channel();
488
489            let write_fn = move |block_with_reference: BlockWithReference| -> Result<usize, BlockStorageError> {
490                let size = block_with_reference.block.len();
491                block_tx.send(block_with_reference).unwrap();
492                Ok(size)
493            };
494
495            let encoder = thread::spawn(move || {
496                let res = encode(&mut cursor, &convergence_secret, block_size, &write_fn);
497                return_tx.send(res.unwrap()).unwrap();
498            });
499
500            while let Ok(block_with_reference) = block_rx.recv() {
501                let b32_ref = base32::encode(base32_alphabet, &block_with_reference.reference);
502                generated_blocks.insert(b32_ref, block_with_reference.block);
503            }
504            let result = return_rx.recv().unwrap();
505            encoder.join().unwrap();
506
507            let b32_root_key = base32::encode(base32_alphabet, &result.root.key);
508            let b32_root_ref = base32::encode(base32_alphabet, &result.root.reference);
509            assert_eq!(vector.data.blocks.len(), generated_blocks.len());
510            assert_eq!(vector.data.read_capability.level, result.level);
511            assert_eq!(vector.data.read_capability.root_reference, b32_root_ref);
512            assert_eq!(vector.data.read_capability.root_key, b32_root_key);
513
514            for block in &generated_blocks {
515                let bs_usize: usize = block_size.into();
516                assert_eq!(block.1.len(), bs_usize);
517            }
518            for (_, block) in generated_blocks.iter().enumerate() {
519                let reference = blake2b256_hash(block.1, None);
520                let b32_reference = base32::encode(base32_alphabet, &reference);
521                assert!(vector.data.blocks.contains_key(&b32_reference));
522            }
523        }
524    }
525}