Share-токен приватного видео считался в legacy/keygen.a только от video id, без ключа: seed=(seed*17+42)%62, out[i]=alpha[seed]. После первой итерации состояние схлопывалось в id%62, то есть на весь сервис приходилось 62 различных токена. Разбирать бинарь не требовалось: 62 своих приватных видео дают таблицу токенов ко всем чужим. Форж токена => POST /access => строка в таблице access => haveAccess() отдаёт и description, и субтитры, и .webm совершенно легально, мимо патча /vtt/. Теперь HMAC-SHA256(secret, vid), те же сигнатуры и тот же 30-символьный base62, cgo и keygen.a из сборки выпали (образ собирается под любую архитектуру). Сверка constant-time через hmac.Equal. Совместимость со старыми токенами намеренно не сохранена: раунды ещё не начинались, приватных видео нет. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
35 lines
989 B
Go
35 lines
989 B
Go
package keygen
|
|
|
|
import "testing"
|
|
|
|
func TestTokenProperties(t *testing.T) {
|
|
seen := map[string]int{}
|
|
for vid := 1; vid <= 5000; vid++ {
|
|
tok := GenerateKey(vid)
|
|
if len(tok) != 30 {
|
|
t.Fatalf("vid %d: len %d, want 30", vid, len(tok))
|
|
}
|
|
for _, ch := range tok {
|
|
if !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
|
|
t.Fatalf("vid %d: bad char %q", vid, ch)
|
|
}
|
|
}
|
|
if prev, dup := seen[tok]; dup {
|
|
t.Fatalf("COLLISION: vid %d and vid %d share a token", prev, vid)
|
|
}
|
|
seen[tok] = vid
|
|
if !ValidateKey(tok, vid) {
|
|
t.Fatalf("vid %d: own token rejected", vid)
|
|
}
|
|
if ValidateKey(tok, vid+1) || ValidateKey(tok, vid+62) {
|
|
t.Fatalf("vid %d: token accepted for another video", vid)
|
|
}
|
|
}
|
|
t.Logf("5000 ids -> %d distinct tokens (legacy gave 62)", len(seen))
|
|
for _, bad := range []string{"", "aaaa", GenerateKey(1)[:29], GenerateKey(1) + "x"} {
|
|
if ValidateKey(bad, 1) {
|
|
t.Fatalf("garbage token %q accepted", bad)
|
|
}
|
|
}
|
|
}
|