mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-01-05 04:56:07 +00:00
This is a rather large change. It consists of the following changes: + Direct access to the keycache has been removed from the core package. This forces all interaction with the cache to go through the Cryptor, which is required for persistence. The Cryptor needs to know when the cache has changed, and the only way to do this effectively is to make the Cryptor responsible for managing the keycache. + A new persist package has been added. This provides a Store interface, for which two implementations are provided. The first is a null persister: this is used when no persistence is configured. The second is a file-backed persistence store. + The Cryptor now persists the cache every time it changes. Additionally, a number of missing returns in a function in the core package have been added.
433 lines
9.3 KiB
Go
433 lines
9.3 KiB
Go
// keycache_test.go: tests for keycache.go
|
|
//
|
|
// Copyright (c) 2013 CloudFlare, Inc.
|
|
package keycache
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cloudflare/redoctober/passvault"
|
|
"github.com/cloudflare/redoctober/symcrypt"
|
|
)
|
|
|
|
func TestUsesFlush(t *testing.T) {
|
|
// Initialize passvault with one dummy user.
|
|
records, err := passvault.InitFrom("memory")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
// Initialize keycache and delegate the user's key to it.
|
|
cache := NewCache()
|
|
|
|
err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, nil, 2, "", "1h")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
removed := cache.Refresh()
|
|
if removed != 0 {
|
|
t.Fatalf("No active users should have been removed")
|
|
}
|
|
|
|
if len(cache.UserKeys) != 1 {
|
|
t.Fatalf("Error in number of live keys")
|
|
}
|
|
|
|
// Generate a random symmetric key, encrypt a blank block with it, and encrypt
|
|
// the key itself with the user's public key.
|
|
dummy := make([]byte, 16)
|
|
key, err := symcrypt.MakeRandom(16)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
encKey, err := symcrypt.EncryptCBC(dummy, dummy, key)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
pubEncryptedKey, err := pr.EncryptKey(key)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
key2, err := cache.DecryptKey(encKey, "user", "anybody", []string{}, pubEncryptedKey)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
if bytes.Equal(key, key2) {
|
|
t.Fatalf("cache.DecryptKey didnt decrypt the right key!")
|
|
}
|
|
|
|
// Second decryption allowed.
|
|
_, err = cache.DecryptKey(encKey, "user", "anybody else", []string{}, pubEncryptedKey)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
if len(cache.UserKeys) != 0 {
|
|
t.Fatalf("Error in number of live keys %v", cache.UserKeys)
|
|
}
|
|
}
|
|
|
|
func TestTimeFlush(t *testing.T) {
|
|
// Initialize passvault and keycache. Delegate a key for 1s, wait a
|
|
// second and then make sure that it's gone.
|
|
records, err := passvault.InitFrom("memory")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache := NewCache()
|
|
|
|
err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, nil, 10, "", "1s")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache.Refresh()
|
|
if len(cache.UserKeys) != 1 {
|
|
t.Fatalf("Error in number of live keys")
|
|
}
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
dummy := make([]byte, 16)
|
|
pubEncryptedKey, err := pr.EncryptKey(dummy)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
_, err = cache.DecryptKey(dummy, "user", "anybody", []string{}, pubEncryptedKey)
|
|
if err == nil {
|
|
t.Fatalf("Error in pruning expired key")
|
|
}
|
|
}
|
|
|
|
func TestGoodLabel(t *testing.T) {
|
|
// Initialize passvault and keycache. Delegate a key with the tag "red" and
|
|
// verify that decryption with the tag "red" is allowed.
|
|
records, err := passvault.InitFrom("memory")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache := NewCache()
|
|
|
|
err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, []string{"red"}, 1, "", "1h")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache.Refresh()
|
|
if len(cache.UserKeys) != 1 {
|
|
t.Fatalf("Error in number of live keys")
|
|
}
|
|
|
|
dummy := make([]byte, 16)
|
|
pubEncryptedKey, err := pr.EncryptKey(dummy)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
_, err = cache.DecryptKey(dummy, "user", "anybody", []string{"red"}, pubEncryptedKey)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
if len(cache.UserKeys) != 0 {
|
|
t.Fatalf("Error in number of live keys %v", cache.UserKeys)
|
|
}
|
|
}
|
|
|
|
func TestBadLabel(t *testing.T) {
|
|
// Initialize passvault and keycache. Delegate a key with the tag "red" and
|
|
// verify that decryption with the tag "blue" is disallowed.
|
|
records, err := passvault.InitFrom("memory")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache := NewCache()
|
|
|
|
err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, []string{"red"}, 1, "", "1h")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache.Refresh()
|
|
if len(cache.UserKeys) != 1 {
|
|
t.Fatalf("Error in number of live keys")
|
|
}
|
|
|
|
dummy := make([]byte, 16)
|
|
pubEncryptedKey, err := pr.EncryptKey(dummy)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
_, err = cache.DecryptKey(dummy, "user", "anybody", []string{"blue"}, pubEncryptedKey)
|
|
if err == nil {
|
|
t.Fatalf("Decryption of labeled key allowed without permission.")
|
|
}
|
|
|
|
cache.Refresh()
|
|
if len(cache.UserKeys) != 1 {
|
|
t.Fatalf("Error in number of live keys %v", cache.UserKeys)
|
|
}
|
|
}
|
|
|
|
func TestGoodUser(t *testing.T) {
|
|
// Initialize passvault and keycache. Delegate a key with tag and user
|
|
// restrictions and verify that permissible decryption is allowed.
|
|
records, err := passvault.InitFrom("memory")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache := NewCache()
|
|
|
|
err = cache.AddKeyFromRecord(
|
|
pr, "user", "weakpassword",
|
|
[]string{"ci", "buildeng", "user"},
|
|
[]string{"red", "blue"},
|
|
1, "", "1h",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache.Refresh()
|
|
if len(cache.UserKeys) != 1 {
|
|
t.Fatalf("Error in number of live keys")
|
|
}
|
|
|
|
dummy := make([]byte, 16)
|
|
pubEncryptedKey, err := pr.EncryptKey(dummy)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
_, err = cache.DecryptKey(dummy, "user", "ci", []string{"red"}, pubEncryptedKey)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache.Refresh()
|
|
if len(cache.UserKeys) != 0 {
|
|
t.Fatalf("Error in number of live keys %v", cache.UserKeys)
|
|
}
|
|
}
|
|
|
|
func TestBadUser(t *testing.T) {
|
|
// Initialize passvault and keycache. Delegate a key with tag and user
|
|
// restrictions and verify that illegal decryption is disallowed.
|
|
records, err := passvault.InitFrom("memory")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache := NewCache()
|
|
|
|
err = cache.AddKeyFromRecord(
|
|
pr, "user", "weakpassword",
|
|
[]string{"ci", "buildeng", "user"},
|
|
[]string{"red", "blue"},
|
|
1, "", "1h",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache.Refresh()
|
|
if len(cache.UserKeys) != 1 {
|
|
t.Fatalf("Error in number of live keys")
|
|
}
|
|
|
|
dummy := make([]byte, 16)
|
|
pubEncryptedKey, err := pr.EncryptKey(dummy)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
_, err = cache.DecryptKey(dummy, "user", "anybody", []string{"blue"}, pubEncryptedKey)
|
|
if err == nil {
|
|
t.Fatalf("Decryption of labeled key allowed without permission.")
|
|
}
|
|
|
|
cache.Refresh()
|
|
if len(cache.UserKeys) != 1 {
|
|
t.Fatalf("Error in number of live keys %v", cache.UserKeys)
|
|
}
|
|
}
|
|
|
|
func TestRefresh(t *testing.T) {
|
|
records, err := passvault.InitFrom("memory")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
pr, err := records.AddNewRecord("user", "weakpassword", true, passvault.DefaultRecordType)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache := NewCache()
|
|
|
|
err = cache.AddKeyFromRecord(
|
|
pr, "user", "weakpassword",
|
|
[]string{"ci", "buildeng", "user"},
|
|
[]string{"red", "blue"},
|
|
1, "", "1s",
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
removed := cache.Refresh()
|
|
if removed != 0 {
|
|
t.Fatalf("Refresh should not have removed any active users.")
|
|
}
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
removed = cache.Refresh()
|
|
if removed != 1 {
|
|
t.Fatalf("Refresh should have removed an active user, removed %d", removed)
|
|
}
|
|
|
|
if len(cache.GetSummary()) != 0 {
|
|
t.Fatalf("There should be no active users in the cache, but there are %d", len(cache.GetSummary()))
|
|
}
|
|
}
|
|
|
|
func cmpEntry(c, d ActiveUser) bool {
|
|
if c.Uses != d.Uses {
|
|
return false
|
|
}
|
|
|
|
if len(c.Labels) != len(d.Labels) {
|
|
return false
|
|
}
|
|
|
|
for i := range c.Labels {
|
|
if c.Labels[i] != d.Labels[i] {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if len(c.Users) != len(d.Users) {
|
|
return false
|
|
}
|
|
|
|
for i := range c.Users {
|
|
if c.Users[i] != d.Users[i] {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if c.Expiry != d.Expiry {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func cmpCache(a, b Cache) bool {
|
|
if len(a.UserKeys) != len(b.UserKeys) {
|
|
return false
|
|
}
|
|
|
|
for aIndex, aUser := range a.UserKeys {
|
|
bUser, ok := b.UserKeys[aIndex]
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
if !cmpEntry(aUser, bUser) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func TestNewFrom(t *testing.T) {
|
|
records, err := passvault.InitFrom("memory")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
cache := NewCache()
|
|
|
|
users := []string{"alice", "bob", "carol"}
|
|
for _, user := range users {
|
|
pr, err := records.AddNewRecord(user, "weakpassword", true, passvault.DefaultRecordType)
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
|
|
err = cache.AddKeyFromRecord(
|
|
pr, user, "weakpassword",
|
|
[]string{"ci", "buildeng", "user"},
|
|
[]string{"red", "blue"},
|
|
1, "", "1h")
|
|
if err != nil {
|
|
t.Fatalf("%v", err)
|
|
}
|
|
}
|
|
|
|
pr, ok := records.GetRecord("alice")
|
|
if !ok {
|
|
t.Fatal("Couldn't retrieve 'alice' record.")
|
|
}
|
|
|
|
err = cache.AddKeyFromRecord(pr, "alice", "weakpassword",
|
|
[]string{"ci", "alice"}, []string{"blue", "yellow"},
|
|
2, "slotname", "1h")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
summary := cache.GetSummary()
|
|
cache2 := NewFrom(summary)
|
|
if !cmpCache(cache, *cache2) {
|
|
t.Fatal("caches don't match")
|
|
}
|
|
|
|
}
|