44package asymkey
55
66import (
7+ "bytes"
78 "context"
9+ "fmt"
810 "strings"
911
1012 asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -14,6 +16,7 @@ import (
1416 "code.gitea.io/gitea/modules/log"
1517 "code.gitea.io/gitea/modules/setting"
1618
19+ "github.com/42wim/sshsig"
1720 "github.com/ProtonMail/go-crypto/openpgp/packet"
1821)
1922
@@ -57,7 +60,7 @@ func ParseCommitWithSignatureCommitter(ctx context.Context, c *git.Commit, commi
5760
5861 // If this a SSH signature handle it differently
5962 if strings .HasPrefix (c .Signature .Signature , "-----BEGIN SSH SIGNATURE-----" ) {
60- return asymkey_model . ParseCommitWithSSHSignature (ctx , c , committer )
63+ return ParseCommitWithSSHSignature (ctx , c , committer )
6164 }
6265
6366 // Parsing signature
@@ -361,3 +364,65 @@ func VerifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, si
361364 }
362365 return nil
363366}
367+
368+ // ParseCommitWithSSHSignature check if signature is good against keystore.
369+ func ParseCommitWithSSHSignature (ctx context.Context , c * git.Commit , committer * user_model.User ) * asymkey_model.CommitVerification {
370+ // Now try to associate the signature with the committer, if present
371+ if committer .ID != 0 {
372+ keys , err := db .Find [asymkey_model.PublicKey ](ctx , asymkey_model.FindPublicKeyOptions {
373+ OwnerID : committer .ID ,
374+ NotKeytype : asymkey_model .KeyTypePrincipal ,
375+ })
376+ if err != nil { // Skipping failed to get ssh keys of user
377+ log .Error ("ListPublicKeys: %v" , err )
378+ return & asymkey_model.CommitVerification {
379+ CommittingUser : committer ,
380+ Verified : false ,
381+ Reason : "gpg.error.failed_retrieval_gpg_keys" ,
382+ }
383+ }
384+
385+ committerEmailAddresses , err := user_model .GetEmailAddresses (ctx , committer .ID )
386+ if err != nil {
387+ log .Error ("GetEmailAddresses: %v" , err )
388+ }
389+
390+ activated := false
391+ for _ , e := range committerEmailAddresses {
392+ if e .IsActivated && strings .EqualFold (e .Email , c .Committer .Email ) {
393+ activated = true
394+ break
395+ }
396+ }
397+
398+ for _ , k := range keys {
399+ if k .Verified && activated {
400+ commitVerification := verifySSHCommitVerification (c .Signature .Signature , c .Signature .Payload , k , committer , committer , c .Committer .Email )
401+ if commitVerification != nil {
402+ return commitVerification
403+ }
404+ }
405+ }
406+ }
407+
408+ return & asymkey_model.CommitVerification {
409+ CommittingUser : committer ,
410+ Verified : false ,
411+ Reason : asymkey_model .NoKeyFound ,
412+ }
413+ }
414+
415+ func verifySSHCommitVerification (sig , payload string , k * asymkey_model.PublicKey , committer , signer * user_model.User , email string ) * asymkey_model.CommitVerification {
416+ if err := sshsig .Verify (bytes .NewBuffer ([]byte (payload )), []byte (sig ), []byte (k .Content ), "git" ); err != nil {
417+ return nil
418+ }
419+
420+ return & asymkey_model.CommitVerification { // Everything is ok
421+ CommittingUser : committer ,
422+ Verified : true ,
423+ Reason : fmt .Sprintf ("%s / %s" , signer .Name , k .Fingerprint ),
424+ SigningUser : signer ,
425+ SigningSSHKey : k ,
426+ SigningEmail : email ,
427+ }
428+ }
0 commit comments