Make hashing parameters configurable

This commit is contained in:
eikendev 2020-07-31 19:58:06 +02:00
parent c0ac5c3d16
commit ba0306f384
No known key found for this signature in database
GPG key ID: A1BDB1B28C8EF694
8 changed files with 86 additions and 37 deletions

View file

@ -12,7 +12,7 @@ import (
// The UserDatabase interface for encapsulating database access. // The UserDatabase interface for encapsulating database access.
type UserDatabase interface { type UserDatabase interface {
CreateUser(user *model.User) error CreateUser(user model.ExternalUserWithCredentials) (*model.User, error)
DeleteUser(user *model.User) error DeleteUser(user *model.User) error
GetUserByID(ID uint) (*model.User, error) GetUserByID(ID uint) (*model.User, error)
GetUserByName(name string) (*model.User, error) GetUserByName(name string) (*model.User, error)
@ -44,14 +44,14 @@ func (h *UserHandler) CreateUser(ctx *gin.Context) {
return return
} }
user := externalUser.IntoInternalUser() if h.userExists(externalUser.Name) {
if h.userExists(user.Name) {
ctx.AbortWithError(http.StatusBadRequest, errors.New("username already exists")) ctx.AbortWithError(http.StatusBadRequest, errors.New("username already exists"))
return return
} }
if success := successOrAbort(ctx, http.StatusInternalServerError, h.DB.CreateUser(user)); !success { user, err := h.DB.CreateUser(externalUser)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return return
} }

5
app.go
View file

@ -6,6 +6,7 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/eikendev/pushbits/authentication/credentials"
"github.com/eikendev/pushbits/configuration" "github.com/eikendev/pushbits/configuration"
"github.com/eikendev/pushbits/database" "github.com/eikendev/pushbits/database"
"github.com/eikendev/pushbits/dispatcher" "github.com/eikendev/pushbits/dispatcher"
@ -30,7 +31,9 @@ func main() {
c := configuration.Get() c := configuration.Get()
db, err := database.Create(c.Database.Dialect, c.Database.Connection) cm := credentials.CreateManager(c.Crypto)
db, err := database.Create(cm, c.Database.Dialect, c.Database.Connection)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -3,28 +3,27 @@ package credentials
import ( import (
"log" "log"
"github.com/eikendev/pushbits/configuration"
"github.com/alexedwards/argon2id" "github.com/alexedwards/argon2id"
) )
// CreatePasswordHash returns a hashed version of the given password. // Manager holds information for managing credentials.
func CreatePasswordHash(password string) []byte { type Manager struct {
hash, err := argon2id.CreateHash(password, argon2id.DefaultParams) argon2Params *argon2id.Params
if err != nil {
panic(err)
}
return []byte(hash)
} }
// ComparePassword compares a hashed password with its possible plaintext equivalent. // CreateManager instanciates a credential manager.
func ComparePassword(hash, password []byte) bool { func CreateManager(c configuration.CryptoConfig) *Manager {
match, err := argon2id.ComparePasswordAndHash(string(password), string(hash)) log.Println("Setting up credential manager.")
if err != nil { argon2Params := &argon2id.Params{
log.Fatal(err) Memory: c.Argon2.Memory,
return false Iterations: c.Argon2.Iterations,
Parallelism: c.Argon2.Parallelism,
SaltLength: c.Argon2.SaltLength,
KeyLength: c.Argon2.KeyLength,
} }
return match return &Manager{argon2Params: argon2Params}
} }

View file

@ -0,0 +1,31 @@
package credentials
import (
"log"
"github.com/alexedwards/argon2id"
)
// CreatePasswordHash returns a hashed version of the given password.
func (m *Manager) CreatePasswordHash(password string) []byte {
hash, err := argon2id.CreateHash(password, m.argon2Params)
if err != nil {
log.Fatal(err)
panic(err)
}
return []byte(hash)
}
// ComparePassword compares a hashed password with its possible plaintext equivalent.
func ComparePassword(hash, password []byte) bool {
match, err := argon2id.ComparePasswordAndHash(string(password), string(hash))
if err != nil {
log.Fatal(err)
return false
}
return match
}

View file

@ -4,6 +4,20 @@ import (
"github.com/jinzhu/configor" "github.com/jinzhu/configor"
) )
// Argon2Config holds the parameters used for creating hashes with Argon2.
type Argon2Config struct {
Memory uint32 `default:"65536"`
Iterations uint32 `default:"1"`
Parallelism uint8 `default:"2"`
SaltLength uint32 `default:"16"`
KeyLength uint32 `default:"32"`
}
// CryptoConfig holds the parameters used for creating hashes.
type CryptoConfig struct {
Argon2 Argon2Config
}
// Configuration holds values that can be configured by the user. // Configuration holds values that can be configured by the user.
type Configuration struct { type Configuration struct {
Database struct { Database struct {
@ -20,6 +34,7 @@ type Configuration struct {
Username string `required:"true"` Username string `required:"true"`
Password string `required:"true"` Password string `required:"true"`
} }
Crypto CryptoConfig
} }
func configFiles() []string { func configFiles() []string {

View file

@ -18,8 +18,9 @@ import (
// Database holds information for the database connection. // Database holds information for the database connection.
type Database struct { type Database struct {
gormdb *gorm.DB gormdb *gorm.DB
sqldb *sql.DB sqldb *sql.DB
credentialsManager *credentials.Manager
} }
func createFileDir(file string) { func createFileDir(file string) {
@ -31,7 +32,7 @@ func createFileDir(file string) {
} }
// Create instanciates a database connection. // Create instanciates a database connection.
func Create(dialect, connection string) (*Database, error) { func Create(cm *credentials.Manager, dialect, connection string) (*Database, error) {
log.Println("Setting up database connection.") log.Println("Setting up database connection.")
maxOpenConns := 5 maxOpenConns := 5
@ -68,7 +69,7 @@ func Create(dialect, connection string) (*Database, error) {
db.AutoMigrate(&model.User{}, &model.Application{}) db.AutoMigrate(&model.User{}, &model.Application{})
return &Database{gormdb: db, sqldb: sql}, nil return &Database{gormdb: db, sqldb: sql, credentialsManager: cm}, nil
} }
// Close closes the database connection. // Close closes the database connection.
@ -83,7 +84,7 @@ func (d *Database) Populate(name, password, matrixID string) error {
query := d.gormdb.Where("name = ?", name).First(&user) query := d.gormdb.Where("name = ?", name).First(&user)
if errors.Is(query.Error, gorm.ErrRecordNotFound) { if errors.Is(query.Error, gorm.ErrRecordNotFound) {
user := model.NewUser(name, password, true, matrixID) user := model.NewUser(d.credentialsManager, name, password, true, matrixID)
if err := d.gormdb.Create(&user).Error; err != nil { if err := d.gormdb.Create(&user).Error; err != nil {
return errors.New("user cannot be created") return errors.New("user cannot be created")
@ -91,7 +92,7 @@ func (d *Database) Populate(name, password, matrixID string) error {
} else { } else {
log.Printf("Admin user %s already exists.\n", name) log.Printf("Admin user %s already exists.\n", name)
user.PasswordHash = credentials.CreatePasswordHash(password) user.PasswordHash = d.credentialsManager.CreatePasswordHash(password)
user.IsAdmin = true user.IsAdmin = true
user.MatrixID = matrixID user.MatrixID = matrixID

View file

@ -9,8 +9,10 @@ import (
) )
// CreateUser creates a user. // CreateUser creates a user.
func (d *Database) CreateUser(user *model.User) error { func (d *Database) CreateUser(externalUser model.ExternalUserWithCredentials) (*model.User, error) {
return d.gormdb.Create(user).Error user := externalUser.IntoInternalUser(d.credentialsManager)
return user, d.gormdb.Create(user).Error
} }
// DeleteUser deletes a user. // DeleteUser deletes a user.

View file

@ -36,24 +36,22 @@ type ExternalUserWithCredentials struct {
} }
// NewUser creates a new user. // NewUser creates a new user.
func NewUser(name, password string, isAdmin bool, matrixID string) *User { func NewUser(cm *credentials.Manager, name, password string, isAdmin bool, matrixID string) *User {
log.Printf("Creating user %s.\n", name) log.Printf("Creating user %s.\n", name)
user := User{ return &User{
Name: name, Name: name,
PasswordHash: credentials.CreatePasswordHash(password), PasswordHash: cm.CreatePasswordHash(password),
IsAdmin: isAdmin, IsAdmin: isAdmin,
MatrixID: matrixID, MatrixID: matrixID,
} }
return &user
} }
// IntoInternalUser converts a ExternalUserWithCredentials into a User. // IntoInternalUser converts a ExternalUserWithCredentials into a User.
func (u *ExternalUserWithCredentials) IntoInternalUser() *User { func (u *ExternalUserWithCredentials) IntoInternalUser(cm *credentials.Manager) *User {
return &User{ return &User{
Name: u.Name, Name: u.Name,
PasswordHash: credentials.CreatePasswordHash(u.Password), PasswordHash: cm.CreatePasswordHash(u.Password),
IsAdmin: u.IsAdmin, IsAdmin: u.IsAdmin,
MatrixID: u.MatrixID, MatrixID: u.MatrixID,
} }