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, _ := aerc.SelectedTab().(*widgets.Composer)
|
||||||
|
|
||||||
composer.SetSign(!composer.Sign())
|
err := composer.SetSign(!composer.Sign())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var statusline string
|
var statusline string
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ type Provider interface {
|
||||||
ImportKeys(io.Reader) error
|
ImportKeys(io.Reader) error
|
||||||
Init(*log.Logger) error
|
Init(*log.Logger) error
|
||||||
Close()
|
Close()
|
||||||
|
GetSignerKeyId(string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(s string) Provider {
|
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) Close() {}
|
||||||
|
|
||||||
|
func (m *Mail) GetSignerKeyId(s string) (string, error) {
|
||||||
|
return gpgbin.GetPrivateKeyId(s)
|
||||||
|
}
|
||||||
|
|
||||||
func handleSignatureError(e string) models.SignatureValidity {
|
func handleSignatureError(e string) models.SignatureValidity {
|
||||||
if e == "gpg: missing public key" {
|
if e == "gpg: missing public key" {
|
||||||
return models.UnknownEntity
|
return models.UnknownEntity
|
||||||
|
|
|
@ -77,6 +77,29 @@ func getIdentity(key uint64) string {
|
||||||
return ""
|
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
|
// longKeyToUint64 returns a uint64 version of the given key
|
||||||
func longKeyToUint64(key string) (uint64, error) {
|
func longKeyToUint64(key string) (uint64, error) {
|
||||||
fpr := string(key[len(key)-16:])
|
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
|
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 {
|
func handleSignatureError(e string) models.SignatureValidity {
|
||||||
if e == "openpgp: signature made by unknown entity" {
|
if e == "openpgp: signature made by unknown entity" {
|
||||||
return models.UnknownEntity
|
return models.UnknownEntity
|
||||||
|
|
|
@ -176,10 +176,14 @@ func (c *Composer) Sent() bool {
|
||||||
return c.sent
|
return c.sent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Composer) SetSign(sign bool) *Composer {
|
func (c *Composer) SetSign(sign bool) error {
|
||||||
c.sign = sign
|
c.sign = sign
|
||||||
c.updateCrypto()
|
err := c.updateCrypto()
|
||||||
return c
|
if err != nil {
|
||||||
|
c.sign = !sign
|
||||||
|
return fmt.Errorf("Cannot sign message: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Composer) Sign() bool {
|
func (c *Composer) Sign() bool {
|
||||||
|
@ -196,18 +200,36 @@ func (c *Composer) Encrypt() bool {
|
||||||
return c.encrypt
|
return c.encrypt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Composer) updateCrypto() {
|
func (c *Composer) updateCrypto() error {
|
||||||
if c.crypto == nil {
|
if c.crypto == nil {
|
||||||
c.crypto = newCryptoStatus(&c.config.Ui)
|
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
|
crHeight := 0
|
||||||
st := ""
|
st := ""
|
||||||
switch {
|
switch {
|
||||||
case c.sign && c.encrypt:
|
case c.sign && c.encrypt:
|
||||||
st = "Sign & Encrypt"
|
st = fmt.Sprintf("Sign (%s) & Encrypt", c.crypto.signKey)
|
||||||
crHeight = 1
|
crHeight = 1
|
||||||
case c.sign:
|
case c.sign:
|
||||||
st = "Sign"
|
st = fmt.Sprintf("Sign (%s)", c.crypto.signKey)
|
||||||
crHeight = 1
|
crHeight = 1
|
||||||
case c.encrypt:
|
case c.encrypt:
|
||||||
st = "Encrypt"
|
st = "Encrypt"
|
||||||
|
@ -224,6 +246,7 @@ func (c *Composer) updateCrypto() {
|
||||||
{Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
|
{Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
|
||||||
})
|
})
|
||||||
c.grid.AddChild(c.crypto).At(1, 0)
|
c.grid.AddChild(c.crypto).At(1, 0)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this does not reload the editor. You must call this before the first
|
// Note: this does not reload the editor. You must call this before the first
|
||||||
|
@ -1062,6 +1085,7 @@ type cryptoStatus struct {
|
||||||
title string
|
title string
|
||||||
status *ui.Text
|
status *ui.Text
|
||||||
uiConfig *config.UIConfig
|
uiConfig *config.UIConfig
|
||||||
|
signKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCryptoStatus(uiConfig *config.UIConfig) *cryptoStatus {
|
func newCryptoStatus(uiConfig *config.UIConfig) *cryptoStatus {
|
||||||
|
|
Loading…
Reference in a new issue