#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use super::identity::{verify_signature, DeviceIdentity};
pub const SIGNATURE_SIZE: usize = 64;
pub const MIN_WIRE_SIZE: usize = 1 + SIGNATURE_SIZE;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DecodedPayload<'a> {
pub marker: u8,
pub payload: &'a [u8],
pub signature: &'a [u8; 64],
}
pub struct SignedPayload;
impl SignedPayload {
pub fn encode(marker: u8, payload: &[u8], identity: &DeviceIdentity) -> Vec<u8> {
let mut to_sign = Vec::with_capacity(1 + payload.len());
to_sign.push(marker);
to_sign.extend_from_slice(payload);
let signature = identity.sign(&to_sign);
let mut wire = Vec::with_capacity(1 + payload.len() + SIGNATURE_SIZE);
wire.push(marker);
wire.extend_from_slice(payload);
wire.extend_from_slice(&signature);
wire
}
pub fn encode_with_signature(marker: u8, payload: &[u8], signature: &[u8; 64]) -> Vec<u8> {
let mut wire = Vec::with_capacity(1 + payload.len() + SIGNATURE_SIZE);
wire.push(marker);
wire.extend_from_slice(payload);
wire.extend_from_slice(signature);
wire
}
pub fn decode(wire: &[u8]) -> Option<DecodedPayload<'_>> {
if wire.len() < MIN_WIRE_SIZE {
return None;
}
let marker = wire[0];
let payload_end = wire.len() - SIGNATURE_SIZE;
let payload = &wire[1..payload_end];
let signature: &[u8; 64] = wire[payload_end..].try_into().ok()?;
Some(DecodedPayload {
marker,
payload,
signature,
})
}
pub fn verify(wire: &[u8], public_key: &[u8; 32]) -> bool {
let Some(decoded) = Self::decode(wire) else {
return false;
};
let signed_len = wire.len() - SIGNATURE_SIZE;
let to_verify = &wire[..signed_len];
verify_signature(public_key, to_verify, decoded.signature)
}
pub fn decode_verified<'a>(
wire: &'a [u8],
public_key: &[u8; 32],
) -> Option<DecodedPayload<'a>> {
if !Self::verify(wire, public_key) {
return None;
}
Self::decode(wire)
}
#[inline]
pub const fn payload_size(wire_size: usize) -> usize {
wire_size.saturating_sub(MIN_WIRE_SIZE)
}
#[inline]
pub const fn wire_size(payload_size: usize) -> usize {
1 + payload_size + SIGNATURE_SIZE
}
#[inline]
pub fn peek_marker(wire: &[u8]) -> Option<u8> {
wire.first().copied()
}
pub fn extract_signature(wire: &[u8]) -> Option<&[u8; 64]> {
if wire.len() < MIN_WIRE_SIZE {
return None;
}
let sig_start = wire.len() - SIGNATURE_SIZE;
wire[sig_start..].try_into().ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_decode_roundtrip() {
let identity = DeviceIdentity::generate();
let marker = 0xAF;
let payload = [0x01, 0x02, 0x03, 0x04, 0x05];
let wire = SignedPayload::encode(marker, &payload, &identity);
assert_eq!(wire.len(), SignedPayload::wire_size(payload.len()));
assert_eq!(wire.len(), 1 + 5 + 64);
let decoded = SignedPayload::decode(&wire).unwrap();
assert_eq!(decoded.marker, marker);
assert_eq!(decoded.payload, &payload);
}
#[test]
fn test_verify_valid_signature() {
let identity = DeviceIdentity::generate();
let marker = 0xAF;
let payload = b"Hello, mesh!";
let wire = SignedPayload::encode(marker, payload, &identity);
let pubkey = identity.public_key();
assert!(SignedPayload::verify(&wire, &pubkey));
}
#[test]
fn test_verify_wrong_pubkey() {
let identity1 = DeviceIdentity::generate();
let identity2 = DeviceIdentity::generate();
let wire = SignedPayload::encode(0xAF, b"test", &identity1);
let wrong_pubkey = identity2.public_key();
assert!(!SignedPayload::verify(&wire, &wrong_pubkey));
}
#[test]
fn test_verify_tampered_payload() {
let identity = DeviceIdentity::generate();
let mut wire = SignedPayload::encode(0xAF, b"original", &identity);
let pubkey = identity.public_key();
wire[1] ^= 0xFF;
assert!(!SignedPayload::verify(&wire, &pubkey));
}
#[test]
fn test_verify_tampered_marker() {
let identity = DeviceIdentity::generate();
let mut wire = SignedPayload::encode(0xAF, b"test", &identity);
let pubkey = identity.public_key();
wire[0] = 0xBF;
assert!(!SignedPayload::verify(&wire, &pubkey));
}
#[test]
fn test_decode_verified() {
let identity = DeviceIdentity::generate();
let marker = 0xAF;
let payload = b"verified content";
let wire = SignedPayload::encode(marker, payload, &identity);
let pubkey = identity.public_key();
let decoded = SignedPayload::decode_verified(&wire, &pubkey).unwrap();
assert_eq!(decoded.marker, marker);
assert_eq!(decoded.payload, payload);
}
#[test]
fn test_decode_verified_fails_bad_sig() {
let identity1 = DeviceIdentity::generate();
let identity2 = DeviceIdentity::generate();
let wire = SignedPayload::encode(0xAF, b"test", &identity1);
let wrong_pubkey = identity2.public_key();
assert!(SignedPayload::decode_verified(&wire, &wrong_pubkey).is_none());
}
#[test]
fn test_empty_payload() {
let identity = DeviceIdentity::generate();
let marker = 0x00;
let payload: &[u8] = &[];
let wire = SignedPayload::encode(marker, payload, &identity);
assert_eq!(wire.len(), MIN_WIRE_SIZE);
let decoded = SignedPayload::decode(&wire).unwrap();
assert_eq!(decoded.marker, marker);
assert!(decoded.payload.is_empty());
assert!(SignedPayload::verify(&wire, &identity.public_key()));
}
#[test]
fn test_peek_marker() {
let identity = DeviceIdentity::generate();
let wire = SignedPayload::encode(0xAB, b"test", &identity);
assert_eq!(SignedPayload::peek_marker(&wire), Some(0xAB));
assert_eq!(SignedPayload::peek_marker(&[]), None);
}
#[test]
fn test_extract_signature() {
let identity = DeviceIdentity::generate();
let wire = SignedPayload::encode(0xAF, b"test", &identity);
let sig = SignedPayload::extract_signature(&wire).unwrap();
assert_eq!(sig.len(), 64);
assert!(SignedPayload::extract_signature(&[0x01; 10]).is_none());
}
#[test]
fn test_encode_with_signature() {
let identity = DeviceIdentity::generate();
let marker = 0xAF;
let payload = b"external sig";
let mut to_sign = Vec::new();
to_sign.push(marker);
to_sign.extend_from_slice(payload);
let signature = identity.sign(&to_sign);
let wire = SignedPayload::encode_with_signature(marker, payload, &signature);
assert!(SignedPayload::verify(&wire, &identity.public_key()));
}
#[test]
fn test_wire_size_calculation() {
assert_eq!(SignedPayload::wire_size(0), 65);
assert_eq!(SignedPayload::wire_size(21), 86); assert_eq!(SignedPayload::wire_size(100), 165);
assert_eq!(SignedPayload::payload_size(65), 0);
assert_eq!(SignedPayload::payload_size(86), 21);
assert_eq!(SignedPayload::payload_size(165), 100);
}
#[test]
fn test_canned_message_size() {
let payload_size = 1 + 4 + 4 + 8 + 4; assert_eq!(SignedPayload::wire_size(payload_size), 86);
}
}