code.gitea.io/gitea@v1.21.7/models/asymkey/ssh_key_commit_verification.go (about)

     1  // Copyright 2021 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package asymkey
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"fmt"
    10  	"strings"
    11  
    12  	"code.gitea.io/gitea/models/db"
    13  	user_model "code.gitea.io/gitea/models/user"
    14  	"code.gitea.io/gitea/modules/git"
    15  	"code.gitea.io/gitea/modules/log"
    16  
    17  	"github.com/42wim/sshsig"
    18  )
    19  
    20  // ParseCommitWithSSHSignature check if signature is good against keystore.
    21  func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification {
    22  	// Now try to associate the signature with the committer, if present
    23  	if committer.ID != 0 {
    24  		keys, err := ListPublicKeys(committer.ID, db.ListOptions{})
    25  		if err != nil { // Skipping failed to get ssh keys of user
    26  			log.Error("ListPublicKeys: %v", err)
    27  			return &CommitVerification{
    28  				CommittingUser: committer,
    29  				Verified:       false,
    30  				Reason:         "gpg.error.failed_retrieval_gpg_keys",
    31  			}
    32  		}
    33  
    34  		committerEmailAddresses, err := user_model.GetEmailAddresses(ctx, committer.ID)
    35  		if err != nil {
    36  			log.Error("GetEmailAddresses: %v", err)
    37  		}
    38  
    39  		activated := false
    40  		for _, e := range committerEmailAddresses {
    41  			if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
    42  				activated = true
    43  				break
    44  			}
    45  		}
    46  
    47  		for _, k := range keys {
    48  			if k.Verified && activated {
    49  				commitVerification := verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, k, committer, committer, c.Committer.Email)
    50  				if commitVerification != nil {
    51  					return commitVerification
    52  				}
    53  			}
    54  		}
    55  	}
    56  
    57  	return &CommitVerification{
    58  		CommittingUser: committer,
    59  		Verified:       false,
    60  		Reason:         NoKeyFound,
    61  	}
    62  }
    63  
    64  func verifySSHCommitVerification(sig, payload string, k *PublicKey, committer, signer *user_model.User, email string) *CommitVerification {
    65  	if err := sshsig.Verify(bytes.NewBuffer([]byte(payload)), []byte(sig), []byte(k.Content), "git"); err != nil {
    66  		return nil
    67  	}
    68  
    69  	return &CommitVerification{ // Everything is ok
    70  		CommittingUser: committer,
    71  		Verified:       true,
    72  		Reason:         fmt.Sprintf("%s / %s", signer.Name, k.Fingerprint),
    73  		SigningUser:    signer,
    74  		SigningSSHKey:  k,
    75  		SigningEmail:   email,
    76  	}
    77  }