sequoia_openpgp/packet/
user_attribute.rs

1//! User Attribute packets and subpackets.
2//!
3//! See [Section 5.12 of RFC 9580] for details.
4//!
5//!   [Section 5.12 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.12
6
7use std::fmt;
8
9#[cfg(test)]
10use quickcheck::{Arbitrary, Gen};
11
12use buffered_reader::BufferedReader;
13
14use crate::Error;
15use crate::Result;
16use crate::packet::{
17    self,
18    header::BodyLength,
19};
20use crate::Packet;
21use crate::policy::HashAlgoSecurity;
22use crate::serialize::Marshal;
23use crate::serialize::MarshalInto;
24
25/// Holds a UserAttribute packet.
26///
27/// See [Section 5.12 of RFC 9580] for details.
28///
29///   [Section 5.12 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.12
30// IMPORTANT: If you add fields to this struct, you need to explicitly
31// IMPORTANT: implement PartialEq, Eq, and Hash.
32#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub struct UserAttribute {
34    /// CTB packet header fields.
35    pub(crate) common: packet::Common,
36
37    /// The user attribute.
38    value: Vec<u8>,
39}
40assert_send_and_sync!(UserAttribute);
41
42impl From<Vec<u8>> for UserAttribute {
43    fn from(u: Vec<u8>) -> Self {
44        UserAttribute {
45            common: Default::default(),
46            value: u,
47        }
48    }
49}
50
51impl fmt::Debug for UserAttribute {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        f.debug_struct("UserAttribute")
54            .field("value (bytes)", &self.value.len())
55            .finish()
56    }
57}
58
59impl UserAttribute {
60    /// Returns a new `UserAttribute` packet.
61    ///
62    /// Note: a valid UserAttribute has at least one subpacket.
63    pub fn new(subpackets: &[Subpacket]) -> Result<Self> {
64        let mut value = Vec::with_capacity(
65            subpackets.iter().fold(0, |l, s| l + s.serialized_len()));
66        for s in subpackets {
67            s.serialize(&mut value)?
68        }
69
70        Ok(UserAttribute {
71            common: Default::default(),
72            value
73        })
74    }
75
76    /// The security requirements of the hash algorithm for
77    /// self-signatures.
78    ///
79    /// A cryptographic hash algorithm usually has [three security
80    /// properties]: pre-image resistance, second pre-image
81    /// resistance, and collision resistance.  If an attacker can
82    /// influence the signed data, then the hash algorithm needs to
83    /// have both second pre-image resistance, and collision
84    /// resistance.  If not, second pre-image resistance is
85    /// sufficient.
86    ///
87    ///   [three security properties]: https://en.wikipedia.org/wiki/Cryptographic_hash_function#Properties
88    ///
89    /// In general, an attacker may be able to influence third-party
90    /// signatures.  But direct key signatures, and binding signatures
91    /// are only over data fully determined by signer.  And, an
92    /// attacker's control over self signatures over User IDs is
93    /// limited due to their structure.
94    ///
95    /// These observations can be used to extend the life of a hash
96    /// algorithm after its collision resistance has been partially
97    /// compromised, but not completely broken.  For more details,
98    /// please refer to the documentation for [HashAlgoSecurity].
99    ///
100    ///   [HashAlgoSecurity]: crate::policy::HashAlgoSecurity
101    pub fn hash_algo_security(&self) -> HashAlgoSecurity {
102        HashAlgoSecurity::CollisionResistance
103    }
104
105    /// Gets the user attribute packet's raw, unparsed value.
106    ///
107    /// Most likely you will want to use [`subpackets()`] to iterate
108    /// over the subpackets.
109    ///
110    /// [`subpackets()`]: UserAttribute::subpackets()
111    pub fn value(&self) -> &[u8] {
112        self.value.as_slice()
113    }
114
115    /// Gets a mutable reference to the user attribute packet's raw
116    /// value.
117    pub fn value_mut(&mut self) -> &mut Vec<u8> {
118        &mut self.value
119    }
120
121    /// Iterates over the subpackets.
122    pub fn subpackets(&self) -> SubpacketIterator {
123        SubpacketIterator {
124            reader: buffered_reader::Memory::new(&self.value[..]),
125        }
126    }
127}
128
129impl From<UserAttribute> for Packet {
130    fn from(s: UserAttribute) -> Self {
131        Packet::UserAttribute(s)
132    }
133}
134
135#[cfg(test)]
136impl Arbitrary for UserAttribute {
137    fn arbitrary(g: &mut Gen) -> Self {
138        use crate::arbitrary_helper::gen_arbitrary_from_range;
139
140        UserAttribute::new(
141            &(0..gen_arbitrary_from_range(1..10, g))
142                .map(|_| Subpacket::arbitrary(g))
143                .collect::<Vec<_>>()[..]).unwrap()
144    }
145}
146
147/// Iterates over subpackets.
148pub struct SubpacketIterator<'a> {
149    reader: buffered_reader::Memory<'a, ()>,
150}
151assert_send_and_sync!(SubpacketIterator<'_>);
152
153impl<'a> Iterator for SubpacketIterator<'a> {
154    type Item = Result<Subpacket>;
155    fn next(&mut self) -> Option<Self::Item> {
156        let length = match BodyLength::parse_new_format(&mut self.reader) {
157            Ok(BodyLength::Full(l)) => l,
158            Ok(BodyLength::Partial(_)) | Ok(BodyLength::Indeterminate) =>
159                return Some(Err(Error::MalformedPacket(
160                    "Partial or Indeterminate length of subpacket".into())
161                                .into())),
162            Err(e) =>
163                if e.kind() == ::std::io::ErrorKind::UnexpectedEof {
164                    return None;
165                } else {
166                    return Some(Err(e.into()));
167                },
168        };
169
170        let raw = match self.reader.data_consume_hard(length as usize) {
171            Ok(r) => &r[..length as usize],
172            Err(e) => return Some(Err(e.into())),
173        };
174
175        if raw.is_empty() {
176            return Some(Err(Error::MalformedPacket(
177                "Subpacket without type octet".into()).into()));
178        }
179
180        let typ = raw[0];
181        let raw = &raw[1..];
182        match typ {
183            // Image.
184            1 => if raw.len() >= 16 &&
185                    raw[..3] == [0x10, 0x00, 0x01]
186                    && raw[4..16].iter().all(|b| *b == 0)
187            {
188                let image_kind = raw[3];
189                Some(Ok(Subpacket::Image(match image_kind {
190                    1 =>
191                        Image::JPEG(Vec::from(&raw[16..]).into_boxed_slice()),
192                    n @ 100..=110 =>
193                        Image::Private(
194                            n, Vec::from(&raw[16..]).into_boxed_slice()),
195                    n =>
196                        Image::Unknown(
197                            n, Vec::from(&raw[16..]).into_boxed_slice()),
198                })))
199            } else {
200                Some(Err(Error::MalformedPacket(
201                    "Malformed image subpacket".into()).into()))
202            },
203            n =>
204                Some(Ok(Subpacket::Unknown(
205                    n, Vec::from(raw).into_boxed_slice()))),
206        }
207    }
208}
209
210/// User Attribute subpackets.
211///
212/// See [Section 5.12 of RFC 9580] for details.
213///
214///   [Section 5.12 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.12
215#[derive(Clone, Debug, PartialEq, Eq, Hash)]
216#[non_exhaustive]
217pub enum Subpacket {
218    /// Image subpacket.
219    ///
220    /// See [Section 5.12.1 of RFC 9580] for details.
221    ///
222    ///   [Section 5.12.1 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.12.1
223    Image(Image),
224    /// Unknown subpacket.
225    Unknown(u8, Box<[u8]>),
226}
227assert_send_and_sync!(Subpacket);
228
229#[cfg(test)]
230impl Arbitrary for Subpacket {
231    fn arbitrary(g: &mut Gen) -> Self {
232        use crate::arbitrary_helper::gen_arbitrary_from_range;
233
234        match gen_arbitrary_from_range(0..3, g) {
235            0 => Subpacket::Image(Image::arbitrary(g)),
236            1 => Subpacket::Unknown(
237                0,
238                Vec::<u8>::arbitrary(g).into_boxed_slice()
239            ),
240            2 => Subpacket::Unknown(
241                gen_arbitrary_from_range(2..256, g) as u8,
242                Vec::<u8>::arbitrary(g).into_boxed_slice()
243            ),
244            _ => unreachable!(),
245        }
246    }
247}
248
249/// Image subpacket.
250///
251/// See [Section 5.12.1 of RFC 9580] for details.
252///
253///   [Section 5.12.1 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.12.1
254#[derive(Clone, Debug, PartialEq, Eq, Hash)]
255#[non_exhaustive]
256pub enum Image {
257    /// A JPEG image format.
258    JPEG(Box<[u8]>),
259    /// Private, experimental image format.
260    Private(u8, Box<[u8]>),
261    /// Unknown image format.
262    Unknown(u8, Box<[u8]>),
263}
264assert_send_and_sync!(Image);
265
266#[cfg(test)]
267impl Arbitrary for Image {
268
269    fn arbitrary(g: &mut Gen) -> Self {
270        use crate::arbitrary_helper::gen_arbitrary_from_range;
271
272        match gen_arbitrary_from_range(0..5, g) {
273            0 =>
274                Image::JPEG(
275                    Vec::<u8>::arbitrary(g).into_boxed_slice()
276                ),
277            1 =>
278                Image::Unknown(
279                    gen_arbitrary_from_range(2..100, g),
280                    Vec::<u8>::arbitrary(g).into_boxed_slice()
281                ),
282            2 =>
283                Image::Private(
284                    gen_arbitrary_from_range(100..111, g),
285                    Vec::<u8>::arbitrary(g).into_boxed_slice()
286                ),
287            3 =>
288                Image::Unknown(
289                    0,
290                    Vec::<u8>::arbitrary(g).into_boxed_slice()
291                ),
292            4 =>
293                Image::Unknown(
294                    gen_arbitrary_from_range(111..256, g) as u8,
295                    Vec::<u8>::arbitrary(g).into_boxed_slice()
296                ),
297            _ => unreachable!(),
298        }
299    }
300}
301
302
303#[cfg(test)]
304mod tests {
305    use super::*;
306    use crate::parse::Parse;
307
308    quickcheck! {
309        fn roundtrip(p: UserAttribute) -> bool {
310            let buf = p.to_vec().unwrap();
311            assert_eq!(p.serialized_len(), buf.len());
312            let q = UserAttribute::from_bytes(&buf).unwrap();
313            assert_eq!(p, q);
314            true
315        }
316    }
317
318    quickcheck! {
319        fn roundtrip_subpacket(sp: Subpacket) -> bool {
320            let value = sp.to_vec().unwrap();
321            assert_eq!(sp.serialized_len(), value.len());
322            let ua = UserAttribute {
323                common: Default::default(),
324                value,
325            };
326            let buf = ua.to_vec().unwrap();
327            let q = UserAttribute::from_bytes(&buf).unwrap();
328            let subpackets = q.subpackets().collect::<Vec<_>>();
329            assert_eq!(subpackets.len(), 1);
330            assert_eq!(&sp, subpackets[0].as_ref().unwrap());
331            true
332        }
333    }
334
335    quickcheck! {
336        fn roundtrip_image(img: Image) -> bool {
337            let mut body = img.to_vec().unwrap();
338            assert_eq!(img.serialized_len(), body.len());
339            let mut value =
340                BodyLength::Full(1 + body.len() as u32).to_vec().unwrap();
341            value.push(1); // Image subpacket tag.
342            value.append(&mut body);
343            let ua = UserAttribute {
344                common: Default::default(),
345                value,
346            };
347            let buf = ua.to_vec().unwrap();
348            let q = UserAttribute::from_bytes(&buf).unwrap();
349            let subpackets = q.subpackets().collect::<Vec<_>>();
350            assert_eq!(subpackets.len(), 1);
351            if let Ok(Subpacket::Image(i)) = &subpackets[0] {
352                assert_eq!(&img, i);
353            } else {
354                panic!("expected image subpacket, got {:?}", subpackets[0]);
355            }
356            true
357        }
358    }
359
360    #[test]
361    fn image() {
362        use crate::Packet;
363        let p = Packet::from_bytes("
364-----BEGIN PGP ARMORED FILE-----
365
3660cFuwWwBEAABAQAAAAAAAAAAAAAAAP/Y/+AAEEpGSUYAAQEBASwBLAAA//4AE0Ny
367ZWF0ZWQgd2l0aCBHSU1Q/9sAQwADAgIDAgIDAwMDBAMDBAUIBQUEBAUKBwcGCAwK
368DAwLCgsLDQ4SEA0OEQ4LCxAWEBETFBUVFQwPFxgWFBgSFBUU/9sAQwEDBAQFBAUJ
369BQUJFA0LDRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU
370FBQUFBQUFBQU/8IAEQgAAQABAwERAAIRAQMRAf/EABQAAQAAAAAAAAAAAAAAAAAA
371AAj/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIQAxAAAAFUn//EABQQAQAA
372AAAAAAAAAAAAAAAAAAD/2gAIAQEAAQUCf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/
3732gAIAQMBAT8Bf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Bf//EABQQ
374AQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEABj8Cf//EABQQAQAAAAAAAAAAAAAAAAAA
375AAD/2gAIAQEAAT8hf//aAAwDAQACAAMAAAAQn//EABQRAQAAAAAAAAAAAAAAAAAA
376AAD/2gAIAQMBAT8Qf//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Qf//E
377ABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAT8Qf//Z
378=nUQg
379-----END PGP ARMORED FILE-----
380").unwrap();
381        let subpackets: Vec<_> = if let Packet::UserAttribute(ua) = p {
382            ua.subpackets().collect()
383        } else {
384            panic!("Expected a UserAttribute, got: {:?}", p);
385        };
386        assert_eq!(subpackets.len(), 1);
387        if let Ok(Subpacket::Image(Image::JPEG(img))) = &subpackets[0] {
388            assert_eq!(img.len(), 539 /* Image data */);
389            assert_eq!(&img[6..10], b"JFIF");
390            assert_eq!(&img[24..41], b"Created with GIMP");
391        } else {
392            panic!("Expected JPEG, got {:?}", &subpackets[0]);
393        }
394
395        if let Ok(Subpacket::Image(img)) = &subpackets[0] {
396            let buf = img.to_vec().unwrap();
397            assert_eq!(buf.len(), 539 + 16 /* Image header */);
398            assert_eq!(img.serialized_len(), 539 + 16 /* Image header */);
399        } else {
400            unreachable!("decomposed fine before");
401        }
402
403        if let Ok(img) = &subpackets[0] {
404            let buf = img.to_vec().unwrap();
405            assert_eq!(buf.len(), 539 + 16 + 3 /* Subpacket header */);
406            assert_eq!(img.serialized_len(), 539 + 16 + 3 /* Subpacket header */);
407        } else {
408            unreachable!("decomposed fine before");
409        }
410    }
411}