use bytes::{BufMut, Bytes, BytesMut};
use super::DeserializeError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct KeyPrefix {
subsystem: u8,
version: u8,
}
pub const KEY_PREFIX_LEN: usize = 2;
impl KeyPrefix {
pub fn new(subsystem: u8, version: u8) -> Self {
assert!(subsystem > 0, "subsystem 0 is reserved");
assert!(version > 0, "key version 0 is reserved");
Self { subsystem, version }
}
pub fn subsystem(&self) -> u8 {
self.subsystem
}
pub fn version(&self) -> u8 {
self.version
}
pub fn from_bytes(data: &[u8]) -> Result<Self, DeserializeError> {
if data.len() < KEY_PREFIX_LEN {
return Err(DeserializeError {
message: format!(
"buffer too short for key prefix: need {} bytes, got {}",
KEY_PREFIX_LEN,
data.len()
),
});
}
let subsystem = validate_subsystem(data[0])?;
let version = validate_key_version(data[1])?;
Ok(Self { subsystem, version })
}
pub fn from_bytes_with_validation(
data: &[u8],
expected_subsystem: u8,
expected_version: u8,
) -> Result<Self, DeserializeError> {
let prefix = Self::from_bytes(data)?;
if prefix.subsystem != expected_subsystem {
return Err(DeserializeError {
message: format!(
"invalid subsystem: expected 0x{:02x}, got 0x{:02x}",
expected_subsystem, prefix.subsystem
),
});
}
if prefix.version != expected_version {
return Err(DeserializeError {
message: format!(
"invalid key version: expected 0x{:02x}, got 0x{:02x}",
expected_version, prefix.version
),
});
}
Ok(prefix)
}
pub fn to_bytes(&self) -> Bytes {
Bytes::from(vec![self.subsystem, self.version])
}
pub fn write_to(&self, buf: &mut BytesMut) {
buf.put_u8(self.subsystem);
buf.put_u8(self.version);
}
}
fn validate_key_version(byte: u8) -> Result<u8, DeserializeError> {
if byte == 0 {
return Err(DeserializeError {
message: format!(
"invalid key version: 0x{:02x} (version 0 is reserved)",
byte
),
});
}
Ok(byte)
}
fn validate_subsystem(byte: u8) -> Result<u8, DeserializeError> {
if byte == 0 {
return Err(DeserializeError {
message: format!(
"invalid subsystem: 0x{:02x} (subsystem 0 is reserved)",
byte
),
});
}
Ok(byte)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_create_key_prefix() {
let subsystem = 0x10;
let version = 0x01;
let prefix = KeyPrefix::new(subsystem, version);
assert_eq!(prefix.subsystem(), subsystem);
assert_eq!(prefix.version(), version);
}
#[test]
#[should_panic(expected = "subsystem 0 is reserved")]
fn should_panic_on_zero_subsystem() {
KeyPrefix::new(0, 0x01);
}
#[test]
#[should_panic(expected = "key version 0 is reserved")]
fn should_panic_on_zero_version() {
KeyPrefix::new(0x10, 0);
}
#[test]
fn should_parse_prefix_from_bytes() {
let bytes = [0x42, 0x17];
let key_prefix = KeyPrefix::from_bytes(&bytes).unwrap();
assert_eq!(key_prefix.subsystem(), 0x42);
assert_eq!(key_prefix.version(), 0x17);
}
#[test]
fn should_parse_prefix_ignoring_trailing_bytes() {
let bytes = [0x42, 0x17, 0x99, 0xAA, 0xBB];
let key_prefix = KeyPrefix::from_bytes(&bytes).unwrap();
assert_eq!(key_prefix.subsystem(), 0x42);
assert_eq!(key_prefix.version(), 0x17);
}
#[test]
fn should_reject_zero_subsystem_byte() {
let bytes = [0x00, 0x17];
let result = KeyPrefix::from_bytes(&bytes);
assert!(
matches!(result, Err(DeserializeError { message }) if message.contains("subsystem 0 is reserved"))
);
}
#[test]
fn should_reject_zero_version_byte() {
let bytes = [0x53, 0x00];
let result = KeyPrefix::from_bytes(&bytes);
assert!(
matches!(result, Err(DeserializeError { message }) if message.contains("version 0 is reserved"))
);
}
#[test]
fn should_write_and_read_key_prefix() {
let prefix = KeyPrefix::new(0x10, 0x01);
let mut buf = BytesMut::new();
prefix.write_to(&mut buf);
let parsed = KeyPrefix::from_bytes(&buf).unwrap();
assert_eq!(parsed, prefix);
}
#[test]
fn should_serialize_key_prefix_to_bytes() {
let prefix = KeyPrefix::new(0x10, 0x01);
let bytes = prefix.to_bytes();
assert_eq!(bytes.len(), KEY_PREFIX_LEN);
assert_eq!(bytes[0], 0x10);
assert_eq!(bytes[1], 0x01);
}
#[test]
fn should_parse_key_prefix_with_validation() {
let expected_subsystem = 0x10;
let expected_version = 0x01;
let data = [expected_subsystem, expected_version];
let prefix =
KeyPrefix::from_bytes_with_validation(&data, expected_subsystem, expected_version)
.unwrap();
assert_eq!(prefix.subsystem(), expected_subsystem);
assert_eq!(prefix.version(), expected_version);
}
#[test]
fn should_reject_wrong_subsystem() {
let data = [0x10, 0x01];
let result = KeyPrefix::from_bytes_with_validation(&data, 0x20, 0x01);
assert!(
matches!(result, Err(DeserializeError { message }) if message.contains("invalid subsystem"))
);
}
#[test]
fn should_reject_wrong_version() {
let data = [0x10, 0x02];
let result = KeyPrefix::from_bytes_with_validation(&data, 0x10, 0x01);
assert!(
matches!(result, Err(DeserializeError { message }) if message.contains("invalid key version"))
);
}
#[test]
fn should_reject_short_buffer() {
let data = [0x01];
let result = KeyPrefix::from_bytes(&data);
assert!(
matches!(result, Err(DeserializeError { message }) if message.contains("buffer too short"))
);
}
}