[][src]Struct sequoia_openpgp::parse::PacketParser

pub struct PacketParser<'a> {
    pub packet: Packet,
    // some fields omitted
}

A low-level OpenPGP message parser.

A PacketParser provides a low-level, iterator-like interface to parse OpenPGP messages.

For each iteration, the user is presented with a Packet corresponding to the last packet, a PacketParser for the next packet, and their positions within the message.

Using the PacketParser, the user is able to configure how the new packet will be parsed. For instance, it is possible to stream the packet's contents (a PacketParser implements the std::io::Read and the BufferedReader traits), buffer them within the Packet, or drop them. The user can also decide to recurse into the packet, if it is a container, instead of getting the following packet.

See the PacketParser::next and PacketParser::recurse methods for more details.

Examples

These examples demonstrate how to process packet bodies by parsing the simplest possible OpenPGP message containing just a single literal data packet with the body "Hello world.". There are three options. First, the body can be dropped. Second, it can be buffered. Lastly, the body can be streamed. In general, streaming should be preferred, because it avoids buffering in Sequoia.

This example demonstrates simply ignoring the packet body:

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// By default, the `PacketParser` will drop packet bodies.
let mut ppr =
    PacketParser::from_bytes(b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?;
while let PacketParserResult::Some(pp) = ppr {
    // Get the packet out of the parser and start parsing the next
    // packet, recursing.
    let (packet, next_ppr) = pp.recurse()?;
    ppr = next_ppr;

    // Process the packet.
    if let Packet::Literal(literal) = packet {
        // The body was dropped.
        assert_eq!(literal.body(), b"");
    } else {
        unreachable!("We know it is a literal packet.");
    }
}

This example demonstrates how the body can be buffered by configuring the PacketParser to buffer all packet bodies:

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder};

// By default, the `PacketParser` will drop packet bodies.  Use a
// `PacketParserBuilder` to change that.
let mut ppr =
    PacketParserBuilder::from_bytes(
        b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?
    .buffer_unread_content()
    .build()?;
while let PacketParserResult::Some(pp) = ppr {
    // Get the packet out of the parser and start parsing the next
    // packet, recursing.
    let (packet, next_ppr) = pp.recurse()?;
    ppr = next_ppr;

    // Process the packet.
    if let Packet::Literal(literal) = packet {
        // The body was buffered.
        assert_eq!(literal.body(), b"Hello world.");
    } else {
        unreachable!("We know it is a literal packet.");
    }
}

This example demonstrates how the body can be buffered by buffering an individual packet:

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// By default, the `PacketParser` will drop packet bodies.
let mut ppr =
    PacketParser::from_bytes(b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?;
while let PacketParserResult::Some(mut pp) = ppr {
    if let Packet::Literal(_) = pp.packet {
        // Buffer this packet's body.
        pp.buffer_unread_content()?;
    }

    // Get the packet out of the parser and start parsing the next
    // packet, recursing.
    let (packet, next_ppr) = pp.recurse()?;
    ppr = next_ppr;

    // Process the packet.
    if let Packet::Literal(literal) = packet {
        // The body was buffered.
        assert_eq!(literal.body(), b"Hello world.");
    } else {
        unreachable!("We know it is a literal packet.");
    }
}

This example demonstrates how to stream the packet body:

use std::io::Read;

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

let mut ppr =
    PacketParser::from_bytes(b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?;
while let PacketParserResult::Some(mut pp) = ppr {
    if let Packet::Literal(_) = pp.packet {
        // Stream the body.
        let mut buf = Vec::new();
        pp.read_to_end(&mut buf)?;
        assert_eq!(buf, b"Hello world.");
    } else {
        unreachable!("We know it is a literal packet.");
    }

    // Get the packet out of the parser and start parsing the next
    // packet, recursing.
    let (packet, next_ppr) = pp.recurse()?;
    ppr = next_ppr;

    // Process the packet.
    if let Packet::Literal(literal) = packet {
        // The body was streamed, not buffered.
        assert_eq!(literal.body(), b"");
    } else {
        unreachable!("We know it is a literal packet.");
    }
}

Packet Parser Design

There are two major concerns that inform the design of the parsing API.

First, when processing a container, it is possible to either recurse into the container, and process its children, or treat the contents of the container as an opaque byte stream, and process the packet following the container. The low-level PacketParser and mid-level PacketPileParser abstractions allow the caller to choose the behavior by either calling the PacketParser::recurse method or the PacketParser::next method, as appropriate. OpenPGP doesn't impose any restrictions on the amount of nesting. So, to prevent a denial of service attack, the parsers don't recurse more than DEFAULT_MAX_RECURSION_DEPTH times, by default.

Second, packets can contain an effectively unbounded amount of data. To avoid errors due to memory exhaustion, the PacketParser and PacketPileParser abstractions support parsing packets in a streaming manner, i.e., never buffering more than O(1) bytes of data. To do this, the parsers initially only parse a packet's header (which is rarely more than a few kilobytes of data), and return control to the caller. After inspecting that data, the caller can decide how to handle the packet's contents. If the content is deemed interesting, it can be streamed or buffered. Otherwise, it can be dropped. Streaming is possible not only for literal data packets, but also containers (other packets also support the interface, but just return EOF). For instance, encryption can be stripped by saving the decrypted content of an encryption packet, which is just an OpenPGP message.

Iterator Design

We explicitly chose to not use a callback-based API, but something that is closer to Rust's iterator API. Unfortunately, because a PacketParser needs mutable access to the input stream (so that the content can be streamed), only a single PacketParser item can be live at a time (without a fair amount of unsafe nastiness). This is incompatible with Rust's iterator concept, which allows any number of items to be live at any time. For instance:

let mut v = vec![1, 2, 3, 4];
let mut iter = v.iter_mut();

let x = iter.next().unwrap();
let y = iter.next().unwrap();

*x += 10; // This does not cause an error!
*y += 10;

Fields

packet: Packet

The packet that is being parsed.

Implementations

impl<'a> PacketParser<'a>[src]

pub fn encrypted(&self) -> bool[src]

Returns whether the packet's contents are encrypted.

This function returns true while processing an encryption container before it is decrypted using PacketParser::decrypt. Once successfully decrypted, it returns false.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::fmt::hex;
use openpgp::types::SymmetricAlgorithm;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse an encrypted message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    if let Packet::SEIP(_) = pp.packet {
        assert!(pp.encrypted());
        pp.decrypt(SymmetricAlgorithm::AES256,
                   &hex::decode("7EF4F08C44F780BEA866961423306166\
                                 B8912C43352F3D9617F745E4E3939710")?
                       .into())?;
        assert!(! pp.encrypted());
    }

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn last_path(&self) -> &[usize][src]

Returns the path of the last packet.

This function returns the path (see PacketPile::path_ref for a description of paths) of the packet last returned by a call to PacketParser::recurse or PacketParser::next. If no packet has been returned (i.e. the current packet is the first packet), this returns the empty slice.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a compressed message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    match pp.packet {
        Packet::CompressedData(_) => assert_eq!(pp.last_path(), &[]),
        Packet::Literal(_) => assert_eq!(pp.last_path(), &[0]),
        _ => (),
    }

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn path(&self) -> &[usize][src]

Returns the path of the current packet.

This function returns the path (see PacketPile::path_ref for a description of paths) of the packet currently being processed (see PacketParser::packet).

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a compressed message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    match pp.packet {
        Packet::CompressedData(_) => assert_eq!(pp.path(), &[0]),
        Packet::Literal(_) => assert_eq!(pp.path(), &[0, 0]),
        _ => (),
    }

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn recursion_depth(&self) -> isize[src]

The current packet's recursion depth.

A top-level packet has a recursion depth of 0. Packets in a top-level container have a recursion depth of 1, etc.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a compressed message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    match pp.packet {
        Packet::CompressedData(_) => assert_eq!(pp.recursion_depth(), 0),
        Packet::Literal(_) => assert_eq!(pp.recursion_depth(), 1),
        _ => (),
    }

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn last_recursion_depth(&self) -> Option<isize>[src]

The last packet's recursion depth.

A top-level packet has a recursion depth of 0. Packets in a top-level container have a recursion depth of 1, etc.

Note: if no packet has been returned yet, this returns None.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a compressed message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    match pp.packet {
        Packet::CompressedData(_) => assert_eq!(pp.last_recursion_depth(), None),
        Packet::Literal(_) => assert_eq!(pp.last_recursion_depth(), Some(0)),
        _ => (),
    }

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn possible_message(&self) -> Result<()>[src]

Returns whether the message appears to be an OpenPGP Message.

Only when the whole message has been processed is it possible to say whether the message is definitely an OpenPGP Message. Before that, it is only possible to say that the message is a valid prefix or definitely not an OpenPGP message (see PacketParserEOF::is_message).

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a compressed message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    pp.possible_message()?;

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn possible_keyring(&self) -> Result<()>[src]

Returns whether the message appears to be an OpenPGP keyring.

Only when the whole message has been processed is it possible to say whether the message is definitely an OpenPGP keyring. Before that, it is only possible to say that the message is a valid prefix or definitely not an OpenPGP keyring (see PacketParserEOF::is_keyring).

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a certificate.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    pp.possible_keyring()?;

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn possible_cert(&self) -> Result<()>[src]

Returns whether the message appears to be an OpenPGP Cert.

Only when the whole message has been processed is it possible to say whether the message is definitely an OpenPGP Cert. Before that, it is only possible to say that the message is a valid prefix or definitely not an OpenPGP Cert (see PacketParserEOF::is_cert).

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a certificate.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    pp.possible_cert()?;

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn next(self) -> Result<(Packet, PacketParserResult<'a>)>[src]

Finishes parsing the current packet and starts parsing the next one.

This function finishes parsing the current packet. By default, any unread content is dropped. (See PacketParsererBuilder for how to configure this.) It then creates a new packet parser for the next packet. If the current packet is a container, this function does not recurse into the container, but skips any packets it contains. To recurse into the container, use the recurse() method.

The return value is a tuple containing:

  • A Packet holding the fully processed old packet;

  • A PacketParser holding the new packet;

To determine the two packet's position within the parse tree, you can use last_path() and path(), respectively. To determine their depth, you can use last_recursion_depth() and recursion_depth(), respectively.

Note: A recursion depth of 0 means that the packet is a top-level packet, a recursion depth of 1 means that the packet is an immediate child of a top-level-packet, etc.

Since the packets are serialized in depth-first order and all interior nodes are visited, we know that if the recursion depth is the same, then the packets are siblings (they have a common parent) and not, e.g., cousins (they have a common grandparent). This is because, if we move up the tree, the only way to move back down is to first visit a new container (e.g., an aunt).

Using the two positions, we can compute the change in depth as new_depth - old_depth. Thus, if the change in depth is 0, the two packets are siblings. If the value is 1, the old packet is a container, and the new packet is its first child. And, if the value is -1, the new packet is contained in the old packet's grandparent. The idea is illustrated below:

            ancestor
            |       \
           ...      -n
            |
          grandparent
          |          \
        parent       -1
        |      \
     packet    0
        |
        1

Note: since this function does not automatically recurse into a container, the change in depth will always be non-positive. If the current container is empty, this function DOES pop that container off the container stack, and returns the following packet in the parent container.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    // Start parsing the next packet.
    ppr = pp.next()?.1;
}

pub fn recurse(self) -> Result<(Packet, PacketParserResult<'a>)>[src]

Finishes parsing the current packet and starts parsing the next one, recursing if possible.

This method is similar to the next() method (see that method for more details), but if the current packet is a container (and we haven't reached the maximum recursion depth, and the user hasn't started reading the packet's contents), we recurse into the container, and return a PacketParser for its first child. Otherwise, we return the next packet in the packet stream. If this function recurses, then the new packet's recursion depth will be last_recursion_depth() + 1; because we always visit interior nodes, we can't recurse more than one level at a time.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn buffer_unread_content(&mut self) -> Result<&[u8]>[src]

Causes the PacketParser to buffer the packet's contents.

The packet's contents can be retrieved using e.g. Container::body. In general, you should avoid buffering a packet's content and prefer streaming its content unless you are certain that the content is small.

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    // Process the packet.

    if let Packet::Literal(_) = pp.packet {
        assert!(pp.buffer_unread_content()?
                    .starts_with(b"A Cypherpunk's Manifesto"));
        if let Packet::Literal(l) = &pp.packet {
            assert!(l.body().starts_with(b"A Cypherpunk's Manifesto"));
            assert_eq!(l.body().len(), 5158);
        } else {
            unreachable!();
        }
    }

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn finish<'b>(&'b mut self) -> Result<&'b Packet>[src]

Finishes parsing the current packet.

By default, this drops any unread content. Use, for instance, PacketParserBuilder to customize the default behavior.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    let p = pp.finish()?;

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn header(&self) -> &Header[src]

Returns a reference to the current packet's header.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse a message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    pp.header().valid(false)?;

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

pub fn map(&self) -> Option<&Map>[src]

Returns a reference to the map (if any is written).

Examples

use sequoia_openpgp as openpgp;
use openpgp::parse::{Parse, PacketParserBuilder};

let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.";
let pp = PacketParserBuilder::from_bytes(message_data)?
    .map(true) // Enable mapping.
    .build()?
    .expect("One packet, not EOF");
let map = pp.map().expect("Mapping is enabled");

assert_eq!(map.iter().nth(0).unwrap().name(), "CTB");
assert_eq!(map.iter().nth(0).unwrap().offset(), 0);
assert_eq!(map.iter().nth(0).unwrap().as_bytes(), &[0xcb]);

pub fn take_map(&mut self) -> Option<Map>[src]

Takes the map (if any is written).

Examples

use sequoia_openpgp as openpgp;
use openpgp::parse::{Parse, PacketParserBuilder};

let message_data = b"\xcb\x12t\x00\x00\x00\x00\x00Hello world.";
let mut pp = PacketParserBuilder::from_bytes(message_data)?
    .map(true) // Enable mapping.
    .build()?
    .expect("One packet, not EOF");
let map = pp.take_map().expect("Mapping is enabled");

assert_eq!(map.iter().nth(0).unwrap().name(), "CTB");
assert_eq!(map.iter().nth(0).unwrap().offset(), 0);
assert_eq!(map.iter().nth(0).unwrap().as_bytes(), &[0xcb]);

impl<'a> PacketParser<'a>[src]

pub fn decrypt(
    &mut self,
    algo: SymmetricAlgorithm,
    key: &SessionKey
) -> Result<()>
[src]

Tries to decrypt the current packet.

On success, this function pushes one or more readers onto the PacketParser's reader stack, and clears the packet parser's encrypted flag (see PacketParser::encrypted).

If this function is called on a packet that does not contain encrypted data, or some of the data was already read, then it returns Error::InvalidOperation.

Examples

use sequoia_openpgp as openpgp;
use openpgp::Packet;
use openpgp::fmt::hex;
use openpgp::types::SymmetricAlgorithm;
use openpgp::parse::{Parse, PacketParserResult, PacketParser};

// Parse an encrypted message.
let message_data: &[u8] = // ...
let mut ppr = PacketParser::from_bytes(message_data)?;
while let PacketParserResult::Some(mut pp) = ppr {
    if let Packet::SEIP(_) = pp.packet {
        pp.decrypt(SymmetricAlgorithm::AES256,
                   &hex::decode("7EF4F08C44F780BEA866961423306166\
                                 B8912C43352F3D9617F745E4E3939710")?
                       .into())?;
    }

    // Start parsing the next packet, recursing.
    ppr = pp.recurse()?.1;
}

Security Considerations

This functions returns rich errors in case the decryption fails. In combination with certain asymmetric algorithms (RSA), this may lead to compromise of secret key material. See Section 14 of RFC 4880. Do not relay these errors in situations where an attacker can request decryption of messages in an automated fashion.

Trait Implementations

impl<'a> Debug for PacketParser<'a>[src]

impl<'a> Display for PacketParser<'a>[src]

impl<'a> Parse<'a, PacketParserResult<'a>> for PacketParser<'a>[src]

fn from_reader<R: Read + 'a>(reader: R) -> Result<PacketParserResult<'a>>[src]

Starts parsing an OpenPGP message stored in a std::io::Read object.

This function returns a PacketParser for the first packet in the stream.

fn from_file<P: AsRef<Path>>(path: P) -> Result<PacketParserResult<'a>>[src]

Starts parsing an OpenPGP message stored in a file named path.

This function returns a PacketParser for the first packet in the stream.

fn from_bytes<D: AsRef<[u8]> + ?Sized>(
    data: &'a D
) -> Result<PacketParserResult<'a>>
[src]

Starts parsing an OpenPGP message stored in a buffer.

This function returns a PacketParser for the first packet in the stream.

impl<'a> Read for PacketParser<'a>[src]

This interface allows a caller to read the content of a PacketParser using the Read interface. This is essential to supporting streaming operation.

Note: it is safe to mix the use of the std::io::Read and BufferedReader interfaces.

Auto Trait Implementations

impl<'a> !RefUnwindSafe for PacketParser<'a>

impl<'a> !Send for PacketParser<'a>

impl<'a> !Sync for PacketParser<'a>

impl<'a> Unpin for PacketParser<'a>

impl<'a> !UnwindSafe for PacketParser<'a>

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T> ToString for T where
    T: Display + ?Sized
[src]

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.