package api import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "time" ) // SignStreamToken returns the HMAC-SHA256 hex of "|" keyed // by secret. The token is constant-time-comparable via VerifyStreamToken // so it's safe to expose the verification function in handlers. // // trackID is the canonical track UUID. exp is the Unix time after which // the token is invalid; the handler checks exp before serving. // // The secret comes from MINSTREL_STREAM_SECRET (or its auto-generated + // persisted fallback in app_preferences). The loader lands in slice 2 // (POST /api/cast/stream-token); this file is the primitive both the // loader and the stream handler share. func SignStreamToken(secret []byte, trackID string, exp int64) string { mac := hmac.New(sha256.New, secret) fmt.Fprintf(mac, "%s|%d", trackID, exp) return hex.EncodeToString(mac.Sum(nil)) } // VerifyStreamToken returns true iff token is a valid HMAC for // (trackID, exp) AND exp has not yet passed. Wall-clock comparison — // callers must trust the server's clock. Uses hmac.Equal for // constant-time compare so a timing oracle can't bias token guessing. func VerifyStreamToken(secret []byte, trackID string, exp int64, token string) bool { if time.Now().Unix() > exp { return false } expected := SignStreamToken(secret, trackID, exp) return hmac.Equal([]byte(expected), []byte(token)) }