gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package asymkey
  4. import (
  5. "errors"
  6. "fmt"
  7. "hash"
  8. repo_model "code.gitea.io/gitea/models/repo"
  9. user_model "code.gitea.io/gitea/models/user"
  10. "code.gitea.io/gitea/modules/log"
  11. "github.com/ProtonMail/go-crypto/openpgp/packet"
  12. )
  13. // This file provides functions relating commit verification
  14. // CommitVerification represents a commit validation of signature
  15. type CommitVerification struct {
  16. Verified bool
  17. Warning bool
  18. Reason string
  19. SigningUser *user_model.User // if Verified, then SigningUser is non-nil
  20. CommittingUser *user_model.User // if Verified, then CommittingUser is non-nil
  21. SigningEmail string
  22. SigningKey *GPGKey // FIXME: need to refactor it to a new name like "SigningGPGKey", it is also used in some templates
  23. SigningSSHKey *PublicKey
  24. TrustStatus string
  25. }
  26. // SignCommit represents a commit with validation of signature.
  27. type SignCommit struct {
  28. Verification *CommitVerification
  29. *user_model.UserCommit
  30. }
  31. const (
  32. // BadSignature is used as the reason when the signature has a KeyID that is in the db
  33. // but no key that has that ID verifies the signature. This is a suspicious failure.
  34. BadSignature = "gpg.error.probable_bad_signature"
  35. // BadDefaultSignature is used as the reason when the signature has a KeyID that matches the
  36. // default Key but is not verified by the default key. This is a suspicious failure.
  37. BadDefaultSignature = "gpg.error.probable_bad_default_signature"
  38. // NoKeyFound is used as the reason when no key can be found to verify the signature.
  39. NoKeyFound = "gpg.error.no_gpg_keys_found"
  40. )
  41. func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
  42. // Check if key can sign
  43. if !k.CanSign {
  44. return errors.New("key can not sign")
  45. }
  46. // Decode key
  47. pkey, err := base64DecPubKey(k.Content)
  48. if err != nil {
  49. return err
  50. }
  51. return pkey.VerifySignature(h, s)
  52. }
  53. func hashAndVerify(sig *packet.Signature, payload string, k *GPGKey) (*GPGKey, error) {
  54. // Generating hash of commit
  55. hash, err := populateHash(sig.Hash, []byte(payload))
  56. if err != nil { // Skipping as failed to generate hash
  57. log.Error("PopulateHash: %v", err)
  58. return nil, err
  59. }
  60. // We will ignore errors in verification as they don't need to be propagated up
  61. err = verifySign(sig, hash, k)
  62. if err != nil {
  63. return nil, nil
  64. }
  65. return k, nil
  66. }
  67. func hashAndVerifyWithSubKeys(sig *packet.Signature, payload string, k *GPGKey) (*GPGKey, error) {
  68. verified, err := hashAndVerify(sig, payload, k)
  69. if err != nil || verified != nil {
  70. return verified, err
  71. }
  72. for _, sk := range k.SubsKey {
  73. verified, err := hashAndVerify(sig, payload, sk)
  74. if err != nil || verified != nil {
  75. return verified, err
  76. }
  77. }
  78. return nil, nil
  79. }
  80. func HashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification {
  81. key, err := hashAndVerifyWithSubKeys(sig, payload, k)
  82. if err != nil { // Skipping failed to generate hash
  83. return &CommitVerification{
  84. CommittingUser: committer,
  85. Verified: false,
  86. Reason: "gpg.error.generate_hash",
  87. }
  88. }
  89. if key != nil {
  90. return &CommitVerification{ // Everything is ok
  91. CommittingUser: committer,
  92. Verified: true,
  93. Reason: fmt.Sprintf("%s / %s", signer.Name, key.KeyID),
  94. SigningUser: signer,
  95. SigningKey: key,
  96. SigningEmail: email,
  97. }
  98. }
  99. return nil
  100. }
  101. // CalculateTrustStatus will calculate the TrustStatus for a commit verification within a repository
  102. // There are several trust models in Gitea
  103. func CalculateTrustStatus(verification *CommitVerification, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error), keyMap *map[string]bool) error {
  104. if !verification.Verified {
  105. return nil
  106. }
  107. // In the Committer trust model a signature is trusted if it matches the committer
  108. // - it doesn't matter if they're a collaborator, the owner, Gitea or Github
  109. // NB: This model is commit verification only
  110. if repoTrustModel == repo_model.CommitterTrustModel {
  111. // default to "unmatched"
  112. verification.TrustStatus = "unmatched"
  113. // We can only verify against users in our database but the default key will match
  114. // against by email if it is not in the db.
  115. if (verification.SigningUser.ID != 0 &&
  116. verification.CommittingUser.ID == verification.SigningUser.ID) ||
  117. (verification.SigningUser.ID == 0 && verification.CommittingUser.ID == 0 &&
  118. verification.SigningUser.Email == verification.CommittingUser.Email) {
  119. verification.TrustStatus = "trusted"
  120. }
  121. return nil
  122. }
  123. // Now we drop to the more nuanced trust models...
  124. verification.TrustStatus = "trusted"
  125. if verification.SigningUser.ID == 0 {
  126. // This commit is signed by the default key - but this key is not assigned to a user in the DB.
  127. // However in the repo_model.CollaboratorCommitterTrustModel we cannot mark this as trusted
  128. // unless the default key matches the email of a non-user.
  129. if repoTrustModel == repo_model.CollaboratorCommitterTrustModel && (verification.CommittingUser.ID != 0 ||
  130. verification.SigningUser.Email != verification.CommittingUser.Email) {
  131. verification.TrustStatus = "untrusted"
  132. }
  133. return nil
  134. }
  135. // Check we actually have a GPG SigningKey
  136. var err error
  137. if verification.SigningKey != nil {
  138. var isMember bool
  139. if keyMap != nil {
  140. var has bool
  141. isMember, has = (*keyMap)[verification.SigningKey.KeyID]
  142. if !has {
  143. isMember, err = isOwnerMemberCollaborator(verification.SigningUser)
  144. (*keyMap)[verification.SigningKey.KeyID] = isMember
  145. }
  146. } else {
  147. isMember, err = isOwnerMemberCollaborator(verification.SigningUser)
  148. }
  149. if !isMember {
  150. verification.TrustStatus = "untrusted"
  151. if verification.CommittingUser.ID != verification.SigningUser.ID {
  152. // The committing user and the signing user are not the same
  153. // This should be marked as questionable unless the signing user is a collaborator/team member etc.
  154. verification.TrustStatus = "unmatched"
  155. }
  156. } else if repoTrustModel == repo_model.CollaboratorCommitterTrustModel && verification.CommittingUser.ID != verification.SigningUser.ID {
  157. // The committing user and the signing user are not the same and our trustmodel states that they must match
  158. verification.TrustStatus = "unmatched"
  159. }
  160. }
  161. return err
  162. }