summaryrefslogtreecommitdiffstats
path: root/openpgp/src/message/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/message/mod.rs')
-rw-r--r--openpgp/src/message/mod.rs336
1 files changed, 238 insertions, 98 deletions
diff --git a/openpgp/src/message/mod.rs b/openpgp/src/message/mod.rs
index 1e3f3e69..9c30f74f 100644
--- a/openpgp/src/message/mod.rs
+++ b/openpgp/src/message/mod.rs
@@ -1,4 +1,4 @@
-//! OpenPGP Message support.
+//! Message support.
//!
//! An OpenPGP message is a sequence of OpenPGP packets that
//! corresponds to an optionally signed, optionally encrypted,
@@ -8,8 +8,20 @@
//! This module provides support for validating and working with
//! OpenPGP messages.
//!
+//! Example of ASCII-armored OpenPGP message:
+//!
+//! ```txt
+//! -----BEGIN PGP MESSAGE-----
+//!
+//! yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS
+//! vBSFjNSiVHsuAA==
+//! =njUN
+//! -----END PGP MESSAGE-----
+//! ```
+//!
//! [Section 11.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.3
+use std::convert::TryFrom;
use std::fmt;
use std::io;
use std::path::Path;
@@ -18,7 +30,6 @@ use crate::Result;
use crate::Error;
use crate::Packet;
use crate::PacketPile;
-use crate::Message;
use crate::packet::Literal;
use crate::packet::Tag;
use crate::parse::Parse;
@@ -60,9 +71,9 @@ impl From<MessageParserError> for anyhow::Error {
}
-/// Whether a packet sequence is a valid OpenPGP Message.
+/// Represents the status of a parsed message.
#[derive(Debug)]
-pub enum MessageValidity {
+pub(crate) enum MessageValidity {
/// The packet sequence is a valid OpenPGP message.
Message,
/// The packet sequence appears to be a valid OpenPGP message that
@@ -73,6 +84,7 @@ pub enum MessageValidity {
Error(anyhow::Error),
}
+#[allow(unused)]
impl MessageValidity {
/// Returns whether the packet sequence is a valid message.
///
@@ -112,7 +124,7 @@ impl MessageValidity {
/// Used to help validate a packet sequence is a valid OpenPGP message.
#[derive(Debug)]
-pub struct MessageValidator {
+pub(crate) struct MessageValidator {
tokens: Vec<Token>,
finished: bool,
// Once a raw token is pushed, this is set to None and pushing
@@ -140,29 +152,6 @@ impl MessageValidator {
}
}
- /// Returns whether the packet sequence is a valid message.
- ///
- /// Note: a `MessageValidator` will only return this after
- /// `MessageValidator::finish` has been called.
- pub fn is_message(&self) -> bool {
- self.check().is_message()
- }
-
- /// Returns whether the packet sequence forms a valid message
- /// prefix.
- ///
- /// Note: a `MessageValidator` will only return this before
- /// `MessageValidator::finish` has been called.
- pub fn is_message_prefix(&self) -> bool {
- self.check().is_message_prefix()
- }
-
- /// Returns whether the packet sequence is definitely not a valid
- /// OpenPGP Message.
- pub fn is_err(&self) -> bool {
- self.check().is_err()
- }
-
/// Adds a token to the token stream.
#[cfg(test)]
pub(crate) fn push_raw(&mut self, token: Token) {
@@ -278,7 +267,7 @@ impl MessageValidator {
/// this function will only ever return either
/// MessageValidity::MessagePrefix or MessageValidity::Error. Once
/// MessageValidity::finish() has been called, then only
- /// MessageValidity::Message or MessageValidity::Bad will be called.
+ /// MessageValidity::Message or MessageValidity::Error will be returned.
pub fn check(&self) -> MessageValidity {
if let Some(ref err) = self.error {
return MessageValidity::Error((*err).clone().into());
@@ -306,7 +295,62 @@ impl MessageValidator {
}
}
}
-
+
+// DOC-HACK: To avoid having a top-level re-export of `Cert`, we move
+// it in a submodule `def`.
+pub use def::Message;
+mod def {
+use super::*;
+/// A message.
+///
+/// An OpenPGP message is a structured sequence of OpenPGP packets.
+/// Basically, it's an optionally encrypted, optionally signed literal
+/// data packet. The exact structure is defined in [Section 11.3 of RFC
+/// 4880].
+///
+/// [Section 11.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.3
+///
+/// [ASCII Armored Messages] are wrapped in `-----BEGIN PGP MESSAGE-----` header
+/// and `-----END PGP MESSAGE-----` tail lines:
+///
+/// ```txt
+/// -----BEGIN PGP MESSAGE-----
+///
+/// xA0DAAoW5saJekzviSQByxBiAAAAAADYtdiv2KfZgtipwnUEABYKACcFglwJHYoW
+/// IQRnpIdTo4Cms7fffcXmxol6TO+JJAkQ5saJekzviSQAAIJ6APwK6FxtHXn8txDl
+/// tBFsIXlOSLOs4BvArlZzZSMomIyFLAEAwCLJUChMICDxWXRlHxORqU5x6hlO3DdW
+/// sl/1DAbnRgI=
+/// =AqoO
+/// -----END PGP MESSAGE-----
+/// ```
+///
+/// [ASCII Armored Messages]: https://tools.ietf.org/html/rfc4880#section-6.6
+///
+/// # Examples
+///
+/// Creating a Messaage encrypted with a password.
+///
+/// ```
+/// # use sequoia_openpgp as openpgp;
+/// # f().unwrap(); fn f() -> sequoia_openpgp::Result<()> {
+/// use std::io::Write;
+/// use openpgp::serialize::stream::{Message, Encryptor, LiteralWriter};
+///
+/// let mut sink = vec![];
+/// let message = Encryptor::with_passwords(
+/// Message::new(&mut sink), Some("ściśle tajne")).build()?;
+/// let mut w = LiteralWriter::new(message).build()?;
+/// w.write_all(b"Hello world.")?;
+/// w.finalize()?;
+/// # Ok(()) }
+/// ```
+#[derive(PartialEq)]
+pub struct Message {
+ /// A message is just a validated packet pile.
+ pub(super) pile: PacketPile,
+}
+} // doc-hack, see above
+
impl fmt::Debug for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Message")
@@ -318,29 +362,29 @@ impl fmt::Debug for Message {
impl<'a> Parse<'a, Message> for Message {
/// Reads a `Message` from the specified reader.
///
- /// See [`Message::from_packet_pile`] for more details.
+ /// See [`Message::try_from`] for more details.
///
- /// [`Message::from_packet_pile`]: #method.from_packet_pile
+ /// [`Message::try_from`]: #method.try_from
fn from_reader<R: 'a + io::Read>(reader: R) -> Result<Self> {
- Self::from_packet_pile(PacketPile::from_reader(reader)?)
+ Self::try_from(PacketPile::from_reader(reader)?)
}
/// Reads a `Message` from the specified file.
///
- /// See [`Message::from_packet_pile`] for more details.
+ /// See [`Message::try_from`] for more details.
///
- /// [`Message::from_packet_pile`]: #method.from_packet_pile
+ /// [`Message::try_from`]: #method.try_from
fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
- Self::from_packet_pile(PacketPile::from_file(path)?)
+ Self::try_from(PacketPile::from_file(path)?)
}
/// Reads a `Message` from `buf`.
///
- /// See [`Message::from_packet_pile`] for more details.
+ /// See [`Message::try_from`] for more details.
///
- /// [`Message::from_packet_pile`]: #method.from_packet_pile
+ /// [`Message::try_from`]: #method.try_from
fn from_bytes<D: AsRef<[u8]> + ?Sized>(data: &'a D) -> Result<Self> {
- Self::from_packet_pile(PacketPile::from_bytes(data)?)
+ Self::try_from(PacketPile::from_bytes(data)?)
}
}
@@ -353,6 +397,51 @@ impl std::str::FromStr for Message {
}
impl Message {
+ /// Returns the body of the message.
+ ///
+ /// Returns `None` if no literal data packet is found. This
+ /// happens if a SEIP container has not been decrypted.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # extern crate sequoia_openpgp as openpgp;
+ /// # fn main() { f().unwrap(); }
+ /// # fn f() -> openpgp::Result<()> {
+ /// use std::io;
+ /// use std::io::Read;
+ /// use openpgp::Message;
+ /// use openpgp::armor::{Reader, ReaderMode};
+ /// use openpgp::parse::Parse;
+ ///
+ /// let data = "yxJiAAAAAABIZWxsbyB3b3JsZCE="; // base64 over literal data packet
+ ///
+ /// let mut cursor = io::Cursor::new(&data);
+ /// let mut reader = Reader::new(&mut cursor, ReaderMode::VeryTolerant);
+ ///
+ /// let mut buf = Vec::new();
+ /// reader.read_to_end(&mut buf)?;
+ ///
+ /// let message = Message::from_bytes(&buf)?;
+ /// assert_eq!(message.body().unwrap().body(), b"Hello world!");
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn body(&self) -> Option<&Literal> {
+ for packet in self.pile.descendants() {
+ if let &Packet::Literal(ref l) = packet {
+ return Some(l);
+ }
+ }
+
+ // No literal data packet found.
+ None
+ }
+}
+
+impl TryFrom<PacketPile> for Message {
+ type Error = anyhow::Error;
+
/// Converts the `PacketPile` to a `Message`.
///
/// Converting a `PacketPile` to a `Message` doesn't change the
@@ -365,7 +454,7 @@ impl Message {
/// or still compressed parts are valid messages.
///
/// [Section 11.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-11.3
- pub fn from_packet_pile(pile: PacketPile) -> Result<Self> {
+ fn try_from(pile: PacketPile) -> Result<Self> {
let mut v = MessageValidator::new();
for (mut path, packet) in pile.descendants().paths() {
match packet {
@@ -386,9 +475,7 @@ impl Message {
// we treat the content as an opaque message.
path.push(0);
- if packet.children().next().is_none()
- && packet.body().is_some()
- {
+ if packet.children().is_none() {
v.push_token(Token::OpaqueContent, &path);
}
}
@@ -398,7 +485,7 @@ impl Message {
v.finish();
match v.check() {
- MessageValidity::Message => Ok(Message { pile: pile }),
+ MessageValidity::Message => Ok(Message { pile }),
MessageValidity::MessagePrefix => unreachable!(),
// We really want to squash the lexer's error: it is an
// internal detail that may change, and meaningless even
@@ -406,29 +493,18 @@ impl Message {
MessageValidity::Error(e) => Err(e.into()),
}
}
+}
+
+impl TryFrom<Vec<Packet>> for Message {
+ type Error = anyhow::Error;
/// Converts the vector of `Packets` to a `Message`.
///
- /// See [`Message::from_packet_pile`] for more details.
- ///
- /// [`Message::from_packet_pile`]: #method.from_packet_pile
- pub fn from_packets(packets: Vec<Packet>) -> Result<Self> {
- Self::from_packet_pile(PacketPile::from(packets))
- }
-
- /// Returns the body of the message.
+ /// See [`Message::try_from`] for more details.
///
- /// Returns `None` if no literal data packet is found. This
- /// happens if a SEIP container has not been decrypted.
- pub fn body(&self) -> Option<&Literal> {
- for packet in self.pile.descendants() {
- if let &Packet::Literal(ref l) = packet {
- return Some(l);
- }
- }
-
- // No literal data packet found.
- None
+ /// [`Message::try_from`]: #method.try_from
+ fn try_from(packets: Vec<Packet>) -> Result<Self> {
+ Self::try_from(PacketPile::from(packets))
}
}
@@ -457,9 +533,8 @@ mod tests {
use crate::PublicKeyAlgorithm;
use crate::SignatureType;
use crate::crypto::S2K;
- use crate::crypto::mpis::{Ciphertext, MPI};
+ use crate::crypto::mpi::{Ciphertext, MPI};
use crate::packet::prelude::*;
- use crate::KeyID;
#[test]
fn tokens() {
@@ -731,7 +806,7 @@ mod tests {
fn basic() {
// Empty.
// => bad.
- let message = Message::from_packets(vec![]);
+ let message = Message::try_from(vec![]);
assert!(message.is_err(), "{:?}", message);
// 0: Literal
@@ -741,7 +816,7 @@ mod tests {
lit.set_body(b"data".to_vec());
packets.push(lit.into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_ok(), "{:?}", message);
}
@@ -759,7 +834,7 @@ mod tests {
.push(lit.clone().into())
.into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_ok(), "{:?}", message);
// 0: CompressedData
@@ -773,7 +848,7 @@ mod tests {
.push(lit.clone().into())
.into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_err(), "{:?}", message);
// 0: CompressedData
@@ -787,7 +862,7 @@ mod tests {
.into());
packets.push(lit.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_err(), "{:?}", message);
// 0: CompressedData
@@ -803,7 +878,7 @@ mod tests {
.into())
.into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_ok(), "{:?}", message);
}
@@ -817,7 +892,7 @@ mod tests {
crate::packet::key::Key4::generate_ecc(true, crate::types::Curve::Ed25519)
.unwrap().into();
let mut pair = key.clone().into_keypair().unwrap();
- let sig = crate::packet::signature::Builder::new(SignatureType::Binary)
+ let sig = crate::packet::signature::SignatureBuilder::new(SignatureType::Binary)
.sign_hash(&mut pair, hash.context().unwrap()).unwrap();
// 0: OnePassSig
@@ -825,7 +900,7 @@ mod tests {
let mut packets : Vec<Packet> = Vec::new();
packets.push(OnePassSig3::new(SignatureType::Binary).into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_err(), "{:?}", message);
// 0: OnePassSig
@@ -835,7 +910,7 @@ mod tests {
packets.push(OnePassSig3::new(SignatureType::Binary).into());
packets.push(lit.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_err(), "{:?}", message);
// 0: OnePassSig
@@ -847,7 +922,7 @@ mod tests {
packets.push(lit.clone().into());
packets.push(sig.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_ok(), "{:?}", message);
// 0: OnePassSig
@@ -861,7 +936,7 @@ mod tests {
packets.push(sig.clone().into());
packets.push(sig.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_err(), "{:?}", message);
// 0: OnePassSig
@@ -877,7 +952,7 @@ mod tests {
packets.push(sig.clone().into());
packets.push(sig.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_ok(), "{:?}", message);
// 0: OnePassSig
@@ -895,7 +970,7 @@ mod tests {
packets.push(sig.clone().into());
packets.push(sig.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_err(), "{:?}", message);
// 0: OnePassSig
@@ -915,7 +990,7 @@ mod tests {
packets.push(sig.clone().into());
packets.push(sig.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_ok(), "{:?}", message);
}
@@ -929,7 +1004,7 @@ mod tests {
crate::packet::key::Key4::generate_ecc(true, crate::types::Curve::Ed25519)
.unwrap().into();
let mut pair = key.clone().into_keypair().unwrap();
- let sig = crate::packet::signature::Builder::new(SignatureType::Binary)
+ let sig = crate::packet::signature::SignatureBuilder::new(SignatureType::Binary)
.sign_hash(&mut pair, hash.context().unwrap()).unwrap();
// 0: Signature
@@ -937,7 +1012,7 @@ mod tests {
let mut packets : Vec<Packet> = Vec::new();
packets.push(sig.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_err(), "{:?}", message);
// 0: Signature
@@ -947,7 +1022,7 @@ mod tests {
packets.push(sig.clone().into());
packets.push(lit.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_ok(), "{:?}", message);
// 0: Signature
@@ -959,7 +1034,7 @@ mod tests {
packets.push(sig.clone().into());
packets.push(lit.clone().into());
- let message = Message::from_packets(packets);
+ let message = Message::try_from(packets);
assert!(message.is_ok(), "{:?}", message);
}
@@ -983,7 +1058,7 @@ mod tests {
S2K::Simple { hash: HashAlgorithm::SHA256 },
&sk,
&"12345678".into()).unwrap().into());
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_err(), "{:?}", message);
// 0: SK-ESK
@@ -994,7 +1069,7 @@ mod tests {
assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>()
== [ Tag::SKESK, Tag::Literal ]);
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_err(), "{:?}", message);
// 0: SK-ESK
@@ -1003,16 +1078,16 @@ mod tests {
// 1: MDC
// => good.
let mut seip = SEIP1::new();
- seip.children_mut().push(
+ seip.children_mut().unwrap().push(
lit.clone().into());
- seip.children_mut().push(
+ seip.children_mut().unwrap().push(
MDC::from([0u8; 20]).into());
packets[1] = seip.into();
assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>()
== [ Tag::SKESK, Tag::SEIP ]);
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_ok(), "{:#?}", message);
// 0: SK-ESK
@@ -1027,7 +1102,7 @@ mod tests {
assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>()
== [ Tag::SKESK, Tag::SEIP, Tag::SKESK ]);
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_err(), "{:#?}", message);
// 0: SK-ESK
@@ -1041,7 +1116,7 @@ mod tests {
assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>()
== [ Tag::SKESK, Tag::SKESK, Tag::SEIP ]);
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_ok(), "{:#?}", message);
// 0: SK-ESK
@@ -1059,7 +1134,7 @@ mod tests {
assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>()
== [ Tag::SKESK, Tag::SKESK, Tag::SEIP, Tag::SEIP ]);
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_err(), "{:#?}", message);
// 0: SK-ESK
@@ -1074,7 +1149,7 @@ mod tests {
assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>()
== [ Tag::SKESK, Tag::SKESK, Tag::SEIP, Tag::Literal ]);
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_err(), "{:#?}", message);
// 0: SK-ESK
@@ -1086,12 +1161,12 @@ mod tests {
// => bad.
packets.remove(3);
packets[2].container_mut().unwrap()
- .children_mut().push(lit.clone().into());
+ .children_mut().unwrap().push(lit.clone().into());
assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>()
== [ Tag::SKESK, Tag::SKESK, Tag::SEIP ]);
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_err(), "{:#?}", message);
// 0: SK-ESK
@@ -1100,26 +1175,91 @@ mod tests {
// 2: SEIP
// 0: Literal
// => good.
- packets[2].container_mut().unwrap().children_mut().pop().unwrap();
+ packets[2].container_mut().unwrap()
+ .children_mut().unwrap().pop().unwrap();
#[allow(deprecated)]
packets.insert(
1,
PKESK3::new(
- KeyID::from_hex("0000111122223333").unwrap(),
+ "0000111122223333".parse().unwrap(),
PublicKeyAlgorithm::RSAEncrypt,
Ciphertext::RSA { c: MPI::new(&[]) }).unwrap().into());
assert!(packets.iter().map(|p| p.tag()).collect::<Vec<Tag>>()
== [ Tag::SKESK, Tag::PKESK, Tag::SKESK, Tag::SEIP ]);
- let message = Message::from_packets(packets.clone());
+ let message = Message::try_from(packets.clone());
assert!(message.is_ok(), "{:#?}", message);
}
#[test]
fn message_is_send_and_sync() {
fn f<T: Send + Sync>(_: T) {}
- f(Message::from_packets(vec![]));
+ f(Message::try_from(vec![]));
+ }
+
+ #[test]
+ fn basic_message_validator() {
+ use crate::message::{MessageValidator, MessageValidity, Token};
+
+ let mut l = MessageValidator::new();
+ l.push_token(Token::Literal, &[0]);
+ l.finish();
+ assert!(if let MessageValidity::Message = l.check() { true } else { false });
+ }
+
+ #[test]
+ fn message_validator_push_token() {
+ use crate::message::{MessageValidator, MessageValidity, Token};
+
+ let mut l = MessageValidator::new();
+ l.push_token(Token::Literal, &[0]);
+ l.finish();
+
+ assert!(if let MessageValidity::Message = l.check() { true } else { false });
+ }
+
+ #[test]
+ fn message_validator_push() {
+ use crate::message::{MessageValidator, MessageValidity};
+ use crate::packet::Tag;
+
+ let mut l = MessageValidator::new();
+ l.push(Tag::Literal, &[0]);
+ l.finish();
+
+ assert!(if let MessageValidity::Message = l.check() { true } else { false });
+ }
+
+ #[test]
+ fn message_validator_finish() {
+ use crate::message::{MessageValidator, MessageValidity};
+
+ let mut l = MessageValidator::new();
+ l.finish();
+
+ assert!(if let MessageValidity::Error(_) = l.check() { true } else { false });
+ }
+
+ #[test]
+ fn message_validator_check() {
+ use crate::message::{MessageValidator, MessageValidity};
+ use crate::packet::Tag;
+
+ // No packets will return an error.
+ let mut l = MessageValidator::new();
+ assert!(if let MessageValidity::MessagePrefix = l.check() { true } else { false });
+ l.finish();
+
+ assert!(if let MessageValidity::Error(_) = l.check() { true } else { false });
+
+ // Simple one-literal message.
+ let mut l = MessageValidator::new();
+ l.push(Tag::Literal, &[0]);
+ assert!(if let MessageValidity::MessagePrefix = l.check() { true } else { false });
+ l.finish();
+
+ assert!(if let MessageValidity::Message = l.check() { true } else { false });
}
}