Files
orbits/internal/shared/security/hash.go
T
2026-04-29 23:38:43 +02:00

73 lines
1.5 KiB
Go

package security
import (
"crypto/rand"
"crypto/sha512"
"crypto/subtle"
"encoding/base64"
"fmt"
"io"
"strings"
"golang.org/x/crypto/argon2"
)
const (
argonTime = 1
argonMemory = 32 * 1024
argonThreads = 2
argonSaltLen = 16
argonKeyLen = 32
)
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
}
// we use argon2 for key hashing - since it won the key encryption "war"
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(storedKey, candidate string) bool {
parts := strings.Split(storedKey, ":")
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(candidate), salt, argonTime, argonMemory, argonThreads, argonKeyLen)
return subtle.ConstantTimeCompare(actual, expected) == 1
}