/*
* Copyright 2023 Jonas Eriksson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use std::time::SystemTime;
use sp800_185::KMac;
pub fn epoch_now_s() -> u64 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("Unable to calculate epoch time")
.as_secs()
}
pub fn epoch_now_ms() -> u128 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("Unable to calculate epoch time")
.as_millis()
}
pub struct SeqMac {
mac: KMac,
period: u64,
}
impl SeqMac {
pub fn new(psk: &str, port: u16, period: u64) -> Self {
// Generate customization string
let custom = format!("seqknock-v1-{port:05}");
SeqMac {
mac: KMac::new_kmac128(psk.as_bytes(), custom.as_bytes()),
period,
}
}
pub fn calc(&self, time: u64) -> u32 {
let mut mac = self.mac.clone();
mac.update(&(time / self.period).to_le_bytes());
let mut result: [u8; 4] = [0; 4];
mac.xof().squeeze(&mut result);
u32::from_le_bytes(result)
}
}
#[cfg(test)]
mod tests {
use super::SeqMac;
#[test]
fn key_lengths() {
let time = 1672531200;
let mac_1 = SeqMac::new("a", 1, 1);
assert_eq!(mac_1.calc(time), 3568379456);
// 1025 chars
let mac_1025 = SeqMac::new(
"iemumahchua4waihauH4eChe7feonohHeimeubah0Hae1Weyaequiech5ohhufaeRahl9uz4shahv8eiceis5efah6iaw3oph6ievol9ohSeeXiocheishel5tuc4logaluoF7eeraetaiwoe3aih5eetee9hoo0iSh4phietha9aix7nohkaibohv6aesu9Dieviulu9Jeil3Ahmah9Aifohk4ahphae8eiKaechei1cie6aehoor9ofu1goo9ahfoo9shaa4aey2obaechai5oopeig6ailoovaeFouv3kihishaeChohPie8eigeeH2eod9ohvah6zue2ca4eGhaimiethiengoongeethemei9nee0IjieBahlik5riekiyaiphahb3lu0hee6otievie6go8Aijohng8ce5ooshophoda2Eunaepeethaecopae1Thuwaeyahpooz9ez7thi3lieL2Eope5air7iet2voh1lah9Oa5poo0eehohzai7rahPhaeghozaex6Eim4Soo7ChooceeNg8Gai6Air1wi5roogeeweic7ohngiewaiGohphoomee0ahdiree9Bedaibah4ook6sujae1oaxughei1quahphee6mohMeoP8hohaeng8niiXiethohchah1ootie4ibai4zaeceex8Iebaem1gu8keG7ing3rahchahHeihieteeQuohjeib1ia5Zie8so1pou2doohi9eelaejixoje2eegh1lee9ohdingaQueecai1aez0eexaez3el2ieB7ievooleefohg3Aidi4kieRoo9eicheiH0DoBohngaeshaigh6Biey8eecooyahchahgh9yoo8aiSho9Pho1aeWi0ohxohs1Kewah8aey2iho7oboh4jied1Ooquool0uel7eeg6roothai8Ahfoo8deh5ahqua7Zipaish7Rooxae7zeeGa6ja8iecoo4Goo8uequei7bohngiyohFoh2uCu8eingi",
1,
1,
);
assert_eq!(mac_1025.calc(time), 2346448994);
// This does work, but perhaps it should not?
let mac_0 = SeqMac::new("", 1, 1);
assert_eq!(mac_0.calc(time), 4132275335);
}
#[test]
fn with_static_macs() {
let mac_asdf_29 = SeqMac::new("asdf", 22, 29);
let mac_asdf_30 = SeqMac::new("asdf", 22, 30);
let mac_asdf_31 = SeqMac::new("asdf", 22, 31);
let mac_asde_30 = SeqMac::new("asde", 22, 30);
let mac_asdf_23_30 = SeqMac::new("asdf", 23, 30);
let mac_asdf_30_2 = SeqMac::new("asdf", 22, 30);
let time = 1672531200;
// Call twice, check for stability
assert_eq!(mac_asdf_29.calc(time), mac_asdf_29.calc(time));
assert_eq!(mac_asdf_30.calc(time), mac_asdf_30.calc(time));
assert_eq!(mac_asdf_31.calc(time), mac_asdf_31.calc(time));
// Check for stability over instansiations
assert_eq!(mac_asdf_30.calc(time), mac_asdf_30_2.calc(time));
// Other PSK should yield different result
assert_ne!(mac_asdf_30.calc(time), mac_asde_30.calc(time));
// Jump in time == period should yield different results
assert_ne!(mac_asdf_30.calc(time), mac_asdf_30.calc(time + 30));
assert_ne!(mac_asdf_30.calc(time), mac_asdf_30.calc(time - 30));
// Other periods should yield different results
assert_ne!(mac_asdf_30.calc(time), mac_asdf_29.calc(time));
assert_ne!(mac_asdf_30.calc(time), mac_asdf_31.calc(time));
// Assure that the point of change is after 30s
let result0 = mac_asdf_30.calc(time);
let mut offset1 = 1;
// Run until result change
loop {
if result0 != mac_asdf_30.calc(time + offset1) {
break;
}
offset1 += 1;
}
let result1 = mac_asdf_30.calc(time + offset1);
let mut offset2 = 1;
// Run until it changes again
loop {
if result1 != mac_asdf_30.calc(time + offset1 + offset2) {
break;
}
offset2 += 1;
}
// Check that the change point is correct
assert_eq!(offset2, 30);
// Different ports should yield different results
assert_ne!(mac_asdf_30.calc(time), mac_asdf_23_30.calc(time));
// Check against pre-calculated values to ensure stability of algorithm
assert_eq!(mac_asdf_29.calc(time), 2407226594);
assert_eq!(mac_asdf_30.calc(time), 2237033466);
assert_eq!(mac_asdf_31.calc(time), 967110946);
}
}