113 lines
3.2 KiB
Go
113 lines
3.2 KiB
Go
package util
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"crypto/subtle"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// SHA256Hex returns the hex-encoded SHA-256 hash of a string.
|
|
func SHA256Hex(input string) string {
|
|
h := sha256.Sum256([]byte(input))
|
|
return hex.EncodeToString(h[:])
|
|
}
|
|
|
|
// IsTokenValid checks a plaintext input against a plaintext secret.
|
|
// Per review-resolution #24: SHA-256 both sides, then constant-time compare.
|
|
func IsTokenValid(input, secret string) bool {
|
|
if secret == "" || input == "" {
|
|
return false
|
|
}
|
|
inputHash := SHA256Hex(input)
|
|
secretHash := SHA256Hex(secret)
|
|
return subtle.ConstantTimeCompare([]byte(inputHash), []byte(secretHash)) == 1
|
|
}
|
|
|
|
// IsGrantTokenValid checks a plaintext input against a stored SHA-256 hash.
|
|
// Per review-resolution #24: hash the input, then constant-time compare with stored hash.
|
|
func IsGrantTokenValid(input, storedHash string) bool {
|
|
if input == "" || storedHash == "" {
|
|
return false
|
|
}
|
|
inputHash := SHA256Hex(input)
|
|
return subtle.ConstantTimeCompare([]byte(inputHash), []byte(storedHash)) == 1
|
|
}
|
|
|
|
// RandomToken generates a 24-byte random base64url token (no padding).
|
|
func RandomToken() (string, error) {
|
|
var buf [24]byte
|
|
if _, err := readRandom(buf[:]); err != nil {
|
|
return "", err
|
|
}
|
|
return base64.RawURLEncoding.EncodeToString(buf[:]), nil
|
|
}
|
|
|
|
// EncodeBase64URL encodes to standard base64 URL encoding with padding.
|
|
func EncodeBase64URL(input string) string {
|
|
return base64.URLEncoding.EncodeToString([]byte(input))
|
|
}
|
|
|
|
// EncodeBase64RawURL encodes to base64 URL encoding without padding.
|
|
func EncodeBase64RawURL(input string) string {
|
|
return base64.RawURLEncoding.EncodeToString([]byte(input))
|
|
}
|
|
|
|
// EncodeBase64Std encodes to standard base64 encoding.
|
|
func EncodeBase64Std(input string) string {
|
|
return base64.StdEncoding.EncodeToString([]byte(input))
|
|
}
|
|
|
|
// DecodeBase64Std decodes standard base64.
|
|
func DecodeBase64Std(s string) (string, error) {
|
|
b, err := base64.StdEncoding.DecodeString(s)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
// DecodeBase64URL decodes URL-safe base64 with padding.
|
|
func DecodeBase64URL(s string) (string, error) {
|
|
b, err := base64.URLEncoding.DecodeString(s)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
// DecodeBase64RawURL decodes URL-safe base64 without padding.
|
|
// Per review-resolution #15: used for SSR.
|
|
func DecodeBase64RawURL(s string) (string, error) {
|
|
b, err := base64.RawURLEncoding.DecodeString(s)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
// DecodeBase64Auto tries Std first, then RawURL — per review-resolution #15.
|
|
func DecodeBase64Auto(s string) (string, error) {
|
|
s = strings.TrimSpace(s)
|
|
// Try standard base64 first
|
|
if b, err := base64.StdEncoding.DecodeString(s); err == nil {
|
|
return string(b), nil
|
|
}
|
|
// Try raw URL encoding (no padding)
|
|
if b, err := base64.RawURLEncoding.DecodeString(s); err == nil {
|
|
return string(b), nil
|
|
}
|
|
// Try URL encoding with padding
|
|
if b, err := base64.URLEncoding.DecodeString(s); err == nil {
|
|
return string(b), nil
|
|
}
|
|
return "", fmt.Errorf("invalid base64")
|
|
}
|
|
|
|
// Base64Utf8 encodes a UTF-8 string to standard base64.
|
|
func Base64Utf8(input string) string {
|
|
return base64.StdEncoding.EncodeToString([]byte(input))
|
|
}
|