mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-28 15:37:35 +00:00
4498a26222
Backport #24573 Help some users like #16832 #1851 There are many users reporting similar problem: if the SECRET_KEY mismatches, some operations (like 2FA login) only reports unclear 500 error and unclear "base64 decode error" log (some maintainers ever spent a lot of time on debugging such problem) The SECRET_KEY was not well-designed and it is also a kind of technical debt. Since it couldn't be fixed easily, it's good to add clearer error messages, then at least users could know what the real problem is.
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package secret
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// 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, fmt.Errorf("AesEncrypt invalid key: %v", 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, fmt.Errorf("AesEncrypt unable to read IV: %w", 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("AesDecrypt 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, fmt.Errorf("AesDecrypt invalid decrypted base64 string: %w", err)
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// EncryptSecret encrypts a string with given key into a hex string
|
|
func EncryptSecret(key, str string) (string, error) {
|
|
keyHash := sha256.Sum256([]byte(key))
|
|
plaintext := []byte(str)
|
|
ciphertext, err := AesEncrypt(keyHash[:], plaintext)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to encrypt by secret: %w", err)
|
|
}
|
|
return hex.EncodeToString(ciphertext), nil
|
|
}
|
|
|
|
// DecryptSecret decrypts a previously encrypted hex string
|
|
func DecryptSecret(key, cipherHex string) (string, error) {
|
|
keyHash := sha256.Sum256([]byte(key))
|
|
ciphertext, err := hex.DecodeString(cipherHex)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to decrypt by secret, invalid hex string: %w", err)
|
|
}
|
|
plaintext, err := AesDecrypt(keyHash[:], ciphertext)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to decrypt by secret, the key (maybe SECRET_KEY?) might be incorrect: %w", err)
|
|
}
|
|
return string(plaintext), nil
|
|
}
|