Skip to content

Commit

Permalink
Use age/armor for encrypted data key (getsops#819)
Browse files Browse the repository at this point in the history
* Use age/armor for encrypted data key

Currently the encrypted data key is stored as a binary value, and this
results in SOPS encrypted DOTENV files having weird binary characters.

This changes the encrypt/decrypt methods to use the armor reader writer
provided by: filippo.io/age/armor

Signed-off-by: Andreas Amstutz <[email protected]>

* upgrade filippo.io/age to v1.0.0-beta7

Signed-off-by: Andreas Amstutz <[email protected]>

* add unit test

Signed-off-by: Andreas Amstutz <[email protected]>

Co-authored-by: Andreas Amstutz <[email protected]>
  • Loading branch information
tullo and tullo committed Feb 21, 2021
1 parent 0f2ebcf commit 5d1376d
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 37 deletions.
22 changes: 15 additions & 7 deletions age/keysource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"filippo.io/age"
"filippo.io/age/armor"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3/logging"
)
Expand Down Expand Up @@ -46,7 +47,8 @@ func (key *MasterKey) Encrypt(datakey []byte) error {
key.parsedRecipient = parsedRecipient
}

w, err := age.Encrypt(buffer, key.parsedRecipient)
aw := armor.NewWriter(buffer)
w, err := age.Encrypt(aw, key.parsedRecipient)
if err != nil {
return fmt.Errorf("failed to open file for encrypting sops data key with age: %w", err)
}
Expand All @@ -61,6 +63,11 @@ func (key *MasterKey) Encrypt(datakey []byte) error {
return fmt.Errorf("failed to close file for encrypting sops data key with age: %w", err)
}

if err := aw.Close(); err != nil {
log.WithField("recipient", key.parsedRecipient).Error("Encryption failed")
return fmt.Errorf("failed to close armored writer: %w", err)
}

key.EncryptedKey = buffer.String()

log.WithField("recipient", key.parsedRecipient).Info("Encryption succeeded")
Expand Down Expand Up @@ -115,19 +122,20 @@ func (key *MasterKey) Decrypt() ([]byte, error) {
return nil, err
}

buffer := &bytes.Buffer{}
reader := bytes.NewReader([]byte(key.EncryptedKey))
r, err := age.Decrypt(reader, identities...)
src := bytes.NewReader([]byte(key.EncryptedKey))
ar := armor.NewReader(src)
r, err := age.Decrypt(ar, identities...)

if err != nil {
return nil, fmt.Errorf("no age identity found in %q that could decrypt the data", ageKeyFilePath)
}

if _, err := io.Copy(buffer, r); err != nil {
return nil, fmt.Errorf("failed to copy decrypted data into bytes.Buffer")
var b bytes.Buffer
if _, err := io.Copy(&b, r); err != nil {
return nil, fmt.Errorf("failed to copy decrypted data into bytes.Buffer: %w", err)
}

return buffer.Bytes(), nil
return b.Bytes(), nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
Expand Down
28 changes: 27 additions & 1 deletion age/keysource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestMasterKeysFromRecipientsEmpty(t *testing.T) {

assert.NoError(err)

assert.Equal(recipients, make([]*MasterKey,0))
assert.Equal(recipients, make([]*MasterKey, 0))
}

func TestAge(t *testing.T) {
Expand All @@ -41,3 +41,29 @@ func TestAge(t *testing.T) {
assert.NoError(err)
assert.Equal(dataKey, decryptedKey)
}

func TestAgeDotEnv(t *testing.T) {
assert := assert.New(t)

key, err := MasterKeyFromRecipient("age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw")

assert.NoError(err)
assert.Equal("age1yt3tfqlfrwdwx0z0ynwplcr6qxcxfaqycuprpmy89nr83ltx74tqdpszlw", key.ToString())

dotenv := `IMAGE_PREFIX=repo/service-
APPLICATION_KEY=K6pfAWuUVND9Fz5SC7jmA6pfAWuUVND9Fz5SC7jmA
KEY_ID=003683d721f2ae683d721f2a1
DOMAIN=files.127.0.0.1.nip.io`
dataKey := []byte(dotenv)

err = key.Encrypt(dataKey)
assert.NoError(err)

_, filename, _, _ := runtime.Caller(0)
err = os.Setenv("SOPS_AGE_KEY_FILE", path.Join(path.Dir(filename), "keys.txt"))
assert.NoError(err)

decryptedKey, err := key.Decrypt()
assert.NoError(err)
assert.Equal(dataKey, decryptedKey)
}
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.13

require (
cloud.google.com/go v0.43.0
filippo.io/age v1.0.0-beta5
filippo.io/age v1.0.0-beta7
github.com/Azure/azure-sdk-for-go v31.2.0+incompatible
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Azure/go-autorest/autorest v0.9.0
Expand Down Expand Up @@ -40,9 +40,10 @@ require (
github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect
github.com/stretchr/testify v1.5.1
go.mozilla.org/gopgagent v0.0.0-20170926210634-4d7ea76ff71a
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43 // indirect
google.golang.org/api v0.7.0
google.golang.org/grpc v1.27.0
google.golang.org/protobuf v1.25.0
Expand Down
Loading

0 comments on commit 5d1376d

Please sign in to comment.