* Use base32 for 2FA scratch token * rename Secure* to Crypto*, add commentstags/v1.18.0-dev
"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 | ||||
} | } |
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 | ||||
} | } |
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 | ||||
} | } |
// 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 | ||||
} | } |
// 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 | ||||
} | } |
// 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 | ||||
} | } |
// 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. |
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 | |||||
} | } |
} | } | ||||
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) |
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 |
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 |