37 lines
992 B
Go
37 lines
992 B
Go
package captcha
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const prngMask = 0xFFFFFFFF
|
|
|
|
// prng generates a deterministic lowercase-hex string of length chars from
|
|
// seed, matching the Cap.js widget PRNG exactly: FNV-1a seeds a 32-bit state,
|
|
// then xorshift32 produces the hex output. Must stay byte-for-byte compatible
|
|
// with the widget or solutions will never verify.
|
|
func prng(seed string, length int) string {
|
|
state := fnv1a(seed)
|
|
var b strings.Builder
|
|
for b.Len() < length {
|
|
state ^= (state << 13) & prngMask
|
|
state ^= state >> 17
|
|
state ^= (state << 5) & prngMask
|
|
state &= prngMask
|
|
fmt.Fprintf(&b, "%08x", state)
|
|
}
|
|
return b.String()[:length]
|
|
}
|
|
|
|
// fnv1a returns the 32-bit FNV-1a hash of s. Iterates over runes (code points)
|
|
// to match the JS/Python reference (which use charCodeAt / ord).
|
|
func fnv1a(s string) uint32 {
|
|
var h uint32 = 2166136261
|
|
for _, ch := range s {
|
|
h ^= uint32(ch)
|
|
h = (h + (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) & prngMask
|
|
}
|
|
return h
|
|
}
|