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 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 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, 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, urn: "urn:eris:B4BL4DKSEOPGMYS2CU2OFNYCH4BGQT774GXKGURLFO5FDXAQQPJGJ35AZR3PEK6CVCV74FVTAXHRSWLUUNYYA46ZPOPDOV2M5NVLBETWVI".to_owned(),
368 block_size: BlockSize::Size32KiB
369 },
370 ];
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; 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 #[test]
426 fn test_chacha_reader() {
427 let name = "ChaCha!";
428 let key = blake2b256_hash(name.as_bytes(), None);
429 let size = 10 * 1024; 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}