kasimir-sys 0.1.0

Small helper crate for communication with HS100 smart wifi plugs and similar devices.
Documentation
use std::io::{Read, Write};

pub fn decrypt_hs100(data: &mut [u8]) {
    let mut key = 171u8;
    for b in data {
        if *b != 0 {
            *b = *b ^ key;
            key = *b ^ key;
        }
    }
}

pub fn encrypt_hs100(data: &mut [u8]) {
    let mut key = 171u8;
    for b in data {
        if *b != 0 {
            *b = *b ^ key;
            key = *b;
        }
    }
}

pub fn read_hs100_str<T: Read + ?Sized>(stream: &mut T) -> std::io::Result<String> {
    let mut len = [0; 4];

    stream.read(&mut len)?;

    let len = ((len[0] as usize) << 24)
        + ((len[1] as usize) << 16)
        + ((len[2] as usize) << 8)
        + (len[3] as usize);

    let mut buf: Vec<u8> = vec![0; len];
    stream.read_exact(&mut buf)?;

    decrypt_hs100(&mut buf);

    let mut result = std::str::from_utf8(&buf).unwrap().to_owned();
    result.truncate(len);

    Ok(result)
}

pub fn write_hs100_str<T: Write + ?Sized>(
    stream: &mut T,
    mut message: String,
) -> std::io::Result<()> {
    let mut len = [0; 4];

    len[3] = (message.len() % 0xFF) as u8;
    len[2] = ((message.len() >> 8) % 0xFF) as u8;
    len[1] = ((message.len() >> 16) % 0xFF) as u8;
    len[0] = ((message.len() >> 24) % 0xFF) as u8;

    let bytes = unsafe { message.as_bytes_mut() };
    encrypt_hs100(bytes);

    stream.write(&len)?;
    stream.write(bytes)?;

    Ok(())
}

#[cfg(test)]
mod tests {
    #[test]
    fn encrytion() {
        let plaintext: &mut [u8] = &mut br#"{"system":{"get_sysinfo":{}}}"#.clone();
        let cyphertext = [
            208, 242, 129, 248, 139, 255, 154, 247, 213, 239, 148, 182, 209, 180, 192, 159, 236,
            149, 230, 143, 225, 135, 232, 202, 240, 139, 246, 139, 246,
        ];

        super::encrypt_hs100(plaintext);
        assert_eq!(&plaintext, &cyphertext);
    }

    #[test]
    fn decryption() {
        let plaintext = br#"{"system":{"get_sysinfo":{}}}"#;
        let cyphertext: &mut [u8] = &mut [
            208, 242, 129, 248, 139, 255, 154, 247, 213, 239, 148, 182, 209, 180, 192, 159, 236,
            149, 230, 143, 225, 135, 232, 202, 240, 139, 246, 139, 246,
        ]
        .clone();

        super::decrypt_hs100(cyphertext);
        assert_eq!(&cyphertext, &plaintext);
    }

    #[test]
    fn encrytion_roundtrip() {
        let original = br#"{"system":{"get_sysinfo":{}}}"#;
        let modified: &mut [u8] = &mut br#"{"system":{"get_sysinfo":{}}}"#.clone();

        super::encrypt_hs100(modified);
        super::decrypt_hs100(modified);
        assert_eq!(&original, &modified);
    }

    #[test]
    fn read() {
        let plaintext = r#"{"system":{"get_sysinfo":{}}}"#.to_string();
        let cyphertext: &[u8] = &[
            0, 0, 0, 29, 208, 242, 129, 248, 139, 255, 154, 247, 213, 239, 148, 182, 209, 180, 192,
            159, 236, 149, 230, 143, 225, 135, 232, 202, 240, 139, 246, 139, 246,
        ];

        let result = super::read_hs100_str(&mut cyphertext.as_ref()).unwrap();
        assert_eq!(&result, &plaintext);
    }

    #[test]
    fn write() {
        let plaintext = r#"{"system":{"get_sysinfo":{}}}"#.to_string();
        let cyphertext = [
            0, 0, 0, 29, 208, 242, 129, 248, 139, 255, 154, 247, 213, 239, 148, 182, 209, 180, 192,
            159, 236, 149, 230, 143, 225, 135, 232, 202, 240, 139, 246, 139, 246,
        ];

        let mut buffer: Vec<u8> = Vec::new();
        super::write_hs100_str(&mut buffer, plaintext).unwrap();
        assert_eq!(&buffer, &cyphertext);
    }
}