1
0
mirror of https://github.com/getsops/sops.git synced 2026-02-05 12:45:21 +01:00
Files
sops/stores/stores.go

248 lines
7.9 KiB
Go
Raw Normal View History

2017-09-12 20:01:12 -07:00
/*
Package stores acts as a layer between the internal representation of encrypted files and the encrypted files
themselves.
Subpackages implement serialization and deserialization to multiple formats.
This package defines the structure SOPS files should have and conversions to and from the internal representation. Part
of the purpose of this package is to make it easy to change the SOPS file format while remaining backwards-compatible.
*/
package stores
import (
"time"
"fmt"
"go.mozilla.org/sops"
2017-09-18 12:22:36 +03:00
"go.mozilla.org/sops/gcpkms"
"go.mozilla.org/sops/kms"
"go.mozilla.org/sops/pgp"
)
2017-09-12 20:01:12 -07:00
// SopsFile is a struct used by the stores as a helper to unmarshal the SOPS metadata
type SopsFile struct {
2017-09-12 20:01:12 -07:00
Metadata Metadata `yaml:"sops" json:"sops"`
}
2017-09-12 09:59:23 -07:00
// Metadata is stored in SOPS encrypted files, and it contains the information necessary to decrypt the file.
// This struct is just used for serialization, and SOPS uses another struct internally, sops.Metadata. It exists
// in order to allow the binary format to stay backwards compatible over time, but at the same time allow the internal
// representation SOPS uses to change over time.
type Metadata struct {
2017-09-18 12:22:36 +03:00
ShamirThreshold int `yaml:"shamir_threshold,omitempty" json:"shamir_threshold,omitempty"`
KeyGroups []keygroup `yaml:"key_groups,omitempty" json:"key_groups,omitempty"`
KMSKeys []kmskey `yaml:"kms" json:"kms"`
GCPKMSKeys []gcpkmskey `yaml:"gcp_kms" json:"gcp_kms"`
LastModified string `yaml:"lastmodified" json:"lastmodified"`
MessageAuthenticationCode string `yaml:"mac" json:"mac"`
PGPKeys []pgpkey `yaml:"pgp" json:"pgp"`
UnencryptedSuffix string `yaml:"unencrypted_suffix" json:"unencrypted_suffix"`
Version string `yaml:"version" json:"version"`
}
type keygroup struct {
2017-09-18 12:22:36 +03:00
PGPKeys []pgpkey `yaml:"pgp,omitempty" json:"pgp,omitempty"`
KMSKeys []kmskey `yaml:"kms,omitempty" json:"kms,omitempty"`
GCPKMSKeys []gcpkmskey `yaml:"gcp_kms,omitempty" json:"gcp_kms,omitempty"`
}
type pgpkey struct {
CreatedAt string `yaml:"created_at" json:"created_at"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
Fingerprint string `yaml:"fp" json:"fp"`
}
type kmskey struct {
Arn string `yaml:"arn" json:"arn"`
Role string `yaml:"role,omitempty" json:"role,omitempty"`
Context map[string]*string `yaml:"context,omitempty" json:"context,omitempty"`
CreatedAt string `yaml:"created_at" json:"created_at"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
}
2017-09-18 12:22:36 +03:00
type gcpkmskey struct {
ResourceID string `yaml:"resource_id" json:"resource_id"`
CreatedAt string `yaml:"created_at" json:"created_at"`
EncryptedDataKey string `yaml:"enc" json:"enc"`
}
2017-09-12 09:59:23 -07:00
// MetadataFromInternal converts an internal SOPS metadata representation to a representation appropriate for storage
func MetadataFromInternal(sopsMetadata sops.Metadata) Metadata {
var m Metadata
m.LastModified = sopsMetadata.LastModified.Format(time.RFC3339)
m.UnencryptedSuffix = sopsMetadata.UnencryptedSuffix
m.MessageAuthenticationCode = sopsMetadata.MessageAuthenticationCode
m.Version = sopsMetadata.Version
m.ShamirThreshold = sopsMetadata.ShamirThreshold
if len(sopsMetadata.KeyGroups) == 1 {
group := sopsMetadata.KeyGroups[0]
m.PGPKeys = pgpKeysFromGroup(group)
m.KMSKeys = kmsKeysFromGroup(group)
2017-09-18 12:22:36 +03:00
m.GCPKMSKeys = gcpkmsKeysFromGroup(group)
} else {
for _, group := range sopsMetadata.KeyGroups {
m.KeyGroups = append(m.KeyGroups, keygroup{
2017-09-18 12:22:36 +03:00
KMSKeys: kmsKeysFromGroup(group),
PGPKeys: pgpKeysFromGroup(group),
GCPKMSKeys: gcpkmsKeysFromGroup(group),
})
}
}
return m
}
func pgpKeysFromGroup(group sops.KeyGroup) (keys []pgpkey) {
for _, key := range group {
switch key := key.(type) {
case *pgp.MasterKey:
keys = append(keys, pgpkey{
Fingerprint: key.Fingerprint,
EncryptedDataKey: key.EncryptedKey,
CreatedAt: key.CreationDate.Format(time.RFC3339),
})
}
}
return
}
func kmsKeysFromGroup(group sops.KeyGroup) (keys []kmskey) {
for _, key := range group {
switch key := key.(type) {
case *kms.MasterKey:
keys = append(keys, kmskey{
Arn: key.Arn,
CreatedAt: key.CreationDate.Format(time.RFC3339),
EncryptedDataKey: key.EncryptedKey,
Context: key.EncryptionContext,
Role: key.Role,
})
}
}
return
}
2017-09-18 12:22:36 +03:00
func gcpkmsKeysFromGroup(group sops.KeyGroup) (keys []gcpkmskey) {
for _, key := range group {
switch key := key.(type) {
case *gcpkms.MasterKey:
keys = append(keys, gcpkmskey{
ResourceID: key.ResourceID,
CreatedAt: key.CreationDate.Format(time.RFC3339),
EncryptedDataKey: key.EncryptedKey,
})
}
}
return
}
2017-09-12 09:59:23 -07:00
// ToInternal converts a storage-appropriate Metadata struct to a SOPS internal representation
func (m *Metadata) ToInternal() (sops.Metadata, error) {
lastModified, err := time.Parse(time.RFC3339, m.LastModified)
if err != nil {
return sops.Metadata{}, err
}
groups, err := m.internalKeygroups()
if err != nil {
return sops.Metadata{}, err
}
if m.UnencryptedSuffix == "" {
m.UnencryptedSuffix = sops.DefaultUnencryptedSuffix
}
return sops.Metadata{
KeyGroups: groups,
ShamirThreshold: m.ShamirThreshold,
Version: m.Version,
MessageAuthenticationCode: m.MessageAuthenticationCode,
UnencryptedSuffix: m.UnencryptedSuffix,
LastModified: lastModified,
}, nil
}
2017-09-18 12:22:36 +03:00
func internalGroupFrom(kmsKeys []kmskey, pgpKeys []pgpkey, gcpKmsKeys []gcpkmskey) (sops.KeyGroup, error) {
var internalGroup sops.KeyGroup
for _, kmsKey := range kmsKeys {
k, err := kmsKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
2017-09-18 12:22:36 +03:00
for _, gcpKmsKey := range gcpKmsKeys {
k, err := gcpKmsKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
for _, pgpKey := range pgpKeys {
k, err := pgpKey.toInternal()
if err != nil {
return nil, err
}
internalGroup = append(internalGroup, k)
}
return internalGroup, nil
}
func (m *Metadata) internalKeygroups() ([]sops.KeyGroup, error) {
var internalGroups []sops.KeyGroup
2017-09-18 12:22:36 +03:00
if len(m.PGPKeys) > 0 || len(m.KMSKeys) > 0 || len(m.GCPKMSKeys) > 0 {
internalGroup, err := internalGroupFrom(m.KMSKeys, m.PGPKeys, m.GCPKMSKeys)
if err != nil {
return nil, err
}
internalGroups = append(internalGroups, internalGroup)
return internalGroups, nil
} else if len(m.KeyGroups) > 0 {
for _, group := range m.KeyGroups {
2017-09-18 12:22:36 +03:00
internalGroup, err := internalGroupFrom(group.KMSKeys, group.PGPKeys, group.GCPKMSKeys)
if err != nil {
return nil, err
}
internalGroups = append(internalGroups, internalGroup)
}
return internalGroups, nil
} else {
return nil, fmt.Errorf("No keys found in file")
}
}
func (kmsKey *kmskey) toInternal() (*kms.MasterKey, error) {
creationDate, err := time.Parse(time.RFC3339, kmsKey.CreatedAt)
if err != nil {
return nil, err
}
return &kms.MasterKey{
Role: kmsKey.Role,
EncryptionContext: kmsKey.Context,
EncryptedKey: kmsKey.EncryptedDataKey,
CreationDate: creationDate,
Arn: kmsKey.Arn,
}, nil
}
2017-09-18 12:22:36 +03:00
func (gcpKmsKey *gcpkmskey) toInternal() (*gcpkms.MasterKey, error) {
creationDate, err := time.Parse(time.RFC3339, gcpKmsKey.CreatedAt)
if err != nil {
return nil, err
}
return &gcpkms.MasterKey{
ResourceID: gcpKmsKey.ResourceID,
EncryptedKey: gcpKmsKey.EncryptedDataKey,
CreationDate: creationDate,
}, nil
}
func (pgpKey *pgpkey) toInternal() (*pgp.MasterKey, error) {
creationDate, err := time.Parse(time.RFC3339, pgpKey.CreatedAt)
if err != nil {
return nil, err
}
return &pgp.MasterKey{
EncryptedKey: pgpKey.EncryptedDataKey,
CreationDate: creationDate,
Fingerprint: pgpKey.Fingerprint,
}, nil
}