package keygen import ( "crypto/hmac" "crypto/sha256" "strconv" ) // The legacy C implementation (legacy/keygen.a, no source shipped) derived the // share token from the video id ALONE -- no key, no salt. Worse, its state // collapsed to (seed*17+42)%62 after the first round, so the token depended // only on id%62: 62 distinct tokens for the whole service. Anyone could upload // 62 private videos, copy the tokens off their own /home and unlock every // private video on every team's box. Replaced with a keyed HMAC. // // Same signatures, same 30-char base62 alphabet, so nothing else changes. // keygen.h / legacy/keygen.a are no longer part of the build (cgo is gone). const ( tokenLen = 30 alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" // Server-side secret. Rotating it invalidates every token already handed // out, so change it only between rounds. secret = "D1gkxNv7_HphLJjNuFag_stjaByXLZ1Y0l12RGppZtw" ) func GenerateKey(vid int) string { mac := hmac.New(sha256.New, []byte(secret)) mac.Write([]byte(strconv.Itoa(vid))) sum := mac.Sum(nil) out := make([]byte, tokenLen) for i := 0; i < tokenLen; i++ { out[i] = alphabet[int(sum[i%len(sum)])%len(alphabet)] } return string(out) } func ValidateKey(token string, vid int) bool { // Constant-time: never leak how much of the token was correct. if hmac.Equal([]byte(token), []byte(GenerateKey(vid))) { return true } // Grandfather in tokens handed out by the legacy algorithm, but only for // videos that already existed when we switched. See legacy.go. return legacyAccepted(token, vid) }