11//! Sign commit data.
22
3- use ssh_key:: { HashAlg , LineEnding , PrivateKey } ;
43use 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-
209198impl 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 `
278267pub 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
318272impl 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