pgp/packet/
padding.rs

1use std::io::{self, BufRead};
2
3use bytes::Bytes;
4#[cfg(test)]
5use proptest::prelude::*;
6use rand::{CryptoRng, RngCore};
7
8use crate::{
9    errors::Result,
10    packet::{PacketHeader, PacketTrait},
11    parsing_reader::BufReadParsing,
12    ser::Serialize,
13    types::{PacketHeaderVersion, PacketLength, Tag},
14};
15
16/// Padding Packet
17///
18/// <https://www.rfc-editor.org/rfc/rfc9580.html#name-padding-packet-type-id-21>
19#[derive(derive_more::Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
21pub struct Padding {
22    packet_header: PacketHeader,
23    /// Random data.
24    #[debug("{}", hex::encode(data))]
25    #[cfg_attr(test, proptest(strategy = "any::<Vec<u8>>().prop_map(Into::into)"))]
26    data: Bytes,
27}
28
29impl Padding {
30    /// Parses a `Padding` packet from the given slice.
31    pub fn try_from_reader<B: BufRead>(packet_header: PacketHeader, mut input: B) -> Result<Self> {
32        let data = input.rest()?.freeze();
33
34        Ok(Padding {
35            packet_header,
36            data,
37        })
38    }
39
40    /// Create a new padding packet of `size` in bytes.
41    pub fn new<R: CryptoRng + RngCore>(
42        mut rng: R,
43        packet_version: PacketHeaderVersion,
44        size: usize,
45    ) -> Result<Self> {
46        let mut data = vec![0u8; size];
47        rng.fill_bytes(&mut data);
48
49        let len = PacketLength::Fixed(data.len().try_into()?);
50        let packet_header = PacketHeader::from_parts(packet_version, Tag::Padding, len)?;
51
52        Ok(Padding {
53            packet_header,
54            data: data.into(),
55        })
56    }
57}
58
59impl Serialize for Padding {
60    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
61        writer.write_all(&self.data)?;
62
63        Ok(())
64    }
65
66    fn write_len(&self) -> usize {
67        self.data.len()
68    }
69}
70
71impl PacketTrait for Padding {
72    fn packet_header(&self) -> &PacketHeader {
73        &self.packet_header
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use proptest::prelude::*;
80    use rand::SeedableRng;
81    use rand_chacha::ChaCha20Rng;
82
83    use super::*;
84    use crate::{
85        packet::{Packet, PacketHeader},
86        types::PacketLength,
87    };
88
89    #[test]
90    fn test_padding_roundtrip() {
91        let packet_raw = hex::decode("d50ec5a293072991628147d72c8f86b7").expect("valid hex");
92        let mut to_parse = std::io::Cursor::new(&packet_raw);
93        let header = PacketHeader::try_from_reader(&mut to_parse).expect("parse");
94
95        let PacketLength::Fixed(_len) = header.packet_length() else {
96            panic!("invalid parse result");
97        };
98
99        let full_packet = Packet::from_reader(header, &mut to_parse).expect("body parse");
100
101        let Packet::Padding(ref packet) = full_packet else {
102            panic!("invalid packet: {full_packet:?}");
103        };
104        assert_eq!(
105            packet.data,
106            hex::decode("c5a293072991628147d72c8f86b7").expect("valid hex")
107        );
108
109        // encode
110        let encoded = full_packet.to_bytes().expect("encode");
111        assert_eq!(encoded, packet_raw);
112    }
113
114    #[test]
115    fn test_padding_new() {
116        let mut rng = ChaCha20Rng::seed_from_u64(1);
117        let packet = Padding::new(&mut rng, PacketHeaderVersion::New, 20).unwrap();
118        assert_eq!(packet.data.len(), 20);
119
120        let encoded = packet.to_bytes().expect("encode");
121        assert_eq!(encoded, packet.data);
122    }
123
124    proptest! {
125        #[test]
126        fn write_len(padding: Padding) {
127            let mut buf = Vec::new();
128            padding.to_writer(&mut buf).unwrap();
129            assert_eq!(buf.len(), padding.write_len());
130        }
131
132
133        #[test]
134        fn packet_roundtrip(padding: Padding) {
135            let mut buf = Vec::new();
136            padding.to_writer(&mut buf).unwrap();
137            let new_padding = Padding::try_from_reader(padding.packet_header, &mut &buf[..]).unwrap();
138            assert_eq!(padding, new_padding);
139        }
140    }
141}