#![cfg_attr(not(feature = "std"), no_std)]
use crc16::{State, X_25};
const NETWORK_BYTES_IDX: usize = 0;
const NETWORK_BYTES_LENGTH: usize = network::DEFAULT.len();
#[cfg_attr(feature = "no_std", allow(dead_code))]
const HASH_RND_PART_IDX: usize = NETWORK_BYTES_IDX + NETWORK_BYTES_LENGTH; const HASH_RND_PART_LENGTH: usize = 3;
#[cfg_attr(feature = "no_std", allow(dead_code))]
const PREFIX_IDX: usize = HASH_RND_PART_IDX + HASH_RND_PART_LENGTH; const PREFIX_LENGTH: usize = 3;
const HASH_IDX: usize = NETWORK_BYTES_IDX + NETWORK_BYTES_LENGTH; const HASH_LENGTH: usize = HASH_RND_PART_LENGTH + PREFIX_LENGTH;
const PART_NUMBER_IDX: usize = HASH_IDX + HASH_LENGTH; const PART_NUMBER_LENGTH: usize = 1;
const TOTAL_COUNT_IDX: usize = PART_NUMBER_IDX + PART_NUMBER_LENGTH; const TOTAL_COUNT_LENGTH: usize = 1;
const LENGTH_IDX: usize = TOTAL_COUNT_IDX + TOTAL_COUNT_LENGTH; const LENGTH_LENGTH: usize = 1;
pub const MSG_TYPE_IDX: usize = LENGTH_IDX + LENGTH_LENGTH; const MSG_TYPE_LENGTH: usize = 1;
#[cfg_attr(feature = "no_std", allow(dead_code))]
const DATA_TYPE_IDX: usize = MSG_TYPE_IDX + MSG_TYPE_LENGTH; const DATA_TYPE_LENGTH: usize = 1;
const HEADER_LENGTH: usize = NETWORK_BYTES_LENGTH
+ HASH_LENGTH
+ PART_NUMBER_LENGTH
+ TOTAL_COUNT_LENGTH
+ LENGTH_LENGTH
+ MSG_TYPE_LENGTH
+ DATA_TYPE_LENGTH;
const CRC_LENGTH: usize = 2;
#[cfg(feature = "std")]
mod lib_impl;
pub mod network {
pub type Network = [u8; 2];
pub const DEFAULT: Network = [0xAA, 0xCC];
pub const TEST: Network = [0xCC, 0xAA];
}
#[cfg(feature = "std")]
pub use lib_impl::*;
#[cfg(feature = "no_std")]
pub type WirelessMessagePart = heapless::Vec<u8, MAX_LORA_MESSAGE_SIZE>;
#[cfg(feature = "std")]
pub type WirelessMessagePart = Vec<u8>;
pub const MAX_LORA_MESSAGE_SIZE: usize = 255;
#[derive(PartialEq, Clone)]
#[cfg_attr(feature = "std", derive(Debug))]
#[cfg_attr(feature = "no_std", derive(defmt::Format))]
pub enum MessageType {
Data,
Challenge,
Proof,
Flush,
Receipt,
Other,
}
impl From<u8> for MessageType {
fn from(n: u8) -> Self {
match n {
0x01 => Self::Data,
0x02 => Self::Challenge,
0x03 => Self::Proof,
0x04 => Self::Flush,
0x05 => Self::Receipt,
_ => Self::Other,
}
}
}
impl Into<u8> for MessageType {
fn into(self) -> u8 {
match self {
Self::Data => 0x01,
Self::Challenge => 0x02,
Self::Proof => 0x03,
Self::Flush => 0x04,
Self::Receipt => 0x05,
Self::Other => 0xff,
}
}
}
#[cfg_attr(feature = "std", derive(Debug, PartialEq))]
#[cfg_attr(feature = "no_std", derive(defmt::Format))]
pub enum ValidationError {
LessThanMinimalLength,
NetworkBytesMismatch,
PartNumHigherThanTotalCount,
IndicatedLenHigherThanMaxLen,
IndicatedLenDifferentFromActualLen,
IncorrectCrc(u16, u16),
}
pub fn is_valid_message(network: network::Network, msg: &[u8]) -> Result<(), ValidationError> {
if msg.len() < HEADER_LENGTH + 1 + CRC_LENGTH {
return Err(ValidationError::LessThanMinimalLength);
}
let network_actual = &msg[NETWORK_BYTES_IDX..NETWORK_BYTES_LENGTH];
if network_actual != network {
return Err(ValidationError::NetworkBytesMismatch);
}
let len = &msg[LENGTH_IDX];
let part_num = &msg[PART_NUMBER_IDX];
let total_count = &msg[TOTAL_COUNT_IDX];
if *part_num > *total_count {
return Err(ValidationError::PartNumHigherThanTotalCount);
}
let max_length = crate::MAX_LORA_MESSAGE_SIZE - HEADER_LENGTH - CRC_LENGTH;
if *len as usize > max_length {
return Err(ValidationError::IndicatedLenHigherThanMaxLen);
}
if *len as usize != msg.len() - HEADER_LENGTH - CRC_LENGTH {
return Err(ValidationError::IndicatedLenDifferentFromActualLen);
}
let i = msg.len() - CRC_LENGTH;
let expected_crc = &msg[i..];
let data = &msg[..i];
let actual_crc = State::<X_25>::calculate(data);
if actual_crc.to_be_bytes() != expected_crc {
return Err(ValidationError::IncorrectCrc(
u16::from_be_bytes([expected_crc[0], expected_crc[1]]),
u16::from_be(actual_crc),
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::is_valid_message;
use super::ValidationError;
#[test]
fn test_is_valid_message_missing_network_bytes() {
assert_eq!(
Err(ValidationError::NetworkBytesMismatch),
is_valid_message(
crate::network::DEFAULT,
&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x04, 0x01, 0x02
]
)
);
}
#[test]
fn test_is_valid_message_shorter_than_possible() {
assert_eq!(
Err(ValidationError::LessThanMinimalLength),
is_valid_message(
crate::network::DEFAULT,
&[0xff, 0xff, 0xff, 0xff, 0x04, 0x01, 0x02]
)
);
}
#[test]
fn test_is_valid_message_wrong_num() {
assert_eq!(
Err(ValidationError::PartNumHigherThanTotalCount),
is_valid_message(
crate::network::DEFAULT,
&[
0xAA, 0xCC, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0x01, 0x02, 0x01, 0x01,
0xff, 0xff, 0x4b, 0x8c ]
)
);
}
#[test]
fn test_is_valid_message_wrong_len() {
assert_eq!(
Err(ValidationError::IndicatedLenDifferentFromActualLen),
is_valid_message(
crate::network::DEFAULT,
&[
0xAA, 0xCC, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01,
0xff, 0xff, 0xfe, 0x2e ],
)
);
}
}