use panweave_codec::tlv::{TlvSet, write_tlv};
use panweave_codec::{CodecError, Writer};
use panweave_types::ExtendedAddress;
pub const TAG_METHOD: u8 = 0;
pub const TAG_P256_POINT: u8 = 1;
pub const TAG_CURVE25519_POINT: u8 = 2;
pub const TAG_KEY_SEQUENCE: u8 = 3;
pub const TAG_MAC_TAG: u8 = 4;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Method {
Curve25519AesMmo,
Curve25519Sha256,
P256Sha256,
}
impl Method {
pub const fn raw(self) -> u8 {
match self {
Method::Curve25519AesMmo => 1,
Method::Curve25519Sha256 => 2,
Method::P256Sha256 => 3,
}
}
pub const fn from_raw(v: u8) -> Option<Self> {
match v {
1 => Some(Method::Curve25519AesMmo),
2 => Some(Method::Curve25519Sha256),
3 => Some(Method::P256Sha256),
_ => None,
}
}
pub const fn point_tag(self) -> u8 {
match self {
Method::P256Sha256 => TAG_P256_POINT,
_ => TAG_CURVE25519_POINT,
}
}
pub const fn point_len(self) -> usize {
match self {
Method::P256Sha256 => 64,
_ => 32,
}
}
pub const fn mac_tag_len(self) -> usize {
match self {
Method::P256Sha256 | Method::Curve25519Sha256 => 32,
Method::Curve25519AesMmo => 16,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Psk {
SymmetricToken,
InstallCode,
Passcode,
BasicAuthorization,
AdminAuthorization,
Anonymous,
}
impl Psk {
pub const fn raw(self) -> u8 {
match self {
Psk::SymmetricToken => 0,
Psk::InstallCode => 1,
Psk::Passcode => 2,
Psk::BasicAuthorization => 3,
Psk::AdminAuthorization => 4,
Psk::Anonymous => 255,
}
}
pub const fn from_raw(v: u8) -> Option<Self> {
match v {
0 => Some(Psk::SymmetricToken),
1 => Some(Psk::InstallCode),
2 => Some(Psk::Passcode),
3 => Some(Psk::BasicAuthorization),
4 => Some(Psk::AdminAuthorization),
255 => Some(Psk::Anonymous),
_ => None,
}
}
pub const fn is_provisioning(self) -> bool {
matches!(self, Psk::Anonymous | Psk::InstallCode | Psk::Passcode)
}
}
pub fn write_method(w: &mut Writer<'_>, method: Method, psk: Psk) -> Result<(), CodecError> {
write_tlv(w, TAG_METHOD, &[method.raw(), psk.raw()])
}
pub fn method(set: &TlvSet<'_>) -> Option<(Method, Psk)> {
let v = set.find(TAG_METHOD)?.value;
match v {
[m, p, ..] => Some((Method::from_raw(*m)?, Psk::from_raw(*p)?)),
_ => None,
}
}
pub fn write_point(
w: &mut Writer<'_>,
tag: u8,
device: ExtendedAddress,
point: &[u8],
) -> Result<(), CodecError> {
let mut buf = [0u8; 72];
let n = 8 + point.len();
let too_small = || CodecError::BufferTooSmall {
needed: n,
available: 72,
};
let b = buf.get_mut(..n).ok_or_else(too_small)?;
b.get_mut(..8)
.ok_or_else(too_small)?
.copy_from_slice(&device.0.to_le_bytes());
b.get_mut(8..).ok_or_else(too_small)?.copy_from_slice(point);
write_tlv(w, tag, b)
}
pub fn point<'a>(set: &TlvSet<'a>, method: Method) -> Option<(ExtendedAddress, &'a [u8])> {
let v = set.find(method.point_tag())?.value;
if v.len() != 8 + method.point_len() {
return None;
}
let (a, p) = v.split_at(8);
let mut le = [0u8; 8];
le.copy_from_slice(a);
Some((ExtendedAddress(u64::from_le_bytes(le)), p))
}
pub fn write_key_sequence(w: &mut Writer<'_>, sequence: u8) -> Result<(), CodecError> {
write_tlv(w, TAG_KEY_SEQUENCE, &[sequence])
}
pub fn key_sequence(set: &TlvSet<'_>) -> Option<u8> {
set.find(TAG_KEY_SEQUENCE)?.value.first().copied()
}
pub fn write_mac_tag(w: &mut Writer<'_>, tag: &[u8]) -> Result<(), CodecError> {
write_tlv(w, TAG_MAC_TAG, tag)
}
pub fn mac_tag<'a>(set: &TlvSet<'a>) -> Option<&'a [u8]> {
set.find(TAG_MAC_TAG).map(|t| t.value)
}