use crate::payload_v3::errors::PayloadV3ParseError;
use crate::payload_v3::header::PayloadV3Header;
use crate::payload_v3::types::{
ExtensionEntry, V3_CORE_SIZE, V3_MAGIC, V3_MAX_EXTENSION_COUNT, V3_MAX_EXTENSION_SIZE,
V3_PAYLOAD_VERSION,
};
#[derive(Debug)]
pub enum ParsedPayload {
V1(V1Payload),
V2(V2Payload),
V3(V3Payload),
}
#[derive(Debug)]
pub struct V1Payload {
pub protection_level: u8,
pub seed: u64,
pub intensity: u16,
pub timestamp: u64,
}
#[derive(Debug)]
pub struct V2Payload {
pub protection_level: u8,
pub seed: u64,
pub intensity: u16,
pub timestamp: u64,
pub content_hash: [u8; 4],
pub dmi_value: u8,
pub flags: u8,
}
#[derive(Debug)]
pub struct V3Payload {
pub header: PayloadV3Header,
pub key_id: Vec<u8>,
pub extensions: Vec<ExtensionEntry>,
}
pub fn parse_payload(data: &[u8]) -> Result<ParsedPayload, PayloadV3ParseError> {
if data.len() >= 3
&& data[0] == V3_MAGIC[0]
&& data[1] == V3_MAGIC[1]
&& data[2] == V3_PAYLOAD_VERSION
{
return parse_v3(data);
}
if !data.is_empty() {
match data[0] {
2 => return parse_v2(data),
1 => return parse_v1(data),
_ => {}
}
}
if data.len() >= 2 {
return Err(PayloadV3ParseError::InvalidMagic([data[0], data[1]]));
}
Err(PayloadV3ParseError::TooShort {
min: 1,
actual: data.len(),
})
}
fn parse_v1(data: &[u8]) -> Result<ParsedPayload, PayloadV3ParseError> {
const V1_MIN_SIZE: usize = 24;
if data.len() < V1_MIN_SIZE {
return Err(PayloadV3ParseError::TooShort {
min: V1_MIN_SIZE,
actual: data.len(),
});
}
if data[0] != 1 {
return Err(PayloadV3ParseError::UnsupportedVersion(data[0]));
}
let protection_level = data[1];
let seed = u64::from_le_bytes([
data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9],
]);
let intensity = u16::from_le_bytes([data[10], data[11]]);
let timestamp = u64::from_le_bytes([
data[12], data[13], data[14], data[15], data[16], data[17], data[18], data[19],
]);
Ok(ParsedPayload::V1(V1Payload {
protection_level,
seed,
intensity,
timestamp,
}))
}
fn parse_v2(data: &[u8]) -> Result<ParsedPayload, PayloadV3ParseError> {
const V2_SIZE: usize = 32;
if data.len() < V2_SIZE {
return Err(PayloadV3ParseError::TooShort {
min: V2_SIZE,
actual: data.len(),
});
}
if data[0] != 2 {
return Err(PayloadV3ParseError::UnsupportedVersion(data[0]));
}
let protection_level = data[1];
let seed = u64::from_le_bytes([
data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9],
]);
let intensity = u16::from_le_bytes([data[10], data[11]]);
let timestamp = u64::from_le_bytes([
data[12], data[13], data[14], data[15], data[16], data[17], data[18], data[19],
]);
let mut content_hash = [0u8; 4];
content_hash.copy_from_slice(&data[20..24]);
let dmi_value = data[24];
let flags = data[25];
Ok(ParsedPayload::V2(V2Payload {
protection_level,
seed,
intensity,
timestamp,
content_hash,
dmi_value,
flags,
}))
}
fn parse_v3(data: &[u8]) -> Result<ParsedPayload, PayloadV3ParseError> {
let header = PayloadV3Header::from_bytes(data)?;
let key_id_start = V3_CORE_SIZE;
let key_id_end = key_id_start + header.key_id_len as usize;
if key_id_end > data.len() {
return Err(PayloadV3ParseError::TooShort {
min: key_id_end,
actual: data.len(),
});
}
let key_id = data[key_id_start..key_id_end].to_vec();
let mut extensions = Vec::new();
let ext_start = key_id_end;
let ext_end = header.header_length as usize;
if ext_end > data.len() {
return Err(PayloadV3ParseError::TooShort {
min: ext_end,
actual: data.len(),
});
}
if ext_end > ext_start {
let ext_data = &data[ext_start..ext_end];
extensions = parse_extensions(ext_data)?;
}
if header.total_length as usize > data.len() {
return Err(PayloadV3ParseError::TooShort {
min: header.total_length as usize,
actual: data.len(),
});
}
Ok(ParsedPayload::V3(V3Payload {
header,
key_id,
extensions,
}))
}
fn parse_extensions(data: &[u8]) -> Result<Vec<ExtensionEntry>, PayloadV3ParseError> {
let mut extensions = Vec::new();
let mut total_ext_size = 0usize;
let mut seen_types = [false; 256];
let mut offset = 0usize;
while offset + 4 <= data.len() {
let ext_type = u16::from_le_bytes([data[offset], data[offset + 1]]);
let ext_len = u16::from_le_bytes([data[offset + 2], data[offset + 3]]);
if ext_type == 0xFFFF {
break;
}
let ext_len = ext_len as usize;
if offset + 4 + ext_len > data.len() {
return Err(PayloadV3ParseError::ExtensionsTooLarge);
}
total_ext_size += 4 + ext_len;
if total_ext_size > V3_MAX_EXTENSION_SIZE {
return Err(PayloadV3ParseError::ExtensionsTooLarge);
}
if extensions.len() >= V3_MAX_EXTENSION_COUNT {
break;
}
if ext_type < 0x0100 {
let idx = ext_type as usize;
if idx < seen_types.len() && seen_types[idx] {
return Err(PayloadV3ParseError::DuplicateExtension(ext_type));
}
if idx < seen_types.len() {
seen_types[idx] = true;
}
}
let ext_data = data[offset + 4..offset + 4 + ext_len].to_vec();
extensions.push(ExtensionEntry {
extension_type: ext_type,
critical: crate::payload_v3::types::ExtensionType::is_critical(ext_type),
data: ext_data,
});
offset += 4 + ext_len;
}
Ok(extensions)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::payload_v3::types::V3_MAGIC;
#[test]
fn test_parse_v1_payload() {
let mut data = vec![1u8; 32];
data[0] = 1;
data[1] = 2;
data[2..10].copy_from_slice(&42u64.to_le_bytes());
data[10..12].copy_from_slice(&5000u16.to_le_bytes());
data[12..20].copy_from_slice(&12345u64.to_le_bytes());
let parsed = parse_payload(&data).unwrap();
match parsed {
ParsedPayload::V1(v1) => {
assert_eq!(v1.protection_level, 2);
assert_eq!(v1.seed, 42);
assert_eq!(v1.intensity, 5000);
}
_ => panic!("Expected V1"),
}
}
#[test]
fn test_parse_v2_payload() {
let mut data = vec![0u8; 32];
data[0] = 2;
data[1] = 2;
data[2..10].copy_from_slice(&99u64.to_le_bytes());
data[10..12].copy_from_slice(&7500u16.to_le_bytes());
data[12..20].copy_from_slice(&67890u64.to_le_bytes());
data[24] = 3;
data[25] = 0;
let parsed = parse_payload(&data).unwrap();
match parsed {
ParsedPayload::V2(v2) => {
assert_eq!(v2.protection_level, 2);
assert_eq!(v2.seed, 99);
assert_eq!(v2.intensity, 7500);
assert_eq!(v2.dmi_value, 3);
}
_ => panic!("Expected V2"),
}
}
#[test]
fn test_parse_v3_minimal() {
let mut data = vec![0u8; V3_CORE_SIZE];
data[0] = V3_MAGIC[0];
data[1] = V3_MAGIC[1];
data[2] = V3_PAYLOAD_VERSION;
data[3] = V3_CORE_SIZE as u8;
data[4..6].copy_from_slice(&(V3_CORE_SIZE as u16).to_le_bytes());
data[11..19].copy_from_slice(&42u64.to_le_bytes());
data[19..21].copy_from_slice(&5000u16.to_le_bytes());
let parsed = parse_payload(&data).unwrap();
match parsed {
ParsedPayload::V3(v3) => {
assert_eq!(v3.header.seed, 42);
assert_eq!(v3.header.intensity, 5000);
assert!(v3.extensions.is_empty());
assert!(v3.key_id.is_empty());
}
_ => panic!("Expected V3"),
}
}
#[test]
fn test_parse_v3_with_key_id() {
let key_id = vec![0xAA; 16];
let total_size = V3_CORE_SIZE + 16;
let mut data = vec![0u8; total_size];
data[0] = V3_MAGIC[0];
data[1] = V3_MAGIC[1];
data[2] = V3_PAYLOAD_VERSION;
data[3] = total_size as u8;
data[4..6].copy_from_slice(&(total_size as u16).to_le_bytes());
data[11..19].copy_from_slice(&42u64.to_le_bytes());
data[19..21].copy_from_slice(&5000u16.to_le_bytes());
data[31] = 16;
data[V3_CORE_SIZE..].copy_from_slice(&key_id);
let parsed = parse_payload(&data).unwrap();
match parsed {
ParsedPayload::V3(v3) => {
assert_eq!(v3.key_id, vec![0xAA; 16]);
assert_eq!(v3.header.key_id_len, 16);
}
_ => panic!("Expected V3"),
}
}
#[test]
fn test_parse_unknown_version() {
let data = vec![99u8; 32];
assert!(parse_payload(&data).is_err());
}
#[test]
fn test_parse_empty() {
assert!(parse_payload(&[]).is_err());
}
}