/src/components/mail/pgp.rs

https://github.com/meli/meli · Rust · 179 lines · 150 code · 8 blank · 21 comment · 2 complexity · 81c0a402c68300a5c5a498ce3092a3d9 MD5 · raw file

  1. /*
  2. * meli
  3. *
  4. * Copyright 2019 Manos Pitsidianakis
  5. *
  6. * This file is part of meli.
  7. *
  8. * meli is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * meli is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with meli. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. use super::*;
  22. use melib::email::pgp as melib_pgp;
  23. use std::io::Write;
  24. use std::process::{Command, Stdio};
  25. pub fn verify_signature(a: &Attachment, context: &mut Context) -> Result<Vec<u8>> {
  26. let (bytes, sig) =
  27. melib_pgp::verify_signature(a).chain_err_summary(|| "Could not verify signature.")?;
  28. let bytes_file = create_temp_file(&bytes, None, None, true);
  29. let signature_file = create_temp_file(sig, None, None, true);
  30. let binary = context
  31. .settings
  32. .pgp
  33. .gpg_binary
  34. .as_ref()
  35. .map(String::as_str)
  36. .unwrap_or("gpg2");
  37. Ok(Command::new(binary)
  38. .args(&[
  39. "--output",
  40. "-",
  41. "--verify",
  42. signature_file.path.to_str().unwrap(),
  43. bytes_file.path.to_str().unwrap(),
  44. ])
  45. .stdin(Stdio::piped())
  46. .stderr(Stdio::piped())
  47. .spawn()
  48. .and_then(|gpg| gpg.wait_with_output())
  49. .map(|gpg| gpg.stderr)
  50. .chain_err_summary(|| {
  51. format!(
  52. "Failed to launch {} to verify PGP signature",
  53. context
  54. .settings
  55. .pgp
  56. .gpg_binary
  57. .as_ref()
  58. .map(String::as_str)
  59. .unwrap_or("gpg2"),
  60. )
  61. })?)
  62. }
  63. /// Returns multipart/signed
  64. pub fn sign(
  65. a: AttachmentBuilder,
  66. gpg_binary: Option<&str>,
  67. pgp_key: Option<&str>,
  68. ) -> Result<AttachmentBuilder> {
  69. let binary = gpg_binary.unwrap_or("gpg2");
  70. let mut command = Command::new(binary);
  71. command.args(&[
  72. "--digest-algo",
  73. "sha512",
  74. "--output",
  75. "-",
  76. "--detach-sig",
  77. "--armor",
  78. ]);
  79. if let Some(key) = pgp_key {
  80. command.args(&["--local-user", key]);
  81. }
  82. let a: Attachment = a.into();
  83. let sig_attachment = command
  84. .stdin(Stdio::piped())
  85. .stdout(Stdio::piped())
  86. .stderr(Stdio::null())
  87. .spawn()
  88. .and_then(|mut gpg| {
  89. gpg.stdin
  90. .as_mut()
  91. .expect("Could not get gpg stdin")
  92. .write_all(&melib_pgp::convert_attachment_to_rfc_spec(
  93. a.into_raw().as_bytes(),
  94. ))?;
  95. let gpg = gpg.wait_with_output()?;
  96. Ok(Attachment::new(
  97. ContentType::PGPSignature,
  98. Default::default(),
  99. gpg.stdout,
  100. ))
  101. })
  102. .chain_err_summary(|| format!("Failed to launch {} to verify PGP signature", binary))?;
  103. let a: AttachmentBuilder = a.into();
  104. let parts = vec![a, sig_attachment.into()];
  105. let boundary = ContentType::make_boundary(&parts);
  106. Ok(Attachment::new(
  107. ContentType::Multipart {
  108. boundary: boundary.into_bytes(),
  109. kind: MultipartType::Signed,
  110. parts: parts.into_iter().map(|a| a.into()).collect::<Vec<_>>(),
  111. },
  112. Default::default(),
  113. Vec::new(),
  114. )
  115. .into())
  116. }
  117. pub async fn decrypt(
  118. raw: Vec<u8>,
  119. gpg_binary: Option<String>,
  120. decrypt_key: Option<String>,
  121. ) -> Result<(melib_pgp::DecryptionMetadata, Vec<u8>)> {
  122. let bin = gpg_binary.as_ref().map(|s| s.as_str()).unwrap_or("gpg2");
  123. let mut command = Command::new(bin);
  124. command.args(&["--digest-algo", "sha512", "--output", "-"]);
  125. if let Some(ref key) = decrypt_key {
  126. command.args(&["--local-user", key]);
  127. }
  128. let stdout = command
  129. .args(&["--decrypt"])
  130. .stdin(Stdio::piped())
  131. .stdout(Stdio::piped())
  132. .stderr(Stdio::piped())
  133. .spawn()
  134. .and_then(|mut gpg| {
  135. gpg.stdin
  136. .as_mut()
  137. .expect("Could not get gpg stdin")
  138. .write_all(&raw)?;
  139. let gpg = gpg.wait_with_output()?;
  140. Ok(gpg.stdout)
  141. })
  142. .chain_err_summary(|| format!("Failed to launch {} to verify PGP signature", bin))?;
  143. Ok((melib_pgp::DecryptionMetadata::default(), stdout))
  144. }
  145. pub async fn verify(a: Attachment, gpg_binary: Option<String>) -> Result<Vec<u8>> {
  146. let (bytes, sig) =
  147. melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
  148. let bytes_file = create_temp_file(&bytes, None, None, true);
  149. let signature_file = create_temp_file(sig, None, None, true);
  150. Ok(
  151. Command::new(gpg_binary.as_ref().map(String::as_str).unwrap_or("gpg2"))
  152. .args(&[
  153. "--output",
  154. "-",
  155. "--verify",
  156. signature_file.path.to_str().unwrap(),
  157. bytes_file.path.to_str().unwrap(),
  158. ])
  159. .stdin(Stdio::piped())
  160. .stderr(Stdio::piped())
  161. .spawn()
  162. .and_then(|gpg| gpg.wait_with_output())
  163. .map(|gpg| gpg.stderr)
  164. .chain_err_summary(|| {
  165. format!(
  166. "Failed to launch {} to verify PGP signature",
  167. gpg_binary.as_ref().map(String::as_str).unwrap_or("gpg2"),
  168. )
  169. })?,
  170. )
  171. }