From e84d74239e91b032a080ea4f59661dbeaad69da9 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Sun, 3 Jul 2022 02:18:11 +0200 Subject: [PATCH] age: wrap decryption errors through and add armor.Error --- age.go | 4 ++-- armor/armor.go | 26 +++++++++++++++++++++----- internal/format/format.go | 26 ++++++++++++++++---------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/age.go b/age.go index 22f45c4..a0ef0bb 100644 --- a/age.go +++ b/age.go @@ -179,7 +179,7 @@ func Decrypt(src io.Reader, identities ...Identity) (io.Reader, error) { hdr, payload, err := format.Parse(src) if err != nil { - return nil, fmt.Errorf("failed to read header: %v", err) + return nil, fmt.Errorf("failed to read header: %w", err) } stanzas := make([]*Stanza, 0, len(hdr.Recipients)) @@ -212,7 +212,7 @@ func Decrypt(src io.Reader, identities ...Identity) (io.Reader, error) { nonce := make([]byte, streamNonceSize) if _, err := io.ReadFull(payload, nonce); err != nil { - return nil, fmt.Errorf("failed to read nonce: %v", err) + return nil, fmt.Errorf("failed to read nonce: %w", err) } return stream.NewReader(streamKey(fileKey, nonce), payload) diff --git a/armor/armor.go b/armor/armor.go index e218e89..7b22df6 100644 --- a/armor/armor.go +++ b/armor/armor.go @@ -14,6 +14,7 @@ import ( "bytes" "encoding/base64" "errors" + "fmt" "io" "filippo.io/age/internal/format" @@ -90,7 +91,7 @@ func (r *armoredReader) Read(p []byte) (int, error) { line, err := r.r.ReadBytes('\n') if err != nil && len(line) == 0 { if err == io.EOF { - err = errors.New("invalid armor: unexpected EOF") + err = io.ErrUnexpectedEOF } return nil, err } @@ -103,7 +104,7 @@ func (r *armoredReader) Read(p []byte) (int, error) { return 0, r.setErr(err) } if string(line) != Header { - return 0, r.setErr(errors.New("invalid armor first line: " + string(line))) + return 0, r.setErr(fmt.Errorf("invalid first line: %q", line)) } r.started = true } @@ -115,12 +116,12 @@ func (r *armoredReader) Read(p []byte) (int, error) { return 0, r.setErr(io.EOF) } if len(line) > format.ColumnsPerLine { - return 0, r.setErr(errors.New("invalid armor: column limit exceeded")) + return 0, r.setErr(errors.New("column limit exceeded")) } r.unread = r.buf[:] n, err := base64.StdEncoding.Strict().Decode(r.unread, line) if err != nil { - return 0, r.setErr(errors.New("invalid armor: " + err.Error())) + return 0, r.setErr(err) } r.unread = r.unread[:n] @@ -130,7 +131,7 @@ func (r *armoredReader) Read(p []byte) (int, error) { return 0, r.setErr(err) } if string(line) != Footer { - return 0, r.setErr(errors.New("invalid armor closing line: " + string(line))) + return 0, r.setErr(fmt.Errorf("invalid closing line: %q", line)) } r.err = io.EOF } @@ -140,7 +141,22 @@ func (r *armoredReader) Read(p []byte) (int, error) { return nn, nil } +type Error struct { + err error +} + +func (e *Error) Error() string { + return "invalid armor: " + e.err.Error() +} + +func (e *Error) Unwrap() error { + return e.err +} + func (r *armoredReader) setErr(err error) error { + if err != io.EOF { + err = &Error{err} + } r.err = err return err } diff --git a/internal/format/format.go b/internal/format/format.go index a91064a..aa77b75 100644 --- a/internal/format/format.go +++ b/internal/format/format.go @@ -175,7 +175,7 @@ func (r *StanzaReader) ReadStanza() (s *Stanza, err error) { line, err := r.r.ReadBytes('\n') if err != nil { - return nil, fmt.Errorf("failed to read line: %v", err) + return nil, fmt.Errorf("failed to read line: %w", err) } if !bytes.HasPrefix(line, stanzaPrefix) { return nil, fmt.Errorf("malformed stanza opening line: %q", line) @@ -195,7 +195,7 @@ func (r *StanzaReader) ReadStanza() (s *Stanza, err error) { for { line, err := r.r.ReadBytes('\n') if err != nil { - return nil, fmt.Errorf("failed to read line: %v", err) + return nil, fmt.Errorf("failed to read line: %w", err) } b, err := DecodeString(strings.TrimSuffix(string(line), "\n")) @@ -216,14 +216,20 @@ func (r *StanzaReader) ReadStanza() (s *Stanza, err error) { } } -type ParseError string +type ParseError struct { + err error +} -func (e ParseError) Error() string { - return "parsing age header: " + string(e) +func (e *ParseError) Error() string { + return "parsing age header: " + e.err.Error() +} + +func (e *ParseError) Unwrap() error { + return e.err } func errorf(format string, a ...interface{}) error { - return ParseError(fmt.Sprintf(format, a...)) + return &ParseError{fmt.Errorf(format, a...)} } // Parse returns the header and a Reader that begins at the start of the @@ -234,7 +240,7 @@ func Parse(input io.Reader) (*Header, io.Reader, error) { line, err := rr.ReadString('\n') if err != nil { - return nil, nil, errorf("failed to read intro: %v", err) + return nil, nil, errorf("failed to read intro: %w", err) } if line != intro { return nil, nil, errorf("unexpected intro: %q", line) @@ -244,13 +250,13 @@ func Parse(input io.Reader) (*Header, io.Reader, error) { for { peek, err := rr.Peek(len(footerPrefix)) if err != nil { - return nil, nil, errorf("failed to read header: %v", err) + return nil, nil, errorf("failed to read header: %w", err) } if bytes.Equal(peek, footerPrefix) { line, err := rr.ReadBytes('\n') if err != nil { - return nil, nil, fmt.Errorf("failed to read header: %v", err) + return nil, nil, fmt.Errorf("failed to read header: %w", err) } prefix, args := splitArgs(line) @@ -266,7 +272,7 @@ func Parse(input io.Reader) (*Header, io.Reader, error) { s, err := sr.ReadStanza() if err != nil { - return nil, nil, fmt.Errorf("failed to parse header: %v", err) + return nil, nil, fmt.Errorf("failed to parse header: %w", err) } h.Recipients = append(h.Recipients, s) }