cmail_rpgp/packet/
user_attribute.rs

1use std::{fmt, io};
2
3use aes_gcm::aead::rand_core::CryptoRng;
4use byteorder::{LittleEndian, WriteBytesExt};
5use chrono::{SubsecRound, Utc};
6use log::debug;
7use nom::bytes::streaming::take;
8use nom::combinator::{map, map_parser, rest};
9use nom::number::streaming::{be_u8, le_u16};
10use rand::Rng;
11
12use crate::errors::{IResult, Result};
13use crate::packet::{
14    PacketTrait, Signature, SignatureConfig, SignatureType, Subpacket, SubpacketData,
15};
16use crate::ser::Serialize;
17use crate::types::{KeyVersion, PublicKeyTrait, SecretKeyTrait, SignedUserAttribute, Tag, Version};
18use crate::util::{packet_length, write_packet_length};
19
20/// User Attribute Packet
21/// <https://www.rfc-editor.org/rfc/rfc9580.html#name-user-attribute-packet-type->
22#[derive(Clone, PartialEq, Eq, derive_more::Debug)]
23pub enum UserAttribute {
24    Image {
25        packet_version: Version,
26        #[debug("{}", hex::encode(header))]
27        header: Vec<u8>,
28        #[debug("{}", hex::encode(data))]
29        data: Vec<u8>,
30    },
31    Unknown {
32        packet_version: Version,
33        typ: u8,
34        #[debug("{}", hex::encode(data))]
35        data: Vec<u8>,
36    },
37}
38
39impl UserAttribute {
40    /// Parses a `UserAttribute` packet from the given slice.
41    pub fn from_slice(packet_version: Version, input: &[u8]) -> Result<Self> {
42        let (_, pk) = parse(packet_version)(input)?;
43
44        Ok(pk)
45    }
46
47    pub fn to_u8(&self) -> u8 {
48        match *self {
49            UserAttribute::Image { .. } => 1,
50            UserAttribute::Unknown { typ, .. } => typ,
51        }
52    }
53
54    pub fn packet_len(&self) -> usize {
55        match self {
56            UserAttribute::Image { ref data, .. } => {
57                // typ + image header + data length
58                1 + 16 + data.len()
59            }
60            UserAttribute::Unknown { ref data, .. } => {
61                // typ + data length
62                1 + data.len()
63            }
64        }
65    }
66
67    /// Create a self-signature
68    pub fn sign<R, F>(
69        &self,
70        rng: R,
71        key: &impl SecretKeyTrait,
72        key_pw: F,
73    ) -> Result<SignedUserAttribute>
74    where
75        R: CryptoRng + Rng,
76        F: FnOnce() -> String,
77    {
78        self.sign_third_party(rng, key, key_pw, key)
79    }
80
81    /// Create a third-party signature
82    pub fn sign_third_party<R, F>(
83        &self,
84        mut rng: R,
85        signer: &impl SecretKeyTrait,
86        signer_pw: F,
87        signee: &impl PublicKeyTrait,
88    ) -> Result<SignedUserAttribute>
89    where
90        R: CryptoRng + Rng,
91        F: FnOnce() -> String,
92    {
93        let hashed_subpackets = vec![Subpacket::regular(SubpacketData::SignatureCreationTime(
94            Utc::now().trunc_subsecs(0),
95        ))];
96        let unhashed_subpackets = vec![Subpacket::regular(SubpacketData::Issuer(signer.key_id()))];
97
98        let mut config = match signer.version() {
99            KeyVersion::V4 => SignatureConfig::v4(
100                SignatureType::CertGeneric,
101                signer.algorithm(),
102                signer.hash_alg(),
103            ),
104
105            KeyVersion::V6 => SignatureConfig::v6(
106                &mut rng,
107                SignatureType::CertGeneric,
108                signer.algorithm(),
109                signer.hash_alg(),
110            )?,
111            v => unsupported_err!("unsupported key version: {:?}", v),
112        };
113
114        config.hashed_subpackets = hashed_subpackets;
115        config.unhashed_subpackets = unhashed_subpackets;
116
117        let sig =
118            config.sign_certification_third_party(signer, signer_pw, signee, self.tag(), &self)?;
119
120        Ok(SignedUserAttribute::new(self.clone(), vec![sig]))
121    }
122
123    pub fn into_signed(self, sig: Signature) -> SignedUserAttribute {
124        SignedUserAttribute::new(self, vec![sig])
125    }
126}
127
128impl fmt::Display for UserAttribute {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        match self {
131            UserAttribute::Image { data, .. } => {
132                write!(f, "User Attribute: Image (len: {})", data.len())
133            }
134            UserAttribute::Unknown { typ, data, .. } => {
135                write!(f, "User Attribute: typ: {} (len: {})", typ, data.len())
136            }
137        }
138    }
139}
140
141fn image(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], UserAttribute> {
142    move |i: &[u8]| {
143        // little endian, for historical reasons..
144        let (i, len) = le_u16(i)?;
145        if len < 2 {
146            return Err(nom::Err::Error(crate::errors::Error::InvalidInput));
147        }
148        let (img, header) = take(len - 2)(i)?;
149
150        // the actual image is the rest
151        Ok((
152            &[][..],
153            UserAttribute::Image {
154                packet_version,
155                header: header.to_vec(),
156                data: img.to_vec(),
157            },
158        ))
159    }
160}
161
162fn parse(packet_version: Version) -> impl Fn(&[u8]) -> IResult<&[u8], UserAttribute> {
163    move |i: &[u8]| {
164        let (i, len) = packet_length(i)?;
165        if len < 1 {
166            return Err(nom::Err::Error(crate::errors::Error::InvalidInput));
167        }
168        let (i, typ) = be_u8(i)?;
169        let (i, attr) = map_parser(take(len - 1), |i| match typ {
170            1 => image(packet_version)(i),
171            _ => map(rest, |data: &[u8]| UserAttribute::Unknown {
172                packet_version,
173                typ,
174                data: data.to_vec(),
175            })(i),
176        })(i)?;
177        Ok((i, {
178            debug!("attr with len {}", len);
179            attr
180        }))
181    }
182}
183
184impl Serialize for UserAttribute {
185    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
186        debug!("write_packet_len {}", self.packet_len());
187        write_packet_length(self.packet_len(), writer)?;
188
189        match self {
190            UserAttribute::Image {
191                ref data,
192                ref header,
193                ..
194            } => {
195                // typ: image
196                writer.write_u8(0x01)?;
197                writer.write_u16::<LittleEndian>((header.len() + 2).try_into()?)?;
198                writer.write_all(header)?;
199
200                // actual data
201                writer.write_all(data)?;
202            }
203            UserAttribute::Unknown { ref data, typ, .. } => {
204                writer.write_u8(*typ)?;
205                writer.write_all(data)?;
206            }
207        }
208        Ok(())
209    }
210}
211
212impl PacketTrait for UserAttribute {
213    fn packet_version(&self) -> Version {
214        match self {
215            UserAttribute::Image { packet_version, .. } => *packet_version,
216            UserAttribute::Unknown { packet_version, .. } => *packet_version,
217        }
218    }
219
220    fn tag(&self) -> Tag {
221        Tag::UserAttribute
222    }
223}