From edce41ae652b278aa938dd8b2208722f3ee6afb4 Mon Sep 17 00:00:00 2001
From: Morgan Bazalgette <git@howl.moe>
Date: Sun, 31 Dec 2017 03:19:42 +0100
Subject: [PATCH] go back to using CFB for AES decryption/encryption for 2FA
 (#3274)

---
 models/twofactor.go | 47 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 44 insertions(+), 3 deletions(-)

diff --git a/models/twofactor.go b/models/twofactor.go
index 36ff5db422..526ab917e8 100644
--- a/models/twofactor.go
+++ b/models/twofactor.go
@@ -5,11 +5,15 @@
 package models
 
 import (
+	"crypto/aes"
+	"crypto/cipher"
 	"crypto/md5"
+	"crypto/rand"
 	"crypto/subtle"
 	"encoding/base64"
+	"errors"
+	"io"
 
-	"github.com/Unknwon/com"
 	"github.com/pquerna/otp/totp"
 
 	"code.gitea.io/gitea/modules/base"
@@ -52,7 +56,7 @@ func (t *TwoFactor) getEncryptionKey() []byte {
 
 // SetSecret sets the 2FA secret.
 func (t *TwoFactor) SetSecret(secret string) error {
-	secretBytes, err := com.AESGCMEncrypt(t.getEncryptionKey(), []byte(secret))
+	secretBytes, err := aesEncrypt(t.getEncryptionKey(), []byte(secret))
 	if err != nil {
 		return err
 	}
@@ -66,7 +70,7 @@ func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
 	if err != nil {
 		return false, err
 	}
-	secret, err := com.AESGCMDecrypt(t.getEncryptionKey(), decodedStoredSecret)
+	secret, err := aesDecrypt(t.getEncryptionKey(), decodedStoredSecret)
 	if err != nil {
 		return false, err
 	}
@@ -74,6 +78,43 @@ func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
 	return totp.Validate(passcode, secretStr), nil
 }
 
+// aesEncrypt encrypts text and given key with AES.
+func aesEncrypt(key, text []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	b := base64.StdEncoding.EncodeToString(text)
+	ciphertext := make([]byte, aes.BlockSize+len(b))
+	iv := ciphertext[:aes.BlockSize]
+	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+		return nil, err
+	}
+	cfb := cipher.NewCFBEncrypter(block, iv)
+	cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
+	return ciphertext, nil
+}
+
+// aesDecrypt decrypts text and given key with AES.
+func aesDecrypt(key, text []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	if len(text) < aes.BlockSize {
+		return nil, errors.New("ciphertext too short")
+	}
+	iv := text[:aes.BlockSize]
+	text = text[aes.BlockSize:]
+	cfb := cipher.NewCFBDecrypter(block, iv)
+	cfb.XORKeyStream(text, text)
+	data, err := base64.StdEncoding.DecodeString(string(text))
+	if err != nil {
+		return nil, err
+	}
+	return data, nil
+}
+
 // NewTwoFactor creates a new two-factor authentication token.
 func NewTwoFactor(t *TwoFactor) error {
 	err := t.GenerateScratchToken()