code.gitea.io/gitea@v1.22.3/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 := db.Find[PublicKey](ctx, FindPublicKeyOptions{
    25  			OwnerID:    committer.ID,
    26  			NotKeytype: KeyTypePrincipal,
    27  		})
    28  		if err != nil { // Skipping failed to get ssh keys of user
    29  			log.Error("ListPublicKeys: %v", err)
    30  			return &CommitVerification{
    31  				CommittingUser: committer,
    32  				Verified:       false,
    33  				Reason:         "gpg.error.failed_retrieval_gpg_keys",
    34  			}
    35  		}
    36  
    37  		committerEmailAddresses, err := user_model.GetEmailAddresses(ctx, committer.ID)
    38  		if err != nil {
    39  			log.Error("GetEmailAddresses: %v", err)
    40  		}
    41  
    42  		activated := false
    43  		for _, e := range committerEmailAddresses {
    44  			if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
    45  				activated = true
    46  				break
    47  			}
    48  		}
    49  
    50  		for _, k := range keys {
    51  			if k.Verified && activated {
    52  				commitVerification := verifySSHCommitVerification(c.Signature.Signature, c.Signature.Payload, k, committer, committer, c.Committer.Email)
    53  				if commitVerification != nil {
    54  					return commitVerification
    55  				}
    56  			}
    57  		}
    58  	}
    59  
    60  	return &CommitVerification{
    61  		CommittingUser: committer,
    62  		Verified:       false,
    63  		Reason:         NoKeyFound,
    64  	}
    65  }
    66  
    67  func verifySSHCommitVerification(sig, payload string, k *PublicKey, committer, signer *user_model.User, email string) *CommitVerification {
    68  	if err := sshsig.Verify(bytes.NewBuffer([]byte(payload)), []byte(sig), []byte(k.Content), "git"); err != nil {
    69  		return nil
    70  	}
    71  
    72  	return &CommitVerification{ // Everything is ok
    73  		CommittingUser: committer,
    74  		Verified:       true,
    75  		Reason:         fmt.Sprintf("%s / %s", signer.Name, k.Fingerprint),
    76  		SigningUser:    signer,
    77  		SigningSSHKey:  k,
    78  		SigningEmail:   email,
    79  	}
    80  }