mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-07 04:51:45 +00:00
Revert "sign: Implement GPG signing backend"
This reverts commit 0efaef2da9
.
This commit is contained in:
parent
2e62162a76
commit
5995f0cc42
3 changed files with 4 additions and 247 deletions
|
@ -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 <long keyid> <primary uid..>
|
||||
// [GNUPG:] EXPKEYSIG <long keyid> <primary uid..>
|
||||
// [GNUPG:] NO_PUBKEY <long keyid>
|
||||
// [GNUPG:] BADSIG <long keyid> <primary uid..>
|
||||
// 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<Verification, SignError> {
|
||||
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<OsString>,
|
||||
}
|
||||
|
||||
#[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<GpgError> 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<Vec<u8>, 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<Vec<u8>, 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<Verification, SignError> {
|
||||
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()))
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<String>, display: Option<String>) -> 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<Self, SignInitError> {
|
||||
let mut backends = vec![
|
||||
Box::new(GpgBackend::from_config(settings.config())) as Box<dyn SigningBackend>,
|
||||
// Box::new(SshBackend::from_settings(settings)?) as Box<dyn SigningBackend>,
|
||||
// Box::new(X509Backend::from_settings(settings)?) as Box<dyn SigningBackend>,
|
||||
let mut backends: Vec<Box<dyn SigningBackend>> = vec![
|
||||
// Box::new(GpgBackend::from_settings(settings)?),
|
||||
// Box::new(SshBackend::from_settings(settings)?),
|
||||
// Box::new(X509Backend::from_settings(settings)?),
|
||||
];
|
||||
|
||||
let main_backend = settings
|
||||
|
|
Loading…
Reference in a new issue