Files
orbits/internal/shared/security/hash.go
T
2026-04-28 23:04:15 +02:00

72 lines
1.4 KiB
Go

package security
import (
"crypto/rand"
"crypto/sha512"
"crypto/subtle"
"encoding/base64"
"fmt"
"io"
"strings"
"golang.org/x/crypto/argon2"
)
const (
argonTime = 3
argonMemory = 64 * 1024
argonThreads = 2
argonSaltLen = 16
argonKeyLen = 64
)
func HashFileReader(r io.Reader) (string, error) {
h := sha512.New()
if _, err := io.Copy(h, r); err != nil {
return "", err
}
// return in b64 encoding
return base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
}
func HashKey(key string) (string, error) {
salt := make([]byte, argonSaltLen)
rand.Read(salt)
hash := argon2.IDKey([]byte(key), salt, argonTime, argonMemory, argonThreads, argonKeyLen)
// encode internally (NOT in handler)
b64Salt := base64.RawStdEncoding.EncodeToString(salt)
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
// single stored string
encoded := fmt.Sprintf("v1:%s:%s", b64Salt, b64Hash)
return encoded, nil
}
func CompareKey(key, candidate string) bool {
parts := strings.Split(candidate, ":")
if len(parts) != 3 {
return false
}
//version := parts[0]
b64Salt := parts[1]
b64Hash := parts[2]
salt, err := base64.RawStdEncoding.DecodeString(b64Salt)
if err != nil {
return false
}
expected, err := base64.RawStdEncoding.DecodeString(b64Hash)
if err != nil {
return false
}
actual := argon2.IDKey([]byte(key), salt, argonTime, argonMemory, argonThreads, argonKeyLen)
return subtle.ConstantTimeCompare(actual, expected) == 1
}