~rjarry/aerc-devel

5 2

[PATCH aerc v4 0/4] changes to pgp indicator

Details
Message ID
<20220620082222.196328-1-moritz@poldrack.dev>
DKIM signature
pass
Download raw message
From: Moritz Poldrack <git@moritz.sh>

If a message is encrypted and not signed, this shows a small warning
that the message is not signed. It has already happened to me twice that
an encrypted+unsigned message was parsed by my brain as "yup, it's
authentic" even though that's exactly what's not proven.

This patchset also adds a more direct information that a signature could
not be verified instead of the somewhat ambiguous *.

v1 → v2:
	- Default is now pure ASCII in case no UTF-8 capable font is
	  installed.
	- "old" defaults moved to configuration file
	- option to show status on every message

v2 → v3:
	- nothing. There was some mailserver hiccup, so I'm retrying.

v3 → v4:
	- combined sign&encrypt icon is now optional
	- refactored signature validation

Moritz Poldrack (4):
  pgp: add note for encrypted messages that are not signed
  pgp: add customizable icons
  pgp: add icon for unencrypted, unsigned messages if an icon is set
  pgp: refactor signature validity display

 config/aerc.conf      | 10 ++++++
 config/config.go      | 12 +++++++
 doc/aerc-config.5.scd | 35 ++++++++++++++++++++
 widgets/msgviewer.go  |  6 ++--
 widgets/pgpinfo.go    | 75 ++++++++++++++++++++++++++++++-------------
 5 files changed, 112 insertions(+), 26 deletions(-)

--
2.36.1

[PATCH aerc v4 3/4] pgp: add icon for unencrypted, unsigned messages if an icon is set

Details
Message ID
<20220620082222.196328-4-moritz@poldrack.dev>
In-Reply-To
<20220620082222.196328-3-moritz@poldrack.dev> (view parent)
DKIM signature
pass
Download raw message
Patch: +23 -16
From: Moritz Poldrack <git@moritz.sh>

Signed-off-by: Moritz Poldrack <git@moritz.sh>
---
 doc/aerc-config.5.scd |  8 +++++---
 widgets/msgviewer.go  |  6 +++---
 widgets/pgpinfo.go    | 25 +++++++++++++++----------
 3 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 0f3f4b9..c503901 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -254,7 +254,8 @@ These options are configured in the *[ui]* section of aerc.conf.
	Have a look at *aerc-stylesets*(7) as to how a styleset looks like.

*icon-unencrypted*
	The icon to display for unencrypted mails.
	The icon to display for unencrypted mails. The status indicator is only
	displayed if an icon is set.

	Default: ""

@@ -271,9 +272,10 @@ These options are configured in the *[ui]* section of aerc.conf.

*icon-signed-encrypted*
	The icon to display for signed and encrypted mails where the signature
	was successfully verified.
	was successfully verified. The combined icon is only used if set,
	otherwise the signed and encrypted icons are displayed separately.

	Default: [s|e]
	Default: ""

*icon-unknown*
	The icon to display for signed mails which could not be verified due to
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index b41b57c..398c767 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -93,9 +93,9 @@ func NewMessageViewer(acct *AccountView,
		{Strategy: ui.SIZE_EXACT, Size: ui.Const(headerHeight)},
	}

	if msg.MessageDetails() != nil {
	if msg.MessageDetails() != nil || conf.Ui.IconUnencrypted != "" {
		height := 1
		if msg.MessageDetails().IsSigned && msg.MessageDetails().IsEncrypted {
		if msg.MessageDetails() != nil && msg.MessageDetails().IsSigned && msg.MessageDetails().IsEncrypted {
			height = 2
		}
		rows = append(rows, ui.GridSpec{Strategy: ui.SIZE_EXACT, Size: ui.Const(height)})
@@ -125,7 +125,7 @@ func NewMessageViewer(acct *AccountView,
	borderChar := acct.UiConfig().BorderCharHorizontal

	grid.AddChild(header).At(0, 0)
	if msg.MessageDetails() != nil {
	if msg.MessageDetails() != nil || conf.Ui.IconUnencrypted != "" {
		grid.AddChild(NewPGPInfo(msg.MessageDetails(), acct.UiConfig())).At(1, 0)
		grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(2, 0)
		grid.AddChild(switcher).At(3, 0)
diff --git a/widgets/pgpinfo.go b/widgets/pgpinfo.go
index 38118b7..2b21c22 100644
--- a/widgets/pgpinfo.go
+++ b/widgets/pgpinfo.go
@@ -40,7 +40,7 @@ func (p *PGPInfo) DrawSignature(ctx *ui.Context) {
			p.details.SignatureError)
	} else {
		icon := p.uiConfig.IconSigned
		if p.details.IsEncrypted {
		if p.details.IsEncrypted && p.uiConfig.IconSignedEncrypted != "" {
			icon = p.uiConfig.IconSignedEncrypted
		}
		x := ctx.Printf(0, 0, validStyle, "%s Authentic ", icon)
@@ -55,29 +55,34 @@ func (p *PGPInfo) DrawEncryption(ctx *ui.Context, y int) {
	validStyle := p.uiConfig.GetStyle(config.STYLE_SUCCESS)
	defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT)

	// if a sign-encrypt combination icon is set, use that
	icon := p.uiConfig.IconEncrypted
	if p.details.IsSigned && p.details.SignatureValidity == models.Valid {
	if p.details.IsSigned && p.details.SignatureValidity == models.Valid && p.uiConfig.IconSignedEncrypted != "" {
		icon = strings.Repeat(" ", utf8.RuneCountInString(p.uiConfig.IconSignedEncrypted))
	}

	x := ctx.Printf(0, y, validStyle, "%s Encrypted ", icon)
	x += ctx.Printf(x, y, defaultStyle,
		"To %s (%8X) ", p.details.DecryptedWith, p.details.DecryptedWithKeyId)
	x := ctx.Printf(0, y, validStyle, "%s Encrypted", icon)
	x += ctx.Printf(x+1, y, defaultStyle, "To %s (%8X) ", p.details.DecryptedWith, p.details.DecryptedWithKeyId)
	if !p.details.IsSigned {
		x += ctx.Printf(x, y, warningStyle,
			"(message not signed!)")
		x += ctx.Printf(x, y, warningStyle, "(message not signed!)")
	}
}

func (p *PGPInfo) Draw(ctx *ui.Context) {
	warningStyle := p.uiConfig.GetStyle(config.STYLE_WARNING)
	defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT)
	ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', defaultStyle)
	if p.details.IsSigned && p.details.IsEncrypted {

	switch {
	case p.details == nil && p.uiConfig.IconUnencrypted != "":
		x := ctx.Printf(0, 0, warningStyle, "%s ", p.uiConfig.IconUnencrypted)
		ctx.Printf(x, 0, defaultStyle, "message unencrypted and unsigned")
	case p.details.IsSigned && p.details.IsEncrypted:
		p.DrawSignature(ctx)
		p.DrawEncryption(ctx, 1)
	} else if p.details.IsSigned {
	case p.details.IsSigned:
		p.DrawSignature(ctx)
	} else if p.details.IsEncrypted {
	case p.details.IsEncrypted:
		p.DrawEncryption(ctx, 0)
	}
}
-- 
2.36.1

[PATCH aerc v4 1/4] pgp: add note for encrypted messages that are not signed

Details
Message ID
<20220620082222.196328-2-moritz@poldrack.dev>
In-Reply-To
<20220620082222.196328-1-moritz@poldrack.dev> (view parent)
DKIM signature
pass
Download raw message
Patch: +5 -0
From: Moritz Poldrack <git@moritz.sh>

Since there is a prominent checkmark for encrypted messages, it might
not be entirely clear that the contents have not been signed.

Signed-off-by: Moritz Poldrack <git@moritz.sh>
---
 widgets/pgpinfo.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/widgets/pgpinfo.go b/widgets/pgpinfo.go
index 8dc0570..05e2053 100644
--- a/widgets/pgpinfo.go
+++ b/widgets/pgpinfo.go
@@ -44,12 +44,17 @@ func (p *PGPInfo) DrawSignature(ctx *ui.Context) {
}

func (p *PGPInfo) DrawEncryption(ctx *ui.Context, y int) {
	warningStyle := p.uiConfig.GetStyle(config.STYLE_WARNING)
	validStyle := p.uiConfig.GetStyle(config.STYLE_SUCCESS)
	defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT)

	x := ctx.Printf(0, y, validStyle, "✓ Encrypted ")
	x += ctx.Printf(x, y, defaultStyle,
		"To %s (%8X) ", p.details.DecryptedWith, p.details.DecryptedWithKeyId)
	if !p.details.IsSigned {
		x += ctx.Printf(x, y, warningStyle,
			"(message not signed!)")
	}
}

func (p *PGPInfo) Draw(ctx *ui.Context) {
-- 
2.36.1

[PATCH aerc v4 4/4] pgp: refactor signature validity display

Details
Message ID
<20220620082222.196328-5-moritz@poldrack.dev>
In-Reply-To
<20220620082222.196328-4-moritz@poldrack.dev> (view parent)
DKIM signature
pass
Download raw message
Patch: +25 -18
From: Moritz Poldrack <git@moritz.sh>

This commit changes the signature validity display to not use valid as
the default. Now invalid is the default which can cause fewer issues if
an attack vector emerges.

Signed-off-by: Moritz Poldrack <git@moritz.sh>
---
 widgets/pgpinfo.go | 43 +++++++++++++++++++++++++------------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/widgets/pgpinfo.go b/widgets/pgpinfo.go
index 2b21c22..b817277 100644
--- a/widgets/pgpinfo.go
+++ b/widgets/pgpinfo.go
@@ -1,12 +1,14 @@
package widgets

import (
	"fmt"
	"strings"
	"unicode/utf8"

	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib/ui"
	"git.sr.ht/~rjarry/aerc/models"
	"github.com/gdamore/tcell/v2"
)

type PGPInfo struct {
@@ -25,29 +27,34 @@ func (p *PGPInfo) DrawSignature(ctx *ui.Context) {
	validStyle := p.uiConfig.GetStyle(config.STYLE_SUCCESS)
	defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT)

	var icon string
	var indicatorStyle, textstyle tcell.Style
	textstyle = defaultStyle
	var indicatorText, messageText string
	// TODO: Nicer prompt for TOFU, fetch from keyserver, etc
	if p.details.SignatureValidity == models.UnknownEntity ||
		p.details.SignedBy == "" {

		x := ctx.Printf(0, 0, warningStyle, "%s unknown", p.uiConfig.IconUnknown)
		x += ctx.Printf(x, 0, defaultStyle,
			" Signed with unknown key (%8X); authenticity unknown",
			p.details.SignedByKeyId)
	} else if p.details.SignatureValidity != models.Valid {
		x := ctx.Printf(0, 0, errorStyle, "%s Invalid signature!", p.uiConfig.IconInvalid)
		x += ctx.Printf(x, 0, errorStyle,
			" This message may have been tampered with! (%s)",
			p.details.SignatureError)
	} else {
		icon := p.uiConfig.IconSigned
	switch p.details.SignatureValidity {
	case models.UnknownEntity:
		icon = p.uiConfig.IconUnknown
		indicatorStyle = warningStyle
		indicatorText = "unknown"
		messageText = fmt.Sprintf("Signed with unknown key (%8X); authenticity unknown", p.details.SignedByKeyId)
	case models.Valid:
		icon = p.uiConfig.IconSigned
		if p.details.IsEncrypted && p.uiConfig.IconSignedEncrypted != "" {
			icon = p.uiConfig.IconSignedEncrypted
		}
		x := ctx.Printf(0, 0, validStyle, "%s Authentic ", icon)
		x += ctx.Printf(x, 0, defaultStyle,
			"Signature from %s (%8X)",
			p.details.SignedBy, p.details.SignedByKeyId)
		indicatorStyle = validStyle
		indicatorText = "authentic"
		messageText = fmt.Sprintf("Signature from %s (%8X)", p.details.SignedBy, p.details.SignedByKeyId)
	default:
		icon = p.uiConfig.IconInvalid
		indicatorStyle = errorStyle
		indicatorText = "Invalid signature!"
		messageText = fmt.Sprintf("This message may have been tampered with! (%s)", p.details.SignatureError)
	}

	x := ctx.Printf(0, 0, indicatorStyle, "%s %s ", icon, indicatorText)
	ctx.Printf(x, 0, textstyle, messageText)
}

func (p *PGPInfo) DrawEncryption(ctx *ui.Context, y int) {
-- 
2.36.1

[PATCH aerc v4 2/4] pgp: add customizable icons

Details
Message ID
<20220620082222.196328-3-moritz@poldrack.dev>
In-Reply-To
<20220620082222.196328-2-moritz@poldrack.dev> (view parent)
DKIM signature
pass
Download raw message
Patch: +71 -4
From: Moritz Poldrack <git@moritz.sh>

Signed-off-by: Moritz Poldrack <git@moritz.sh>
---
 config/aerc.conf      | 10 ++++++++++
 config/config.go      | 12 ++++++++++++
 doc/aerc-config.5.scd | 33 +++++++++++++++++++++++++++++++++
 widgets/pgpinfo.go    | 20 ++++++++++++++++----
 4 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/config/aerc.conf b/config/aerc.conf
index 8043412..49f3f58 100644
--- a/config/aerc.conf
+++ b/config/aerc.conf
@@ -145,6 +145,16 @@ styleset-name=default
#   consecutive characters in the command or option.
#fuzzy-complete=false

# Uncomment to use UTF-8 symbols to indicate PGP status of messages
#
# Default: ASCII
#icon-unencrypted=
#icon-encrypted=✔
#icon-signed=✔
#icon-signed-encrypted=✔
#icon-unknown=✘
#icon-invalid=⚠

#[ui:account=foo]
#
# Enable threading in the ui. Only works with notmuch:// and imap:// accounts
diff --git a/config/config.go b/config/config.go
index dae32b7..2e0cd58 100644
--- a/config/config.go
+++ b/config/config.go
@@ -50,6 +50,12 @@ type UIConfig struct {
	NewMessageBell      bool          `ini:"new-message-bell"`
	Spinner             string        `ini:"spinner"`
	SpinnerDelimiter    string        `ini:"spinner-delimiter"`
	IconUnencrypted     string        `ini:"icon-unencrypted"`
	IconEncrypted       string        `ini:"icon-encrypted"`
	IconSigned          string        `ini:"icon-signed"`
	IconSignedEncrypted string        `ini:"icon-signed-encrypted"`
	IconUnknown         string        `ini:"icon-unknown"`
	IconInvalid         string        `ini:"icon-invalid"`
	DirListFormat       string        `ini:"dirlist-format"`
	DirListDelay        time.Duration `ini:"dirlist-delay"`
	DirListTree         bool          `ini:"dirlist-tree"`
@@ -682,6 +688,12 @@ func LoadConfigFromFile(root *string, logger *log.Logger) (*AercConfig, error) {
			FuzzyComplete:       false,
			Spinner:             "[..]    , [..]   ,  [..]  ,   [..] ,    [..],   [..] ,  [..]  , [..]   ",
			SpinnerDelimiter:    ",",
			IconUnencrypted:     "",
			IconSigned:          "[s]",
			IconEncrypted:       "[e]",
			IconSignedEncrypted: "",
			IconUnknown:         "[s?]",
			IconInvalid:         "[s!]",
			DirListFormat:       "%n %>r",
			DirListDelay:        200 * time.Millisecond,
			NextMessageOnDelete: true,
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 47ae752..0f3f4b9 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -253,6 +253,39 @@ These options are configured in the *[ui]* section of aerc.conf.

	Have a look at *aerc-stylesets*(7) as to how a styleset looks like.

*icon-unencrypted*
	The icon to display for unencrypted mails.

	Default: ""

*icon-encrypted*
	The icon to display for encrypted mails.

	Default: [e]

*icon-signed*
	The icon to display for signed mails where the signature was
	successfully validated.

	Default: [s]

*icon-signed-encrypted*
	The icon to display for signed and encrypted mails where the signature
	was successfully verified.

	Default: [s|e]

*icon-unknown*
	The icon to display for signed mails which could not be verified due to
	the key being unknown.

	Default: [s?]

*icon-invalid*
	The icon to display for signed mails where verification failed.

	Default: [s!]

*fuzzy-complete*
	When typing a command or option, the popover will now show not only the
	items /starting/ with the string input by the user, but it will also show
diff --git a/widgets/pgpinfo.go b/widgets/pgpinfo.go
index 05e2053..38118b7 100644
--- a/widgets/pgpinfo.go
+++ b/widgets/pgpinfo.go
@@ -1,6 +1,9 @@
package widgets

import (
	"strings"
	"unicode/utf8"

	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib/ui"
	"git.sr.ht/~rjarry/aerc/models"
@@ -26,17 +29,21 @@ func (p *PGPInfo) DrawSignature(ctx *ui.Context) {
	if p.details.SignatureValidity == models.UnknownEntity ||
		p.details.SignedBy == "" {

		x := ctx.Printf(0, 0, warningStyle, "*")
		x := ctx.Printf(0, 0, warningStyle, "%s unknown", p.uiConfig.IconUnknown)
		x += ctx.Printf(x, 0, defaultStyle,
			" Signed with unknown key (%8X); authenticity unknown",
			p.details.SignedByKeyId)
	} else if p.details.SignatureValidity != models.Valid {
		x := ctx.Printf(0, 0, errorStyle, "Invalid signature!")
		x := ctx.Printf(0, 0, errorStyle, "%s Invalid signature!", p.uiConfig.IconInvalid)
		x += ctx.Printf(x, 0, errorStyle,
			" This message may have been tampered with! (%s)",
			p.details.SignatureError)
	} else {
		x := ctx.Printf(0, 0, validStyle, "✓ Authentic ")
		icon := p.uiConfig.IconSigned
		if p.details.IsEncrypted {
			icon = p.uiConfig.IconSignedEncrypted
		}
		x := ctx.Printf(0, 0, validStyle, "%s Authentic ", icon)
		x += ctx.Printf(x, 0, defaultStyle,
			"Signature from %s (%8X)",
			p.details.SignedBy, p.details.SignedByKeyId)
@@ -48,7 +55,12 @@ func (p *PGPInfo) DrawEncryption(ctx *ui.Context, y int) {
	validStyle := p.uiConfig.GetStyle(config.STYLE_SUCCESS)
	defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT)

	x := ctx.Printf(0, y, validStyle, "✓ Encrypted ")
	icon := p.uiConfig.IconEncrypted
	if p.details.IsSigned && p.details.SignatureValidity == models.Valid {
		icon = strings.Repeat(" ", utf8.RuneCountInString(p.uiConfig.IconSignedEncrypted))
	}

	x := ctx.Printf(0, y, validStyle, "%s Encrypted ", icon)
	x += ctx.Printf(x, y, defaultStyle,
		"To %s (%8X) ", p.details.DecryptedWith, p.details.DecryptedWithKeyId)
	if !p.details.IsSigned {
-- 
2.36.1

Re: [PATCH aerc v4 4/4] pgp: refactor signature validity display

Details
Message ID
<CKW20LWYEUD0.39WQ2OQOO06MC@TimBook-Arch>
In-Reply-To
<20220620082222.196328-5-moritz@poldrack.dev> (view parent)
DKIM signature
pass
Download raw message
On Mon Jun 20, 2022 at 3:22 AM CDT, Moritz Poldrack wrote:
> +	switch p.details.SignatureValidity {
> +	case models.UnknownEntity:
> +		icon = p.uiConfig.IconUnknown
> +		indicatorStyle = warningStyle
> +		indicatorText = "unknown"

I think this should be "Unknown" (capital U). See below too

> +		messageText = fmt.Sprintf("Signed with unknown key (%8X); authenticity unknown", p.details.SignedByKeyId)
> +	case models.Valid:
> +		icon = p.uiConfig.IconSigned
>  		if p.details.IsEncrypted && p.uiConfig.IconSignedEncrypted != "" {
>  			icon = p.uiConfig.IconSignedEncrypted
>  		}
> -		x := ctx.Printf(0, 0, validStyle, "%s Authentic ", icon)
> -		x += ctx.Printf(x, 0, defaultStyle,
> -			"Signature from %s (%8X)",
> -			p.details.SignedBy, p.details.SignedByKeyId)
> +		indicatorStyle = validStyle
> +		indicatorText = "authentic"

Same here, this should be "Authentic" (capital A).

> +		messageText = fmt.Sprintf("Signature from %s (%8X)", p.details.SignedBy, p.details.SignedByKeyId)
> +	default:
> +		icon = p.uiConfig.IconInvalid
> +		indicatorStyle = errorStyle
> +		indicatorText = "Invalid signature!"
> +		messageText = fmt.Sprintf("This message may have been tampered with! (%s)", p.details.SignatureError)
>  	}
> +
> +	x := ctx.Printf(0, 0, indicatorStyle, "%s %s ", icon, indicatorText)
> +	ctx.Printf(x, 0, textstyle, messageText)
>  }
Details
Message ID
<CKW2428AHV7S.58SCOZC4OYT3@TimBook-Arch>
In-Reply-To
<20220620082222.196328-1-moritz@poldrack.dev> (view parent)
DKIM signature
pass
Download raw message
On Mon Jun 20, 2022 at 3:22 AM CDT, Moritz Poldrack wrote:
> From: Moritz Poldrack <git@moritz.sh>
>
> If a message is encrypted and not signed, this shows a small warning
> that the message is not signed. It has already happened to me twice that
> an encrypted+unsigned message was parsed by my brain as "yup, it's
> authentic" even though that's exactly what's not proven.
>
> This patchset also adds a more direct information that a signature could
> not be verified instead of the somewhat ambiguous *.
>
> v1 → v2:
> 	- Default is now pure ASCII in case no UTF-8 capable font is
> 	  installed.
> 	- "old" defaults moved to configuration file
> 	- option to show status on every message
>
> v2 → v3:
> 	- nothing. There was some mailserver hiccup, so I'm retrying.
>
> v3 → v4:
> 	- combined sign&encrypt icon is now optional
> 	- refactored signature validation
>
> Moritz Poldrack (4):
>   pgp: add note for encrypted messages that are not signed
>   pgp: add customizable icons
>   pgp: add icon for unencrypted, unsigned messages if an icon is set
>   pgp: refactor signature validity display
>
>  config/aerc.conf      | 10 ++++++
>  config/config.go      | 12 +++++++
>  doc/aerc-config.5.scd | 35 ++++++++++++++++++++
>  widgets/msgviewer.go  |  6 ++--
>  widgets/pgpinfo.go    | 75 ++++++++++++++++++++++++++++++-------------
>  5 files changed, 112 insertions(+), 26 deletions(-)
>
> --
> 2.36.1

I tested v4 and everything looks good. The refactor is nicer too, I
agree the default shouldn't be "Valid". Only thing I noticed was the
capital letters on Unknown and Authentic I mentioned in response to v4
4/4.
Reply to thread Export thread (mbox)