first commit, i i have no idea what i have done
This commit is contained in:
102
server/internal/crypto/argon2id.go
Normal file
102
server/internal/crypto/argon2id.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
)
|
||||
|
||||
type Argon2Params struct {
|
||||
Memory uint32 // KiB
|
||||
Time uint32
|
||||
Threads uint8
|
||||
SaltLen uint32
|
||||
KeyLen uint32
|
||||
}
|
||||
|
||||
var DefaultArgon2 = Argon2Params{
|
||||
Memory: 7168, // 7 MiB
|
||||
Time: 5,
|
||||
Threads: 1,
|
||||
SaltLen: 16,
|
||||
KeyLen: 32,
|
||||
}
|
||||
|
||||
// Hash returns PHC string: $argon2id$v=19$m=...,t=...,p=...$<salt>$<hash>
|
||||
func Hash(password string, p Argon2Params) (string, error) {
|
||||
salt := make([]byte, p.SaltLen)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
sum := argon2.IDKey([]byte(password), salt, p.Time, p.Memory, p.Threads, p.KeyLen)
|
||||
return fmt.Sprintf("$argon2id$v=19$m=%d,t=%d,p=%d$%s$%s",
|
||||
p.Memory, p.Time, p.Threads,
|
||||
base64.RawStdEncoding.EncodeToString(salt),
|
||||
base64.RawStdEncoding.EncodeToString(sum),
|
||||
), nil
|
||||
}
|
||||
|
||||
func Verify(password, phc string) (bool, error) {
|
||||
parts := strings.Split(phc, "$")
|
||||
// Expect: ["", "argon2id", "v=19", "m=...,t=...,p=...", "<saltB64>", "<hashB64>"]
|
||||
if len(parts) != 6 || parts[1] != "argon2id" || parts[2] != "v=19" {
|
||||
return false, errors.New("invalid PHC header")
|
||||
}
|
||||
|
||||
// parse params
|
||||
var mem, iters uint64
|
||||
var threads uint64
|
||||
for _, kv := range strings.Split(parts[3], ",") {
|
||||
if strings.HasPrefix(kv, "m=") {
|
||||
v := strings.TrimPrefix(kv, "m=")
|
||||
x, err := strconv.ParseUint(v, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("mem: %w", err)
|
||||
}
|
||||
mem = x
|
||||
} else if strings.HasPrefix(kv, "t=") {
|
||||
v := strings.TrimPrefix(kv, "t=")
|
||||
x, err := strconv.ParseUint(v, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("time: %w", err)
|
||||
}
|
||||
iters = x
|
||||
} else if strings.HasPrefix(kv, "p=") {
|
||||
v := strings.TrimPrefix(kv, "p=")
|
||||
x, err := strconv.ParseUint(v, 10, 8)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("threads: %w", err)
|
||||
}
|
||||
threads = x
|
||||
}
|
||||
}
|
||||
if mem == 0 || iters == 0 || threads == 0 {
|
||||
return false, errors.New("invalid PHC params")
|
||||
}
|
||||
|
||||
salt, err := b64DecodeFlex(parts[4])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("salt decode: %w", err)
|
||||
}
|
||||
|
||||
want, err := b64DecodeFlex(parts[5])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("hash decode: %w", err)
|
||||
}
|
||||
|
||||
got := argon2.IDKey([]byte(password), salt, uint32(iters), uint32(mem), uint8(threads), uint32(len(want)))
|
||||
return subtle.ConstantTimeCompare(got, want) == 1, nil
|
||||
}
|
||||
|
||||
func b64DecodeFlex(s string) ([]byte, error) {
|
||||
if b, err := base64.RawStdEncoding.DecodeString(s); err == nil {
|
||||
return b, nil
|
||||
}
|
||||
return base64.StdEncoding.DecodeString(s) // padded fallback
|
||||
}
|
||||
30
server/internal/crypto/jwt.go
Normal file
30
server/internal/crypto/jwt.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
|
||||
type JWTManager struct {
|
||||
secret []byte
|
||||
}
|
||||
|
||||
func NewJWT(secret []byte) *JWTManager {
|
||||
return &JWTManager{secret: secret}
|
||||
}
|
||||
|
||||
func (j *JWTManager) Generate(userID uint, username, role string) (string, error) {
|
||||
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"sub": userID,
|
||||
"name": username,
|
||||
"role": role,
|
||||
"iat": time.Now().Unix(),
|
||||
"exp": time.Now().Add(24 * time.Hour).Unix(),
|
||||
})
|
||||
return t.SignedString(j.secret)
|
||||
}
|
||||
|
||||
func (j *JWTManager) Parse(tok string) (*jwt.Token, error) {
|
||||
return jwt.Parse(tok, func(t *jwt.Token) (interface{}, error) { return j.secret, nil })
|
||||
}
|
||||
Reference in New Issue
Block a user