mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-04-27 01:56:22 +00:00
chore(sec): unify usage of crypto/rand.Read
(#7453)
- Unify the usage of [`crypto/rand.Read`](https://pkg.go.dev/crypto/rand#Read) to `util.CryptoRandomBytes`. - Refactor `util.CryptoRandomBytes` to never return an error. It is documented by Go, https://go.dev/issue/66821, to always succeed. So if we still receive a error or if the returned bytes read is not equal to the expected bytes to be read we panic (just to be on the safe side). - This simplifies a lot of code to no longer care about error handling. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7453 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-committed-by: Gusted <postmaster@gusted.xyz>
This commit is contained in:
parent
99fc04b763
commit
53df0bf9a4
25 changed files with 61 additions and 163 deletions
|
@ -70,10 +70,7 @@ func runGenerateInternalToken(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
func runGenerateLfsJwtSecret(c *cli.Context) error {
|
||||||
_, jwtSecretBase64, err := generate.NewJwtSecret()
|
_, jwtSecretBase64 := generate.NewJwtSecret()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("%s", jwtSecretBase64)
|
fmt.Printf("%s", jwtSecretBase64)
|
||||||
|
|
||||||
|
|
|
@ -168,12 +168,7 @@ func (r *ActionRunner) GenerateToken() (err error) {
|
||||||
// UpdateSecret updates the hash based on the specified token. It does not
|
// UpdateSecret updates the hash based on the specified token. It does not
|
||||||
// ensure that the runner's UUID matches the first 16 bytes of the token.
|
// ensure that the runner's UUID matches the first 16 bytes of the token.
|
||||||
func (r *ActionRunner) UpdateSecret(token string) error {
|
func (r *ActionRunner) UpdateSecret(token string) error {
|
||||||
saltBytes, err := util.CryptoRandomBytes(16)
|
salt := hex.EncodeToString(util.CryptoRandomBytes(16))
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("CryptoRandomBytes %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
salt := hex.EncodeToString(saltBytes)
|
|
||||||
|
|
||||||
r.Token = token
|
r.Token = token
|
||||||
r.TokenSalt = salt
|
r.TokenSalt = salt
|
||||||
|
|
|
@ -22,11 +22,7 @@ func generateSaltedToken() (string, string, string, string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", "", err
|
return "", "", "", "", err
|
||||||
}
|
}
|
||||||
buf, err := util.CryptoRandomBytes(20)
|
token := hex.EncodeToString(util.CryptoRandomBytes(20))
|
||||||
if err != nil {
|
|
||||||
return "", "", "", "", err
|
|
||||||
}
|
|
||||||
token := hex.EncodeToString(buf)
|
|
||||||
hash := auth_model.HashToken(token, salt)
|
hash := auth_model.HashToken(token, salt)
|
||||||
return token, salt, hash, token[len(token)-8:], nil
|
return token, salt, hash, token[len(token)-8:], nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,12 +111,8 @@ func generateAccessToken(t *AccessToken) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
token, err := util.CryptoRandomBytes(20)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
t.TokenSalt = salt
|
t.TokenSalt = salt
|
||||||
t.Token = hex.EncodeToString(token)
|
t.Token = hex.EncodeToString(util.CryptoRandomBytes(20))
|
||||||
t.TokenHash = HashToken(t.Token, t.TokenSalt)
|
t.TokenHash = HashToken(t.Token, t.TokenSalt)
|
||||||
t.TokenLastEight = t.Token[len(t.Token)-8:]
|
t.TokenLastEight = t.Token[len(t.Token)-8:]
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -63,10 +63,7 @@ func (authToken *AuthorizationToken) IsExpired() bool {
|
||||||
func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeStamp, purpose AuthorizationPurpose) (lookupKey, validator string, err error) {
|
func GenerateAuthToken(ctx context.Context, userID int64, expiry timeutil.TimeStamp, purpose AuthorizationPurpose) (lookupKey, validator string, err error) {
|
||||||
// Request 64 random bytes. The first 32 bytes will be used for the lookupKey
|
// Request 64 random bytes. The first 32 bytes will be used for the lookupKey
|
||||||
// and the other 32 bytes will be used for the validator.
|
// and the other 32 bytes will be used for the validator.
|
||||||
rBytes, err := util.CryptoRandomBytes(64)
|
rBytes := util.CryptoRandomBytes(64)
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
hexEncoded := hex.EncodeToString(rBytes)
|
hexEncoded := hex.EncodeToString(rBytes)
|
||||||
validator, lookupKey = hexEncoded[64:], hexEncoded[:64]
|
validator, lookupKey = hexEncoded[64:], hexEncoded[:64]
|
||||||
|
|
||||||
|
|
|
@ -184,13 +184,9 @@ var base32Lower = base32.NewEncoding(lowerBase32Chars).WithPadding(base32.NoPadd
|
||||||
|
|
||||||
// GenerateClientSecret will generate the client secret and returns the plaintext and saves the hash at the database
|
// GenerateClientSecret will generate the client secret and returns the plaintext and saves the hash at the database
|
||||||
func (app *OAuth2Application) GenerateClientSecret(ctx context.Context) (string, error) {
|
func (app *OAuth2Application) GenerateClientSecret(ctx context.Context) (string, error) {
|
||||||
rBytes, err := util.CryptoRandomBytes(32)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// Add a prefix to the base32, this is in order to make it easier
|
// Add a prefix to the base32, this is in order to make it easier
|
||||||
// for code scanners to grab sensitive tokens.
|
// for code scanners to grab sensitive tokens.
|
||||||
clientSecret := "gto_" + base32Lower.EncodeToString(rBytes)
|
clientSecret := "gto_" + base32Lower.EncodeToString(util.CryptoRandomBytes(32))
|
||||||
|
|
||||||
hashedSecret, err := bcrypt.GenerateFromPassword([]byte(clientSecret), bcrypt.DefaultCost)
|
hashedSecret, err := bcrypt.GenerateFromPassword([]byte(clientSecret), bcrypt.DefaultCost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -475,13 +471,9 @@ func (grant *OAuth2Grant) TableName() string {
|
||||||
|
|
||||||
// GenerateNewAuthorizationCode generates a new authorization code for a grant and saves it to the database
|
// GenerateNewAuthorizationCode generates a new authorization code for a grant and saves it to the database
|
||||||
func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redirectURI, codeChallenge, codeChallengeMethod string) (code *OAuth2AuthorizationCode, err error) {
|
func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redirectURI, codeChallenge, codeChallengeMethod string) (code *OAuth2AuthorizationCode, err error) {
|
||||||
rBytes, err := util.CryptoRandomBytes(32)
|
|
||||||
if err != nil {
|
|
||||||
return &OAuth2AuthorizationCode{}, err
|
|
||||||
}
|
|
||||||
// Add a prefix to the base32, this is in order to make it easier
|
// Add a prefix to the base32, this is in order to make it easier
|
||||||
// for code scanners to grab sensitive tokens.
|
// for code scanners to grab sensitive tokens.
|
||||||
codeSecret := "gta_" + base32Lower.EncodeToString(rBytes)
|
codeSecret := "gta_" + base32Lower.EncodeToString(util.CryptoRandomBytes(32))
|
||||||
|
|
||||||
code = &OAuth2AuthorizationCode{
|
code = &OAuth2AuthorizationCode{
|
||||||
Grant: grant,
|
Grant: grant,
|
||||||
|
|
|
@ -61,17 +61,13 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
tokenBytes, err := util.CryptoRandomBytes(6)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// these chars are specially chosen, avoid ambiguous chars like `0`, `O`, `1`, `I`.
|
// these chars are specially chosen, avoid ambiguous chars like `0`, `O`, `1`, `I`.
|
||||||
const base32Chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
const base32Chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
|
||||||
token := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding).EncodeToString(tokenBytes)
|
token := base32.NewEncoding(base32Chars).WithPadding(base32.NoPadding).EncodeToString(util.CryptoRandomBytes(6))
|
||||||
t.ScratchSalt, _ = util.CryptoRandomString(10)
|
t.ScratchSalt, _ = util.CryptoRandomString(10)
|
||||||
t.ScratchHash = HashToken(token, t.ScratchSalt)
|
t.ScratchHash = HashToken(token, t.ScratchSalt)
|
||||||
return token, nil
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashToken return the hashable salt
|
// HashToken return the hashable salt
|
||||||
|
|
|
@ -289,12 +289,8 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
org.LowerName = strings.ToLower(org.Name)
|
org.LowerName = strings.ToLower(org.Name)
|
||||||
if org.Rands, err = user_model.GetUserSalt(); err != nil {
|
org.Rands = user_model.GetUserSalt()
|
||||||
return err
|
org.Salt = user_model.GetUserSalt()
|
||||||
}
|
|
||||||
if org.Salt, err = user_model.GetUserSalt(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
org.UseCustomAvatar = true
|
org.UseCustomAvatar = true
|
||||||
org.MaxRepoCreation = -1
|
org.MaxRepoCreation = -1
|
||||||
org.NumTeams = 1
|
org.NumTeams = 1
|
||||||
|
|
|
@ -266,9 +266,7 @@ func updateActivation(ctx context.Context, email *EmailAddress, activate bool) e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if user.Rands, err = GetUserSalt(); err != nil {
|
user.Rands = GetUserSalt()
|
||||||
return err
|
|
||||||
}
|
|
||||||
email.IsActivated = activate
|
email.IsActivated = activate
|
||||||
if _, err := db.GetEngine(ctx).ID(email.ID).Cols("is_activated").Update(email); err != nil {
|
if _, err := db.GetEngine(ctx).ID(email.ID).Cols("is_activated").Update(email); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -403,9 +401,7 @@ func ActivateUserEmail(ctx context.Context, userID int64, email string, activate
|
||||||
// The user's activation state should be synchronized with the primary email
|
// The user's activation state should be synchronized with the primary email
|
||||||
if user.IsActive != activate {
|
if user.IsActive != activate {
|
||||||
user.IsActive = activate
|
user.IsActive = activate
|
||||||
if user.Rands, err = GetUserSalt(); err != nil {
|
user.Rands = GetUserSalt()
|
||||||
return fmt.Errorf("unable to generate salt: %w", err)
|
|
||||||
}
|
|
||||||
if err = UpdateUserCols(ctx, user, "is_active", "rands"); err != nil {
|
if err = UpdateUserCols(ctx, user, "is_active", "rands"); err != nil {
|
||||||
return fmt.Errorf("unable to updateUserCols() for user ID: %d: %w", userID, err)
|
return fmt.Errorf("unable to updateUserCols() for user ID: %d: %w", userID, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -387,9 +387,7 @@ func (u *User) SetPassword(passwd string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.Salt, err = GetUserSalt(); err != nil {
|
u.Salt = GetUserSalt()
|
||||||
return err
|
|
||||||
}
|
|
||||||
if u.Passwd, err = hash.Parse(setting.PasswordHashAlgo).Hash(passwd, u.Salt); err != nil {
|
if u.Passwd, err = hash.Parse(setting.PasswordHashAlgo).Hash(passwd, u.Salt); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -559,13 +557,9 @@ func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
|
||||||
const SaltByteLength = 16
|
const SaltByteLength = 16
|
||||||
|
|
||||||
// GetUserSalt returns a random user salt token.
|
// GetUserSalt returns a random user salt token.
|
||||||
func GetUserSalt() (string, error) {
|
func GetUserSalt() string {
|
||||||
rBytes, err := util.CryptoRandomBytes(SaltByteLength)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
// Returns a 32 bytes long string.
|
// Returns a 32 bytes long string.
|
||||||
return hex.EncodeToString(rBytes), nil
|
return hex.EncodeToString(util.CryptoRandomBytes(SaltByteLength))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: The set of characters here can safely expand without a breaking change,
|
// Note: The set of characters here can safely expand without a breaking change,
|
||||||
|
@ -772,9 +766,7 @@ func createUser(ctx context.Context, u *User, createdByAdmin bool, overwriteDefa
|
||||||
|
|
||||||
u.LowerName = strings.ToLower(u.Name)
|
u.LowerName = strings.ToLower(u.Name)
|
||||||
u.AvatarEmail = u.Email
|
u.AvatarEmail = u.Email
|
||||||
if u.Rands, err = GetUserSalt(); err != nil {
|
u.Rands = GetUserSalt()
|
||||||
return err
|
|
||||||
}
|
|
||||||
if u.Passwd != "" {
|
if u.Passwd != "" {
|
||||||
if err = u.SetPassword(u.Passwd); err != nil {
|
if err = u.SetPassword(u.Passwd); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -5,10 +5,8 @@
|
||||||
package generate
|
package generate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"forgejo.org/modules/util"
|
"forgejo.org/modules/util"
|
||||||
|
@ -18,18 +16,11 @@ import (
|
||||||
|
|
||||||
// NewInternalToken generate a new value intended to be used by INTERNAL_TOKEN.
|
// NewInternalToken generate a new value intended to be used by INTERNAL_TOKEN.
|
||||||
func NewInternalToken() (string, error) {
|
func NewInternalToken() (string, error) {
|
||||||
secretBytes := make([]byte, 32)
|
secretKey := base64.RawURLEncoding.EncodeToString(util.CryptoRandomBytes(32))
|
||||||
_, err := io.ReadFull(rand.Reader, secretBytes)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
secretKey := base64.RawURLEncoding.EncodeToString(secretBytes)
|
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
var internalToken string
|
internalToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||||
internalToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
||||||
"nbf": now.Unix(),
|
"nbf": now.Unix(),
|
||||||
}).SignedString([]byte(secretKey))
|
}).SignedString([]byte(secretKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,14 +45,9 @@ func DecodeJwtSecret(src string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewJwtSecret generates a new base64 encoded value intended to be used for JWT secrets.
|
// NewJwtSecret generates a new base64 encoded value intended to be used for JWT secrets.
|
||||||
func NewJwtSecret() ([]byte, string, error) {
|
func NewJwtSecret() ([]byte, string) {
|
||||||
bytes := make([]byte, 32)
|
bytes := util.CryptoRandomBytes(32)
|
||||||
_, err := rand.Read(bytes)
|
return bytes, base64.RawURLEncoding.EncodeToString(bytes)
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes, base64.RawURLEncoding.EncodeToString(bytes), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSecretKey generate a new value intended to be used by SECRET_KEY.
|
// NewSecretKey generate a new value intended to be used by SECRET_KEY.
|
||||||
|
|
|
@ -26,8 +26,7 @@ func TestDecodeJwtSecret(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewJwtSecret(t *testing.T) {
|
func TestNewJwtSecret(t *testing.T) {
|
||||||
secret, encoded, err := NewJwtSecret()
|
secret, encoded := NewJwtSecret()
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Len(t, secret, 32)
|
assert.Len(t, secret, 32)
|
||||||
decoded, err := DecodeJwtSecret(encoded)
|
decoded, err := DecodeJwtSecret(encoded)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -17,10 +17,11 @@ package keying
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hkdf"
|
"crypto/hkdf"
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
|
"forgejo.org/modules/util"
|
||||||
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -95,10 +96,7 @@ func (k *Key) Encrypt(plaintext, additionalData []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a random nonce.
|
// Generate a random nonce.
|
||||||
nonce := make([]byte, aeadNonceSize)
|
nonce := util.CryptoRandomBytes(aeadNonceSize)
|
||||||
if n, err := rand.Read(nonce); err != nil || n != aeadNonceSize {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the ciphertext of this plaintext.
|
// Returns the ciphertext of this plaintext.
|
||||||
return e.Seal(nonce, nonce, plaintext, additionalData)
|
return e.Seal(nonce, nonce, plaintext, additionalData)
|
||||||
|
|
|
@ -80,10 +80,7 @@ func loadLFSFrom(rootCfg ConfigProvider) error {
|
||||||
jwtSecretBase64 := loadSecret(rootCfg.Section("server"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET")
|
jwtSecretBase64 := loadSecret(rootCfg.Section("server"), "LFS_JWT_SECRET_URI", "LFS_JWT_SECRET")
|
||||||
LFS.JWTSecretBytes, err = generate.DecodeJwtSecret(jwtSecretBase64)
|
LFS.JWTSecretBytes, err = generate.DecodeJwtSecret(jwtSecretBase64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LFS.JWTSecretBytes, jwtSecretBase64, err = generate.NewJwtSecret()
|
LFS.JWTSecretBytes, jwtSecretBase64 = generate.NewJwtSecret()
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error generating JWT Secret for custom config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save secret
|
// Save secret
|
||||||
saveCfg, err := rootCfg.PrepareSaving()
|
saveCfg, err := rootCfg.PrepareSaving()
|
||||||
|
|
|
@ -138,10 +138,7 @@ func loadOAuth2From(rootCfg ConfigProvider) {
|
||||||
if InstallLock {
|
if InstallLock {
|
||||||
jwtSecretBytes, err := generate.DecodeJwtSecret(jwtSecretBase64)
|
jwtSecretBytes, err := generate.DecodeJwtSecret(jwtSecretBase64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jwtSecretBytes, jwtSecretBase64, err = generate.NewJwtSecret()
|
jwtSecretBytes, jwtSecretBase64 = generate.NewJwtSecret()
|
||||||
if err != nil {
|
|
||||||
log.Fatal("error generating JWT secret: %v", err)
|
|
||||||
}
|
|
||||||
saveCfg, err := rootCfg.PrepareSaving()
|
saveCfg, err := rootCfg.PrepareSaving()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
|
log.Fatal("save oauth2.JWT_SECRET failed: %v", err)
|
||||||
|
@ -161,10 +158,7 @@ var generalSigningSecret atomic.Pointer[[]byte]
|
||||||
func GetGeneralTokenSigningSecret() []byte {
|
func GetGeneralTokenSigningSecret() []byte {
|
||||||
old := generalSigningSecret.Load()
|
old := generalSigningSecret.Load()
|
||||||
if old == nil || len(*old) == 0 {
|
if old == nil || len(*old) == 0 {
|
||||||
jwtSecret, _, err := generate.NewJwtSecret()
|
jwtSecret, _ := generate.NewJwtSecret()
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Unable to generate general JWT secret: %v", err)
|
|
||||||
}
|
|
||||||
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
|
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
|
||||||
return jwtSecret
|
return jwtSecret
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,10 +88,16 @@ func CryptoRandomString(length int64) (string, error) {
|
||||||
// CryptoRandomBytes generates `length` crypto bytes
|
// CryptoRandomBytes generates `length` crypto bytes
|
||||||
// This differs from CryptoRandomString, as each byte in CryptoRandomString is generated by [0,61] range
|
// 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
|
// This function generates totally random bytes, each byte is generated by [0,255] range
|
||||||
func CryptoRandomBytes(length int64) ([]byte, error) {
|
func CryptoRandomBytes(length int64) []byte {
|
||||||
|
// crypto/rand.Read is documented to never return a error.
|
||||||
|
// https://go.dev/issue/66821
|
||||||
buf := make([]byte, length)
|
buf := make([]byte, length)
|
||||||
_, err := rand.Read(buf)
|
n, err := rand.Read(buf)
|
||||||
return buf, err
|
if err != nil || n != int(length) {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToUpperASCII returns s with all ASCII letters mapped to their upper case.
|
// ToUpperASCII returns s with all ASCII letters mapped to their upper case.
|
||||||
|
|
|
@ -163,20 +163,18 @@ func Test_RandomString(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_RandomBytes(t *testing.T) {
|
func Test_RandomBytes(t *testing.T) {
|
||||||
bytes1, err := util.CryptoRandomBytes(32)
|
bytes1 := util.CryptoRandomBytes(32)
|
||||||
require.NoError(t, err)
|
bytes2 := util.CryptoRandomBytes(32)
|
||||||
|
|
||||||
bytes2, err := util.CryptoRandomBytes(32)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
|
assert.Len(t, bytes1, 32)
|
||||||
|
assert.Len(t, bytes2, 32)
|
||||||
assert.NotEqual(t, bytes1, bytes2)
|
assert.NotEqual(t, bytes1, bytes2)
|
||||||
|
|
||||||
bytes3, err := util.CryptoRandomBytes(256)
|
bytes3 := util.CryptoRandomBytes(256)
|
||||||
require.NoError(t, err)
|
bytes4 := util.CryptoRandomBytes(256)
|
||||||
|
|
||||||
bytes4, err := util.CryptoRandomBytes(256)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
|
assert.Len(t, bytes3, 256)
|
||||||
|
assert.Len(t, bytes4, 256)
|
||||||
assert.NotEqual(t, bytes3, bytes4)
|
assert.NotEqual(t, bytes3, bytes4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,5 @@
|
||||||
"error.not_found.title": "Page not found",
|
"error.not_found.title": "Page not found",
|
||||||
"alert.asset_load_failed": "Failed to load asset files from {path}. Please make sure the asset files can be accessed.",
|
"alert.asset_load_failed": "Failed to load asset files from {path}. Please make sure the asset files can be accessed.",
|
||||||
"alert.range_error": " must be a number between %[1]s and %[2]s.",
|
"alert.range_error": " must be a number between %[1]s and %[2]s.",
|
||||||
"install.invalid_lfs_path": "Unable to create the LFS root at the specified path: %[1]s",
|
"install.invalid_lfs_path": "Unable to create the LFS root at the specified path: %[1]s"
|
||||||
"install.lfs_jwt_secret_failed": "Unable to generate a LFS JWT secret: %[1]s"
|
|
||||||
}
|
}
|
|
@ -408,11 +408,7 @@ func SubmitInstall(ctx *context.Context) {
|
||||||
if form.LFSRootPath != "" {
|
if form.LFSRootPath != "" {
|
||||||
cfg.Section("server").Key("LFS_START_SERVER").SetValue("true")
|
cfg.Section("server").Key("LFS_START_SERVER").SetValue("true")
|
||||||
cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath)
|
cfg.Section("lfs").Key("PATH").SetValue(form.LFSRootPath)
|
||||||
var lfsJwtSecret string
|
_, lfsJwtSecret := generate.NewJwtSecret()
|
||||||
if _, lfsJwtSecret, err = generate.NewJwtSecret(); err != nil {
|
|
||||||
ctx.RenderWithErr(ctx.Tr("install.lfs_jwt_secret_failed", err), tplInstall, &form)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(lfsJwtSecret)
|
cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(lfsJwtSecret)
|
||||||
} else {
|
} else {
|
||||||
cfg.Section("server").Key("LFS_START_SERVER").SetValue("false")
|
cfg.Section("server").Key("LFS_START_SERVER").SetValue("false")
|
||||||
|
@ -483,11 +479,7 @@ func SubmitInstall(ctx *context.Context) {
|
||||||
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
|
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
|
||||||
// see the "loadOAuth2From" in "setting/oauth2.go"
|
// see the "loadOAuth2From" in "setting/oauth2.go"
|
||||||
if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") {
|
if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") {
|
||||||
_, jwtSecretBase64, err := generate.NewJwtSecret()
|
_, jwtSecretBase64 := generate.NewJwtSecret()
|
||||||
if err != nil {
|
|
||||||
ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64)
|
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,11 +133,7 @@ func TwoFactorScratchPost(ctx *context.Context) {
|
||||||
// Validate the passcode with the stored TOTP secret.
|
// Validate the passcode with the stored TOTP secret.
|
||||||
if twofa.VerifyScratchToken(form.Token) {
|
if twofa.VerifyScratchToken(form.Token) {
|
||||||
// Invalidate the scratch token.
|
// Invalidate the scratch token.
|
||||||
_, err = twofa.GenerateScratchToken()
|
twofa.GenerateScratchToken()
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("UserSignIn", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = auth.UpdateTwoFactor(ctx, twofa); err != nil {
|
if err = auth.UpdateTwoFactor(ctx, twofa); err != nil {
|
||||||
ctx.ServerError("UserSignIn", err)
|
ctx.ServerError("UserSignIn", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -784,11 +784,7 @@ func ActivatePost(ctx *context.Context) {
|
||||||
|
|
||||||
func handleAccountActivation(ctx *context.Context, user *user_model.User) {
|
func handleAccountActivation(ctx *context.Context, user *user_model.User) {
|
||||||
user.IsActive = true
|
user.IsActive = true
|
||||||
var err error
|
user.Rands = user_model.GetUserSalt()
|
||||||
if user.Rands, err = user_model.GetUserSalt(); err != nil {
|
|
||||||
ctx.ServerError("UpdateUser", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := user_model.UpdateUserCols(ctx, user, "is_active", "rands"); err != nil {
|
if err := user_model.UpdateUserCols(ctx, user, "is_active", "rands"); err != nil {
|
||||||
if user_model.IsErrUserNotExist(err) {
|
if user_model.IsErrUserNotExist(err) {
|
||||||
ctx.NotFound("UpdateUserCols", err)
|
ctx.NotFound("UpdateUserCols", err)
|
||||||
|
|
|
@ -242,12 +242,8 @@ func ResetPasswdPost(ctx *context.Context) {
|
||||||
|
|
||||||
if regenerateScratchToken {
|
if regenerateScratchToken {
|
||||||
// Invalidate the scratch token.
|
// Invalidate the scratch token.
|
||||||
_, err := twofa.GenerateScratchToken()
|
twofa.GenerateScratchToken()
|
||||||
if err != nil {
|
if err := auth.UpdateTwoFactor(ctx, twofa); err != nil {
|
||||||
ctx.ServerError("UserSignIn", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = auth.UpdateTwoFactor(ctx, twofa); err != nil {
|
|
||||||
ctx.ServerError("UserSignIn", err)
|
ctx.ServerError("UserSignIn", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,7 @@ func RegenerateScratchTwoFactor(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := t.GenerateScratchToken()
|
token := t.GenerateScratchToken()
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("SettingsTwoFactor: Failed to GenerateScratchToken", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = auth.UpdateTwoFactor(ctx, t); err != nil {
|
if err = auth.UpdateTwoFactor(ctx, t); err != nil {
|
||||||
ctx.ServerError("SettingsTwoFactor: Failed to UpdateTwoFactor", err)
|
ctx.ServerError("SettingsTwoFactor: Failed to UpdateTwoFactor", err)
|
||||||
|
@ -220,11 +216,7 @@ func EnrollTwoFactorPost(ctx *context.Context) {
|
||||||
t = &auth.TwoFactor{
|
t = &auth.TwoFactor{
|
||||||
UID: ctx.Doer.ID,
|
UID: ctx.Doer.ID,
|
||||||
}
|
}
|
||||||
token, err := t.GenerateScratchToken()
|
token := t.GenerateScratchToken()
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("SettingsTwoFactor: Failed to generate scratch token", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we have to delete the secrets - because if we fail to insert then it's highly likely that they have already been used
|
// Now we have to delete the secrets - because if we fail to insert then it's highly likely that they have already been used
|
||||||
// If we can detect the unique constraint failure below we can move this to after the NewTwoFactor
|
// If we can detect the unique constraint failure below we can move this to after the NewTwoFactor
|
||||||
|
|
|
@ -5,7 +5,6 @@ package e2e
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
@ -23,6 +22,7 @@ import (
|
||||||
"forgejo.org/modules/json"
|
"forgejo.org/modules/json"
|
||||||
modules_session "forgejo.org/modules/session"
|
modules_session "forgejo.org/modules/session"
|
||||||
"forgejo.org/modules/setting"
|
"forgejo.org/modules/setting"
|
||||||
|
"forgejo.org/modules/util"
|
||||||
"forgejo.org/tests"
|
"forgejo.org/tests"
|
||||||
|
|
||||||
"code.forgejo.org/go-chi/session"
|
"code.forgejo.org/go-chi/session"
|
||||||
|
@ -153,11 +153,7 @@ func stateHelper(t testing.TB) func(stateFile string, user *user_model.User) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return func(stateFile string, user *user_model.User) {
|
return func(stateFile string, user *user_model.User) {
|
||||||
buf := make([]byte, opt.IDLength/2)
|
sessionID := hex.EncodeToString(util.CryptoRandomBytes(int64(opt.IDLength) / 2))
|
||||||
_, err = rand.Read(buf)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
sessionID := hex.EncodeToString(buf)
|
|
||||||
|
|
||||||
s, err := vsp.Read(sessionID)
|
s, err := vsp.Read(sessionID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -486,7 +486,7 @@ func TestPackageCleanup(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
// Upload and delete a generic package and upload a container blob
|
// Upload and delete a generic package and upload a container blob
|
||||||
data, _ := util.CryptoRandomBytes(5)
|
data := util.CryptoRandomBytes(5)
|
||||||
url := fmt.Sprintf("/api/packages/%s/generic/cleanup-test/1.1.1/file.bin", user.Name)
|
url := fmt.Sprintf("/api/packages/%s/generic/cleanup-test/1.1.1/file.bin", user.Name)
|
||||||
req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(data)).
|
req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(data)).
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
|
@ -496,7 +496,7 @@ func TestPackageCleanup(t *testing.T) {
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
MakeRequest(t, req, http.StatusNoContent)
|
MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
|
||||||
data, _ = util.CryptoRandomBytes(5)
|
data = util.CryptoRandomBytes(5)
|
||||||
url = fmt.Sprintf("/v2/%s/cleanup-test/blobs/uploads?digest=sha256:%x", user.Name, sha256.Sum256(data))
|
url = fmt.Sprintf("/v2/%s/cleanup-test/blobs/uploads?digest=sha256:%x", user.Name, sha256.Sum256(data))
|
||||||
req = NewRequestWithBody(t, "POST", url, bytes.NewReader(data)).
|
req = NewRequestWithBody(t, "POST", url, bytes.NewReader(data)).
|
||||||
AddBasicAuth(user.Name)
|
AddBasicAuth(user.Name)
|
||||||
|
|
Loading…
Add table
Reference in a new issue