sphinx_packet/header/
filler.rs

1// Copyright 2020 Nym Technologies SA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::constants::{HEADER_INTEGRITY_MAC_SIZE, MAX_PATH_LENGTH, NODE_META_INFO_SIZE};
16use crate::crypto;
17use crate::header::shared_secret::ExpandedSharedSecret;
18use crate::{constants, utils};
19
20pub const FILLER_STEP_SIZE_INCREASE: usize = NODE_META_INFO_SIZE + HEADER_INTEGRITY_MAC_SIZE;
21
22#[derive(Debug, PartialEq, Eq)]
23pub struct Filler(Vec<u8>);
24
25impl Filler {
26    pub(crate) fn new(expanded_shared_secrets: &[ExpandedSharedSecret]) -> Self {
27        assert!(expanded_shared_secrets.len() <= MAX_PATH_LENGTH);
28        let filler_value = expanded_shared_secrets
29            .iter()
30            .map(|ess| ess.stream_cipher_key()) // we only want the cipher key
31            .map(|cipher_key| {
32                crypto::generate_pseudorandom_bytes(
33                    cipher_key,
34                    &crypto::STREAM_CIPHER_INIT_VECTOR,
35                    constants::STREAM_CIPHER_OUTPUT_LENGTH,
36                )
37            }) // the actual cipher key is only used to generate the pseudorandom bytes
38            .enumerate() // we need to know index of each element to take correct slice of the PRNG output
39            .map(|(i, pseudorandom_bytes)| (i + 1, pseudorandom_bytes)) // the zeroth step is the empty filler and we add on top of it
40            .fold(
41                Vec::new(),
42                |filler_string_accumulator, (i, pseudorandom_bytes)| {
43                    Self::filler_step(filler_string_accumulator, i, pseudorandom_bytes)
44                },
45            );
46        Self(filler_value)
47    }
48
49    fn filler_step(
50        mut filler_string_accumulator: Vec<u8>,
51        i: usize,
52        pseudorandom_bytes: Vec<u8>,
53    ) -> Vec<u8> {
54        assert_eq!(
55            pseudorandom_bytes.len(),
56            constants::STREAM_CIPHER_OUTPUT_LENGTH
57        );
58        assert_eq!(
59            filler_string_accumulator.len(),
60            FILLER_STEP_SIZE_INCREASE * (i - 1) // make sure it has length of the previous step
61        );
62        let zero_bytes = vec![0u8; FILLER_STEP_SIZE_INCREASE];
63        filler_string_accumulator.extend(&zero_bytes);
64
65        // after computing the output vector of AES_CTR we take the last 3*k*i elements of the returned vector
66        // and xor it with the current filler string
67        utils::bytes::xor_with(
68            &mut filler_string_accumulator,
69            &pseudorandom_bytes[pseudorandom_bytes.len() - i * FILLER_STEP_SIZE_INCREASE..],
70        );
71
72        filler_string_accumulator
73    }
74}
75
76impl From<Vec<u8>> for Filler {
77    fn from(raw_bytes: Vec<u8>) -> Self {
78        Self(raw_bytes)
79    }
80}
81
82impl From<Filler> for Vec<u8> {
83    fn from(filler: Filler) -> Self {
84        filler.0
85    }
86}
87
88#[cfg(test)]
89mod test_creating_pseudorandom_bytes {
90    use super::*;
91    use crate::header::shared_secret::ExpandSecret;
92    use x25519_dalek::{PublicKey, StaticSecret};
93
94    #[test]
95    fn with_no_keys_it_generates_empty_filler_string() {
96        let expanded_shared_secret: Vec<_> = vec![];
97        let filler_string = Filler::new(&expanded_shared_secret);
98
99        assert_eq!(0, filler_string.0.len());
100    }
101
102    #[test]
103    fn with_1_key_it_generates_filler_of_length_1_times_3_times_security_parameter() {
104        let shared_keys = [PublicKey::from(&StaticSecret::random())];
105        let expanded_shared_secret: Vec<_> = shared_keys
106            .iter()
107            .map(|&key| key.expand_shared_secret())
108            .collect();
109        let filler_string = Filler::new(&expanded_shared_secret);
110
111        assert_eq!(FILLER_STEP_SIZE_INCREASE, filler_string.0.len());
112    }
113
114    #[test]
115    fn with_3_key_it_generates_filler_of_length_3_times_3_times_security_parameter() {
116        let shared_keys = [
117            PublicKey::from(&StaticSecret::random()),
118            PublicKey::from(&StaticSecret::random()),
119            PublicKey::from(&StaticSecret::random()),
120        ];
121        let expanded_shared_secret: Vec<_> = shared_keys
122            .iter()
123            .map(|&key| key.expand_shared_secret())
124            .collect();
125        let filler_string = Filler::new(&expanded_shared_secret);
126        assert_eq!(3 * FILLER_STEP_SIZE_INCREASE, filler_string.0.len());
127    }
128
129    #[test]
130    #[should_panic]
131    fn panics_with_more_keys_than_the_maximum_path_length() {
132        let shared_keys: Vec<_> = std::iter::repeat_n((), constants::MAX_PATH_LENGTH + 1)
133            .map(|_| PublicKey::from(&StaticSecret::random()))
134            .collect();
135        let expanded_shared_secrets: Vec<_> = shared_keys
136            .iter()
137            .map(|&key| key.expand_shared_secret())
138            .collect();
139        Filler::new(&expanded_shared_secrets);
140    }
141}
142
143#[cfg(test)]
144mod test_new_filler_bytes {
145    use super::*;
146    use crate::test_utils::fixtures::expanded_shared_secret_fixture;
147
148    #[test]
149    fn it_retusn_filler_bytes_of_correct_length_for_3_expanded_shared_secret() {
150        let routing_key_1 = expanded_shared_secret_fixture();
151        let routing_key_2 = expanded_shared_secret_fixture();
152        let routing_key_3 = expanded_shared_secret_fixture();
153        let expanded_shared_secret = [routing_key_1, routing_key_2, routing_key_3];
154        let filler = Filler::new(&expanded_shared_secret);
155        assert_eq!(
156            FILLER_STEP_SIZE_INCREASE * (expanded_shared_secret.len()),
157            filler.0.len()
158        )
159    }
160
161    #[test]
162    fn it_retusn_filler_bytes_of_correct_length_for_4_expanded_shared_secret() {
163        let routing_key_1 = expanded_shared_secret_fixture();
164        let routing_key_2 = expanded_shared_secret_fixture();
165        let routing_key_3 = expanded_shared_secret_fixture();
166        let routing_key_4 = expanded_shared_secret_fixture();
167        let expanded_shared_secret = [routing_key_1, routing_key_2, routing_key_3, routing_key_4];
168        let filler = Filler::new(&expanded_shared_secret);
169        assert_eq!(
170            FILLER_STEP_SIZE_INCREASE * (expanded_shared_secret.len()),
171            filler.0.len()
172        )
173    }
174}
175
176#[cfg(test)]
177mod test_generating_filler_bytes {
178    use super::*;
179
180    mod for_valid_inputs {
181        use super::*;
182
183        #[test]
184        fn it_returns_the_xored_byte_vector_of_a_correct_length_for_i_1() {
185            let pseudorandom_bytes = vec![0; constants::STREAM_CIPHER_OUTPUT_LENGTH];
186            let filler_string_accumulator = vec![];
187            let filler_string =
188                Filler::filler_step(filler_string_accumulator, 1, pseudorandom_bytes);
189            assert_eq!(FILLER_STEP_SIZE_INCREASE, filler_string.len());
190            for x in filler_string {
191                assert_eq!(0, x); // XOR of 0 + 0 == 0
192            }
193        }
194
195        #[test]
196        fn it_returns_the_xored_byte_vector_of_a_correct_length_for_i_3() {
197            let pseudorandom_bytes = vec![0; constants::STREAM_CIPHER_OUTPUT_LENGTH];
198            let filler_string_accumulator = vec![0u8; 2 * FILLER_STEP_SIZE_INCREASE];
199            let filler_string =
200                Filler::filler_step(filler_string_accumulator, 3, pseudorandom_bytes);
201            assert_eq!(FILLER_STEP_SIZE_INCREASE * 3, filler_string.len());
202            for x in filler_string {
203                assert_eq!(0, x); // XOR of 0 + 0 == 0
204            }
205        }
206
207        mod for_an_empty_filler_string_accumulator {
208            use super::*;
209
210            #[test]
211            #[should_panic]
212            fn it_panics() {
213                let pseudorandom_bytes = vec![0; constants::STREAM_CIPHER_OUTPUT_LENGTH];
214                Filler::filler_step(vec![], 0, pseudorandom_bytes);
215            }
216        }
217    }
218
219    mod for_invalid_inputs {
220        use super::*;
221
222        #[test]
223        #[should_panic]
224        fn panics_for_incorrectly_sized_pseudorandom_bytes_vector_and_accumulator_vector() {
225            let pseudorandom_bytes = vec![0; 1];
226            Filler::filler_step(vec![], 0, pseudorandom_bytes);
227        }
228
229        #[test]
230        #[should_panic]
231        fn panics_with_incorrect_length_filler_accumulator() {
232            let good_pseudorandom_bytes = vec![0; constants::STREAM_CIPHER_OUTPUT_LENGTH];
233            let wrong_accumulator = vec![0; 25];
234            Filler::filler_step(wrong_accumulator, 1, good_pseudorandom_bytes);
235        }
236    }
237}