use std::{fmt, io};
use chrono::{SubsecRound, Utc};
use byteorder::{LittleEndian, WriteBytesExt};
use nom::bytes::streaming::take;
use nom::combinator::{map, map_parser, rest};
use nom::multi::length_data;
use nom::number::streaming::{be_u8, le_u16};
use nom::sequence::pair;
use crate::errors::{IResult, Result};
use crate::packet::{PacketTrait, Signature, SignatureConfigBuilder, SignatureType, Subpacket};
use crate::ser::Serialize;
use crate::types::{SecretKeyTrait, SignedUserAttribute, Tag, Version};
use crate::util::{packet_length, write_packet_length};
use super::SubpacketData;
#[derive(Clone, PartialEq, Eq)]
pub enum UserAttribute {
Image {
packet_version: Version,
header: Vec<u8>,
data: Vec<u8>,
},
Unknown {
packet_version: Version,
typ: u8,
data: Vec<u8>,
},
}
impl UserAttribute {
pub fn from_slice(packet_version: Version, input: &[u8]) -> Result<Self> {
let (_, pk) = parse(packet_version)(input)?;
Ok(pk)
}
pub fn to_u8(&self) -> u8 {
match *self {
UserAttribute::Image { .. } => 1,
UserAttribute::Unknown { typ, .. } => typ,
}
}
pub fn packet_len(&self) -> usize {
match self {
UserAttribute::Image { ref data, .. } => {
1 + 16 + data.len()
}
UserAttribute::Unknown { ref data, .. } => {
1 + data.len()
}
}
}
pub fn sign<F>(&self, key: &impl SecretKeyTrait, key_pw: F) -> Result<SignedUserAttribute>
where
F: FnOnce() -> String,
{
let config = SignatureConfigBuilder::default()
.typ(SignatureType::CertGeneric)
.pub_alg(key.algorithm())
.hashed_subpackets(vec![Subpacket::regular(
SubpacketData::SignatureCreationTime(Utc::now().trunc_subsecs(0)),
)])
.unhashed_subpackets(vec![Subpacket::regular(SubpacketData::Issuer(
key.key_id(),
))])
.build()?;
let sig = config.sign_certificate(key, key_pw, self.tag(), &self)?;
Ok(SignedUserAttribute::new(self.clone(), vec![sig]))
}
pub fn into_signed(self, sig: Signature) -> SignedUserAttribute {
SignedUserAttribute::new(self, vec![sig])
}
}
impl fmt::Display for UserAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UserAttribute::Image { data, .. } => {
write!(f, "User Attribute: Image (len: {})", data.len())
}
UserAttribute::Unknown { typ, data, .. } => {
write!(f, "User Attribute: typ: {} (len: {})", typ, data.len())
}
}
}
}
fn image(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], UserAttribute> {
move |i: &[u8]| {
map(
pair(
length_data(map(le_u16, |l| l - 2)),
rest,
),
|(header, img): (&[u8], &[u8])| UserAttribute::Image {
packet_version,
header: header.to_vec(),
data: img.to_vec(),
},
)(i)
}
}
fn parse(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], UserAttribute> {
move |i: &[u8]| {
let (i, len) = packet_length(i)?;
let (i, typ) = be_u8(i)?;
let (i, attr) = map_parser(take(len - 1), |i| match typ {
1 => image(packet_version)(i),
_ => map(rest, |data: &[u8]| UserAttribute::Unknown {
packet_version,
typ,
data: data.to_vec(),
})(i),
})(i)?;
Ok((i, {
debug!("attr with len {}", len);
attr
}))
}
}
impl Serialize for UserAttribute {
fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
debug!("write_packet_len {}", self.packet_len());
write_packet_length(self.packet_len(), writer)?;
match self {
UserAttribute::Image {
ref data,
ref header,
..
} => {
writer.write_all(&[0x01])?;
writer.write_u16::<LittleEndian>((header.len() + 2) as u16)?;
writer.write_all(header)?;
writer.write_all(data)?;
}
UserAttribute::Unknown { ref data, typ, .. } => {
writer.write_all(&[*typ])?;
writer.write_all(data)?;
}
}
Ok(())
}
}
impl fmt::Debug for UserAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UserAttribute::Image {
ref header,
ref data,
..
} => f
.debug_struct("UserAttribute::Image")
.field("header", &hex::encode(header))
.field("data", &hex::encode(data))
.finish(),
UserAttribute::Unknown { typ, ref data, .. } => f
.debug_struct("UserAttribute::Image")
.field("type", &hex::encode([*typ]))
.field("data", &hex::encode(data))
.finish(),
}
}
}
impl PacketTrait for UserAttribute {
fn packet_version(&self) -> Version {
match self {
UserAttribute::Image { packet_version, .. } => *packet_version,
UserAttribute::Unknown { packet_version, .. } => *packet_version,
}
}
fn tag(&self) -> Tag {
Tag::UserAttribute
}
}