use std::io;
use byteorder::{BigEndian, WriteBytesExt};
use log::debug;
use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive};
use crate::errors::Result;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Packet {
pub version: Version,
pub tag: Tag,
pub body: Vec<u8>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PacketLength {
Fixed(usize),
Indeterminate,
Partial(usize),
}
impl From<usize> for PacketLength {
fn from(val: usize) -> PacketLength {
PacketLength::Fixed(val)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive, IntoPrimitive)]
#[repr(u8)]
pub enum Tag {
PublicKeyEncryptedSessionKey = 1,
Signature = 2,
SymKeyEncryptedSessionKey = 3,
OnePassSignature = 4,
SecretKey = 5,
PublicKey = 6,
SecretSubkey = 7,
CompressedData = 8,
SymEncryptedData = 9,
Marker = 10,
LiteralData = 11,
Trust = 12,
UserId = 13,
PublicSubkey = 14,
UserAttribute = 17,
SymEncryptedProtectedData = 18,
ModDetectionCode = 19,
Padding = 21,
#[num_enum(catch_all)]
Other(u8),
}
impl Tag {
pub fn encode(self) -> u8 {
let t: u8 = self.into();
0b1100_0000 | t
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, TryFromPrimitive)]
#[repr(u8)]
#[derive(Default)]
pub enum Version {
Old = 0,
#[default]
New = 1,
}
impl Version {
pub fn write_header(self, writer: &mut impl io::Write, tag: u8, len: usize) -> Result<()> {
debug!("write_header {:?} {} {}", self, tag, len);
match self {
Version::Old => {
if len < 256 {
writer.write_u8(0b1000_0000 | tag << 2)?;
writer.write_u8(len.try_into()?)?;
} else if len < 65536 {
writer.write_u8(0b1000_0001 | tag << 2)?;
writer.write_u16::<BigEndian>(len as u16)?;
} else {
writer.write_u8(0b1000_0010 | tag << 2)?;
writer.write_u32::<BigEndian>(len as u32)?;
}
}
Version::New => {
writer.write_u8(0b1100_0000 | tag)?;
if len < 192 {
writer.write_u8(len.try_into()?)?;
} else if len < 8384 {
writer.write_u8((((len - 192) >> 8) + 192) as u8)?;
writer.write_u8(((len - 192) & 0xFF) as u8)?;
} else {
writer.write_u8(255)?;
writer.write_u32::<BigEndian>(len as u32)?;
}
}
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive, IntoPrimitive)]
#[repr(u8)]
pub enum KeyVersion {
V2 = 2,
V3 = 3,
V4 = 4,
V5 = 5,
V6 = 6,
#[num_enum(catch_all)]
Other(u8),
}
impl KeyVersion {
pub const fn fingerprint_len(&self) -> Option<usize> {
match self {
KeyVersion::V2 | KeyVersion::V3 => Some(16), KeyVersion::V4 => Some(20), KeyVersion::V5 | KeyVersion::V6 => Some(32), KeyVersion::Other(_) => None,
}
}
}
impl Default for KeyVersion {
fn default() -> Self {
Self::V4
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive, IntoPrimitive)]
#[repr(u8)]
pub enum PkeskVersion {
V3 = 3,
V6 = 6,
#[num_enum(catch_all)]
Other(u8),
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive, IntoPrimitive)]
#[repr(u8)]
pub enum SkeskVersion {
V4 = 4,
V6 = 6,
#[num_enum(catch_all)]
Other(u8),
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn test_write_header() {
let mut buf = Vec::new();
Version::New
.write_header(&mut buf, Tag::UserAttribute.into(), 12875)
.unwrap();
assert_eq!(hex::encode(buf), "d1ff0000324b");
let mut buf = Vec::new();
Version::New
.write_header(&mut buf, Tag::Signature.into(), 302)
.unwrap();
assert_eq!(hex::encode(buf), "c2c06e");
let mut buf = Vec::new();
Version::New
.write_header(&mut buf, Tag::Signature.into(), 303)
.unwrap();
assert_eq!(hex::encode(buf), "c2c06f");
}
}