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>;
fn make_iv(usp: USMSecurityParameters) -> [u8; 16] {
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
}
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() .decrypt(data);
data.to_vec()
}
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() .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());
}
}