seqknock-common 0.3.1

TCP Sequence number-based knocking; common sources
Documentation
/*
 * 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);
    }
}