Files
keyfunc/internal/agekey/agekey_test.go
T
sneak 72dd386bbf
check / check (push) Successful in 54s
Move the Go module to sneak.berlin/go/keyfunc (closes #15)
REPO_POLICIES.md sets the Go module root to sneak.berlin/go/<name>.
go.mod, every import of the old path and the -X path in the Makefile
LDFLAGS now use sneak.berlin/go/keyfunc; make fmt moved those imports
to their new place in the sort order. The old path keeps no alias. The
README gives the go install line next to the build from a clone and
drops this issue from its TODO list.

Model: opus-5-5
2026-10-03 12:10:07 +00:00

170 lines
4.1 KiB
Go

package agekey_test
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/require"
"sneak.berlin/go/keyfunc/internal/agekey"
"sneak.berlin/go/keyfunc/internal/derive"
)
// The recipients the example mnemonic produces at the first two
// indexes, and the secret key behind the first of them. They are what
// makes the derivation reproducible: if the recipients change, every
// file anyone encrypted becomes unreadable, and if the secret key
// changes, the key is no longer the one other tools derive from the
// same mnemonic.
const (
recipientZero = "age1xwdy9y6ckyfsgjc8k02e9uhsf3fmjy0ufysew" +
"lj68kmx5n67e3nsg2mftq"
recipientOne = "age1pmm92sxaf5mazjwvjph7dx2zq9r5p8l3rarfg" +
"qm7hmakqhvgyy4q5p3w7j"
identityZero = "AGE-SECRET-KEY-19QKK2P38598XLXMQFFU3P7J9PLDD" +
"7527T70JDHGDJ7AMNF3XT44S00JFU5"
)
// example returns the mnemonic every BIP-39 document uses to show its
// test vectors: eleven abandons and about.
func example() string {
return strings.Repeat("abandon ", 11) + "about"
}
func TestTooFewBytesAreRefused(t *testing.T) {
t.Parallel()
_, err := agekey.New([]byte("short"))
require.ErrorIs(t, err, agekey.ErrSize)
}
func TestTheSameMnemonicAlwaysGivesTheSameKey(t *testing.T) {
t.Parallel()
require.Equal(t, recipientZero, forIndex(t, 0).Recipient())
require.Equal(t, recipientOne, forIndex(t, 1).Recipient())
}
func TestTheSameMnemonicAlwaysGivesTheSameSecretKey(t *testing.T) {
t.Parallel()
require.Equal(t, identityZero, forIndex(t, 0).Identity())
}
func TestWhatWasEncryptedComesBack(t *testing.T) {
t.Parallel()
// Every byte value, so nothing assumes the input is text, and
// then text, which is what most of it will be.
payloads := map[string][]byte{
"every byte": everyByte(),
"text": []byte("the quick brown fox\nand a second line\n"),
}
forms := map[string]bool{"binary": false, "armored": true}
for name, payload := range payloads {
for form, armored := range forms {
t.Run(name+" "+form, func(t *testing.T) {
t.Parallel()
key := forIndex(t, 0)
var sealed, opened bytes.Buffer
err := key.Encrypt(
&sealed, bytes.NewReader(payload), nil, armored,
)
require.NoError(t, err)
err = key.Decrypt(&opened, &sealed)
require.NoError(t, err)
require.Equal(t, payload, opened.Bytes())
})
}
}
}
func TestTheArmoredFormIsText(t *testing.T) {
t.Parallel()
var sealed bytes.Buffer
err := forIndex(t, 0).Encrypt(
&sealed, strings.NewReader("hello"), nil, true,
)
require.NoError(t, err)
require.True(t, strings.HasPrefix(
sealed.String(), "-----BEGIN AGE ENCRYPTED FILE-----",
))
}
func TestAFileForSomebodyElseIsRefused(t *testing.T) {
t.Parallel()
var sealed, opened bytes.Buffer
err := forIndex(t, 1).Encrypt(
&sealed, strings.NewReader("hello"), nil, false,
)
require.NoError(t, err)
err = forIndex(t, 0).Decrypt(&opened, &sealed)
require.ErrorIs(t, err, agekey.ErrNotRecipient)
require.Empty(t, opened.Bytes())
}
func TestAnExtraRecipientCanReadItTooAndSoCanTheDerivedOne(t *testing.T) {
t.Parallel()
mine, theirs := forIndex(t, 0), forIndex(t, 1)
var sealed bytes.Buffer
err := mine.Encrypt(
&sealed, strings.NewReader("hello"),
[]string{theirs.Recipient()}, false,
)
require.NoError(t, err)
for _, key := range []*agekey.Key{mine, theirs} {
var opened bytes.Buffer
require.NoError(t, key.Decrypt(&opened, bytes.NewReader(sealed.Bytes())))
require.Equal(t, "hello", opened.String())
}
}
func TestARecipientThatIsNotOneIsRefused(t *testing.T) {
t.Parallel()
err := forIndex(t, 0).Encrypt(
&bytes.Buffer{}, strings.NewReader("hello"),
[]string{"not a recipient"}, false,
)
require.Error(t, err)
}
// everyByte returns a payload holding all 256 byte values.
func everyByte() []byte {
out := make([]byte, 256)
for i := range out {
out[i] = byte(i)
}
return out
}
// forIndex derives the key for one index.
func forIndex(t *testing.T, index uint32) *agekey.Key {
t.Helper()
material, err := derive.Bytes(example(), agekey.Application, index)
require.NoError(t, err)
key, err := agekey.New(material)
require.NoError(t, err)
return key
}