From f3158b36f1f210ff54febbe82b571c1379b30c98 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Mar 2020 16:20:07 -0500 Subject: Initial support for PGP decryption & signatures --- widgets/pgpinfo.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 widgets/pgpinfo.go (limited to 'widgets/pgpinfo.go') diff --git a/widgets/pgpinfo.go b/widgets/pgpinfo.go new file mode 100644 index 0000000..b6a7a16 --- /dev/null +++ b/widgets/pgpinfo.go @@ -0,0 +1,93 @@ +package widgets + +import ( + "errors" + + "git.sr.ht/~sircmpwn/aerc/lib/ui" + + "github.com/gdamore/tcell" + "golang.org/x/crypto/openpgp" + pgperrors "golang.org/x/crypto/openpgp/errors" +) + +type PGPInfo struct { + ui.Invalidatable + details *openpgp.MessageDetails +} + +func NewPGPInfo(details *openpgp.MessageDetails) *PGPInfo { + return &PGPInfo{details: details} +} + +func (p *PGPInfo) DrawSignature(ctx *ui.Context, offs bool) { + errorStyle := tcell.StyleDefault.Background(tcell.ColorRed). + Foreground(tcell.ColorWhite).Bold(true) + softErrorStyle := tcell.StyleDefault.Foreground(tcell.ColorYellow). + Reverse(true).Bold(true) + validStyle := tcell.StyleDefault.Foreground(tcell.ColorGreen).Bold(true) + header := "Signature " + if offs { + header += " " + } + + // TODO: Nicer prompt for TOFU, fetch from keyserver, etc + if errors.Is(p.details.SignatureError, pgperrors.ErrUnknownIssuer) || + p.details.SignedBy == nil { + + x := ctx.Printf(0, 0, tcell.StyleDefault.Bold(true), "%s", header) + x += ctx.Printf(x, 0, softErrorStyle, " Unknown ") + x += ctx.Printf(x, 0, tcell.StyleDefault, + " Signed with unknown key (%8X); authenticity unknown", + p.details.SignedByKeyId) + } else if p.details.SignatureError != nil { + x := ctx.Printf(0, 0, tcell.StyleDefault.Bold(true), "%s", header) + x += ctx.Printf(x, 0, errorStyle, " ✗ Invalid! ") + x += ctx.Printf(x, 0, tcell.StyleDefault. + Foreground(tcell.ColorRed).Bold(true), + " This message may have been tampered with! (%s)", + p.details.SignatureError.Error()) + } else { + entity := p.details.SignedBy.Entity + var ident *openpgp.Identity + // TODO: Pick identity more intelligently + for _, ident = range entity.Identities { + break + } + ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', validStyle) + x := ctx.Printf(0, 0, tcell.StyleDefault.Bold(true), "%s", header) + x += ctx.Printf(x, 0, validStyle, "✓ Signed ") + x += ctx.Printf(x, 0, tcell.StyleDefault, + "by %s (%8X)", ident.Name, p.details.SignedByKeyId) + } +} + +func (p *PGPInfo) DrawEncryption(ctx *ui.Context, y int) { + validStyle := tcell.StyleDefault.Foreground(tcell.ColorGreen).Bold(true) + entity := p.details.DecryptedWith.Entity + var ident *openpgp.Identity + // TODO: Pick identity more intelligently + for _, ident = range entity.Identities { + break + } + + x := ctx.Printf(0, y, tcell.StyleDefault.Bold(true), "Encryption ") + x += ctx.Printf(x, y, validStyle, "✓ Encrypted ") + x += ctx.Printf(x, y, tcell.StyleDefault, + "for %s (%8X) ", ident.Name, p.details.DecryptedWith.PublicKey.KeyId) +} + +func (p *PGPInfo) Draw(ctx *ui.Context) { + ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault) + if p.details.IsSigned && p.details.IsEncrypted { + p.DrawSignature(ctx, true) + p.DrawEncryption(ctx, 1) + } else if p.details.IsSigned { + p.DrawSignature(ctx, false) + } else if p.details.IsEncrypted { + p.DrawEncryption(ctx, 0) + } +} + +func (p *PGPInfo) Invalidate() { + p.DoInvalidate(p) +} -- cgit v1.2.3