added the --encrypted-suffix option

This commit is contained in:
Dov Reshef 2018-04-08 12:43:43 +03:00
Родитель e57c8f2c85
Коммит efd8521436
6 изменённых файлов: 115 добавлений и 20 удалений

Просмотреть файл

@ -36,6 +36,7 @@ type editOpts struct {
type editExampleOpts struct {
editOpts
UnencryptedSuffix string
EncryptedSuffix string
KeyGroups []sops.KeyGroup
GroupThreshold int
}
@ -96,6 +97,7 @@ func editExample(opts editExampleOpts) ([]byte, error) {
tree.Metadata = sops.Metadata{
KeyGroups: opts.KeyGroups,
UnencryptedSuffix: opts.UnencryptedSuffix,
EncryptedSuffix: opts.EncryptedSuffix,
Version: version,
ShamirThreshold: opts.GroupThreshold,
}

Просмотреть файл

@ -18,6 +18,7 @@ type encryptOpts struct {
InputPath string
KeyServices []keyservice.KeyServiceClient
UnencryptedSuffix string
EncryptedSuffix string
KeyGroups []sops.KeyGroup
GroupThreshold int
}
@ -37,6 +38,7 @@ func encrypt(opts encryptOpts) (encryptedFile []byte, err error) {
tree.Metadata = sops.Metadata{
KeyGroups: opts.KeyGroups,
UnencryptedSuffix: opts.UnencryptedSuffix,
EncryptedSuffix: opts.EncryptedSuffix,
Version: version,
ShamirThreshold: opts.GroupThreshold,
}

Просмотреть файл

@ -328,6 +328,10 @@ func main() {
Usage: "override the unencrypted key suffix.",
Value: sops.DefaultUnencryptedSuffix,
},
cli.StringFlag{
Name: "encrypted-suffix",
Usage: "override the encrypted key suffix. When empty, all keys will be encrypted, unless otherwise marked with unencrypted-suffix.",
},
cli.StringFlag{
Name: "config",
Usage: "path to sops' config file. If set, sops will not search for the config file recursively.",
@ -392,6 +396,7 @@ func main() {
InputPath: fileName,
Cipher: aes.NewCipher(),
UnencryptedSuffix: c.String("unencrypted-suffix"),
EncryptedSuffix: c.String("encrypted-suffix"),
KeyServices: svcs,
KeyGroups: groups,
GroupThreshold: threshold,
@ -498,6 +503,7 @@ func main() {
output, err = editExample(editExampleOpts{
editOpts: opts,
UnencryptedSuffix: c.String("unencrypted-suffix"),
EncryptedSuffix: c.String("encrypted-suffix"),
KeyGroups: groups,
GroupThreshold: threshold,
})

59
sops.go
Просмотреть файл

@ -107,7 +107,7 @@ func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} {
leaf,
}
} else {
return []interface{} {
return []interface{}{
valueFromPathAndLeaf(path[1:], leaf),
}
}
@ -115,14 +115,14 @@ func valueFromPathAndLeaf(path []interface{}, leaf interface{}) interface{} {
if len(path) == 1 {
return TreeBranch{
TreeItem{
Key: component,
Key: component,
Value: leaf,
},
}
} else {
return TreeBranch{
TreeItem{
Key: component,
Key: component,
Value: valueFromPathAndLeaf(path[1:], leaf),
},
}
@ -171,7 +171,7 @@ func set(branch interface{}, path []interface{}, value interface{}) interface{}
}
}
func (branch TreeBranch) Set(path []interface{}, value interface{}) (TreeBranch) {
func (branch TreeBranch) Set(path []interface{}, value interface{}) TreeBranch {
return set(branch, path, value).(TreeBranch)
}
@ -281,7 +281,8 @@ func (branch TreeBranch) walkBranch(in TreeBranch, path []string, onLeaves func(
return in, nil
}
// Encrypt walks over the tree and encrypts all values with the provided cipher, except those whose key ends with the UnencryptedSuffix specified on the Metadata struct. If encryption is successful, it returns the MAC for the encrypted tree.
// Encrypt walks over the tree and encrypts all values with the provided cipher, except those whose key ends with the UnencryptedSuffix specified on the Metadata struct, or those not ending with EncryptedSuffix, if EncryptedSuffix is provided (by default it is not).
// If encryption is successful, it returns the MAC for the encrypted tree.
func (tree Tree) Encrypt(key []byte, cipher Cipher) (string, error) {
hash := sha512.New()
_, err := tree.Branch.walkBranch(tree.Branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
@ -294,11 +295,27 @@ func (tree Tree) Encrypt(key []byte, cipher Cipher) (string, error) {
hash.Write(bytes)
}
encrypted := true
for _, v := range path {
if strings.HasSuffix(v, tree.Metadata.UnencryptedSuffix) {
encrypted = false
if tree.Metadata.UnencryptedSuffix != "" {
for _, v := range path {
if strings.HasSuffix(v, tree.Metadata.UnencryptedSuffix) {
encrypted = false
break
}
}
}
if tree.Metadata.EncryptedSuffix != "" {
encryptedSuffixFound := false
for _, v := range path {
if strings.HasSuffix(v, tree.Metadata.EncryptedSuffix) {
encryptedSuffixFound = true
if !encrypted {
return nil, fmt.Errorf("Cannot use both encrypted_suffix and unencrypted_suffix in the same file")
}
break
}
}
encrypted = encryptedSuffixFound
}
if encrypted {
var err error
pathString := strings.Join(path, ":") + ":"
@ -315,17 +332,34 @@ func (tree Tree) Encrypt(key []byte, cipher Cipher) (string, error) {
return fmt.Sprintf("%X", hash.Sum(nil)), nil
}
// Decrypt walks over the tree and decrypts all values with the provided cipher, except those whose key ends with the UnencryptedSuffix specified on the Metadata struct. If decryption is successful, it returns the MAC for the decrypted tree.
// Decrypt walks over the tree and decrypts all values with the provided cipher, except those whose key ends with the UnencryptedSuffix specified on the Metadata struct or those not ending with EncryptedSuffix, if EncryptedSuffix is provided (by default it is not).
// If decryption is successful, it returns the MAC for the decrypted tree.
func (tree Tree) Decrypt(key []byte, cipher Cipher) (string, error) {
log.Debug("Decrypting tree")
hash := sha512.New()
_, err := tree.Branch.walkBranch(tree.Branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
encrypted := true
for _, p := range path {
if strings.HasSuffix(p, tree.Metadata.UnencryptedSuffix) {
encrypted = false
if tree.Metadata.UnencryptedSuffix != "" {
for _, p := range path {
if strings.HasSuffix(p, tree.Metadata.UnencryptedSuffix) {
encrypted = false
break
}
}
}
if tree.Metadata.EncryptedSuffix != "" {
encryptedSuffixFound := false
for _, p := range path {
if strings.HasSuffix(p, tree.Metadata.EncryptedSuffix) {
encryptedSuffixFound = true
if !encrypted {
return nil, fmt.Errorf("Cannot use both encrypted_suffix and unencrypted_suffix in the same file")
}
break
}
}
encrypted = encryptedSuffixFound
}
var v interface{}
if encrypted {
var err error
@ -390,6 +424,7 @@ func (tree *Tree) GenerateDataKeyWithKeyServices(svcs []keyservice.KeyServiceCli
type Metadata struct {
LastModified time.Time
UnencryptedSuffix string
EncryptedSuffix string
MessageAuthenticationCode string
Version string
KeyGroups []KeyGroup

Просмотреть файл

@ -83,6 +83,56 @@ func TestUnencryptedSuffix(t *testing.T) {
}
}
func TestEncryptedSuffix(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "foo_encrypted",
Value: "bar",
},
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
}
tree := Tree{Branch: branch, Metadata: Metadata{EncryptedSuffix: "_encrypted"}}
expected := TreeBranch{
TreeItem{
Key: "foo_encrypted",
Value: "rab",
},
TreeItem{
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "foo",
Value: "bar",
},
},
},
}
cipher := reverseCipher{}
_, err := tree.Encrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Encrypting the tree failed: %s", err)
}
if !reflect.DeepEqual(tree.Branch, expected) {
t.Errorf("Trees don't match: \ngot \t\t%+v,\n expected \t\t%+v", tree.Branch, expected)
}
_, err = tree.Decrypt(bytes.Repeat([]byte("f"), 32), cipher)
if err != nil {
t.Errorf("Decrypting the tree failed: %s", err)
}
expected[0].Value = "bar"
if !reflect.DeepEqual(tree.Branch, expected) {
t.Errorf("Trees don't match: \ngot\t\t\t%+v,\nexpected\t\t%+v", tree.Branch, expected)
}
}
type MockCipher struct{}
func (m MockCipher) Encrypt(value interface{}, key []byte, path string) (string, error) {
@ -248,7 +298,6 @@ func TestTruncateTree(t *testing.T) {
assert.Equal(t, expected, result)
}
func TestEncryptComments(t *testing.T) {
tree := Tree{
Branch: TreeBranch{
@ -327,7 +376,7 @@ func TestSetNewKey(t *testing.T) {
Key: "bar",
Value: TreeBranch{
TreeItem{
Key: "baz",
Key: "baz",
Value: "foobar",
},
},
@ -356,7 +405,7 @@ func TestSetArrayDeepNew(t *testing.T) {
func TestSetNewKeyDeep(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "foo",
Key: "foo",
Value: "bar",
},
}
@ -364,7 +413,6 @@ func TestSetNewKeyDeep(t *testing.T) {
assert.Equal(t, "hello", set[0].Value.(TreeBranch)[0].Value.(TreeBranch)[0].Value)
}
func TestSetNewKeyOnEmptyBranch(t *testing.T) {
branch := TreeBranch{}
set := branch.Set([]interface{}{"foo", "bar", "baz"}, "hello")
@ -386,7 +434,6 @@ func TestSetArray(t *testing.T) {
assert.Equal(t, "uno", set[0].Value.([]interface{})[0])
}
func TestSetArrayNew(t *testing.T) {
branch := TreeBranch{}
set := branch.Set([]interface{}{"foo", 0, 0}, "uno")
@ -396,7 +443,7 @@ func TestSetArrayNew(t *testing.T) {
func TestSetExisting(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "foo",
Key: "foo",
Value: "foobar",
},
}
@ -407,7 +454,7 @@ func TestSetExisting(t *testing.T) {
func TestSetArrayLeafNewItem(t *testing.T) {
branch := TreeBranch{
TreeItem{
Key: "array",
Key: "array",
Value: []interface{}{},
},
}
@ -438,7 +485,7 @@ func TestSetArrayNonLeaf(t *testing.T) {
Value: []interface{}{
TreeBranch{
TreeItem{
Key: "hello",
Key: "hello",
Value: "hello",
},
},

Просмотреть файл

@ -42,6 +42,7 @@ type Metadata struct {
MessageAuthenticationCode string `yaml:"mac" json:"mac"`
PGPKeys []pgpkey `yaml:"pgp" json:"pgp"`
UnencryptedSuffix string `yaml:"unencrypted_suffix" json:"unencrypted_suffix"`
EncryptedSuffix string `yaml:"encrypted_suffix,omitempty" json:"encrypted_suffix,omitempty"`
Version string `yaml:"version" json:"version"`
}
@ -76,6 +77,7 @@ func MetadataFromInternal(sopsMetadata sops.Metadata) Metadata {
var m Metadata
m.LastModified = sopsMetadata.LastModified.Format(time.RFC3339)
m.UnencryptedSuffix = sopsMetadata.UnencryptedSuffix
m.EncryptedSuffix = sopsMetadata.EncryptedSuffix
m.MessageAuthenticationCode = sopsMetadata.MessageAuthenticationCode
m.Version = sopsMetadata.Version
m.ShamirThreshold = sopsMetadata.ShamirThreshold
@ -159,6 +161,7 @@ func (m *Metadata) ToInternal() (sops.Metadata, error) {
Version: m.Version,
MessageAuthenticationCode: m.MessageAuthenticationCode,
UnencryptedSuffix: m.UnencryptedSuffix,
EncryptedSuffix: m.EncryptedSuffix,
LastModified: lastModified,
}, nil
}