diff options
author | Tim Culverhouse <tim@timculverhouse.com> | 2022-04-29 11:19:52 -0500 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2022-05-04 14:07:15 +0200 |
commit | dbf52bb4b48748586bb6343ae4ad6d424f0631ac (patch) | |
tree | bd806636b0be51f07218f5a9db9be45af72db9ba | |
parent | b29293d7b53c73629911ec75b2ec5954d365feed (diff) | |
download | aerc-dbf52bb4b48748586bb6343ae4ad6d424f0631ac.tar.gz |
pgp: check for signing key before signing time
Check that the signing key exists when the user issues the :sign
command. The signing key ID will be displayed in the security status
also, allowing the user to see what key will be used to sign the
message.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: Jens Grassel <jens@wegtam.com>
-rw-r--r-- | commands/compose/sign.go | 5 | ||||
-rw-r--r-- | lib/crypto/crypto.go | 1 | ||||
-rw-r--r-- | lib/crypto/gpg/gpg.go | 4 | ||||
-rw-r--r-- | lib/crypto/gpg/gpgbin/gpgbin.go | 23 | ||||
-rw-r--r-- | lib/crypto/gpg/gpgbin/keys.go | 13 | ||||
-rw-r--r-- | lib/crypto/pgp/pgp.go | 18 | ||||
-rw-r--r-- | widgets/compose.go | 36 |
7 files changed, 93 insertions, 7 deletions
diff --git a/commands/compose/sign.go b/commands/compose/sign.go index eb985e99..635b07c6 100644 --- a/commands/compose/sign.go +++ b/commands/compose/sign.go @@ -28,7 +28,10 @@ func (Sign) Execute(aerc *widgets.Aerc, args []string) error { composer, _ := aerc.SelectedTab().(*widgets.Composer) - composer.SetSign(!composer.Sign()) + err := composer.SetSign(!composer.Sign()) + if err != nil { + return err + } var statusline string diff --git a/lib/crypto/crypto.go b/lib/crypto/crypto.go index 47eca99d..cab93462 100644 --- a/lib/crypto/crypto.go +++ b/lib/crypto/crypto.go @@ -19,6 +19,7 @@ type Provider interface { ImportKeys(io.Reader) error Init(*log.Logger) error Close() + GetSignerKeyId(string) (string, error) } func New(s string) Provider { diff --git a/lib/crypto/gpg/gpg.go b/lib/crypto/gpg/gpg.go index 66cd3725..457788dc 100644 --- a/lib/crypto/gpg/gpg.go +++ b/lib/crypto/gpg/gpg.go @@ -51,6 +51,10 @@ func (m *Mail) Sign(buf *bytes.Buffer, signer string, decryptKeys openpgp.Prompt func (m *Mail) Close() {} +func (m *Mail) GetSignerKeyId(s string) (string, error) { + return gpgbin.GetPrivateKeyId(s) +} + func handleSignatureError(e string) models.SignatureValidity { if e == "gpg: missing public key" { return models.UnknownEntity diff --git a/lib/crypto/gpg/gpgbin/gpgbin.go b/lib/crypto/gpg/gpgbin/gpgbin.go index da046f46..3ee81399 100644 --- a/lib/crypto/gpg/gpgbin/gpgbin.go +++ b/lib/crypto/gpg/gpgbin/gpgbin.go @@ -77,6 +77,29 @@ func getIdentity(key uint64) string { return "" } +// getKeyId returns the 16 digit key id, if key exists +func getKeyId(s string, private bool) string { + cmd := exec.Command("gpg", "--with-colons", "--batch") + listArg := "--list-keys" + if private { + listArg = "--list-secret-keys" + } + cmd.Args = append(cmd.Args, listArg, s) + + var outbuf strings.Builder + cmd.Stdout = &outbuf + cmd.Run() + out := strings.Split(outbuf.String(), "\n") + for _, line := range out { + if strings.HasPrefix(line, "fpr") { + flds := strings.Split(line, ":") + id := flds[9] + return id[len(id)-16:] + } + } + return "" +} + // longKeyToUint64 returns a uint64 version of the given key func longKeyToUint64(key string) (uint64, error) { fpr := string(key[len(key)-16:]) diff --git a/lib/crypto/gpg/gpgbin/keys.go b/lib/crypto/gpg/gpgbin/keys.go new file mode 100644 index 00000000..660ce821 --- /dev/null +++ b/lib/crypto/gpg/gpgbin/keys.go @@ -0,0 +1,13 @@ +package gpgbin + +import "fmt" + +// GetPrivateKeyId runs gpg --list-secret-keys s +func GetPrivateKeyId(s string) (string, error) { + private := true + id := getKeyId(s, private) + if id == "" { + return "", fmt.Errorf("no private key found") + } + return id, nil +} diff --git a/lib/crypto/pgp/pgp.go b/lib/crypto/pgp/pgp.go index 92a15ee6..e0c5671b 100644 --- a/lib/crypto/pgp/pgp.go +++ b/lib/crypto/pgp/pgp.go @@ -245,6 +245,24 @@ func (m *Mail) getSigner(signer string, decryptKeys openpgp.PromptFunction) (sig return signerEntity, nil } +func (m *Mail) GetSignerKeyId(s string) (string, error) { + var err error + var signerEntity *openpgp.Entity + switch strings.Contains(s, "@") { + case true: + signerEntity, err = m.getSignerEntityByEmail(s) + if err != nil { + return "", err + } + case false: + signerEntity, err = m.getSignerEntityByKeyId(s) + if err != nil { + return "", err + } + } + return signerEntity.PrimaryKey.KeyIdString(), nil +} + func handleSignatureError(e string) models.SignatureValidity { if e == "openpgp: signature made by unknown entity" { return models.UnknownEntity diff --git a/widgets/compose.go b/widgets/compose.go index b956abc2..d9080d17 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -176,10 +176,14 @@ func (c *Composer) Sent() bool { return c.sent } -func (c *Composer) SetSign(sign bool) *Composer { +func (c *Composer) SetSign(sign bool) error { c.sign = sign - c.updateCrypto() - return c + err := c.updateCrypto() + if err != nil { + c.sign = !sign + return fmt.Errorf("Cannot sign message: %v", err) + } + return nil } func (c *Composer) Sign() bool { @@ -196,18 +200,36 @@ func (c *Composer) Encrypt() bool { return c.encrypt } -func (c *Composer) updateCrypto() { +func (c *Composer) updateCrypto() error { if c.crypto == nil { c.crypto = newCryptoStatus(&c.config.Ui) } + var err error + // Check if signKey is empty so we only run this once + if c.sign && c.crypto.signKey == "" { + cp := c.aerc.Crypto + var s string + if c.acctConfig.PgpKeyId != "" { + s = c.acctConfig.PgpKeyId + } else { + s, err = getSenderEmail(c) + if err != nil { + return err + } + } + c.crypto.signKey, err = cp.GetSignerKeyId(s) + if err != nil { + return err + } + } crHeight := 0 st := "" switch { case c.sign && c.encrypt: - st = "Sign & Encrypt" + st = fmt.Sprintf("Sign (%s) & Encrypt", c.crypto.signKey) crHeight = 1 case c.sign: - st = "Sign" + st = fmt.Sprintf("Sign (%s)", c.crypto.signKey) crHeight = 1 case c.encrypt: st = "Encrypt" @@ -224,6 +246,7 @@ func (c *Composer) updateCrypto() { {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)}, }) c.grid.AddChild(c.crypto).At(1, 0) + return nil } // Note: this does not reload the editor. You must call this before the first @@ -1062,6 +1085,7 @@ type cryptoStatus struct { title string status *ui.Text uiConfig *config.UIConfig + signKey string } func newCryptoStatus(uiConfig *config.UIConfig) *cryptoStatus { |