Skip to main content

gpg_inspector_lib/packet/
mod.rs

1//! OpenPGP packet parsing and type definitions.
2//!
3//! This module provides the core packet parsing functionality, including
4//! the [`Packet`] container, [`PacketBody`] enum for typed packet data,
5//! and the [`Field`] struct for hierarchical field representation.
6//!
7//! # Packet Structure
8//!
9//! OpenPGP data consists of a sequence of packets, each with:
10//! - A header (tag + length)
11//! - A body containing packet-specific data
12//!
13//! This module parses both old-format and new-format packet headers
14//! as defined in RFC 4880 and RFC 9580.
15
16/// Compressed Data packet parsing.
17pub mod compressed_data;
18/// Key fingerprint and key ID derivation.
19pub mod fingerprint;
20/// Literal Data packet parsing.
21pub mod literal_data;
22/// Modification Detection Code packet parsing.
23pub mod mdc;
24/// Miscellaneous simple packet types (Marker, SED, AEAD, Padding).
25pub mod misc;
26/// One-Pass Signature packet parsing.
27pub mod one_pass_signature;
28/// Public-Key Encrypted Session Key packet parsing.
29pub mod pkesk;
30/// Public key and subkey packet parsing.
31pub mod public_key;
32/// Secret key and subkey packet parsing.
33pub mod secret_key;
34/// Symmetrically Encrypted Integrity Protected Data packet parsing.
35pub mod seipd;
36/// Signature packet parsing.
37pub mod signature;
38/// Symmetric-Key Encrypted Session Key packet parsing.
39pub mod skesk;
40/// Signature subpacket parsing.
41pub mod subpackets;
42/// Packet tag definitions.
43pub mod tags;
44/// User Attribute packet parsing.
45pub mod user_attribute;
46/// User ID packet parsing.
47pub mod user_id;
48
49use std::sync::Arc;
50
51use crate::error::{Error, Result};
52use crate::stream::ByteStream;
53use tags::PacketTag;
54
55/// A field with its name, value, byte span, and indent level.
56///
57/// Fields represent individual pieces of parsed data within a packet.
58/// They form a hierarchy with three indent levels:
59/// - Level 0: Packet headers
60/// - Level 1: Top-level fields within a packet
61/// - Level 2: Nested subfields (e.g., signature subpackets)
62///
63/// # Visualization
64///
65/// The `span` field supports hex dump visualization, allowing UI code
66/// to highlight the corresponding bytes and assign colors as needed.
67#[derive(Debug, Clone)]
68pub struct Field {
69    /// The field name (e.g., "Version", "Algorithm", "Creation Time").
70    pub name: Arc<str>,
71    /// The field value as a human-readable string.
72    pub value: Arc<str>,
73    /// Indentation level: 0 = packet, 1 = field, 2 = subfield.
74    pub indent: u8,
75    /// Byte range `(start, end)` in the original data.
76    pub span: (usize, usize),
77}
78
79impl Field {
80    /// Creates a new field with all parameters specified.
81    pub fn new(
82        name: impl Into<Arc<str>>,
83        value: impl Into<Arc<str>>,
84        indent: u8,
85        span: (usize, usize),
86    ) -> Self {
87        Self {
88            name: name.into(),
89            value: value.into(),
90            indent,
91            span,
92        }
93    }
94
95    /// Creates a packet-level field (indent 0).
96    ///
97    /// Used for packet type headers like "Packet: Public Key".
98    pub fn packet(
99        name: impl Into<Arc<str>>,
100        value: impl Into<Arc<str>>,
101        span: (usize, usize),
102    ) -> Self {
103        Self::new(name, value, 0, span)
104    }
105
106    /// Creates a regular field (indent 1).
107    ///
108    /// Used for top-level packet fields like "Version", "Algorithm".
109    #[allow(clippy::self_named_constructors)]
110    pub fn field(
111        name: impl Into<Arc<str>>,
112        value: impl Into<Arc<str>>,
113        span: (usize, usize),
114    ) -> Self {
115        Self::new(name, value, 1, span)
116    }
117
118    /// Creates a subfield (indent 2).
119    ///
120    /// Used for nested data like signature subpackets.
121    pub fn subfield(
122        name: impl Into<Arc<str>>,
123        value: impl Into<Arc<str>>,
124        span: (usize, usize),
125    ) -> Self {
126        Self::new(name, value, 2, span)
127    }
128}
129
130/// A parsed OpenPGP packet with metadata and fields.
131///
132/// Contains the raw packet boundaries, parsed body, and a list of
133/// human-readable fields.
134#[derive(Debug, Clone)]
135pub struct Packet {
136    /// Byte offset where this packet starts in the original data.
137    pub start: usize,
138    /// Byte offset where this packet ends (exclusive).
139    pub end: usize,
140    /// The packet type tag.
141    pub tag: PacketTag,
142    /// The parsed packet body.
143    pub body: PacketBody,
144    /// Parsed fields for display.
145    pub fields: Vec<Field>,
146    /// Nested packets parsed from this packet's decompressed payload
147    /// (Compressed Data packets only). Their spans index
148    /// [`Packet::child_buffer`], not the original data.
149    pub children: Vec<Packet>,
150    /// The decompressed buffer that `children` spans index into.
151    pub child_buffer: Option<Arc<[u8]>>,
152}
153
154impl Packet {
155    /// Creates a packet with no nested children.
156    pub fn new(
157        start: usize,
158        end: usize,
159        tag: PacketTag,
160        body: PacketBody,
161        fields: Vec<Field>,
162    ) -> Self {
163        Self {
164            start,
165            end,
166            tag,
167            body,
168            fields,
169            children: Vec::new(),
170            child_buffer: None,
171        }
172    }
173}
174
175/// The typed body of a parsed packet.
176///
177/// Each variant contains the parsed structure for that packet type.
178/// Unknown or unsupported packet types are stored as raw bytes.
179#[derive(Debug, Clone)]
180pub enum PacketBody {
181    /// Public-Key Encrypted Session Key packet (tag 1).
182    Pkesk(pkesk::PkeskPacket),
183    /// Signature packet (tag 2).
184    Signature(signature::SignaturePacket),
185    /// Symmetric-Key Encrypted Session Key packet (tag 3).
186    Skesk(skesk::SkeskPacket),
187    /// One-Pass Signature packet (tag 4).
188    OnePassSignature(one_pass_signature::OnePassSignaturePacket),
189    /// Secret key packet (tag 5).
190    SecretKey(secret_key::SecretKeyPacket),
191    /// Public key packet (tag 6).
192    PublicKey(public_key::PublicKeyPacket),
193    /// Secret subkey packet (tag 7).
194    SecretSubkey(secret_key::SecretKeyPacket),
195    /// Compressed Data packet (tag 8).
196    CompressedData(compressed_data::CompressedDataPacket),
197    /// Symmetrically Encrypted Data packet (tag 9, legacy).
198    SymmetricallyEncryptedData(misc::SymmetricallyEncryptedDataPacket),
199    /// Marker packet (tag 10, obsolete).
200    Marker(misc::MarkerPacket),
201    /// Literal Data packet (tag 11).
202    LiteralData(literal_data::LiteralDataPacket),
203    /// User ID packet (tag 13).
204    UserId(user_id::UserIdPacket),
205    /// Public subkey packet (tag 14).
206    PublicSubkey(public_key::PublicKeyPacket),
207    /// User Attribute packet (tag 17).
208    UserAttribute(user_attribute::UserAttributePacket),
209    /// Symmetrically Encrypted Integrity Protected Data packet (tag 18).
210    Seipd(seipd::SeipdPacket),
211    /// Modification Detection Code packet (tag 19).
212    Mdc(mdc::MdcPacket),
213    /// AEAD Encrypted Data packet (tag 20, RFC 9580).
214    AeadEncryptedData(misc::AeadEncryptedDataPacket),
215    /// Padding packet (tag 21, RFC 9580).
216    Padding(misc::PaddingPacket),
217    /// Unknown or unsupported packet type.
218    Unknown(Vec<u8>),
219}
220
221/// Parses binary PGP data into a vector of packets.
222///
223/// This is the main entry point for parsing raw (non-armored) PGP data.
224/// For armored data, use [`crate::parse`] which handles decoding first.
225///
226/// # Errors
227///
228/// Returns an error if any packet has an invalid structure.
229pub fn parse_packets(bytes: Arc<[u8]>) -> Result<Vec<Packet>> {
230    parse_packets_at_depth(bytes, 0)
231}
232
233/// Maximum nesting depth for recursive (compressed) packet expansion.
234#[cfg(feature = "decompress")]
235const MAX_DEPTH: usize = 4;
236
237fn parse_packets_at_depth(bytes: Arc<[u8]>, depth: usize) -> Result<Vec<Packet>> {
238    let mut stream = ByteStream::from_arc(bytes);
239    let mut packets = Vec::new();
240
241    while !stream.is_empty() {
242        let packet = parse_packet(&mut stream, depth)?;
243        packets.push(packet);
244    }
245
246    Ok(packets)
247}
248
249fn parse_packet(stream: &mut ByteStream, depth: usize) -> Result<Packet> {
250    let packet_start = stream.abs_pos();
251    let mut fields = Vec::new();
252
253    let header_start = stream.abs_pos();
254    let header_byte = stream.octet()?;
255
256    if header_byte & 0x80 == 0 {
257        return Err(Error::InvalidPacketHeader(packet_start));
258    }
259
260    let new_format = header_byte & 0x40 != 0;
261    let (tag, body_length) = if new_format {
262        let tag = PacketTag::from_u8(header_byte & 0x3F);
263        let len = parse_new_length(stream)?;
264        (tag, len)
265    } else {
266        let tag = PacketTag::from_u8((header_byte >> 2) & 0x0F);
267        let len_type = header_byte & 0x03;
268        let len = parse_old_length(stream, len_type)?;
269        (tag, len)
270    };
271
272    let header_end = stream.abs_pos();
273    let packet_name: Arc<str> = format!("Packet: {}", tag).into();
274    fields.push(Field::packet(
275        packet_name,
276        format!("{} bytes", body_length),
277        (header_start, header_end),
278    ));
279
280    let body_start = stream.abs_pos();
281    let mut body_stream = stream.slice(stream.pos(), stream.pos() + body_length);
282    stream.skip(body_length)?;
283    let packet_end = stream.abs_pos();
284
285    let body = parse_packet_body(tag, &mut body_stream, &mut fields, body_start)?;
286
287    let mut children = Vec::new();
288    let mut child_buffer = None;
289    if let PacketBody::CompressedData(ref cd) = body {
290        expand_compressed(
291            cd,
292            depth,
293            &mut fields,
294            &mut children,
295            &mut child_buffer,
296            (body_start, packet_end),
297        );
298    }
299
300    Ok(Packet {
301        start: packet_start,
302        end: packet_end,
303        tag,
304        body,
305        fields,
306        children,
307        child_buffer,
308    })
309}
310
311/// Decompresses a Compressed Data packet and parses the nested packets.
312///
313/// Never fails the surrounding parse: any decompression or nested-parse
314/// problem is reported as a "Decompressed" field instead.
315#[cfg(feature = "decompress")]
316fn expand_compressed(
317    cd: &compressed_data::CompressedDataPacket,
318    depth: usize,
319    fields: &mut Vec<Field>,
320    children: &mut Vec<Packet>,
321    child_buffer: &mut Option<Arc<[u8]>>,
322    span: (usize, usize),
323) {
324    if depth >= MAX_DEPTH {
325        fields.push(Field::field(
326            "Decompressed",
327            "max nesting depth reached; not expanded",
328            span,
329        ));
330        return;
331    }
332
333    match compressed_data::decompress(cd.algorithm, &cd.compressed_data) {
334        Ok(buf) => {
335            let buf: Arc<[u8]> = buf.into();
336            match parse_packets_at_depth(Arc::clone(&buf), depth + 1) {
337                Ok(packets) => {
338                    fields.push(Field::field(
339                        "Decompressed",
340                        format!("{} bytes, {} packets", buf.len(), packets.len()),
341                        span,
342                    ));
343                    *children = packets;
344                    *child_buffer = Some(buf);
345                }
346                Err(e) => {
347                    fields.push(Field::field("Decompressed", format!("error: {}", e), span));
348                }
349            }
350        }
351        Err(msg) => {
352            fields.push(Field::field(
353                "Decompressed",
354                format!("error: {}", msg),
355                span,
356            ));
357        }
358    }
359}
360
361#[cfg(not(feature = "decompress"))]
362fn expand_compressed(
363    _cd: &compressed_data::CompressedDataPacket,
364    _depth: usize,
365    _fields: &mut Vec<Field>,
366    _children: &mut Vec<Packet>,
367    _child_buffer: &mut Option<Arc<[u8]>>,
368    _span: (usize, usize),
369) {
370}
371
372fn parse_new_length(stream: &mut ByteStream) -> Result<usize> {
373    let first = stream.octet()? as usize;
374    if first < 192 {
375        Ok(first)
376    } else if first < 224 {
377        let second = stream.octet()? as usize;
378        Ok(((first - 192) << 8) + second + 192)
379    } else if first == 255 {
380        Ok(stream.uint32()? as usize)
381    } else {
382        Ok(1 << (first & 0x1F))
383    }
384}
385
386fn parse_old_length(stream: &mut ByteStream, len_type: u8) -> Result<usize> {
387    match len_type {
388        0 => Ok(stream.octet()? as usize),
389        1 => Ok(stream.uint16()? as usize),
390        2 => Ok(stream.uint32()? as usize),
391        3 => Ok(stream.remaining()),
392        _ => unreachable!(),
393    }
394}
395
396fn parse_packet_body(
397    tag: PacketTag,
398    stream: &mut ByteStream,
399    fields: &mut Vec<Field>,
400    body_offset: usize,
401) -> Result<PacketBody> {
402    match tag {
403        PacketTag::PublicKeyEncryptedSessionKey => {
404            let pkesk = pkesk::parse_pkesk(stream, fields, body_offset)?;
405            Ok(PacketBody::Pkesk(pkesk))
406        }
407        PacketTag::Signature => {
408            let sig = signature::parse_signature(stream, fields, body_offset)?;
409            Ok(PacketBody::Signature(sig))
410        }
411        PacketTag::SymmetricKeyEncryptedSessionKey => {
412            let skesk = skesk::parse_skesk(stream, fields, body_offset)?;
413            Ok(PacketBody::Skesk(skesk))
414        }
415        PacketTag::OnePassSignature => {
416            let ops = one_pass_signature::parse_one_pass_signature(stream, fields, body_offset)?;
417            Ok(PacketBody::OnePassSignature(ops))
418        }
419        PacketTag::SecretKey => {
420            let sk = secret_key::parse_secret_key(stream, fields, body_offset)?;
421            Ok(PacketBody::SecretKey(sk))
422        }
423        PacketTag::PublicKey => {
424            let pk = public_key::parse_public_key(stream, fields, body_offset)?;
425            Ok(PacketBody::PublicKey(pk))
426        }
427        PacketTag::SecretSubkey => {
428            let sk = secret_key::parse_secret_key(stream, fields, body_offset)?;
429            Ok(PacketBody::SecretSubkey(sk))
430        }
431        PacketTag::CompressedData => {
432            let cd = compressed_data::parse_compressed_data(stream, fields, body_offset)?;
433            Ok(PacketBody::CompressedData(cd))
434        }
435        PacketTag::SymmetricallyEncryptedData => {
436            let sed = misc::parse_symmetrically_encrypted_data(stream, fields, body_offset)?;
437            Ok(PacketBody::SymmetricallyEncryptedData(sed))
438        }
439        PacketTag::Marker => {
440            let marker = misc::parse_marker(stream, fields, body_offset)?;
441            Ok(PacketBody::Marker(marker))
442        }
443        PacketTag::LiteralData => {
444            let ld = literal_data::parse_literal_data(stream, fields, body_offset)?;
445            Ok(PacketBody::LiteralData(ld))
446        }
447        PacketTag::UserId => {
448            let uid = user_id::parse_user_id(stream, fields, body_offset)?;
449            Ok(PacketBody::UserId(uid))
450        }
451        PacketTag::PublicSubkey => {
452            let pk = public_key::parse_public_key(stream, fields, body_offset)?;
453            Ok(PacketBody::PublicSubkey(pk))
454        }
455        PacketTag::UserAttribute => {
456            let ua = user_attribute::parse_user_attribute(stream, fields, body_offset)?;
457            Ok(PacketBody::UserAttribute(ua))
458        }
459        PacketTag::SymmetricallyEncryptedIntegrityProtectedData => {
460            let seipd = seipd::parse_seipd(stream, fields, body_offset)?;
461            Ok(PacketBody::Seipd(seipd))
462        }
463        PacketTag::ModificationDetectionCode => {
464            let mdc = mdc::parse_mdc(stream, fields, body_offset)?;
465            Ok(PacketBody::Mdc(mdc))
466        }
467        PacketTag::AeadEncryptedData => {
468            let aead = misc::parse_aead_encrypted_data(stream, fields, body_offset)?;
469            Ok(PacketBody::AeadEncryptedData(aead))
470        }
471        PacketTag::Padding => {
472            let padding = misc::parse_padding(stream, fields, body_offset)?;
473            Ok(PacketBody::Padding(padding))
474        }
475        // Trust (tag 12) is implementation-specific and not exported
476        // Reserved (tag 0) should never appear
477        // Unknown tags are stored as raw bytes
478        _ => {
479            let data = stream.rest();
480            if !data.is_empty() {
481                let data_start = body_offset + stream.pos() - data.len();
482                let data_end = body_offset + stream.pos();
483                fields.push(Field::field(
484                    "Data",
485                    format!("{} bytes", data.len()),
486                    (data_start, data_end),
487                ));
488            }
489            Ok(PacketBody::Unknown(data))
490        }
491    }
492}