Skip to content

Commit 1dc2860

Browse files
committed
Use base32 for 2FA scratch token
1 parent 4bfd749 commit 1dc2860

File tree

11 files changed

+40
-37
lines changed

11 files changed

+40
-37
lines changed

models/auth/twofactor.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"crypto/md5"
99
"crypto/sha256"
1010
"crypto/subtle"
11+
"encoding/base32"
1112
"encoding/base64"
1213
"fmt"
1314

@@ -58,11 +59,13 @@ func init() {
5859

5960
// GenerateScratchToken recreates the scratch token the user is using.
6061
func (t *TwoFactor) GenerateScratchToken() (string, error) {
61-
token, err := util.RandomString(8)
62+
tokenBytes, err := util.SecureRandomBytes(6)
6263
if err != nil {
6364
return "", err
6465
}
65-
t.ScratchSalt, _ = util.RandomString(10)
66+
const base32Chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
67+
token := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding).EncodeToString(tokenBytes)
68+
t.ScratchSalt, _ = util.SecureRandomString(10)
6669
t.ScratchHash = HashToken(token, t.ScratchSalt)
6770
return token, nil
6871
}

models/migrations/v71.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func addScratchHash(x *xorm.Engine) error {
5353

5454
for _, tfa := range tfas {
5555
// generate salt
56-
salt, err := util.RandomString(10)
56+
salt, err := util.SecureRandomString(10)
5757
if err != nil {
5858
return err
5959
}

models/migrations/v85.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func hashAppToken(x *xorm.Engine) error {
6565

6666
for _, token := range tokens {
6767
// generate salt
68-
salt, err := util.RandomString(10)
68+
salt, err := util.SecureRandomString(10)
6969
if err != nil {
7070
return err
7171
}

models/token.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func init() {
6262

6363
// NewAccessToken creates new access token.
6464
func NewAccessToken(t *AccessToken) error {
65-
salt, err := util.RandomString(10)
65+
salt, err := util.SecureRandomString(10)
6666
if err != nil {
6767
return err
6868
}

models/user/user.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ const SaltByteLength = 16
533533

534534
// GetUserSalt returns a random user salt token.
535535
func GetUserSalt() (string, error) {
536-
rBytes, err := util.RandomBytes(SaltByteLength)
536+
rBytes, err := util.SecureRandomBytes(SaltByteLength)
537537
if err != nil {
538538
return "", err
539539
}

modules/generate/generate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func NewJwtSecretBase64() (string, error) {
6060

6161
// NewSecretKey generate a new value intended to be used by SECRET_KEY.
6262
func NewSecretKey() (string, error) {
63-
secretKey, err := util.RandomString(64)
63+
secretKey, err := util.SecureRandomString(64)
6464
if err != nil {
6565
return "", err
6666
}

modules/secret/secret.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func New() (string, error) {
2424

2525
// NewWithLength creates a new secret for a given length
2626
func NewWithLength(length int64) (string, error) {
27-
return util.RandomString(length)
27+
return util.SecureRandomString(length)
2828
}
2929

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

modules/util/util.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -137,36 +137,36 @@ func MergeInto(dict map[string]interface{}, values ...interface{}) (map[string]i
137137
return dict, nil
138138
}
139139

140-
// RandomInt returns a random integer between 0 and limit, inclusive
141-
func RandomInt(limit int64) (int64, error) {
140+
// SecureRandomInt returns a secure random integer between 0 and limit, inclusive
141+
func SecureRandomInt(limit int64) (int64, error) {
142142
rInt, err := rand.Int(rand.Reader, big.NewInt(limit))
143143
if err != nil {
144144
return 0, err
145145
}
146146
return rInt.Int64(), nil
147147
}
148148

149-
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
149+
const randomLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
150150

151-
// RandomString generates a random alphanumerical string
152-
func RandomString(length int64) (string, error) {
153-
bytes := make([]byte, length)
154-
limit := int64(len(letters))
155-
for i := range bytes {
156-
num, err := RandomInt(limit)
151+
// SecureRandomString generates a secure random alphanumerical string, each byte is generated by [0,61] range
152+
func SecureRandomString(length int64) (string, error) {
153+
buf := make([]byte, length)
154+
limit := int64(len(randomLetters))
155+
for i := range buf {
156+
num, err := SecureRandomInt(limit)
157157
if err != nil {
158158
return "", err
159159
}
160-
bytes[i] = letters[num]
160+
buf[i] = randomLetters[num]
161161
}
162-
return string(bytes), nil
162+
return string(buf), nil
163163
}
164164

165-
// RandomBytes generates `length` bytes
166-
// This differs from RandomString, as RandomString is limits each byte to have
167-
// a maximum value of 63 instead of 255(max byte size)
168-
func RandomBytes(length int64) ([]byte, error) {
169-
bytes := make([]byte, length)
170-
_, err := rand.Read(bytes)
171-
return bytes, err
165+
// SecureRandomBytes generates `length` bytes
166+
// This differs from SecureRandomString, as each byte in SecureRandomString is generated by [0,61] range
167+
// This function generates totally random bytes, each byte is generated by [0,255] range
168+
func SecureRandomBytes(length int64) ([]byte, error) {
169+
buf := make([]byte, length)
170+
_, err := rand.Read(buf)
171+
return buf, err
172172
}

modules/util/util_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -120,34 +120,34 @@ func Test_NormalizeEOL(t *testing.T) {
120120
}
121121

122122
func Test_RandomInt(t *testing.T) {
123-
int, err := RandomInt(255)
123+
int, err := SecureRandomInt(255)
124124
assert.True(t, int >= 0)
125125
assert.True(t, int <= 255)
126126
assert.NoError(t, err)
127127
}
128128

129129
func Test_RandomString(t *testing.T) {
130-
str1, err := RandomString(32)
130+
str1, err := SecureRandomString(32)
131131
assert.NoError(t, err)
132132
matches, err := regexp.MatchString(`^[a-zA-Z0-9]{32}$`, str1)
133133
assert.NoError(t, err)
134134
assert.True(t, matches)
135135

136-
str2, err := RandomString(32)
136+
str2, err := SecureRandomString(32)
137137
assert.NoError(t, err)
138138
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{32}$`, str1)
139139
assert.NoError(t, err)
140140
assert.True(t, matches)
141141

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

144-
str3, err := RandomString(256)
144+
str3, err := SecureRandomString(256)
145145
assert.NoError(t, err)
146146
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{256}$`, str3)
147147
assert.NoError(t, err)
148148
assert.True(t, matches)
149149

150-
str4, err := RandomString(256)
150+
str4, err := SecureRandomString(256)
151151
assert.NoError(t, err)
152152
matches, err = regexp.MatchString(`^[a-zA-Z0-9]{256}$`, str4)
153153
assert.NoError(t, err)
@@ -157,18 +157,18 @@ func Test_RandomString(t *testing.T) {
157157
}
158158

159159
func Test_RandomBytes(t *testing.T) {
160-
bytes1, err := RandomBytes(32)
160+
bytes1, err := SecureRandomBytes(32)
161161
assert.NoError(t, err)
162162

163-
bytes2, err := RandomBytes(32)
163+
bytes2, err := SecureRandomBytes(32)
164164
assert.NoError(t, err)
165165

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

168-
bytes3, err := RandomBytes(256)
168+
bytes3, err := SecureRandomBytes(256)
169169
assert.NoError(t, err)
170170

171-
bytes4, err := RandomBytes(256)
171+
bytes4, err := SecureRandomBytes(256)
172172
assert.NoError(t, err)
173173

174174
assert.NotEqual(t, bytes3, bytes4)

routers/web/auth/openid.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ func RegisterOpenIDPost(ctx *context.Context) {
416416
if length < 256 {
417417
length = 256
418418
}
419-
password, err := util.RandomString(int64(length))
419+
password, err := util.SecureRandomString(int64(length))
420420
if err != nil {
421421
ctx.RenderWithErr(err.Error(), tplSignUpOID, form)
422422
return

routers/web/repo/setting.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ func SettingsPost(ctx *context.Context) {
337337
return
338338
}
339339

340-
remoteSuffix, err := util.RandomString(10)
340+
remoteSuffix, err := util.SecureRandomString(10)
341341
if err != nil {
342342
ctx.ServerError("RandomString", err)
343343
return

0 commit comments

Comments
 (0)