pgp/packet/
mod_detection_code.rs

1use std::io::{self, BufRead};
2
3use crate::{
4    errors::Result,
5    packet::{PacketHeader, PacketTrait},
6    parsing_reader::BufReadParsing,
7    ser::Serialize,
8};
9
10/// Modification Detection Code Packet
11/// <https://www.rfc-editor.org/rfc/rfc9580.html#version-one-seipd>
12///
13/// Also see <https://www.rfc-editor.org/rfc/rfc9580.html#name-terminology-changes>:
14///
15/// "Modification Detection Code" or "MDC" was originally described as a distinct packet
16/// (Packet Type ID 19), and its corresponding flag in the Features signature subpacket
17/// (Section 5.2.3.32) was known as "Modification Detection".
18/// It is now described as an intrinsic part of v1 SEIPD (Section 5.13.1), and the same
19/// corresponding flag is known as "Version 1 Symmetrically Encrypted and Integrity Protected
20/// Data packet".
21#[derive(derive_more::Debug, Clone, PartialEq, Eq)]
22#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
23pub struct ModDetectionCode {
24    packet_header: PacketHeader,
25    /// 20 byte SHA1 hash of the preceding plaintext data.
26    #[debug("{}", hex::encode(hash))]
27    hash: [u8; 20],
28}
29
30impl ModDetectionCode {
31    /// Parses a `ModDetectionCode` packet.
32    pub fn try_from_reader<B: BufRead>(packet_header: PacketHeader, mut input: B) -> Result<Self> {
33        let hash = input.read_array::<20>()?;
34
35        Ok(ModDetectionCode {
36            packet_header,
37            hash,
38        })
39    }
40}
41
42impl Serialize for ModDetectionCode {
43    fn to_writer<W: io::Write>(&self, writer: &mut W) -> Result<()> {
44        writer.write_all(&self.hash[..])?;
45        Ok(())
46    }
47
48    fn write_len(&self) -> usize {
49        self.hash.len()
50    }
51}
52
53impl PacketTrait for ModDetectionCode {
54    fn packet_header(&self) -> &PacketHeader {
55        &self.packet_header
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use proptest::prelude::*;
62
63    use super::*;
64
65    proptest! {
66        #[test]
67        fn write_len(packet: ModDetectionCode) {
68            let mut buf = Vec::new();
69            packet.to_writer(&mut buf).unwrap();
70            prop_assert_eq!(buf.len(), packet.write_len());
71        }
72
73        #[test]
74        fn packet_roundtrip(packet: ModDetectionCode) {
75            let mut buf = Vec::new();
76            packet.to_writer(&mut buf).unwrap();
77            let new_packet = ModDetectionCode::try_from_reader(packet.packet_header, &mut &buf[..]).unwrap();
78            prop_assert_eq!(packet, new_packet);
79        }
80    }
81}