From 1dd0332bc18969751d06f1cdbeee0ea1d331ccfd Mon Sep 17 00:00:00 2001 From: bronku Date: Fri, 28 Mar 2025 21:53:04 +0100 Subject: [PATCH] Add password hashing with argon2 --- cmd/iroon/main.go | 1 + go.mod | 5 +++ go.sum | 4 +++ internal/auth/credentials.go | 14 ++++++-- internal/auth/token.go | 14 ++------ internal/crypto/crypto.go | 21 +++++++++++ internal/models/user.go | 6 ++++ internal/store/migrations/8.sql | 4 +++ internal/store/{auth.go => session.go} | 0 internal/store/store.go | 5 +++ internal/store/user.go | 48 ++++++++++++++++++++++++++ 11 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 internal/crypto/crypto.go create mode 100644 internal/models/user.go create mode 100644 internal/store/migrations/8.sql rename internal/store/{auth.go => session.go} (100%) create mode 100644 internal/store/user.go diff --git a/cmd/iroon/main.go b/cmd/iroon/main.go index b8801d4..606bf14 100644 --- a/cmd/iroon/main.go +++ b/cmd/iroon/main.go @@ -13,6 +13,7 @@ import ( func Run() { s := store.OpenStore("./foo.db") + s.AddUser("admin", "secret") defer s.Close() h := server.New(s) defer h.Close() diff --git a/go.mod b/go.mod index d3bfd04..6d58c2f 100644 --- a/go.mod +++ b/go.mod @@ -3,3 +3,8 @@ module github.com/Bronku/iroon go 1.23.5 require github.com/mattn/go-sqlite3 v1.14.24 + +require ( + golang.org/x/crypto v0.36.0 + golang.org/x/sys v0.31.0 // indirect +) diff --git a/go.sum b/go.sum index 9dcdc9b..6e5b973 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,6 @@ github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= diff --git a/internal/auth/credentials.go b/internal/auth/credentials.go index 0d23c66..e82e22f 100644 --- a/internal/auth/credentials.go +++ b/internal/auth/credentials.go @@ -1,10 +1,18 @@ package auth -import "errors" +import ( + "errors" + + "github.com/Bronku/iroon/internal/crypto" +) func (a *Authenticator) verifyCredentials(login, password string) error { - - if login == "admin" && password == "secret" { + user, ok := a.s.GetUser(login) + if !ok { + return errors.New("user with this login doesn't exist") + } + hash := crypto.PasswordHash(password, user.Salt) + if hash == user.Password { return nil } return errors.New("wrong credentials") diff --git a/internal/auth/token.go b/internal/auth/token.go index 7b3601a..3ae52f4 100644 --- a/internal/auth/token.go +++ b/internal/auth/token.go @@ -1,17 +1,15 @@ package auth import ( - "crypto/rand" - "encoding/base64" - "log" "net/http" "time" + "github.com/Bronku/iroon/internal/crypto" "github.com/Bronku/iroon/internal/models" ) func (a *Authenticator) newSession(user string) (http.Cookie, error) { - key := generateKey() + key := crypto.GenerateKey() var session models.Token var cookie http.Cookie session.User = user @@ -30,11 +28,3 @@ func (a *Authenticator) newSession(user string) (http.Cookie, error) { //cookie.Secure = true return cookie, nil } - -func generateKey() string { - key := [32]byte{} - if _, err := rand.Read(key[:]); err != nil { - log.Fatal("can't generate a vaild key", err) - } - return base64.StdEncoding.EncodeToString(key[:]) -} diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go new file mode 100644 index 0000000..aaad767 --- /dev/null +++ b/internal/crypto/crypto.go @@ -0,0 +1,21 @@ +package crypto + +import ( + "crypto/rand" + "encoding/base64" + "log" + + "golang.org/x/crypto/argon2" +) + +func PasswordHash(password, salt string) string { + return string(argon2.Key([]byte(password), []byte(salt), 3, 32*1024, 4, 32)) +} + +func GenerateKey() string { + key := [32]byte{} + if _, err := rand.Read(key[:]); err != nil { + log.Fatal("can't generate a vaild key", err) + } + return base64.StdEncoding.EncodeToString(key[:]) +} diff --git a/internal/models/user.go b/internal/models/user.go new file mode 100644 index 0000000..8841f56 --- /dev/null +++ b/internal/models/user.go @@ -0,0 +1,6 @@ +package models + +type User struct { + Password string + Salt string +} diff --git a/internal/store/migrations/8.sql b/internal/store/migrations/8.sql new file mode 100644 index 0000000..d6e9191 --- /dev/null +++ b/internal/store/migrations/8.sql @@ -0,0 +1,4 @@ +alter table user +add column salt text; + +pragma user_version = 8; diff --git a/internal/store/auth.go b/internal/store/session.go similarity index 100% rename from internal/store/auth.go rename to internal/store/session.go diff --git a/internal/store/store.go b/internal/store/store.go index 6fa37b4..c7e96ed 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -11,6 +11,7 @@ import ( type Store struct { db *sql.DB cakes []models.Cake + users map[string]models.User } func OpenStore(filename string) *Store { @@ -27,6 +28,10 @@ func OpenStore(filename string) *Store { if err != nil { log.Fatal(err) } + out.users, err = out.loadUsers() + if err != nil { + log.Fatal(err) + } return &out } diff --git a/internal/store/user.go b/internal/store/user.go new file mode 100644 index 0000000..ef4e874 --- /dev/null +++ b/internal/store/user.go @@ -0,0 +1,48 @@ +package store + +import ( + "errors" + + "github.com/Bronku/iroon/internal/crypto" + "github.com/Bronku/iroon/internal/models" +) + +func (s *Store) AddUser(login, password string) error { + _, exists := s.GetUser(login) + if exists { + return errors.New("the user already exists") + } + query := "insert into user (login, password, salt) values(?, ?, ?)" + salt := crypto.GenerateKey() + hash := crypto.PasswordHash(password, salt) + _, err := s.db.Exec(query, login, hash, salt) + if err == nil { + s.users[login] = models.User{Password: hash, Salt: salt} + } + return err +} + +func (s *Store) loadUsers() (map[string]models.User, error) { + out := make(map[string]models.User) + query := "select login, password, salt from user;" + rows, err := s.db.Query(query) + if err != nil { + return out, err + } + defer rows.Close() + for rows.Next() { + var current models.User + var login string + err = rows.Scan(&login, ¤t.Password, ¤t.Salt) + if err != nil { + continue + } + out[login] = current + } + return out, nil +} + +func (s *Store) GetUser(login string) (models.User, bool) { + value, ok := s.users[login] + return value, ok +}