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
This commit is contained in:
Andrew Buss
2015-11-30 02:02:42 -08:00
parent f06cd674ba
commit 13f6616e60
4 changed files with 109 additions and 48 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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
}

View File

@@ -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 {