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

Use age/armor for encrypted data key (#819)

* 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 <tullo@users.noreply.github.com>

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

Signed-off-by: Andreas Amstutz <tullo@users.noreply.github.com>

* add unit test

Signed-off-by: Andreas Amstutz <tullo@users.noreply.github.com>

Co-authored-by: Andreas Amstutz <tullo@users.noreply.github.com>
This commit is contained in:
Andreas
2021-02-21 09:06:40 +01:00
committed by GitHub
parent 0f2ebcf7ff
commit 5d1376d56d
4 changed files with 55 additions and 37 deletions

View File

@@ -9,6 +9,7 @@ import (
"strings"
"filippo.io/age"
"filippo.io/age/armor"
"github.com/sirupsen/logrus"
"go.mozilla.org/sops/v3/logging"
)
@@ -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)
}
@@ -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")
@@ -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.

View File

@@ -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) {
@@ -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)
}