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>
This commit is contained in:
parent
b29293d7b5
commit
dbf52bb4b4
7 changed files with 93 additions and 7 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:])
|
||||
|
|
13
lib/crypto/gpg/gpgbin/keys.go
Normal file
13
lib/crypto/gpg/gpgbin/keys.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue