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.
|
|
|
|
|
*/
|
2017-08-23 16:36:49 -07:00
|
|
|
package stores
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"go.mozilla.org/sops"
|
2017-09-18 12:22:36 +03:00
|
|
|
"go.mozilla.org/sops/gcpkms"
|
2017-08-23 16:36:49 -07:00
|
|
|
"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
|
2017-08-23 16:36:49 -07:00
|
|
|
type SopsFile struct {
|
2017-09-12 20:01:12 -07:00
|
|
|
Metadata Metadata `yaml:"sops" json:"sops"`
|
2017-08-23 16:36:49 -07:00
|
|
|
}
|
|
|
|
|
|
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.
|
2017-08-23 16:36:49 -07:00
|
|
|
// 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"`
|
2017-08-23 16:36:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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"`
|
2017-08-23 16:36:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2017-09-11 13:08:21 -07:00
|
|
|
Arn string `yaml:"arn" json:"arn"`
|
|
|
|
|
Role string `yaml:"role,omitempty" json:"role,omitempty"`
|
|
|
|
|
Context map[string]*string `yaml:"context,omitempty" json:"context,omitempty"`
|
2017-08-23 16:36:49 -07:00
|
|
|
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
|
2017-08-23 16:36:49 -07:00
|
|
|
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
|
2017-09-12 10:58:53 -07:00
|
|
|
m.ShamirThreshold = sopsMetadata.ShamirThreshold
|
2017-08-23 16:36:49 -07:00
|
|
|
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)
|
2017-08-23 16:36:49 -07:00
|
|
|
} 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),
|
2017-08-23 16:36:49 -07:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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
|
2017-08-29 12:16:00 -07:00
|
|
|
func (m *Metadata) ToInternal() (sops.Metadata, error) {
|
2017-08-23 16:36:49 -07:00
|
|
|
lastModified, err := time.Parse(time.RFC3339, m.LastModified)
|
|
|
|
|
if err != nil {
|
2017-08-29 12:16:00 -07:00
|
|
|
return sops.Metadata{}, err
|
2017-08-23 16:36:49 -07:00
|
|
|
}
|
|
|
|
|
groups, err := m.internalKeygroups()
|
|
|
|
|
if err != nil {
|
2017-08-29 12:16:00 -07:00
|
|
|
return sops.Metadata{}, err
|
2017-08-23 16:36:49 -07:00
|
|
|
}
|
|
|
|
|
if m.UnencryptedSuffix == "" {
|
|
|
|
|
m.UnencryptedSuffix = sops.DefaultUnencryptedSuffix
|
|
|
|
|
}
|
2017-08-29 12:16:00 -07:00
|
|
|
return sops.Metadata{
|
2017-08-23 16:36:49 -07:00
|
|
|
KeyGroups: groups,
|
2017-09-12 10:58:53 -07:00
|
|
|
ShamirThreshold: m.ShamirThreshold,
|
2017-08-23 16:36:49 -07:00
|
|
|
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) {
|
2017-08-23 16:36:49 -07:00
|
|
|
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)
|
|
|
|
|
}
|
2017-08-23 16:36:49 -07:00
|
|
|
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)
|
2017-08-23 16:36:49 -07:00
|
|
|
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)
|
2017-08-23 16:36:49 -07:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-23 16:36:49 -07:00
|
|
|
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
|
|
|
|
|
}
|