@@ -41,12 +41,20 @@ static const char *x509_sigs[] = {
41
41
NULL
42
42
};
43
43
44
+ static const char * ssh_verify_args [] = { NULL };
45
+ static const char * ssh_sigs [] = {
46
+ "-----BEGIN SSH SIGNATURE-----" ,
47
+ NULL
48
+ };
49
+
44
50
static int verify_gpg_signed_buffer (struct signature_check * sigc ,
45
51
struct gpg_format * fmt , const char * payload ,
46
52
size_t payload_size , const char * signature ,
47
53
size_t signature_size );
48
54
static int sign_buffer_gpg (struct strbuf * buffer , struct strbuf * signature ,
49
55
const char * signing_key );
56
+ static int sign_buffer_ssh (struct strbuf * buffer , struct strbuf * signature ,
57
+ const char * signing_key );
50
58
51
59
static struct gpg_format gpg_format [] = {
52
60
{
@@ -65,6 +73,14 @@ static struct gpg_format gpg_format[] = {
65
73
.verify_signed_buffer = verify_gpg_signed_buffer ,
66
74
.sign_buffer = sign_buffer_gpg ,
67
75
},
76
+ {
77
+ .name = "ssh" ,
78
+ .program = "ssh-keygen" ,
79
+ .verify_args = ssh_verify_args ,
80
+ .sigs = ssh_sigs ,
81
+ .verify_signed_buffer = NULL , /* TODO */
82
+ .sign_buffer = sign_buffer_ssh
83
+ },
68
84
};
69
85
70
86
static struct gpg_format * use_format = & gpg_format [0 ];
@@ -443,6 +459,9 @@ int git_gpg_config(const char *var, const char *value, void *cb)
443
459
if (!strcmp (var , "gpg.x509.program" ))
444
460
fmtname = "x509" ;
445
461
462
+ if (!strcmp (var , "gpg.ssh.program" ))
463
+ fmtname = "ssh" ;
464
+
446
465
if (fmtname ) {
447
466
fmt = get_format_by_name (fmtname );
448
467
return git_config_string (& fmt -> program , var , value );
@@ -463,12 +482,30 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
463
482
return use_format -> sign_buffer (buffer , signature , signing_key );
464
483
}
465
484
485
+ /*
486
+ * Strip CR from the line endings, in case we are on Windows.
487
+ * NEEDSWORK: make it trim only CRs before LFs and rename
488
+ */
489
+ static void remove_cr_after (struct strbuf * buffer , size_t offset )
490
+ {
491
+ size_t i , j ;
492
+
493
+ for (i = j = offset ; i < buffer -> len ; i ++ ) {
494
+ if (buffer -> buf [i ] != '\r' ) {
495
+ if (i != j )
496
+ buffer -> buf [j ] = buffer -> buf [i ];
497
+ j ++ ;
498
+ }
499
+ }
500
+ strbuf_setlen (buffer , j );
501
+ }
502
+
466
503
static int sign_buffer_gpg (struct strbuf * buffer , struct strbuf * signature ,
467
504
const char * signing_key )
468
505
{
469
506
struct child_process gpg = CHILD_PROCESS_INIT ;
470
507
int ret ;
471
- size_t i , j , bottom ;
508
+ size_t bottom ;
472
509
struct strbuf gpg_status = STRBUF_INIT ;
473
510
474
511
strvec_pushl (& gpg .args ,
@@ -494,13 +531,98 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
494
531
return error (_ ("gpg failed to sign the data" ));
495
532
496
533
/* Strip CR from the line endings, in case we are on Windows. */
497
- for (i = j = bottom ; i < signature -> len ; i ++ )
498
- if (signature -> buf [i ] != '\r' ) {
499
- if (i != j )
500
- signature -> buf [j ] = signature -> buf [i ];
501
- j ++ ;
502
- }
503
- strbuf_setlen (signature , j );
534
+ remove_cr_after (signature , bottom );
504
535
505
536
return 0 ;
506
537
}
538
+
539
+ static int sign_buffer_ssh (struct strbuf * buffer , struct strbuf * signature ,
540
+ const char * signing_key )
541
+ {
542
+ struct child_process signer = CHILD_PROCESS_INIT ;
543
+ int ret = -1 ;
544
+ size_t bottom , keylen ;
545
+ struct strbuf signer_stderr = STRBUF_INIT ;
546
+ struct tempfile * key_file = NULL , * buffer_file = NULL ;
547
+ char * ssh_signing_key_file = NULL ;
548
+ struct strbuf ssh_signature_filename = STRBUF_INIT ;
549
+
550
+ if (!signing_key || signing_key [0 ] == '\0' )
551
+ return error (
552
+ _ ("user.signingkey needs to be set for ssh signing" ));
553
+
554
+ if (starts_with (signing_key , "ssh-" )) {
555
+ /* A literal ssh key */
556
+ key_file = mks_tempfile_t (".git_signing_key_tmpXXXXXX" );
557
+ if (!key_file )
558
+ return error_errno (
559
+ _ ("could not create temporary file" ));
560
+ keylen = strlen (signing_key );
561
+ if (write_in_full (key_file -> fd , signing_key , keylen ) < 0 ||
562
+ close_tempfile_gently (key_file ) < 0 ) {
563
+ error_errno (_ ("failed writing ssh signing key to '%s'" ),
564
+ key_file -> filename .buf );
565
+ goto out ;
566
+ }
567
+ ssh_signing_key_file = strbuf_detach (& key_file -> filename , NULL );
568
+ } else {
569
+ /* We assume a file */
570
+ ssh_signing_key_file = expand_user_path (signing_key , 1 );
571
+ }
572
+
573
+ buffer_file = mks_tempfile_t (".git_signing_buffer_tmpXXXXXX" );
574
+ if (!buffer_file ) {
575
+ error_errno (_ ("could not create temporary file" ));
576
+ goto out ;
577
+ }
578
+
579
+ if (write_in_full (buffer_file -> fd , buffer -> buf , buffer -> len ) < 0 ||
580
+ close_tempfile_gently (buffer_file ) < 0 ) {
581
+ error_errno (_ ("failed writing ssh signing key buffer to '%s'" ),
582
+ buffer_file -> filename .buf );
583
+ goto out ;
584
+ }
585
+
586
+ strvec_pushl (& signer .args , use_format -> program ,
587
+ "-Y" , "sign" ,
588
+ "-n" , "git" ,
589
+ "-f" , ssh_signing_key_file ,
590
+ buffer_file -> filename .buf ,
591
+ NULL );
592
+
593
+ sigchain_push (SIGPIPE , SIG_IGN );
594
+ ret = pipe_command (& signer , NULL , 0 , NULL , 0 , & signer_stderr , 0 );
595
+ sigchain_pop (SIGPIPE );
596
+
597
+ if (ret ) {
598
+ if (strstr (signer_stderr .buf , "usage:" ))
599
+ error (_ ("ssh-keygen -Y sign is needed for ssh signing (available in openssh version 8.2p1+)" ));
600
+
601
+ error ("%s" , signer_stderr .buf );
602
+ goto out ;
603
+ }
604
+
605
+ bottom = signature -> len ;
606
+
607
+ strbuf_addbuf (& ssh_signature_filename , & buffer_file -> filename );
608
+ strbuf_addstr (& ssh_signature_filename , ".sig" );
609
+ if (strbuf_read_file (signature , ssh_signature_filename .buf , 0 ) < 0 ) {
610
+ error_errno (
611
+ _ ("failed reading ssh signing data buffer from '%s'" ),
612
+ ssh_signature_filename .buf );
613
+ }
614
+ unlink_or_warn (ssh_signature_filename .buf );
615
+
616
+ /* Strip CR from the line endings, in case we are on Windows. */
617
+ remove_cr_after (signature , bottom );
618
+
619
+ out :
620
+ if (key_file )
621
+ delete_tempfile (& key_file );
622
+ if (buffer_file )
623
+ delete_tempfile (& buffer_file );
624
+ strbuf_release (& signer_stderr );
625
+ strbuf_release (& ssh_signature_filename );
626
+ FREE_AND_NULL (ssh_signing_key_file );
627
+ return ret ;
628
+ }
0 commit comments