~rjarry/aerc-devel

aerc: pgp: PGP/MIME encryption and signing v1 APPLIED

Koni Marti: 4
 pgp: PGP/MIME encryption and signing
 pgp: update openpgp packages (go-crypto and go-pgpmail)
 pgp: PGP/MIME signing for outgoing emails
 pgp: PGP/MIME encryption for outgoing emails

 21 files changed, 556 insertions(+), 95 deletions(-)
#654840 .build.yml success
Koni Marti, Dec 30, 2021 at 10:25:
Next
Koni Marti, Dec 30, 2021 at 10:25:
Next
Koni Marti, Dec 30, 2021 at 10:25:
Next
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~rjarry/aerc-devel/patches/27588/mbox | git am -3
Learn more about email & git

[PATCH aerc] pgp: PGP/MIME encryption and signing Export this patch

implements PGP/MIME encryption and signing of outgoing emails using the
keys from the internal keystore. The heavy lifiting is done in the go-pgpmail
package that provides the Encrypt() and Sign() functions.

The Sign() function needs access to the private key of the signer and the
Encrypt() function requires also the public keys of all recipients. The
private and public keys should thus be stored in aerc's keystore.

Public and private keys can be exported from gpg into the aerc keystore as
follows:
$ gpg --export > ~/.local/share/aerc/keyring.asc
$ gpg --export-secret-keys  >> ~/.local/share/aerc/keyring.asc

The exact location of the keyring file depends on the xdg.DataHome()
variable, but it's usually at ~/.local/share/aerc.

The github.com/x/crypto package has been consistently replaced by the
ProtonMail/go-crypto fork.

The PGP encryption (none, encryption or signing) can be enabled with the ':pgp'
command at the review window before sending the email. The command expects one
argument that can either be "none", "encrypt", or "sign". Default is none.
Replaces this patch https://lists.sr.ht/~rjarry/aerc-devel/patches/27550
Fixes ~rjarry/aerc/6
Signed-off-by: Koni Marti <koni.marti@gmail.com>
---
 commands/compose/pgp.go |  45 +++++++++
 go.mod                  |   5 +-
 go.sum                  |  21 +----
 lib/keystore.go         |  28 +++++-
 lib/messageview.go      |   2 +-
 widgets/aerc.go         |   2 +-
 widgets/compose.go      | 196 +++++++++++++++++++++++++++++++++++-----
 widgets/pgpinfo.go      |   4 +-
 8 files changed, 258 insertions(+), 45 deletions(-)
 create mode 100644 commands/compose/pgp.go

diff --git a/commands/compose/pgp.go b/commands/compose/pgp.go
new file mode 100644
index 0000000..90913ce
--- /dev/null
+++ b/commands/compose/pgp.go
@@ -0,0 +1,45 @@
package compose

import (
	"errors"
	"strings"
	"time"

	"git.sr.ht/~rjarry/aerc/widgets"
)

type PGP struct{}

func init() {
	register(PGP{})
}

func (PGP) Aliases() []string {
	return []string{"pgp"}
}

func (PGP) Complete(aerc *widgets.Aerc, args []string) []string {
	return nil
}

func (PGP) Execute(aerc *widgets.Aerc, args []string) error {
	if len(args) != 2 {
		return errors.New("Usage: pgp [none|encrypt|sign]")
	}
	composer, _ := aerc.SelectedTab().(*widgets.Composer)

	var enc widgets.PGPEncryption
	switch arg := args[1]; {
	case strings.HasPrefix("encrypt", arg):
		enc = widgets.PGPEncrypt
		aerc.PushStatus("Message will be PGP encrypted...", 10*time.Second)
	case strings.HasPrefix("sign", arg):
		enc = widgets.PGPSign
		aerc.PushStatus("Message will be PGP signed...", 10*time.Second)
	default:
		enc = widgets.PGPNone
		aerc.PushStatus("No PGP encryption...", 10*time.Second)
	}
	composer.SetPGPEncryption(enc)
	return nil
}
diff --git a/go.mod b/go.mod
index 181f6a9..3a18379 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.13

require (
	git.sr.ht/~sircmpwn/getopt v1.0.0
	github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab
	github.com/creack/pty v1.1.17
	github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964
	github.com/ddevault/go-libvterm v0.0.0-20190526194226-b7d861da3810
@@ -11,7 +12,7 @@ require (
	github.com/emersion/go-imap-sortthread v1.2.0
	github.com/emersion/go-maildir v0.2.0
	github.com/emersion/go-message v0.15.0
	github.com/emersion/go-pgpmail v0.1.0
	github.com/emersion/go-pgpmail v0.2.0
	github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac
	github.com/emersion/go-smtp v0.15.0
	github.com/fsnotify/fsnotify v1.5.1
@@ -31,7 +32,7 @@ require (
	github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab
	github.com/stretchr/testify v1.4.0
	github.com/zenhack/go.notmuch v0.0.0-20211022191430-4d57e8ad2a8b
	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
	golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
	golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
	golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5
	golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021 // indirect
diff --git a/go.sum b/go.sum
index 2635c9c..00e4036 100644
--- a/go.sum
+++ b/go.sum
@@ -37,6 +37,9 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3 h1:JW27/kGLQzeM1Fxg5YQhdkTEAU7HIAHMgSag35zVTnY=
github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3/go.mod h1:Pxr7w4gA2ikI4sWyYwEffm+oew1WAJHzG1SiDpQMkrI=
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab h1:5FiL/TCaiKCss/BLMIACDxxadYrx767l9kh0qYX+sLQ=
github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/brunnre8/go.notmuch v0.0.0-20201126061756-caa2daf7093c h1:dh58QrW3/S/aCnQPFoeRRE9zMauKooDFd5zh1dLtxXs=
github.com/brunnre8/go.notmuch v0.0.0-20201126061756-caa2daf7093c/go.mod h1:zJtFvR3NinVdmBiLyB4MyXKmqyVfZEb2cK97ISfTgV8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -64,17 +67,14 @@ github.com/emersion/go-maildir v0.2.0/go.mod h1:I2j27lND/SRLgxROe50Vam81MSaqPFvJ
github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
github.com/emersion/go-message v0.15.0 h1:urgKGqt2JAc9NFJcgncQcohHdiYb803YTH9OQwHBHIY=
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
github.com/emersion/go-pgpmail v0.1.0 h1:+NuU9UtGnmmKvmI8evxmTAPXfUod6Gbf2uYT7rxTZ7w=
github.com/emersion/go-pgpmail v0.1.0/go.mod h1:9Sy6uI+dlTN56tcWMtBQHqNDeea27xYItaiZ/3XC76g=
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b h1:uhWtEWBHgop1rqEk2klKaxPAkVDCXexai6hSuRQ7Nvs=
github.com/emersion/go-pgpmail v0.2.0 h1:BU9kEGQcDVXi6n0v3JBsWAikyo63xsUGZ1lnVaWa6ks=
github.com/emersion/go-pgpmail v0.2.0/go.mod h1:8mQ8Rpn+w28DDaiP8HvJuZjSAymaWr87K3zA/bwwkU0=
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac h1:tn/OQ2PmwQ0XFVgAHfjlLyqMewry25Rz7jWnVoh4Ggs=
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe h1:40SWqY0zE3qCi6ZrtTf5OUdNm5lDnGnjRSq9GgmeTrg=
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
@@ -108,7 +108,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -161,11 +160,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI=
github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQxB52A=
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
@@ -191,7 +188,6 @@ github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab h1:ZjX6I48eZSFetP
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -251,7 +247,6 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@@ -267,7 +262,6 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5 h1:v79phzBz03tsVCUTbvTBmmC3CUXF5mKYt7DA4ZVldpM=
golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@@ -296,7 +290,6 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -312,7 +305,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021 h1:giLT+HuUP/gXYrG2Plg9WTjj4qhfgaW424ZIFog3rlk=
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -392,7 +384,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
@@ -449,11 +440,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/lib/keystore.go b/lib/keystore.go
index dcdbd74..0b9d41a 100644
--- a/lib/keystore.go
+++ b/lib/keystore.go
@@ -1,13 +1,14 @@
package lib

import (
	"fmt"
	"io"
	"os"
	"path"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/ProtonMail/go-crypto/openpgp/packet"
	"github.com/kyoh86/xdg"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/packet"
)

var (
@@ -52,6 +53,29 @@ func UnlockKeyring() {
	os.Remove(lockpath)
}

func GetEntityByEmail(email string) (e *openpgp.Entity, err error) {
	for _, entity := range Keyring {
		ident := entity.PrimaryIdentity()
		if ident != nil && ident.UserId.Email == email {
			return entity, nil
		}
	}
	return nil, fmt.Errorf("entity not found in keyring")
}

func GetSignerEntityByEmail(email string) (e *openpgp.Entity, err error) {
	for _, key := range Keyring.DecryptionKeys() {
		if key.Entity == nil {
			continue
		}
		ident := key.Entity.PrimaryIdentity()
		if ident != nil && ident.UserId.Email == email {
			return key.Entity, nil
		}
	}
	return nil, fmt.Errorf("entity not found in keyring")
}

func ImportKeys(r io.Reader) error {
	keys, err := openpgp.ReadKeyRing(r)
	if err != nil {
diff --git a/lib/messageview.go b/lib/messageview.go
index 532d2c8..8db7994 100644
--- a/lib/messageview.go
+++ b/lib/messageview.go
@@ -5,10 +5,10 @@ import (
	"io"
	"io/ioutil"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/emersion/go-message"
	_ "github.com/emersion/go-message/charset"
	"github.com/emersion/go-pgpmail"
	"golang.org/x/crypto/openpgp"

	"git.sr.ht/~rjarry/aerc/models"
	"git.sr.ht/~rjarry/aerc/worker/lib"
diff --git a/widgets/aerc.go b/widgets/aerc.go
index b84dd87..3c52f7e 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -9,10 +9,10 @@ import (
	"strings"
	"time"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/emersion/go-message/mail"
	"github.com/gdamore/tcell/v2"
	"github.com/google/shlex"
	"golang.org/x/crypto/openpgp"

	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib"
diff --git a/widgets/compose.go b/widgets/compose.go
index d3e57c5..ff20dc0 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -15,7 +15,9 @@ import (
	"strings"
	"time"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/emersion/go-message/mail"
	"github.com/emersion/go-pgpmail"
	"github.com/gdamore/tcell/v2"
	"github.com/mattn/go-runewidth"
	"github.com/mitchellh/go-homedir"
@@ -23,6 +25,7 @@ import (

	"git.sr.ht/~rjarry/aerc/completer"
	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib"
	"git.sr.ht/~rjarry/aerc/lib/format"
	"git.sr.ht/~rjarry/aerc/lib/templates"
	"git.sr.ht/~rjarry/aerc/lib/ui"
@@ -30,6 +33,14 @@ import (
	"git.sr.ht/~rjarry/aerc/worker/types"
)

type PGPEncryption int

const (
	PGPNone PGPEncryption = iota
	PGPEncrypt
	PGPSign
)

type Composer struct {
	editors map[string]*headerEditor // indexes in lower case (from / cc / bcc)
	header  *mail.Header
@@ -40,14 +51,15 @@ type Composer struct {
	acct       *AccountView
	aerc       *Aerc

	attachments []string
	editor      *Terminal
	email       *os.File
	grid        *ui.Grid
	heditors    *ui.Grid // from, to, cc display a user can jump to
	review      *reviewMessage
	worker      *types.Worker
	completer   *completer.Completer
	attachments   []string
	editor        *Terminal
	email         *os.File
	grid          *ui.Grid
	heditors      *ui.Grid // from, to, cc display a user can jump to
	review        *reviewMessage
	worker        *types.Worker
	completer     *completer.Completer
	pgpencryption PGPEncryption

	layout    HeaderLayout
	focusable []ui.MouseableDrawableInteractive
@@ -172,6 +184,10 @@ func (c *Composer) Sent() bool {
	return c.sent
}

func (c *Composer) SetPGPEncryption(enc PGPEncryption) {
	c.pgpencryption = enc
}

// Note: this does not reload the editor. You must call this before the first
// Draw() call.
func (c *Composer) SetContents(reader io.Reader) *Composer {
@@ -392,34 +408,127 @@ func (c *Composer) PrepareHeader() (*mail.Header, error) {
	return c.header, nil
}

func getSenderEmail(c *Composer) (string, error) {
	// add the from: field also to the 'recipients' list
	if c.acctConfig.From == "" {
		return "", errors.New("No 'From' configured for this account")
	}
	from, err := mail.ParseAddress(c.acctConfig.From)
	if err != nil {
		return "", errors.Wrap(err, "ParseAddress(config.From)")
	}
	return from.Address, nil
}

func getRecipientsEmail(c *Composer) ([]string, error) {
	h, err := c.PrepareHeader()
	if err != nil {
		return nil, errors.Wrap(err, "PrepareHeader")
	}

	// collect all 'recipients' from header (to:, cc:, bcc:)
	rcpts := make(map[string]bool)
	for _, key := range []string{"to", "cc", "bcc"} {
		list, err := h.AddressList(key)
		if err != nil {
			continue
		}
		for _, entry := range list {
			if entry != nil {
				rcpts[entry.Address] = true
			}
		}
	}

	// return email addresses as string slice
	results := []string{}
	for email, _ := range rcpts {
		results = append(results, email)
	}
	return results, nil
}

func (c *Composer) WriteMessage(header *mail.Header, writer io.Writer) error {
	if err := c.reloadEmail(); err != nil {
		return err
	}

	if len(c.attachments) == 0 {
		// don't create a multipart email if we only have text
		return writeInlineBody(header, c.email, writer)
	// PGPNone
	if c.pgpencryption == PGPNone {
		return writeMsgImpl(c, header, writer)
	}

	// otherwise create a multipart email,
	// with a multipart/alternative part for the text
	w, err := mail.CreateWriter(writer, *header)
	// PGPEncrypt & PGPSign
	signer, err := getSigner(c)
	if err != nil {
		return errors.Wrap(err, "CreateWriter")
		return err
	}
	defer w.Close()

	if err := writeMultipartBody(c.email, w); err != nil {
		return errors.Wrap(err, "writeMultipartBody")
	var signedHeader mail.Header
	signedHeader.SetContentType("text/plain", nil)

	to := []*openpgp.Entity{}
	if c.pgpencryption == PGPEncrypt {
		rcpts, err := getRecipientsEmail(c)
		if err != nil {
			return err
		}
		for _, rcpt := range rcpts {
			toEntity, err := lib.GetEntityByEmail(rcpt)
			if err != nil {
				return errors.Wrap(err, "no key for "+rcpt)
			}
			to = append(to, toEntity)
		}
	}

	for _, a := range c.attachments {
		if err := writeAttachment(a, w); err != nil {
			return errors.Wrap(err, "writeAttachment")
	var buf bytes.Buffer
	var cleartext io.WriteCloser

	if c.pgpencryption == PGPEncrypt {
		// PGPEncrypt
		cleartext, err = pgpmail.Encrypt(&buf, header.Header.Header, to, signer, nil)
		if err != nil {
			return err
		}
	} else if c.pgpencryption == PGPSign {
		// PGPSign
		cleartext, err = pgpmail.Sign(&buf, header.Header.Header, signer, nil)
		if err != nil {
			return err
		}
	} else {
		return fmt.Errorf("implementation error in pgpencryption")
	}
	err = writeMsgImpl(c, &signedHeader, cleartext)
	if err != nil {
		return err
	}
	cleartext.Close()
	io.Copy(writer, &buf)
	return nil
}

func writeMsgImpl(c *Composer, header *mail.Header, writer io.Writer) error {
	if len(c.attachments) == 0 {
		// no attachements
		return writeInlineBody(header, c.email, writer)
	} else {
		// with attachements
		w, err := mail.CreateWriter(writer, *header)
		if err != nil {
			return errors.Wrap(err, "CreateWriter")
		}
		if err := writeMultipartBody(c.email, w); err != nil {
			return errors.Wrap(err, "writeMultipartBody")
		}
		for _, a := range c.attachments {
			if err := writeAttachment(a, w); err != nil {
				return errors.Wrap(err, "writeAttachment")
			}
		}
		w.Close()
	}
	return nil
}

@@ -884,3 +993,48 @@ func (rm *reviewMessage) OnInvalidate(fn func(ui.Drawable)) {
func (rm *reviewMessage) Draw(ctx *ui.Context) {
	rm.grid.Draw(ctx)
}

func getSigner(c *Composer) (signer *openpgp.Entity, err error) {
	signerEmail, err := getSenderEmail(c)
	if err != nil {
		return nil, err
	}
	signer, err = lib.GetSignerEntityByEmail(signerEmail)
	if err != nil {
		return nil, err
	}

	if signer.PrivateKey == nil {
		return nil, fmt.Errorf("no private key for signer")
	}

	if !signer.PrivateKey.Encrypted {
		return signer, nil
	}

	// decrypt key with password
	// FIXME: can we combine this with aerc.DecryptKeys()?
	var pass string
	ident := signer.PrimaryIdentity()
	chPass, chErr := c.aerc.GetPassword("Decrypt PGP private key",
		fmt.Sprintf("Enter password for %s (%8X)\nPress <ESC> to cancel",
			ident.Name, signer.PrimaryKey.KeyId))
	for {
		select {
		case err = <-chErr:
			if err != nil {
				goto exitWithError
			}
			pass = <-chPass
			err = signer.PrivateKey.Decrypt([]byte(pass))
			if err == nil {
				return signer, nil
			}
		default:
			c.aerc.ui.Tick()
		}
	}

exitWithError:
	return nil, fmt.Errorf("no signer found")
}
diff --git a/widgets/pgpinfo.go b/widgets/pgpinfo.go
index febf29a..6c07ed9 100644
--- a/widgets/pgpinfo.go
+++ b/widgets/pgpinfo.go
@@ -6,8 +6,8 @@ import (
	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib/ui"

	"golang.org/x/crypto/openpgp"
	pgperrors "golang.org/x/crypto/openpgp/errors"
	"github.com/ProtonMail/go-crypto/openpgp"
	pgperrors "github.com/ProtonMail/go-crypto/openpgp/errors"
)

type PGPInfo struct {
-- 
2.34.1
aerc/patches/.build.yml: SUCCESS in 58s

[pgp: PGP/MIME encryption and signing][0] from [Koni Marti][1]

[0]: https://lists.sr.ht/~rjarry/aerc-devel/patches/27588
[1]: mailto:koni.marti@gmail.com

✓ #654840 SUCCESS aerc/patches/.build.yml https://builds.sr.ht/~rjarry/job/654840
Hi Koni,

Sorry not to have replied earlier. The holidays season is slowing me
down a bit :)

Koni Marti, Dec 22, 2021 at 16:08:

[PATCH aerc v2 1/3] pgp: update openpgp packages (go-crypto and go-pgpmail) Export this patch

Replaces golang.org/x/crypto with github.com/ProtonMail/go-crypto
consistently and updates go-pgpmail to v0.2.0

Signed-off-by: Koni Marti <koni.marti@gmail.com>
---
 go.mod             |  5 +++--
 go.sum             | 21 +++++----------------
 lib/keystore.go    |  4 ++--
 lib/messageview.go |  2 +-
 widgets/aerc.go    |  2 +-
 widgets/compose.go |  1 +
 widgets/pgpinfo.go |  4 ++--
 7 files changed, 15 insertions(+), 24 deletions(-)

diff --git a/go.mod b/go.mod
index 181f6a9..3a18379 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.13

require (
	git.sr.ht/~sircmpwn/getopt v1.0.0
	github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab
	github.com/creack/pty v1.1.17
	github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964
	github.com/ddevault/go-libvterm v0.0.0-20190526194226-b7d861da3810
@@ -11,7 +12,7 @@ require (
	github.com/emersion/go-imap-sortthread v1.2.0
	github.com/emersion/go-maildir v0.2.0
	github.com/emersion/go-message v0.15.0
	github.com/emersion/go-pgpmail v0.1.0
	github.com/emersion/go-pgpmail v0.2.0
	github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac
	github.com/emersion/go-smtp v0.15.0
	github.com/fsnotify/fsnotify v1.5.1
@@ -31,7 +32,7 @@ require (
	github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab
	github.com/stretchr/testify v1.4.0
	github.com/zenhack/go.notmuch v0.0.0-20211022191430-4d57e8ad2a8b
	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
	golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect
	golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
	golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5
	golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021 // indirect
diff --git a/go.sum b/go.sum
index 2635c9c..00e4036 100644
--- a/go.sum
+++ b/go.sum
@@ -37,6 +37,9 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3 h1:JW27/kGLQzeM1Fxg5YQhdkTEAU7HIAHMgSag35zVTnY=
github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3/go.mod h1:Pxr7w4gA2ikI4sWyYwEffm+oew1WAJHzG1SiDpQMkrI=
github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab h1:5FiL/TCaiKCss/BLMIACDxxadYrx767l9kh0qYX+sLQ=
github.com/ProtonMail/go-crypto v0.0.0-20211221144345-a4f6767435ab/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
github.com/brunnre8/go.notmuch v0.0.0-20201126061756-caa2daf7093c h1:dh58QrW3/S/aCnQPFoeRRE9zMauKooDFd5zh1dLtxXs=
github.com/brunnre8/go.notmuch v0.0.0-20201126061756-caa2daf7093c/go.mod h1:zJtFvR3NinVdmBiLyB4MyXKmqyVfZEb2cK97ISfTgV8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -64,17 +67,14 @@ github.com/emersion/go-maildir v0.2.0/go.mod h1:I2j27lND/SRLgxROe50Vam81MSaqPFvJ
github.com/emersion/go-message v0.11.1/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY=
github.com/emersion/go-message v0.15.0 h1:urgKGqt2JAc9NFJcgncQcohHdiYb803YTH9OQwHBHIY=
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
github.com/emersion/go-pgpmail v0.1.0 h1:+NuU9UtGnmmKvmI8evxmTAPXfUod6Gbf2uYT7rxTZ7w=
github.com/emersion/go-pgpmail v0.1.0/go.mod h1:9Sy6uI+dlTN56tcWMtBQHqNDeea27xYItaiZ/3XC76g=
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b h1:uhWtEWBHgop1rqEk2klKaxPAkVDCXexai6hSuRQ7Nvs=
github.com/emersion/go-pgpmail v0.2.0 h1:BU9kEGQcDVXi6n0v3JBsWAikyo63xsUGZ1lnVaWa6ks=
github.com/emersion/go-pgpmail v0.2.0/go.mod h1:8mQ8Rpn+w28DDaiP8HvJuZjSAymaWr87K3zA/bwwkU0=
github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21 h1:OJyUGMJTzHTd1XQp98QTaHernxMYzRaOasRir9hUlFQ=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac h1:tn/OQ2PmwQ0XFVgAHfjlLyqMewry25Rz7jWnVoh4Ggs=
github.com/emersion/go-sasl v0.0.0-20211008083017-0b9dcfb154ac/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe h1:40SWqY0zE3qCi6ZrtTf5OUdNm5lDnGnjRSq9GgmeTrg=
github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
@@ -108,7 +108,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@@ -161,11 +160,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kyoh86/xdg v1.2.0 h1:CERuT/ShdTDj+A2UaX3hQ3mOV369+Sj+wyn2nIRIIkI=
github.com/kyoh86/xdg v1.2.0/go.mod h1:/mg8zwu1+qe76oTFUBnyS7rJzk7LLC0VGEzJyJ19DHs=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/martinlindhe/base36 v1.0.0 h1:eYsumTah144C0A8P1T/AVSUk5ZoLnhfYFM3OGQxB52A=
github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
@@ -191,7 +188,6 @@ github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab h1:ZjX6I48eZSFetP
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -251,7 +247,6 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@@ -267,7 +262,6 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5 h1:v79phzBz03tsVCUTbvTBmmC3CUXF5mKYt7DA4ZVldpM=
golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@@ -296,7 +290,6 @@ golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -312,7 +305,6 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021 h1:giLT+HuUP/gXYrG2Plg9WTjj4qhfgaW424ZIFog3rlk=
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -392,7 +384,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
@@ -449,11 +440,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/lib/keystore.go b/lib/keystore.go
index dcdbd74..df048f4 100644
--- a/lib/keystore.go
+++ b/lib/keystore.go
@@ -5,9 +5,9 @@ import (
	"os"
	"path"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/ProtonMail/go-crypto/openpgp/packet"
	"github.com/kyoh86/xdg"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/packet"
)

var (
diff --git a/lib/messageview.go b/lib/messageview.go
index 532d2c8..8db7994 100644
--- a/lib/messageview.go
+++ b/lib/messageview.go
@@ -5,10 +5,10 @@ import (
	"io"
	"io/ioutil"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/emersion/go-message"
	_ "github.com/emersion/go-message/charset"
	"github.com/emersion/go-pgpmail"
	"golang.org/x/crypto/openpgp"

	"git.sr.ht/~rjarry/aerc/models"
	"git.sr.ht/~rjarry/aerc/worker/lib"
diff --git a/widgets/aerc.go b/widgets/aerc.go
index b84dd87..3c52f7e 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -9,10 +9,10 @@ import (
	"strings"
	"time"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/emersion/go-message/mail"
	"github.com/gdamore/tcell/v2"
	"github.com/google/shlex"
	"golang.org/x/crypto/openpgp"

	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib"
diff --git a/widgets/compose.go b/widgets/compose.go
index d3e57c5..5ca0932 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -15,6 +15,7 @@ import (
	"strings"
	"time"

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/emersion/go-message/mail"
	"github.com/gdamore/tcell/v2"
	"github.com/mattn/go-runewidth"
diff --git a/widgets/pgpinfo.go b/widgets/pgpinfo.go
index febf29a..6c07ed9 100644
--- a/widgets/pgpinfo.go
+++ b/widgets/pgpinfo.go
@@ -6,8 +6,8 @@ import (
	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib/ui"

	"golang.org/x/crypto/openpgp"
	pgperrors "golang.org/x/crypto/openpgp/errors"
	"github.com/ProtonMail/go-crypto/openpgp"
	pgperrors "github.com/ProtonMail/go-crypto/openpgp/errors"
)

type PGPInfo struct {
-- 
2.34.1
Koni Marti, Dec 30, 2021 at 10:25:

[PATCH aerc v2 2/3] pgp: PGP/MIME signing for outgoing emails Export this patch

implements PGP/MIME signing with go-pgpmail. The Sign() function of
go-pgpmail requires a private (signing) key. The signing key which matches
the senders email address (from field in email header) is looked up
in aerc's copy of the keyring.

Private keys can be exported from gpg into aerc as follows:
$ gpg --export-secret-keys  >> ~/.local/share/aerc/keyring.asc

A message is signed with the ":sign" command. The sign command sets
a bool flag in the Composer struct. Using the command repeatedly will
toggle the flag.

References: https://todo.sr.ht/~rjarry/aerc/6
Signed-off-by: Koni Marti <koni.marti@gmail.com>
---
 commands/compose/sign.go |  44 +++++++++++++++
 lib/keystore.go          |  14 +++++
 widgets/compose.go       | 113 +++++++++++++++++++++++++++++++++------
 3 files changed, 154 insertions(+), 17 deletions(-)
 create mode 100644 commands/compose/sign.go

diff --git a/commands/compose/sign.go b/commands/compose/sign.go
new file mode 100644
index 0000000..eb985e9
--- /dev/null
+++ b/commands/compose/sign.go
@@ -0,0 +1,44 @@
package compose

import (
	"errors"
	"time"

	"git.sr.ht/~rjarry/aerc/widgets"
)

type Sign struct{}

func init() {
	register(Sign{})
}

func (Sign) Aliases() []string {
	return []string{"sign"}
}

func (Sign) Complete(aerc *widgets.Aerc, args []string) []string {
	return nil
}

func (Sign) Execute(aerc *widgets.Aerc, args []string) error {
	if len(args) != 1 {
		return errors.New("Usage: sign")
	}

	composer, _ := aerc.SelectedTab().(*widgets.Composer)

	composer.SetSign(!composer.Sign())

	var statusline string

	if composer.Sign() {
		statusline = "Message will be signed."
	} else {
		statusline = "Message will not be signed."
	}

	aerc.PushStatus(statusline, 10*time.Second)

	return nil
}
diff --git a/lib/keystore.go b/lib/keystore.go
index df048f4..c211067 100644
--- a/lib/keystore.go
+++ b/lib/keystore.go
@@ -1,6 +1,7 @@
package lib

import (
	"fmt"
	"io"
	"os"
	"path"
@@ -52,6 +53,19 @@ func UnlockKeyring() {
	os.Remove(lockpath)
}

func GetSignerEntityByEmail(email string) (e *openpgp.Entity, err error) {
	for _, key := range Keyring.DecryptionKeys() {
		if key.Entity == nil {
			continue
		}
		ident := key.Entity.PrimaryIdentity()
		if ident != nil && ident.UserId.Email == email {
			return key.Entity, nil
		}
	}
	return nil, fmt.Errorf("entity not found in keyring")
}

func ImportKeys(r io.Reader) error {
	keys, err := openpgp.ReadKeyRing(r)
	if err != nil {
diff --git a/widgets/compose.go b/widgets/compose.go
index 5ca0932..6b7f5cd 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -17,6 +17,7 @@ import (

	"github.com/ProtonMail/go-crypto/openpgp"
	"github.com/emersion/go-message/mail"
	"github.com/emersion/go-pgpmail"
	"github.com/gdamore/tcell/v2"
	"github.com/mattn/go-runewidth"
	"github.com/mitchellh/go-homedir"
@@ -24,6 +25,7 @@ import (

	"git.sr.ht/~rjarry/aerc/completer"
	"git.sr.ht/~rjarry/aerc/config"
	"git.sr.ht/~rjarry/aerc/lib"
	"git.sr.ht/~rjarry/aerc/lib/format"
	"git.sr.ht/~rjarry/aerc/lib/templates"
	"git.sr.ht/~rjarry/aerc/lib/ui"
@@ -49,6 +51,7 @@ type Composer struct {
	review      *reviewMessage
	worker      *types.Worker
	completer   *completer.Completer
	sign        bool

	layout    HeaderLayout
	focusable []ui.MouseableDrawableInteractive
@@ -173,6 +176,15 @@ func (c *Composer) Sent() bool {
	return c.sent
}

func (c *Composer) SetSign(sign bool) *Composer {
	c.sign = sign
	return c
}

func (c *Composer) Sign() bool {
	return c.sign
}

// Note: this does not reload the editor. You must call this before the first
// Draw() call.
func (c *Composer) SetContents(reader io.Reader) *Composer {
@@ -393,34 +405,74 @@ func (c *Composer) PrepareHeader() (*mail.Header, error) {
	return c.header, nil
}

func getSenderEmail(c *Composer) (string, error) {
	// add the from: field also to the 'recipients' list
	if c.acctConfig.From == "" {
		return "", errors.New("No 'From' configured for this account")
	}
	from, err := mail.ParseAddress(c.acctConfig.From)
	if err != nil {
		return "", errors.Wrap(err, "ParseAddress(config.From)")
	}
	return from.Address, nil
}

func (c *Composer) WriteMessage(header *mail.Header, writer io.Writer) error {
	if err := c.reloadEmail(); err != nil {
		return err
	}

	if len(c.attachments) == 0 {
		// don't create a multipart email if we only have text
		return writeInlineBody(header, c.email, writer)
	}
	if c.sign {

	// otherwise create a multipart email,
	// with a multipart/alternative part for the text
	w, err := mail.CreateWriter(writer, *header)
	if err != nil {
		return errors.Wrap(err, "CreateWriter")
	}
	defer w.Close()
		signer, err := getSigner(c)
		if err != nil {
			return err
		}

	if err := writeMultipartBody(c.email, w); err != nil {
		return errors.Wrap(err, "writeMultipartBody")
	}
		var signedHeader mail.Header
		signedHeader.SetContentType("text/plain", nil)

		var buf bytes.Buffer
		var cleartext io.WriteCloser

	for _, a := range c.attachments {
		if err := writeAttachment(a, w); err != nil {
			return errors.Wrap(err, "writeAttachment")
		cleartext, err = pgpmail.Sign(&buf, header.Header.Header, signer, nil)
		if err != nil {
			return err
		}

		err = writeMsgImpl(c, &signedHeader, cleartext)
		if err != nil {
			return err
		}
		cleartext.Close()
		io.Copy(writer, &buf)
		return nil

	} else {
		return writeMsgImpl(c, header, writer)
	}
}

func writeMsgImpl(c *Composer, header *mail.Header, writer io.Writer) error {
	if len(c.attachments) == 0 {
		// no attachements
		return writeInlineBody(header, c.email, writer)
	} else {
		// with attachements
		w, err := mail.CreateWriter(writer, *header)
		if err != nil {
			return errors.Wrap(err, "CreateWriter")
		}
		if err := writeMultipartBody(c.email, w); err != nil {
			return errors.Wrap(err, "writeMultipartBody")
		}
		for _, a := range c.attachments {
			if err := writeAttachment(a, w); err != nil {
				return errors.Wrap(err, "writeAttachment")
			}
		}
		w.Close()
	}
	return nil
}

@@ -885,3 +937,30 @@ func (rm *reviewMessage) OnInvalidate(fn func(ui.Drawable)) {
func (rm *reviewMessage) Draw(ctx *ui.Context) {
	rm.grid.Draw(ctx)
}

func getSigner(c *Composer) (signer *openpgp.Entity, err error) {
	signerEmail, err := getSenderEmail(c)
	if err != nil {
		return nil, err
	}
	signer, err = lib.GetSignerEntityByEmail(signerEmail)
	if err != nil {
		return nil, err
	}

	key, ok := signer.SigningKey(time.Now())
	if !ok {
		return nil, fmt.Errorf("no signing key found for %s", signerEmail)
	}

	if !key.PrivateKey.Encrypted {
		return signer, nil
	}

	_, err = c.aerc.DecryptKeys([]openpgp.Key{key}, false)
	if err != nil {
		return nil, err
	}

	return signer, nil
}
-- 
2.34.1
Koni Marti, Dec 30, 2021 at 10:25:

[PATCH aerc v2 3/3] pgp: PGP/MIME encryption for outgoing emails Export this patch

implements PGP/MIME encryption with go-pgpmail. The Encrypt() function of
go-pgpmail requires a list of public keys which are taken from the
keystore. The keystore is searched for the email addresses of all
recipients (to, cc, and bcc).
If you want to be able to read the encrypted email afterwards, add
yourself as a recipient in either to, cc, or bcc as well.

Public keys can be exported from gpg into aerc as follows:
$ gpg --export  >> ~/.local/share/aerc/keyring.asc

When composing a message, the encryption is enabled with the
":encrypt" command. This sets a bool flag in the Composer struct.
A reapted application of this command will toggle the flag.
The encrypted message can also be signed by using the ":sign"
command before or after ":encrypt".

References: https://todo.sr.ht/~rjarry/aerc/6
Signed-off-by: Koni Marti <koni.marti@gmail.com>
---
 commands/compose/encrypt.go | 44 +++++++++++++++++++
 lib/keystore.go             | 10 +++++
 widgets/compose.go          | 84 +++++++++++++++++++++++++++++++++----
 3 files changed, 129 insertions(+), 9 deletions(-)
 create mode 100644 commands/compose/encrypt.go

diff --git a/commands/compose/encrypt.go b/commands/compose/encrypt.go
new file mode 100644
index 0000000..d63940b
--- /dev/null
+++ b/commands/compose/encrypt.go
@@ -0,0 +1,44 @@
package compose

import (
	"errors"
	"time"

	"git.sr.ht/~rjarry/aerc/widgets"
)

type Encrypt struct{}

func init() {
	register(Encrypt{})
}

func (Encrypt) Aliases() []string {
	return []string{"encrypt"}
}

func (Encrypt) Complete(aerc *widgets.Aerc, args []string) []string {
	return nil
}

func (Encrypt) Execute(aerc *widgets.Aerc, args []string) error {
	if len(args) != 1 {
		return errors.New("Usage: encrypt")
	}

	composer, _ := aerc.SelectedTab().(*widgets.Composer)

	composer.SetEncrypt(!composer.Encrypt())

	var statusline string

	if composer.Encrypt() {
		statusline = "Message will be encrypted."
	} else {
		statusline = "Message will not be encrypted."
	}

	aerc.PushStatus(statusline, 10*time.Second)

	return nil
}
diff --git a/lib/keystore.go b/lib/keystore.go
index c211067..0b9d41a 100644
--- a/lib/keystore.go
+++ b/lib/keystore.go
@@ -53,6 +53,16 @@ func UnlockKeyring() {
	os.Remove(lockpath)
}

func GetEntityByEmail(email string) (e *openpgp.Entity, err error) {
	for _, entity := range Keyring {
		ident := entity.PrimaryIdentity()
		if ident != nil && ident.UserId.Email == email {
			return entity, nil
		}
	}
	return nil, fmt.Errorf("entity not found in keyring")
}

func GetSignerEntityByEmail(email string) (e *openpgp.Entity, err error) {
	for _, key := range Keyring.DecryptionKeys() {
		if key.Entity == nil {
diff --git a/widgets/compose.go b/widgets/compose.go
index 6b7f5cd..46d4025 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -52,6 +52,7 @@ type Composer struct {
	worker      *types.Worker
	completer   *completer.Completer
	sign        bool
	encrypt     bool

	layout    HeaderLayout
	focusable []ui.MouseableDrawableInteractive
@@ -185,6 +186,15 @@ func (c *Composer) Sign() bool {
	return c.sign
}

func (c *Composer) SetEncrypt(encrypt bool) *Composer {
	c.encrypt = encrypt
	return c
}

func (c *Composer) Encrypt() bool {
	return c.encrypt
}

// Note: this does not reload the editor. You must call this before the first
// Draw() call.
func (c *Composer) SetContents(reader io.Reader) *Composer {
@@ -417,27 +427,83 @@ func getSenderEmail(c *Composer) (string, error) {
	return from.Address, nil
}

func getRecipientsEmail(c *Composer) ([]string, error) {
	h, err := c.PrepareHeader()
	if err != nil {
		return nil, errors.Wrap(err, "PrepareHeader")
	}

	// collect all 'recipients' from header (to:, cc:, bcc:)
	rcpts := make(map[string]bool)
	for _, key := range []string{"to", "cc", "bcc"} {
		list, err := h.AddressList(key)
		if err != nil {
			continue
		}
		for _, entry := range list {
			if entry != nil {
				rcpts[entry.Address] = true
			}
		}
	}

	// return email addresses as string slice
	results := []string{}
	for email, _ := range rcpts {
		results = append(results, email)
	}
	return results, nil
}

func (c *Composer) WriteMessage(header *mail.Header, writer io.Writer) error {
	if err := c.reloadEmail(); err != nil {
		return err
	}

	if c.sign {

		signer, err := getSigner(c)
		if err != nil {
			return err
		}
	if c.sign || c.encrypt {

		var signedHeader mail.Header
		signedHeader.SetContentType("text/plain", nil)

		var buf bytes.Buffer
		var cleartext io.WriteCloser
		var err error

		cleartext, err = pgpmail.Sign(&buf, header.Header.Header, signer, nil)
		if err != nil {
			return err
		var signer *openpgp.Entity
		if c.sign {
			signer, err = getSigner(c)
			if err != nil {
				return err
			}
		} else {
			signer = nil
		}

		if c.encrypt {
			var to []*openpgp.Entity
			rcpts, err := getRecipientsEmail(c)
			if err != nil {
				return err
			}
			for _, rcpt := range rcpts {
				toEntity, err := lib.GetEntityByEmail(rcpt)
				if err != nil {
					return errors.Wrap(err, "no key for "+rcpt)
				}
				to = append(to, toEntity)
			}
			cleartext, err = pgpmail.Encrypt(&buf, header.Header.Header,
				to, signer, nil)

			if err != nil {
				return err
			}
		} else {
			cleartext, err = pgpmail.Sign(&buf, header.Header.Header,
				signer, nil)
			if err != nil {
				return err
			}
		}

		err = writeMsgImpl(c, &signedHeader, cleartext)
-- 
2.34.1
Koni Marti, Dec 30, 2021 at 10:25: