diff --git a/age.go b/age.go index 91314b4..eafe090 100644 --- a/age.go +++ b/age.go @@ -204,6 +204,9 @@ type NoIdentityMatchError struct { // Errors is a slice of all the errors returned to Decrypt by the Unwrap // calls it made. They all wrap [ErrIncorrectIdentity]. Errors []error + // StanzaTypes are the first argument of each recipient stanza in the + // encrypted file's header. + StanzaTypes []string } func (*NoIdentityMatchError) Error() string { @@ -265,10 +268,11 @@ func decryptHdr(hdr *format.Header, identities ...Identity) ([]byte, error) { }) stanzas := make([]*Stanza, 0, len(hdr.Recipients)) + errNoMatch := &NoIdentityMatchError{} for _, s := range hdr.Recipients { + errNoMatch.StanzaTypes = append(errNoMatch.StanzaTypes, s.Type) stanzas = append(stanzas, (*Stanza)(s)) } - errNoMatch := &NoIdentityMatchError{} var fileKey []byte for _, id := range identities { var err error diff --git a/age_test.go b/age_test.go index dfc753b..da65e3a 100644 --- a/age_test.go +++ b/age_test.go @@ -6,10 +6,12 @@ package age_test import ( "bytes" + "errors" "fmt" "io" "log" "os" + "slices" "strings" "testing" @@ -329,6 +331,54 @@ func TestDecryptNativeIdentitiesFirst(t *testing.T) { } } +type stanzaTypeRecipient string + +func (s stanzaTypeRecipient) Wrap(fileKey []byte) ([]*age.Stanza, error) { + return []*age.Stanza{{Type: string(s)}}, nil +} + +func TestNoIdentityMatchErrorStanzaTypes(t *testing.T) { + a, err := age.GenerateX25519Identity() + if err != nil { + t.Fatal(err) + } + b, err := age.GenerateX25519Identity() + if err != nil { + t.Fatal(err) + } + wrong, err := age.GenerateX25519Identity() + if err != nil { + t.Fatal(err) + } + + buf := &bytes.Buffer{} + w, err := age.Encrypt(buf, a.Recipient(), stanzaTypeRecipient("other"), b.Recipient()) + if err != nil { + t.Fatal(err) + } + if _, err := io.WriteString(w, helloWorld); err != nil { + t.Fatal(err) + } + if err := w.Close(); err != nil { + t.Fatal(err) + } + + _, err = age.Decrypt(bytes.NewReader(buf.Bytes()), wrong) + if err == nil { + t.Fatal("expected decryption to fail") + } + + var noMatch *age.NoIdentityMatchError + if !errors.As(err, &noMatch) { + t.Fatalf("expected NoIdentityMatchError, got %T: %v", err, err) + } + + want := []string{"X25519", "other", "X25519"} + if !slices.Equal(noMatch.StanzaTypes, want) { + t.Errorf("StanzaTypes = %v, want %v", noMatch.StanzaTypes, want) + } +} + func TestDetachedHeader(t *testing.T) { i, err := age.GenerateX25519Identity() if err != nil {