seqknock_common/
mac.rs

1/*
2 * Copyright 2023 Jonas Eriksson
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use std::time::SystemTime;
18
19use hmac::{Hmac, Mac};
20use sha3::Sha3_256;
21
22pub fn epoch_now_s() -> u64 {
23    SystemTime::now()
24        .duration_since(SystemTime::UNIX_EPOCH)
25        .expect("Unable to calculate epoch time")
26        .as_secs()
27}
28
29pub fn epoch_now_ms() -> u128 {
30    SystemTime::now()
31        .duration_since(SystemTime::UNIX_EPOCH)
32        .expect("Unable to calculate epoch time")
33        .as_millis()
34}
35
36pub struct SeqMac {
37    mac: Hmac<Sha3_256>,
38    period: u64,
39}
40
41impl SeqMac {
42    pub fn new(psk: &str, port: u16, period: u64) -> Self {
43        let psk_bytes = psk.as_bytes();
44        let mut port_padded_psk = Vec::with_capacity(psk_bytes.len() + 3);
45
46        // Add port and \0
47        let port_bytes = port.to_be_bytes();
48        port_padded_psk.push(port_bytes[0]);
49        port_padded_psk.push(port_bytes[1]);
50        port_padded_psk.push(0);
51
52        // Add psk
53        for byte in psk_bytes {
54            port_padded_psk.push(*byte);
55        }
56
57        SeqMac {
58            mac: Hmac::new_from_slice(&port_padded_psk).expect("Unable to construct HMAC"),
59            period,
60        }
61    }
62
63    pub fn calc(&self, time: u64) -> u32 {
64        let mut mac = self.mac.clone();
65        mac.update(format!("{}", time / self.period).as_bytes());
66        let result = mac.finalize();
67        let bytes = result.into_bytes();
68        let first = [bytes[0], bytes[1], bytes[2], bytes[3]];
69        u32::from_be_bytes(first)
70    }
71}
72
73#[cfg(test)]
74mod tests {
75
76    use super::SeqMac;
77
78    #[test]
79    fn key_lengths() {
80        let time = 1672531200;
81
82        let mac_1 = SeqMac::new("a", 1, 1);
83        assert_eq!(mac_1.calc(time), 3233246158);
84        // 1025 chars
85        let mac_1025 = SeqMac::new("iemumahchua4waihauH4eChe7feonohHeimeubah0Hae1Weyaequiech5ohhufaeRahl9uz4shahv8eiceis5efah6iaw3oph6ievol9ohSeeXiocheishel5tuc4logaluoF7eeraetaiwoe3aih5eetee9hoo0iSh4phietha9aix7nohkaibohv6aesu9Dieviulu9Jeil3Ahmah9Aifohk4ahphae8eiKaechei1cie6aehoor9ofu1goo9ahfoo9shaa4aey2obaechai5oopeig6ailoovaeFouv3kihishaeChohPie8eigeeH2eod9ohvah6zue2ca4eGhaimiethiengoongeethemei9nee0IjieBahlik5riekiyaiphahb3lu0hee6otievie6go8Aijohng8ce5ooshophoda2Eunaepeethaecopae1Thuwaeyahpooz9ez7thi3lieL2Eope5air7iet2voh1lah9Oa5poo0eehohzai7rahPhaeghozaex6Eim4Soo7ChooceeNg8Gai6Air1wi5roogeeweic7ohngiewaiGohphoomee0ahdiree9Bedaibah4ook6sujae1oaxughei1quahphee6mohMeoP8hohaeng8niiXiethohchah1ootie4ibai4zaeceex8Iebaem1gu8keG7ing3rahchahHeihieteeQuohjeib1ia5Zie8so1pou2doohi9eelaejixoje2eegh1lee9ohdingaQueecai1aez0eexaez3el2ieB7ievooleefohg3Aidi4kieRoo9eicheiH0DoBohngaeshaigh6Biey8eecooyahchahgh9yoo8aiSho9Pho1aeWi0ohxohs1Kewah8aey2iho7oboh4jied1Ooquool0uel7eeg6roothai8Ahfoo8deh5ahqua7Zipaish7Rooxae7zeeGa6ja8iecoo4Goo8uequei7bohngiyohFoh2uCu8eingi", 1, 1);
86        assert_eq!(mac_1025.calc(time), 3874154907);
87        // This does work, but perhaps it should not?
88        let mac_0 = SeqMac::new("", 1, 1);
89        assert_eq!(mac_0.calc(time), 925434999);
90    }
91
92    #[test]
93    fn with_static_macs() {
94        let mac_asdf_29 = SeqMac::new("asdf", 22, 29);
95        let mac_asdf_30 = SeqMac::new("asdf", 22, 30);
96        let mac_asdf_31 = SeqMac::new("asdf", 22, 31);
97        let mac_asde_30 = SeqMac::new("asde", 22, 30);
98        let mac_asdf_23_30 = SeqMac::new("asdf", 23, 30);
99        let mac_asdf_30_2 = SeqMac::new("asdf", 22, 30);
100        let time = 1672531200;
101
102        // Call twice, check for stability
103        assert_eq!(mac_asdf_29.calc(time), mac_asdf_29.calc(time));
104        assert_eq!(mac_asdf_30.calc(time), mac_asdf_30.calc(time));
105        assert_eq!(mac_asdf_31.calc(time), mac_asdf_31.calc(time));
106
107        // Check for stability over instansiations
108        assert_eq!(mac_asdf_30.calc(time), mac_asdf_30_2.calc(time));
109
110        // Other PSK should yield different result
111        assert_ne!(mac_asdf_30.calc(time), mac_asde_30.calc(time));
112
113        // Jump in time == period should yield different results
114        assert_ne!(mac_asdf_30.calc(time), mac_asdf_30.calc(time + 30));
115        assert_ne!(mac_asdf_30.calc(time), mac_asdf_30.calc(time - 30));
116
117        // Other periods should yield different results
118        assert_ne!(mac_asdf_30.calc(time), mac_asdf_29.calc(time));
119        assert_ne!(mac_asdf_30.calc(time), mac_asdf_31.calc(time));
120
121        // Assure that the point of change is after 30s
122        let result0 = mac_asdf_30.calc(time);
123        let mut offset1 = 1;
124        // Run until result change
125        loop {
126            if result0 != mac_asdf_30.calc(time + offset1) {
127                break;
128            }
129            offset1 += 1;
130        }
131        let result1 = mac_asdf_30.calc(time + offset1);
132        let mut offset2 = 1;
133        // Run until it changes again
134        loop {
135            if result1 != mac_asdf_30.calc(time + offset1 + offset2) {
136                break;
137            }
138            offset2 += 1;
139        }
140        // Check that the change point is correct
141        assert_eq!(offset2, 30);
142
143        // Different ports should yield different results
144        assert_ne!(mac_asdf_30.calc(time), mac_asdf_23_30.calc(time));
145
146        // Check against pre-calculated values to ensure stability of algorithm
147        assert_eq!(mac_asdf_29.calc(time), 390276340);
148        assert_eq!(mac_asdf_30.calc(time), 4262803033);
149        assert_eq!(mac_asdf_31.calc(time), 1960043237);
150    }
151}