瀏覽代碼

Use base32 for 2FA scratch token (#18384)

* Use base32 for 2FA scratch token
* rename Secure* to Crypto*, add comments
tags/v1.18.0-dev
wxiaoguang 2 年之前
父節點
當前提交
49dd906753
沒有連結到貢獻者的電子郵件帳戶。

+ 6
- 2
models/auth/twofactor.go 查看文件

"crypto/md5" "crypto/md5"
"crypto/sha256" "crypto/sha256"
"crypto/subtle" "crypto/subtle"
"encoding/base32"
"encoding/base64" "encoding/base64"
"fmt" "fmt"




// GenerateScratchToken recreates the scratch token the user is using. // GenerateScratchToken recreates the scratch token the user is using.
func (t *TwoFactor) GenerateScratchToken() (string, error) { func (t *TwoFactor) GenerateScratchToken() (string, error) {
token, err := util.RandomString(8)
tokenBytes, err := util.CryptoRandomBytes(6)
if err != nil { if err != nil {
return "", err return "", err
} }
t.ScratchSalt, _ = util.RandomString(10)
// these chars are specially chosen, avoid ambiguous chars like `0`, `O`, `1`, `I`.
const base32Chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
token := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding).EncodeToString(tokenBytes)
t.ScratchSalt, _ = util.CryptoRandomString(10)
t.ScratchHash = HashToken(token, t.ScratchSalt) t.ScratchHash = HashToken(token, t.ScratchSalt)
return token, nil return token, nil
} }

+ 1
- 1
models/migrations/v71.go 查看文件



for _, tfa := range tfas { for _, tfa := range tfas {
// generate salt // generate salt
salt, err := util.RandomString(10)
salt, err := util.CryptoRandomString(10)
if err != nil { if err != nil {
return err return err
} }

+ 1
- 1
models/migrations/v85.go 查看文件



for _, token := range tokens { for _, token := range tokens {
// generate salt // generate salt
salt, err := util.RandomString(10)
salt, err := util.CryptoRandomString(10)
if err != nil { if err != nil {
return err return err
} }

+ 1
- 1
models/token.go 查看文件



// NewAccessToken creates new access token. // NewAccessToken creates new access token.
func NewAccessToken(t *AccessToken) error { func NewAccessToken(t *AccessToken) error {
salt, err := util.RandomString(10)
salt, err := util.CryptoRandomString(10)
if err != nil { if err != nil {
return err return err
} }

+ 1
- 1
models/user/user.go 查看文件



// GetUserSalt returns a random user salt token. // GetUserSalt returns a random user salt token.
func GetUserSalt() (string, error) { func GetUserSalt() (string, error) {
rBytes, err := util.RandomBytes(SaltByteLength)
rBytes, err := util.CryptoRandomBytes(SaltByteLength)
if err != nil { if err != nil {
return "", err return "", err
} }

+ 1
- 1
modules/generate/generate.go 查看文件



// NewSecretKey generate a new value intended to be used by SECRET_KEY. // NewSecretKey generate a new value intended to be used by SECRET_KEY.
func NewSecretKey() (string, error) { func NewSecretKey() (string, error) {
secretKey, err := util.RandomString(64)
secretKey, err := util.CryptoRandomString(64)
if err != nil { if err != nil {
return "", err return "", err
} }

+ 1
- 1
modules/secret/secret.go 查看文件



// NewWithLength creates a new secret for a given length // NewWithLength creates a new secret for a given length
func NewWithLength(length int64) (string, error) { func NewWithLength(length int64) (string, error) {
return util.RandomString(length)
return util.CryptoRandomString(length)
} }


// AesEncrypt encrypts text and given key with AES. // AesEncrypt encrypts text and given key with AES.

+ 18
- 18
modules/util/util.go 查看文件

return dict, nil return dict, nil
} }


// RandomInt returns a random integer between 0 and limit, inclusive
func RandomInt(limit int64) (int64, error) {
// CryptoRandomInt returns a crypto random integer between 0 and limit, inclusive
func CryptoRandomInt(limit int64) (int64, error) {
rInt, err := rand.Int(rand.Reader, big.NewInt(limit)) rInt, err := rand.Int(rand.Reader, big.NewInt(limit))
if err != nil { if err != nil {
return 0, err return 0, err
return rInt.Int64(), nil return rInt.Int64(), nil
} }


const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const alphanumericalChars = "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)
// CryptoRandomString generates a crypto random alphanumerical string, each byte is generated by [0,61] range
func CryptoRandomString(length int64) (string, error) {
buf := make([]byte, length)
limit := int64(len(alphanumericalChars))
for i := range buf {
num, err := CryptoRandomInt(limit)
if err != nil { if err != nil {
return "", err return "", err
} }
bytes[i] = letters[num]
buf[i] = alphanumericalChars[num]
} }
return string(bytes), nil
return string(buf), nil
} }


// RandomBytes generates `length` bytes
// This differs from RandomString, as RandomString is limits each byte to have
// a maximum value of 63 instead of 255(max byte size)
func RandomBytes(length int64) ([]byte, error) {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
return bytes, err
// CryptoRandomBytes generates `length` crypto bytes
// This differs from CryptoRandomString, as each byte in CryptoRandomString is generated by [0,61] range
// This function generates totally random bytes, each byte is generated by [0,255] range
func CryptoRandomBytes(length int64) ([]byte, error) {
buf := make([]byte, length)
_, err := rand.Read(buf)
return buf, err
} }

+ 9
- 9
modules/util/util_test.go 查看文件

} }


func Test_RandomInt(t *testing.T) { func Test_RandomInt(t *testing.T) {
int, err := RandomInt(255)
int, err := CryptoRandomInt(255)
assert.True(t, int >= 0) assert.True(t, int >= 0)
assert.True(t, int <= 255) assert.True(t, int <= 255)
assert.NoError(t, err) assert.NoError(t, err)
} }


func Test_RandomString(t *testing.T) { func Test_RandomString(t *testing.T) {
str1, err := RandomString(32)
str1, err := CryptoRandomString(32)
assert.NoError(t, err) assert.NoError(t, err)
matches, err := regexp.MatchString(`^[a-zA-Z0-9]{32}$`, str1) matches, err := regexp.MatchString(`^[a-zA-Z0-9]{32}$`, str1)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, matches) assert.True(t, matches)


str2, err := RandomString(32)
str2, err := CryptoRandomString(32)
assert.NoError(t, err) assert.NoError(t, err)
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{32}$`, str1) matches, err = regexp.MatchString(`^[a-zA-Z0-9]{32}$`, str1)
assert.NoError(t, err) assert.NoError(t, err)


assert.NotEqual(t, str1, str2) assert.NotEqual(t, str1, str2)


str3, err := RandomString(256)
str3, err := CryptoRandomString(256)
assert.NoError(t, err) assert.NoError(t, err)
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{256}$`, str3) matches, err = regexp.MatchString(`^[a-zA-Z0-9]{256}$`, str3)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, matches) assert.True(t, matches)


str4, err := RandomString(256)
str4, err := CryptoRandomString(256)
assert.NoError(t, err) assert.NoError(t, err)
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{256}$`, str4) matches, err = regexp.MatchString(`^[a-zA-Z0-9]{256}$`, str4)
assert.NoError(t, err) assert.NoError(t, err)
} }


func Test_RandomBytes(t *testing.T) { func Test_RandomBytes(t *testing.T) {
bytes1, err := RandomBytes(32)
bytes1, err := CryptoRandomBytes(32)
assert.NoError(t, err) assert.NoError(t, err)


bytes2, err := RandomBytes(32)
bytes2, err := CryptoRandomBytes(32)
assert.NoError(t, err) assert.NoError(t, err)


assert.NotEqual(t, bytes1, bytes2) assert.NotEqual(t, bytes1, bytes2)


bytes3, err := RandomBytes(256)
bytes3, err := CryptoRandomBytes(256)
assert.NoError(t, err) assert.NoError(t, err)


bytes4, err := RandomBytes(256)
bytes4, err := CryptoRandomBytes(256)
assert.NoError(t, err) assert.NoError(t, err)


assert.NotEqual(t, bytes3, bytes4) assert.NotEqual(t, bytes3, bytes4)

+ 1
- 1
routers/web/auth/openid.go 查看文件

if length < 256 { if length < 256 {
length = 256 length = 256
} }
password, err := util.RandomString(int64(length))
password, err := util.CryptoRandomString(int64(length))
if err != nil { if err != nil {
ctx.RenderWithErr(err.Error(), tplSignUpOID, form) ctx.RenderWithErr(err.Error(), tplSignUpOID, form)
return return

+ 1
- 1
routers/web/repo/setting.go 查看文件

return return
} }


remoteSuffix, err := util.RandomString(10)
remoteSuffix, err := util.CryptoRandomString(10)
if err != nil { if err != nil {
ctx.ServerError("RandomString", err) ctx.ServerError("RandomString", err)
return return

Loading…
取消
儲存