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;