From 57699b1fa6367a42d5877afcfdb1504e52835ed9 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Mon, 25 Apr 2022 08:30:44 -0500 Subject: feat: add gpg integration This commit adds gpg system integration. This is done through two new packages: gpgbin, which handles the system calls and parsing; and gpg which is mostly a copy of emersion/go-pgpmail with modifications to interface with package gpgbin. gpg includes tests for many cases, and by it's nature also tests package gpgbin. I separated these in case an external dependency is ever used for the gpg sys-calls/parsing (IE we mirror how go-pgpmail+openpgp currently are dependencies) Two new config options are introduced: * pgp-provider. If it is not explicitly set to "gpg", aerc will default to it's internal pgp provider * pgp-key-id: (Optionally) specify a key by short or long keyId Signed-off-by: Tim Culverhouse Acked-by: Koni Marti Acked-by: Robin Jarry --- lib/crypto/pgp/pgp.go | 61 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 21 deletions(-) (limited to 'lib/crypto/pgp/pgp.go') diff --git a/lib/crypto/pgp/pgp.go b/lib/crypto/pgp/pgp.go index 70a003a0..92a15ee6 100644 --- a/lib/crypto/pgp/pgp.go +++ b/lib/crypto/pgp/pgp.go @@ -79,6 +79,20 @@ func (m *Mail) getEntityByEmail(email string) (e *openpgp.Entity, err error) { return nil, fmt.Errorf("entity not found in keyring") } +func (m *Mail) getSignerEntityByKeyId(id string) (*openpgp.Entity, error) { + id = strings.ToUpper(id) + for _, key := range Keyring.DecryptionKeys() { + if key.Entity == nil { + continue + } + kId := key.Entity.PrimaryKey.KeyIdString() + if strings.Contains(kId, id) { + return key.Entity, nil + } + } + return nil, fmt.Errorf("entity not found in keyring") +} + func (m *Mail) getSignerEntityByEmail(email string) (e *openpgp.Entity, err error) { for _, key := range Keyring.DecryptionKeys() { if key.Entity == nil { @@ -157,12 +171,12 @@ func (m *Mail) ImportKeys(r io.Reader) error { return nil } -func (m *Mail) Encrypt(buf *bytes.Buffer, rcpts []string, signerEmail string, decryptKeys openpgp.PromptFunction, header *mail.Header) (io.WriteCloser, error) { +func (m *Mail) Encrypt(buf *bytes.Buffer, rcpts []string, signer string, decryptKeys openpgp.PromptFunction, header *mail.Header) (io.WriteCloser, error) { var err error var to []*openpgp.Entity - var signer *openpgp.Entity - if signerEmail != "" { - signer, err = m.getSigner(signerEmail, decryptKeys) + var signerEntity *openpgp.Entity + if signer != "" { + signerEntity, err = m.getSigner(signer, decryptKeys) if err != nil { return nil, err } @@ -177,45 +191,50 @@ func (m *Mail) Encrypt(buf *bytes.Buffer, rcpts []string, signerEmail string, de } cleartext, err := pgpmail.Encrypt(buf, header.Header.Header, - to, signer, nil) + to, signerEntity, nil) if err != nil { return nil, err } return cleartext, nil } -func (m *Mail) Sign(buf *bytes.Buffer, signerEmail string, decryptKeys openpgp.PromptFunction, header *mail.Header) (io.WriteCloser, error) { +func (m *Mail) Sign(buf *bytes.Buffer, signer string, decryptKeys openpgp.PromptFunction, header *mail.Header) (io.WriteCloser, error) { var err error - var signer *openpgp.Entity - if signerEmail != "" { - signer, err = m.getSigner(signerEmail, decryptKeys) + var signerEntity *openpgp.Entity + if signer != "" { + signerEntity, err = m.getSigner(signer, decryptKeys) if err != nil { return nil, err } } - cleartext, err := pgpmail.Sign(buf, header.Header.Header, signer, nil) + cleartext, err := pgpmail.Sign(buf, header.Header.Header, signerEntity, nil) if err != nil { return nil, err } return cleartext, nil } -func (m *Mail) getSigner(signerEmail string, decryptKeys openpgp.PromptFunction) (signer *openpgp.Entity, err error) { - if err != nil { - return nil, err - } - signer, err = m.getSignerEntityByEmail(signerEmail) - if err != nil { - return nil, err +func (m *Mail) getSigner(signer string, decryptKeys openpgp.PromptFunction) (signerEntity *openpgp.Entity, err error) { + switch strings.Contains(signer, "@") { + case true: + signerEntity, err = m.getSignerEntityByEmail(signer) + if err != nil { + return nil, err + } + case false: + signerEntity, err = m.getSignerEntityByKeyId(signer) + if err != nil { + return nil, err + } } - key, ok := signer.SigningKey(time.Now()) + key, ok := signerEntity.SigningKey(time.Now()) if !ok { - return nil, fmt.Errorf("no signing key found for %s", signerEmail) + return nil, fmt.Errorf("no signing key found for %s", signer) } if !key.PrivateKey.Encrypted { - return signer, nil + return signerEntity, nil } _, err = decryptKeys([]openpgp.Key{key}, false) @@ -223,7 +242,7 @@ func (m *Mail) getSigner(signerEmail string, decryptKeys openpgp.PromptFunction) return nil, err } - return signer, nil + return signerEntity, nil } func handleSignatureError(e string) models.SignatureValidity { -- cgit