From b57fceaad4bfcbd4ca3022e013b73eff72079c0b Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Thu, 5 May 2022 12:53:16 -0500 Subject: pgp: add attach key command Add compose command ("attach-key") to attach the public key associated with the sending account. Public key is attached in ascii armor format, with the mimetype set according to RFC 3156 ("application/pgp-keys"). Signed-off-by: Tim Culverhouse Tested-by: Koni Marti --- lib/crypto/crypto.go | 1 + lib/crypto/gpg/gpg.go | 4 ++++ lib/crypto/gpg/gpgbin/keys.go | 23 ++++++++++++++++++++++- lib/crypto/pgp/pgp.go | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/crypto/crypto.go b/lib/crypto/crypto.go index 54a20e6..3c961ad 100644 --- a/lib/crypto/crypto.go +++ b/lib/crypto/crypto.go @@ -21,6 +21,7 @@ type Provider interface { Close() GetSignerKeyId(string) (string, error) GetKeyId(string) (string, error) + ExportKey(string) (io.Reader, error) } func New(s string) Provider { diff --git a/lib/crypto/gpg/gpg.go b/lib/crypto/gpg/gpg.go index fe32468..00125ba 100644 --- a/lib/crypto/gpg/gpg.go +++ b/lib/crypto/gpg/gpg.go @@ -59,6 +59,10 @@ func (m *Mail) GetKeyId(s string) (string, error) { return gpgbin.GetKeyId(s) } +func (m *Mail) ExportKey(k string) (io.Reader, error) { + return gpgbin.ExportPublicKey(k) +} + func handleSignatureError(e string) models.SignatureValidity { if e == "gpg: missing public key" { return models.UnknownEntity diff --git a/lib/crypto/gpg/gpgbin/keys.go b/lib/crypto/gpg/gpgbin/keys.go index 9c8b233..bef90cf 100644 --- a/lib/crypto/gpg/gpgbin/keys.go +++ b/lib/crypto/gpg/gpgbin/keys.go @@ -1,6 +1,12 @@ package gpgbin -import "fmt" +import ( + "bytes" + "fmt" + "io" + "os/exec" + "strings" +) // GetPrivateKeyId runs gpg --list-secret-keys s func GetPrivateKeyId(s string) (string, error) { @@ -21,3 +27,18 @@ func GetKeyId(s string) (string, error) { } return id, nil } + +// ExportPublicKey exports the public key identified by k in armor format +func ExportPublicKey(k string) (io.Reader, error) { + cmd := exec.Command("gpg", "--export", "--armor", k) + + var outbuf bytes.Buffer + var stderr strings.Builder + cmd.Stdout = &outbuf + cmd.Stderr = &stderr + cmd.Run() + if strings.Contains(stderr.String(), "gpg") { + return nil, fmt.Errorf("gpg: error exporting key") + } + return &outbuf, nil +} diff --git a/lib/crypto/pgp/pgp.go b/lib/crypto/pgp/pgp.go index f0f3f65..4dbe37c 100644 --- a/lib/crypto/pgp/pgp.go +++ b/lib/crypto/pgp/pgp.go @@ -13,6 +13,7 @@ import ( "git.sr.ht/~rjarry/aerc/models" "github.com/ProtonMail/go-crypto/openpgp" + "github.com/ProtonMail/go-crypto/openpgp/armor" "github.com/ProtonMail/go-crypto/openpgp/packet" "github.com/emersion/go-message/mail" "github.com/emersion/go-pgpmail" @@ -271,6 +272,39 @@ func (m *Mail) GetKeyId(s string) (string, error) { return entity.PrimaryKey.KeyIdString(), nil } +func (m *Mail) ExportKey(k string) (io.Reader, error) { + var err error + var entity *openpgp.Entity + switch strings.Contains(k, "@") { + case true: + entity, err = m.getSignerEntityByEmail(k) + if err != nil { + return nil, err + } + case false: + entity, err = m.getSignerEntityByKeyId(k) + if err != nil { + return nil, err + } + } + pks := bytes.NewBuffer(nil) + err = entity.Serialize(pks) + if err != nil { + return nil, fmt.Errorf("pgp: error exporting key: %v", err) + } + pka := bytes.NewBuffer(nil) + w, err := armor.Encode(pka, "PGP PUBLIC KEY BLOCK", map[string]string{}) + if err != nil { + return nil, fmt.Errorf("pgp: error exporting key: %v", err) + } + w.Write(pks.Bytes()) + if err != nil { + return nil, fmt.Errorf("pgp: error exporting key: %v", err) + } + w.Close() + return pka, nil +} + func handleSignatureError(e string) models.SignatureValidity { if e == "openpgp: signature made by unknown entity" { return models.UnknownEntity -- cgit v1.2.3