sphinx_packet/header/
mac.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    HeaderIntegrityHmacAlgorithm, HeaderIntegrityMacSize, HEADER_INTEGRITY_MAC_SIZE,
17};
18use crate::crypto;
19use crate::header::keys::HeaderIntegrityMacKey;
20use digest::generic_array::GenericArray;
21use subtle::{Choice, ConstantTimeEq};
22
23// In paper gamma
24#[derive(Clone, Debug)]
25pub struct HeaderIntegrityMac(GenericArray<u8, HeaderIntegrityMacSize>);
26
27impl HeaderIntegrityMac {
28    pub(crate) fn compute(key: &HeaderIntegrityMacKey, header_data: &[u8]) -> Self {
29        let routing_info_mac =
30            crypto::compute_keyed_hmac::<HeaderIntegrityHmacAlgorithm>(key, header_data);
31
32        // NOTE: BE EXTREMELY CAREFUL HOW YOU MANAGE THOSE BYTES
33        // YOU CAN'T TREAT THEM AS NORMAL ONES
34        let mac_bytes = routing_info_mac.into_bytes();
35        if mac_bytes.len() < HEADER_INTEGRITY_MAC_SIZE {
36            panic!("Algorithm used for computing header integrity mac produced output smaller than minimum length of {}", HEADER_INTEGRITY_MAC_SIZE)
37        }
38
39        // only take first HEADER_INTEGRITY_MAC_SIZE bytes
40        Self(
41            mac_bytes
42                .into_iter()
43                .take(HEADER_INTEGRITY_MAC_SIZE)
44                .collect(),
45        )
46    }
47
48    pub fn verify(
49        &self,
50        integrity_mac_key: &HeaderIntegrityMacKey,
51        enc_routing_info: &[u8],
52    ) -> bool {
53        let recomputed_integrity_mac = Self::compute(integrity_mac_key, enc_routing_info);
54        self.ct_eq(&recomputed_integrity_mac).into()
55    }
56
57    pub fn into_inner(self) -> GenericArray<u8, HeaderIntegrityMacSize> {
58        self.0
59    }
60
61    pub fn as_bytes(&self) -> &[u8] {
62        &self.0
63    }
64
65    pub fn from_bytes(bytes: [u8; HEADER_INTEGRITY_MAC_SIZE]) -> Self {
66        Self(bytes.into())
67    }
68}
69
70impl ConstantTimeEq for HeaderIntegrityMac {
71    fn ct_eq(&self, other: &Self) -> Choice {
72        self.0.ct_eq(&other.0)
73    }
74}
75
76#[cfg(test)]
77mod computing_integrity_mac {
78    use super::*;
79    use crate::constants::INTEGRITY_MAC_KEY_SIZE;
80    use crate::header::routing::ENCRYPTED_ROUTING_INFO_SIZE;
81
82    #[test]
83    fn it_is_possible_to_verify_correct_mac() {
84        let key = [2u8; INTEGRITY_MAC_KEY_SIZE];
85        let data = vec![3u8; ENCRYPTED_ROUTING_INFO_SIZE];
86        let integrity_mac = HeaderIntegrityMac::compute(&key, &data);
87
88        assert!(integrity_mac.verify(&key, &data));
89    }
90
91    #[test]
92    fn it_lets_detecting_flipped_data_bits() {
93        let key = [2u8; INTEGRITY_MAC_KEY_SIZE];
94        let mut data = vec![3u8; ENCRYPTED_ROUTING_INFO_SIZE];
95        let integrity_mac = HeaderIntegrityMac::compute(&key, &data);
96        data[10] = !data[10];
97        assert!(!integrity_mac.verify(&key, &data));
98    }
99}