use super::keys::{CryptoFactory, Encrypter, AES128, MIC};
use super::maccommands::{parse_mac_commands, DLSettings, Frequency, MacCommandIterator};
use super::securityhelpers;
use super::securityhelpers::generic_array::GenericArray;
#[cfg(feature = "default-crypto")]
use super::default_crypto::DefaultFactory;
macro_rules! fixed_len_struct {
(
$(#[$outer:meta])*
struct $type:ident[$size:expr];
) => {
$(#[$outer])*
#[derive(Debug, Eq)]
pub struct $type<T: AsRef<[u8]>>(T);
impl<T: AsRef<[u8]>> $type<T> {
fn new_from_raw(bytes: T) -> $type<T> {
$type(bytes)
}
pub fn new(data: T) -> Option<$type<T>> {
let bytes = data.as_ref();
if bytes.len() != $size {
None
} else {
Some($type(data))
}
}
}
impl<T: AsRef<[u8]> + Clone> Clone for $type<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: AsRef<[u8]> + Copy> Copy for $type<T> {
}
impl<T: AsRef<[u8]>, V: AsRef<[u8]>> PartialEq<$type<T>> for $type<V> {
fn eq(&self, other: &$type<T>) -> bool {
self.as_ref() == other.as_ref()
}
}
impl<'a> From<&'a [u8; $size]> for $type<&'a [u8; $size]> {
fn from(v: &'a [u8; $size]) -> Self {
$type(v)
}
}
impl<T: AsRef<[u8]>> AsRef<[u8]> for $type<T> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: AsRef<[u8]>> $type<T> {
#[inline]
pub fn to_owned(&self) -> $type<[u8; $size]> {
let mut data = [0 as u8; $size];
data.copy_from_slice(self.0.as_ref());
$type(data)
}
}
impl<T: AsRef<[u8]> + Default> Default for $type<T> {
#[inline]
fn default() -> $type<T> {
$type(T::default())
}
}
};
}
#[derive(Debug, PartialEq)]
pub enum PhyPayload<T, F> {
JoinRequest(JoinRequestPayload<T, F>),
JoinAccept(JoinAcceptPayload<T, F>),
Data(DataPayload<T, F>),
}
impl<T: AsRef<[u8]>, F> AsRef<[u8]> for PhyPayload<T, F> {
fn as_ref(&self) -> &[u8] {
match self {
PhyPayload::JoinRequest(jr) => jr.as_bytes(),
PhyPayload::JoinAccept(ja) => ja.as_bytes(),
PhyPayload::Data(data) => data.as_bytes(),
}
}
}
#[derive(Debug, PartialEq)]
pub enum JoinAcceptPayload<T, F> {
Encrypted(EncryptedJoinAcceptPayload<T, F>),
Decrypted(DecryptedJoinAcceptPayload<T, F>),
}
impl<T: AsRef<[u8]>, F> AsPhyPayloadBytes for JoinAcceptPayload<T, F> {
fn as_bytes(&self) -> &[u8] {
match self {
JoinAcceptPayload::Encrypted(e) => e.as_bytes(),
JoinAcceptPayload::Decrypted(d) => d.as_bytes(),
}
}
}
#[derive(Debug, PartialEq)]
pub enum DataPayload<T, F> {
Encrypted(EncryptedDataPayload<T, F>),
Decrypted(DecryptedDataPayload<T>),
}
impl<T: AsRef<[u8]>, F> DataHeader for DataPayload<T, F> {
fn as_data_bytes(&self) -> &[u8] {
match self {
DataPayload::Encrypted(data) => data.as_data_bytes(),
DataPayload::Decrypted(data) => data.as_data_bytes(),
}
}
}
pub trait AsPhyPayloadBytes {
fn as_bytes(&self) -> &[u8];
}
impl AsRef<[u8]> for dyn AsPhyPayloadBytes {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
pub trait MICAble {
fn mic(&self) -> MIC;
}
impl<T: AsPhyPayloadBytes> MICAble for T {
fn mic(&self) -> MIC {
let data = self.as_bytes();
let len = data.len();
MIC([data[len - 4], data[len - 3], data[len - 2], data[len - 1]])
}
}
pub trait MHDRAble {
fn mhdr(&self) -> MHDR;
}
impl<T: AsPhyPayloadBytes> MHDRAble for T {
fn mhdr(&self) -> MHDR {
let data = self.as_bytes();
MHDR(data[0])
}
}
#[derive(Debug, PartialEq)]
pub struct JoinRequestPayload<T, F>(T, F);
impl<T: AsRef<[u8]>, F> AsPhyPayloadBytes for JoinRequestPayload<T, F> {
fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: AsRef<[u8]>, F: CryptoFactory> JoinRequestPayload<T, F> {
pub fn new_with_factory<'a>(data: T, factory: F) -> Result<Self, &'a str> {
if !Self::can_build_from(data.as_ref()) {
Err("can not build JoinRequestPayload from the provided data")
} else {
Ok(Self(data, factory))
}
}
fn can_build_from(bytes: &[u8]) -> bool {
bytes.len() == 23 && MHDR(bytes[0]).mtype() == MType::JoinRequest
}
pub fn app_eui(&self) -> EUI64<&[u8]> {
EUI64::new_from_raw(&self.0.as_ref()[1..9])
}
pub fn dev_eui(&self) -> EUI64<&[u8]> {
EUI64::new_from_raw(&self.0.as_ref()[9..17])
}
pub fn dev_nonce(&self) -> DevNonce<&[u8]> {
DevNonce::new_from_raw(&self.0.as_ref()[17..19])
}
pub fn validate_mic(&self, key: &AES128) -> bool {
self.mic() == self.calculate_mic(key)
}
fn calculate_mic(&self, key: &AES128) -> MIC {
let d = self.0.as_ref();
securityhelpers::calculate_mic(&d[..d.len() - 4], self.1.new_mac(key))
}
}
#[derive(Debug, PartialEq)]
pub struct EncryptedJoinAcceptPayload<T, F>(T, F);
impl<T: AsRef<[u8]>, F> AsPhyPayloadBytes for EncryptedJoinAcceptPayload<T, F> {
fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: AsRef<[u8]> + AsMut<[u8]>, F: CryptoFactory> EncryptedJoinAcceptPayload<T, F> {
pub fn new_with_factory<'a>(data: T, factory: F) -> Result<Self, &'a str> {
if Self::can_build_from(data.as_ref()) {
Ok(Self(data, factory))
} else {
Err("can not build EncryptedJoinAcceptPayload from the provided data")
}
}
fn can_build_from(bytes: &[u8]) -> bool {
(bytes.len() == 17 || bytes.len() == 33) && MHDR(bytes[0]).mtype() == MType::JoinAccept
}
pub fn decrypt(mut self, key: &AES128) -> DecryptedJoinAcceptPayload<T, F> {
{
let bytes = self.0.as_mut();
let len = bytes.len();
let aes_enc = self.1.new_enc(key);
for i in 0..(len >> 4) {
let start = (i << 4) + 1;
let block = GenericArray::from_mut_slice(&mut bytes[start..(start + 16)]);
aes_enc.encrypt_block(block);
}
}
DecryptedJoinAcceptPayload(self.0, self.1)
}
}
#[derive(Debug, PartialEq)]
pub struct DecryptedJoinAcceptPayload<T, F>(T, F);
impl<T: AsRef<[u8]>, F> AsPhyPayloadBytes for DecryptedJoinAcceptPayload<T, F> {
fn as_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: AsRef<[u8]>, F: CryptoFactory> DecryptedJoinAcceptPayload<T, F> {
pub fn validate_mic(&self, key: &AES128) -> bool {
self.mic() == self.calculate_mic(key)
}
pub fn calculate_mic(&self, key: &AES128) -> MIC {
let d = self.0.as_ref();
securityhelpers::calculate_mic(&d[..d.len() - 4], self.1.new_mac(key))
}
pub fn derive_newskey<TT: AsRef<[u8]>>(
&self,
dev_nonce: &DevNonce<TT>,
key: &AES128,
) -> AES128 {
self.derive_session_key(0x1, dev_nonce, key)
}
pub fn derive_appskey<TT: AsRef<[u8]>>(
&self,
dev_nonce: &DevNonce<TT>,
key: &AES128,
) -> AES128 {
self.derive_session_key(0x2, dev_nonce, key)
}
fn derive_session_key<TT: AsRef<[u8]>>(
&self,
first_byte: u8,
dev_nonce: &DevNonce<TT>,
key: &AES128,
) -> AES128 {
let cipher = self.1.new_enc(key);
let app_nonce = self.app_nonce();
let nwk_addr = self.net_id();
let (app_nonce_arr, nwk_addr_arr, dev_nonce_arr) =
(app_nonce.as_ref(), nwk_addr.as_ref(), dev_nonce.as_ref());
let mut block = [0u8; 16];
block[0] = first_byte;
block[1] = app_nonce_arr[0];
block[2] = app_nonce_arr[1];
block[3] = app_nonce_arr[2];
block[4] = nwk_addr_arr[0];
block[5] = nwk_addr_arr[1];
block[6] = nwk_addr_arr[2];
block[7] = dev_nonce_arr[0];
block[8] = dev_nonce_arr[1];
let mut input = GenericArray::clone_from_slice(&block);
cipher.encrypt_block(&mut input);
let mut output_key = [0u8; 16];
output_key.copy_from_slice(&input[0..16]);
AES128(output_key)
}
}
impl<T: AsRef<[u8]>, F> DecryptedJoinAcceptPayload<T, F> {
pub fn app_nonce(&self) -> AppNonce<&[u8]> {
AppNonce::new_from_raw(&self.0.as_ref()[1..4])
}
pub fn net_id(&self) -> NwkAddr<&[u8]> {
NwkAddr::new_from_raw(&self.0.as_ref()[4..7])
}
pub fn dev_addr(&self) -> DevAddr<&[u8]> {
DevAddr::new_from_raw(&self.0.as_ref()[7..11])
}
pub fn dl_settings(&self) -> DLSettings {
DLSettings::new(self.0.as_ref()[11])
}
pub fn rx_delay(&self) -> u8 {
self.0.as_ref()[12] & 0x0f
}
pub fn c_f_list(&self) -> Option<[Frequency; 5]> {
if self.0.as_ref().len() == 17 {
return None;
}
let d = self.0.as_ref();
let res = [
Frequency::new_from_raw(&d[13..16]),
Frequency::new_from_raw(&d[16..19]),
Frequency::new_from_raw(&d[19..22]),
Frequency::new_from_raw(&d[22..25]),
Frequency::new_from_raw(&d[25..28]),
];
Some(res)
}
}
impl<T: AsRef<[u8]> + AsMut<[u8]>, F: CryptoFactory> DecryptedJoinAcceptPayload<T, F> {
pub fn new_with_factory<'a, 'b>(data: T, key: &'a AES128, factory: F) -> Result<Self, &'b str> {
let t = EncryptedJoinAcceptPayload::new_with_factory(data, factory)?;
let res = t.decrypt(key);
if res.validate_mic(key) {
Ok(res)
} else {
Err("MIC did not match")
}
}
}
pub trait DataHeader {
fn as_data_bytes(&self) -> &[u8];
fn fhdr(&self) -> FHDR {
FHDR::new_from_raw(
&self.as_data_bytes()[1..(1 + self.fhdr_length())],
self.is_uplink(),
)
}
fn is_uplink(&self) -> bool {
let mtype = MHDR(self.as_data_bytes()[0]).mtype();
mtype == MType::UnconfirmedDataUp || mtype == MType::ConfirmedDataUp
}
fn f_port(&self) -> Option<u8> {
let fhdr_length = self.fhdr_length();
let data = self.as_data_bytes();
if fhdr_length + 1 >= data.len() - 5 {
return None;
}
Some(data[1 + fhdr_length])
}
fn fhdr_length(&self) -> usize {
fhdr_length(self.as_data_bytes()[5])
}
}
fn fhdr_length(b: u8) -> usize {
7 + (b & 0x0f) as usize
}
impl<T: DataHeader> AsPhyPayloadBytes for T {
fn as_bytes(&self) -> &[u8] {
self.as_data_bytes()
}
}
#[derive(Debug, PartialEq)]
pub struct EncryptedDataPayload<T, F>(T, F);
impl<T: AsRef<[u8]>, F> DataHeader for EncryptedDataPayload<T, F> {
fn as_data_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: AsRef<[u8]>, F: CryptoFactory> EncryptedDataPayload<T, F> {
pub fn new_with_factory<'a>(data: T, factory: F) -> Result<Self, &'a str> {
if Self::can_build_from(data.as_ref()) {
Ok(Self(data, factory))
} else {
Err("can not build EncryptedDataPayload from the provided data")
}
}
fn can_build_from(bytes: &[u8]) -> bool {
let has_acceptable_len = bytes.len() >= 12 &&
fhdr_length(bytes[5]) <= bytes.len();
if !has_acceptable_len {
return false;
}
matches!(
MHDR(bytes[0]).mtype(),
MType::ConfirmedDataUp
| MType::ConfirmedDataDown
| MType::UnconfirmedDataUp
| MType::UnconfirmedDataDown
)
}
pub fn validate_mic(&self, key: &AES128, fcnt: u32) -> bool {
self.mic() == self.calculate_mic(key, fcnt)
}
fn calculate_mic(&self, key: &AES128, fcnt: u32) -> MIC {
let d = self.0.as_ref();
securityhelpers::calculate_data_mic(&d[..d.len() - 4], self.1.new_mac(key), fcnt)
}
}
impl<T: AsRef<[u8]> + AsMut<[u8]>, F: CryptoFactory> EncryptedDataPayload<T, F> {
pub fn decrypt<'a, 'b>(
mut self,
nwk_skey: Option<&'a AES128>,
app_skey: Option<&'a AES128>,
fcnt: u32,
) -> Result<DecryptedDataPayload<T>, &'b str> {
let fhdr_length = self.fhdr_length();
let fhdr = self.fhdr();
let full_fcnt = compute_fcnt(fcnt, fhdr.fcnt());
let key = if self.f_port().is_some() && self.f_port().unwrap() != 0 {
app_skey
} else {
nwk_skey
};
if key.is_none() {
return Err("key needed to decrypt the frm data payload was None");
}
let data = self.0.as_mut();
let len = data.len();
let start = 1 + fhdr_length + 1;
let end = len - 4;
if start < end {
securityhelpers::encrypt_frm_data_payload(
&mut data[..],
start,
end,
full_fcnt,
&self.1.new_enc(&key.unwrap()),
);
}
Ok(DecryptedDataPayload(self.0))
}
pub fn decrypt_if_mic_ok<'a>(
self,
nwk_skey: &'a AES128,
app_skey: &'a AES128,
fcnt: u32,
) -> Result<DecryptedDataPayload<T>, Self> {
if !self.validate_mic(nwk_skey, fcnt) {
Err(self)
} else {
Ok(self.decrypt(Some(nwk_skey), Some(app_skey), fcnt).unwrap())
}
}
}
fn compute_fcnt(old_fcnt: u32, fcnt: u16) -> u32 {
((old_fcnt >> 16) << 16) ^ u32::from(fcnt)
}
#[derive(Debug, PartialEq)]
pub struct DecryptedDataPayload<T>(T);
impl<T: AsRef<[u8]>> DataHeader for DecryptedDataPayload<T> {
fn as_data_bytes(&self) -> &[u8] {
self.0.as_ref()
}
}
impl<T: AsRef<[u8]>> DecryptedDataPayload<T> {
pub fn frm_payload(&self) -> Result<FRMPayload, &str> {
let data = self.as_data_bytes();
let len = data.len();
let fhdr_length = self.fhdr_length();
if len < fhdr_length + 6 {
Ok(FRMPayload::None)
} else if self.f_port() != Some(0) {
Ok(FRMPayload::Data(&data[(1 + fhdr_length + 1)..(len - 4)]))
} else {
Ok(FRMPayload::MACCommands(FRMMacCommands::new(
&data[(1 + fhdr_length + 1)..(len - 4)],
self.is_uplink(),
)))
}
}
}
#[cfg(feature = "default-crypto")]
pub fn parse<'a, T: AsRef<[u8]> + AsMut<[u8]>>(
data: T,
) -> Result<PhyPayload<T, DefaultFactory>, &'a str> {
parse_with_factory(data, DefaultFactory)
}
pub fn parse_with_factory<'a, T, F>(data: T, factory: F) -> Result<PhyPayload<T, F>, &'a str>
where
T: AsRef<[u8]> + AsMut<[u8]>,
F: CryptoFactory,
{
let bytes = data.as_ref();
let len = bytes.len();
if len < 12 {
return Err("insufficient number of bytes");
}
match MHDR(bytes[0]).mtype() {
MType::JoinRequest => Ok(PhyPayload::JoinRequest(
JoinRequestPayload::new_with_factory(data, factory)?,
)),
MType::JoinAccept => Ok(PhyPayload::JoinAccept(JoinAcceptPayload::Encrypted(
EncryptedJoinAcceptPayload::new_with_factory(data, factory)?,
))),
MType::UnconfirmedDataUp
| MType::ConfirmedDataUp
| MType::UnconfirmedDataDown
| MType::ConfirmedDataDown => Ok(PhyPayload::Data(DataPayload::Encrypted(
EncryptedDataPayload::new_with_factory(data, factory)?,
))),
_ => Err("unsupported message type"),
}
}
#[derive(Debug, PartialEq)]
pub struct MHDR(u8);
impl MHDR {
pub fn new(byte: u8) -> MHDR {
MHDR(byte)
}
pub fn mtype(&self) -> MType {
match self.0 >> 5 {
0 => MType::JoinRequest,
1 => MType::JoinAccept,
2 => MType::UnconfirmedDataUp,
3 => MType::UnconfirmedDataDown,
4 => MType::ConfirmedDataUp,
5 => MType::ConfirmedDataDown,
6 => MType::RFU,
_ => MType::Proprietary,
}
}
pub fn major(&self) -> Major {
if self.0.trailing_zeros() >= 2 {
Major::LoRaWANR1
} else {
Major::RFU
}
}
}
impl From<u8> for MHDR {
fn from(v: u8) -> Self {
MHDR(v)
}
}
#[derive(Debug, PartialEq)]
pub enum MType {
JoinRequest,
JoinAccept,
UnconfirmedDataUp,
UnconfirmedDataDown,
ConfirmedDataUp,
ConfirmedDataDown,
RFU,
Proprietary,
}
#[derive(Debug, PartialEq)]
pub enum Major {
LoRaWANR1,
RFU,
}
fixed_len_struct! {
struct EUI64[8];
}
fixed_len_struct! {
struct DevNonce[2];
}
fixed_len_struct! {
struct AppNonce[3];
}
fixed_len_struct! {
struct DevAddr[4];
}
#[allow(clippy::should_implement_trait)]
impl<T: AsRef<[u8]>> DevAddr<T> {
pub fn nwk_id(&self) -> u8 {
self.0.as_ref()[0] >> 1
}
pub fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
fixed_len_struct! {
struct NwkAddr[3];
}
#[derive(Debug, PartialEq)]
pub struct FHDR<'a>(&'a [u8], bool);
impl<'a> FHDR<'a> {
pub fn new_from_raw(bytes: &'a [u8], uplink: bool) -> FHDR {
FHDR(bytes, uplink)
}
pub fn new(bytes: &'a [u8], uplink: bool) -> Option<FHDR> {
let data_len = bytes.len();
if data_len < 7 {
return None;
}
if data_len < fhdr_length(bytes[4]) {
return None;
}
Some(FHDR(bytes, uplink))
}
pub fn dev_addr(&self) -> DevAddr<&'a [u8]> {
DevAddr::new_from_raw(&self.0[0..4])
}
pub fn fctrl(&self) -> FCtrl {
FCtrl(self.0[4], self.1)
}
pub fn fcnt(&self) -> u16 {
(u16::from(self.0[6]) << 8) | u16::from(self.0[5])
}
pub fn fopts(&self) -> MacCommandIterator {
let f_opts_len = FCtrl(self.0[4], self.1).f_opts_len();
parse_mac_commands(&self.0[7 as usize..(7 + f_opts_len) as usize], self.1)
}
}
#[derive(Debug, PartialEq)]
pub struct FCtrl(u8, bool);
impl FCtrl {
pub fn new(bytes: u8, uplink: bool) -> FCtrl {
FCtrl(bytes, uplink)
}
pub fn adr(&self) -> bool {
self.0 >> 7 == 1
}
pub fn adr_ack_req(&self) -> bool {
self.1 && self.0 & (1 << 6) != 0
}
pub fn ack(&self) -> bool {
self.0 & (1 << 5) != 0
}
pub fn f_pending(&self) -> bool {
!self.1 && self.0 & (1 << 4) != 0
}
pub fn f_opts_len(&self) -> u8 {
self.0 & 0x0f
}
pub fn raw_value(&self) -> u8 {
self.0
}
}
#[derive(Debug, PartialEq)]
pub enum FRMPayload<'a> {
Data(&'a [u8]),
MACCommands(FRMMacCommands<'a>),
None,
}
#[derive(Debug, PartialEq)]
pub struct FRMMacCommands<'a>(bool, &'a [u8]);
impl<'a> FRMMacCommands<'a> {
pub fn new(bytes: &'a [u8], uplink: bool) -> Self {
FRMMacCommands(uplink, bytes)
}
pub fn mac_commands(&self) -> MacCommandIterator {
parse_mac_commands(self.1, self.0)
}
}