From d09636ee0b9957ed60fc01224ddfbb03c4f4b7fa Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Mon, 25 Apr 2022 08:30:43 -0500 Subject: [PATCH] refactor: refactor pgp implementation This commit refactors the internal PGP implementation to make way for GPG integration. Signed-off-by: Tim Culverhouse Acked-by: Koni Marti Acked-by: Robin Jarry --- aerc.go | 11 +- commands/account/view.go | 2 +- commands/msg/delete.go | 2 +- commands/msgview/next.go | 2 +- config/config.go | 20 ++++ lib/crypto/crypto.go | 28 +++++ lib/crypto/pgp/pgp.go | 240 +++++++++++++++++++++++++++++++++++++++ lib/keystore.go | 105 ----------------- lib/messageview.go | 16 +-- models/models.go | 24 ++++ widgets/aerc.go | 10 +- widgets/compose.go | 51 +-------- widgets/msglist.go | 3 +- widgets/msgviewer.go | 8 +- widgets/pgpinfo.go | 27 ++--- 15 files changed, 356 insertions(+), 193 deletions(-) create mode 100644 lib/crypto/crypto.go create mode 100644 lib/crypto/pgp/pgp.go delete mode 100644 lib/keystore.go diff --git a/aerc.go b/aerc.go index f9a19a3..57259be 100644 --- a/aerc.go +++ b/aerc.go @@ -22,6 +22,7 @@ import ( "git.sr.ht/~rjarry/aerc/commands/terminal" "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" + "git.sr.ht/~rjarry/aerc/lib/crypto" "git.sr.ht/~rjarry/aerc/lib/templates" libui "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/logging" @@ -168,7 +169,11 @@ func main() { deferLoop := make(chan struct{}) - aerc = widgets.NewAerc(conf, logger, func(cmd []string) error { + c := crypto.New(conf.General.PgpProvider) + c.Init(logger) + defer c.Close() + + aerc = widgets.NewAerc(conf, logger, c, func(cmd []string) error { return execCommand(aerc, ui, cmd) }, func(cmd string) []string { return getCompletions(aerc, cmd) @@ -188,10 +193,6 @@ func main() { ui.EnableMouse() } - logger.Println("Initializing PGP keyring") - lib.InitKeyring() - defer lib.UnlockKeyring() - logger.Println("Starting Unix server") as, err := lib.StartServer(logger) if err != nil { diff --git a/commands/account/view.go b/commands/account/view.go index 53ad267..8537d33 100644 --- a/commands/account/view.go +++ b/commands/account/view.go @@ -45,7 +45,7 @@ func (ViewMessage) Execute(aerc *widgets.Aerc, args []string) error { aerc.PushError(msg.Error.Error()) return nil } - lib.NewMessageStoreView(msg, store, aerc.DecryptKeys, + lib.NewMessageStoreView(msg, store, aerc.Crypto, aerc.DecryptKeys, func(view lib.MessageView, err error) { if err != nil { aerc.PushError(err.Error()) diff --git a/commands/msg/delete.go b/commands/msg/delete.go index 5eb622d..d26169f 100644 --- a/commands/msg/delete.go +++ b/commands/msg/delete.go @@ -68,7 +68,7 @@ func (Delete) Execute(aerc *widgets.Aerc, args []string) error { acct.Messages().Invalidate() return nil } - lib.NewMessageStoreView(next, store, aerc.DecryptKeys, + lib.NewMessageStoreView(next, store, aerc.Crypto, aerc.DecryptKeys, func(view lib.MessageView, err error) { if err != nil { aerc.PushError(err.Error()) diff --git a/commands/msgview/next.go b/commands/msgview/next.go index 742bc66..928b9fb 100644 --- a/commands/msgview/next.go +++ b/commands/msgview/next.go @@ -42,7 +42,7 @@ func (NextPrevMsg) Execute(aerc *widgets.Aerc, args []string) error { aerc.RemoveTab(mv) return nil } - lib.NewMessageStoreView(nextMsg, store, aerc.DecryptKeys, + lib.NewMessageStoreView(nextMsg, store, aerc.Crypto, aerc.DecryptKeys, func(view lib.MessageView, err error) { if err != nil { aerc.PushError(err.Error()) diff --git a/config/config.go b/config/config.go index 048dd23..0c9a40f 100644 --- a/config/config.go +++ b/config/config.go @@ -27,6 +27,7 @@ import ( type GeneralConfig struct { DefaultSavePath string `ini:"default-save-path"` + PgpProvider string `ini:"pgp-provider"` UnsafeAccountsConf bool `ini:"unsafe-accounts-conf"` } @@ -579,6 +580,21 @@ func validateBorderChars(section *ini.Section, config *UIConfig) error { return nil } +func validatePgpProvider(section *ini.Section) error { + m := map[string]bool{ + "internal": true, + } + for key, val := range section.KeysHash() { + switch key { + case "pgp-provider": + if !m[strings.ToLower(val)] { + return fmt.Errorf("%v must be 'internal'", key) + } + } + } + return nil +} + func LoadConfigFromFile(root *string, logger *log.Logger) (*AercConfig, error) { if root == nil { _root := path.Join(xdg.ConfigHome(), "aerc") @@ -618,6 +634,7 @@ func LoadConfigFromFile(root *string, logger *log.Logger) (*AercConfig, error) { Ini: file, General: GeneralConfig{ + PgpProvider: "internal", UnsafeAccountsConf: false, }, @@ -704,6 +721,9 @@ func LoadConfigFromFile(root *string, logger *log.Logger) (*AercConfig, error) { if err := ui.MapTo(&config.General); err != nil { return nil, err } + if err := validatePgpProvider(ui); err != nil { + return nil, err + } } filename = path.Join(*root, "accounts.conf") diff --git a/lib/crypto/crypto.go b/lib/crypto/crypto.go new file mode 100644 index 0000000..47cb954 --- /dev/null +++ b/lib/crypto/crypto.go @@ -0,0 +1,28 @@ +package crypto + +import ( + "bytes" + "io" + "log" + + "git.sr.ht/~rjarry/aerc/lib/crypto/pgp" + "git.sr.ht/~rjarry/aerc/models" + "github.com/ProtonMail/go-crypto/openpgp" + "github.com/emersion/go-message/mail" +) + +type Provider interface { + Decrypt(io.Reader, openpgp.PromptFunction) (*models.MessageDetails, error) + Encrypt(*bytes.Buffer, []string, string, openpgp.PromptFunction, *mail.Header) (io.WriteCloser, error) + Sign(*bytes.Buffer, string, openpgp.PromptFunction, *mail.Header) (io.WriteCloser, error) + ImportKeys(io.Reader) error + Init(*log.Logger) error + Close() +} + +func New(s string) Provider { + switch s { + default: + return &pgp.Mail{} + } +} diff --git a/lib/crypto/pgp/pgp.go b/lib/crypto/pgp/pgp.go new file mode 100644 index 0000000..70a003a --- /dev/null +++ b/lib/crypto/pgp/pgp.go @@ -0,0 +1,240 @@ +package pgp + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "strings" + "time" + + "git.sr.ht/~rjarry/aerc/models" + "github.com/ProtonMail/go-crypto/openpgp" + "github.com/ProtonMail/go-crypto/openpgp/packet" + "github.com/emersion/go-message/mail" + "github.com/emersion/go-pgpmail" + "github.com/kyoh86/xdg" + "github.com/pkg/errors" +) + +type Mail struct { + logger *log.Logger +} + +var ( + Keyring openpgp.EntityList + + locked bool +) + +func (m *Mail) Init(l *log.Logger) error { + m.logger = l + m.logger.Println("Initializing PGP keyring") + os.MkdirAll(path.Join(xdg.DataHome(), "aerc"), 0700) + + lockpath := path.Join(xdg.DataHome(), "aerc", "keyring.lock") + lockfile, err := os.OpenFile(lockpath, os.O_CREATE|os.O_EXCL, 0600) + if err != nil { + // TODO: Consider connecting to main process over IPC socket + locked = false + } else { + locked = true + lockfile.Close() + } + + keypath := path.Join(xdg.DataHome(), "aerc", "keyring.asc") + keyfile, err := os.Open(keypath) + if os.IsNotExist(err) { + return nil + } else if err != nil { + panic(err) + } + defer keyfile.Close() + + Keyring, err = openpgp.ReadKeyRing(keyfile) + if err != nil { + panic(err) + } + return nil +} + +func (m *Mail) Close() { + if !locked { + return + } + lockpath := path.Join(xdg.DataHome(), "aerc", "keyring.lock") + os.Remove(lockpath) +} + +func (m *Mail) getEntityByEmail(email string) (e *openpgp.Entity, err error) { + for _, entity := range Keyring { + ident := entity.PrimaryIdentity() + if ident != nil && ident.UserId.Email == email { + return 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 { + continue + } + ident := key.Entity.PrimaryIdentity() + if ident != nil && ident.UserId.Email == email { + return key.Entity, nil + } + } + return nil, fmt.Errorf("entity not found in keyring") +} + +func (m *Mail) Decrypt(r io.Reader, decryptKeys openpgp.PromptFunction) (*models.MessageDetails, error) { + md := new(models.MessageDetails) + + pgpReader, err := pgpmail.Read(r, Keyring, decryptKeys, nil) + if err != nil { + return nil, err + } + if pgpReader.MessageDetails.IsEncrypted { + md.IsEncrypted = true + md.DecryptedWith = pgpReader.MessageDetails.DecryptedWith.Entity.PrimaryIdentity().Name + md.DecryptedWithKeyId = pgpReader.MessageDetails.DecryptedWith.PublicKey.KeyId + } + if pgpReader.MessageDetails.IsSigned { + // we should consume the UnverifiedBody until EOF in order + // to get the correct signature data + data, err := ioutil.ReadAll(pgpReader.MessageDetails.UnverifiedBody) + if err != nil { + return nil, err + } + pgpReader.MessageDetails.UnverifiedBody = bytes.NewReader(data) + + md.IsSigned = true + md.SignedBy = "" + md.SignedByKeyId = pgpReader.MessageDetails.SignedByKeyId + md.SignatureValidity = models.Valid + if pgpReader.MessageDetails.SignatureError != nil { + md.SignatureError = pgpReader.MessageDetails.SignatureError.Error() + md.SignatureValidity = handleSignatureError(md.SignatureError) + } + if pgpReader.MessageDetails.SignedBy != nil { + md.SignedBy = pgpReader.MessageDetails.SignedBy.Entity.PrimaryIdentity().Name + } + } + md.Body = pgpReader.MessageDetails.UnverifiedBody + return md, nil +} + +func (m *Mail) ImportKeys(r io.Reader) error { + keys, err := openpgp.ReadKeyRing(r) + if err != nil { + return err + } + Keyring = append(Keyring, keys...) + if locked { + keypath := path.Join(xdg.DataHome(), "aerc", "keyring.asc") + keyfile, err := os.OpenFile(keypath, os.O_CREATE|os.O_APPEND, 0600) + if err != nil { + return err + } + defer keyfile.Close() + + for _, key := range keys { + if key.PrivateKey != nil { + err = key.SerializePrivate(keyfile, &packet.Config{}) + } else { + err = key.Serialize(keyfile) + } + if err != nil { + return err + } + } + } + return nil +} + +func (m *Mail) Encrypt(buf *bytes.Buffer, rcpts []string, signerEmail 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) + if err != nil { + return nil, err + } + } + + for _, rcpt := range rcpts { + toEntity, err := m.getEntityByEmail(rcpt) + if err != nil { + return nil, errors.Wrap(err, "no key for "+rcpt) + } + to = append(to, toEntity) + } + + cleartext, err := pgpmail.Encrypt(buf, header.Header.Header, + to, signer, 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) { + var err error + var signer *openpgp.Entity + if signerEmail != "" { + signer, err = m.getSigner(signerEmail, decryptKeys) + if err != nil { + return nil, err + } + } + cleartext, err := pgpmail.Sign(buf, header.Header.Header, signer, 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 + } + + key, ok := signer.SigningKey(time.Now()) + if !ok { + return nil, fmt.Errorf("no signing key found for %s", signerEmail) + } + + if !key.PrivateKey.Encrypted { + return signer, nil + } + + _, err = decryptKeys([]openpgp.Key{key}, false) + if err != nil { + return nil, err + } + + return signer, nil +} + +func handleSignatureError(e string) models.SignatureValidity { + if e == "openpgp: signature made by unknown entity" { + return models.UnknownEntity + } + if strings.HasPrefix(e, "pgpmail: unsupported micalg") { + return models.UnsupportedMicalg + } + if strings.HasPrefix(e, "pgpmail") { + return models.InvalidSignature + } + return models.UnknownValidity +} diff --git a/lib/keystore.go b/lib/keystore.go deleted file mode 100644 index 0b9d41a..0000000 --- a/lib/keystore.go +++ /dev/null @@ -1,105 +0,0 @@ -package lib - -import ( - "fmt" - "io" - "os" - "path" - - "github.com/ProtonMail/go-crypto/openpgp" - "github.com/ProtonMail/go-crypto/openpgp/packet" - "github.com/kyoh86/xdg" -) - -var ( - Keyring openpgp.EntityList - - locked bool -) - -func InitKeyring() { - os.MkdirAll(path.Join(xdg.DataHome(), "aerc"), 0700) - - lockpath := path.Join(xdg.DataHome(), "aerc", "keyring.lock") - lockfile, err := os.OpenFile(lockpath, os.O_CREATE|os.O_EXCL, 0600) - if err != nil { - // TODO: Consider connecting to main process over IPC socket - locked = false - } else { - locked = true - lockfile.Close() - } - - keypath := path.Join(xdg.DataHome(), "aerc", "keyring.asc") - keyfile, err := os.Open(keypath) - if os.IsNotExist(err) { - return - } else if err != nil { - panic(err) - } - defer keyfile.Close() - - Keyring, err = openpgp.ReadKeyRing(keyfile) - if err != nil { - panic(err) - } -} - -func UnlockKeyring() { - if !locked { - return - } - lockpath := path.Join(xdg.DataHome(), "aerc", "keyring.lock") - os.Remove(lockpath) -} - -func GetEntityByEmail(email string) (e *openpgp.Entity, err error) { - for _, entity := range Keyring { - ident := entity.PrimaryIdentity() - if ident != nil && ident.UserId.Email == email { - return entity, nil - } - } - return nil, fmt.Errorf("entity not found in keyring") -} - -func GetSignerEntityByEmail(email string) (e *openpgp.Entity, err error) { - for _, key := range Keyring.DecryptionKeys() { - if key.Entity == nil { - continue - } - ident := key.Entity.PrimaryIdentity() - if ident != nil && ident.UserId.Email == email { - return key.Entity, nil - } - } - return nil, fmt.Errorf("entity not found in keyring") -} - -func ImportKeys(r io.Reader) error { - keys, err := openpgp.ReadKeyRing(r) - if err != nil { - return err - } - Keyring = append(Keyring, keys...) - if locked { - keypath := path.Join(xdg.DataHome(), "aerc", "keyring.asc") - keyfile, err := os.OpenFile(keypath, os.O_CREATE|os.O_APPEND, 0600) - if err != nil { - return err - } - defer keyfile.Close() - - for _, key := range keys { - if key.PrivateKey != nil { - err = key.SerializePrivate(keyfile, &packet.Config{}) - } else { - err = key.Serialize(keyfile) - } - if err != nil { - return err - } - } - } - return nil -} diff --git a/lib/messageview.go b/lib/messageview.go index f221fb2..a1797d5 100644 --- a/lib/messageview.go +++ b/lib/messageview.go @@ -8,8 +8,8 @@ import ( "github.com/ProtonMail/go-crypto/openpgp" "github.com/emersion/go-message" _ "github.com/emersion/go-message/charset" - "github.com/emersion/go-pgpmail" + "git.sr.ht/~rjarry/aerc/lib/crypto" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/lib" "git.sr.ht/~rjarry/aerc/worker/types" @@ -30,7 +30,7 @@ type MessageView interface { // Fetches a specific body part for this message FetchBodyPart(part []int, cb func(io.Reader)) - PGPDetails() *openpgp.MessageDetails + MessageDetails() *models.MessageDetails } func usePGP(info *models.BodyStructure) bool { @@ -56,12 +56,12 @@ type MessageStoreView struct { messageInfo *models.MessageInfo messageStore *MessageStore message []byte - details *openpgp.MessageDetails + details *models.MessageDetails bodyStructure *models.BodyStructure } func NewMessageStoreView(messageInfo *models.MessageInfo, - store *MessageStore, decryptKeys openpgp.PromptFunction, + store *MessageStore, pgp crypto.Provider, decryptKeys openpgp.PromptFunction, cb func(MessageView, error)) { msv := &MessageStoreView{messageInfo, store, @@ -70,12 +70,12 @@ func NewMessageStoreView(messageInfo *models.MessageInfo, if usePGP(messageInfo.BodyStructure) { store.FetchFull([]uint32{messageInfo.Uid}, func(fm *types.FullMessage) { reader := lib.NewCRLFReader(fm.Content.Reader) - pgpReader, err := pgpmail.Read(reader, Keyring, decryptKeys, nil) + md, err := pgp.Decrypt(reader, decryptKeys) if err != nil { cb(nil, err) return } - msv.message, err = ioutil.ReadAll(pgpReader.MessageDetails.UnverifiedBody) + msv.message, err = ioutil.ReadAll(md.Body) if err != nil { cb(nil, err) return @@ -91,7 +91,7 @@ func NewMessageStoreView(messageInfo *models.MessageInfo, return } msv.bodyStructure = bs - msv.details = pgpReader.MessageDetails + msv.details = md cb(msv, nil) }) } else { @@ -112,7 +112,7 @@ func (msv *MessageStoreView) Store() *MessageStore { return msv.messageStore } -func (msv *MessageStoreView) PGPDetails() *openpgp.MessageDetails { +func (msv *MessageStoreView) MessageDetails() *models.MessageDetails { return msv.details } diff --git a/models/models.go b/models/models.go index 4087c9d..96683f2 100644 --- a/models/models.go +++ b/models/models.go @@ -185,3 +185,27 @@ type OriginalMail struct { MIMEType string RFC822Headers *mail.Header } + +type SignatureValidity int32 + +const ( + UnknownValidity SignatureValidity = iota + Valid + InvalidSignature + UnknownEntity + UnsupportedMicalg + MicalgMismatch +) + +type MessageDetails struct { + IsEncrypted bool + IsSigned bool + SignedBy string // Primary identity of signing key + SignedByKeyId uint64 + SignatureValidity SignatureValidity + SignatureError string + DecryptedWith string // Primary Identity of decryption key + DecryptedWithKeyId uint64 // Public key id of decryption key + Body io.Reader + Micalg string +} diff --git a/widgets/aerc.go b/widgets/aerc.go index 717547d..0b4e36d 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -16,6 +16,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" + "git.sr.ht/~rjarry/aerc/lib/crypto" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/models" ) @@ -38,6 +39,8 @@ type Aerc struct { ui *ui.UI beep func() error dialog ui.DrawableInteractive + + Crypto crypto.Provider } type Choice struct { @@ -47,9 +50,9 @@ type Choice struct { } func NewAerc(conf *config.AercConfig, logger *log.Logger, - cmd func(cmd []string) error, complete func(cmd string) []string, - cmdHistory lib.History, deferLoop chan struct{}, -) *Aerc { + crypto crypto.Provider, cmd func(cmd []string) error, + complete func(cmd string) []string, cmdHistory lib.History, + deferLoop chan struct{}) *Aerc { tabs := ui.NewTabs(&conf.Ui) statusbar := ui.NewStack(conf.Ui) @@ -79,6 +82,7 @@ func NewAerc(conf *config.AercConfig, logger *log.Logger, statusline: statusline, prompts: ui.NewStack(conf.Ui), tabs: tabs, + Crypto: crypto, } statusline.SetAerc(aerc) diff --git a/widgets/compose.go b/widgets/compose.go index 5f2c706..6298be3 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -15,9 +15,7 @@ import ( "strings" "time" - "github.com/ProtonMail/go-crypto/openpgp" "github.com/emersion/go-message/mail" - "github.com/emersion/go-pgpmail" "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "github.com/mitchellh/go-homedir" @@ -25,7 +23,6 @@ import ( "git.sr.ht/~rjarry/aerc/completer" "git.sr.ht/~rjarry/aerc/config" - "git.sr.ht/~rjarry/aerc/lib" "git.sr.ht/~rjarry/aerc/lib/format" "git.sr.ht/~rjarry/aerc/lib/templates" "git.sr.ht/~rjarry/aerc/lib/ui" @@ -455,38 +452,27 @@ func (c *Composer) WriteMessage(header *mail.Header, writer io.Writer) error { var cleartext io.WriteCloser var err error - var signer *openpgp.Entity + var signerEmail string if c.sign { - signer, err = getSigner(c) + signerEmail, err = getSenderEmail(c) if err != nil { return err } } else { - signer = nil + signerEmail = "" } if c.encrypt { - var to []*openpgp.Entity rcpts, err := getRecipientsEmail(c) if err != nil { return err } - for _, rcpt := range rcpts { - toEntity, err := lib.GetEntityByEmail(rcpt) - if err != nil { - return errors.Wrap(err, "no key for "+rcpt) - } - to = append(to, toEntity) - } - cleartext, err = pgpmail.Encrypt(&buf, header.Header.Header, - to, signer, nil) - + cleartext, err = c.aerc.Crypto.Encrypt(&buf, rcpts, signerEmail, c.aerc.DecryptKeys, header) if err != nil { return err } } else { - cleartext, err = pgpmail.Sign(&buf, header.Header.Header, - signer, nil) + cleartext, err = c.aerc.Crypto.Sign(&buf, signerEmail, c.aerc.DecryptKeys, header) if err != nil { return err } @@ -1031,30 +1017,3 @@ func (rm *reviewMessage) OnInvalidate(fn func(ui.Drawable)) { func (rm *reviewMessage) Draw(ctx *ui.Context) { rm.grid.Draw(ctx) } - -func getSigner(c *Composer) (signer *openpgp.Entity, err error) { - signerEmail, err := getSenderEmail(c) - if err != nil { - return nil, err - } - signer, err = lib.GetSignerEntityByEmail(signerEmail) - if err != nil { - return nil, err - } - - key, ok := signer.SigningKey(time.Now()) - if !ok { - return nil, fmt.Errorf("no signing key found for %s", signerEmail) - } - - if !key.PrivateKey.Encrypted { - return signer, nil - } - - _, err = c.aerc.DecryptKeys([]openpgp.Key{key}, false) - if err != nil { - return nil, err - } - - return signer, nil -} diff --git a/widgets/msglist.go b/widgets/msglist.go index 5eee7ed..df24526 100644 --- a/widgets/msglist.go +++ b/widgets/msglist.go @@ -295,7 +295,8 @@ func (ml *MessageList) MouseEvent(localX int, localY int, event tcell.Event) { if msg == nil { return } - lib.NewMessageStoreView(msg, store, ml.aerc.DecryptKeys, + lib.NewMessageStoreView(msg, store, ml.aerc.Crypto, + ml.aerc.DecryptKeys, func(view lib.MessageView, err error) { if err != nil { ml.aerc.PushError(err.Error()) diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go index 89cbc2c..c88c981 100644 --- a/widgets/msgviewer.go +++ b/widgets/msgviewer.go @@ -75,9 +75,9 @@ func NewMessageViewer(acct *AccountView, {Strategy: ui.SIZE_EXACT, Size: ui.Const(headerHeight)}, } - if msg.PGPDetails() != nil { + if msg.MessageDetails() != nil { height := 1 - if msg.PGPDetails().IsSigned && msg.PGPDetails().IsEncrypted { + if msg.MessageDetails().IsSigned && msg.MessageDetails().IsEncrypted { height = 2 } rows = append(rows, ui.GridSpec{Strategy: ui.SIZE_EXACT, Size: ui.Const(height)}) @@ -107,8 +107,8 @@ func NewMessageViewer(acct *AccountView, borderChar := acct.UiConfig().BorderCharHorizontal grid.AddChild(header).At(0, 0) - if msg.PGPDetails() != nil { - grid.AddChild(NewPGPInfo(msg.PGPDetails(), acct.UiConfig())).At(1, 0) + if msg.MessageDetails() != nil { + grid.AddChild(NewPGPInfo(msg.MessageDetails(), acct.UiConfig())).At(1, 0) grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(2, 0) grid.AddChild(switcher).At(3, 0) } else { diff --git a/widgets/pgpinfo.go b/widgets/pgpinfo.go index 6c07ed9..8dc0570 100644 --- a/widgets/pgpinfo.go +++ b/widgets/pgpinfo.go @@ -1,22 +1,18 @@ package widgets import ( - "errors" - "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" - - "github.com/ProtonMail/go-crypto/openpgp" - pgperrors "github.com/ProtonMail/go-crypto/openpgp/errors" + "git.sr.ht/~rjarry/aerc/models" ) type PGPInfo struct { ui.Invalidatable - details *openpgp.MessageDetails + details *models.MessageDetails uiConfig config.UIConfig } -func NewPGPInfo(details *openpgp.MessageDetails, uiConfig config.UIConfig) *PGPInfo { +func NewPGPInfo(details *models.MessageDetails, uiConfig config.UIConfig) *PGPInfo { return &PGPInfo{details: details, uiConfig: uiConfig} } @@ -27,38 +23,33 @@ func (p *PGPInfo) DrawSignature(ctx *ui.Context) { defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT) // TODO: Nicer prompt for TOFU, fetch from keyserver, etc - if errors.Is(p.details.SignatureError, pgperrors.ErrUnknownIssuer) || - p.details.SignedBy == nil { + if p.details.SignatureValidity == models.UnknownEntity || + p.details.SignedBy == "" { x := ctx.Printf(0, 0, warningStyle, "*") x += ctx.Printf(x, 0, defaultStyle, " Signed with unknown key (%8X); authenticity unknown", p.details.SignedByKeyId) - } else if p.details.SignatureError != nil { + } else if p.details.SignatureValidity != models.Valid { x := ctx.Printf(0, 0, errorStyle, "Invalid signature!") x += ctx.Printf(x, 0, errorStyle, " This message may have been tampered with! (%s)", - p.details.SignatureError.Error()) + p.details.SignatureError) } else { - entity := p.details.SignedBy.Entity - ident := entity.PrimaryIdentity() - x := ctx.Printf(0, 0, validStyle, "✓ Authentic ") x += ctx.Printf(x, 0, defaultStyle, "Signature from %s (%8X)", - ident.Name, p.details.SignedByKeyId) + p.details.SignedBy, p.details.SignedByKeyId) } } func (p *PGPInfo) DrawEncryption(ctx *ui.Context, y int) { validStyle := p.uiConfig.GetStyle(config.STYLE_SUCCESS) defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT) - entity := p.details.DecryptedWith.Entity - ident := entity.PrimaryIdentity() x := ctx.Printf(0, y, validStyle, "✓ Encrypted ") x += ctx.Printf(x, y, defaultStyle, - "To %s (%8X) ", ident.Name, p.details.DecryptedWith.PublicKey.KeyId) + "To %s (%8X) ", p.details.DecryptedWith, p.details.DecryptedWithKeyId) } func (p *PGPInfo) Draw(ctx *ui.Context) {