gitea源码

gpg_key.go 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package asymkey
  4. import (
  5. "context"
  6. "errors"
  7. "fmt"
  8. "strings"
  9. "time"
  10. "code.gitea.io/gitea/models/db"
  11. user_model "code.gitea.io/gitea/models/user"
  12. "code.gitea.io/gitea/modules/timeutil"
  13. "github.com/ProtonMail/go-crypto/openpgp"
  14. "github.com/ProtonMail/go-crypto/openpgp/packet"
  15. "xorm.io/builder"
  16. )
  17. // GPGKey represents a GPG key.
  18. type GPGKey struct {
  19. ID int64 `xorm:"pk autoincr"`
  20. OwnerID int64 `xorm:"INDEX NOT NULL"`
  21. KeyID string `xorm:"INDEX CHAR(16) NOT NULL"`
  22. PrimaryKeyID string `xorm:"CHAR(16)"`
  23. Content string `xorm:"MEDIUMTEXT NOT NULL"`
  24. CreatedUnix timeutil.TimeStamp `xorm:"created"`
  25. ExpiredUnix timeutil.TimeStamp
  26. AddedUnix timeutil.TimeStamp
  27. SubsKey []*GPGKey `xorm:"-"`
  28. Emails []*user_model.EmailAddress
  29. Verified bool `xorm:"NOT NULL DEFAULT false"`
  30. CanSign bool
  31. CanEncryptComms bool
  32. CanEncryptStorage bool
  33. CanCertify bool
  34. }
  35. func init() {
  36. db.RegisterModel(new(GPGKey))
  37. }
  38. // BeforeInsert will be invoked by XORM before inserting a record
  39. func (key *GPGKey) BeforeInsert() {
  40. key.AddedUnix = timeutil.TimeStampNow()
  41. }
  42. func (key *GPGKey) LoadSubKeys(ctx context.Context) error {
  43. if err := db.GetEngine(ctx).Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey); err != nil {
  44. return fmt.Errorf("find Sub GPGkeys[%s]: %v", key.KeyID, err)
  45. }
  46. return nil
  47. }
  48. // PaddedKeyID show KeyID padded to 16 characters
  49. func (key *GPGKey) PaddedKeyID() string {
  50. return PaddedKeyID(key.KeyID)
  51. }
  52. // PaddedKeyID show KeyID padded to 16 characters
  53. func PaddedKeyID(keyID string) string {
  54. if len(keyID) > 15 {
  55. return keyID
  56. }
  57. zeros := "0000000000000000"
  58. return zeros[0:16-len(keyID)] + keyID
  59. }
  60. type FindGPGKeyOptions struct {
  61. db.ListOptions
  62. OwnerID int64
  63. KeyID string
  64. IncludeSubKeys bool
  65. }
  66. func (opts FindGPGKeyOptions) ToConds() builder.Cond {
  67. cond := builder.NewCond()
  68. if !opts.IncludeSubKeys {
  69. cond = cond.And(builder.Eq{"primary_key_id": ""})
  70. }
  71. if opts.OwnerID > 0 {
  72. cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
  73. }
  74. if opts.KeyID != "" {
  75. cond = cond.And(builder.Eq{"key_id": opts.KeyID})
  76. }
  77. return cond
  78. }
  79. func GetGPGKeyForUserByID(ctx context.Context, ownerID, keyID int64) (*GPGKey, error) {
  80. key := new(GPGKey)
  81. has, err := db.GetEngine(ctx).Where("id=? AND owner_id=?", keyID, ownerID).Get(key)
  82. if err != nil {
  83. return nil, err
  84. } else if !has {
  85. return nil, ErrGPGKeyNotExist{keyID}
  86. }
  87. return key, nil
  88. }
  89. // GPGKeyToEntity retrieve the imported key and the traducted entity
  90. func GPGKeyToEntity(ctx context.Context, k *GPGKey) (*openpgp.Entity, error) {
  91. impKey, err := GetGPGImportByKeyID(ctx, k.KeyID)
  92. if err != nil {
  93. return nil, err
  94. }
  95. keys, err := CheckArmoredGPGKeyString(impKey.Content)
  96. if err != nil {
  97. return nil, err
  98. }
  99. return keys[0], err
  100. }
  101. // parseSubGPGKey parse a sub Key
  102. func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, expiry time.Time) (*GPGKey, error) {
  103. content, err := Base64EncPubKey(pubkey)
  104. if err != nil {
  105. return nil, err
  106. }
  107. return &GPGKey{
  108. OwnerID: ownerID,
  109. KeyID: pubkey.KeyIdString(),
  110. PrimaryKeyID: primaryID,
  111. Content: content,
  112. CreatedUnix: timeutil.TimeStamp(pubkey.CreationTime.Unix()),
  113. ExpiredUnix: timeutil.TimeStamp(expiry.Unix()),
  114. CanSign: pubkey.CanSign(),
  115. CanEncryptComms: pubkey.PubKeyAlgo.CanEncrypt(),
  116. CanEncryptStorage: pubkey.PubKeyAlgo.CanEncrypt(),
  117. CanCertify: pubkey.PubKeyAlgo.CanSign(),
  118. }, nil
  119. }
  120. // parseGPGKey parse a PrimaryKey entity (primary key + subs keys + self-signature)
  121. func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified bool) (*GPGKey, error) {
  122. pubkey := e.PrimaryKey
  123. expiry := getExpiryTime(e)
  124. // Parse Subkeys
  125. subkeys := make([]*GPGKey, len(e.Subkeys))
  126. for i, k := range e.Subkeys {
  127. subkeyExpiry := expiry
  128. if k.Sig.KeyLifetimeSecs != nil {
  129. subkeyExpiry = k.PublicKey.CreationTime.Add(time.Duration(*k.Sig.KeyLifetimeSecs) * time.Second)
  130. }
  131. subs, err := parseSubGPGKey(ownerID, pubkey.KeyIdString(), k.PublicKey, subkeyExpiry)
  132. if err != nil {
  133. return nil, ErrGPGKeyParsing{ParseError: err}
  134. }
  135. subkeys[i] = subs
  136. }
  137. // Check emails
  138. userEmails, err := user_model.GetEmailAddresses(ctx, ownerID)
  139. if err != nil {
  140. return nil, err
  141. }
  142. emails := make([]*user_model.EmailAddress, 0, len(e.Identities))
  143. for _, ident := range e.Identities {
  144. if ident.Revoked(time.Now()) {
  145. continue
  146. }
  147. email := strings.ToLower(strings.TrimSpace(ident.UserId.Email))
  148. for _, e := range userEmails {
  149. if e.IsActivated && e.LowerEmail == email {
  150. emails = append(emails, e)
  151. break
  152. }
  153. }
  154. }
  155. if !verified {
  156. // In the case no email as been found
  157. if len(emails) == 0 {
  158. failedEmails := make([]string, 0, len(e.Identities))
  159. for _, ident := range e.Identities {
  160. failedEmails = append(failedEmails, ident.UserId.Email)
  161. }
  162. return nil, ErrGPGNoEmailFound{failedEmails, e.PrimaryKey.KeyIdString()}
  163. }
  164. }
  165. content, err := Base64EncPubKey(pubkey)
  166. if err != nil {
  167. return nil, err
  168. }
  169. return &GPGKey{
  170. OwnerID: ownerID,
  171. KeyID: pubkey.KeyIdString(),
  172. PrimaryKeyID: "",
  173. Content: content,
  174. CreatedUnix: timeutil.TimeStamp(pubkey.CreationTime.Unix()),
  175. ExpiredUnix: timeutil.TimeStamp(expiry.Unix()),
  176. Emails: emails,
  177. SubsKey: subkeys,
  178. Verified: verified,
  179. CanSign: pubkey.CanSign(),
  180. CanEncryptComms: pubkey.PubKeyAlgo.CanEncrypt(),
  181. CanEncryptStorage: pubkey.PubKeyAlgo.CanEncrypt(),
  182. CanCertify: pubkey.PubKeyAlgo.CanSign(),
  183. }, nil
  184. }
  185. // deleteGPGKey does the actual key deletion
  186. func deleteGPGKey(ctx context.Context, keyID string) (int64, error) {
  187. if keyID == "" {
  188. return 0, errors.New("empty KeyId forbidden") // Should never happen but just to be sure
  189. }
  190. // Delete imported key
  191. n, err := db.GetEngine(ctx).Where("key_id=?", keyID).Delete(new(GPGKeyImport))
  192. if err != nil {
  193. return n, err
  194. }
  195. return db.GetEngine(ctx).Where("key_id=?", keyID).Or("primary_key_id=?", keyID).Delete(new(GPGKey))
  196. }
  197. // DeleteGPGKey deletes GPG key information in database.
  198. func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err error) {
  199. key, err := GetGPGKeyForUserByID(ctx, doer.ID, id)
  200. if err != nil {
  201. if IsErrGPGKeyNotExist(err) {
  202. return nil
  203. }
  204. return fmt.Errorf("GetPublicKeyByID: %w", err)
  205. }
  206. return db.WithTx(ctx, func(ctx context.Context) error {
  207. _, err = deleteGPGKey(ctx, key.KeyID)
  208. return err
  209. })
  210. }
  211. func FindGPGKeyWithSubKeys(ctx context.Context, keyID string) ([]*GPGKey, error) {
  212. return db.Find[GPGKey](ctx, FindGPGKeyOptions{
  213. KeyID: keyID,
  214. IncludeSubKeys: true,
  215. })
  216. }