mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-01-06 05:27:17 +00:00
Merge pull request #104 from andrewbuss/single_owner
Allow encryption with a single owner
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user