snmp_rust_agent 0.4.0

A framework for building SNMP v3 Agents
Documentation
use aes::cipher::{AsyncStreamCipher, KeyIvInit};
use rasn::types::IntegerType;
use rasn_snmp::v3::USMSecurityParameters;

type Aes128CfbEnc = cfb_mode::Encryptor<aes::Aes128>;
type Aes128CfbDec = cfb_mode::Decryptor<aes::Aes128>;

/// Calculate the Initial Value for the crypt.
/// If you get this wrong, the first block comes out wrong,
/// but it then recovers - this is a CFB feature
fn make_iv(usp: USMSecurityParameters) -> [u8; 16] {
    // Manager chooses salt, agent just uses it (except for v3 traps, which we don't do yet)
    let mut iv: [u8; 16] = [0; 16];
    let (boot32p, needed) = usp.authoritative_engine_boots.to_unsigned_bytes_be();
    let boot32 = boot32p.as_ref();
    let (time32p, t_needed) = usp.authoritative_engine_time.to_unsigned_bytes_be();
    let time32 = time32p.as_ref();
    let saltb = usp.privacy_parameters.clone();
    for i in 0..needed {
        iv[i + 4 - needed] = boot32[i];
    }
    for i in 0..t_needed {
        iv[i + 8 - t_needed] = time32[i];
    }
    for i in 0..8 {
        iv[i + 8] = saltb[i];
    }
    iv
}

/// Decrypt the data
pub fn decrypt(data: &mut [u8], usp: USMSecurityParameters, pkey: &[u8]) -> Vec<u8> {
    let iv = make_iv(usp.clone());
    let key: &[u8] = &pkey[0..16];
    Aes128CfbDec::new_from_slices(key, &iv)
        .unwrap() // Checked, length is valid
        .decrypt(data);
    data.to_vec()
}

/// Encrypt the data
pub fn encrypt(data: &mut [u8], usp: USMSecurityParameters, pkey: &[u8]) -> Vec<u8> {
    let iv = make_iv(usp);
    let key: &[u8] = &pkey[0..16];
    Aes128CfbEnc::new_from_slices(key, &iv)
        .unwrap() // Checked, length is valid
        .encrypt(data);
    data.to_vec()
}

#[cfg(test)]
mod tests {
    use super::*;

    use rasn::types::{Integer, OctetString};

    use rasn_snmp::v3::USMSecurityParameters;

    fn usp0() -> USMSecurityParameters {
        USMSecurityParameters {
            authoritative_engine_id: OctetString::from_static(b""),
            authoritative_engine_boots: Integer::from(0),
            authoritative_engine_time: Integer::from(0),
            user_name: OctetString::from_static(b""),
            authentication_parameters: OctetString::from_static(&[0u8; 8]),
            privacy_parameters: OctetString::from_static(&[0u8; 8]),
        }
    }
    #[test]
    fn test_iv() {
        let iv = make_iv(usp0());
        assert_eq!(iv, [0u8; 16]);
    }

    #[test]
    fn test_round_trip() {
        let usp = usp0();
        let pkey = [0u8; 16];
        let mut data = b"red".to_vec();
        let mut black = encrypt(&mut data, usp.clone(), &pkey);
        let answer = decrypt(&mut black, usp.clone(), &pkey);
        assert_eq!(answer, b"red".to_vec());
    }
}