From 13f6616e603085f34cf3d15c37c01ba591651948 Mon Sep 17 00:00:00 2001 From: Andrew Buss Date: Mon, 30 Nov 2015 02:02:42 -0800 Subject: [PATCH] Allow encryption with a single owner The "minimum" parameter was ignored, so when a single user was provided as an owner, the encrypted data had no KeySet value and could not be decrypted. This change fixes the API and cryptor to pay attention to the Minimum parameter and handle the case where Minimum is 1 --- core/core.go | 3 +++ core/core_test.go | 54 +++++++++++++++++++++++++++++++++++--- cryptor/cryptor.go | 65 +++++++++++++++++++++++++++------------------- redoctober_test.go | 35 +++++++++++++------------ 4 files changed, 109 insertions(+), 48 deletions(-) diff --git a/core/core.go b/core/core.go index 0362703..51608ac 100644 --- a/core/core.go +++ b/core/core.go @@ -69,6 +69,7 @@ type EncryptRequest struct { Name string Password string + Minimum int Owners []string LeftOwners []string RightOwners []string @@ -453,6 +454,7 @@ func Encrypt(jsonIn []byte) ([]byte, error) { } access := cryptor.AccessStructure{ + Minimum: s.Minimum, Names: s.Owners, LeftNames: s.LeftOwners, RightNames: s.RightOwners, @@ -497,6 +499,7 @@ func ReEncrypt(jsonIn []byte) ([]byte, error) { } access := cryptor.AccessStructure{ + Minimum: s.Minimum, Names: s.Owners, LeftNames: s.LeftOwners, RightNames: s.RightOwners, diff --git a/core/core_test.go b/core/core_test.go index 145c60f..9951f74 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -400,8 +400,10 @@ func TestEncryptDecrypt(t *testing.T) { delegateJson3 := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Time\":\"0s\",\"Uses\":0}") 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\"]}") - encryptJson := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Minumum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}") - encryptJson2 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Minumum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\",\"Labels\":[\"blue\",\"red\"]}") + delegateJson6 := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\",\"Time\":\"10s\",\"Uses\":1}") + 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=\"}") Init("memory") @@ -575,6 +577,49 @@ func TestEncryptDecrypt(t *testing.T) { t.Fatalf("Error in decrypt, %v", d.Delegates) } } + + // Encrypt with only a single owner + respJson, err = Encrypt(encryptJson3) + 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) + } + + respJson, err = Delegate(delegateJson6) + if err != nil { + t.Fatalf("Error in delegating account, %v", err) + } + err = json.Unmarshal(respJson, &s) + if err != nil { + t.Fatalf("Error in delegating account, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in delegating account, %v", s.Status) + } + + // decrypt file + decryptJson2, err := json.Marshal(DecryptRequest{Name: "Alice", Password: "Hello", Data: s.Response}) + if err != nil { + t.Fatalf("Error in marshalling decryption, %v", err) + } + + respJson2, err = Decrypt(decryptJson2) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + err = json.Unmarshal(respJson2, &s) + if err != nil { + t.Fatalf("Error in decrypt, %v", err) + } + if s.Status != "ok" { + t.Fatalf("Error in decrypt, %v", s.Status) + } } func TestReEncrypt(t *testing.T) { @@ -585,7 +630,7 @@ func TestReEncrypt(t *testing.T) { 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"]}`) delegateJson7 := []byte(`{"Name":"Carol","Password":"Hello","Time":"10s","Uses":2,"Users":["Alice"],"Labels":["red"]}`) - encryptJson := []byte(`{"Name":"Carol","Password":"Hello","Minumum":2,"Owners":["Alice","Bob","Carol"],"Data":"SGVsbG8gSmVsbG8=","Labels":["blue"]}`) + encryptJson := []byte(`{"Name":"Carol","Password":"Hello","Minimum":2,"Owners":["Alice","Bob","Carol"],"Data":"SGVsbG8gSmVsbG8=","Labels":["blue"]}`) Init("memory") @@ -671,6 +716,7 @@ func TestReEncrypt(t *testing.T) { Name: "Alice", Password: "Hello", Data: s.Response, + Minimum: 2, Owners: []string{"Alice", "Bob", "Carol"}, Labels: []string{"red"}, }) @@ -761,7 +807,7 @@ 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}") - encryptJson := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Minumum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}") + encryptJson := []byte("{\"Name\":\"Carol\",\"Password\":\"Hello\",\"Minimum\":2,\"Owners\":[\"Alice\",\"Bob\",\"Carol\"],\"Data\":\"SGVsbG8gSmVsbG8=\"}") var s ResponseData var l OwnersData diff --git a/cryptor/cryptor.go b/cryptor/cryptor.go index fbdf556..88b27a9 100644 --- a/cryptor/cryptor.go +++ b/cryptor/cryptor.go @@ -42,7 +42,8 @@ func New(records *passvault.Records, cache *keycache.Cache) Cryptor { // both, then he can decrypt it alone). If a predicate is present, it must be // satisfied to decrypt. type AccessStructure struct { - Names []string + Minimum int + Names []string LeftNames []string RightNames []string @@ -253,23 +254,19 @@ func (encrypted *EncryptedData) wrapKey(records *passvault.Records, clearKey []b return } - encryptKey := func(outer, inner string, clearKey []byte) (keyBytes []byte, err error) { - var outerCrypt, innerCrypt cipher.Block + encryptKey := func(keyNames []string, clearKey []byte) (keyBytes []byte, err error) { keyBytes = make([]byte, 16) + copy(keyBytes, clearKey) + for _, keyName := range keyNames { + var keyCrypt cipher.Block + keyCrypt, err = aes.NewCipher(encrypted.KeySetRSA[keyName].aesKey) + if err != nil { + return + } - outerCrypt, err = aes.NewCipher(encrypted.KeySetRSA[outer].aesKey) - if err != nil { - return + keyCrypt.Encrypt(keyBytes, keyBytes) } - innerCrypt, err = aes.NewCipher(encrypted.KeySetRSA[inner].aesKey) - if err != nil { - return - } - - innerCrypt.Encrypt(keyBytes, clearKey) - outerCrypt.Encrypt(keyBytes, keyBytes) - return } @@ -282,26 +279,40 @@ func (encrypted *EncryptedData) wrapKey(records *passvault.Records, clearKey []b if err != nil { return err } - } - // encrypt file key with every combination of two keys - encrypted.KeySet = make([]MultiWrappedKey, 0) - - for i := 0; i < len(access.Names); i++ { - for j := i + 1; j < len(access.Names); j++ { - keyBytes, err := encryptKey(access.Names[i], access.Names[j], clearKey) + if access.Minimum == 1 { + keyBytes, err := encryptKey([]string{access.Names[0]}, clearKey) if err != nil { return err } - out := MultiWrappedKey{ - Name: []string{access.Names[i], access.Names[j]}, + encrypted.KeySet = append(encrypted.KeySet, MultiWrappedKey{ + Name: []string{access.Names[0]}, Key: keyBytes, - } - - encrypted.KeySet = append(encrypted.KeySet, out) + }) } } + + if access.Minimum == 2 { + for i := 0; i < len(access.Names); i++ { + for j := i + 1; j < len(access.Names); j++ { + keyBytes, err := encryptKey([]string{access.Names[j], access.Names[i]}, clearKey) + if err != nil { + return err + } + + out := MultiWrappedKey{ + Name: []string{access.Names[i], access.Names[j]}, + Key: keyBytes, + } + + encrypted.KeySet = append(encrypted.KeySet, out) + } + } + } else if access.Minimum > 3 { + err = errors.New("Encryption to a list of owners with minimum > 2 is not implemented") + return err + } } else if len(access.LeftNames) > 0 && len(access.RightNames) > 0 { // Generate a random AES key for each user and RSA/ECIES encrypt it encrypted.KeySetRSA = make(map[string]SingleWrappedKey) @@ -329,7 +340,7 @@ func (encrypted *EncryptedData) wrapKey(records *passvault.Records, clearKey []b continue } - keyBytes, err := encryptKey(leftName, rightName, clearKey) + keyBytes, err := encryptKey([]string{rightName, leftName}, clearKey) if err != nil { return err } diff --git a/redoctober_test.go b/redoctober_test.go index 84ea7f0..dfcffc5 100644 --- a/redoctober_test.go +++ b/redoctober_test.go @@ -33,19 +33,20 @@ var ( createUserInput3 = &core.CreateUserRequest{Name: "Dodo", Password: "Dodgson"} delegateInput1 = &core.DelegateRequest{ - Name: createUserInput1.Name, + Name: createUserInput1.Name, Password: createUserInput1.Password, - Time: "2h34m", - Uses: 1, + Time: "2h34m", + Uses: 1, } delegateInput2 = &core.DelegateRequest{ - Name: createUserInput2.Name, + Name: createUserInput2.Name, Password: createUserInput2.Password, - Time: "2h34m", - Uses: 1, + Time: "2h34m", + Uses: 1, } - encryptInput = &core.EncryptRequest{ + encryptInput = &core.EncryptRequest{ + Minimum: 2, Name: createVaultInput.Name, Password: createVaultInput.Password, Owners: []string{createUserInput1.Name, createUserInput2.Name}, @@ -378,9 +379,9 @@ func TestDecrypt(t *testing.T) { encryptedData := s.Response decryptInput := &core.DecryptRequest{ - Name: "Alice", + Name: "Alice", Password: "Lewis", - Data: encryptedData, + Data: encryptedData, } // Check the first decrypt command (where not enough owners have decrypted yet). @@ -612,8 +613,8 @@ func TestPassword(t *testing.T) { // Check changing password with invalid password. passwordInput := &core.PasswordRequest{ - Name: createUserInput1.Name, - Password: "badpassword", + Name: createUserInput1.Name, + Password: "badpassword", NewPassword: "worsepassword", } if err := postAndTest("password", passwordInput, 200, "Wrong Password"); err != nil { @@ -623,8 +624,8 @@ func TestPassword(t *testing.T) { // Check changing password with nonexistent user. passwordInput = &core.PasswordRequest{ - Name: createUserInput2.Name, - Password: "badpassword", + Name: createUserInput2.Name, + Password: "badpassword", NewPassword: "worsepassword", } if err := postAndTest("password", passwordInput, 200, "Record not present"); err != nil { @@ -634,8 +635,8 @@ func TestPassword(t *testing.T) { // Check changing the password properly. passwordInput = &core.PasswordRequest{ - Name: createUserInput1.Name, - Password: createUserInput1.Password, + Name: createUserInput1.Name, + Password: createUserInput1.Password, NewPassword: "foobar", } if err := postAndTest("password", passwordInput, 200, "ok"); err != nil { @@ -662,7 +663,7 @@ func TestPurge(t *testing.T) { // Check purging with non-admin user purgeInput := &core.PurgeRequest{ - Name: createUserInput1.Name, + Name: createUserInput1.Name, Password: createUserInput1.Password, } if err := postAndTest("purge", purgeInput, 200, "Admin required"); err != nil { @@ -672,7 +673,7 @@ func TestPurge(t *testing.T) { // Check purging with admin user purgeInput = &core.PurgeRequest{ - Name: createVaultInput.Name, + Name: createVaultInput.Name, Password: createVaultInput.Password, } if err := postAndTest("purge", purgeInput, 200, "ok"); err != nil {