Add password hashing with argon2

This commit is contained in:
bronku 2025-03-28 21:53:04 +01:00
parent 01da46b81e
commit 1dd0332bc1
11 changed files with 107 additions and 15 deletions

View file

@ -13,6 +13,7 @@ import (
func Run() { func Run() {
s := store.OpenStore("./foo.db") s := store.OpenStore("./foo.db")
s.AddUser("admin", "secret")
defer s.Close() defer s.Close()
h := server.New(s) h := server.New(s)
defer h.Close() defer h.Close()

5
go.mod
View file

@ -3,3 +3,8 @@ module github.com/Bronku/iroon
go 1.23.5 go 1.23.5
require github.com/mattn/go-sqlite3 v1.14.24 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
)

4
go.sum
View file

@ -1,2 +1,6 @@
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= 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= 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=

View file

@ -1,10 +1,18 @@
package auth package auth
import "errors" import (
"errors"
"github.com/Bronku/iroon/internal/crypto"
)
func (a *Authenticator) verifyCredentials(login, password string) error { func (a *Authenticator) verifyCredentials(login, password string) error {
user, ok := a.s.GetUser(login)
if login == "admin" && password == "secret" { 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 nil
} }
return errors.New("wrong credentials") return errors.New("wrong credentials")

View file

@ -1,17 +1,15 @@
package auth package auth
import ( import (
"crypto/rand"
"encoding/base64"
"log"
"net/http" "net/http"
"time" "time"
"github.com/Bronku/iroon/internal/crypto"
"github.com/Bronku/iroon/internal/models" "github.com/Bronku/iroon/internal/models"
) )
func (a *Authenticator) newSession(user string) (http.Cookie, error) { func (a *Authenticator) newSession(user string) (http.Cookie, error) {
key := generateKey() key := crypto.GenerateKey()
var session models.Token var session models.Token
var cookie http.Cookie var cookie http.Cookie
session.User = user session.User = user
@ -30,11 +28,3 @@ func (a *Authenticator) newSession(user string) (http.Cookie, error) {
//cookie.Secure = true //cookie.Secure = true
return cookie, nil 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[:])
}

21
internal/crypto/crypto.go Normal file
View file

@ -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[:])
}

6
internal/models/user.go Normal file
View file

@ -0,0 +1,6 @@
package models
type User struct {
Password string
Salt string
}

View file

@ -0,0 +1,4 @@
alter table user
add column salt text;
pragma user_version = 8;

View file

@ -11,6 +11,7 @@ import (
type Store struct { type Store struct {
db *sql.DB db *sql.DB
cakes []models.Cake cakes []models.Cake
users map[string]models.User
} }
func OpenStore(filename string) *Store { func OpenStore(filename string) *Store {
@ -27,6 +28,10 @@ func OpenStore(filename string) *Store {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
out.users, err = out.loadUsers()
if err != nil {
log.Fatal(err)
}
return &out return &out
} }

48
internal/store/user.go Normal file
View file

@ -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, &current.Password, &current.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
}