package seeds import ( "crypto/sha256" "crypto/subtle" "encoding/base64" "encoding/hex" "fmt" "io" ) // ClosedGate is the import gate a rollback installs (spec §5.6 step 1): a // value no token's hash can equal, so the edge refuses even verify requests. const ClosedGate = "closed" // NewVerifyToken mints an import's verify token (spec §5.1): 32 random // bytes, base64url without padding. The record and the box keep only hash. func NewVerifyToken() (token, hash string, err error) { var b [32]byte if _, err := io.ReadFull(randReader, b[:]); err != nil { return "", "", fmt.Errorf("seeds: mint a verify token: %w", err) } token = base64.RawURLEncoding.EncodeToString(b[:]) return token, HashToken(token), nil } // HashToken is the hex sha256 the record's verify_hash and the row's // import_gate hold. func HashToken(token string) string { sum := sha256.Sum256([]byte(token)) return hex.EncodeToString(sum[:]) } // TokenMatches reports whether token's hash is hash, in constant time. An // empty token, an empty hash and ClosedGate match nothing. func TokenMatches(hash, token string) bool { if token == "" || len(hash) != sha256.Size*2 { return false } return subtle.ConstantTimeCompare([]byte(HashToken(token)), []byte(hash)) == 1 }