8000 feat(pgp): save drafts of encrypted messages encrypted by hanthor · Pull Request #603 · hyprlab/hylki · GitHub
[go: up one dir, main page]

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@

## Unreleased

- **Added: Autocrypt** (Level 1). An account can send its OpenPGP key with
its mail and keep the keys others send that way, and the keys passed on in
encrypted mail to several people. When everyone on a message prefers
encrypted mail, Encrypt turns on by itself. Found keys stay in a keyring of
Hylki's own, never the user's GnuPG keyring, and a key in a header that
names someone other than the sender is refused.

- **Added: each recipient shows whether they have an OpenPGP key**, and
where it came from. A recipient without one is looked up on their own
domain (WKD) as they are added; a keyserver only when allowed under
Settings, OpenPGP.

- **Fixed: drafts of encrypted messages were saved readable** by the server
and the cache. They are now encrypted to the user's own key, and decrypted
when opened again.

- **Fixed: Bcc recipients' OpenPGP keys showed in encrypted mail.** Every
copy of an encrypted message named the key of each recipient, so the To and
Cc could tell whose keys were Bcc'd. A Bcc recipient's key is now in the
message without its key ID.

- **Fixed: a signature from someone else's key showed green.** A good
signature by a trusted key whose addresses do not include the message's
From address now shows amber, **Signed by another address**, and the
details name the address the key lacks.

- **Added: a warning before encrypting to a key you have not trusted.**
**Send** names the recipients whose keys nobody has vouched for and asks
before the message goes; **Send Anyway** sends it as before. When an
address has several keys, the trusted one is used.

- **Fixed: `w` and `b` left a message card under the bar above it** (#587, by
James Reilly). The card's top border and selection outline were cut off. The
card now stops 14 px below the bar, as it does when focused for a reply.
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ rusqlite = { version = "0.32", features = ["bundled"] }
# ids are stored in the cache, and std may change its algorithm in any
# release; this crate won't. Already in the graph via phf.
siphasher = "1"
# WKD names a key by the SHA-1 of its address's local part. Already in
# Cargo.lock as another crate's dependency, so nothing new is downloaded.
sha1 = "0.10"

# Configuration
serde = { version = "1", features = ["derive"] }
Expand Down
51 changes: 51 additions & 0 deletions docs/DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,9 @@ signature. There are four ways to get one, none of which need a terminal:
beside their name. Click it and choose **Fetch the sender's key**. Hylki
looks in the message itself first (many clients attach the key in an
Autocrypt header), then asks the sender's provider (WKD), then the keyservers.
A key from the message's Autocrypt header is taken only when the header
names the sender's own address, and it goes into Hylki's own keyring of
found keys, never yours (see below).
- A message with a key file attached shows an **Import OpenPGP key** button on
that attachment.
- Under *Settings → OpenPGP → Other people's keys*, **Fetch by address…**
Expand Down Expand Up @@ -1689,6 +1692,15 @@ recipient has no key in your keyring, or your address has no key of its own,
Hylki says so before anything is sent. Replying to an encrypted message starts
with Encrypt on.

When a recipient's key is one you have not trusted yet (step 4), **Send**
names them and asks first: anyone can publish a key with any address on it.
**Send Anyway** encrypts to the key as it is. With several keys for one
address, the one you trusted is used.

Bcc recipients are encrypted to without their key IDs in the message, so the
To and Cc recipients cannot tell whose keys were Bcc'd. They can still see
that some were.

An encrypted message carries its subject inside, where only the recipients
can read it, and "..." as the subject anyone else sees, as Thunderbird does.
Hylki and other mail programs that read subjects from there show the real one
Expand All @@ -1711,6 +1723,40 @@ in the composer moves Sign to the new account's setting until you press Sign
yourself; after that the composer leaves it as you set it. Stored on the
account as `sign_by_default = true` in `accounts.toml`.

**Autocrypt, found keys and encrypted drafts**

Turn on **Send your key with your mail** under an account's *OpenPGP* group
and every message from it carries your public key in an `Autocrypt:` header
(Autocrypt Level 1). Hylki then also keeps the keys other people send that
way, and the keys passed on inside encrypted mail to several people
(Autocrypt gossip). **Encrypt by default** under it says you prefer encrypted
mail: when everyone you write to says the same, the composer turns Encrypt on
by itself. Stored as `autocrypt = true` and `autocrypt_mutual = true`.

Keys found this way, and keys found by looking a recipient up, go into a
keyring of Hylki's own (`~/.local/share/hylki/discovered-keys`), never your
GnuPG keyring: nobody vouched for them, and gpg would otherwise use them for
everything else too. Which key belongs to whom is kept in the cache.

While you address a message, each recipient's pill shows a lock: green for a
key in your keyring, blue for one Hylki found (Autocrypt or WKD), amber for
one that may be out of date, and dim (red while Encrypt is on) for none.
Hovering says where the key came from. Recipients without a key are looked up
on their own domain's server (WKD) a moment after you add them; *Settings →
OpenPGP → Finding keys* turns that off, or adds keys.openpgp.org for addresses
WKD has nothing for (off by default, as the keyserver learns who you write to).
The pills only show locks when your From address has a key of its own.

A draft of a message set to be encrypted is saved encrypted to your key alone,
with its subject, recipients and Bcc inside, so neither the server nor the
cache holds its text. Opening it again asks for your passphrase if the agent
needs it, and the message opens with Encrypt on. This works for IMAP, JMAP and
Microsoft 365 accounts.

Hylki uses GnuPG for all of this. Whether it should move to an OpenPGP
library for OpenPGP v6 keys is discussed in
[OPENPGP_BACKEND.md](OPENPGP_BACKEND.md).

**Reading the result**

Beside a sender's name, a lock means the message was encrypted and a shield
Expand All @@ -1719,6 +1765,11 @@ Amber: a doubt, such as an unknown or untrusted key, or an expired one. Red:
a failure, such as a signature that does not match or a message that could not
be decrypted. Click the icon for the details.

A signature only shows who owns the key, so it stays amber when none of the
key's addresses is the message's From address, however much the key is
trusted: it reads **Signed by another address**, and the details say which
address the key lacks.

To have the result in words as well, switch on *Settings → Reading → Name the
OpenPGP result*. The icons then sit in a label that reads **Signed**,
**Encrypted**, **Encrypted and signed**, or what is wrong, such as **Bad
Expand Down
54 changes: 54 additions & 0 deletions docs/OPENPGP_BACKEND.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# OpenPGP backend: GnuPG, Sequoia or RNP

A proposal, not a decision. Hylki runs the system `gpg` for every OpenPGP
operation today. This page compares that with the two libraries a mail client
could embed instead, mainly for OpenPGP v6 (RFC 9580), and recommends a path.

## What Hylki needs from a backend

- Decrypt, verify, sign and encrypt PGP/MIME, read inline PGP.
- Key management: generate, import, export, delete, local trust.
- Two keyrings: the user's, and Hylki's own for keys found through Autocrypt,
gossip and WKD, which must never mix.
- Secret keys and passphrases kept out of Hylki's process where possible.
- Something that works inside the Flatpak.

## The three candidates

| | GnuPG 2.4 / 2.5 (today) | Sequoia (`sequoia-openpgp`) | RNP (`librnp`) |
| --- | --- | --- | --- |
| Form | A program, driven over pipes | A Rust library | A C library, with Rust bindings by third parties |
| RFC 9580 (v6 keys, AEAD, Argon2) | No. GnuPG follows LibrePGP, which diverged from the IETF draft before v6 | Yes, since sequoia-openpgp 2.0 | In progress upstream; check the release in use |
| RFC 4880 (v4) interoperability | Yes | Yes | Yes |
| Secret keys | In gpg-agent, out of Hylki's process; pinentry asks for passphrases | In the process, unless the keystore crate (`sequoia-keystore`) or gpg-agent is used | In the process |
| Keyring | `~/.gnupg`, shared with every other GnuPG tool | Its own `pgp.cert.d` store, or anything Hylki writes; can read a GnuPG keybox | Its own files, or GnuPG's keybox (read) |
| Smartcards and tokens | Yes, through scdaemon | Through `openpgp-card` crates | Limited |
| Flatpak today | The runtime's gpg and the host's agent socket | Built into the binary | Built into the binary |
| Licence | GPL-3.0-or-later | LGPL-2.0-or-later | BSD-2-Clause |

## Trade-offs

- **GnuPG** is what users already have: their keys, their trust decisions,
their smartcards and their agent all work unchanged, and Hylki never holds
a secret key. Its costs are a text protocol of status lines, a process per
operation, keyring locks (#316), and no path to v6: the GnuPG project has
said it will not implement RFC 9580's v6 key format. A correspondent with a
v6 key cannot be encrypted to.
- **Sequoia** is a Rust crate, so it fits the codebase, needs no process per
operation, and supports v6 and v4. Hylki would have to take on key storage
and passphrase prompts, or keep gpg-agent for secret keys through
Sequoia's agent support. Users with keys in `~/.gnupg` would need them
read from there.
- **RNP** is what Thunderbird uses. It brings a C dependency and its own
keyring format into a Rust codebase, with less to gain over Sequoia.

## Recommendation

Keep GnuPG for the user's own keys, and move Hylki's own keyring of found
keys (Autocrypt, gossip, WKD; see `mail::pgp::discovery`) to Sequoia first.
That keyring only ever needs public keys: importing, listing, exporting and
encrypting to them, which Sequoia does in-process, with v6 support and no
second gpg home. Reading and writing the user's mail with their own key would
stay with gpg and its agent. Moving that part as well is worth it only once
v6 keys are common among Hylki's users, and is a maintainer decision because
it changes where secret keys live.
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
the words the release page and About use.
- **[SECURITY.md](SECURITY.md):** reporting a vulnerability privately.
- **[LICENSE.md](LICENSE.md):** the licence and third-party notices.
- **[OPENPGP_BACKEND.md](OPENPGP_BACKEND.md):** a proposal: GnuPG, Sequoia or
RNP for OpenPGP v6.

Also at the top of the repository: [CHANGELOG.md](../CHANGELOG.md), and
[po/README.md](../po/README.md) for translators.
Expand Down
40 changes: 36 additions & 4 deletions po/ca.po
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,9 @@ msgstr "Missatge nou a partir d'una plantilla"
msgid "Send the message you are writing"
msgstr "Envia el missatge que estàs escrivint"

#. Save Draft, Save as Template and Delete Draft are not here:
#. the Save Draft button and its arrow stay in the header at
#. any width.
msgid "Attach files"
msgstr "Adjunta fitxers"

Expand Down Expand Up @@ -1905,6 +1908,11 @@ msgstr "El fitxer no és un fitxer de text"
msgid "The file has no rows below its first."
msgstr "El fitxer no és un fitxer de text"

msgid ""
"The signing key is not for {addr}, the address this message is from, so the "
"signature does not show who sent it."
msgstr ""

msgid "GnuPG (gpg) is not installed, so this message cannot be read."
msgstr ""
"El GnuPG (gpg) no està instal·lat, així que aquest missatge no es pot llegir."
Expand Down Expand Up @@ -2164,6 +2172,12 @@ msgstr "Xifrat amb OpenPGP; no s'ha pogut desxifrar"
msgid "Encrypted with OpenPGP"
msgstr "Xifrat amb OpenPGP"

#, fuzzy
msgid ""
"Encrypted, and signed by {signer}, whose key is not for the sender's address "
"{addr}"
msgstr "Xifrat i signat per {signer}; la signatura ha caducat"

msgid "Encrypted, and signed by {signer}"
msgstr "Xifrat i signat per {signer}"

Expand All @@ -2188,6 +2202,9 @@ msgstr "Xifrat i signat per {signer}; la signatura ha caducat"
msgid "OpenPGP-signed; the signature could not be checked"
msgstr "Signat amb OpenPGP; no s'ha pogut comprovar la signatura"

msgid "Signed by {signer}, whose key is not for the sender's address {addr}"
msgstr ""

msgid "Signed by {signer}"
msgstr "Signat per {signer}"

Expand Down Expand Up @@ -2217,6 +2234,10 @@ msgstr "Xifrat"
msgid "Signature not checked"
msgstr "Signatura no comprovada"

#, fuzzy
msgid "Signed by another address"
msgstr "Mou a una altra llibreta d'adreces"

msgid "Encrypted and signed"
msgstr "Xifrat i signat"

Expand Down Expand Up @@ -3468,8 +3489,8 @@ msgid ""
"1. At dropbox.com/developers/apps, press Create app.\n"
"2. Choose Scoped access, then App folder or Full Dropbox.\n"
"3. Give it a name no one else has used and press Create app.\n"
"4. On Permissions, tick account_info.read, files.content.write and "
"sharing.write, then press Submit.\n"
"4. On Permissions, tick account_info.read, files.content.write and sharing."
"write, then press Submit.\n"
"5. On Settings, under OAuth 2, add the redirect URI {uri} and press Add.\n"
"6. Copy the App key into the field above.\n"
"Development status is enough. Leave the field empty to use this build's own "
Expand All @@ -3479,8 +3500,8 @@ msgstr ""
"1. A dropbox.com/developers/apps, prem Create app.\n"
"2. Tria Scoped access, i després App folder o Full Dropbox.\n"
"3. Dona-li un nom que ningú més no hagi fet servir i prem Create app.\n"
"4. A Permissions, marca account_info.read, files.content.write i "
"sharing.write, i després prem Submit.\n"
"4. A Permissions, marca account_info.read, files.content.write i sharing."
"write, i després prem Submit.\n"
"5. A Settings, sota OAuth 2, afegeix l'URI de redirecció {uri} i prem Add.\n"
"6. Copia l'App key al camp de dalt.\n"
"L'estat Development és suficient. Deixa el camp buit per utilitzar la clau "
Expand Down Expand Up @@ -3867,6 +3888,16 @@ msgstr ""
"No hi ha cap clau OpenPGP per a {addr}. Demana'ls la seva clau pública, o "
"obtén-la a Paràmetres, OpenPGP."

msgid "Encrypt to keys you have not checked?"
msgstr ""

msgid ""
"Nobody has confirmed that the OpenPGP keys for {addresses} belong to them. "
"If a key is someone else's, they can read the message and the recipient "
"cannot. Compare the fingerprint with the recipient, then mark the key as "
"trusted under Settings, OpenPGP."
msgstr ""

msgid "Send later"
msgstr "Envia més tard"

Expand Down Expand Up @@ -6175,6 +6206,7 @@ msgstr "Traducció"
msgid "General"
msgstr "General"

#. Rendered as Pango markup — a bare "&" breaks it.
msgid "Appearance"
msgstr "Aparença"

Expand Down
Loading
0