use alloc::vec::Vec;
use broadcast_common::{Parse, Serialize};
use crate::error::{Error, Result};
pub const UNIVERSAL_LABEL_LEN: usize = 16;
pub type UniversalLabel = [u8; UNIVERSAL_LABEL_LEN];
pub const UAS_LS_KEY: UniversalLabel = [
0x06, 0x0E, 0x2B, 0x34, 0x02, 0x0B, 0x01, 0x01, 0x0E, 0x01, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00,
];
const BER_LONG_FORM_FLAG: u8 = 0x80;
const BER_LOW7_MASK: u8 = 0x7F;
const BER_OID_CONTINUATION: u8 = 0x80;
pub const TAG_CHECKSUM: u32 = 1;
pub const CHECKSUM_LEN: usize = 2;
pub const TAG_PRECISION_TIMESTAMP: u32 = 2;
pub const PRECISION_TIMESTAMP_LEN: usize = 8;
const CRC16_CCITT_POLY: u16 = 0x1021;
const CRC16_CCITT_INIT: u16 = 0xFFFF;
pub fn encode_ber_length(len: usize) -> Vec<u8> {
if len < BER_LONG_FORM_FLAG as usize {
return alloc::vec![len as u8];
}
let be = (len as u64).to_be_bytes();
let first = be.iter().position(|&b| b != 0).unwrap_or(be.len() - 1);
let value_bytes = &be[first..];
let mut out = Vec::with_capacity(1 + value_bytes.len());
out.push(BER_LONG_FORM_FLAG | (value_bytes.len() as u8));
out.extend_from_slice(value_bytes);
out
}
pub fn ber_length(bytes: &[u8]) -> Result<(usize, usize)> {
let first = *bytes.first().ok_or(Error::BufferTooShort {
need: 1,
have: 0,
what: "BER length first byte",
})?;
if first & BER_LONG_FORM_FLAG == 0 {
return Ok((first as usize, 1));
}
let n = (first & BER_LOW7_MASK) as usize;
if n == 0 {
return Err(Error::InvalidValue {
field: "ber_length",
value: first as u64,
reason: "indefinite BER length form is not permitted in KLV",
});
}
if n > core::mem::size_of::<usize>() {
return Err(Error::InvalidValue {
field: "ber_length",
value: n as u64,
reason: "BER long-form length exceeds usize width",
});
}
if bytes.len() < 1 + n {
return Err(Error::BufferTooShort {
need: 1 + n,
have: bytes.len(),
what: "BER long-form length bytes",
});
}
let mut value: usize = 0;
for &b in &bytes[1..1 + n] {
value = (value << 8) | b as usize;
}
Ok((value, 1 + n))
}
pub fn encode_ber_oid(tag: u32) -> Vec<u8> {
if tag < BER_OID_CONTINUATION as u32 {
return alloc::vec![tag as u8];
}
let mut digits: Vec<u8> = Vec::new();
let mut v = tag;
while v > 0 {
digits.push((v & BER_LOW7_MASK as u32) as u8);
v >>= 7;
}
digits.reverse();
let last = digits.len() - 1;
for (i, d) in digits.iter_mut().enumerate() {
if i != last {
*d |= BER_OID_CONTINUATION;
}
}
digits
}
pub fn ber_oid(bytes: &[u8]) -> Result<(u32, usize)> {
let mut value: u32 = 0;
let mut consumed = 0;
loop {
let b = *bytes.get(consumed).ok_or(Error::BufferTooShort {
need: consumed + 1,
have: bytes.len(),
what: "BER-OID tag byte",
})?;
if value > (u32::MAX >> 7) {
return Err(Error::InvalidValue {
field: "ber_oid",
value: value as u64,
reason: "BER-OID tag exceeds u32 range",
});
}
value = (value << 7) | (b & BER_LOW7_MASK) as u32;
consumed += 1;
if b & BER_OID_CONTINUATION == 0 {
break;
}
}
Ok((value, consumed))
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct KlvItem {
pub key: UniversalLabel,
pub value: Vec<u8>,
}
impl KlvItem {
pub fn new(key: UniversalLabel, value: Vec<u8>) -> Self {
Self { key, value }
}
}
impl<'a> Parse<'a> for KlvItem {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
if bytes.len() < UNIVERSAL_LABEL_LEN {
return Err(Error::BufferTooShort {
need: UNIVERSAL_LABEL_LEN,
have: bytes.len(),
what: "KLV Universal Label key",
});
}
let mut key: UniversalLabel = [0u8; UNIVERSAL_LABEL_LEN];
key.copy_from_slice(&bytes[..UNIVERSAL_LABEL_LEN]);
let (len, consumed) = ber_length(&bytes[UNIVERSAL_LABEL_LEN..])?;
let value_start = UNIVERSAL_LABEL_LEN + consumed;
let value_end = value_start + len;
if bytes.len() < value_end {
return Err(Error::BufferTooShort {
need: value_end,
have: bytes.len(),
what: "KLV item value",
});
}
Ok(Self {
key,
value: bytes[value_start..value_end].to_vec(),
})
}
}
impl Serialize for KlvItem {
type Error = Error;
fn serialized_len(&self) -> usize {
UNIVERSAL_LABEL_LEN + encode_ber_length(self.value.len()).len() + self.value.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let need = self.serialized_len();
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
let mut off = 0;
buf[off..off + UNIVERSAL_LABEL_LEN].copy_from_slice(&self.key);
off += UNIVERSAL_LABEL_LEN;
let ber = encode_ber_length(self.value.len());
buf[off..off + ber.len()].copy_from_slice(&ber);
off += ber.len();
buf[off..off + self.value.len()].copy_from_slice(&self.value);
off += self.value.len();
Ok(off)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct LocalSetItem {
pub tag: u32,
pub value: Vec<u8>,
}
impl LocalSetItem {
pub fn new(tag: u32, value: Vec<u8>) -> Self {
Self { tag, value }
}
fn encoded_len(&self) -> usize {
encode_ber_oid(self.tag).len()
+ encode_ber_length(self.value.len()).len()
+ self.value.len()
}
fn encode_into(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&encode_ber_oid(self.tag));
out.extend_from_slice(&encode_ber_length(self.value.len()));
out.extend_from_slice(&self.value);
}
}
fn parse_local_set_items(mut body: &[u8]) -> Result<Vec<LocalSetItem>> {
let mut items = Vec::new();
while !body.is_empty() {
let (tag, tag_len) = ber_oid(body)?;
let (val_len, len_len) = ber_length(&body[tag_len..])?;
let value_start = tag_len + len_len;
let value_end = value_start + val_len;
if body.len() < value_end {
return Err(Error::BufferTooShort {
need: value_end,
have: body.len(),
what: "KLV Local Set item value",
});
}
items.push(LocalSetItem {
tag,
value: body[value_start..value_end].to_vec(),
});
body = &body[value_end..];
}
Ok(items)
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct UasLocalSet {
pub items: Vec<LocalSetItem>,
}
impl UasLocalSet {
pub fn new() -> Self {
Self { items: Vec::new() }
}
pub fn from_items(items: Vec<LocalSetItem>) -> Self {
Self { items }
}
pub fn precision_timestamp(&self) -> Option<u64> {
let item = self
.items
.iter()
.find(|i| i.tag == TAG_PRECISION_TIMESTAMP)?;
if item.value.len() != PRECISION_TIMESTAMP_LEN {
return None;
}
let mut b = [0u8; PRECISION_TIMESTAMP_LEN];
b.copy_from_slice(&item.value);
Some(u64::from_be_bytes(b))
}
pub fn stored_checksum(&self) -> Option<u16> {
let item = self.items.iter().find(|i| i.tag == TAG_CHECKSUM)?;
if item.value.len() != CHECKSUM_LEN {
return None;
}
Some(u16::from_be_bytes([item.value[0], item.value[1]]))
}
fn value_without_checksum(&self) -> Vec<u8> {
let mut out = Vec::new();
for item in &self.items {
if item.tag == TAG_CHECKSUM {
continue;
}
item.encode_into(&mut out);
}
out
}
pub fn serialize_with_checksum(&self) -> Vec<u8> {
let mut value = self.value_without_checksum();
value.extend_from_slice(&encode_ber_oid(TAG_CHECKSUM));
value.extend_from_slice(&encode_ber_length(CHECKSUM_LEN));
let value_total = value.len() + CHECKSUM_LEN;
let mut packet = Vec::with_capacity(UNIVERSAL_LABEL_LEN + 4 + value_total);
packet.extend_from_slice(&UAS_LS_KEY);
packet.extend_from_slice(&encode_ber_length(value_total));
packet.extend_from_slice(&value);
let crc = crc16_ccitt(&packet);
packet.extend_from_slice(&crc.to_be_bytes());
packet
}
pub fn verify_checksum(packet: &[u8]) -> Result<bool> {
if packet.len() < UNIVERSAL_LABEL_LEN + 1 + CHECKSUM_LEN {
return Err(Error::BufferTooShort {
need: UNIVERSAL_LABEL_LEN + 1 + CHECKSUM_LEN,
have: packet.len(),
what: "UAS Local Set checksum",
});
}
let split = packet.len() - CHECKSUM_LEN;
let expected = crc16_ccitt(&packet[..split]);
let actual = u16::from_be_bytes([packet[split], packet[split + 1]]);
Ok(expected == actual)
}
}
impl Default for UasLocalSet {
fn default() -> Self {
Self::new()
}
}
impl<'a> Parse<'a> for UasLocalSet {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let item = KlvItem::parse(bytes)?;
if item.key != UAS_LS_KEY {
return Err(Error::InvalidValue {
field: "uas_ls_key",
value: item.key[0] as u64,
reason: "not the MISB ST 0601 UAS Datalink Local Set Universal Label",
});
}
let items = parse_local_set_items(&item.value)?;
Ok(Self { items })
}
}
impl Serialize for UasLocalSet {
type Error = Error;
fn serialized_len(&self) -> usize {
let value_len: usize = self
.items
.iter()
.filter(|i| i.tag != TAG_CHECKSUM)
.map(|i| i.encoded_len())
.sum::<usize>()
+ encode_ber_oid(TAG_CHECKSUM).len()
+ encode_ber_length(CHECKSUM_LEN).len()
+ CHECKSUM_LEN;
UNIVERSAL_LABEL_LEN + encode_ber_length(value_len).len() + value_len
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
let bytes = self.serialize_with_checksum();
if buf.len() < bytes.len() {
return Err(Error::OutputBufferTooSmall {
need: bytes.len(),
have: buf.len(),
});
}
buf[..bytes.len()].copy_from_slice(&bytes);
Ok(bytes.len())
}
}
pub fn crc16_ccitt(data: &[u8]) -> u16 {
let mut crc = CRC16_CCITT_INIT;
for &byte in data {
crc ^= (byte as u16) << 8;
for _ in 0..8 {
if crc & 0x8000 != 0 {
crc = (crc << 1) ^ CRC16_CCITT_POLY;
} else {
crc <<= 1;
}
}
}
crc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crc16_ccitt_known_answer() {
assert_eq!(crc16_ccitt(b"123456789"), 0x29B1);
}
#[test]
fn ber_short_form() {
assert_eq!(encode_ber_length(5), alloc::vec![5]);
assert_eq!(ber_length(&[5, 0xAA]).unwrap(), (5, 1));
}
#[test]
fn ber_long_form_300() {
assert_eq!(encode_ber_length(300), alloc::vec![0x82, 0x01, 0x2C]);
assert_eq!(ber_length(&[0x82, 0x01, 0x2C]).unwrap(), (300, 3));
}
#[test]
fn ber_oid_round_trip() {
for tag in [1u32, 2, 127, 128, 300, 16_383] {
let enc = encode_ber_oid(tag);
assert_eq!(ber_oid(&enc).unwrap(), (tag, enc.len()));
}
}
}