self_encryption/
utils.rs

1use crate::aes::{Iv, Key, Pad, IV_SIZE, KEY_SIZE, PAD_SIZE};
2use bytes::Bytes;
3use xor_name::XorName;
4
5/// Helper function to XOR a data with a pad (pad will be rotated to fill the length)
6pub(crate) fn xor(data: &Bytes, &Pad(pad): &Pad) -> Bytes {
7    let vec: Vec<_> = data
8        .iter()
9        .zip(pad.iter().cycle())
10        .map(|(&a, &b)| a ^ b)
11        .collect();
12    Bytes::from(vec)
13}
14
15pub fn extract_hashes(data_map: &crate::DataMap) -> Vec<XorName> {
16    data_map.infos().iter().map(|c| c.src_hash).collect()
17}
18
19pub(crate) fn get_pad_key_and_iv(chunk_index: usize, chunk_hashes: &[XorName]) -> (Pad, Key, Iv) {
20    let (n_1, n_2) = get_n_1_n_2(chunk_index, chunk_hashes.len());
21
22    let src_hash = &chunk_hashes[chunk_index];
23    let n_1_src_hash = &chunk_hashes[n_1];
24    let n_2_src_hash = &chunk_hashes[n_2];
25
26    get_pki(src_hash, n_1_src_hash, n_2_src_hash)
27}
28
29pub(crate) fn get_n_1_n_2(chunk_index: usize, total_num_chunks: usize) -> (usize, usize) {
30    match chunk_index {
31        0 => (total_num_chunks - 1, total_num_chunks - 2),
32        1 => (0, total_num_chunks - 1),
33        n => (n - 1, n - 2),
34    }
35}
36
37pub(crate) fn get_pki(
38    src_hash: &XorName,
39    n_1_src_hash: &XorName,
40    n_2_src_hash: &XorName,
41) -> (Pad, Key, Iv) {
42    let mut pad = [0u8; PAD_SIZE];
43    let mut key = [0u8; KEY_SIZE];
44    let mut iv = [0u8; IV_SIZE];
45
46    for (pad_iv_el, element) in pad
47        .iter_mut()
48        .zip(src_hash.iter().chain(n_2_src_hash.iter()))
49    {
50        *pad_iv_el = *element;
51    }
52
53    for (key_el, element) in key.iter_mut().chain(iv.iter_mut()).zip(n_1_src_hash.iter()) {
54        *key_el = *element;
55    }
56
57    (Pad(pad), Key(key), Iv(iv))
58}
59
60// Returns the number of chunks according to file size.
61pub(crate) fn get_num_chunks(file_size: usize) -> usize {
62    get_num_chunks_with_variable_max(file_size, crate::MAX_CHUNK_SIZE)
63}
64
65// Returns the number of chunks according to file size.
66pub(crate) fn get_num_chunks_with_variable_max(file_size: usize, max_chunk_size: usize) -> usize {
67    if file_size < (3 * crate::MIN_CHUNK_SIZE) {
68        return 0;
69    }
70    if file_size < (3 * max_chunk_size) {
71        return 3;
72    }
73    if file_size.is_multiple_of(max_chunk_size) {
74        file_size / max_chunk_size
75    } else {
76        (file_size / max_chunk_size) + 1
77    }
78}
79
80// Returns the size of a chunk according to file size.
81pub(crate) fn get_chunk_size(file_size: usize, chunk_index: usize) -> usize {
82    get_chunk_size_with_variable_max(file_size, chunk_index, crate::MAX_CHUNK_SIZE)
83}
84
85// Returns the size of a chunk according to file size.
86pub(crate) fn get_chunk_size_with_variable_max(
87    file_size: usize,
88    chunk_index: usize,
89    max_chunk_size: usize,
90) -> usize {
91    if file_size < 3 * crate::MIN_CHUNK_SIZE {
92        return 0;
93    }
94    if file_size < 3 * max_chunk_size {
95        if chunk_index < 2 {
96            return file_size / 3;
97        } else {
98            // When the file_size % 3 > 0, the third (last) chunk includes the remainder
99            return file_size - (2 * (file_size / 3));
100        }
101    }
102    let total_chunks = get_num_chunks_with_variable_max(file_size, max_chunk_size);
103    if chunk_index < total_chunks - 2 {
104        return max_chunk_size;
105    }
106    let remainder = file_size % max_chunk_size;
107    let penultimate = (total_chunks - 2) == chunk_index;
108    if remainder == 0 {
109        return max_chunk_size;
110    }
111    if remainder < crate::MIN_CHUNK_SIZE {
112        if penultimate {
113            max_chunk_size - crate::MIN_CHUNK_SIZE
114        } else {
115            crate::MIN_CHUNK_SIZE + remainder
116        }
117    } else if penultimate {
118        max_chunk_size
119    } else {
120        remainder
121    }
122}
123
124// Returns the [start, end) half-open byte range of a chunk.
125pub(crate) fn get_start_end_positions(file_size: usize, chunk_index: usize) -> (usize, usize) {
126    if get_num_chunks(file_size) == 0 {
127        return (0, 0);
128    }
129    let start = get_start_position(file_size, chunk_index);
130    (start, start + get_chunk_size(file_size, chunk_index))
131}
132
133pub(crate) fn get_start_position(file_size: usize, chunk_index: usize) -> usize {
134    let total_chunks = get_num_chunks(file_size);
135    if total_chunks == 0 {
136        return 0;
137    }
138    let last = (total_chunks - 1) == chunk_index;
139    let first_chunk_size = get_chunk_size(file_size, 0);
140    if last {
141        first_chunk_size * (chunk_index - 1) + get_chunk_size(file_size, chunk_index - 1)
142    } else {
143        first_chunk_size * chunk_index
144    }
145}
146
147#[allow(dead_code)]
148pub(crate) fn get_chunk_index(file_size: usize, position: usize) -> usize {
149    let num_chunks = get_num_chunks(file_size);
150    if num_chunks == 0 {
151        return 0; // FIX THIS SHOULD NOT BE ALLOWED
152    }
153
154    let chunk_size = get_chunk_size(file_size, 0);
155    let remainder = file_size % chunk_size;
156
157    if remainder == 0
158        || remainder >= crate::MIN_CHUNK_SIZE
159        || position < file_size - remainder - crate::MIN_CHUNK_SIZE
160    {
161        usize::min(position / chunk_size, num_chunks - 1)
162    } else {
163        num_chunks - 1
164    }
165}