2017-09-12 20:01:12 -07:00
|
|
|
/*
|
|
|
|
|
Package decrypt is the external API other Go programs can use to decrypt SOPS files. It is the only package in SOPS with
|
|
|
|
|
a stable API.
|
|
|
|
|
*/
|
2017-01-22 10:55:52 -05:00
|
|
|
package decrypt // import "go.mozilla.org/sops/decrypt"
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"go.mozilla.org/sops"
|
|
|
|
|
"go.mozilla.org/sops/aes"
|
2018-09-20 11:18:34 -06:00
|
|
|
sopsdotenv "go.mozilla.org/sops/stores/dotenv"
|
2017-08-23 16:36:49 -07:00
|
|
|
sopsjson "go.mozilla.org/sops/stores/json"
|
|
|
|
|
sopsyaml "go.mozilla.org/sops/stores/yaml"
|
2017-01-22 10:55:52 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// File is a wrapper around Data that reads a local encrypted
|
|
|
|
|
// file and returns its cleartext data in an []byte
|
|
|
|
|
func File(path, format string) (cleartext []byte, err error) {
|
|
|
|
|
// Read the file into an []byte
|
|
|
|
|
encryptedData, err := ioutil.ReadFile(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("Failed to read %q: %v", path, err)
|
|
|
|
|
}
|
|
|
|
|
return Data(encryptedData, format)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Data is a helper that takes encrypted data and a format string,
|
|
|
|
|
// decrypts the data and returns its cleartext in an []byte.
|
2018-10-29 21:50:13 +01:00
|
|
|
// The format string can be `json`, `yaml`, `dotenv` or `binary`.
|
2017-01-22 10:55:52 -05:00
|
|
|
// If the format string is empty, binary format is assumed.
|
|
|
|
|
func Data(data []byte, format string) (cleartext []byte, err error) {
|
|
|
|
|
// Initialize a Sops JSON store
|
|
|
|
|
var store sops.Store
|
|
|
|
|
switch format {
|
|
|
|
|
case "json":
|
|
|
|
|
store = &sopsjson.Store{}
|
|
|
|
|
case "yaml":
|
|
|
|
|
store = &sopsyaml.Store{}
|
2018-10-29 21:50:13 +01:00
|
|
|
case "dotenv":
|
|
|
|
|
store = &sopsdotenv.Store{}
|
2017-01-22 10:55:52 -05:00
|
|
|
default:
|
|
|
|
|
store = &sopsjson.BinaryStore{}
|
|
|
|
|
}
|
2018-04-20 10:13:46 +02:00
|
|
|
// Load SOPS file and access the data key
|
|
|
|
|
tree, err := store.LoadEncryptedFile(data)
|
2017-01-22 10:55:52 -05:00
|
|
|
if err != nil {
|
2017-03-23 13:38:59 -04:00
|
|
|
return nil, err
|
2017-01-22 10:55:52 -05:00
|
|
|
}
|
2018-04-20 10:13:46 +02:00
|
|
|
key, err := tree.Metadata.GetDataKey()
|
2017-01-22 10:55:52 -05:00
|
|
|
if err != nil {
|
2017-03-23 13:38:59 -04:00
|
|
|
return nil, err
|
2017-01-22 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decrypt the tree
|
2017-09-12 20:41:02 -07:00
|
|
|
cipher := aes.NewCipher()
|
|
|
|
|
mac, err := tree.Decrypt(key, cipher)
|
2017-01-22 10:55:52 -05:00
|
|
|
if err != nil {
|
2017-03-23 13:38:59 -04:00
|
|
|
return nil, err
|
2017-01-22 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compute the hash of the cleartext tree and compare it with
|
|
|
|
|
// the one that was stored in the document. If they match,
|
|
|
|
|
// integrity was preserved
|
2017-09-12 20:41:02 -07:00
|
|
|
originalMac, err := cipher.Decrypt(
|
2018-04-20 10:13:46 +02:00
|
|
|
tree.Metadata.MessageAuthenticationCode,
|
2017-01-22 10:55:52 -05:00
|
|
|
key,
|
2018-04-20 10:13:46 +02:00
|
|
|
tree.Metadata.LastModified.Format(time.RFC3339),
|
2017-01-22 10:55:52 -05:00
|
|
|
)
|
2017-08-08 18:18:26 -07:00
|
|
|
if originalMac != mac {
|
|
|
|
|
return nil, fmt.Errorf("Failed to verify data integrity. expected mac %q, got %q", originalMac, mac)
|
2017-01-22 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
2018-09-20 11:18:34 -06:00
|
|
|
return store.EmitPlainFile(tree.Branches)
|
2017-01-22 10:55:52 -05:00
|
|
|
}
|