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

twofactor.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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. "crypto/aes"
  7. "crypto/cipher"
  8. "crypto/md5"
  9. "crypto/rand"
  10. "crypto/subtle"
  11. "encoding/base64"
  12. "errors"
  13. "io"
  14. "github.com/pquerna/otp/totp"
  15. "code.gitea.io/gitea/modules/generate"
  16. "code.gitea.io/gitea/modules/setting"
  17. "code.gitea.io/gitea/modules/util"
  18. )
  19. // TwoFactor represents a two-factor authentication token.
  20. type TwoFactor struct {
  21. ID int64 `xorm:"pk autoincr"`
  22. UID int64 `xorm:"UNIQUE"`
  23. Secret string
  24. ScratchToken string
  25. LastUsedPasscode string `xorm:"VARCHAR(10)"`
  26. CreatedUnix util.TimeStamp `xorm:"INDEX created"`
  27. UpdatedUnix util.TimeStamp `xorm:"INDEX updated"`
  28. }
  29. // GenerateScratchToken recreates the scratch token the user is using.
  30. func (t *TwoFactor) GenerateScratchToken() error {
  31. token, err := generate.GetRandomString(8)
  32. if err != nil {
  33. return err
  34. }
  35. t.ScratchToken = token
  36. return nil
  37. }
  38. // VerifyScratchToken verifies if the specified scratch token is valid.
  39. func (t *TwoFactor) VerifyScratchToken(token string) bool {
  40. if len(token) == 0 {
  41. return false
  42. }
  43. return subtle.ConstantTimeCompare([]byte(token), []byte(t.ScratchToken)) == 1
  44. }
  45. func (t *TwoFactor) getEncryptionKey() []byte {
  46. k := md5.Sum([]byte(setting.SecretKey))
  47. return k[:]
  48. }
  49. // SetSecret sets the 2FA secret.
  50. func (t *TwoFactor) SetSecret(secret string) error {
  51. secretBytes, err := aesEncrypt(t.getEncryptionKey(), []byte(secret))
  52. if err != nil {
  53. return err
  54. }
  55. t.Secret = base64.StdEncoding.EncodeToString(secretBytes)
  56. return nil
  57. }
  58. // ValidateTOTP validates the provided passcode.
  59. func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
  60. decodedStoredSecret, err := base64.StdEncoding.DecodeString(t.Secret)
  61. if err != nil {
  62. return false, err
  63. }
  64. secret, err := aesDecrypt(t.getEncryptionKey(), decodedStoredSecret)
  65. if err != nil {
  66. return false, err
  67. }
  68. secretStr := string(secret)
  69. return totp.Validate(passcode, secretStr), nil
  70. }
  71. // aesEncrypt encrypts text and given key with AES.
  72. func aesEncrypt(key, text []byte) ([]byte, error) {
  73. block, err := aes.NewCipher(key)
  74. if err != nil {
  75. return nil, err
  76. }
  77. b := base64.StdEncoding.EncodeToString(text)
  78. ciphertext := make([]byte, aes.BlockSize+len(b))
  79. iv := ciphertext[:aes.BlockSize]
  80. if _, err := io.ReadFull(rand.Reader, iv); err != nil {
  81. return nil, err
  82. }
  83. cfb := cipher.NewCFBEncrypter(block, iv)
  84. cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
  85. return ciphertext, nil
  86. }
  87. // aesDecrypt decrypts text and given key with AES.
  88. func aesDecrypt(key, text []byte) ([]byte, error) {
  89. block, err := aes.NewCipher(key)
  90. if err != nil {
  91. return nil, err
  92. }
  93. if len(text) < aes.BlockSize {
  94. return nil, errors.New("ciphertext too short")
  95. }
  96. iv := text[:aes.BlockSize]
  97. text = text[aes.BlockSize:]
  98. cfb := cipher.NewCFBDecrypter(block, iv)
  99. cfb.XORKeyStream(text, text)
  100. data, err := base64.StdEncoding.DecodeString(string(text))
  101. if err != nil {
  102. return nil, err
  103. }
  104. return data, nil
  105. }
  106. // NewTwoFactor creates a new two-factor authentication token.
  107. func NewTwoFactor(t *TwoFactor) error {
  108. err := t.GenerateScratchToken()
  109. if err != nil {
  110. return err
  111. }
  112. _, err = x.Insert(t)
  113. return err
  114. }
  115. // UpdateTwoFactor updates a two-factor authentication token.
  116. func UpdateTwoFactor(t *TwoFactor) error {
  117. _, err := x.ID(t.ID).AllCols().Update(t)
  118. return err
  119. }
  120. // GetTwoFactorByUID returns the two-factor authentication token associated with
  121. // the user, if any.
  122. func GetTwoFactorByUID(uid int64) (*TwoFactor, error) {
  123. twofa := &TwoFactor{UID: uid}
  124. has, err := x.Get(twofa)
  125. if err != nil {
  126. return nil, err
  127. } else if !has {
  128. return nil, ErrTwoFactorNotEnrolled{uid}
  129. }
  130. return twofa, nil
  131. }
  132. // DeleteTwoFactorByID deletes two-factor authentication token by given ID.
  133. func DeleteTwoFactorByID(id, userID int64) error {
  134. cnt, err := x.ID(id).Delete(&TwoFactor{
  135. UID: userID,
  136. })
  137. if err != nil {
  138. return err
  139. } else if cnt != 1 {
  140. return ErrTwoFactorNotEnrolled{userID}
  141. }
  142. return nil
  143. }