You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

gpg_key.go 7.5 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "bytes"
  7. "encoding/base64"
  8. "fmt"
  9. "strings"
  10. "time"
  11. "github.com/go-xorm/xorm"
  12. "golang.org/x/crypto/openpgp"
  13. "golang.org/x/crypto/openpgp/packet"
  14. )
  15. // GPGKey represents a GPG key.
  16. type GPGKey struct {
  17. ID int64 `xorm:"pk autoincr"`
  18. OwnerID int64 `xorm:"INDEX NOT NULL"`
  19. KeyID string `xorm:"INDEX TEXT NOT NULL"`
  20. PrimaryKeyID string `xorm:"TEXT"`
  21. Content string `xorm:"TEXT NOT NULL"`
  22. Created time.Time `xorm:"-"`
  23. CreatedUnix int64
  24. Expired time.Time `xorm:"-"`
  25. ExpiredUnix int64
  26. Added time.Time `xorm:"-"`
  27. AddedUnix int64
  28. SubsKey []*GPGKey `xorm:"-"`
  29. Emails []*EmailAddress
  30. CanSign bool
  31. CanEncryptComms bool
  32. CanEncryptStorage bool
  33. CanCertify bool
  34. }
  35. // BeforeInsert will be invoked by XORM before inserting a record
  36. func (key *GPGKey) BeforeInsert() {
  37. key.AddedUnix = time.Now().Unix()
  38. key.ExpiredUnix = key.Expired.Unix()
  39. key.CreatedUnix = key.Created.Unix()
  40. }
  41. // AfterSet is invoked from XORM after setting the value of a field of this object.
  42. func (key *GPGKey) AfterSet(colName string, _ xorm.Cell) {
  43. switch colName {
  44. case "key_id":
  45. x.Where("primary_key_id=?", key.KeyID).Find(&key.SubsKey)
  46. case "added_unix":
  47. key.Added = time.Unix(key.AddedUnix, 0).Local()
  48. case "expired_unix":
  49. key.Expired = time.Unix(key.ExpiredUnix, 0).Local()
  50. case "created_unix":
  51. key.Created = time.Unix(key.CreatedUnix, 0).Local()
  52. }
  53. }
  54. // ListGPGKeys returns a list of public keys belongs to given user.
  55. func ListGPGKeys(uid int64) ([]*GPGKey, error) {
  56. keys := make([]*GPGKey, 0, 5)
  57. return keys, x.Where("owner_id=? AND primary_key_id=''", uid).Find(&keys)
  58. }
  59. // GetGPGKeyByID returns public key by given ID.
  60. func GetGPGKeyByID(keyID int64) (*GPGKey, error) {
  61. key := new(GPGKey)
  62. has, err := x.Id(keyID).Get(key)
  63. if err != nil {
  64. return nil, err
  65. } else if !has {
  66. return nil, ErrGPGKeyNotExist{keyID}
  67. }
  68. return key, nil
  69. }
  70. // checkArmoredGPGKeyString checks if the given key string is a valid GPG armored key.
  71. // The function returns the actual public key on success
  72. func checkArmoredGPGKeyString(content string) (*openpgp.Entity, error) {
  73. list, err := openpgp.ReadArmoredKeyRing(strings.NewReader(content))
  74. if err != nil {
  75. return nil, err
  76. }
  77. return list[0], nil
  78. }
  79. //addGPGKey add key and subkeys to database
  80. func addGPGKey(e Engine, key *GPGKey) (err error) {
  81. // Save GPG primary key.
  82. if _, err = e.Insert(key); err != nil {
  83. return err
  84. }
  85. // Save GPG subs key.
  86. for _, subkey := range key.SubsKey {
  87. if err := addGPGKey(e, subkey); err != nil {
  88. return err
  89. }
  90. }
  91. return nil
  92. }
  93. // AddGPGKey adds new public key to database.
  94. func AddGPGKey(ownerID int64, content string) (*GPGKey, error) {
  95. ekey, err := checkArmoredGPGKeyString(content)
  96. if err != nil {
  97. return nil, err
  98. }
  99. // Key ID cannot be duplicated.
  100. has, err := x.Where("key_id=?", ekey.PrimaryKey.KeyIdString()).
  101. Get(new(GPGKey))
  102. if err != nil {
  103. return nil, err
  104. } else if has {
  105. return nil, ErrGPGKeyIDAlreadyUsed{ekey.PrimaryKey.KeyIdString()}
  106. }
  107. //Get DB session
  108. sess := x.NewSession()
  109. defer sessionRelease(sess)
  110. if err = sess.Begin(); err != nil {
  111. return nil, err
  112. }
  113. key, err := parseGPGKey(ownerID, ekey)
  114. if err != nil {
  115. return nil, err
  116. }
  117. if err = addGPGKey(sess, key); err != nil {
  118. return nil, err
  119. }
  120. return key, sess.Commit()
  121. }
  122. //base64EncPubKey encode public kay content to base 64
  123. func base64EncPubKey(pubkey *packet.PublicKey) (string, error) {
  124. var w bytes.Buffer
  125. err := pubkey.Serialize(&w)
  126. if err != nil {
  127. return "", err
  128. }
  129. return base64.StdEncoding.EncodeToString(w.Bytes()), nil
  130. }
  131. //parseSubGPGKey parse a sub Key
  132. func parseSubGPGKey(ownerID int64, primaryID string, pubkey *packet.PublicKey, expiry time.Time) (*GPGKey, error) {
  133. content, err := base64EncPubKey(pubkey)
  134. if err != nil {
  135. return nil, err
  136. }
  137. return &GPGKey{
  138. OwnerID: ownerID,
  139. KeyID: pubkey.KeyIdString(),
  140. PrimaryKeyID: primaryID,
  141. Content: content,
  142. Created: pubkey.CreationTime,
  143. Expired: expiry,
  144. CanSign: pubkey.CanSign(),
  145. CanEncryptComms: pubkey.PubKeyAlgo.CanEncrypt(),
  146. CanEncryptStorage: pubkey.PubKeyAlgo.CanEncrypt(),
  147. CanCertify: pubkey.PubKeyAlgo.CanSign(),
  148. }, nil
  149. }
  150. //parseGPGKey parse a PrimaryKey entity (primary key + subs keys + self-signature)
  151. func parseGPGKey(ownerID int64, e *openpgp.Entity) (*GPGKey, error) {
  152. pubkey := e.PrimaryKey
  153. //Extract self-sign for expire date based on : https://github.com/golang/crypto/blob/master/openpgp/keys.go#L165
  154. var selfSig *packet.Signature
  155. for _, ident := range e.Identities {
  156. if selfSig == nil {
  157. selfSig = ident.SelfSignature
  158. } else if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
  159. selfSig = ident.SelfSignature
  160. break
  161. }
  162. }
  163. expiry := time.Time{}
  164. if selfSig.KeyLifetimeSecs != nil {
  165. expiry = selfSig.CreationTime.Add(time.Duration(*selfSig.KeyLifetimeSecs) * time.Second)
  166. }
  167. //Parse Subkeys
  168. subkeys := make([]*GPGKey, len(e.Subkeys))
  169. for i, k := range e.Subkeys {
  170. subs, err := parseSubGPGKey(ownerID, pubkey.KeyIdString(), k.PublicKey, expiry)
  171. if err != nil {
  172. return nil, err
  173. }
  174. subkeys[i] = subs
  175. }
  176. //Check emails
  177. userEmails, err := GetEmailAddresses(ownerID)
  178. if err != nil {
  179. return nil, err
  180. }
  181. emails := make([]*EmailAddress, len(e.Identities))
  182. n := 0
  183. for _, ident := range e.Identities {
  184. for _, e := range userEmails {
  185. if e.Email == ident.UserId.Email && e.IsActivated {
  186. emails[n] = e
  187. break
  188. }
  189. }
  190. if emails[n] == nil {
  191. return nil, fmt.Errorf("Failed to found email or is not confirmed : %s", ident.UserId.Email)
  192. }
  193. n++
  194. }
  195. content, err := base64EncPubKey(pubkey)
  196. if err != nil {
  197. return nil, err
  198. }
  199. return &GPGKey{
  200. OwnerID: ownerID,
  201. KeyID: pubkey.KeyIdString(),
  202. PrimaryKeyID: "",
  203. Content: content,
  204. Created: pubkey.CreationTime,
  205. Expired: expiry,
  206. Emails: emails,
  207. SubsKey: subkeys,
  208. CanSign: pubkey.CanSign(),
  209. CanEncryptComms: pubkey.PubKeyAlgo.CanEncrypt(),
  210. CanEncryptStorage: pubkey.PubKeyAlgo.CanEncrypt(),
  211. CanCertify: pubkey.PubKeyAlgo.CanSign(),
  212. }, nil
  213. }
  214. // deleteGPGKey does the actual key deletion
  215. func deleteGPGKey(e *xorm.Session, keyID string) (int64, error) {
  216. if keyID == "" {
  217. return 0, fmt.Errorf("empty KeyId forbidden") //Should never happen but just to be sure
  218. }
  219. return e.Where("key_id=?", keyID).Or("primary_key_id=?", keyID).Delete(new(GPGKey))
  220. }
  221. // DeleteGPGKey deletes GPG key information in database.
  222. func DeleteGPGKey(doer *User, id int64) (err error) {
  223. key, err := GetGPGKeyByID(id)
  224. if err != nil {
  225. if IsErrGPGKeyNotExist(err) {
  226. return nil
  227. }
  228. return fmt.Errorf("GetPublicKeyByID: %v", err)
  229. }
  230. // Check if user has access to delete this key.
  231. if !doer.IsAdmin && doer.ID != key.OwnerID {
  232. return ErrGPGKeyAccessDenied{doer.ID, key.ID}
  233. }
  234. sess := x.NewSession()
  235. defer sessionRelease(sess)
  236. if err = sess.Begin(); err != nil {
  237. return err
  238. }
  239. if _, err = deleteGPGKey(sess, key.KeyID); err != nil {
  240. return err
  241. }
  242. if err = sess.Commit(); err != nil {
  243. return err
  244. }
  245. return nil
  246. }