This commit is contained in:
Adrian Utrilla 2016-08-24 10:29:28 -07:00
Родитель e908f46228
Коммит c9d63a838e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 6BA64E6212CDEBE9
7 изменённых файлов: 101 добавлений и 52 удалений

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

@ -10,7 +10,7 @@ import (
"strconv"
)
type EncryptedValue struct {
type encryptedValue struct {
data []byte
iv []byte
tag []byte
@ -19,7 +19,7 @@ type EncryptedValue struct {
var encre = regexp.MustCompile(`^ENC\[AES256_GCM,data:(.+),iv:(.+),tag:(.+),type:(.+)\]`)
func parse(value string) (*EncryptedValue, error) {
func parse(value string) (*encryptedValue, error) {
matches := encre.FindStringSubmatch(value)
if matches == nil {
return nil, fmt.Errorf("Input string %s does not match sops' data format", value)
@ -38,7 +38,7 @@ func parse(value string) (*EncryptedValue, error) {
}
datatype := string(matches[4])
return &EncryptedValue{data, iv, tag, datatype}, nil
return &encryptedValue{data, iv, tag, datatype}, nil
}
// Decrypt takes a sops-format value string and a key and returns the decrypted value.
@ -79,6 +79,7 @@ func Decrypt(value string, key []byte, additionalAuthData []byte) (interface{},
}
}
// Encrypt takes one of (string, int, float, bool) and encrypts it with the provided key and additional auth data, returning a sops-format encrypted string.
func Encrypt(value interface{}, key []byte, additionalAuthData []byte) (string, error) {
aescipher, err := cryptoaes.NewCipher(key)
if err != nil {

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

@ -6,13 +6,16 @@ import (
"go.mozilla.org/sops"
"go.mozilla.org/sops/kms"
"go.mozilla.org/sops/pgp"
"strconv"
"time"
)
type JSONStore struct {
// Store handles storage of JSON data. It's not finished yet, and therefore you should not use it.
type Store struct {
}
func (store JSONStore) Load(in string) (sops.TreeBranch, error) {
// Load takes an input json string and returns a sops tree branch
func (store Store) Load(in string) (sops.TreeBranch, error) {
var branch sops.TreeBranch
err := json.Unmarshal([]byte(in), branch)
if err != nil {
@ -26,7 +29,8 @@ func (store JSONStore) Load(in string) (sops.TreeBranch, error) {
return branch, nil
}
func (store JSONStore) Dump(tree sops.TreeBranch) (string, error) {
// Dump performs the opposite operation to Load, it takes a sops tree branch and returns a json formatted string
func (store Store) Dump(tree sops.TreeBranch) (string, error) {
out, err := json.Marshal(tree)
if err != nil {
return "", fmt.Errorf("Error marshaling to json: %s", err)
@ -34,7 +38,8 @@ func (store JSONStore) Dump(tree sops.TreeBranch) (string, error) {
return string(out), nil
}
func (store JSONStore) DumpWithMetadata(tree sops.TreeBranch, metadata sops.Metadata) (string, error) {
// DumpWithMetadata takes a sops tree branch and sops metadata and marshals them to json.
func (store Store) DumpWithMetadata(tree sops.TreeBranch, metadata sops.Metadata) (string, error) {
tree = append(tree, sops.TreeItem{Key: "sops", Value: metadata.ToMap()})
out, err := json.Marshal(tree)
if err != nil {
@ -43,7 +48,9 @@ func (store JSONStore) DumpWithMetadata(tree sops.TreeBranch, metadata sops.Meta
return string(out), nil
}
func (store JSONStore) LoadMetadata(in string) (sops.Metadata, error) {
// LoadMetadata takes a json string and extracts sops' metadata from it
func (store Store) LoadMetadata(in string) (sops.Metadata, error) {
var ok bool
var metadata sops.Metadata
data := make(map[string]interface{})
if err := json.Unmarshal([]byte(in), &data); err != nil {
@ -51,7 +58,7 @@ func (store JSONStore) LoadMetadata(in string) (sops.Metadata, error) {
}
data = data["sops"].(map[string]interface{})
metadata.MessageAuthenticationCode = data["mac"].(string)
lastModified, err := time.Parse(sops.DateFormat, data["lastmodified"].(string))
lastModified, err := time.Parse(time.RFC3339, data["lastmodified"].(string))
if err != nil {
return metadata, fmt.Errorf("Could not parse last modified date: %s", err)
}
@ -77,7 +84,7 @@ func (store JSONStore) LoadMetadata(in string) (sops.Metadata, error) {
return metadata, nil
}
func (store JSONStore) kmsEntries(in []interface{}) (sops.KeySource, error) {
func (store Store) kmsEntries(in []interface{}) (sops.KeySource, error) {
var keys []sops.MasterKey
keysource := sops.KeySource{Name: "kms", Keys: keys}
for _, v := range in {
@ -89,7 +96,7 @@ func (store JSONStore) kmsEntries(in []interface{}) (sops.KeySource, error) {
if ok {
key.Role = role
}
creationDate, err := time.Parse(sops.DateFormat, entry["created_at"].(string))
creationDate, err := time.Parse(time.RFC3339, entry["created_at"].(string))
if err != nil {
return keysource, fmt.Errorf("Could not parse creation date: %s", err)
}
@ -99,7 +106,7 @@ func (store JSONStore) kmsEntries(in []interface{}) (sops.KeySource, error) {
return keysource, nil
}
func (store JSONStore) pgpEntries(in []interface{}) (sops.KeySource, error) {
func (store Store) pgpEntries(in []interface{}) (sops.KeySource, error) {
var keys []sops.MasterKey
keysource := sops.KeySource{Name: "pgp", Keys: keys}
for _, v := range in {
@ -107,7 +114,7 @@ func (store JSONStore) pgpEntries(in []interface{}) (sops.KeySource, error) {
key := &pgp.MasterKey{}
key.Fingerprint = entry["fp"].(string)
key.EncryptedKey = entry["enc"].(string)
creationDate, err := time.Parse(sops.DateFormat, entry["created_at"].(string))
creationDate, err := time.Parse(time.RFC3339, entry["created_at"].(string))
if err != nil {
return keysource, fmt.Errorf("Could not parse creation date: %s", err)
}

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

@ -14,6 +14,7 @@ import (
"time"
)
// MasterKey is a AWS KMS key used to encrypt and decrypt sops' data key.
type MasterKey struct {
Arn string
Role string
@ -21,6 +22,7 @@ type MasterKey struct {
CreationDate time.Time
}
// Encrypt takes a sops data key, encrypts it with KMS and stores the result in the EncryptedKey field
func (key *MasterKey) Encrypt(dataKey []byte) error {
sess, err := key.createSession()
if err != nil {
@ -35,6 +37,7 @@ func (key *MasterKey) Encrypt(dataKey []byte) error {
return nil
}
// EncryptIfNeeded encrypts the provided sops' data ket and encrypts it if it hasn't been encrypted yet
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
if key.EncryptedKey == "" {
return key.Encrypt(dataKey)
@ -42,6 +45,7 @@ func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
return nil
}
// Decrypt decrypts the EncryptedKey field with AWS KMS and returns the result.
func (key *MasterKey) Decrypt() ([]byte, error) {
k, err := base64.StdEncoding.DecodeString(key.EncryptedKey)
if err != nil {
@ -60,14 +64,17 @@ func (key *MasterKey) Decrypt() ([]byte, error) {
return decrypted.Plaintext, nil
}
// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
return time.Since(key.CreationDate) > (time.Hour * 24 * 30 * 6)
}
// ToString converts the key to a string representation
func (key *MasterKey) ToString() string {
return key.Arn
}
// NewMasterKeyFromArn takes an ARN string and returns a new MasterKey for that ARN
func NewMasterKeyFromArn(arn string) MasterKey {
k := MasterKey{}
arn = strings.Replace(arn, " ", "", -1)
@ -82,6 +89,7 @@ func NewMasterKeyFromArn(arn string) MasterKey {
return k
}
// MasterKeysFromArnString takes a comma separated list of AWS KMS ARNs and returns a slice of new MasterKeys for those ARNs
func MasterKeysFromArnString(arn string) []MasterKey {
var keys []MasterKey
if arn == "" {
@ -93,7 +101,7 @@ func MasterKeysFromArnString(arn string) []MasterKey {
return keys
}
func (k MasterKey) createStsSession(config aws.Config, sess *session.Session) (*session.Session, error) {
func (key MasterKey) createStsSession(config aws.Config, sess *session.Session) (*session.Session, error) {
hostname, err := os.Hostname()
if err != nil {
return nil, err
@ -101,7 +109,7 @@ func (k MasterKey) createStsSession(config aws.Config, sess *session.Session) (*
stsService := sts.New(sess)
name := "sops@" + hostname
out, err := stsService.AssumeRole(&sts.AssumeRoleInput{
RoleArn: &k.Role, RoleSessionName: &name})
RoleArn: &key.Role, RoleSessionName: &name})
if err != nil {
return nil, err
}
@ -114,30 +122,31 @@ func (k MasterKey) createStsSession(config aws.Config, sess *session.Session) (*
return sess, nil
}
func (k MasterKey) createSession() (*session.Session, error) {
func (key MasterKey) createSession() (*session.Session, error) {
re := regexp.MustCompile(`^arn:aws:kms:(.+):([0-9]+):key/(.+)$`)
matches := re.FindStringSubmatch(k.Arn)
matches := re.FindStringSubmatch(key.Arn)
if matches == nil {
return nil, fmt.Errorf("No valid ARN found in %s", k.Arn)
return nil, fmt.Errorf("No valid ARN found in %s", key.Arn)
}
config := aws.Config{Region: aws.String(matches[1])}
sess, err := session.NewSession(&config)
if err != nil {
return nil, err
}
if k.Role != "" {
return k.createStsSession(config, sess)
if key.Role != "" {
return key.createStsSession(config, sess)
}
return sess, nil
}
func (k MasterKey) ToMap() map[string]string {
// ToMap converts the MasterKey to a map for serialization purposes
func (key MasterKey) ToMap() map[string]string {
out := make(map[string]string)
out["arn"] = k.Arn
if k.Role != "" {
out["role"] = k.Role
out["arn"] = key.Arn
if key.Role != "" {
out["role"] = key.Role
}
out["created_at"] = k.CreationDate.UTC().Format(time.RFC3339)
out["enc"] = k.EncryptedKey
out["created_at"] = key.CreationDate.UTC().Format(time.RFC3339)
out["enc"] = key.EncryptedKey
return out
}

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

@ -131,8 +131,6 @@ func main() {
return decrypt(c, file, fileBytes, output)
} else if c.Bool("rotate") {
return rotate(c, file, fileBytes, output)
} else {
}
return nil
}

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

@ -16,12 +16,14 @@ import (
"time"
)
// MasterKey is a PGP key used to securely store sops' data key by encrypting it and decrypting it
type MasterKey struct {
Fingerprint string
EncryptedKey string
CreationDate time.Time
}
// Encrypt encrypts the data key with the PGP key with the same fingerprint as the MasterKey. It looks for PGP public keys in $PGPHOME/pubring.gpg.
func (key *MasterKey) Encrypt(dataKey []byte) error {
ring, err := key.pubRing()
if err != nil {
@ -61,6 +63,7 @@ func (key *MasterKey) Encrypt(dataKey []byte) error {
return nil
}
// EncryptIfNeeded encrypts the data key with PGP only if it's needed, that is, if it hasn't been encrypted already
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
if key.EncryptedKey == "" {
return key.Encrypt(dataKey)
@ -68,6 +71,7 @@ func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
return nil
}
// Decrypt uses PGP to obtain the data key from the EncryptedKey store in the MasterKey and returns it
func (key *MasterKey) Decrypt() ([]byte, error) {
ring, err := key.secRing()
if err != nil {
@ -87,10 +91,12 @@ func (key *MasterKey) Decrypt() ([]byte, error) {
return nil, fmt.Errorf("The key could not be decrypted with any of the GPG entries")
}
// NeedsRotation returns whether the data key needs to be rotated or not
func (key *MasterKey) NeedsRotation() bool {
return time.Since(key.CreationDate).Hours() > 24*30*6
}
// ToString returns the string representation of the key, i.e. its fingerprint
func (key *MasterKey) ToString() string {
return key.Fingerprint
}
@ -107,6 +113,7 @@ func (key *MasterKey) gpgHome() string {
return dir
}
// NewMasterKeyFromFingerprint takes a PGP fingerprint and returns a new MasterKey with that fingerprint
func NewMasterKeyFromFingerprint(fingerprint string) MasterKey {
return MasterKey{
Fingerprint: strings.Replace(fingerprint, " ", "", -1),
@ -114,6 +121,7 @@ func NewMasterKeyFromFingerprint(fingerprint string) MasterKey {
}
}
// MasterKeysFromFingerprintString takes a comma separated list of PGP fingerprints and returns a slice of new MasterKeys with those fingerprints
func MasterKeysFromFingerprintString(fingerprint string) []MasterKey {
var keys []MasterKey
if fingerprint == "" {
@ -191,6 +199,7 @@ func (key *MasterKey) passphrasePrompt(keys []openpgp.Key, symmetric bool) ([]by
return nil, fmt.Errorf("No key to unlock")
}
// ToMap converts the MasterKey into a map for serialization purposes
func (key MasterKey) ToMap() map[string]string {
out := make(map[string]string)
out["fp"] = key.Fingerprint

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

@ -11,27 +11,32 @@ import (
"time"
)
// DefaultUnencryptedSuffix is the default suffix a TreeItem key has to end with for sops to leave its Value unencrypted
const DefaultUnencryptedSuffix = "_unencrypted"
type Error string
type sopsError string
func (e Error) Error() string { return string(e) }
func (e sopsError) Error() string { return string(e) }
const MacMismatch = Error("MAC mismatch")
// MacMismatch occurs when the computed MAC does not match the expected ones
const MacMismatch = sopsError("MAC mismatch")
// TreeItem is an item inside sops's tree
type TreeItem struct {
Key string
Value interface{}
}
// TreeBranch is a branch inside sops's tree. It is a slice of TreeItems and is therefore ordered
type TreeBranch []TreeItem
// Tree is the data structure used by sops to represent documents internally
type Tree struct {
Branch TreeBranch
Metadata Metadata
}
func (tree TreeBranch) WalkValue(in interface{}, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (interface{}, error) {
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)
@ -40,17 +45,17 @@ func (tree TreeBranch) WalkValue(in interface{}, path []string, onLeaves func(in
case bool:
return onLeaves(in, path)
case TreeBranch:
return tree.WalkBranch(in, path, onLeaves)
return tree.walkBranch(in, path, onLeaves)
case []interface{}:
return tree.WalkSlice(in, path, onLeaves)
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) {
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)
newV, err := tree.walkValue(v, path, onLeaves)
if err != nil {
return nil, err
}
@ -59,9 +64,9 @@ func (tree TreeBranch) WalkSlice(in []interface{}, path []string, onLeaves func(
return in, nil
}
func (tree TreeBranch) WalkBranch(in TreeBranch, path []string, onLeaves func(in interface{}, path []string) (interface{}, error)) (TreeBranch, error) {
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)
newV, err := tree.walkValue(item.Value, append(path, item.Key), onLeaves)
if err != nil {
return nil, err
}
@ -70,9 +75,10 @@ func (tree TreeBranch) WalkBranch(in TreeBranch, path []string, onLeaves func(in
return in, nil
}
// Encrypt walks over the tree and encrypts all values, 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.
func (tree Tree) Encrypt(key []byte) (string, error) {
hash := sha512.New()
_, err := tree.Branch.WalkBranch(tree.Branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
_, err := tree.Branch.walkBranch(tree.Branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
bytes, err := toBytes(in)
if !strings.HasSuffix(path[len(path)-1], tree.Metadata.UnencryptedSuffix) {
var err error
@ -93,9 +99,10 @@ func (tree Tree) Encrypt(key []byte) (string, error) {
return fmt.Sprintf("%X", hash.Sum(nil)), nil
}
// Decrypt walks over the tree and decrypts all values, 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.
func (tree Tree) Decrypt(key []byte) (string, error) {
hash := sha512.New()
_, err := tree.Branch.WalkBranch(tree.Branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
_, err := tree.Branch.walkBranch(tree.Branch, make([]string, 0), func(in interface{}, path []string) (interface{}, error) {
var v interface{}
if !strings.HasSuffix(path[len(path)-1], tree.Metadata.UnencryptedSuffix) {
var err error
@ -120,6 +127,7 @@ func (tree Tree) Decrypt(key []byte) (string, error) {
}
// Metadata holds information about a file encrypted by sops
type Metadata struct {
LastModified time.Time
UnencryptedSuffix string
@ -128,11 +136,13 @@ type Metadata struct {
KeySources []KeySource
}
// KeySource is a collection of MasterKeys with a Name.
type KeySource struct {
Name string
Keys []MasterKey
}
// MasterKey provides a way of securing the key used to encrypt the Tree by encrypting and decrypting said key.
type MasterKey interface {
Encrypt(dataKey []byte) error
EncryptIfNeeded(dataKey []byte) error
@ -142,6 +152,7 @@ type MasterKey interface {
ToMap() map[string]string
}
// Store provides a way to load and save the sops tree along with metadata
type Store interface {
Load(in string) (TreeBranch, error)
LoadMetadata(in string) (Metadata, error)
@ -149,6 +160,7 @@ type Store interface {
DumpWithMetadata(TreeBranch, Metadata) (string, error)
}
// MasterKeyCount returns the number of master keys available
func (m *Metadata) MasterKeyCount() int {
count := 0
for _, ks := range m.KeySources {
@ -156,6 +168,8 @@ func (m *Metadata) MasterKeyCount() int {
}
return count
}
// RemoveMasterKeys removes all of the provided keys from the metadata's KeySources, if they exist there.
func (m *Metadata) RemoveMasterKeys(keys []MasterKey) {
for j, ks := range m.KeySources {
for i, k := range ks.Keys {
@ -169,6 +183,7 @@ func (m *Metadata) RemoveMasterKeys(keys []MasterKey) {
}
}
// UpdateMasterKeys encrypts the data key with all master keys if it's needed
func (m *Metadata) UpdateMasterKeys(dataKey []byte) {
for _, ks := range m.KeySources {
for _, k := range ks.Keys {
@ -180,8 +195,9 @@ func (m *Metadata) UpdateMasterKeys(dataKey []byte) {
}
}
func (metadata *Metadata) AddPGPMasterKeys(pgpFps string) {
for i, ks := range metadata.KeySources {
// AddPGPMasterKeys parses the input comma separated string of GPG fingerprints, generates a PGP MasterKey for each fingerprint, and adds the keys to the PGP KeySource
func (m *Metadata) AddPGPMasterKeys(pgpFps string) {
for i, ks := range m.KeySources {
if ks.Name == "pgp" {
var keys []MasterKey
for _, k := range pgp.MasterKeysFromFingerprintString(pgpFps) {
@ -189,48 +205,52 @@ func (metadata *Metadata) AddPGPMasterKeys(pgpFps string) {
fmt.Println("Keys to add:", keys)
}
ks.Keys = append(ks.Keys, keys...)
metadata.KeySources[i] = ks
m.KeySources[i] = ks
}
}
}
func (metadata *Metadata) AddKMSMasterKeys(kmsArns string) {
for i, ks := range metadata.KeySources {
// AddKMSMasterKeys parses the input comma separated string of AWS KMS ARNs, generates a KMS MasterKey for each ARN, and then adds the keys to the KMS KeySource
func (m *Metadata) AddKMSMasterKeys(kmsArns string) {
for i, ks := range m.KeySources {
if ks.Name == "kms" {
var keys []MasterKey
for _, k := range kms.MasterKeysFromArnString(kmsArns) {
keys = append(keys, &k)
}
ks.Keys = append(ks.Keys, keys...)
metadata.KeySources[i] = ks
m.KeySources[i] = ks
}
}
}
func (metadata *Metadata) RemovePGPMasterKeys(pgpFps string) {
// RemovePGPMasterKeys takes a comma separated string of PGP fingerprints and removes the keys corresponding to those fingerprints from the metadata's KeySources
func (m *Metadata) RemovePGPMasterKeys(pgpFps string) {
var keys []MasterKey
for _, k := range pgp.MasterKeysFromFingerprintString(pgpFps) {
keys = append(keys, &k)
}
metadata.RemoveMasterKeys(keys)
m.RemoveMasterKeys(keys)
}
func (metadata *Metadata) RemoveKMSMasterKeys(arns string) {
// RemoveKMSMasterKeys takes a comma separated string of AWS KMS ARNs and removes the keys corresponding to those ARNs from the metadata's KeySources
func (m *Metadata) RemoveKMSMasterKeys(arns string) {
var keys []MasterKey
for _, k := range kms.MasterKeysFromArnString(arns) {
keys = append(keys, &k)
}
metadata.RemoveMasterKeys(keys)
m.RemoveMasterKeys(keys)
}
// ToMap converts the Metadata to a map for serialization purposes
func (m *Metadata) ToMap() map[string]interface{} {
out := make(map[string]interface{})
out["lastmodified"] = m.LastModified.Format("2006-01-02T15:04:05Z")
out["lastmodified"] = m.LastModified.Format(time.RFC3339)
out["unencrypted_suffix"] = m.UnencryptedSuffix
out["mac"] = m.MessageAuthenticationCode
out["version"] = m.Version
for _, ks := range m.KeySources {
keys := make([]map[string]string, 0)
var keys []map[string]string
for _, k := range ks.Keys {
keys = append(keys, k.ToMap())
}

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

@ -10,6 +10,7 @@ import (
"time"
)
// Store handles storage of YAML data
type Store struct {
}
@ -24,6 +25,7 @@ func (store Store) mapSliceToTreeBranch(in yaml.MapSlice) sops.TreeBranch {
return branch
}
// Load takes a YAML document as input and unmarshals it into a sops tree, returning the tree
func (store Store) Load(in string) (sops.TreeBranch, error) {
var data yaml.MapSlice
if err := yaml.Unmarshal([]byte(in), &data); err != nil {
@ -88,6 +90,7 @@ func (store Store) treeBranchToYamlMap(in sops.TreeBranch) yaml.MapSlice {
return branch
}
// Dump takes a sops tree branch and marshals it into a yaml document
func (store Store) Dump(tree sops.TreeBranch) (string, error) {
yamlMap := store.treeBranchToYamlMap(tree)
out, err := yaml.Marshal(yamlMap)
@ -97,6 +100,7 @@ func (store Store) Dump(tree sops.TreeBranch) (string, error) {
return string(out), nil
}
// DumpWithMetadata takes a sops tree branch and metadata and marshals them into a yaml document
func (store Store) DumpWithMetadata(tree sops.TreeBranch, metadata sops.Metadata) (string, error) {
yamlMap := store.treeBranchToYamlMap(tree)
yamlMap = append(yamlMap, yaml.MapItem{Key: "sops", Value: metadata.ToMap()})
@ -107,6 +111,7 @@ func (store Store) DumpWithMetadata(tree sops.TreeBranch, metadata sops.Metadata
return string(out), nil
}
// LoadMetadata takes a yaml document as a string and extracts sops' metadata from it
func (store *Store) LoadMetadata(in string) (sops.Metadata, error) {
var metadata sops.Metadata
var ok bool