diff --git a/lib/src/gpg_signing.rs b/lib/src/gpg_signing.rs deleted file mode 100644 index b27dc16b1..000000000 --- a/lib/src/gpg_signing.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2023 The Jujutsu Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![allow(missing_docs)] - -use std::ffi::{OsStr, OsString}; -use std::fmt::Debug; -use std::io::Write; -use std::process::{Command, ExitStatus, Stdio}; -use std::str; - -use thiserror::Error; - -use crate::signing::{SigStatus, SignError, SigningBackend, Verification}; - -// Search for one of the: -// [GNUPG:] GOODSIG -// [GNUPG:] EXPKEYSIG -// [GNUPG:] NO_PUBKEY -// [GNUPG:] BADSIG -// in the output from --status-fd=1 -// Assume signature is invalid if none of the above was found -fn parse_gpg_verify_output( - output: &[u8], - allow_expired_keys: bool, -) -> Result { - output - .split(|&b| b == b'\n') - .filter_map(|line| line.strip_prefix(b"[GNUPG:] ")) - .find_map(|line| { - let mut parts = line.splitn(3, |&b| b == b' ').fuse(); - let status = match parts.next()? { - b"GOODSIG" => SigStatus::Good, - b"EXPKEYSIG" => { - if allow_expired_keys { - SigStatus::Good - } else { - SigStatus::Bad - } - } - b"NO_PUBKEY" => SigStatus::Unknown, - b"BADSIG" => SigStatus::Bad, - _ => return None, - }; - let key = parts - .next() - .and_then(|bs| str::from_utf8(bs).ok()) - .map(|value| value.trim().to_owned()); - let display = parts - .next() - .and_then(|bs| str::from_utf8(bs).ok()) - .map(|value| value.trim().to_owned()); - Some(Verification::new(status, key, display)) - }) - .ok_or(SignError::InvalidSignatureFormat) -} - -#[derive(Debug)] -pub struct GpgBackend { - program: OsString, - allow_expired_keys: bool, - extra_args: Vec, -} - -#[derive(Debug, Error)] -pub enum GpgError { - #[error("GPG failed with exit status {exit_status}:\n{stderr}")] - Command { - exit_status: ExitStatus, - stderr: String, - }, - #[error("Failed to run GPG")] - Io(#[from] std::io::Error), -} - -impl From for SignError { - fn from(e: GpgError) -> Self { - SignError::Backend(Box::new(e)) - } -} - -impl GpgBackend { - pub fn new(program: OsString, allow_expired_keys: bool) -> Self { - Self { - program, - allow_expired_keys, - extra_args: vec![], - } - } - - /// Primarily intended for testing - pub fn with_extra_args(mut self, args: &[OsString]) -> Self { - self.extra_args.extend_from_slice(args); - self - } - - pub fn from_config(config: &config::Config) -> Self { - Self::new( - config - .get_string("signing.backends.gpg.program") - .unwrap_or_else(|_| "gpg2".into()) - .into(), - config - .get_bool("signing.backends.gpg.allow-expired-keys") - .unwrap_or(false), - ) - } - - fn run(&self, input: &[u8], args: &[&OsStr], check: bool) -> Result, GpgError> { - let process = Command::new(&self.program) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(if check { Stdio::piped() } else { Stdio::null() }) - .args(&self.extra_args) - .args(args) - .spawn()?; - process.stdin.as_ref().unwrap().write_all(input)?; - let output = process.wait_with_output()?; - if check && !output.status.success() { - Err(GpgError::Command { - exit_status: output.status, - stderr: String::from_utf8_lossy(&output.stderr).trim_end().into(), - }) - } else { - Ok(output.stdout) - } - } -} - -impl SigningBackend for GpgBackend { - fn name(&self) -> &str { - "gpg" - } - - fn can_read(&self, signature: &[u8]) -> bool { - signature.starts_with(b"-----BEGIN PGP SIGNATURE-----") - } - - fn sign(&self, data: &[u8], key: Option<&str>) -> Result, SignError> { - Ok(match key { - Some(key) => self.run(data, &["-abu".as_ref(), key.as_ref()], true)?, - None => self.run(data, &["-ab".as_ref()], true)?, - }) - } - - fn verify(&self, data: &[u8], signature: &[u8]) -> Result { - let mut signature_file = tempfile::Builder::new() - .prefix(".jj-gpg-sig-tmp-") - .tempfile() - .map_err(GpgError::Io)?; - signature_file.write_all(signature).map_err(GpgError::Io)?; - signature_file.flush().map_err(GpgError::Io)?; - - let sig_path = signature_file.into_temp_path(); - - let output = self.run( - data, - &[ - "--status-fd=1".as_ref(), - "--verify".as_ref(), - // the only reason we have those .as_refs transmuting to &OsStr everywhere - sig_path.as_os_str(), - "-".as_ref(), - ], - false, - )?; - - parse_gpg_verify_output(&output, self.allow_expired_keys) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn gpg_verify_invalid_signature_format() { - use assert_matches::assert_matches; - assert_matches!( - parse_gpg_verify_output(b"", true), - Err(SignError::InvalidSignatureFormat) - ); - } - - #[test] - fn gpg_verify_bad_signature() { - assert_eq!( - parse_gpg_verify_output(b"[GNUPG:] BADSIG 123 456", true).unwrap(), - Verification::new(SigStatus::Bad, Some("123".into()), Some("456".into())) - ); - } - - #[test] - fn gpg_verify_unknown_signature() { - assert_eq!( - parse_gpg_verify_output(b"[GNUPG:] NO_PUBKEY 123", true).unwrap(), - Verification::new(SigStatus::Unknown, Some("123".into()), None) - ); - } - - #[test] - fn gpg_verify_good_signature() { - assert_eq!( - parse_gpg_verify_output(b"[GNUPG:] GOODSIG 123 456", true).unwrap(), - Verification::new(SigStatus::Good, Some("123".into()), Some("456".into())) - ); - } - - #[test] - fn gpg_verify_expired_signature() { - assert_eq!( - parse_gpg_verify_output(b"[GNUPG:] EXPKEYSIG 123 456", true).unwrap(), - Verification::new(SigStatus::Good, Some("123".into()), Some("456".into())) - ); - - assert_eq!( - parse_gpg_verify_output(b"[GNUPG:] EXPKEYSIG 123 456", false).unwrap(), - Verification::new(SigStatus::Bad, Some("123".into()), Some("456".into())) - ); - } -} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 1f1c74efc..a469da3e9 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -43,7 +43,6 @@ pub mod fsmonitor; pub mod git; pub mod git_backend; pub mod gitignore; -pub mod gpg_signing; pub mod hex_util; pub mod id_prefix; pub mod index; diff --git a/lib/src/signing.rs b/lib/src/signing.rs index 607121df4..17659e04f 100644 --- a/lib/src/signing.rs +++ b/lib/src/signing.rs @@ -22,7 +22,6 @@ use std::sync::RwLock; use thiserror::Error; use crate::backend::CommitId; -use crate::gpg_signing::GpgBackend; use crate::settings::UserSettings; /// A status of the signature, part of the [Verification] type. @@ -61,15 +60,6 @@ impl Verification { display: None, } } - - /// Create a new verification - pub fn new(status: SigStatus, key: Option, display: Option) -> Self { - Self { - status, - key, - display, - } - } } /// The backend for signing and verifying cryptographic signatures. @@ -160,10 +150,10 @@ impl Signer { /// Creates a signer based on user settings. Uses all known backends, and /// chooses one of them to be used for signing depending on the config. pub fn from_settings(settings: &UserSettings) -> Result { - let mut backends = vec![ - Box::new(GpgBackend::from_config(settings.config())) as Box, - // Box::new(SshBackend::from_settings(settings)?) as Box, - // Box::new(X509Backend::from_settings(settings)?) as Box, + let mut backends: Vec> = vec![ + // Box::new(GpgBackend::from_settings(settings)?), + // Box::new(SshBackend::from_settings(settings)?), + // Box::new(X509Backend::from_settings(settings)?), ]; let main_backend = settings