From 0fbba591633fed63c828ab2de09ab04fc00a2fb4 Mon Sep 17 00:00:00 2001 From: Adrian Utrilla Date: Thu, 18 Aug 2016 15:49:27 -0700 Subject: [PATCH] Added YAML encryption --- json/store.go | 7 ++++++- kms/keysource.go | 11 +++++++++++ main/main.go | 7 ++++++- pgp/keysource.go | 9 +++++++++ sops.go | 22 ++++++++++++++++++++++ yaml/store.go | 35 +++++++++++++++++++++++++++++------ 6 files changed, 83 insertions(+), 8 deletions(-) diff --git a/json/store.go b/json/store.go index ea15d5887..097c47281 100644 --- a/json/store.go +++ b/json/store.go @@ -8,7 +8,8 @@ import ( ) type JSONStore struct { - Data map[string]interface{} + Data map[string]interface{} + metadata sops.Metadata } func (store *JSONStore) WalkValue(in interface{}, additionalAuthData string, onLeaves func(interface{}, string) (interface{}, error)) (interface{}, error) { @@ -107,3 +108,7 @@ func (store JSONStore) LoadMetadata(in string) error { func (store JSONStore) Metadata() sops.Metadata { return sops.Metadata{} } + +func (store *JSONStore) SetMetadata(metadata sops.Metadata) { + store.metadata = metadata +} diff --git a/kms/keysource.go b/kms/keysource.go index ed26f9f76..693ddc890 100644 --- a/kms/keysource.go +++ b/kms/keysource.go @@ -127,3 +127,14 @@ func (k KMSMasterKey) createSession() (*session.Session, error) { } return sess, nil } + +func (k KMSMasterKey) ToMap() map[string]string { + out := make(map[string]string) + out["arn"] = k.Arn + if k.Role != "" { + out["role"] = k.Role + } + out["created_at"] = k.CreationDate.Format("2006-01-02T15:04:05Z") + out["enc"] = k.EncryptedKey + return out +} diff --git a/main/main.go b/main/main.go index 6599d8723..bb984fd2c 100644 --- a/main/main.go +++ b/main/main.go @@ -87,6 +87,7 @@ func main() { cli.StringFlag{ Name: "unencrypted-suffix", Usage: "override the unencrypted key suffix. default: unencrypted_", + Value: sops.DefaultUnencryptedSuffix, }, cli.StringFlag{ Name: "config", @@ -175,9 +176,10 @@ func decrypt(c *cli.Context, file string, fileBytes []byte) error { 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 != nil { + } else if err != sops.MacMismatch { return cli.NewExitError(fmt.Sprintf("Error loading file: %s", err), 6) } s, err := store.DumpUnencrypted() @@ -195,6 +197,8 @@ func encrypt(c *cli.Context, file string, fileBytes []byte) error { return cli.NewExitError(fmt.Sprintf("Error loading file: %s", err), 4) } var metadata sops.Metadata + metadata.UnencryptedSuffix = c.String("unencrypted-suffix") + metadata.Version = "2.0.0" var kmsKeys []sops.MasterKey if c.String("kms") != "" { for _, k := range kms.KMSMasterKeysFromArnString(c.String("kms")) { @@ -221,6 +225,7 @@ func encrypt(c *cli.Context, file string, fileBytes []byte) error { } } + store.SetMetadata(metadata) out, err := store.Dump(string(key)) fmt.Println(out) return nil diff --git a/pgp/keysource.go b/pgp/keysource.go index a9f8cabac..8fabd740a 100644 --- a/pgp/keysource.go +++ b/pgp/keysource.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "github.com/howeyc/gopass" + "go.mozilla.org/sops" "go.mozilla.org/sops/gpgagent" "golang.org/x/crypto/openpgp" "golang.org/x/crypto/openpgp/armor" @@ -187,3 +188,11 @@ func (key *GPGMasterKey) passphrasePrompt(keys []openpgp.Key, symmetric bool) ([ } return nil, fmt.Errorf("No key to unlock") } + +func (key GPGMasterKey) ToMap() map[string]string { + out := make(map[string]string) + out["fp"] = key.Fingerprint + out["created_at"] = key.CreationDate.Format(sops.DateFormat) + out["enc"] = key.EncryptedKey + return out +} diff --git a/sops.go b/sops.go index baaf8a359..61abb00bb 100644 --- a/sops.go +++ b/sops.go @@ -5,6 +5,10 @@ import ( "time" ) +const DateFormat = "2006-01-02T15:04:05Z" + +const DefaultUnencryptedSuffix = "_unencrypted" + type Error string func (e Error) Error() string { return string(e) } @@ -30,6 +34,7 @@ type MasterKey interface { Decrypt() (string, error) NeedsRotation() bool ToString() string + ToMap() map[string]string } type Store interface { @@ -39,6 +44,7 @@ type Store interface { DumpUnencrypted() (string, error) Metadata() Metadata LoadMetadata(in string) error + SetMetadata(Metadata) } func (m *Metadata) MasterKeyCount() int { @@ -71,3 +77,19 @@ func (m *Metadata) UpdateMasterKeys(dataKey string) { } } } + +func (m *Metadata) ToMap() map[string]interface{} { + out := make(map[string]interface{}) + out["lastmodified"] = m.LastModified.Format("2006-01-02T15:04:05Z") + out["unencrypted_suffix"] = m.UnencryptedSuffix + out["mac"] = m.MessageAuthenticationCode + out["version"] = m.Version + for _, ks := range m.KeySources { + keys := make([]map[string]string, 0) + for _, k := range ks.Keys { + keys = append(keys, k.ToMap()) + } + out[ks.Name] = keys + } + return out +} diff --git a/yaml/store.go b/yaml/store.go index 21ec34bc0..d2319e462 100644 --- a/yaml/store.go +++ b/yaml/store.go @@ -3,11 +3,12 @@ package yaml import ( "crypto/sha512" "fmt" + "github.com/autrilla/yaml" "go.mozilla.org/sops" "go.mozilla.org/sops/decryptor" "go.mozilla.org/sops/kms" "go.mozilla.org/sops/pgp" - "gopkg.in/yaml.v2" + "os" "strconv" "time" ) @@ -123,7 +124,7 @@ func (store *YAMLStore) Load(data, key string) error { if err != nil { return fmt.Errorf("Error walking tree: %s", err) } - originalMac, err := decryptor.Decrypt(store.metadata.MessageAuthenticationCode, key, []byte(store.metadata.LastModified.Format("2006-01-02T15:04:05Z"))) + originalMac, err := decryptor.Decrypt(store.metadata.MessageAuthenticationCode, key, []byte(store.metadata.LastModified.Format(sops.DateFormat))) if err != nil { return fmt.Errorf("Error decrypting MAC: %s", err) } @@ -135,12 +136,30 @@ func (store *YAMLStore) Load(data, key string) error { } func (store *YAMLStore) Dump(key string) (string, error) { + hash := sha512.New() _, err := store.WalkValue(store.Data, "", func(in interface{}, additionalAuthData string) (interface{}, error) { - return decryptor.Encrypt(in, key, []byte(additionalAuthData)) + v, err := decryptor.Encrypt(in, key, []byte(additionalAuthData)) + if err != nil { + return nil, err + } + bytes, err := toBytes(in) + if err != nil { + return nil, err + } + hash.Write(bytes) + return v, err }) if err != nil { return "", fmt.Errorf("Error walking tree: %s", err) } + + mac := fmt.Sprintf("%X", hash.Sum(nil)) + os.Stderr.WriteString(mac) + store.metadata.MessageAuthenticationCode, err = decryptor.Encrypt(mac, key, []byte(store.metadata.LastModified.Format(sops.DateFormat))) + if err != nil { + return "", fmt.Errorf("Error encrypting MAC: %s", err) + } + store.Data = append(store.Data, yaml.MapItem{Key: "sops", Value: store.metadata.ToMap()}) out, err := yaml.Marshal(store.Data) if err != nil { return "", fmt.Errorf("Error marshaling to yaml: %s", err) @@ -173,7 +192,7 @@ func (store *YAMLStore) LoadMetadata(in string) error { return err } store.metadata.MessageAuthenticationCode = data["mac"].(string) - lastModified, err := time.Parse("2006-01-02T15:04:05Z", data["lastmodified"].(string)) + lastModified, err := time.Parse(sops.DateFormat, data["lastmodified"].(string)) if err != nil { return fmt.Errorf("Could not parse last modified date: %s", err) } @@ -213,7 +232,7 @@ func (store *YAMLStore) kmsEntries(in []interface{}) (sops.KeySource, error) { if ok { key.Role = role } - creationDate, err := time.Parse("2006-01-02T15:04:05Z", entry["created_at"].(string)) + creationDate, err := time.Parse(sops.DateFormat, entry["created_at"].(string)) if err != nil { return keysource, fmt.Errorf("Could not parse creation date: %s", err) } @@ -231,7 +250,7 @@ func (store *YAMLStore) pgpEntries(in []interface{}) (sops.KeySource, error) { key := &pgp.GPGMasterKey{} key.Fingerprint = entry["fp"].(string) key.EncryptedKey = entry["enc"].(string) - creationDate, err := time.Parse("2006-01-02T15:04:05Z", entry["created_at"].(string)) + creationDate, err := time.Parse(sops.DateFormat, entry["created_at"].(string)) if err != nil { return keysource, fmt.Errorf("Could not parse creation date: %s", err) } @@ -240,3 +259,7 @@ func (store *YAMLStore) pgpEntries(in []interface{}) (sops.KeySource, error) { } return keysource, nil } + +func (store *YAMLStore) SetMetadata(metadata sops.Metadata) { + store.metadata = metadata +}