Add password hashing with argon2
This commit is contained in:
parent
01da46b81e
commit
1dd0332bc1
11 changed files with 107 additions and 15 deletions
|
|
@ -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
5
go.mod
|
|
@ -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
4
go.sum
|
|
@ -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=
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
21
internal/crypto/crypto.go
Normal 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
6
internal/models/user.go
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Password string
|
||||||
|
Salt string
|
||||||
|
}
|
||||||
4
internal/store/migrations/8.sql
Normal file
4
internal/store/migrations/8.sql
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
alter table user
|
||||||
|
add column salt text;
|
||||||
|
|
||||||
|
pragma user_version = 8;
|
||||||
|
|
@ -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
48
internal/store/user.go
Normal 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, ¤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
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue