1
1
//! Sign commit data.
2
2
3
- use ssh_key:: { HashAlg , LineEnding , PrivateKey } ;
4
3
use std:: path:: PathBuf ;
5
4
6
5
/// Error type for [`SignBuilder`], used to create [`Sign`]'s
@@ -72,11 +71,11 @@ pub trait Sign {
72
71
73
72
/// only available in `#[cfg(test)]` helping to diagnose issues
74
73
#[ cfg( test) ]
75
- fn program ( & self ) -> & String ;
74
+ fn program ( & self ) -> String ;
76
75
77
76
/// only available in `#[cfg(test)]` helping to diagnose issues
78
77
#[ cfg( test) ]
79
- fn signing_key ( & self ) -> & String ;
78
+ fn signing_key ( & self ) -> String ;
80
79
}
81
80
82
81
/// A builder to facilitate the creation of a signing method ([`Sign`]) by examining the git configuration.
@@ -181,7 +180,7 @@ impl SignBuilder {
181
180
"ssh key setting absent" ,
182
181
) )
183
182
} )
184
- . and_then ( |key_path| SSHSign :: new ( program, key_path) ) ?;
183
+ . and_then ( |key_path| Ok ( SSHSign { program, signing_key : key_path} ) ) ?;
185
184
let signer: Box < dyn Sign > = Box :: new ( ssh_signer) ;
186
185
Ok ( signer)
187
186
}
@@ -196,16 +195,6 @@ pub struct GPGSign {
196
195
signing_key : String ,
197
196
}
198
197
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
-
209
198
impl Sign for GPGSign {
210
199
fn sign (
211
200
& self ,
@@ -264,79 +253,81 @@ impl Sign for GPGSign {
264
253
}
265
254
266
255
#[ cfg( test) ]
267
- fn program ( & self ) -> & String {
268
- & self . program
256
+ fn program ( & self ) -> String {
257
+ self . program . clone ( )
269
258
}
270
259
271
260
#[ cfg( test) ]
272
- fn signing_key ( & self ) -> & String {
273
- & self . signing_key
261
+ fn signing_key ( & self ) -> String {
262
+ self . signing_key . clone ( )
274
263
}
275
264
}
276
265
277
- /// Sign commit data using `SSHDiskKeySign `
266
+ /// Sign commit data using `SSHSign `
278
267
pub struct SSHSign {
279
- #[ cfg( test) ]
280
268
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 ,
316
270
}
317
271
318
272
impl Sign for SSHSign {
319
273
fn sign (
320
274
& self ,
321
275
commit : & [ u8 ] ,
322
276
) -> 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 ) )
330
321
}
331
322
332
323
#[ cfg( test) ]
333
- fn program ( & self ) -> & String {
334
- & self . program
324
+ fn program ( & self ) -> String {
325
+ self . program . clone ( )
335
326
}
336
327
337
328
#[ cfg( test) ]
338
- fn signing_key ( & self ) -> & String {
339
- & self . key_path
329
+ fn signing_key ( & self ) -> String {
330
+ format ! ( "{}" , self . signing_key . display ( ) )
340
331
}
341
332
}
342
333
0 commit comments