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}