Check arguments before looking for the mnemonic (closes #79)
check / check (push) Waiting to run

The key index, --words, the --to recipients and the input file of
age encrypt and age decrypt were checked only after the mnemonic had
been obtained, so a run that could not succeed still prompted for it or
ran the mnemonic command. options.Index now refuses an index above
derive.MaxIndex, which covers every command group; the mnemonic command
calls the new childmnemonic.CheckWords first; age encrypt reads its
recipients with the new agekey.Recipients, and both age commands open
their input, before the key is derived. The README says which checks
come first.

Judgement call: the -o path is still looked at after the mnemonic.

Model: opus-5-5
This commit was merged in pull request #84.
This commit is contained in:
2026-10-14 21:05:48 +02:00
parent b02f502928
commit d1ed234bc9
9 changed files with 162 additions and 62 deletions
+9 -12
View File
@@ -94,15 +94,12 @@ func (k *Key) Identity() string {
}
// Encrypt copies src to dst, encrypted to this key and to every extra
// recipient named, so the same mnemonic can always read it back again.
// recipient given, so the same mnemonic can always read it back again.
// Armored output is the text form age also reads.
func (k *Key) Encrypt(
dst io.Writer, src io.Reader, to []string, armored bool,
dst io.Writer, src io.Reader, to []age.Recipient, armored bool,
) error {
all, err := recipients(k.identity.Recipient(), to)
if err != nil {
return err
}
all := append([]age.Recipient{k.identity.Recipient()}, to...)
out := dst
@@ -113,7 +110,7 @@ func (k *Key) Encrypt(
out = text
}
err = encrypt(out, src, all)
err := encrypt(out, src, all)
if err != nil {
return err
}
@@ -130,12 +127,12 @@ func (k *Key) Encrypt(
return nil
}
// recipients returns the key's own recipient followed by the ones
// named on the command line, so what the key encrypts it can read.
func recipients(mine age.Recipient, to []string) ([]age.Recipient, error) {
list := []age.Recipient{mine}
// Recipients reads the recipients named on the command line, for
// Encrypt to add to the key's own.
func Recipients(names []string) ([]age.Recipient, error) {
list := make([]age.Recipient, 0, len(names))
for _, name := range to {
for _, name := range names {
parsed, err := age.ParseX25519Recipient(name)
if err != nil {
return nil, fmt.Errorf("reading recipient %q: %w", name, err)
+5 -8
View File
@@ -119,12 +119,12 @@ func TestAnExtraRecipientCanReadItTooAndSoCanTheDerivedOne(t *testing.T) {
mine, theirs := forIndex(t, 0), forIndex(t, 1)
to, err := agekey.Recipients([]string{theirs.Recipient()})
require.NoError(t, err)
var sealed bytes.Buffer
err := mine.Encrypt(
&sealed, strings.NewReader("hello"),
[]string{theirs.Recipient()}, false,
)
err = mine.Encrypt(&sealed, strings.NewReader("hello"), to, false)
require.NoError(t, err)
for _, key := range []*agekey.Key{mine, theirs} {
@@ -138,10 +138,7 @@ func TestAnExtraRecipientCanReadItTooAndSoCanTheDerivedOne(t *testing.T) {
func TestARecipientThatIsNotOneIsRefused(t *testing.T) {
t.Parallel()
err := forIndex(t, 0).Encrypt(
&bytes.Buffer{}, strings.NewReader("hello"),
[]string{"not a recipient"}, false,
)
_, err := agekey.Recipients([]string{"not a recipient"})
require.Error(t, err)
}