Skip to content

Commit

Permalink
Refactored tree outside of YAML store
Browse files Browse the repository at this point in the history
  • Loading branch information
autrilla committed Aug 19, 2016
1 parent eca5943 commit 2077806
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 168 deletions.
32 changes: 18 additions & 14 deletions main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"crypto/rand"
"fmt"
"go.mozilla.org/sops/json"
"go.mozilla.org/sops/kms"
"go.mozilla.org/sops/pgp"
"go.mozilla.org/sops/yaml"
Expand Down Expand Up @@ -146,7 +145,7 @@ func store(path string) sops.Store {
if strings.HasSuffix(path, ".yaml") {
return &yaml.YAMLStore{}
} else if strings.HasSuffix(path, ".json") {
return &json.JSONStore{}
// return &json.JSONStore{}
}
panic("Unknown file type for file " + path)
}
Expand All @@ -167,32 +166,36 @@ func findKey(keysources []sops.KeySource) (string, error) {

func decrypt(c *cli.Context, file string, fileBytes []byte) error {
store := store(file)
err := store.LoadMetadata(string(fileBytes))
metadata, err := store.LoadMetadata(string(fileBytes))
if err != nil {
return cli.NewExitError(fmt.Sprintf("Error loading file: %s", err), 3)
}
key, err := findKey(store.Metadata().KeySources)
key, err := findKey(metadata.KeySources)
if err != nil {
return cli.NewExitError(err.Error(), 4)
}
err = store.Load(string(fileBytes), key)
fmt.Println(err == sops.MacMismatch)
if err == sops.MacMismatch && !c.Bool("ignore-mac") {
return cli.NewExitError("MAC mismatch", 5)
} else if err != sops.MacMismatch {
tree, err := store.Load(string(fileBytes))
if err != nil {
return cli.NewExitError(fmt.Sprintf("Error loading file: %s", err), 6)
}
s, err := store.DumpUnencrypted()
mac, err := tree.Decrypt(key)
if err != nil {
return cli.NewExitError(fmt.Sprintf("Error decrypting tree: %s", err), 8)
}
if metadata.MessageAuthenticationCode != mac && !c.Bool("ignore-mac") {
return cli.NewExitError("MAC mismatch.", 9)
}
out, err := store.Dump(tree)
if err != nil {
return cli.NewExitError(fmt.Sprintf("Error dumping file: %s", err), 7)
}
fmt.Print(s)
fmt.Print(string(out))
return nil
}

func encrypt(c *cli.Context, file string, fileBytes []byte) error {
store := store(file)
err := store.LoadUnencrypted(string(fileBytes))
tree, err := store.Load(string(fileBytes))
if err != nil {
return cli.NewExitError(fmt.Sprintf("Error loading file: %s", err), 4)
}
Expand Down Expand Up @@ -225,8 +228,9 @@ func encrypt(c *cli.Context, file string, fileBytes []byte) error {
}
}

store.SetMetadata(metadata)
out, err := store.Dump(string(key))
mac, err := tree.Encrypt(string(key))
metadata.MessageAuthenticationCode = mac
out, err := store.DumpWithMetadata(tree, metadata)
fmt.Println(out)
return nil
}
119 changes: 112 additions & 7 deletions sops.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package sops

import (
"crypto/sha512"
"fmt"
"go.mozilla.org/sops/decryptor"
"strconv"
"strings"
"time"
)

Expand All @@ -15,6 +19,93 @@ func (e Error) Error() string { return string(e) }

const MacMismatch = Error("MAC mismatch")

type TreeItem struct {
Key string
Value interface{}
}

type TreeBranch []TreeItem

func (tree TreeBranch) WalkValue(in interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (interface{}, error) {
switch in := in.(type) {
case string:
return onLeaves(in, path)
case int:
return onLeaves(in, path)
case bool:
return onLeaves(in, path)
case TreeBranch:
return tree.WalkBranch(in, path, onLeaves)
case []interface{}:
return tree.WalkSlice(in, path, onLeaves)
default:
return nil, fmt.Errorf("Cannot walk value, unknown type: %T", in)
}
}

func (tree TreeBranch) WalkSlice(in []interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) ([]interface{}, error) {
for i, v := range in {
newV, err := tree.WalkValue(v, path, onLeaves)
if err != nil {
return nil, err
}
in[i] = newV
}
return in, nil
}

func (tree TreeBranch) WalkBranch(in TreeBranch, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (TreeBranch, error) {
for i, item := range in {
newV, err := tree.WalkValue(item.Value, append(path, item.Key), onLeaves)
if err != nil {
return nil, err
}
in[i].Value = newV
}
return in, nil
}

func (tree TreeBranch) Encrypt(key string) (string, error) {
hash := sha512.New()
_, err := tree.WalkBranch(tree, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
v, err := decryptor.Encrypt(in, key, []byte(strings.Join(path, ":")+":"))
if err != nil {
return nil, fmt.Errorf("Could not encrypt value: %s", err)
}
bytes, err := toBytes(in)
if err != nil {
return nil, fmt.Errorf("Could not convert %s to bytes: %s", in, err)
}
hash.Write(bytes)
return v, err
})
if err != nil {
return "", fmt.Errorf("Error walking tree: %s", err)
}
return fmt.Sprintf("%X", hash.Sum(nil)), nil
}

func (tree TreeBranch) Decrypt(key string) (string, error) {
hash := sha512.New()
_, err := tree.WalkBranch(tree, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
v, err := decryptor.Decrypt(in.(string), key, []byte(strings.Join(path, ":")+":"))
if err != nil {
return nil, fmt.Errorf("Could not encrypt value: %s", err)
}
bytes, err := toBytes(v)
if err != nil {
return nil, fmt.Errorf("Could not convert %s to bytes: %s", in, err)
}
hash.Write(bytes)
return v, err
})
if err != nil {
return "", fmt.Errorf("Error walking tree: %s", err)
}
return fmt.Sprintf("%X", hash.Sum(nil)), nil

}

type Metadata struct {
LastModified time.Time
UnencryptedSuffix string
Expand All @@ -38,13 +129,10 @@ type MasterKey interface {
}

type Store interface {
LoadUnencrypted(data string) error
Load(data, key string) error
Dump(key string) (string, error)
DumpUnencrypted() (string, error)
Metadata() Metadata
LoadMetadata(in string) error
SetMetadata(Metadata)
Load(in string) (TreeBranch, error)
LoadMetadata(in string) (Metadata, error)
Dump(TreeBranch) (string, error)
DumpWithMetadata(TreeBranch, Metadata) (string, error)
}

func (m *Metadata) MasterKeyCount() int {
Expand Down Expand Up @@ -93,3 +181,20 @@ func (m *Metadata) ToMap() map[string]interface{} {
}
return out
}

func toBytes(in interface{}) ([]byte, error) {
switch in := in.(type) {
case string:
return []byte(in), nil
case int:
return []byte(strconv.Itoa(in)), nil
case float64:
return []byte(strconv.FormatFloat(in, 'f', -1, 64)), nil
case bool:
return []byte(strconv.FormatBool(in)), nil
case []byte:
return in, nil
default:
return nil, fmt.Errorf("Could not convert unknown type %T to bytes", in)
}
}

0 comments on commit 2077806

Please sign in to comment.