Download this file
| 1 | package seeds |
| 2 | |
| 3 | import ( |
| 4 | "crypto/sha256" |
| 5 | "crypto/subtle" |
| 6 | "encoding/base64" |
| 7 | "encoding/hex" |
| 8 | "fmt" |
| 9 | "io" |
| 10 | ) |
| 11 | |
| 12 | // ClosedGate is the import gate a rollback installs (spec §5.6 step 1): a |
| 13 | // value no token's hash can equal, so the edge refuses even verify requests. |
| 14 | const ClosedGate = "closed" |
| 15 | |
| 16 | // NewVerifyToken mints an import's verify token (spec §5.1): 32 random |
| 17 | // bytes, base64url without padding. The record and the box keep only hash. |
| 18 | func NewVerifyToken() (token, hash string, err error) { |
| 19 | var b [32]byte |
| 20 | if _, err := io.ReadFull(randReader, b[:]); err != nil { |
| 21 | return "", "", fmt.Errorf("seeds: mint a verify token: %w", err) |
| 22 | } |
| 23 | token = base64.RawURLEncoding.EncodeToString(b[:]) |
| 24 | return token, HashToken(token), nil |
| 25 | } |
| 26 | |
| 27 | // HashToken is the hex sha256 the record's verify_hash and the row's |
| 28 | // import_gate hold. |
| 29 | func HashToken(token string) string { |
| 30 | sum := sha256.Sum256([]byte(token)) |
| 31 | return hex.EncodeToString(sum[:]) |
| 32 | } |
| 33 | |
| 34 | // TokenMatches reports whether token's hash is hash, in constant time. An |
| 35 | // empty token, an empty hash and ClosedGate match nothing. |
| 36 | func TokenMatches(hash, token string) bool { |
| 37 | if token == "" || len(hash) != sha256.Size*2 { |
| 38 | return false |
| 39 | } |
| 40 | return subtle.ConstantTimeCompare([]byte(HashToken(token)), []byte(hash)) == 1 |
| 41 | } |
| 42 | |