134 lines
2.9 KiB
Go
134 lines
2.9 KiB
Go
package auth
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
|
|
"github.com/Cameron-Reed1/todo-web/types"
|
|
"golang.org/x/crypto/argon2"
|
|
"golang.org/x/crypto/scrypt"
|
|
)
|
|
|
|
|
|
var algorithm argon2idHasher = argon2idHasher{
|
|
hashLen: 64,
|
|
saltLen: 32,
|
|
time: 6,
|
|
memory: 24 * 1024,
|
|
threads: 1,
|
|
}
|
|
|
|
|
|
func Hash(password, salt []byte) (*HashSalt, error) {
|
|
return algorithm.Hash(password, salt)
|
|
}
|
|
|
|
func Validate(hash, salt, password []byte) bool {
|
|
return algorithm.Validate(hash, salt, password)
|
|
}
|
|
|
|
func CreateSessionFor(user_id int64) (*types.Session, error) {
|
|
buf := make([]byte, 32)
|
|
_, err := rand.Read(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.Session{ SessionId: base64.StdEncoding.EncodeToString(buf), UserId: user_id }, nil
|
|
}
|
|
|
|
|
|
func generateSalt(length uint) ([]byte, error) {
|
|
salt := make([]byte, length)
|
|
|
|
_, err := rand.Read(salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return salt, nil
|
|
}
|
|
|
|
type HashSalt struct {
|
|
Hash []byte
|
|
Salt []byte
|
|
}
|
|
|
|
type hashAlgo interface {
|
|
Hash(password, salt []byte) ([]byte, error)
|
|
Validate(hash, salt, password []byte) bool
|
|
}
|
|
|
|
type scryptHasher struct {
|
|
hashLen int
|
|
saltLen uint
|
|
cost int
|
|
blockSize int
|
|
parallelism int
|
|
}
|
|
|
|
type argon2idHasher struct {
|
|
hashLen uint32
|
|
saltLen uint
|
|
time uint32
|
|
memory uint32
|
|
threads uint8
|
|
}
|
|
|
|
func (s *scryptHasher) Hash(password, salt []byte) (*HashSalt, error) {
|
|
var err error
|
|
|
|
if salt == nil || len(salt) == 0 {
|
|
salt, err = generateSalt(s.saltLen)
|
|
if err != nil {
|
|
fmt.Println("\x1b[31mError: Failed to generate a password salt\x1b[0m")
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
hash, err := scrypt.Key(password, salt, s.cost, s.blockSize, s.parallelism, s.hashLen)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &HashSalt{Hash: hash, Salt: salt}, nil
|
|
}
|
|
|
|
func (s *scryptHasher) Validate(hash, salt, password []byte) bool {
|
|
hashed_password, err := s.Hash(password, salt)
|
|
if err != nil {
|
|
fmt.Println("\x1b[31mError: Failed to generate a password hash\x1b[0m")
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(hash, hashed_password.Hash)
|
|
}
|
|
|
|
func (a *argon2idHasher) Hash(password, salt []byte) (*HashSalt, error) {
|
|
var err error
|
|
|
|
if salt == nil || len(salt) == 0 {
|
|
salt, err = generateSalt(a.saltLen)
|
|
if err != nil {
|
|
fmt.Println("\x1b[31mError: Failed to generate a password salt\x1b[0m")
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
hash := argon2.IDKey(password, salt, a.time, a.memory, a.threads, a.hashLen)
|
|
|
|
return &HashSalt{Hash: hash, Salt: salt}, nil
|
|
}
|
|
|
|
func (s *argon2idHasher) Validate(hash, salt, password []byte) bool {
|
|
hashed_password, err := s.Hash(password, salt)
|
|
if err != nil {
|
|
fmt.Println("\x1b[31mError: Failed to generate a password hash\x1b[0m")
|
|
return false
|
|
}
|
|
|
|
return bytes.Equal(hash, hashed_password.Hash)
|
|
}
|