Skip to content

Commit c051b93

Browse files
committed
wip(external-ssh-signer): rewrite ssh signing
1 parent 06bbc99 commit c051b93

File tree

1 file changed

+57
-66
lines changed

1 file changed

+57
-66
lines changed

asyncgit/src/sync/sign.rs

Lines changed: 57 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Sign commit data.
22
3-
use ssh_key::{HashAlg, LineEnding, PrivateKey};
43
use std::path::PathBuf;
54

65
/// Error type for [`SignBuilder`], used to create [`Sign`]'s
@@ -72,11 +71,11 @@ pub trait Sign {
7271

7372
/// only available in `#[cfg(test)]` helping to diagnose issues
7473
#[cfg(test)]
75-
fn program(&self) -> &String;
74+
fn program(&self) -> String;
7675

7776
/// only available in `#[cfg(test)]` helping to diagnose issues
7877
#[cfg(test)]
79-
fn signing_key(&self) -> &String;
78+
fn signing_key(&self) -> String;
8079
}
8180

8281
/// A builder to facilitate the creation of a signing method ([`Sign`]) by examining the git configuration.
@@ -181,7 +180,7 @@ impl SignBuilder {
181180
"ssh key setting absent",
182181
))
183182
})
184-
.and_then(|key_path| SSHSign::new(program, key_path))?;
183+
.and_then(|key_path| Ok(SSHSign { program, signing_key: key_path}))?;
185184
let signer: Box<dyn Sign> = Box::new(ssh_signer);
186185
Ok(signer)
187186
}
@@ -196,16 +195,6 @@ pub struct GPGSign {
196195
signing_key: String,
197196
}
198197

199-
impl GPGSign {
200-
/// Create new [`GPGSign`] using given program and signing key.
201-
pub fn new(program: &str, signing_key: &str) -> Self {
202-
Self {
203-
program: program.to_string(),
204-
signing_key: signing_key.to_string(),
205-
}
206-
}
207-
}
208-
209198
impl Sign for GPGSign {
210199
fn sign(
211200
&self,
@@ -264,79 +253,81 @@ impl Sign for GPGSign {
264253
}
265254

266255
#[cfg(test)]
267-
fn program(&self) -> &String {
268-
&self.program
256+
fn program(&self) -> String {
257+
self.program.clone()
269258
}
270259

271260
#[cfg(test)]
272-
fn signing_key(&self) -> &String {
273-
&self.signing_key
261+
fn signing_key(&self) -> String {
262+
self.signing_key.clone()
274263
}
275264
}
276265

277-
/// Sign commit data using `SSHDiskKeySign`
266+
/// Sign commit data using `SSHSign`
278267
pub struct SSHSign {
279-
#[cfg(test)]
280268
program: String,
281-
#[cfg(test)]
282-
key_path: String,
283-
secret_key: PrivateKey,
284-
}
285-
286-
impl SSHSign {
287-
/// Create new [`SSHDiskKeySign`] for sign.
288-
pub fn new(program: String, mut key: PathBuf) -> Result<Self, SignBuilderError> {
289-
key.set_extension("");
290-
if key.is_file() {
291-
#[cfg(test)]
292-
let key_path = format!("{}", &key.display());
293-
std::fs::read(key)
294-
.ok()
295-
.and_then(|bytes| {
296-
PrivateKey::from_openssh(bytes).ok()
297-
})
298-
.map(|secret_key| Self {
299-
#[cfg(test)]
300-
program,
301-
#[cfg(test)]
302-
key_path,
303-
secret_key,
304-
})
305-
.ok_or_else(|| {
306-
SignBuilderError::SSHSigningKey(String::from(
307-
"Fail to read the private key for sign.",
308-
))
309-
})
310-
} else {
311-
Err(SignBuilderError::SSHSigningKey(
312-
String::from("Currently, we only support a pair of ssh key in disk."),
313-
))
314-
}
315-
}
269+
signing_key: PathBuf,
316270
}
317271

318272
impl Sign for SSHSign {
319273
fn sign(
320274
&self,
321275
commit: &[u8],
322276
) -> Result<(String, Option<String>), SignError> {
323-
let sig = self
324-
.secret_key
325-
.sign("git", HashAlg::Sha256, commit)
326-
.map_err(|err| SignError::Spawn(err.to_string()))?
327-
.to_pem(LineEnding::LF)
328-
.map_err(|err| SignError::Spawn(err.to_string()))?;
329-
Ok((sig, None))
277+
use std::io::Write;
278+
use std::process::{Command, Stdio};
279+
280+
let mut cmd = Command::new(&self.program);
281+
cmd.stdin(Stdio::piped())
282+
.stdout(Stdio::piped())
283+
.stderr(Stdio::piped())
284+
.arg("-Y")
285+
.arg("sign")
286+
.arg("-n")
287+
.arg("git")
288+
.arg("-f")
289+
.arg(&self.signing_key);
290+
291+
log::trace!("signing command: {cmd:?}");
292+
293+
let mut child = cmd
294+
.spawn()
295+
.map_err(|e| SignError::Spawn(e.to_string()))?;
296+
297+
let mut stdin = child.stdin.take().ok_or(SignError::Stdin)?;
298+
299+
stdin
300+
.write_all(commit)
301+
.map_err(|e| SignError::WriteBuffer(e.to_string()))?;
302+
drop(stdin);
303+
304+
let output = child
305+
.wait_with_output()
306+
.map_err(|e| SignError::Output(e.to_string()))?;
307+
308+
if !output.status.success() {
309+
return Err(SignError::Shellout(format!(
310+
"failed to sign data, program '{}' exited non-zero: {}",
311+
&self.program,
312+
std::str::from_utf8(&output.stderr)
313+
.unwrap_or("[error could not be read from stderr]")
314+
)));
315+
}
316+
317+
let signed_commit = std::str::from_utf8(&output.stdout)
318+
.map_err(|e| SignError::Shellout(e.to_string()))?;
319+
320+
Ok((signed_commit.to_string(), None))
330321
}
331322

332323
#[cfg(test)]
333-
fn program(&self) -> &String {
334-
&self.program
324+
fn program(&self) -> String {
325+
self.program.clone()
335326
}
336327

337328
#[cfg(test)]
338-
fn signing_key(&self) -> &String {
339-
&self.key_path
329+
fn signing_key(&self) -> String {
330+
format!("{}", self.signing_key.display())
340331
}
341332
}
342333

0 commit comments

Comments
 (0)