sops/age: drop fork of keysource implementation
As the forked code has been contributed upstream in a modified format. Signed-off-by: Hidde Beydals <hidde@hhh.computer>
This commit is contained in:
parent
815f3f0530
commit
6c67d3811c
|
|
@ -33,6 +33,7 @@ import (
|
|||
securejoin "github.com/cyphar/filepath-securejoin"
|
||||
"github.com/getsops/sops/v3"
|
||||
"github.com/getsops/sops/v3/aes"
|
||||
"github.com/getsops/sops/v3/age"
|
||||
"github.com/getsops/sops/v3/cmd/sops/common"
|
||||
"github.com/getsops/sops/v3/cmd/sops/formats"
|
||||
"github.com/getsops/sops/v3/keyservice"
|
||||
|
|
@ -47,7 +48,6 @@ import (
|
|||
"sigs.k8s.io/yaml"
|
||||
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/age"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/awskms"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/azkv"
|
||||
intkeyservice "github.com/fluxcd/kustomize-controller/internal/sops/keyservice"
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import (
|
|||
|
||||
extage "filippo.io/age"
|
||||
"github.com/getsops/sops/v3"
|
||||
sopsage "github.com/getsops/sops/v3/age"
|
||||
"github.com/getsops/sops/v3/age"
|
||||
"github.com/getsops/sops/v3/cmd/sops/formats"
|
||||
. "github.com/onsi/gomega"
|
||||
gt "github.com/onsi/gomega/types"
|
||||
|
|
@ -48,7 +48,6 @@ import (
|
|||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/age"
|
||||
)
|
||||
|
||||
func TestIsEncryptedSecret(t *testing.T) {
|
||||
|
|
@ -409,7 +408,7 @@ func TestDecryptor_SopsDecryptWithFormat(t *testing.T) {
|
|||
data := []byte("[config]\nkey = value\n")
|
||||
encData, err := kd.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
},
|
||||
}, data, format, format)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -436,7 +435,7 @@ func TestDecryptor_SopsDecryptWithFormat(t *testing.T) {
|
|||
data := []byte("{\"key\": \"value\"}\n")
|
||||
encData, err := kd.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
},
|
||||
}, data, inputFormat, inputFormat)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -468,7 +467,7 @@ func TestDecryptor_SopsDecryptWithFormat(t *testing.T) {
|
|||
format := formats.Binary
|
||||
encData, err := kd.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
},
|
||||
}, []byte("foo bar"), format, format)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -495,7 +494,7 @@ func TestDecryptor_SopsDecryptWithFormat(t *testing.T) {
|
|||
data := []byte("key=value\n")
|
||||
encData, err := kd.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
},
|
||||
}, data, format, format)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -570,7 +569,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
|
|||
encData, err := d.sopsEncryptWithFormat(sops.Metadata{
|
||||
EncryptedRegex: "^(data|stringData)$",
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
},
|
||||
}, secretData, formats.Json, formats.Json)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -603,7 +602,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
|
|||
plainData := []byte("[config]\napp = secret\n")
|
||||
encData, err := d.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
},
|
||||
}, plainData, formats.Ini, formats.Yaml)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -638,7 +637,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
|
|||
plainData := []byte("structured:\n data:\n key: value\n")
|
||||
encData, err := d.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
},
|
||||
}, plainData, formats.Yaml, formats.Yaml)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -682,7 +681,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
|
|||
}`)
|
||||
encData, err := d.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: ageID.Recipient().String()}},
|
||||
},
|
||||
}, plainData, formats.Json, formats.Yaml)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -901,7 +900,7 @@ func TestDecryptor_decryptKustomizationEnvSources(t *testing.T) {
|
|||
}
|
||||
data, err = d.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: id.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: id.Recipient().String()}},
|
||||
},
|
||||
}, f.data, format, format)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
@ -1043,7 +1042,7 @@ func TestDecryptor_decryptSopsFile(t *testing.T) {
|
|||
if f.encrypt {
|
||||
b, err := d.sopsEncryptWithFormat(sops.Metadata{
|
||||
KeyGroups: []sops.KeyGroup{
|
||||
{&sopsage.MasterKey{Recipient: id.Recipient().String()}},
|
||||
{&age.MasterKey{Recipient: id.Recipient().String()}},
|
||||
},
|
||||
}, data, f.format, f.format)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
|
|
|||
|
|
@ -1,217 +0,0 @@
|
|||
// Copyright (C) 2021 The Mozilla SOPS authors
|
||||
// Copyright (C) 2022 The Flux authors
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package age
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"filippo.io/age"
|
||||
"filippo.io/age/armor"
|
||||
)
|
||||
|
||||
// MasterKey is an age key used to Encrypt and Decrypt SOPS' data key.
|
||||
//
|
||||
// Adapted from https://github.com/mozilla/sops/blob/v3.7.2/age/keysource.go
|
||||
// to be able to have fine-grain control over the used decryption keys
|
||||
// without relying on the existence of file(path)s.
|
||||
type MasterKey struct {
|
||||
// Identities contains the set of Bench32-encoded age identities used to
|
||||
// Decrypt.
|
||||
// They are lazy-loaded using MasterKeyFromIdentities, or on first
|
||||
// Decrypt().
|
||||
// In addition to using this field, ParsedIdentities.ApplyToMasterKey() can
|
||||
// be used to parse and lazy-load identities.
|
||||
Identities []string
|
||||
// Recipient contains the Bench32-encoded age public key used to Encrypt.
|
||||
Recipient string
|
||||
// EncryptedKey contains the SOPS data key encrypted with age.
|
||||
EncryptedKey string
|
||||
|
||||
// parsedIdentities contains a slice of parsed age identities.
|
||||
// It is used to lazy-load the Identities at-most once.
|
||||
// It can also be injected by a (local) keyservice.KeyServiceServer using
|
||||
// ParsedIdentities.ApplyToMasterKey().
|
||||
parsedIdentities []age.Identity
|
||||
// parsedRecipient contains a parsed age public key.
|
||||
// It is used to lazy-load the Recipient at-most once.
|
||||
parsedRecipient *age.X25519Recipient
|
||||
}
|
||||
|
||||
// MasterKeyFromRecipient takes a Bech32-encoded age public key, parses it, and
|
||||
// returns a new MasterKey.
|
||||
func MasterKeyFromRecipient(recipient string) (*MasterKey, error) {
|
||||
parsedRecipient, err := parseRecipient(recipient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &MasterKey{
|
||||
Recipient: recipient,
|
||||
parsedRecipient: parsedRecipient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MasterKeyFromIdentities takes a set if Bech32-encoded age identities, parses
|
||||
// them, and returns a new MasterKey.
|
||||
func MasterKeyFromIdentities(identities ...string) (*MasterKey, error) {
|
||||
parsedIdentities, err := parseIdentities(identities...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &MasterKey{
|
||||
Identities: identities,
|
||||
parsedIdentities: parsedIdentities,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ParsedIdentities contains a set of parsed age identities.
|
||||
// It allows for creating a (local) keyservice.KeyServiceServer which parses
|
||||
// identities only once, to then inject them using ApplyToMasterKey() for all
|
||||
// requests.
|
||||
type ParsedIdentities []age.Identity
|
||||
|
||||
// Import attempts to parse the given identities, to then add them to itself.
|
||||
// It returns any parsing error.
|
||||
// A single identity argument is allowed to be a multiline string containing
|
||||
// multiple identities. Empty lines and lines starting with "#" are ignored.
|
||||
// It is not thread safe, and parallel importing would better be done by
|
||||
// parsing (using age.ParseIdentities) and appending to the slice yourself, in
|
||||
// combination with e.g. a sync.Mutex.
|
||||
func (i *ParsedIdentities) Import(identity ...string) error {
|
||||
identities, err := parseIdentities(identity...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse and add to age identities: %w", err)
|
||||
}
|
||||
*i = append(*i, identities...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyToMasterKey configures the ParsedIdentities on the provided key.
|
||||
func (i ParsedIdentities) ApplyToMasterKey(key *MasterKey) {
|
||||
key.parsedIdentities = i
|
||||
}
|
||||
|
||||
// Encrypt takes a SOPS data key, encrypts it with the Recipient, and stores
|
||||
// the result in the EncryptedKey field.
|
||||
func (key *MasterKey) Encrypt(dataKey []byte) error {
|
||||
if key.parsedRecipient == nil {
|
||||
parsedRecipient, err := parseRecipient(key.Recipient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key.parsedRecipient = parsedRecipient
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
aw := armor.NewWriter(&buffer)
|
||||
w, err := age.Encrypt(aw, key.parsedRecipient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create writer for encrypting sops data key with age: %w", err)
|
||||
}
|
||||
if _, err := w.Write(dataKey); err != nil {
|
||||
return fmt.Errorf("failed to encrypt sops data key with age: %w", err)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close writer for encrypting sops data key with age: %w", err)
|
||||
}
|
||||
if err := aw.Close(); err != nil {
|
||||
return fmt.Errorf("failed to close armored writer: %w", err)
|
||||
}
|
||||
|
||||
key.SetEncryptedDataKey(buffer.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncryptIfNeeded encrypts the provided SOPS data key, if it has not been
|
||||
// encrypted yet.
|
||||
func (key *MasterKey) EncryptIfNeeded(dataKey []byte) error {
|
||||
if key.EncryptedKey == "" {
|
||||
return key.Encrypt(dataKey)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncryptedDataKey returns the encrypted SOPS data key this master key holds.
|
||||
func (key *MasterKey) EncryptedDataKey() []byte {
|
||||
return []byte(key.EncryptedKey)
|
||||
}
|
||||
|
||||
// SetEncryptedDataKey sets the encrypted SOPS data key for this master key.
|
||||
func (key *MasterKey) SetEncryptedDataKey(enc []byte) {
|
||||
key.EncryptedKey = string(enc)
|
||||
}
|
||||
|
||||
// Decrypt decrypts the EncryptedKey with the (parsed) Identities and returns
|
||||
// the result.
|
||||
func (key *MasterKey) Decrypt() ([]byte, error) {
|
||||
if len(key.parsedIdentities) == 0 && len(key.Identities) > 0 {
|
||||
parsedIdentities, err := parseIdentities(key.Identities...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key.parsedIdentities = parsedIdentities
|
||||
}
|
||||
|
||||
src := bytes.NewReader([]byte(key.EncryptedKey))
|
||||
ar := armor.NewReader(src)
|
||||
r, err := age.Decrypt(ar, key.parsedIdentities...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create reader for decrypting sops data key with age: %w", err)
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
if _, err := io.Copy(&b, r); err != nil {
|
||||
return nil, fmt.Errorf("failed to copy age decrypted data into bytes.Buffer: %w", err)
|
||||
}
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// NeedsRotation returns whether the data key needs to be rotated or not.
|
||||
func (key *MasterKey) NeedsRotation() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ToString converts the key to a string representation.
|
||||
func (key *MasterKey) ToString() string {
|
||||
return key.Recipient
|
||||
}
|
||||
|
||||
// ToMap converts the MasterKey to a map for serialization purposes.
|
||||
func (key *MasterKey) ToMap() map[string]interface{} {
|
||||
out := make(map[string]interface{})
|
||||
out["recipient"] = key.Recipient
|
||||
out["enc"] = key.EncryptedKey
|
||||
return out
|
||||
}
|
||||
|
||||
// parseRecipient attempts to parse a string containing an encoded age public
|
||||
// key.
|
||||
func parseRecipient(recipient string) (*age.X25519Recipient, error) {
|
||||
parsedRecipient, err := age.ParseX25519Recipient(recipient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse input as Bech32-encoded age public key: %w", err)
|
||||
}
|
||||
return parsedRecipient, nil
|
||||
}
|
||||
|
||||
// parseIdentities attempts to parse the string set of encoded age identities.
|
||||
// A single identity argument is allowed to be a multiline string containing
|
||||
// multiple identities. Empty lines and lines starting with "#" are ignored.
|
||||
func parseIdentities(identity ...string) ([]age.Identity, error) {
|
||||
var identities []age.Identity
|
||||
for _, i := range identity {
|
||||
parsed, err := age.ParseIdentities(strings.NewReader(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
identities = append(identities, parsed...)
|
||||
}
|
||||
return identities, nil
|
||||
}
|
||||
|
|
@ -1,327 +0,0 @@
|
|||
// Copyright (C) 2022 The Flux authors
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package age
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
fuzz "github.com/AdaLogics/go-fuzz-headers"
|
||||
"github.com/getsops/sops/v3/age"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
mockRecipient string = "age1lzd99uklcjnc0e7d860axevet2cz99ce9pq6tzuzd05l5nr28ams36nvun"
|
||||
mockIdentity string = "AGE-SECRET-KEY-1G0Q5K9TV4REQ3ZSQRMTMG8NSWQGYT0T7TZ33RAZEE0GZYVZN0APSU24RK7"
|
||||
|
||||
// mockUnrelatedIdentity is not actually utilized in tests, but confirms we
|
||||
// iterate over all available identities.
|
||||
mockUnrelatedIdentity string = "AGE-SECRET-KEY-1432K5YRNSC44GC4986NXMX6GVZ52WTMT9C79CLUVWYY4DKDHD5JSNDP4MC"
|
||||
|
||||
// mockEncryptedKey equals to "data" when decrypted with mockIdentity.
|
||||
mockEncryptedKey string = `-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBvY2t2NkdLUGRvY3l2OGNy
|
||||
MVJWcUhCOEZrUG8yeCtnRnhxL0I5NFk4YjJFCmE4SVQ3MEdyZkFqRWpSa2F0NVhF
|
||||
VDUybzBxdS9nSGpHSVRVMUI0UEVqZkkKLS0tIGJjeGhNQ0Y5L2VZRVVYSm90djFF
|
||||
bzdnQ3UwTGljMmtrbWNMV1MxYkFzUFUK4xjOZOTGdcbzuwUY/zeBXhcF+Md3e5PQ
|
||||
EylloI7MNGbadPGb
|
||||
-----END AGE ENCRYPTED FILE-----`
|
||||
)
|
||||
|
||||
var (
|
||||
mockIdentities = []string{
|
||||
mockUnrelatedIdentity,
|
||||
mockIdentity,
|
||||
}
|
||||
)
|
||||
|
||||
func TestMasterKeyFromRecipient(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
got, err := MasterKeyFromRecipient(mockRecipient)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(got).ToNot(BeNil())
|
||||
g.Expect(got.Recipient).To(Equal(mockRecipient))
|
||||
g.Expect(got.parsedRecipient).ToNot(BeNil())
|
||||
g.Expect(got.parsedIdentities).To(BeEmpty())
|
||||
|
||||
got, err = MasterKeyFromRecipient("invalid")
|
||||
g.Expect(err).To(HaveOccurred())
|
||||
g.Expect(got).To(BeNil())
|
||||
}
|
||||
|
||||
func TestMasterKeyFromIdentities(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
got, err := MasterKeyFromIdentities(mockIdentities...)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(got).ToNot(BeNil())
|
||||
g.Expect(got.Identities).To(HaveLen(2))
|
||||
g.Expect(got.Identities).To(ContainElements(mockIdentities))
|
||||
g.Expect(got.parsedIdentities).To(HaveLen(2))
|
||||
|
||||
got, err = MasterKeyFromIdentities("invalid")
|
||||
g.Expect(err).To(HaveOccurred())
|
||||
g.Expect(got).To(BeNil())
|
||||
}
|
||||
|
||||
func TestParsedIdentities_Import(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
i := make(ParsedIdentities, 0)
|
||||
g.Expect(i.Import(mockIdentities...)).To(Succeed())
|
||||
g.Expect(i).To(HaveLen(2))
|
||||
|
||||
g.Expect(i.Import("invalid")).To(HaveOccurred())
|
||||
g.Expect(i).To(HaveLen(2))
|
||||
}
|
||||
|
||||
func TestParsedIdentities_ApplyToMasterKey(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
i := make(ParsedIdentities, 0)
|
||||
g.Expect(i.Import(mockIdentities...)).To(Succeed())
|
||||
|
||||
key := &MasterKey{}
|
||||
i.ApplyToMasterKey(key)
|
||||
g.Expect(key.parsedIdentities).To(BeEquivalentTo(i))
|
||||
}
|
||||
|
||||
func TestMasterKey_Encrypt(t *testing.T) {
|
||||
mockParsedRecipient, _ := parseRecipient(mockRecipient)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *MasterKey
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "recipient",
|
||||
key: &MasterKey{
|
||||
Recipient: mockRecipient,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "parsed recipient",
|
||||
key: &MasterKey{
|
||||
parsedRecipient: mockParsedRecipient,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid recipient",
|
||||
key: &MasterKey{
|
||||
Recipient: "invalid",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "parsed recipient and invalid recipient",
|
||||
key: &MasterKey{
|
||||
Recipient: "invalid",
|
||||
parsedRecipient: mockParsedRecipient,
|
||||
},
|
||||
// This should pass, confirming parsedRecipient > Recipient
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
err := tt.key.Encrypt([]byte("data"))
|
||||
if tt.wantErr {
|
||||
g.Expect(err).To(HaveOccurred())
|
||||
g.Expect(tt.key.EncryptedKey).To(BeEmpty())
|
||||
return
|
||||
}
|
||||
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(tt.key.EncryptedKey).ToNot(BeEmpty())
|
||||
g.Expect(tt.key.EncryptedKey).To(ContainSubstring("AGE ENCRYPTED FILE"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMasterKey_Encrypt_SOPS_Compat(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
dataKey := []byte("foo")
|
||||
|
||||
encryptKey := &MasterKey{
|
||||
Recipient: mockRecipient,
|
||||
}
|
||||
g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed())
|
||||
|
||||
t.Setenv(age.SopsAgeKeyEnv, mockIdentity)
|
||||
decryptKey := &age.MasterKey{
|
||||
EncryptedKey: encryptKey.EncryptedKey,
|
||||
}
|
||||
dec, err := decryptKey.Decrypt()
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(dec).To(Equal(dataKey))
|
||||
}
|
||||
|
||||
func TestMasterKey_EncryptIfNeeded(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
key, err := MasterKeyFromRecipient(mockRecipient)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
g.Expect(key.EncryptIfNeeded([]byte("data"))).To(Succeed())
|
||||
|
||||
encryptedKey := key.EncryptedKey
|
||||
g.Expect(encryptedKey).To(ContainSubstring("AGE ENCRYPTED FILE"))
|
||||
|
||||
g.Expect(key.EncryptIfNeeded([]byte("some other data"))).To(Succeed())
|
||||
g.Expect(key.EncryptedKey).To(Equal(encryptedKey))
|
||||
}
|
||||
|
||||
func TestMasterKey_EncryptedDataKey(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
key := &MasterKey{EncryptedKey: "some key"}
|
||||
g.Expect(key.EncryptedDataKey()).To(BeEquivalentTo(key.EncryptedKey))
|
||||
}
|
||||
|
||||
func TestMasterKey_Decrypt(t *testing.T) {
|
||||
parsedIdentities, _ := parseIdentities(mockIdentities...)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key *MasterKey
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "identities",
|
||||
key: &MasterKey{
|
||||
Identities: mockIdentities,
|
||||
EncryptedKey: mockEncryptedKey,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "parsed identities",
|
||||
key: &MasterKey{
|
||||
EncryptedKey: mockEncryptedKey,
|
||||
parsedIdentities: parsedIdentities,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no identities",
|
||||
key: &MasterKey{
|
||||
Identities: []string{},
|
||||
EncryptedKey: mockEncryptedKey,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid identity",
|
||||
key: &MasterKey{
|
||||
Identities: []string{"invalid"},
|
||||
EncryptedKey: mockEncryptedKey,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid encrypted key",
|
||||
key: &MasterKey{
|
||||
Identities: mockIdentities,
|
||||
EncryptedKey: "invalid",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
data, err := tt.key.Decrypt()
|
||||
if tt.wantErr {
|
||||
g.Expect(err).To(HaveOccurred())
|
||||
g.Expect(data).To(BeNil())
|
||||
return
|
||||
}
|
||||
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(data).To(Equal([]byte("data")))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMasterKey_Decrypt_SOPS_Compat(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
dataKey := []byte("foo")
|
||||
|
||||
encryptKey := &age.MasterKey{
|
||||
Recipient: mockRecipient,
|
||||
}
|
||||
g.Expect(encryptKey.Encrypt(dataKey)).To(Succeed())
|
||||
|
||||
decryptKey := &MasterKey{
|
||||
Identities: []string{mockIdentity},
|
||||
EncryptedKey: encryptKey.EncryptedKey,
|
||||
}
|
||||
dec, err := decryptKey.Decrypt()
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(dec).To(Equal(dataKey))
|
||||
}
|
||||
|
||||
func TestMasterKey_EncryptDecrypt_RoundTrip(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
encryptKey, err := MasterKeyFromRecipient(mockRecipient)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
data := []byte("some secret data")
|
||||
g.Expect(encryptKey.Encrypt(data)).To(Succeed())
|
||||
g.Expect(encryptKey.EncryptedKey).ToNot(BeEmpty())
|
||||
|
||||
decryptKey, err := MasterKeyFromIdentities(mockIdentity)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
decryptKey.EncryptedKey = encryptKey.EncryptedKey
|
||||
decryptedData, err := decryptKey.Decrypt()
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
g.Expect(decryptedData).To(Equal(data))
|
||||
}
|
||||
|
||||
func TestMasterKey_ToString(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
key := &MasterKey{Recipient: mockRecipient}
|
||||
g.Expect(key.ToString()).To(Equal(key.Recipient))
|
||||
}
|
||||
|
||||
func TestMasterKey_ToMap(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
key := &MasterKey{
|
||||
Recipient: mockRecipient,
|
||||
Identities: mockIdentities, // must never be included
|
||||
EncryptedKey: "some-encrypted-key",
|
||||
}
|
||||
g.Expect(key.ToMap()).To(Equal(map[string]interface{}{
|
||||
"recipient": mockRecipient,
|
||||
"enc": key.EncryptedKey,
|
||||
}))
|
||||
}
|
||||
|
||||
func Fuzz_Age(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, receipt, identities string, seed, data []byte) {
|
||||
fc := fuzz.NewConsumer(seed)
|
||||
masterKey := MasterKey{}
|
||||
|
||||
if err := fc.GenerateStruct(&masterKey); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_ = masterKey.Encrypt(data)
|
||||
_ = masterKey.EncryptIfNeeded(data)
|
||||
_, _ = MasterKeyFromRecipient(receipt)
|
||||
_, _ = MasterKeyFromIdentities(identities)
|
||||
})
|
||||
}
|
||||
|
|
@ -8,12 +8,12 @@ package keyservice
|
|||
|
||||
import (
|
||||
extage "filippo.io/age"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/gcpkms"
|
||||
"github.com/getsops/sops/v3/age"
|
||||
"github.com/getsops/sops/v3/keyservice"
|
||||
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/age"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/awskms"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/azkv"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/gcpkms"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/hcvault"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/pgp"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ package keyservice
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/getsops/sops/v3/age"
|
||||
"github.com/getsops/sops/v3/keyservice"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/age"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/awskms"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/azkv"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/gcpkms"
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ import (
|
|||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/getsops/sops/v3/age"
|
||||
"github.com/getsops/sops/v3/keyservice"
|
||||
. "github.com/onsi/gomega"
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/age"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/awskms"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/azkv"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/gcpkms"
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/getsops/sops/v3/age"
|
||||
"github.com/getsops/sops/v3/keys"
|
||||
"github.com/getsops/sops/v3/keyservice"
|
||||
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/age"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/awskms"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/azkv"
|
||||
"github.com/fluxcd/kustomize-controller/internal/sops/gcpkms"
|
||||
|
|
|
|||
Loading…
Reference in New Issue