From 93e7917a6fef14bf4b3f1e11346215f26833d07b Mon Sep 17 00:00:00 2001 From: Tyler J Date: Tue, 12 Apr 2016 22:21:07 -0700 Subject: [PATCH 1/5] Add delegation option for any user to be able to decrypt core: Add AnyUser field to DelegateRequest and pass to cache calls keycache: Add AnyUser parameter to AddKeyFromRecord function signature keycache_test: Add tests for AnyUser and update AddKeyFromRecord calls cryptor: Update tests to AddKeyFromRecord to reflect API update cmd/ro: Add bool flag for anyUser parameter Note: This commit was further amended to change the AddKeyFromRecord API to accept keycache.Usage as a parameter, which will make updating usage simpler in the future. --- cmd/ro/main.go | 4 ++ core/core.go | 37 ++++++++++++++--- cryptor/cryptor_test.go | 4 +- keycache/keycache.go | 30 ++++++-------- keycache/keycache_test.go | 86 +++++++++++++++++++++++++++++++++------ 5 files changed, 125 insertions(+), 36 deletions(-) diff --git a/cmd/ro/main.go b/cmd/ro/main.go index a9055ab..6d6906e 100644 --- a/cmd/ro/main.go +++ b/cmd/ro/main.go @@ -23,6 +23,8 @@ var owners, lefters, righters, inPath, labels, outPath, outEnv string var uses, minUsers int +var anyUser bool + var duration, users string var pollInterval time.Duration @@ -52,6 +54,7 @@ func registerFlags() { flag.StringVar(&users, "users", "", "comma separated user list") flag.IntVar(&uses, "uses", 0, "number of delegated key uses") flag.IntVar(&minUsers, "minUsers", 2, "minimum number of delegations") + flag.BoolVar(&anyUser, "anyUser", false, "whether any user can decrypt") flag.StringVar(&duration, "time", "0h", "duration of delegated key uses") flag.StringVar(&lefters, "left", "", "comma separated left owners") flag.StringVar(&righters, "right", "", "comma separated right owners") @@ -110,6 +113,7 @@ func runDelegate() { Time: duration, Users: processCSL(users), Labels: processCSL(labels), + AnyUser: anyUser, } resp, err := roServer.Delegate(req) processError(err) diff --git a/core/core.go b/core/core.go index 855475a..7361c1e 100644 --- a/core/core.go +++ b/core/core.go @@ -50,11 +50,12 @@ type DelegateRequest struct { Name string Password string - Uses int - Time string - Slot string - Users []string - Labels []string + Uses int + Time string + Slot string + Users []string + Labels []string + AnyUser bool } type CreateUserRequest struct { @@ -216,6 +217,26 @@ func validateName(name, password string) error { return nil } +// createCacheUsageFromDelegateRequest converts a DelegateRequest to a cache.Usage object. +func createCacheUsageFromDelegateRequest(s *DelegateRequest) (*keycache.Usage, error) { + var u keycache.Usage + u.Uses = s.Uses + u.AnyUser = s.AnyUser + // copy splices + u.Labels = make([]string, len(s.Labels)) + u.Users = make([]string, len(s.Users)) + copy(u.Labels, s.Labels) + copy(u.Users, s.Users) + // compute exipiration + duration, err := time.ParseDuration(s.Time) + if err != nil { + return &u, err + } + u.Expiry = time.Now().Add(duration) + fmt.Printf("%#v\n", u) + return &u, nil +} + // Init reads the records from disk from a given path func Init(path, hcKey, hcRoom, hcHost, roHost string) error { var err error @@ -396,7 +417,11 @@ func Delegate(jsonIn []byte) ([]byte, error) { } // add signed-in record to active set - if err = cache.AddKeyFromRecord(pr, s.Name, s.Password, s.Users, s.Labels, s.Uses, s.Slot, s.Time); err != nil { + u, err := createCacheUsageFromDelegateRequest(&s) + if err != nil { + return jsonStatusError(err) + } + if err = cache.AddKeyFromRecord(pr, s.Name, s.Password, s.Slot, u); err != nil { return jsonStatusError(err) } diff --git a/cryptor/cryptor_test.go b/cryptor/cryptor_test.go index fa4727c..5f34eb0 100644 --- a/cryptor/cryptor_test.go +++ b/cryptor/cryptor_test.go @@ -9,6 +9,7 @@ import ( "encoding/base64" "encoding/json" "testing" + "time" "github.com/cloudflare/redoctober/keycache" "github.com/cloudflare/redoctober/passvault" @@ -107,7 +108,8 @@ func TestDuplicates(t *testing.T) { // Delegate one key at a time and check that decryption fails. for name, pr := range recs { - err = cache.AddKeyFromRecord(pr, name, "weakpassword", nil, nil, 2, "", "1h") + duration, _ := time.ParseDuration("1h") + err = cache.AddKeyFromRecord(pr, name, "weakpassword", "", &keycache.Usage{2, nil, nil, time.Now().Add(duration), true}) if err != nil { t.Fatalf("%v", err) } diff --git a/keycache/keycache.go b/keycache/keycache.go index 77d2218..cf0a9c8 100644 --- a/keycache/keycache.go +++ b/keycache/keycache.go @@ -31,10 +31,11 @@ type DelegateIndex struct { // Usage holds the permissions of a delegated permission type Usage struct { - Uses int // Number of uses delegated - Labels []string // File labels allowed to decrypt - Users []string // Set of users allows to decrypt - Expiry time.Time // Expiration of usage + Uses int // Number of uses delegated + Labels []string // File labels allowed to decrypt + Users []string // Set of users allows to decrypt + Expiry time.Time // Expiration of usage + AnyUser bool // True if any user is permitted, false otherwise } // ActiveUser holds the information about an actively delegated key. @@ -72,15 +73,19 @@ func (usage Usage) matchesLabel(labels []string) bool { } // matches returns true if this usage applies the user and label -// an empty array of Users indicates that all users are valid +// also returns true if the usage is marked with AnyUser set to true +// DEPRECATED: an empty array of Users indicates that all users are valid func (usage Usage) matches(user string, labels []string) bool { if !usage.matchesLabel(labels) { return false } - // if usage lists no users, always match + // DEPRECATED: if usage lists no users, always match if len(usage.Users) == 0 { return true } + if usage.AnyUser { + return true + } for _, validUser := range usage.Users { if user == validUser { return true @@ -173,20 +178,11 @@ func (cache *Cache) Refresh() { } // AddKeyFromRecord decrypts a key for a given record and adds it to the cache. -func (cache *Cache) AddKeyFromRecord(record passvault.PasswordRecord, name, password string, users, labels []string, uses int, slot, durationString string) (err error) { +func (cache *Cache) AddKeyFromRecord(record passvault.PasswordRecord, name, password, slot string, usage *Usage) (err error) { var current ActiveUser cache.Refresh() - - // compute exipiration - duration, err := time.ParseDuration(durationString) - if err != nil { - return - } - current.Usage.Uses = uses - current.Usage.Expiry = time.Now().Add(duration) - current.Usage.Users = users - current.Usage.Labels = labels + current.Usage = *usage // get decryption keys switch record.Type { diff --git a/keycache/keycache_test.go b/keycache/keycache_test.go index d4049aa..caddb07 100644 --- a/keycache/keycache_test.go +++ b/keycache/keycache_test.go @@ -27,7 +27,8 @@ func TestUsesFlush(t *testing.T) { // Initialize keycache and delegate the user's key to it. cache := NewCache() - err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, nil, 2, "", "1h") + duration, _ := time.ParseDuration("1h") + err = cache.AddKeyFromRecord(pr, "user", "weakpassword", "", &Usage{2, nil, []string{"alice"}, time.Now().Add(duration), false}) if err != nil { t.Fatalf("%v", err) } @@ -90,7 +91,8 @@ func TestTimeFlush(t *testing.T) { cache := NewCache() - err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, nil, 10, "", "1s") + duration, _ := time.ParseDuration("1s") + err = cache.AddKeyFromRecord(pr, "user", "weakpassword", "", &Usage{10, nil, []string{"alice"}, time.Now().Add(duration), false}) if err != nil { t.Fatalf("%v", err) } @@ -129,7 +131,8 @@ func TestGoodLabel(t *testing.T) { cache := NewCache() - err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, []string{"red"}, 1, "", "1h") + duration, _ := time.ParseDuration("1h") + err = cache.AddKeyFromRecord(pr, "user", "weakpassword", "", &Usage{1, []string{"red"}, []string{"alice"}, time.Now().Add(duration), false}) if err != nil { t.Fatalf("%v", err) } @@ -171,7 +174,8 @@ func TestBadLabel(t *testing.T) { cache := NewCache() - err = cache.AddKeyFromRecord(pr, "user", "weakpassword", nil, []string{"red"}, 1, "", "1h") + duration, _ := time.ParseDuration("1h") + err = cache.AddKeyFromRecord(pr, "user", "weakpassword", "", &Usage{1, []string{"red"}, nil, time.Now().Add(duration), true}) if err != nil { t.Fatalf("%v", err) } @@ -213,11 +217,14 @@ func TestGoodUser(t *testing.T) { cache := NewCache() + duration, _ := time.ParseDuration("1h") err = cache.AddKeyFromRecord( - pr, "user", "weakpassword", - []string{"ci", "buildeng", "user"}, - []string{"red", "blue"}, - 1, "", "1h", + pr, "user", "weakpassword", "", + &Usage{ + 1, []string{"red", "blue"}, + []string{"ci", "buildeng", "user"}, + time.Now().Add(duration), false, + }, ) if err != nil { t.Fatalf("%v", err) @@ -260,12 +267,16 @@ func TestBadUser(t *testing.T) { cache := NewCache() + duration, _ := time.ParseDuration("1h") err = cache.AddKeyFromRecord( - pr, "user", "weakpassword", - []string{"ci", "buildeng", "user"}, - []string{"red", "blue"}, - 1, "", "1h", + pr, "user", "weakpassword", "", + &Usage{ + 1, []string{"red", "blue"}, + []string{"ci", "buildeng", "user"}, + time.Now().Add(duration), false, + }, ) + if err != nil { t.Fatalf("%v", err) } @@ -291,3 +302,54 @@ func TestBadUser(t *testing.T) { t.Fatalf("Error in number of live keys %v", cache.UserKeys) } } + +func TestAnyUser(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() + + duration, _ := time.ParseDuration("1h") + err = cache.AddKeyFromRecord( + pr, "user", "weakpassword", "", + &Usage{ + 1, []string{"red", "blue"}, + nil, + time.Now().Add(duration), true, + }, + ) + + 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) + } + + cache.Refresh() + if len(cache.UserKeys) != 0 { + t.Fatalf("Error in number of live keys %v", cache.UserKeys) + } +} From fa16fe5c66276376819f07e68d9325d3caefc678 Mon Sep 17 00:00:00 2001 From: Tyler J Date: Tue, 12 Apr 2016 23:12:11 -0700 Subject: [PATCH 2/5] Remove backwards-compatibility for delegating without a list of users keycache: Remove deprecated behavior in Usage.matches() core: Add check during delegation for a User list or the AnyUser flag tests: Add parameters to conform to new delegation requirements --- core/core.go | 8 +++++++- core/core_test.go | 36 ++++++++++++++++++------------------ keycache/keycache.go | 5 ----- keycache/keycache_test.go | 8 ++++---- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/core/core.go b/core/core.go index 7361c1e..9d40270 100644 --- a/core/core.go +++ b/core/core.go @@ -402,9 +402,15 @@ func Delegate(jsonIn []byte) ([]byte, error) { return jsonStatusError(err) } } + + // Ensure a list of Users is given or the AnyUser flag is set + if s.Users == nil && s.AnyUser == false { + err = errors.New("Must provide a list of Users or set the AnyUser flag to true") + return jsonStatusError(err) + } + // Find password record for user and verify that their password // matches. If not found then add a new entry for this user. - pr, found := records.GetRecord(s.Name) if found { if err = pr.ValidatePassword(s.Password); err != nil { diff --git a/core/core_test.go b/core/core_test.go index 6b00f05..e6a426b 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -50,7 +50,7 @@ func TestCreate(t *testing.T) { func TestSummary(t *testing.T) { createJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}") - delegateJson := []byte("{\"Name\":\"Bob\",\"Password\":\"Rob\",\"Time\":\"2h\",\"Uses\":1}") + delegateJson := []byte("{\"Name\":\"Bob\",\"Password\":\"Rob\",\"Time\":\"2h\",\"Uses\":1,\"AnyUser\":true}") // check for summary of uninitialized vault respJson, err := Summary(createJson) @@ -287,9 +287,9 @@ func TestCreateUser(t *testing.T) { func TestPassword(t *testing.T) { createJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}") - delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"2h\",\"Uses\":1}") + delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"2h\",\"Uses\":1,\"AnyUser\":true}") passwordJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"NewPassword\":\"Olleh\"}") - delegateJson2 := []byte("{\"Name\":\"Alice\",\"Password\":\"Olleh\",\"Time\":\"2h\",\"Uses\":1}") + delegateJson2 := []byte("{\"Name\":\"Alice\",\"Password\":\"Olleh\",\"Time\":\"2h\",\"Uses\":1,\"AnyUser\":true}") passwordJson2 := []byte("{\"Name\":\"Alice\",\"Password\":\"Olleh\",\"NewPassword\":\"Hello\"}") Init("memory", "", "", "", "") @@ -395,12 +395,12 @@ func TestPassword(t *testing.T) { func TestEncryptDecrypt(t *testing.T) { summaryJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}") - delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") - delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") - delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") + delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") + delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") + delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") delegateJson4 := []byte("{\"Name\":\"Bob\",\"Password\":\"Hello\",\"Time\":\"10s\",\"Uses\":2,\"Users\":[\"Alice\"],\"Labels\":[\"blue\"]}") delegateJson5 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"10s\",\"Uses\":2,\"Users\":[\"Alice\"],\"Labels\":[\"blue\"]}") - delegateJson6 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"10s\",\"Uses\":1}") + delegateJson6 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"10s\",\"Uses\":1,\"AnyUser\":true}") encryptJson := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Minimum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}") encryptJson2 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Minimum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\",\"Labels\":[\"blue\",\"red\"]}") encryptJson3 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Minimum\":1,\"Owners\":[\"Alice\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}") @@ -623,9 +623,9 @@ func TestEncryptDecrypt(t *testing.T) { } func TestReEncrypt(t *testing.T) { - delegateJson := []byte(`{"Name":"Alice","Password":"Hello","Time":"0s","Uses":0}`) - delegateJson2 := []byte(`{"Name":"Bob","Password":"Hello","Time":"0s","Uses":0}`) - delegateJson3 := []byte(`{"Name":"Carol","Password":"Hello","Time":"0s","Uses":0}`) + delegateJson := []byte(`{"Name":"Alice","Password":"Hello","Time":"0s","Uses":0,"AnyUser":true}`) + delegateJson2 := []byte(`{"Name":"Bob","Password":"Hello","Time":"0s","Uses":0,"AnyUser":true}`) + delegateJson3 := []byte(`{"Name":"Carol","Password":"Hello","Time":"0s","Uses":0,"AnyUser":true}`) delegateJson4 := []byte(`{"Name":"Bob","Password":"Hello","Time":"10s","Uses":2,"Users":["Alice"],"Labels":["blue"]}`) delegateJson5 := []byte(`{"Name":"Carol","Password":"Hello","Time":"10s","Uses":2,"Users":["Alice"],"Labels":["blue"]}`) delegateJson6 := []byte(`{"Name":"Bob","Password":"Hello","Time":"10s","Uses":2,"Users":["Alice"],"Labels":["red"]}`) @@ -804,9 +804,9 @@ func TestReEncrypt(t *testing.T) { } func TestOwners(t *testing.T) { - delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") - delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") - delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") + delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") + delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") + delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") encryptJson := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Minimum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}") var s ResponseData @@ -859,9 +859,9 @@ func TestOwners(t *testing.T) { func TestModify(t *testing.T) { summaryJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}") summaryJson2 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\"}") - delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") - delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") - delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") + delegateJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") + delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") + delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0,\"AnyUser\":true}") modifyJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"ToModify\":\"Alice\",\"Command\":\"admin\"}") modifyJson2 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"ToModify\":\"Alice\",\"Command\":\"revoke\"}") modifyJson3 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"ToModify\":\"Carol\",\"Command\":\"admin\"}") @@ -1040,8 +1040,8 @@ func TestModify(t *testing.T) { func TestStatic(t *testing.T) { expected := []byte("this is a secret string, shhhhhhhh. don't tell anyone, ok? whatever you do, don't tell the cops\n") - delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Rob\",\"Time\":\"1m\",\"Uses\":5}") - delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"~~~222\",\"Time\":\"1h\",\"Uses\":30}") + delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Rob\",\"Time\":\"1m\",\"Uses\":5,\"AnyUser\":true}") + delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"~~~222\",\"Time\":\"1h\",\"Uses\":30,\"AnyUser\":true}") decryptJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Data\":\"eyJWZXJzaW9uIjoxLCJWYXVsdElkIjo1Mjk4NTM4OTU3NzU0NTQwODEwLCJLZXlTZXQiOlt7Ik5hbWUiOlsiQm9iIiwiQWxpY2UiXSwiS2V5IjoiMmozdEkyUEJGQmJ3RlgwQmxRZFV1QT09In0seyJOYW1lIjpbIkJvYiIsIkNhcm9sIl0sIktleSI6InlMU1NCL1U2KzVyYzFFK2dqR1hUNHc9PSJ9LHsiTmFtZSI6WyJBbGljZSIsIkJvYiJdLCJLZXkiOiJERGxXSEY3c3p6SVN1WFdhRXo4bGxRPT0ifSx7Ik5hbWUiOlsiQWxpY2UiLCJDYXJvbCJdLCJLZXkiOiJUa0ExM2FQcllGTmJ2ZUlibDBxZHd3PT0ifSx7Ik5hbWUiOlsiQ2Fyb2wiLCJCb2IiXSwiS2V5IjoiS1htMHVPYm1SSjJadlNZRVdQd2syQT09In0seyJOYW1lIjpbIkNhcm9sIiwiQWxpY2UiXSwiS2V5IjoiTDljK1BxdHhQaDl5NmFwUnZ0YkNRdz09In1dLCJLZXlTZXRSU0EiOnsiQWxpY2UiOnsiS2V5IjoiZmo1bXFucTd5NUtDYWZDR1QxSTUxeEk1SnNYNzQ2KzlUVFNzcC84eWJmM2laamhGelNsd1AzYU5tc094M1NVS1RtWmxmcytiK01lRDRlS0oydUtCRnpRSEFJUE8wZndvaUNES0hoS0g2S3NvbE5xNCtqZ3BrTEFNT0xzUUdzOGc2QmhKeTZiQ0ZSalpWYzNJZGxRQUJQTTZQa1RidVN2S2huOWF0REZ3WlFENVRKQklTaTdkMmh3NExyYWR0TElUYk5xd2lGTVRRUTkrcHNYenlhdlk4SDNMTkhLR2dmNU9kN0lwdGhFUVBDSGk0bnc3WC9ZVlJURU1mb0lWY01jS093WWpsQzQ1L1ZKRUhLOVp5M0RTaUxCemptcjU3WU5JVmp3OFlaWTVER0JXcWJndTUxUlViSWNycXlMcGhCaFhvQlJ1NFIreXJoeWdCTldidmtraWZBPT0ifSwiQm9iIjp7IktleSI6IkhUWWlaMThzZjcyMWNBTjFMUk5rSi8rTDRBS1dpbE1ya015TmlCaldjbDlIUlRWUE5YSVRxUUJYZDBmQmdnR05QaVpyNlZRVHlTSzRaRnZKS0dER2l6MTdUZS9Ub0RuOFlrL0I5Y3FNc041ZkhvUXRYdmw4SVpvMndpb0E2N2NjQUoxZ0hNTU5wUHlMZEY0M1NRcWdJK1hhUTJsTVNZTE1meHhEbUJCT1ExU1dBdG8wQkRSZG5zcXB3VXdJUEtROVkzLzFvc21yakxtSm9BQzNNUHBsZXhZV2hleE53SnRTZCttRmRWWjNRZTR4OVJzUkhjTi9teWloT3QvNjdWNjBxenMxM0YwUlprTVNEemo1RGRnKzFLVk5KWlk5ZG1vbFBOa0Faajh6MjBMOXV6cGF0clRZVFI2QThxL3NSbitpbk83WlFWUTAwWE82cTZsWVlRenhudz09In0sIkNhcm9sIjp7IktleSI6Ikl0cnZTMDJuU2ZiY0EyZmwxTDFpNjF4cVBFREtSZHNyWWUzK1VDYmtUK2lwaGVpUVJQU3Vpa2J6ZVYya3NobjR5SkRla3U1Ym1UTnFXOEhTR3RVN0dUZ0NvSVdWOFdtRWY0dzZvdnpTaFBidStWckladlJ6M3dqaDJvWUhUL2d0UFZBUW5CYS83MUZlb0JOeHk1bC9oQmNVbUJreTQzajgzTWx0Mis4UVp4NlBFVURtcGFQUWVtVmg5OStDMjBuUXRrQVVGZU1jMkdlNHk3UmxIU3h0ZkFCdndsWHgxTnpDRDQwbnlKZkYxU2pWL2ZaaC9FMkFsNFRhdng2RE9Ka1lHb0oybXA3WEJ2WDBJRjJ0cDhUM1U1VnBuVGVrL1d1TnJMTDl6Ny9qcXpXaDg3bFo1S2hlV1hoR2tVMUJOSDRsZklqNDNwRGtTeTUwYUR2UzB6WWZIUT09In19LCJJViI6IjU4cjlNejhlMDZtSXRCRzluU1YvMFE9PSIsIkRhdGEiOiJRRTlaaGNHWE5YYXVVZE1rMDRiaVVHeTFTb1A1SDJuRi9qMkpqaWlWRktQZElkUnAvR2MrQVp2VUk5bjIyWk00cSt6RGlKejdxdks0YkthUHBYaFRtR1AwWGhlYUZVdWtlVk5TOVNUTW9UYk5jWS9adFZPejZoaXpVUEY3Z1NxMzg4UVBVc1QrQXhtbDNyRVVUV09obnc9PSIsIlNpZ25hdHVyZSI6IlZWaVJvYUN4cUtBdk54eU14RGhydFlvMENaQT0ifQ==\"}") diskVault := []byte("{\"Version\":1,\"VaultId\":5298538957754540810,\"HmacKey\":\"Qugc5ZQ0vC7KQSgmDHTVgQ==\",\"Passwords\":{\"Alice\":{\"Type\":\"RSA\",\"PasswordSalt\":\"HrVbQ4JvEdORxWN6FrSy4w==\",\"HashedPassword\":\"nanadB0t/EmVuHyRUNtfyQ==\",\"KeySalt\":\"u0cwMmHikadpxm9wClrf3g==\",\"AESKey\":\"pOcxCYMk0l+kaEM5IHolfA==\",\"RSAKey\":{\"RSAExp\":\"yDBotK0XkaDk6rt35ciBnlyPGSXjp9ypdTH2j89CybXe2ReF6xLVZ10CCoz91UUFpbiQi2tVWFS8V7lxUehx7C6HI30Zr0iwJMXk8sgRKs2Ee3rAGCrM9vvQMO8ApKwe4kvB+PrFgnhIEgZI7zyPJPcdZnxbcVFyUC3uIivFS4Bv3jVBnrkAx71keQqrkKzu3jTquCIS3rTEm2hrgsZ3n1t/4BADvUpcMpII2Phv2Senonp1EMz9sRFsR4w9HVep8AgfUGuPTcQ/uv9R16xiHvlN80rOtWzVLpEruzGw/JTvlsshFBU5SY/zthGl9TwxWz1yZpVHYhIWhbxw34CXYczB6q19bdEPkJJldi/1coI=\",\"RSAExpIV\":\"RI9B8nzwW/CXiIU4lSYRWg==\",\"RSAPrimeP\":\"yB3TMdRhWLiZ/ayPlu/iLDHxWsuMi9pA8Ctps7WZFxVsxYEzy/s0Otzsbtgay8of72xVkO64MaudXXRjj26kVSQhS8WhPmv7xDO5ba3SsTffCA99aSr3MH+JmUoL+EDjYviUf5F1DSZniv+Ae+6x9AMbbRQqRvmdH/INW76rFbLX4VtsMpgVkAhADwCUSfNS\",\"RSAPrimePIV\":\"ZE8xe7zVqz4fTN1XWvD1Tg==\",\"RSAPrimeQ\":\"pq7rEAmXoiMWhuEpTy2pQiheLHuzcGeGm1IsgtP7TRIpHaumBAjasxhMY95ODspzehfHp8RPb3pz550g+EXpRP5bfmZkiPMGWKsFUWY7h51hm2Yg6t7yOSXxQvzseWDUjJqBXIG56su0ItD/7NT9YPnhOWAcLaV+L/D3dLdUOP3sgrfp2fcz5IWgcAHUKwLj\",\"RSAPrimeQIV\":\"po4GB9LNDJEFwVo7pd7M+w==\",\"RSAPublic\":{\"N\":23149224936745228096494487629641309516714939646787578848987096719230079441991920927625328021835418605829165905957281054739040782220661415211495551591590967025905842090248342119030961900558364831442589592519977574953153506098793781379522492850670706181134690851176026007414311463584686376540335844073069224992659463459793269384975134553539299820716318523937519020382533972574258510063896159690996712751336541794097684994088922002126390397554509324964165816682788604802958437629743951677011277308533675371215221012180522157452368867794857042597831942805118364469257052754518983480732149823871291876054393236400292711237,\"E\":65537}},\"Admin\":true},\"Bob\":{\"Type\":\"RSA\",\"PasswordSalt\":\"yqWZFNuuCRMw+snL83FcWQ==\",\"HashedPassword\":\"jYLSUIdvw8UVXhxVSS5uKQ==\",\"KeySalt\":\"ZJDOGSv9yUlJ5+83+FV6Nw==\",\"AESKey\":\"9bj4qQDJKglD9eIm7MNeag==\",\"RSAKey\":{\"RSAExp\":\"rnmHX1FgAN0KFm89Uj+O4L7/njDlQPwGHSYjGKB7qMyNPGF4jKn9ez1LI9e9jI8uE475KBhoXRmIuHdap6HtD+sH+nHugDzD3np6Dbq1MM+19PW41n9xblwx6tsNwvufCYgb2qtZd0bLXemeKBszJg2UXlTeSHkXGmjY/VAbZYbUFUNKIykiJWnQ+3HlAo7UHjjKSDI6HtiMnODwYucKH9uYdmroM3DqRUP+j+AhMctyeyOt68q6RuVyubzG0PM1/T9QqPgTIzFvg9dxk+LmPYmlv4b7Euea7KQHww4kTUlpNYondRisuA436G7EfWIJFeRShFuSVk0GvmrgN5vK+/FkdqMuikPaPV1dITFzaCU=\",\"RSAExpIV\":\"eoIov2XYwK19tpFkZyTL+Q==\",\"RSAPrimeP\":\"x9VqeHfHZBMksWNz3Bq90KyLYO0+y9tbfH17uAxQHIZbhn6RUHMXJFs4/H+TnL9s7D05HdxKNCD3ilISkhLZ/DQVD+VMvSFQ/2DL/OoKNg4UyxeGpKefJxCU9LOeXGTfN+UpGHN4Myal3PL2yi0yWCVZvX+zPks05Nqmzkmtx1Yz0IqeUaR+I1jw2y0xy+nc\",\"RSAPrimePIV\":\"h+J0h35kSaNFjBFyejmqSQ==\",\"RSAPrimeQ\":\"CUGaukdC8slcy1Qm6kBw8QYhVenJWFylPqJTvf03ATkkaxQJ8ZSK/RodXFiKCztRSx/HVw0LoGGx9RiavSxl1I+NPIiGEBXiYnLCwWjIbohEIviX0XrKEegECKZtEPxlkDGI8C5ScmUiUOoCUFODqPi1ymEPPIpqj6NZu0Q3lOXseZ7vHbCwiO7Nxznoed2V\",\"RSAPrimeQIV\":\"fglAccK/JKbbP6FKd3MoPA==\",\"RSAPublic\":{\"N\":28335031342838743289078885439639803673108967200362163631216970159249785951860249148476503896024171065194110189927394386737974335124568755441420594193064949823336810514078294805084876384362160530316962043860952904024334641317905429962254720146672817625880231384279651687169091903559644110839987019710966818515746956556387614880164417442090115347521394174077031531398826388780347541493782387177778507223791525305993050791342196579264299166530031123364885677134064847664024722422373550942639559346558112737229502294633354781474882714474174085566901518771722057743396746938093859383451665388899451851738694979184054459471,\"E\":65537}},\"Admin\":false},\"Carol\":{\"Type\":\"RSA\",\"PasswordSalt\":\"kPCA68PNIi3qODVfBHonMA==\",\"HashedPassword\":\"L05ueGkNhqRI7xo7OaG5fQ==\",\"KeySalt\":\"bUXHAjmdtpcYMNN/YzHvhA==\",\"AESKey\":\"34sh2HYt11JMd63CTC2g3Q==\",\"RSAKey\":{\"RSAExp\":\"blJ6FvmAC6ZegcR6ITdg8WDSMljcikrUp3dRGbRDgKIfK0sx7b9i/kDtp4uD7/3IuTpD/qq09k07PO10T5R9NI2OdaEUGsoKJ4wqyzP6XzC9l/KeQmU7cMTh5OHO5UcqXWBn+g1INWaWI6DNAPFKK+4jcTyy6gTQQZQKSYRvRmgN6GkjOhbsdH0eM29eZScShJy2cGemZwbx8g2x7+cebALJmnJxycq29uBZaNBq4gV2/oNXxUhAogJY8SVhgPzCgig2MAJBLK/PzzxJ2LgnBLosZkUXo4vLPKSELX0qXSkSOoC/qPXvcQGawsknqUkSaaDwB1T1MYHmcJS/wpiIRM89Qru0Oy8sy8NRApk3ef0=\",\"RSAExpIV\":\"pQnJ6tvdJkPmMkqFjwJg5g==\",\"RSAPrimeP\":\"xTwgxhDvEvMCn8W6Mqv4ogRnBXFTLbuLP8YC2exQ+RwKmUbNsjB1eBlHyYRtKwc7qoDLf/zqpMZk3yPPcQDP+szAS5mmhCgpd+ePee8vvwVR5DImDuCquMGrPNEgpg3LL1aBHMF66pfnYGob+P6GZYzJP3mhlewUlh86NDzP8YkoVlrNRZrarB1/ZmA+w2Y2\",\"RSAPrimePIV\":\"BYynxyFw7WxrpSKJ3ogwOw==\",\"RSAPrimeQ\":\"wrSXyN/gpDTvK/BMeH+04uhzGUFVbBrfo47L3i+E1QsQndJDy2yyyE1D26mFcvyiefObaD097M3ruR9Wz7WfjxmyawG8N2W/BgtHZz/Ds2ThN/T9t1XqskxnIV8l2eq9LL7SqjAyp8jUGMNVS4WODDtDngLIR6OeehfyHpgwVZRuqQMxIT3wA78SwDcWo41s\",\"RSAPrimeQIV\":\"nh42rsJHRpRyrT7DUHCSGQ==\",\"RSAPublic\":{\"N\":21157349824302424474586534982259249211803834319040688464355493234801787797103302507532796077498450217821871970018694577875997137564403612290101696297388762661598985028358214691463545987525906601624475015641369758738594698734277968689933610815708041386873182005594893971935327515126641632178583839753604241275307504709356922001828087700047657269629468786192584360390570302228520684623917906322926285597325969441240603594319235541820769758760147548185811818605055779607442920451051925838835969679126714958513824527149123555098280031335447316080110515987423541389176918537439220114191601986031091845506241054078769049741,\"E\":65537}},\"Admin\":false}}}") diff --git a/keycache/keycache.go b/keycache/keycache.go index cf0a9c8..e66519b 100644 --- a/keycache/keycache.go +++ b/keycache/keycache.go @@ -74,15 +74,10 @@ func (usage Usage) matchesLabel(labels []string) bool { // matches returns true if this usage applies the user and label // also returns true if the usage is marked with AnyUser set to true -// DEPRECATED: an empty array of Users indicates that all users are valid func (usage Usage) matches(user string, labels []string) bool { if !usage.matchesLabel(labels) { return false } - // DEPRECATED: if usage lists no users, always match - if len(usage.Users) == 0 { - return true - } if usage.AnyUser { return true } diff --git a/keycache/keycache_test.go b/keycache/keycache_test.go index caddb07..e2ada8d 100644 --- a/keycache/keycache_test.go +++ b/keycache/keycache_test.go @@ -56,7 +56,7 @@ func TestUsesFlush(t *testing.T) { t.Fatalf("%v", err) } - key2, err := cache.DecryptKey(encKey, "user", "anybody", []string{}, pubEncryptedKey) + key2, err := cache.DecryptKey(encKey, "user", "alice", []string{}, pubEncryptedKey) if err != nil { t.Fatalf("%v", err) } @@ -66,7 +66,7 @@ func TestUsesFlush(t *testing.T) { } // Second decryption allowed. - _, err = cache.DecryptKey(encKey, "user", "anybody else", []string{}, pubEncryptedKey) + _, err = cache.DecryptKey(encKey, "user", "alice", []string{}, pubEncryptedKey) if err != nil { t.Fatalf("%v", err) } @@ -110,7 +110,7 @@ func TestTimeFlush(t *testing.T) { t.Fatalf("%v", err) } - _, err = cache.DecryptKey(dummy, "user", "anybody", []string{}, pubEncryptedKey) + _, err = cache.DecryptKey(dummy, "user", "alice", []string{}, pubEncryptedKey) if err == nil { t.Fatalf("Error in pruning expired key") } @@ -148,7 +148,7 @@ func TestGoodLabel(t *testing.T) { t.Fatalf("%v", err) } - _, err = cache.DecryptKey(dummy, "user", "anybody", []string{"red"}, pubEncryptedKey) + _, err = cache.DecryptKey(dummy, "user", "alice", []string{"red"}, pubEncryptedKey) if err != nil { t.Fatalf("%v", err) } From d97f953bc4082bb38b35f7d9e7444832af54677f Mon Sep 17 00:00:00 2001 From: Tyler J Date: Wed, 20 Apr 2016 17:08:33 -0700 Subject: [PATCH 3/5] Add tests in core for success and failure of the AnyUser parameter core: Add unit tests scenarios core: Check for a non-nil, but empty, Users list keycache: Add unit tests --- core/core.go | 2 +- core/core_test.go | 276 ++++++++++++++++++++++++++++++++++++++ keycache/keycache_test.go | 57 +++++++- 3 files changed, 333 insertions(+), 2 deletions(-) diff --git a/core/core.go b/core/core.go index 9d40270..0c624bf 100644 --- a/core/core.go +++ b/core/core.go @@ -404,7 +404,7 @@ func Delegate(jsonIn []byte) ([]byte, error) { } // Ensure a list of Users is given or the AnyUser flag is set - if s.Users == nil && s.AnyUser == false { + if (s.Users == nil || len(s.Users) == 0) && s.AnyUser == false { err = errors.New("Must provide a list of Users or set the AnyUser flag to true") return jsonStatusError(err) } diff --git a/core/core_test.go b/core/core_test.go index e6a426b..d8e3747 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -1131,3 +1131,279 @@ func TestValidateName(t *testing.T) { t.Fatalf("No error expected when username and password provided, %v", err) } } + +func TestAnyUserDelegation(t *testing.T) { + aliceJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}") + bobJson := []byte("{\"Name\":\"Bob\",\"Password\":\"Bonjour\"}") + carolJson := []byte("{\"Name\":\"Carol\",\"Password\":\"Hola\"}") + encryptJson := []byte("{\"Name\":\"Bob\",\"Password\":\"Bonjour\",\"Minimum\":1,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}") + encryptJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Bonjour\",\"Minimum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}") + delegateJson := []byte("{\"Name\":\"Bob\",\"Password\":\"Bonjour\",\"Time\":\"10s\",\"Uses\":1}") + delegateJson2 := []byte("{\"Name\":\"Bob\",\"Password\":\"Bonjour\",\"Time\":\"10s\",\"Uses\":1,\"AnyUser\":false}") + delegateJson3 := []byte("{\"Name\":\"Bob\",\"Password\":\"Bonjour\",\"Time\":\"10s\",\"Uses\":1,\"Users\":[]}") + delegateJson4 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"10s\",\"Uses\":1,\"AnyUser\":true}") + delegateJson5 := []byte("{\"Name\":\"Bob\",\"Password\":\"Bonjour\",\"Time\":\"10s\",\"Uses\":2,\"AnyUser\":true}") + delegateJson6 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hola\",\"Time\":\"10s\",\"Uses\":1,\"Users\":[\"Alice\"]}") + delegateJson7 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hola\",\"Time\":\"10s\",\"Uses\":1,\"Users\":[\"Bob\"]}") + + Init("memory", "", "", "", "") + + // Create a new vault with an initial user + var s ResponseData + respJson, err := Create(aliceJson) + if err != nil { + t.Fatalf("Error in creating account, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in creating account, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in creating account, %v", s.Status) + } + + // Create new users + respJson, err = CreateUser(bobJson) + if err != nil { + t.Fatalf("Error in creating account, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in creating account, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in creating account, %v", s.Status) + } + + respJson, err = CreateUser(carolJson) + if err != nil { + t.Fatalf("Error in creating account, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in creating account, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in creating account, %v", s.Status) + } + + // Encrypt some test data + respJson, err = Encrypt(encryptJson) + if err != nil { + t.Fatalf("Error in encrypt, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in encrypt, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in encrypt, %v", s.Status) + } + + // Create a decryption request for later + decryptJson, err := json.Marshal(DecryptRequest{Name: "Alice", Password: "Hello", Data: s.Response}) + if err != nil { + t.Fatalf("Error in marshalling decryption, %v", err) + } + + // Test that delegation without specifying a user fails (under multiple scenarios) + for _, delJson := range [][]byte{delegateJson, delegateJson2, delegateJson3} { + respJson, err = Delegate(delJson) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + if s.Status == "ok" { + t.Fatalf("Error expected when no user is provided") + } + } + + // Test that we can delegate to any user + respJson, err = Delegate(delegateJson4) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in delegate, %v", s.Status) + } + + // Verify the presence of a single delegation + cache.Refresh() + var sum SummaryData + respJson, err = Summary(aliceJson) + if err != nil { + t.Fatalf("Error in summary, %v", err) + } + err = json.Unmarshal(respJson, &sum) + if err != nil { + t.Fatalf("Error in summary, %v", err) + } + if sum.Status != "ok" { + t.Fatalf("Error in summary, %v", sum.Status) + } + if len(sum.Live) != 1 { + t.Fatalf("Error in summary, %v", sum.Live) + } + + // Test that we can decrypt the data + respJson, err = Decrypt(decryptJson) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in decrypt, %v", s.Status) + } + + // Verify that no delegations exist after decryption + cache.Refresh() + var sum2 SummaryData + respJson, err = Summary(aliceJson) + if err != nil { + t.Fatalf("Error in summary, %v", err) + } + err = json.Unmarshal(respJson, &sum2) + if err != nil { + t.Fatalf("Error in summary, %v", err) + } + if sum2.Status != "ok" { + t.Fatalf("Error in summary, %v", sum2.Status) + } + if len(sum2.Live) != 0 { + t.Fatalf("Error in summary, %#v", sum2.Live) + } + + // Encrypt some data that requires 2 owners to delegate + respJson, err = Encrypt(encryptJson2) + if err != nil { + t.Fatalf("Error in encrypt, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in encrypt, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in encrypt, %v", s.Status) + } + + // Create decryption requests for later + decryptJson2, err := json.Marshal(DecryptRequest{Name: "Alice", Password: "Hello", Data: s.Response}) + if err != nil { + t.Fatalf("Error in marshalling decryption, %v", err) + } + decryptJson3, err := json.Marshal(DecryptRequest{Name: "Bob", Password: "Bonjour", Data: s.Response}) + if err != nil { + t.Fatalf("Error in marshalling decryption, %v", err) + } + + // Test that we can delegate to any user, with 2 uses + respJson, err = Delegate(delegateJson5) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in delegate, %v", s.Status) + } + + // Test that we can delegate to a specific user, with 1 use + respJson, err = Delegate(delegateJson6) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in delegate, %v", s.Status) + } + + // Verify the presence of the 2 delegations + cache.Refresh() + var sum3 SummaryData + respJson, err = Summary(aliceJson) + if err != nil { + t.Fatalf("Error in summary, %v", err) + } + err = json.Unmarshal(respJson, &sum3) + if err != nil { + t.Fatalf("Error in summary, %v", err) + } + if sum3.Status != "ok" { + t.Fatalf("Error in summary, %v", sum3.Status) + } + if len(sum3.Live) != 2 { + t.Fatalf("Error in summary, %v", sum3.Live) + } + + // Test that we can decrypt the data for one request + respJson, err = Decrypt(decryptJson2) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in decrypt, %v", s.Status) + } + + // Test that we can delegate to another specific user, with 1 use + respJson, err = Delegate(delegateJson7) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in delegate, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in delegate, %v", s.Status) + } + + // Test that we can decrypt a request, using the new user's delegation and the remaining any-user delegation + respJson, err = Decrypt(decryptJson3) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in decrypt, %v", s.Status) + } + + // Verify that no delegations exist after decryption + cache.Refresh() + var sum4 SummaryData + respJson, err = Summary(aliceJson) + if err != nil { + t.Fatalf("Error in summary, %v", err) + } + err = json.Unmarshal(respJson, &sum4) + if err != nil { + t.Fatalf("Error in summary, %v", err) + } + if sum4.Status != "ok" { + t.Fatalf("Error in summary, %v", sum4.Status) + } + if len(sum4.Live) != 0 { + t.Fatalf("Error in summary, %#v", sum4.Live) + } +} diff --git a/keycache/keycache_test.go b/keycache/keycache_test.go index e2ada8d..bbfe71d 100644 --- a/keycache/keycache_test.go +++ b/keycache/keycache_test.go @@ -324,7 +324,8 @@ func TestAnyUser(t *testing.T) { &Usage{ 1, []string{"red", "blue"}, nil, - time.Now().Add(duration), true, + time.Now().Add(duration), + true, // Set AnyUser flag to true }, ) @@ -343,6 +344,7 @@ func TestAnyUser(t *testing.T) { t.Fatalf("%v", err) } + // Ensure that any user can decrypt the message _, err = cache.DecryptKey(dummy, "user", "anybody", []string{"red"}, pubEncryptedKey) if err != nil { t.Fatalf("%v", err) @@ -353,3 +355,56 @@ func TestAnyUser(t *testing.T) { t.Fatalf("Error in number of live keys %v", cache.UserKeys) } } + +func TestAnyUserNotDefaultBehavior(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() + + duration, _ := time.ParseDuration("1h") + err = cache.AddKeyFromRecord( + pr, "user", "weakpassword", "", + &Usage{ + 1, []string{"red", "blue"}, + nil, + time.Now().Add(duration), + false, // Set AnyUser flag to false + }, + ) + + 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) + } + + // Ensure that any user is not able to decrypt the message + _, err = cache.DecryptKey(dummy, "user", "anybody", []string{"red"}, pubEncryptedKey) + if err == nil { + t.Fatalf("Should have seen a decrypt error for user 'anybody'.") + } + + cache.Refresh() + if len(cache.UserKeys) != 1 { + t.Fatalf("Error in number of live keys %v", cache.UserKeys) + } +} From 5485b6e4c6b4f66dcd3acde89e90c39d1b829e94 Mon Sep 17 00:00:00 2001 From: Tyler Julian Date: Tue, 10 May 2016 17:12:01 -0700 Subject: [PATCH 4/5] Move enforcement of Users/AnyUser flag to keycache, pass through error --- core/core.go | 6 ------ keycache/keycache.go | 4 ++++ keycache/keycache_test.go | 42 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/core/core.go b/core/core.go index 0c624bf..836ca69 100644 --- a/core/core.go +++ b/core/core.go @@ -403,12 +403,6 @@ func Delegate(jsonIn []byte) ([]byte, error) { } } - // Ensure a list of Users is given or the AnyUser flag is set - if (s.Users == nil || len(s.Users) == 0) && s.AnyUser == false { - err = errors.New("Must provide a list of Users or set the AnyUser flag to true") - return jsonStatusError(err) - } - // Find password record for user and verify that their password // matches. If not found then add a new entry for this user. pr, found := records.GetRecord(s.Name) diff --git a/keycache/keycache.go b/keycache/keycache.go index e66519b..35d6008 100644 --- a/keycache/keycache.go +++ b/keycache/keycache.go @@ -176,6 +176,10 @@ func (cache *Cache) Refresh() { func (cache *Cache) AddKeyFromRecord(record passvault.PasswordRecord, name, password, slot string, usage *Usage) (err error) { var current ActiveUser + // Ensure a list of Users is given or the AnyUser flag is set + if (usage.Users == nil || len(usage.Users) == 0) && usage.AnyUser == false { + return errors.New("Must provide a list of Users or set the AnyUser flag to true") + } cache.Refresh() current.Usage = *usage diff --git a/keycache/keycache_test.go b/keycache/keycache_test.go index bbfe71d..4345a45 100644 --- a/keycache/keycache_test.go +++ b/keycache/keycache_test.go @@ -371,17 +371,45 @@ func TestAnyUserNotDefaultBehavior(t *testing.T) { cache := NewCache() + // Ensure we can't provide a nil list of Users *and* have a false AnyUser flag duration, _ := time.ParseDuration("1h") err = cache.AddKeyFromRecord( pr, "user", "weakpassword", "", &Usage{ 1, []string{"red", "blue"}, - nil, + nil, // Set a nil list of users time.Now().Add(duration), false, // Set AnyUser flag to false }, ) + if err == nil { + t.Fatalf("Should have seen error with Users=nil and AnyUser=false") + } + // Ensure we can't provide an empty list of Users either + err = cache.AddKeyFromRecord( + pr, "user", "weakpassword", "", + &Usage{ + 1, []string{"red", "blue"}, + []string{}, // Set an empty list of users + time.Now().Add(duration), + false, // Set AnyUser flag to false + }, + ) + if err == nil { + t.Fatalf("Should have seen error with Users=[]string{} and AnyUser=false") + } + + // Ensure we only the specified user can decrypt when AnyUser is false + err = cache.AddKeyFromRecord( + pr, "user", "weakpassword", "", + &Usage{ + 1, []string{"red", "blue"}, + []string{"alice"}, // Set a valid list of users + time.Now().Add(duration), + false, // Set AnyUser flag to false + }, + ) if err != nil { t.Fatalf("%v", err) } @@ -407,4 +435,16 @@ func TestAnyUserNotDefaultBehavior(t *testing.T) { if len(cache.UserKeys) != 1 { t.Fatalf("Error in number of live keys %v", cache.UserKeys) } + + // Sanity check to make sure our user can still decrpyt + _, err = cache.DecryptKey(dummy, "user", "alice", []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) + } + } From 263c73af1ce2b74d8e2c18f6707524a81e2297f7 Mon Sep 17 00:00:00 2001 From: Tyler Julian Date: Tue, 10 May 2016 20:13:42 -0700 Subject: [PATCH 5/5] Use keyed fields in composite literal, per `vet` source report Output before change: cryptor/cryptor_test.go:112: github.com/cloudflare/redoctober/keycache.Usage composite literal uses unkeyed fields --- cryptor/cryptor_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cryptor/cryptor_test.go b/cryptor/cryptor_test.go index 5f34eb0..38ab789 100644 --- a/cryptor/cryptor_test.go +++ b/cryptor/cryptor_test.go @@ -109,7 +109,15 @@ func TestDuplicates(t *testing.T) { // Delegate one key at a time and check that decryption fails. for name, pr := range recs { duration, _ := time.ParseDuration("1h") - err = cache.AddKeyFromRecord(pr, name, "weakpassword", "", &keycache.Usage{2, nil, nil, time.Now().Add(duration), true}) + err = cache.AddKeyFromRecord( + pr, name, "weakpassword", "", &keycache.Usage{ + Uses: 2, + Labels: nil, + Users: nil, + Expiry: time.Now().Add(duration), + AnyUser: true, + }, + ) if err != nil { t.Fatalf("%v", err) }