pyrus_crypto/message/
parser.rs

1use std::sync::Arc;
2
3use zeroize::Zeroizing;
4
5use crate::cert::Cert;
6use crate::crypto;
7use crate::error::{CryptoError, Result};
8
9use super::{header::*, Message, MessageDesc};
10
11/// Parses a message.
12///
13/// This struct implements something akin to the state pattern by using 
14/// enums and internal variables.
15///
16/// # Examples
17///
18/// Typical call chain used to decrypt a message.
19/// ```rust
20/// # use std::error::Error;
21/// use pyrus_crypto::prelude::*;
22/// # use std::sync::Arc;
23///
24/// # fn main() -> Result<(), Box<dyn Error>> {
25/// # let larry = Arc::new(Cert::new("Larry <larry@gentoo.org>"));
26/// # let agentcow = Arc::new(Cert::new("agentcow"));
27/// # let agenthorse = Arc::new(Cert::new("agenthorse"));
28/// # let friends = vec![agentcow.clone(), agenthorse.clone()];
29///
30/// let secret_text = b"Hello there.";
31/// # let message = Message::new(&larry)?
32/// #     .write(&secret_text[..])?
33/// #     .sign()
34/// #     .encrypt_for(&friends)?
35/// #     .finalize()?;
36/// let (message, content, signature) = message
37///     .parse(&agentcow)? // recieve the message as agentcow
38///     .decrypt(larry.clone())? // asymmetrically decrypt from larry
39///     .verify_signature(larry.clone())? // verify the signature from larry
40///     .finalize(); // finish parsing, return the results as a tuple
41///                  // (<original message>, <decrypted content>, Option<signature>)
42///
43/// assert_eq!(&secret_text[..], &content[..]);
44/// assert!(signature.is_some()); // signature is Some
45/// assert!(signature.unwrap()); // signature is good
46/// # Ok(())
47/// # }
48/// ```
49///
50/// Decrypting a symmetric message.
51/// ```rust
52/// # use std::error::Error;
53/// use pyrus_crypto::prelude::*;
54///
55/// # fn main() -> Result<(), Box<dyn Error>> {
56/// # let larry: Cert = Cert::new("Larry <larry@gentoo.org>");
57/// let secret_text = b"This is a very secret message";
58/// # let message = Message::new(&larry)?
59/// #     .write(&secret_text[..])?
60/// #     .encrypt_with(b"password123")? // don't use such passwords
61/// #     .finalize()?;
62/// // another of the aforementioned shortcomings
63/// # let dummy = Cert::new("dummy cert");
64/// // assume we already have a message
65/// let (_, content, sig) = message
66///     .parse(&dummy)? // a dummy certificate is needed
67///     .decrypt_with(&b"password123"[..])?
68///     .finalize();
69///
70/// assert_eq!(&secret_text[..], &content[..]);
71/// assert!(sig.is_none()); // the message is not signed
72/// # Ok(())
73/// # }
74pub struct MessageParser<'a> {
75    pub(super) recipient: &'a Cert,
76    pub(super) desc: MessageDesc,
77    pub(super) message: Message,
78    pub(super) payload: Vec<u8>,
79    pub(super) verified: Option<bool>,
80}
81
82impl<'a> MessageParser<'a> {
83    /// Decrypts an asymmetrically encrypted message using `issuer`'s public 
84    /// key and a private certificate supplied to [`Message::parse`].
85    ///
86    /// # Errors
87    /// * [`CryptoError::NotARecipient`] if the private certificate holder is 
88    /// not a recipient,
89    /// * [`CryptoError::NotType`] if the message cannot be asymmetrically 
90    /// decrypted,
91    /// * [`CryptoError::UnauthenticCipher`] if the message's AAD cannot be 
92    /// verified,
93    /// * [`CryptoError::InsufficientMemory`],
94    /// * [`CryptoError::SerializationError`].
95    pub fn decrypt(mut self, issuer: Arc<Cert>) -> Result<Self> {
96        let (key, nonce) = match self.message.header.msg_type {
97            MessageType::EncryptedAsm {
98                ref keys,
99                ref nonce,
100            }
101            | MessageType::SignedEncryptedAsm {
102                ref keys,
103                ref nonce,
104                ..
105            } => {
106                let nonce = nonce.into();
107                let fpr = self.recipient.fingerprint();
108                let key = if let Some(k) = keys.get(fpr) {
109                    let shared_secret = self
110                        .recipient
111                        .encryption_secret()
112                        .unwrap()
113                        .diffie_hellman(issuer.encryption_public());
114                    let k = crypto::decrypt_key(&k, nonce, &shared_secret)?;
115                    vec_to_key(k.to_vec())?
116                } else {
117                    return Err(CryptoError::NotARecipient(*self.recipient.fingerprint()));
118                };
119                (key, nonce)
120            }
121            _ => return Err(CryptoError::NotType(MessageDesc::EncryptedAsm)),
122        };
123        let aad = self.message.header.as_bytes()?;
124        self.payload = crypto::decrypt_symmetric(&key, nonce, &self.message.payload, &aad)?;
125        self.desc = match self.desc {
126            MessageDesc::EncryptedAsm => MessageDesc::Plaintext,
127            MessageDesc::SignedEncryptedAsm => MessageDesc::Signed,
128            _ => unreachable!(),
129        };
130        Ok(self)
131    }
132
133    /// Decrypts a symmetrically encrypted message using a `passphrase`.
134    ///
135    /// # Errors
136    /// * [`CryptoError::NotType`] if the message cannot be symmetrically 
137    /// decrypted,
138    /// * [`CryptoError::UnauthenticCipher`] if the message's AAD cannot be 
139    /// verified,
140    /// * [`CryptoError::InsufficientMemory`],
141    /// * [`CryptoError::SerializationError`].
142    pub fn decrypt_with(mut self, passphrase: impl AsRef<[u8]>) -> Result<Self> {
143        let (salt, nonce) = match self.message.header.msg_type {
144            MessageType::EncryptedSym {
145                ref salt,
146                ref nonce,
147            }
148            | MessageType::SignedEncryptedSym {
149                ref salt,
150                ref nonce,
151                ..
152            } => (salt, nonce),
153            _ => return Err(CryptoError::NotType(MessageDesc::EncryptedSym)),
154        };
155        let key = crypto::derive_key(passphrase.as_ref(), &salt[..])?;
156        let aad = self.message.header.as_bytes()?;
157        self.payload = crypto::decrypt_symmetric(&key, nonce.into(), &self.message.payload, &aad)?;
158        self.desc = match self.desc {
159            MessageDesc::EncryptedSym => MessageDesc::Plaintext,
160            MessageDesc::SignedEncryptedSym => MessageDesc::Signed,
161            _ => unreachable!(),
162        };
163
164        Ok(self)
165    }
166
167    /// Verifies the signed message using `issuer`'s public key.
168    ///
169    /// # Errors
170    /// * [`CryptoError::NotType`] if the message was not signed,
171    /// * [`CryptoError::NotDecrypted`] if the message is signed, but is not 
172    /// decrypted.
173    pub fn verify_signature(mut self, issuer: Arc<Cert>) -> Result<Self> {
174        let signature = match self.message.header.msg_type {
175            MessageType::Signed { ref signature } => {
176                self.payload = self.message.payload.clone();
177                signature
178            }
179            MessageType::SignedEncryptedSym { ref signature, .. }
180            | MessageType::SignedEncryptedAsm { ref signature, .. } => signature,
181            _ => return Err(CryptoError::NotType(MessageDesc::Signed)),
182        };
183        match self.desc {
184            MessageDesc::Signed => {
185                self.verified = Some(crypto::verify_signature(
186                    issuer.signing_public(),
187                    &self.payload,
188                    signature,
189                ));
190                Ok(self)
191            }
192            MessageDesc::Plaintext => Err(CryptoError::NotType(MessageDesc::Signed)),
193            _ => Err(CryptoError::NotDecrypted),
194        }
195    }
196
197    /// Finishes the process of parsing the message. Returns a tuple of the 
198    /// form: `(<original message>, <decrypted content>, Option<signature>)`.
199    /// * `original message` is the message which was moved into the parser, 
200    /// its ownership is now moved back,
201    /// * `decrypted content` the plaintext content of the message. If the 
202    /// message was not encrypted, it will be the plaintext content which was 
203    /// signed.
204    /// * `Option<signature>`:
205    ///     - `Some(signature)` - `signature` is true if the signature is ok, 
206    ///     false otherwise,
207    ///     - `None` - the message was not signed or signature verification 
208    ///     was not attempted.
209    pub fn finalize(self) -> (Message, Vec<u8>, Option<bool>) {
210        (self.message, self.payload, self.verified)
211    }
212}
213
214fn vec_to_key(v: Vec<u8>) -> Result<Zeroizing<[u8; 32]>> {
215    if v.len() != 32 {
216        return Err(CryptoError::Unknown);
217    }
218
219    let k: [u8; 32] = v.try_into().map_err(|_| CryptoError::Unknown)?;
220    Ok(Zeroizing::new(k))
221}