Skip to content

Commit

Permalink
Sort masterkeys according to decryption-order
Browse files Browse the repository at this point in the history
Signed-off-by: Boris Kreitchman <bkreitch@gmail.com>
  • Loading branch information
bkreitch committed Nov 8, 2023
1 parent 73ec51f commit cba9b72
Show file tree
Hide file tree
Showing 17 changed files with 288 additions and 139 deletions.
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ Given that, the only command a SOPS user needs is:
encrypted if modified, and saved back to its original location. All of these
steps, apart from the actual editing, are transparent to the user.

Order in which availible decryption methods are tried can by specified by ``--decryption-order`` option
or **SOPS_DECRYPTION_ORDER** environment variable as comma separated list.
Default order is ``age,pgp`` - meaning offline methods are tried first.

Test with the dev PGP key
~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
2 changes: 2 additions & 0 deletions cmd/sops/codes/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const (
NoFileSpecified int = 100
CouldNotRetrieveKey int = 128
NoEncryptionKeyFound int = 111
InvalidDecryptionKeyType int = 112
DupicateDecryptionKeyType int = 113
FileHasNotBeenModified int = 200
NoEditorFound int = 201
FailedToCompareVersions int = 202
Expand Down
16 changes: 9 additions & 7 deletions cmd/sops/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ type DecryptTreeOpts struct {
// Tree is the tree to be decrypted
Tree *sops.Tree
// KeyServices are the key services to be used for decryption of the data key
KeyServices []keyservice.KeyServiceClient
KeyServices []keyservice.KeyServiceClient
DecryptionOrder []string
// IgnoreMac is whether or not to ignore the Message Authentication Code included in the SOPS tree
IgnoreMac bool
// Cipher is the cryptographic cipher to use to decrypt the values inside the tree
Expand All @@ -79,7 +80,7 @@ type DecryptTreeOpts struct {

// DecryptTree decrypts the tree passed in through the DecryptTreeOpts and additionally returns the decrypted data key
func DecryptTree(opts DecryptTreeOpts) (dataKey []byte, err error) {
dataKey, err = opts.Tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices)
dataKey, err = opts.Tree.Metadata.GetDataKeyWithKeyServices(opts.KeyServices, opts.DecryptionOrder)
if err != nil {
return nil, NewExitError(err, codes.CouldNotRetrieveKey)
}
Expand Down Expand Up @@ -221,11 +222,12 @@ func GetKMSKeyWithEncryptionCtx(tree *sops.Tree) (keyGroupIndex int, keyIndex in

// GenericDecryptOpts represents decryption options and config
type GenericDecryptOpts struct {
Cipher sops.Cipher
InputStore sops.Store
InputPath string
IgnoreMAC bool
KeyServices []keyservice.KeyServiceClient
Cipher sops.Cipher
InputStore sops.Store
InputPath string
IgnoreMAC bool
KeyServices []keyservice.KeyServiceClient
DecryptionOrder []string
}

// LoadEncryptedFileWithBugFixes is a wrapper around LoadEncryptedFile which includes
Expand Down
24 changes: 13 additions & 11 deletions cmd/sops/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ const notBinaryHint = ("This is likely not an encrypted binary file?" +
" If not, use --output-type to select the correct output type.")

type decryptOpts struct {
Cipher sops.Cipher
InputStore sops.Store
OutputStore sops.Store
InputPath string
IgnoreMAC bool
Extract []interface{}
KeyServices []keyservice.KeyServiceClient
Cipher sops.Cipher
InputStore sops.Store
OutputStore sops.Store
InputPath string
IgnoreMAC bool
Extract []interface{}
KeyServices []keyservice.KeyServiceClient
DecryptionOrder []string
}

func decrypt(opts decryptOpts) (decryptedFile []byte, err error) {
Expand All @@ -37,10 +38,11 @@ func decrypt(opts decryptOpts) (decryptedFile []byte, err error) {
}

_, err = common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher,
IgnoreMac: opts.IgnoreMAC,
Tree: tree,
KeyServices: opts.KeyServices,
Cipher: opts.Cipher,
IgnoreMac: opts.IgnoreMAC,
Tree: tree,
KeyServices: opts.KeyServices,
DecryptionOrder: opts.DecryptionOrder,
})
if err != nil {
return nil, err
Expand Down
21 changes: 13 additions & 8 deletions cmd/sops/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import (
)

type editOpts struct {
Cipher sops.Cipher
InputStore common.Store
OutputStore common.Store
InputPath string
IgnoreMAC bool
KeyServices []keyservice.KeyServiceClient
ShowMasterKeys bool
Cipher sops.Cipher
InputStore common.Store
OutputStore common.Store
InputPath string
IgnoreMAC bool
KeyServices []keyservice.KeyServiceClient
DecryptionOrder []string
ShowMasterKeys bool
}

type editExampleOpts struct {
Expand Down Expand Up @@ -94,7 +95,11 @@ func edit(opts editOpts) ([]byte, error) {
}
// Decrypt the file
dataKey, err := common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher, IgnoreMac: opts.IgnoreMAC, Tree: tree, KeyServices: opts.KeyServices,
Cipher: opts.Cipher,
IgnoreMac: opts.IgnoreMAC,
Tree: tree,
KeyServices: opts.KeyServices,
DecryptionOrder: opts.DecryptionOrder,
})
if err != nil {
return nil, err
Expand Down
134 changes: 91 additions & 43 deletions cmd/sops/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/getsops/sops/v3/version"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"golang.org/x/exp/slices"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
Expand Down Expand Up @@ -145,13 +146,19 @@ func main() {
inputStore := inputStore(c, fileName)

svcs := keyservices(c)

order, err := decryptionOrder(c.String("decryption-order"))
if err != nil {
return toExitError(err)
}
opts := decryptOpts{
OutputStore: &dotenv.Store{},
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
OutputStore: &dotenv.Store{},
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
DecryptionOrder: order,
IgnoreMAC: c.Bool("ignore-mac"),
}

output, err := decrypt(opts)
Expand Down Expand Up @@ -213,13 +220,19 @@ func main() {
outputStore := outputStore(c, fileName)

svcs := keyservices(c)

order, err := decryptionOrder(c.String("decryption-order"))
if err != nil {
return toExitError(err)
}
opts := decryptOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
DecryptionOrder: order,
IgnoreMAC: c.Bool("ignore-mac"),
}

output, err := decrypt(opts)
Expand Down Expand Up @@ -287,21 +300,25 @@ func main() {
if info.IsDir() && !c.Bool("recursive") {
return fmt.Errorf("can't operate on a directory without --recursive flag.")
}
order, err := decryptionOrder(c.String("decryption-order"))
if err != nil {
return toExitError(err)
}
err = filepath.Walk(path, func(subPath string, info os.FileInfo, err error) error {
if err != nil {
return toExitError(err)
}
if !info.IsDir() {
err = publishcmd.Run(publishcmd.Opts{
ConfigPath: configPath,
InputPath: subPath,
Cipher: aes.NewCipher(),
KeyServices: keyservices(c),
InputStore: inputStore(c, subPath),
Interactive: !c.Bool("yes"),
OmitExtensions: c.Bool("omit-extensions"),
Recursive: c.Bool("recursive"),
RootPath: path,
ConfigPath: configPath,
InputPath: subPath,
Cipher: aes.NewCipher(),
KeyServices: keyservices(c),
DecryptionOrder: order,
InputStore: inputStore(c, subPath),
Interactive: !c.Bool("yes"),
OmitExtensions: c.Bool("omit-extensions"),
Recursive: c.Bool("recursive"),
})
if cliErr, ok := err.(*cli.ExitError); ok && cliErr != nil {
return cliErr
Expand Down Expand Up @@ -708,6 +725,11 @@ func main() {
Name: "output",
Usage: "Save the output after encryption or decryption to the file specified",
},
cli.StringFlag{
Name: "decryption-order",
Usage: "comma separated list of decryption key types",
EnvVar: "SOPS_DECRYPTION_ORDER",
},
}, keyserviceFlags...)

app.Action = func(c *cli.Context) error {
Expand Down Expand Up @@ -785,6 +807,10 @@ func main() {
outputStore := outputStore(c, fileName)
svcs := keyservices(c)

order, err := decryptionOrder(c.String("decryption-order"))
if err != nil {
return toExitError(err)
}
var output []byte
if c.Bool("encrypt") {
var groups []sops.KeyGroup
Expand Down Expand Up @@ -819,13 +845,14 @@ func main() {
return common.NewExitError(fmt.Errorf("error parsing --extract path: %s", err), codes.InvalidTreePathFormat)
}
output, err = decrypt(decryptOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
Extract: extract,
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
Extract: extract,
KeyServices: svcs,
DecryptionOrder: order,
IgnoreMAC: c.Bool("ignore-mac"),
})
}
if c.Bool("rotate") {
Expand Down Expand Up @@ -900,6 +927,7 @@ func main() {
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
DecryptionOrder: order,
IgnoreMAC: c.Bool("ignore-mac"),
AddMasterKeys: addMasterKeys,
RemoveMasterKeys: rmMasterKeys,
Expand All @@ -919,14 +947,15 @@ func main() {
return toExitError(err)
}
output, err = set(setOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
Value: value,
TreePath: path,
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
DecryptionOrder: order,
IgnoreMAC: c.Bool("ignore-mac"),
Value: value,
TreePath: path,
})
}

Expand All @@ -935,13 +964,14 @@ func main() {
_, statErr := os.Stat(fileName)
fileExists := statErr == nil
opts := editOpts{
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
IgnoreMAC: c.Bool("ignore-mac"),
ShowMasterKeys: c.Bool("show-master-keys"),
OutputStore: outputStore,
InputStore: inputStore,
InputPath: fileName,
Cipher: aes.NewCipher(),
KeyServices: svcs,
DecryptionOrder: order,
IgnoreMAC: c.Bool("ignore-mac"),
ShowMasterKeys: c.Bool("show-master-keys"),
}
if fileExists {
output, err = edit(opts)
Expand Down Expand Up @@ -1251,3 +1281,21 @@ func extractSetArguments(set string) (path []interface{}, valueToInsert interfac
}
return path, valueToInsert, nil
}

func decryptionOrder(decryptionOrder string) ([]string, error) {
if decryptionOrder == "" {
return sops.SopsDecryptionOrderDefault, nil
}
orderList := strings.Split(decryptionOrder, ",")
unique := make(map[string]bool)
for _, v := range orderList {
if !slices.Contains(sops.MasterKeyTypes, v) {
return nil, common.NewExitError(fmt.Sprintf("Invalid decryption key type: %s", v), codes.InvalidDecryptionKeyType)
}
if _, ok := unique[v]; ok {
return nil, common.NewExitError(fmt.Sprintf("Duplicate decryption key type: %s", v), codes.InvalidDecryptionKeyType)
}
unique[v] = true
}
return orderList, nil
}
19 changes: 12 additions & 7 deletions cmd/sops/rotate.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@ type rotateOpts struct {
AddMasterKeys []keys.MasterKey
RemoveMasterKeys []keys.MasterKey
KeyServices []keyservice.KeyServiceClient
DecryptionOrder []string
}

func rotate(opts rotateOpts) ([]byte, error) {
tree, err := common.LoadEncryptedFileWithBugFixes(common.GenericDecryptOpts{
Cipher: opts.Cipher,
InputStore: opts.InputStore,
InputPath: opts.InputPath,
IgnoreMAC: opts.IgnoreMAC,
KeyServices: opts.KeyServices,
Cipher: opts.Cipher,
InputStore: opts.InputStore,
InputPath: opts.InputPath,
IgnoreMAC: opts.IgnoreMAC,
KeyServices: opts.KeyServices,
DecryptionOrder: opts.DecryptionOrder,
})
if err != nil {
return nil, err
Expand All @@ -39,8 +41,11 @@ func rotate(opts rotateOpts) ([]byte, error) {
})

_, err = common.DecryptTree(common.DecryptTreeOpts{
Cipher: opts.Cipher, IgnoreMac: opts.IgnoreMAC, Tree: tree,
KeyServices: opts.KeyServices,
Cipher: opts.Cipher,
IgnoreMac: opts.IgnoreMAC,
Tree: tree,
KeyServices: opts.KeyServices,
DecryptionOrder: opts.DecryptionOrder,
})
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit cba9b72

Please sign in to comment.