//nolint:testpackage // white-box test of unexported internals package secret import ( "encoding/json" "testing" "github.com/awnumar/memguard" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGenerateRandomPassphrase(t *testing.T) { t.Parallel() first, err := generateRandomPassphrase(64) require.NoError(t, err) defer first.Destroy() second, err := generateRandomPassphrase(64) require.NoError(t, err) defer second.Destroy() assert.Regexp(t, `^[0-9a-f]{64}$`, first.String()) assert.NotEqual(t, first.String(), second.String()) assert.False(t, first.IsMutable()) for _, length := range []int{0, -2, 63} { _, err := generateRandomPassphrase(length) require.ErrorIs(t, err, errPassphraseLength, "length %d", length) } } func TestKeychainDataEncodeDecode(t *testing.T) { t.Parallel() passphrase := memguard.NewBufferFromBytes([]byte("0a1b2c3d")) defer passphrase.Destroy() data := KeychainData{ AgePublicKey: "age1example", AgePrivKeyPassphrase: passphrase, EncryptedLongtermKey: "beef", } encoded, err := data.encode() require.NoError(t, err) defer encoded.Destroy() assert.JSONEq(t, `{"agePublicKey":"age1example",`+ `"agePrivKeyPassphrase":"0a1b2c3d",`+ `"encryptedLongtermKey":"beef"}`, encoded.String()) assert.False(t, encoded.IsMutable()) decoded, err := decodeKeychainData(encoded) require.NoError(t, err) defer decoded.AgePrivKeyPassphrase.Destroy() assert.Equal(t, "age1example", decoded.AgePublicKey) assert.Equal(t, "0a1b2c3d", decoded.AgePrivKeyPassphrase.String()) assert.Equal(t, "beef", decoded.EncryptedLongtermKey) } func TestKeychainDataEncodeRejectsBadPassphrase(t *testing.T) { t.Parallel() tests := []struct { name string passphrase *memguard.LockedBuffer wantErr error }{ {"nil", nil, errNilPassphraseBuffer}, {"empty", memguard.NewBuffer(0), errEmptyPassphrase}, { "not hex", memguard.NewBufferFromBytes([]byte(`abc"def`)), errPassphraseNotHex, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() data := KeychainData{AgePrivKeyPassphrase: tt.passphrase} _, err := data.encode() require.ErrorIs(t, err, tt.wantErr) }) } } func TestDecodeKeychainDataRejectsBadData(t *testing.T) { t.Parallel() for _, text := range []string{ `{"agePublicKey":"age1example"}`, `{"agePrivKeyPassphrase":42}`, } { data := memguard.NewBufferFromBytes([]byte(text)) _, err := decodeKeychainData(data) data.Destroy() require.ErrorIs(t, err, errNoKeychainPassphrase, text) } notJSON := memguard.NewBufferFromBytes([]byte(`{"agePrivKeyPassphrase":`)) defer notJSON.Destroy() _, err := decodeKeychainData(notJSON) var syntaxError *json.SyntaxError require.ErrorAs(t, err, &syntaxError) }