use super::keys;
use super::keys::CryptoFactory;
use super::maccommandcreator;
use super::maccommands::{mac_commands_len, SerializableMacCommand};
#[cfg(feature = "with-downlink")]
use super::maccommands::{DLSettings, Frequency};
use super::parser;
use super::securityhelpers;
#[cfg(feature = "default-crypto")]
use super::default_crypto::DefaultFactory;
#[cfg(feature = "with-downlink")]
use super::keys::Decrypter;
#[cfg(any(feature = "with-downlink", feature = "default-crypto"))]
use aes::cipher::generic_array::GenericArray;
#[cfg(feature = "default-crypto")]
use aes::cipher::generic_array::typenum::U256;
const PIGGYBACK_MAC_COMMANDS_MAX_LEN: usize = 15;
#[cfg(feature = "with-downlink")]
#[derive(Default)]
pub struct JoinAcceptCreator<D, F> {
data: D,
with_c_f_list: bool,
encrypted: bool,
factory: F,
}
#[cfg(feature = "with-downlink")]
impl<D: AsMut<[u8]>, F: CryptoFactory + Default> JoinAcceptCreator<D, F> {
pub fn with_options<'a>(mut data: D, factory: F) -> Result<Self, &'a str> {
let d = data.as_mut();
if d.len() < 33 {
return Err("data slice is too short");
}
d[0] = 0x20;
Ok(Self {
data,
with_c_f_list: false,
encrypted: false,
factory,
})
}
pub fn set_app_nonce<H: AsRef<[u8]>, T: Into<parser::AppNonce<H>>>(
&mut self,
app_nonce: T,
) -> &mut Self {
let converted = app_nonce.into();
self.data.as_mut()[1..4].copy_from_slice(converted.as_ref());
self
}
pub fn set_net_id<H: AsRef<[u8]>, T: Into<parser::NwkAddr<H>>>(
&mut self,
net_id: T,
) -> &mut Self {
let converted = net_id.into();
self.data.as_mut()[4..7].copy_from_slice(converted.as_ref());
self
}
pub fn set_dev_addr<H: AsRef<[u8]>, T: Into<parser::DevAddr<H>>>(
&mut self,
dev_addr: T,
) -> &mut Self {
let converted = dev_addr.into();
self.data.as_mut()[7..11].copy_from_slice(converted.as_ref());
self
}
pub fn set_dl_settings<T: Into<DLSettings>>(&mut self, dl_settings: T) -> &mut Self {
let converted = dl_settings.into();
self.data.as_mut()[11] = converted.raw_value();
self
}
pub fn set_rx_delay(&mut self, rx_delay: u8) -> &mut Self {
self.data.as_mut()[12] = rx_delay;
self
}
pub fn set_c_f_list<'a, C: AsRef<[Frequency<'a>]>>(
&mut self,
list: C,
) -> Result<&mut Self, &str> {
let ch_list = list.as_ref();
if ch_list.len() > 5 {
return Err("too many frequences");
}
let d = self.data.as_mut();
ch_list.iter().enumerate().for_each(|(i, fr)| {
let v = fr.value() / 100;
d[13 + i * 3] = (v & 0xff) as u8;
d[14 + i * 3] = ((v >> 8) & 0xff) as u8;
d[15 + i * 3] = ((v >> 16) & 0xff) as u8;
});
Ok(self)
}
pub fn build(&mut self, key: &keys::AES128) -> Result<&[u8], &str> {
if !self.encrypted {
self.encrypt_payload(key);
}
Ok(self.data.as_mut())
}
fn encrypt_payload(&mut self, key: &keys::AES128) {
let d = if self.with_c_f_list {
self.data.as_mut()
} else {
&mut self.data.as_mut()[..17]
};
set_mic(d, key, &self.factory);
let aes_enc = self.factory.new_dec(key);
for i in 0..(d.len() >> 4) {
let start = (i << 4) + 1;
let tmp = GenericArray::from_mut_slice(&mut d[start..(16 + start)]);
aes_enc.decrypt_block(tmp);
}
self.encrypted = true;
}
}
#[cfg(feature = "default-crypto,with-downlink")]
impl JoinAcceptCreator<[u8; 33], DefaultFactory> {
pub fn new() -> Self {
let mut data = [0; 33];
data[0] = 0x20;
Self {
data,
with_c_f_list: false,
encrypted: false,
factory: DefaultFactory,
}
}
}
fn set_mic<F: CryptoFactory>(data: &mut [u8], key: &keys::AES128, factory: &F) {
let len = data.len();
let mic = securityhelpers::calculate_mic(&data[..len - 4], factory.new_mac(key));
data[len - 4..].copy_from_slice(&mic.0[..]);
}
#[derive(Default)]
pub struct JoinRequestCreator<D, F> {
data: D,
factory: F,
}
impl<D: AsMut<[u8]>, F: CryptoFactory> JoinRequestCreator<D, F> {
pub fn with_options<'a>(mut data: D, factory: F) -> Result<Self, &'a str> {
let d = data.as_mut();
if d.len() < 23 {
return Err("data slice is too short");
}
d[0] = 0x00;
Ok(Self { data, factory })
}
pub fn set_app_eui<H: AsRef<[u8]>, T: Into<parser::EUI64<H>>>(
&mut self,
app_eui: T,
) -> &mut Self {
let converted = app_eui.into();
self.data.as_mut()[1..9].copy_from_slice(converted.as_ref());
self
}
pub fn set_dev_eui<H: AsRef<[u8]>, T: Into<parser::EUI64<H>>>(
&mut self,
dev_eui: T,
) -> &mut Self {
let converted = dev_eui.into();
self.data.as_mut()[9..17].copy_from_slice(converted.as_ref());
self
}
pub fn set_dev_nonce<H: AsRef<[u8]>, T: Into<parser::DevNonce<H>>>(
&mut self,
dev_nonce: T,
) -> &mut Self {
let converted = dev_nonce.into();
self.data.as_mut()[17..19].copy_from_slice(converted.as_ref());
self
}
pub fn build(&mut self, key: &keys::AES128) -> Result<&[u8], &str> {
let d = self.data.as_mut();
set_mic(d, key, &self.factory);
Ok(d)
}
}
#[derive(Default)]
pub struct DataPayloadCreator<D, F> {
data: D,
data_f_port: Option<u8>,
fcnt: u32,
factory: F,
}
impl<D: AsMut<[u8]>, F: CryptoFactory + Default> DataPayloadCreator<D, F> {
pub fn with_options<'a>(mut data: D, factory: F) -> Result<Self, &'a str> {
let d = data.as_mut();
if d.len() < 255 {
return Err("data slice is too short");
}
d[0] = 0x40;
Ok(DataPayloadCreator {
data,
data_f_port: None,
fcnt: 0,
factory,
})
}
pub fn set_uplink(&mut self, uplink: bool) -> &mut Self {
if uplink {
self.data.as_mut()[0] &= 0xdf;
} else {
self.data.as_mut()[0] |= 0x20;
}
self
}
pub fn set_confirmed(&mut self, confirmed: bool) -> &mut Self {
let d = self.data.as_mut();
if confirmed {
d[0] &= 0xbf;
d[0] |= 0x80;
} else {
d[0] &= 0x7f;
d[0] |= 0x40;
}
self
}
pub fn set_dev_addr<H: AsRef<[u8]>, T: Into<parser::DevAddr<H>>>(
&mut self,
dev_addr: T,
) -> &mut Self {
let converted = dev_addr.into();
self.data.as_mut()[1..5].copy_from_slice(converted.as_ref());
self
}
pub fn set_fctrl(&mut self, fctrl: &parser::FCtrl) -> &mut Self {
self.data.as_mut()[5] = fctrl.raw_value();
self
}
pub fn set_fcnt(&mut self, fcnt: u32) -> &mut Self {
let d = self.data.as_mut();
self.fcnt = fcnt;
d[6] = (fcnt & (0xff_u32)) as u8;
d[7] = (fcnt >> 8) as u8;
self
}
pub fn set_f_port(&mut self, f_port: u8) -> &mut Self {
self.data_f_port = Some(f_port);
self
}
pub fn can_piggyback(cmds: &[&dyn SerializableMacCommand]) -> bool {
mac_commands_len(cmds) <= PIGGYBACK_MAC_COMMANDS_MAX_LEN
}
pub fn build<'a, 'b, 'c, 'd, 'e>(
&mut self,
payload: &[u8],
cmds: &'a [&'b dyn SerializableMacCommand],
nwk_skey: &'c keys::AES128,
app_skey: &'d keys::AES128,
) -> Result<&[u8], &'e str> {
let d = self.data.as_mut();
let mut last_filled = 8; let has_fport = self.data_f_port.is_some();
let has_fport_zero = has_fport && self.data_f_port.unwrap() == 0;
let mac_cmds_len = mac_commands_len(cmds);
if mac_cmds_len > PIGGYBACK_MAC_COMMANDS_MAX_LEN && !has_fport_zero {
return Err("mac commands are too big for FOpts");
}
let mut payload_len = payload.len();
if has_fport_zero && payload_len > 0 {
return Err("mac commands in payload can not be send together with payload");
}
if !has_fport && payload_len > 0 {
return Err("fport must be provided when there is FRMPayload");
}
if !has_fport_zero && mac_cmds_len > 0 {
d[5] |= mac_cmds_len as u8 & 0x0f;
maccommandcreator::build_mac_commands(
cmds,
&mut d[last_filled..last_filled + mac_cmds_len],
)
.unwrap();
last_filled += mac_cmds_len;
}
if has_fport {
d[last_filled] = self.data_f_port.unwrap();
last_filled += 1;
}
let mut enc_key = app_skey;
if mac_cmds_len > 0 && has_fport_zero {
enc_key = nwk_skey;
payload_len = mac_cmds_len;
maccommandcreator::build_mac_commands(
cmds,
&mut d[last_filled..last_filled + payload_len],
)
.unwrap();
} else {
d[last_filled..last_filled + payload_len].copy_from_slice(payload);
};
securityhelpers::encrypt_frm_data_payload(
d,
last_filled,
last_filled + payload_len,
self.fcnt,
&self.factory.new_enc(enc_key),
);
let mic = securityhelpers::calculate_data_mic(
&d[..last_filled + payload_len],
self.factory.new_mac(nwk_skey),
self.fcnt,
);
d[last_filled + payload_len..last_filled + payload_len + 4].copy_from_slice(&mic.0);
Ok(&d[..last_filled + payload_len + 4])
}
}
#[cfg(feature = "default-crypto")]
impl DataPayloadCreator<GenericArray<u8, U256>, DefaultFactory> {
pub fn new() -> Self {
let mut data: GenericArray<u8, U256> = GenericArray::default();
data[0] = 0x40;
Self {
data,
data_f_port: None,
fcnt: 0,
factory: DefaultFactory,
}
}
}