pyrus_crypto/message/mod.rs
1mod header;
2pub use header::{Header, MessageType};
3
4mod builder;
5pub use builder::MessageBuilder;
6pub use builder::MessageFinal;
7
8mod parser;
9pub use parser::MessageParser;
10
11use base64::prelude::*;
12
13use postcard;
14use serde::{Deserialize, Serialize};
15
16use crate::Fingerprint;
17use crate::cert::Cert;
18use crate::error::{CryptoError, Result};
19
20mod ser;
21
22/// A struct describing a message.
23///
24/// Each message is composed of:
25/// * a payload - optionally encrypted binary data,
26/// * a header - [`Header`] struct containing the message's metadata.
27///
28/// A message is constructed by using the [`Message::new`] function which
29/// returns a [`MessageBuilder`]. Then the builder configures the message using
30/// the state pattern and after calling `finalize()` returns a message ready
31/// for sending.
32///
33/// # Serialization
34///
35/// Messages implement [`serde::Serialize`] and [`serde::Deserialize`]
36/// traits, which allow for serialization and deserialization using any
37/// serde format. Additionally for human readable formats messages
38/// serialize bytes as hex encoded strings to please the users' eyes.
39///
40/// # Examples
41///
42/// Constructing a signed and encrypted message.
43/// ```rust
44/// # use std::error::Error;
45/// use pyrus_crypto::message::Message;
46/// use pyrus_crypto::message::MessageFinal;
47/// # use pyrus_crypto::cert::Cert;
48/// # use std::sync::Arc;
49///
50/// # fn main() -> Result<(), Box<dyn Error>> {
51/// # let larry: Arc<Cert> = Arc::new(Cert::new("Larry <larry@gentoo.org>"));
52/// # let agentcow = Arc::new(Cert::new("agentcow"));
53/// # let agenthorse = Arc::new(Cert::new("agenthorse"));
54/// # let friends = vec![agentcow.clone(), agenthorse.clone()];
55/// let secret_text = b"Let's meet up in the evening";
56/// let msg = Message::new(&larry)? // returns MessageBuilder here
57/// .write(&secret_text[..])? // MessageBuilder
58/// .sign() // SignedMessage (almost the same API as the builder)
59/// .encrypt_for(&friends)? // AsmEncryptedMessage (can only finalize)
60/// .finalize()?; // returns Message
61/// # Ok(())
62/// # }
63/// ```
64///
65/// The message may then be base64 encoded for transmission:
66/// ```rust
67/// # use std::error::Error;
68/// # use pyrus_crypto::message::Message;
69/// # use pyrus_crypto::message::MessageFinal;
70/// # use pyrus_crypto::cert::Cert;
71/// # use std::sync::Arc;
72/// # fn main() -> Result<(), Box<dyn Error>> {
73/// # let larry: Arc<Cert> = Arc::new(Cert::new("Larry <larry@gentoo.org>"));
74/// # let agentcow = Arc::new(Cert::new("agentcow"));
75/// # let agenthorse = Arc::new(Cert::new("agenthorse"));
76/// # let friends = vec![agentcow.clone(), agenthorse.clone()];
77/// # let secret_text = b"Let's meet up in the evening";
78/// # let msg = Message::new(&larry)?
79/// # .write(&secret_text[..])?
80/// # .sign()
81/// # .encrypt_for(&friends)?
82/// # .finalize()?;
83/// let b64msg: String = msg.b64encode()?;
84/// # let msg_recv = Message::b64decode(b64msg.as_bytes())?;
85/// # assert_eq!(msg, msg_recv);
86/// # Ok(())
87/// # }
88/// ```
89/// And then decoded back into a message:
90/// ```rust
91/// # use std::error::Error;
92/// # use pyrus_crypto::message::Message;
93/// # use pyrus_crypto::message::MessageFinal;
94/// # use pyrus_crypto::cert::Cert;
95/// # use std::sync::Arc;
96/// # fn main() -> Result<(), Box<dyn Error>> {
97/// # let larry: Arc<Cert> = Arc::new(Cert::new("Larry <larry@gentoo.org>"));
98/// # let agentcow = Arc::new(Cert::new("agentcow"));
99/// # let agenthorse = Arc::new(Cert::new("agenthorse"));
100/// # let friends = vec![agentcow.clone(), agenthorse.clone()];
101/// # let secret_text = b"Let's meet up in the evening";
102/// # let msg = Message::new(&larry)?
103/// # .write(&secret_text[..])?
104/// # .sign()
105/// # .encrypt_for(&friends)?
106/// # .finalize()?;
107/// # let b64msg: String = msg.b64encode()?;
108/// let msg_recv = Message::b64decode(b64msg.as_bytes())?;
109/// assert_eq!(msg, msg_recv);
110/// # Ok(())
111/// # }
112/// ```
113#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
114pub struct Message {
115 header: Header,
116 #[serde(with = "ser::payload")]
117 payload: Vec<u8>,
118}
119
120impl Message {
121 /// Starts the construction of the message by returning a
122 /// [`MessageBuilder`]. The builder should be a temporary object
123 /// and live only for a short duration in a method call chain.
124 ///
125 /// # Errors
126 /// * [`CryptoError::NotSecret`] if the issuer's certificate does not
127 /// contain secret keys. (This is not your certificate, why do you try to
128 /// impersonate people?)
129 pub fn new<'a>(issuer: &'a Cert) -> Result<MessageBuilder<'a>> {
130 if issuer.is_secret() {
131 Ok(MessageBuilder {
132 issuer,
133 payload: Vec::new(),
134 })
135 } else {
136 Err(CryptoError::NotSecret)
137 }
138 }
139
140 /// Reads the header of the message, discards any associated data and
141 /// returns the type of the message. See [`MessageDesc`] for more details.
142 pub fn describe(&self) -> MessageDesc {
143 match self.header.msg_type {
144 MessageType::Signed { .. } => MessageDesc::Signed,
145 MessageType::EncryptedSym { .. } => MessageDesc::EncryptedSym,
146 MessageType::EncryptedAsm { .. } => MessageDesc::EncryptedAsm,
147 MessageType::SignedEncryptedSym { .. } => MessageDesc::SignedEncryptedSym,
148 MessageType::SignedEncryptedAsm { .. } => MessageDesc::SignedEncryptedAsm,
149 }
150 }
151
152 /// Convenience function. Instead of checking the header for the message
153 /// issuer, this function may be use to forward it.
154 pub fn issuer(&self) -> &Fingerprint {
155 &self.header.issuer
156 }
157
158 /// Returns the header of this message for inspection. In reality this is
159 /// only useful for automated message parsing. The [`MessageParser`]
160 /// internals access the header differently.
161 pub fn header(&self) -> &Header {
162 &self.header
163 }
164
165 /// Returns the payload of this message. The payload is the optionally
166 /// encrypted message content and on its own is not useful unless the goal
167 /// is to implement automated message parsing.
168 pub fn payload(&self) -> &[u8] {
169 &self.payload
170 }
171
172 /// Starts the process of message parsing. See [`MessageParser`] for more
173 /// details.
174 ///
175 /// # Errors
176 /// * [`CryptoError::NotSecret`] if the recipient's (decrypting)
177 /// certificate does not contain secret keys.
178 pub fn parse<'a>(self, recipient: &'a Cert) -> Result<MessageParser<'a>> {
179 if recipient.is_secret() {
180 Ok(MessageParser {
181 recipient,
182 desc: self.describe(),
183 message: self,
184 payload: Vec::new(),
185 verified: None
186 })
187 } else {
188 Err(CryptoError::NotSecret)
189 }
190 }
191
192 /// Encodes the message as a base64 string.
193 ///
194 /// # Errors
195 /// * [`CryptoError::SerializationError`] if a serialization error occured.
196 pub fn b64encode(&self) -> Result<String> {
197 let bytes = postcard::to_stdvec(self)?;
198 Ok(BASE64_STANDARD.encode(bytes))
199 }
200
201 /// Decodes a message from a base64 representation.
202 ///
203 /// # Errors
204 /// * [`CryptoError::SerializationError`] if a serialization error occured.
205 /// * [`CryptoError::DecodingError`] if the base64 representation is
206 /// invalid.
207 pub fn b64decode(repr: impl AsRef<[u8]>) -> Result<Self> {
208 let bytes = BASE64_STANDARD.decode(repr.as_ref())?;
209 Ok(postcard::from_bytes(&bytes)?)
210 }
211}
212
213/// Describes the message structure.
214///
215/// In case the message is signed it should always be signed first
216/// and then encrypted, i.e. the signature should be done over the
217/// plaintext.
218#[derive(Debug)]
219pub enum MessageDesc {
220 /// Plaintext message. Only possible after using a message parser.
221 Plaintext,
222 /// Signed message.
223 Signed,
224 /// Symmetrically encrypted message.
225 EncryptedSym,
226 /// Asymmetrically encrypted message.
227 EncryptedAsm,
228 /// Signed and symmetrically encrypted message.
229 SignedEncryptedSym,
230 /// Signed and asymmetrically encrypted message.
231 SignedEncryptedAsm,
232}
233
234impl std::fmt::Display for MessageDesc {
235 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 let name = match self {
237 MessageDesc::Plaintext => "Plaintext",
238 MessageDesc::Signed => "Signed",
239 MessageDesc::EncryptedSym => "Symmetrically Encrypted",
240 MessageDesc::EncryptedAsm => "Asymmetrically Encrypted",
241 MessageDesc::SignedEncryptedSym => "Signed & Symmetrically Encrypted",
242 MessageDesc::SignedEncryptedAsm => "Signed & Asymmetrically Encrypted",
243 };
244 write!(f, "{name}")
245 }
246}
247
248#[cfg(test)]
249mod tests;