Least Recently Used Key Store
BSD-3-CLAUSE License
Least Recently Used Key Store
Cache secrets (API keys, passwords) per user without storing them in plaintext, rotating out the least recently used.
Entries are rotated out using the Least Recently Used cache algorithm. Under the hood, we're using hashicorp/golang-lru as a thread safe fixed-size cache. This maps user -> HMAC-SHA(user secret)
. The secret key for the HMAC-SHA is initialized per LRU Key Store.
LRU Key Store was created to be a caching layer between itself and a source of truth. If a key isn't in the store, we test the putative API key provided by the user with the upstream source of truth. If that passes, we add it to the cache. If it doesn't, nothing changes. If a key is in the store, we just respond back immediately. Invalid keys should then take the same amount of time to repudiate while valid keys are instantaneous.
package main
import (
"fmt"
lruks "github.com/rgbkrk/lru-key-store"
)
func main() {
// Cache a whopping 2 keys
ks, err := lruks.New(2)
if err != nil {
panic(err)
}
password := "password"
// The key store should not have this password
in := ks.IsIn("rgbkrk", password)
fmt.Printf("'%v' -> %v\n", password, in)
// Nor another key
password = "secret"
fmt.Printf("'%v' -> %v\n", password, ks.IsIn("rgbkrk", password))
// Give the keystore a secret
ks.Add("rgbkrk", "secret")
// Invalid key
password = "password"
fmt.Printf("'%v' -> %v\n", password, ks.IsIn("rgbkrk", password))
// We have this key!
password = "secret"
fmt.Printf("'%v' -> %v\n", password, ks.IsIn("rgbkrk", password))
// Fill the cache
ks.Add("smashwilson", "myvoiceismypassport")
ks.Add("ycombinator", "stillcantbelievethatsyourgithubusername")
// rgbkrk should no longer have a key stored
password = "secret"
fmt.Printf("'%v' -> %v\n", password, ks.IsIn("rgbkrk", password))
}