// Package apikey handles generation and hashing of public-API keys. // // A key looks like "og_live_". Only the SHA-256 hash is ever persisted; // the plaintext is returned once at creation time and cannot be recovered. package apikey import ( "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "strings" ) // Prefix is the human-readable scheme prefix every key carries. const Prefix = "og_live_" // prefixLen is how many leading characters (including Prefix) are stored in // api_key.key_prefix for identifying a key without revealing its secret. const prefixLen = 12 // Generate returns a new random key (plaintext), its SHA-256 hash, and a short // display prefix. The plaintext must be shown to the caller exactly once. func Generate() (key, hash, displayPrefix string, err error) { buf := make([]byte, 24) if _, err = rand.Read(buf); err != nil { return "", "", "", err } // URL-safe, no padding => stable, copy-pasteable token body. body := base64.RawURLEncoding.EncodeToString(buf) key = Prefix + body hash = Hash(key) displayPrefix = key if len(displayPrefix) > prefixLen { displayPrefix = displayPrefix[:prefixLen] } return key, hash, displayPrefix, nil } // Hash returns the hex-encoded SHA-256 of a key, used for storage and lookup. func Hash(key string) string { sum := sha256.Sum256([]byte(strings.TrimSpace(key))) return hex.EncodeToString(sum[:]) } // Looks like a key issued by this service (cheap pre-check before hashing). func IsWellFormed(key string) bool { return strings.HasPrefix(key, Prefix) && len(key) > len(Prefix)+8 }