sphinx_packet/header/routing/
nodes.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::{
16    DELAY_LENGTH, DESTINATION_ADDRESS_LENGTH, HEADER_INTEGRITY_MAC_SIZE, IDENTIFIER_LENGTH,
17    NODE_ADDRESS_LENGTH, NODE_META_INFO_SIZE, STREAM_CIPHER_OUTPUT_LENGTH, VERSION_LENGTH,
18};
19use crate::crypto;
20use crate::crypto::STREAM_CIPHER_INIT_VECTOR;
21use crate::header::delays::Delay;
22use crate::header::keys::{HeaderIntegrityMacKey, StreamCipherKey};
23use crate::header::mac::HeaderIntegrityMac;
24use crate::header::routing::{
25    EncapsulatedRoutingInformation, RoutingFlag, ENCRYPTED_ROUTING_INFO_SIZE, FINAL_HOP,
26    FORWARD_HOP, TRUNCATED_ROUTING_INFO_SIZE,
27};
28use crate::header::shared_secret::ExpandedSharedSecret;
29use crate::header::{ProcessedHeader, ProcessedHeaderData, SphinxHeader};
30use crate::payload::key::derive_payload_key;
31use crate::route::{DestinationAddressBytes, NodeAddressBytes, SURBIdentifier};
32use crate::utils;
33use crate::version::Version;
34use crate::{Error, ErrorKind, Result};
35use std::fmt;
36use x25519_dalek::PublicKey;
37
38pub const PADDED_ENCRYPTED_ROUTING_INFO_SIZE: usize =
39    ENCRYPTED_ROUTING_INFO_SIZE + NODE_META_INFO_SIZE + HEADER_INTEGRITY_MAC_SIZE;
40
41// in paper beta
42pub(super) struct RoutingInformation {
43    flag: RoutingFlag,
44    version: Version,
45    // in paper nu
46    node_address: NodeAddressBytes,
47    delay: Delay,
48    // in paper gamma
49    header_integrity_mac: HeaderIntegrityMac,
50    // in paper also beta (!)
51    next_routing_information: TruncatedRoutingInformation,
52}
53
54impl RoutingInformation {
55    pub(super) fn new(
56        node_address: NodeAddressBytes,
57        delay: Delay,
58        next_encapsulated_routing_information: EncapsulatedRoutingInformation,
59        version: Version,
60    ) -> Self {
61        RoutingInformation {
62            flag: FORWARD_HOP,
63            version,
64            node_address,
65            delay,
66            header_integrity_mac: next_encapsulated_routing_information.integrity_mac,
67            next_routing_information: next_encapsulated_routing_information
68                .enc_routing_information
69                .truncate(),
70        }
71    }
72
73    fn concatenate_components(self) -> Vec<u8> {
74        std::iter::once(self.flag)
75            .chain(self.version.to_bytes().iter().copied())
76            .chain(self.node_address.as_bytes().iter().copied())
77            .chain(self.delay.to_bytes().iter().copied())
78            .chain(self.header_integrity_mac.into_inner())
79            .chain(self.next_routing_information.iter().copied())
80            .collect()
81    }
82
83    pub(super) fn encrypt(self, key: &StreamCipherKey) -> EncryptedRoutingInformation {
84        let routing_info_components = self.concatenate_components();
85        assert_eq!(ENCRYPTED_ROUTING_INFO_SIZE, routing_info_components.len());
86
87        let pseudorandom_bytes = crypto::generate_pseudorandom_bytes(
88            key,
89            &STREAM_CIPHER_INIT_VECTOR,
90            STREAM_CIPHER_OUTPUT_LENGTH,
91        );
92
93        let encrypted_routing_info_vec = utils::bytes::xor(
94            &routing_info_components,
95            &pseudorandom_bytes[..ENCRYPTED_ROUTING_INFO_SIZE],
96        );
97
98        let mut encrypted_routing_info = [0u8; ENCRYPTED_ROUTING_INFO_SIZE];
99        encrypted_routing_info.copy_from_slice(&encrypted_routing_info_vec);
100
101        EncryptedRoutingInformation {
102            value: encrypted_routing_info,
103        }
104    }
105}
106
107// result of xoring beta with rho (output of PRNG)
108// the derivation is only required for the tests. please remove it in production
109#[derive(Clone)]
110pub struct EncryptedRoutingInformation {
111    value: [u8; ENCRYPTED_ROUTING_INFO_SIZE],
112}
113
114impl AsRef<[u8]> for EncryptedRoutingInformation {
115    fn as_ref(&self) -> &[u8] {
116        self.value.as_ref()
117    }
118}
119
120impl fmt::Debug for EncryptedRoutingInformation {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        f.debug_struct("EncryptedRoutingInformation")
123            .field("value", &self.value)
124            .finish()
125    }
126}
127
128impl EncryptedRoutingInformation {
129    pub fn from_bytes(bytes: [u8; ENCRYPTED_ROUTING_INFO_SIZE]) -> Self {
130        Self { value: bytes }
131    }
132
133    fn truncate(self) -> TruncatedRoutingInformation {
134        let mut truncated_routing_info = [0u8; TRUNCATED_ROUTING_INFO_SIZE];
135        truncated_routing_info.copy_from_slice(&self.value[..TRUNCATED_ROUTING_INFO_SIZE]);
136        truncated_routing_info
137    }
138
139    pub(super) fn encapsulate_with_mac(
140        self,
141        key: &HeaderIntegrityMacKey,
142    ) -> EncapsulatedRoutingInformation {
143        let integrity_mac = HeaderIntegrityMac::compute(key, &self.value);
144        EncapsulatedRoutingInformation {
145            enc_routing_information: self,
146            integrity_mac,
147        }
148    }
149
150    fn add_zero_padding(self) -> PaddedEncryptedRoutingInformation {
151        let zero_bytes = std::iter::repeat_n(0u8, NODE_META_INFO_SIZE + HEADER_INTEGRITY_MAC_SIZE);
152        let padded_enc_routing_info: Vec<u8> =
153            self.value.iter().copied().chain(zero_bytes).collect();
154
155        assert_eq!(
156            PADDED_ENCRYPTED_ROUTING_INFO_SIZE,
157            padded_enc_routing_info.len()
158        );
159        PaddedEncryptedRoutingInformation {
160            value: padded_enc_routing_info,
161        }
162    }
163
164    pub(crate) fn unwrap(
165        self,
166        stream_cipher_key: &StreamCipherKey,
167    ) -> Result<ParsedRawRoutingInformation> {
168        // we have to add padding to the encrypted routing information before decrypting, otherwise we gonna lose information
169        self.add_zero_padding().decrypt(stream_cipher_key).parse()
170    }
171}
172
173pub struct PaddedEncryptedRoutingInformation {
174    value: Vec<u8>,
175}
176
177impl PaddedEncryptedRoutingInformation {
178    pub fn decrypt(self, key: &StreamCipherKey) -> RawRoutingInformation {
179        let pseudorandom_bytes = crypto::generate_pseudorandom_bytes(
180            key,
181            &STREAM_CIPHER_INIT_VECTOR,
182            STREAM_CIPHER_OUTPUT_LENGTH,
183        );
184
185        debug_assert_eq!(self.value.len(), pseudorandom_bytes.len());
186        RawRoutingInformation {
187            value: utils::bytes::xor(&self.value, &pseudorandom_bytes),
188        }
189    }
190}
191
192pub struct RawRoutingInformation {
193    value: Vec<u8>,
194}
195
196pub struct ParsedRawRoutingInformation {
197    pub(crate) version: Version,
198    pub(crate) data: ParsedRawRoutingInformationData,
199}
200
201pub enum ParsedRawRoutingInformationData {
202    ForwardHop {
203        next_hop_address: NodeAddressBytes,
204        delay: Delay,
205        new_routing_information: Box<EncapsulatedRoutingInformation>,
206    },
207    FinalHop {
208        destination: DestinationAddressBytes,
209        identifier: SURBIdentifier,
210    },
211}
212
213impl ParsedRawRoutingInformation {
214    pub(crate) fn into_processed_header(
215        self,
216        shared_secret: PublicKey,
217        expanded_shared_secret: &ExpandedSharedSecret,
218    ) -> ProcessedHeader {
219        let version = self.version;
220        let payload_key = if version.expects_legacy_full_payload_keys() {
221            *expanded_shared_secret.legacy_payload_key()
222        } else {
223            derive_payload_key(expanded_shared_secret.payload_key_seed())
224        };
225
226        match self.data {
227            ParsedRawRoutingInformationData::ForwardHop {
228                next_hop_address,
229                delay,
230                new_routing_information,
231            } => {
232                // blind the shared_secret in the header
233                let new_shared_secret = expanded_shared_secret.blind_shared_secret(shared_secret);
234
235                ProcessedHeader {
236                    payload_key,
237                    version,
238                    data: ProcessedHeaderData::ForwardHop {
239                        updated_header: SphinxHeader {
240                            shared_secret: new_shared_secret,
241                            routing_info: new_routing_information,
242                        },
243                        next_hop_address,
244                        delay,
245                    },
246                }
247            }
248            ParsedRawRoutingInformationData::FinalHop {
249                destination,
250                identifier,
251            } => ProcessedHeader {
252                payload_key,
253                version,
254                data: ProcessedHeaderData::FinalHop {
255                    destination,
256                    identifier,
257                },
258            },
259        }
260    }
261
262    #[deprecated]
263    #[allow(deprecated)]
264    pub(crate) fn legacy_into_processed_header(
265        self,
266        shared_secret: PublicKey,
267        expanded_shared_secret: &ExpandedSharedSecret,
268    ) -> ProcessedHeader {
269        // legacy processing only ever used old key derivation
270        let version = self.version;
271        let payload_key = *expanded_shared_secret.legacy_payload_key();
272
273        match self.data {
274            ParsedRawRoutingInformationData::ForwardHop {
275                next_hop_address,
276                delay,
277                new_routing_information,
278            } => {
279                // blind the shared_secret in the header
280                let new_shared_secret =
281                    expanded_shared_secret.legacy_blind_share_secret(shared_secret);
282
283                ProcessedHeader {
284                    payload_key,
285                    version,
286                    data: ProcessedHeaderData::ForwardHop {
287                        updated_header: SphinxHeader {
288                            shared_secret: new_shared_secret,
289                            routing_info: new_routing_information,
290                        },
291                        next_hop_address,
292                        delay,
293                    },
294                }
295            }
296            ParsedRawRoutingInformationData::FinalHop {
297                destination,
298                identifier,
299            } => ProcessedHeader {
300                payload_key,
301                version,
302                data: ProcessedHeaderData::FinalHop {
303                    destination,
304                    identifier,
305                },
306            },
307        }
308    }
309}
310
311impl RawRoutingInformation {
312    pub(crate) fn parse(self) -> Result<ParsedRawRoutingInformation> {
313        // this assertion must hold as the routing information can only be constructed after padding it to the correct length
314        debug_assert_eq!(
315            NODE_META_INFO_SIZE + HEADER_INTEGRITY_MAC_SIZE + ENCRYPTED_ROUTING_INFO_SIZE,
316            self.value.len()
317        );
318
319        let flag = self.value[0];
320        match flag {
321            FORWARD_HOP => Ok(self.parse_as_forward_hop()),
322            FINAL_HOP => Ok(self.parse_as_final_hop()),
323            _ => Err(Error::new(
324                ErrorKind::InvalidRouting,
325                format!("tried to parse unknown routing flag: {flag}"),
326            )),
327        }
328    }
329
330    // NOTE: the bound checks are not needed here as its performed prior to construction of this type
331    fn parse_as_forward_hop(self) -> ParsedRawRoutingInformation {
332        let mut i = 1;
333
334        let version_ref = &self.value[i..i + VERSION_LENGTH];
335        i += VERSION_LENGTH;
336
337        let mut next_hop_address: [u8; NODE_ADDRESS_LENGTH] = Default::default();
338        next_hop_address.copy_from_slice(&self.value[i..i + NODE_ADDRESS_LENGTH]);
339        i += NODE_ADDRESS_LENGTH;
340
341        let mut delay_bytes: [u8; DELAY_LENGTH] = Default::default();
342        delay_bytes.copy_from_slice(&self.value[i..i + DELAY_LENGTH]);
343        i += DELAY_LENGTH;
344
345        // the next HEADER_INTEGRITY_MAC_SIZE bytes represent the integrity mac on the next hop
346        let mut next_hop_integrity_mac: [u8; HEADER_INTEGRITY_MAC_SIZE] = Default::default();
347        next_hop_integrity_mac.copy_from_slice(&self.value[i..i + HEADER_INTEGRITY_MAC_SIZE]);
348        i += HEADER_INTEGRITY_MAC_SIZE;
349
350        // the next ENCRYPTED_ROUTING_INFO_SIZE bytes represent the routing information for the next hop
351        let mut next_hop_encrypted_routing_information = [0u8; ENCRYPTED_ROUTING_INFO_SIZE];
352        next_hop_encrypted_routing_information
353            .copy_from_slice(&self.value[i..i + ENCRYPTED_ROUTING_INFO_SIZE]);
354
355        let next_hop_encapsulated_routing_info = EncapsulatedRoutingInformation::encapsulate(
356            EncryptedRoutingInformation::from_bytes(next_hop_encrypted_routing_information),
357            HeaderIntegrityMac::from_bytes(next_hop_integrity_mac),
358        );
359
360        ParsedRawRoutingInformation {
361            version: Version::from_bytes([version_ref[0], version_ref[1], version_ref[2]]),
362            data: ParsedRawRoutingInformationData::ForwardHop {
363                next_hop_address: NodeAddressBytes::from_bytes(next_hop_address),
364                delay: Delay::from_bytes(delay_bytes),
365                new_routing_information: Box::new(next_hop_encapsulated_routing_info),
366            },
367        }
368    }
369
370    // NOTE: the bound checks are not needed here as its performed prior to construction of this type
371    fn parse_as_final_hop(self) -> ParsedRawRoutingInformation {
372        let mut i = 1;
373
374        let version_ref = &self.value[i..i + VERSION_LENGTH];
375        i += VERSION_LENGTH;
376
377        let mut destination_bytes: [u8; DESTINATION_ADDRESS_LENGTH] = Default::default();
378        destination_bytes.copy_from_slice(&self.value[i..i + DESTINATION_ADDRESS_LENGTH]);
379        i += DESTINATION_ADDRESS_LENGTH;
380        let destination = DestinationAddressBytes::from_bytes(destination_bytes);
381
382        let mut identifier: [u8; IDENTIFIER_LENGTH] = Default::default();
383        identifier.copy_from_slice(&self.value[i..i + IDENTIFIER_LENGTH]);
384
385        ParsedRawRoutingInformation {
386            version: Version::from_bytes([version_ref[0], version_ref[1], version_ref[2]]),
387            data: ParsedRawRoutingInformationData::FinalHop {
388                destination,
389                identifier,
390            },
391        }
392    }
393}
394
395// result of truncating encrypted beta before passing it to next 'layer'
396type TruncatedRoutingInformation = [u8; TRUNCATED_ROUTING_INFO_SIZE];
397
398#[cfg(test)]
399mod preparing_header_layer {
400    use super::*;
401    use crate::constants::HeaderIntegrityHmacAlgorithm;
402    use crate::test_utils::fixtures::expanded_shared_secret_fixture;
403    use crate::{
404        constants::HEADER_INTEGRITY_MAC_SIZE,
405        test_utils::fixtures::{encapsulated_routing_information_fixture, node_address_fixture},
406    };
407
408    #[test]
409    fn it_returns_encrypted_truncated_address_and_flag_concatenated_with_inner_layer_and_mac_on_it()
410    {
411        let node_address = node_address_fixture();
412        let delay = Delay::new_from_nanos(10);
413        let previous_node_routing_keys = expanded_shared_secret_fixture();
414        let inner_layer_routing = encapsulated_routing_information_fixture();
415
416        let version = Version::default();
417        // calculate everything without using any object methods
418        let concatenated_materials: Vec<u8> = [
419            vec![FORWARD_HOP],
420            version.to_bytes().to_vec(),
421            node_address.to_bytes().to_vec(),
422            delay.to_bytes().to_vec(),
423            inner_layer_routing.integrity_mac.as_bytes().to_vec(),
424            inner_layer_routing
425                .enc_routing_information
426                .value
427                .to_vec()
428                .iter()
429                .cloned()
430                .take(TRUNCATED_ROUTING_INFO_SIZE)
431                .collect(),
432        ]
433        .concat();
434
435        let pseudorandom_bytes = crypto::generate_pseudorandom_bytes(
436            previous_node_routing_keys.stream_cipher_key(),
437            &STREAM_CIPHER_INIT_VECTOR,
438            STREAM_CIPHER_OUTPUT_LENGTH,
439        );
440
441        let expected_encrypted_routing_info_vec = utils::bytes::xor(
442            &concatenated_materials,
443            &pseudorandom_bytes[..ENCRYPTED_ROUTING_INFO_SIZE],
444        );
445
446        let expected_routing_mac = crypto::compute_keyed_hmac::<HeaderIntegrityHmacAlgorithm>(
447            previous_node_routing_keys.header_integrity_hmac_key(),
448            &expected_encrypted_routing_info_vec,
449        );
450        let mut expected_routing_mac = expected_routing_mac.into_bytes().to_vec();
451        expected_routing_mac.truncate(HEADER_INTEGRITY_MAC_SIZE);
452
453        let next_layer_routing =
454            RoutingInformation::new(node_address, delay, inner_layer_routing, Version::default())
455                .encrypt(previous_node_routing_keys.stream_cipher_key())
456                .encapsulate_with_mac(previous_node_routing_keys.header_integrity_hmac_key());
457
458        assert_eq!(
459            expected_encrypted_routing_info_vec,
460            next_layer_routing.enc_routing_information.value.to_vec()
461        );
462        assert_eq!(
463            expected_routing_mac,
464            next_layer_routing.integrity_mac.as_bytes().to_vec()
465        );
466    }
467}
468
469#[cfg(test)]
470mod encrypting_routing_information {
471    use super::*;
472    use crate::{
473        crypto::STREAM_CIPHER_KEY_SIZE,
474        test_utils::fixtures::{header_integrity_mac_fixture, node_address_fixture},
475    };
476
477    #[test]
478    fn it_is_possible_to_decrypt_it_to_recover_original_data() {
479        let key = [2u8; STREAM_CIPHER_KEY_SIZE];
480        let flag = FORWARD_HOP;
481        let address = node_address_fixture();
482        let delay = Delay::new_from_nanos(15);
483        let mac = header_integrity_mac_fixture();
484        let next_routing = [8u8; TRUNCATED_ROUTING_INFO_SIZE];
485
486        let version = Version::default();
487        let encryption_data = [
488            vec![flag],
489            version.to_bytes().to_vec(),
490            address.to_bytes().to_vec(),
491            delay.to_bytes().to_vec(),
492            mac.as_bytes().to_vec(),
493            next_routing.to_vec(),
494        ]
495        .concat();
496
497        let routing_information = RoutingInformation {
498            flag: FORWARD_HOP,
499            version,
500            node_address: address,
501            delay,
502            header_integrity_mac: mac,
503            next_routing_information: next_routing,
504        };
505
506        let encrypted_data = routing_information.encrypt(&key);
507        let decryption_key_source = crypto::generate_pseudorandom_bytes(
508            &key,
509            &STREAM_CIPHER_INIT_VECTOR,
510            STREAM_CIPHER_OUTPUT_LENGTH,
511        );
512        let decryption_key = &decryption_key_source[..ENCRYPTED_ROUTING_INFO_SIZE];
513        let decrypted_data = utils::bytes::xor(&encrypted_data.value, decryption_key);
514        assert_eq!(encryption_data, decrypted_data);
515    }
516}
517
518#[cfg(test)]
519mod truncating_routing_information {
520    use crate::test_utils::fixtures::encrypted_routing_information_fixture;
521
522    #[test]
523    fn it_does_not_change_prefixed_data() {
524        let encrypted_routing_info = encrypted_routing_information_fixture();
525        let routing_info_data_copy = encrypted_routing_info.value;
526
527        let truncated_routing_info = encrypted_routing_info.truncate();
528        for i in 0..truncated_routing_info.len() {
529            assert_eq!(truncated_routing_info[i], routing_info_data_copy[i]);
530        }
531    }
532}
533
534#[cfg(test)]
535mod parse_decrypted_routing_information {
536    use super::*;
537    use crate::{
538        header::routing::ENCRYPTED_ROUTING_INFO_SIZE,
539        test_utils::fixtures::{header_integrity_mac_fixture, node_address_fixture},
540    };
541
542    #[test]
543    fn it_returns_next_hop_address_integrity_mac_enc_routing_info() {
544        let flag = FORWARD_HOP;
545        let address_fixture = node_address_fixture();
546        let delay = Delay::new_from_nanos(10);
547        let integrity_mac = header_integrity_mac_fixture();
548        let next_routing_information = [1u8; ENCRYPTED_ROUTING_INFO_SIZE];
549        let version = Version::default();
550
551        let data = [
552            vec![flag],
553            version.to_bytes().to_vec(),
554            address_fixture.to_bytes().to_vec(),
555            delay.to_bytes().to_vec(),
556            integrity_mac.as_bytes().to_vec(),
557            next_routing_information.to_vec(),
558        ]
559        .concat();
560
561        let raw_routing_info = RawRoutingInformation { value: data };
562
563        let parsed = raw_routing_info.parse().unwrap();
564        match parsed.data {
565            ParsedRawRoutingInformationData::ForwardHop {
566                next_hop_address,
567                delay: _,
568                new_routing_information,
569            } => {
570                assert_eq!(address_fixture, next_hop_address);
571                assert_eq!(
572                    integrity_mac.as_bytes().to_vec(),
573                    new_routing_information.integrity_mac.as_bytes().to_vec()
574                );
575                assert_eq!(
576                    next_routing_information.to_vec(),
577                    new_routing_information
578                        .enc_routing_information
579                        .as_ref()
580                        .to_vec()
581                );
582            }
583            ParsedRawRoutingInformationData::FinalHop { .. } => panic!(),
584        }
585    }
586}