PageRenderTime 103ms CodeModel.GetById 54ms RepoModel.GetById 1ms app.codeStats 0ms

/src/gpg.rs

https://github.com/akheron/sala
Rust | 124 lines | 111 code | 13 blank | 0 comment | 6 complexity | 98f56c73b239a1067fc6d7fe8b99fa7a MD5 | raw file
  1. use nix::{
  2. self,
  3. fcntl::{self, FcntlArg, FdFlag},
  4. unistd,
  5. };
  6. use std::fs::{self, File};
  7. use std::io;
  8. use std::io::Write;
  9. use std::os::unix::io::FromRawFd;
  10. use std::os::unix::net::UnixStream;
  11. use std::path::Path;
  12. use std::process::{Command, Stdio};
  13. #[derive(Debug)]
  14. pub enum GpgError {
  15. IOError(io::Error),
  16. OperationFailed(String),
  17. }
  18. fn nix_err(err: nix::Error) -> GpgError {
  19. if let nix::Error::Sys(err_no) = err {
  20. GpgError::IOError(io::Error::from(err_no))
  21. } else {
  22. panic!("unexpected nix error type: {:?}", err)
  23. }
  24. }
  25. fn gpg_err(msg: &[u8]) -> GpgError {
  26. GpgError::OperationFailed(String::from_utf8_lossy(msg).to_string())
  27. }
  28. pub fn decrypt(path: &Path, key: &[u8]) -> Result<Vec<u8>, GpgError> {
  29. let (passphrase_read_fd, passphrase_write_fd) = unistd::pipe().map_err(nix_err)?;
  30. fcntl::fcntl(passphrase_write_fd, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).map_err(nix_err)?;
  31. let gpg = Command::new("gpg")
  32. .arg("--batch")
  33. .arg("--no-tty")
  34. .arg("--armor")
  35. .arg("--decrypt")
  36. .arg("--passphrase-fd")
  37. .arg(passphrase_read_fd.to_string())
  38. .arg("--")
  39. .arg(path.as_os_str())
  40. .stdout(Stdio::piped())
  41. .stderr(Stdio::piped())
  42. .spawn()
  43. .map_err(GpgError::IOError)?;
  44. unistd::close(passphrase_read_fd).map_err(nix_err)?;
  45. unsafe {
  46. let mut stream = UnixStream::from_raw_fd(passphrase_write_fd);
  47. stream.write_all(key).map_err(GpgError::IOError)?;
  48. }
  49. let output = gpg.wait_with_output().map_err(GpgError::IOError)?;
  50. if output.status.success() {
  51. Ok(output.stdout)
  52. } else {
  53. Err(gpg_err(&output.stderr))
  54. }
  55. }
  56. pub fn encrypt(data: &str, key: &[u8], target: &Path, cipher: &str) -> Result<(), GpgError> {
  57. let mut target_tmp = target.as_os_str().to_os_string();
  58. target_tmp.push(".tmp");
  59. let target_file = File::create(&target_tmp).map_err(GpgError::IOError)?;
  60. match gpg_encrypt_impl(data, key, target_file, cipher) {
  61. Ok(_) => {
  62. fs::rename(&target_tmp, target).map_err(GpgError::IOError)?;
  63. Ok(())
  64. }
  65. Err(err) => {
  66. fs::remove_file(&target_tmp).map_err(GpgError::IOError)?;
  67. Err(err)
  68. }
  69. }
  70. }
  71. fn gpg_encrypt_impl<T: Into<Stdio>>(
  72. data: &str,
  73. key: &[u8],
  74. outfile: T,
  75. cipher: &str,
  76. ) -> Result<(), GpgError> {
  77. let (passphrase_read_fd, passphrase_write_fd) = unistd::pipe().map_err(nix_err)?;
  78. fcntl::fcntl(passphrase_write_fd, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).map_err(nix_err)?;
  79. let mut gpg = Command::new("gpg")
  80. .arg("--batch")
  81. .arg("--no-tty")
  82. .arg("--armor")
  83. .arg("--symmetric")
  84. .arg("--cipher-algo")
  85. .arg(cipher)
  86. .arg("--passphrase-fd")
  87. .arg(passphrase_read_fd.to_string())
  88. .stdin(Stdio::piped())
  89. .stdout(outfile)
  90. .stderr(Stdio::piped())
  91. .spawn()
  92. .map_err(GpgError::IOError)?;
  93. unistd::close(passphrase_read_fd).map_err(nix_err)?;
  94. unsafe {
  95. let mut stream = UnixStream::from_raw_fd(passphrase_write_fd);
  96. stream.write_all(key).map_err(GpgError::IOError)?;
  97. }
  98. {
  99. gpg.stdin
  100. .as_mut()
  101. .unwrap()
  102. .write_all(data.as_bytes())
  103. .map_err(GpgError::IOError)?;
  104. }
  105. let output = gpg.wait_with_output().map_err(GpgError::IOError)?;
  106. if output.status.success() {
  107. Ok(())
  108. } else {
  109. Err(gpg_err(&output.stderr))
  110. }
  111. }