Use single shared random string generation function (#15741)
* Use single shared random string generation function - Replace 3 functions that do the same with 1 shared one - Use crypto/rand over math/rand for a stronger RNG - Output only alphanumerical for URL compatibilty Fixes: #15536 * use const string method * Update modules/avatar/avatar.go Co-authored-by: a1012112796 <1012112796@qq.com> Co-authored-by: a1012112796 <1012112796@qq.com>
This commit is contained in:
parent
270aab429e
commit
1e6fa57acb
15 changed files with 100 additions and 192 deletions
|
@ -8,8 +8,8 @@ import (
|
|||
"crypto/sha256"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"xorm.io/xorm"
|
||||
|
@ -53,7 +53,7 @@ func addScratchHash(x *xorm.Engine) error {
|
|||
|
||||
for _, tfa := range tfas {
|
||||
// generate salt
|
||||
salt, err := generate.GetRandomString(10)
|
||||
salt, err := util.RandomString(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ package migrations
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
@ -65,7 +65,7 @@ func hashAppToken(x *xorm.Engine) error {
|
|||
|
||||
for _, token := range tokens {
|
||||
// generate salt
|
||||
salt, err := generate.GetRandomString(10)
|
||||
salt, err := util.RandomString(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
gouuid "github.com/google/uuid"
|
||||
)
|
||||
|
@ -40,7 +40,7 @@ func (t *AccessToken) AfterLoad() {
|
|||
|
||||
// NewAccessToken creates new access token.
|
||||
func NewAccessToken(t *AccessToken) error {
|
||||
salt, err := generate.GetRandomString(10)
|
||||
salt, err := util.RandomString(10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -11,10 +11,10 @@ import (
|
|||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/secret"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/pquerna/otp/totp"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
@ -34,11 +34,11 @@ type TwoFactor struct {
|
|||
|
||||
// GenerateScratchToken recreates the scratch token the user is using.
|
||||
func (t *TwoFactor) GenerateScratchToken() (string, error) {
|
||||
token, err := generate.GetRandomString(8)
|
||||
token, err := util.RandomString(8)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
t.ScratchSalt, _ = generate.GetRandomString(10)
|
||||
t.ScratchSalt, _ = util.RandomString(10)
|
||||
t.ScratchHash = hashToken(token, t.ScratchSalt)
|
||||
return token, nil
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"unicode/utf8"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -746,7 +745,7 @@ func IsUserExist(uid int64, name string) (bool, error) {
|
|||
|
||||
// GetUserSalt returns a random user salt token.
|
||||
func GetUserSalt() (string, error) {
|
||||
return generate.GetRandomString(10)
|
||||
return util.RandomString(10)
|
||||
}
|
||||
|
||||
// NewGhostUser creates and returns a fake user for someone has deleted his/her account.
|
||||
|
|
|
@ -12,10 +12,9 @@ import (
|
|||
|
||||
// Enable PNG support:
|
||||
_ "image/png"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/issue9/identicon"
|
||||
"github.com/nfnt/resize"
|
||||
|
@ -29,8 +28,11 @@ const AvatarSize = 290
|
|||
// in custom size (height and width).
|
||||
func RandomImageSize(size int, data []byte) (image.Image, error) {
|
||||
randExtent := len(palette.WebSafe) - 32
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
colorIndex := rand.Intn(randExtent)
|
||||
integer, err := util.RandomInt(int64(randExtent))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("util.RandomInt: %v", err)
|
||||
}
|
||||
colorIndex := int(integer)
|
||||
backColorIndex := colorIndex - 1
|
||||
if backColorIndex < 0 {
|
||||
backColorIndex = randExtent - 1
|
||||
|
|
|
@ -13,12 +13,17 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_RandomImage(t *testing.T) {
|
||||
_, err := RandomImage([]byte("gogs@local"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = RandomImageSize(0, []byte("gogs@local"))
|
||||
func Test_RandomImageSize(t *testing.T) {
|
||||
_, err := RandomImageSize(0, []byte("gitea@local"))
|
||||
assert.Error(t, err)
|
||||
|
||||
_, err = RandomImageSize(64, []byte("gitea@local"))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_RandomImage(t *testing.T) {
|
||||
_, err := RandomImage([]byte("gitea@local"))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_PrepareWithPNG(t *testing.T) {
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package context
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// NewSecret creates a new secret
|
||||
func NewSecret() (string, error) {
|
||||
return NewSecretWithLength(32)
|
||||
}
|
||||
|
||||
// NewSecretWithLength creates a new secret for a given length
|
||||
func NewSecretWithLength(length int64) (string, error) {
|
||||
return randomString(length)
|
||||
}
|
||||
|
||||
func randomBytes(len int64) ([]byte, error) {
|
||||
b := make([]byte, len)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func randomString(len int64) (string, error) {
|
||||
b, err := randomBytes(len)
|
||||
return base64.URLEncoding.EncodeToString(b), err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// EncryptSecret encrypts a string with given key into a hex string
|
||||
func EncryptSecret(key string, str string) (string, error) {
|
||||
keyHash := sha256.Sum256([]byte(key))
|
||||
plaintext := []byte(str)
|
||||
ciphertext, err := AesEncrypt(keyHash[:], plaintext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
// DecryptSecret decrypts a previously encrypted hex string
|
||||
func DecryptSecret(key string, cipherhex string) (string, error) {
|
||||
keyHash := sha256.Sum256([]byte(key))
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(cipherhex)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
plaintext, err := AesDecrypt(keyHash[:], ciphertext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(plaintext), nil
|
||||
}
|
|
@ -9,31 +9,12 @@ import (
|
|||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// GetRandomString generate random string by specify chars.
|
||||
func GetRandomString(n int) (string, error) {
|
||||
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
buffer := make([]byte, n)
|
||||
max := big.NewInt(int64(len(alphanum)))
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
index, err := randomInt(max)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buffer[i] = alphanum[index]
|
||||
}
|
||||
|
||||
return string(buffer), nil
|
||||
}
|
||||
|
||||
// NewInternalToken generate a new value intended to be used by INTERNAL_TOKEN.
|
||||
func NewInternalToken() (string, error) {
|
||||
secretBytes := make([]byte, 32)
|
||||
|
@ -69,19 +50,10 @@ func NewJwtSecret() (string, error) {
|
|||
|
||||
// NewSecretKey generate a new value intended to be used by SECRET_KEY.
|
||||
func NewSecretKey() (string, error) {
|
||||
secretKey, err := GetRandomString(64)
|
||||
secretKey, err := util.RandomString(64)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return secretKey, nil
|
||||
}
|
||||
|
||||
func randomInt(max *big.Int) (int, error) {
|
||||
rand, err := rand.Int(rand.Reader, max)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(rand.Int64()), nil
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package generate
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
retVal := m.Run()
|
||||
|
||||
os.Exit(retVal)
|
||||
}
|
||||
|
||||
func TestGetRandomString(t *testing.T) {
|
||||
randomString, err := GetRandomString(4)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, randomString, 4)
|
||||
}
|
|
@ -13,29 +13,18 @@ import (
|
|||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// New creats a new secret
|
||||
func New() (string, error) {
|
||||
return NewWithLength(32)
|
||||
return NewWithLength(44)
|
||||
}
|
||||
|
||||
// NewWithLength creates a new secret for a given length
|
||||
func NewWithLength(length int64) (string, error) {
|
||||
return randomString(length)
|
||||
}
|
||||
|
||||
func randomBytes(len int64) ([]byte, error) {
|
||||
b := make([]byte, len)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func randomString(len int64) (string, error) {
|
||||
b, err := randomBytes(len)
|
||||
return base64.URLEncoding.EncodeToString(b), err
|
||||
return util.RandomString(length)
|
||||
}
|
||||
|
||||
// AesEncrypt encrypts text and given key with AES.
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
func TestNew(t *testing.T) {
|
||||
result, err := New()
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, len(result) > 32)
|
||||
assert.True(t, len(result) == 44)
|
||||
|
||||
result2, err := New()
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -6,7 +6,9 @@ package util
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -124,3 +126,28 @@ func MergeInto(dict map[string]interface{}, values ...interface{}) (map[string]i
|
|||
|
||||
return dict, nil
|
||||
}
|
||||
|
||||
// RandomInt returns a random integer between 0 and limit, inclusive
|
||||
func RandomInt(limit int64) (int64, error) {
|
||||
int, err := rand.Int(rand.Reader, big.NewInt(limit))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int.Int64(), nil
|
||||
}
|
||||
|
||||
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
|
||||
// RandomString generates a random alphanumerical string
|
||||
func RandomString(length int64) (string, error) {
|
||||
bytes := make([]byte, length)
|
||||
limit := int64(len(letters))
|
||||
for i := range bytes {
|
||||
num, err := RandomInt(limit)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bytes[i] = letters[num]
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -118,3 +119,40 @@ func Test_NormalizeEOL(t *testing.T) {
|
|||
|
||||
assert.Equal(t, []byte("mix\nand\nmatch\n."), NormalizeEOL([]byte("mix\r\nand\rmatch\n.")))
|
||||
}
|
||||
|
||||
func Test_RandomInt(t *testing.T) {
|
||||
int, err := RandomInt(255)
|
||||
assert.True(t, int >= 0)
|
||||
assert.True(t, int <= 255)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_RandomString(t *testing.T) {
|
||||
str1, err := RandomString(32)
|
||||
assert.NoError(t, err)
|
||||
matches, err := regexp.MatchString(`^[a-zA-Z0-9]{32}$`, str1)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, matches)
|
||||
|
||||
str2, err := RandomString(32)
|
||||
assert.NoError(t, err)
|
||||
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{32}$`, str1)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, matches)
|
||||
|
||||
assert.NotEqual(t, str1, str2)
|
||||
|
||||
str3, err := RandomString(256)
|
||||
assert.NoError(t, err)
|
||||
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{256}$`, str3)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, matches)
|
||||
|
||||
str4, err := RandomString(256)
|
||||
assert.NoError(t, err)
|
||||
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{256}$`, str4)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, matches)
|
||||
|
||||
assert.NotEqual(t, str3, str4)
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ import (
|
|||
"code.gitea.io/gitea/modules/auth/openid"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/generate"
|
||||
"code.gitea.io/gitea/modules/hcaptcha"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/recaptcha"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
|
@ -411,7 +411,7 @@ func RegisterOpenIDPost(ctx *context.Context) {
|
|||
if length < 256 {
|
||||
length = 256
|
||||
}
|
||||
password, err := generate.GetRandomString(length)
|
||||
password, err := util.RandomString(int64(length))
|
||||
if err != nil {
|
||||
ctx.RenderWithErr(err.Error(), tplSignUpOID, form)
|
||||
return
|
||||
|
|
Reference in a new issue