Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

encryption privacy fix: do not add keys with partial address match #84

Merged
merged 3 commits into from
Nov 15, 2024

Conversation

hbog
Copy link
Contributor

@hbog hbog commented Oct 5, 2024

Fixes a privacy issue caused by a partial match of a recipient address with a public key uid in your key ring. For example, when sending an encrypted mail to eve@example.org, and there is a public key for steve@example.org in your key ring, Steve will be able to decrypt the message.

Fixes #70.
Thanks @The-Compiler for spotting this.

Fixes a privacy issue caused by a partial match of a recipient address with
a public key uid in your keyring. For example, when sending an encrypted mail to
eve@example.org, and there is a public key for steve@example.org in your
keyring, steve will be able to decrypt the message.
dodo/pgp_util.py Outdated
# "mail@example.org <mail@example.org>" are skipped. These uids are considered
# malformed, but they occur.
recipients_keys = [key['fingerprint'] for key in Gpg.list_keys()
if any(addr == email.utils.parseaddr(uid, strict=False)[1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if any(addr == email.utils.parseaddr(uid, strict=False)[1]
if any(addr == utils.strip_email_address(uid, strict=False)

from here maybe?

dodo/dodo/util.py

Lines 282 to 287 in 194fb49

def strip_email_address(e: str) -> str:
"""Strip the display name, leaving just the email address
E.g. "First Last <me@domain.com>" -> "me@domain.com"
"""
return email.utils.parseaddr(e)[1]

Would probably have to add strict=True to that, so feel free to ignore if you think your approach is simpler here.

Copy link
Contributor

@The-Compiler The-Compiler Oct 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I notice now that strict=True was only added in Python 3.12.6 (released not even a month ago), while Dodo claims to support older versions.

I suppose something like kwargs = {"strict": True} if strict and sys.version_info[:3] >= (3, 12, 6) else {} followed by email.utils.parseaddr(e, **kwargs) would do? Or catching TypeError I guess.

# malformed, but they occur.
recipients_keys = [key['fingerprint'] for key in Gpg.list_keys()
if any(addr == email.utils.parseaddr(uid, strict=False)[1]
for uid in key['uids'] for addr in recipients)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this gets quite cramped with [... for ... in ... if any(... for ... in ... for ... in ...)]. That's a lot going on in one list comprehension. What do you think about a (untested):

def find_gpg_keys(recipients: list[str]) -> list[str]:
    keys = []
    for key in Gpg.list_keys():
        for uid in key["uid"]:
            address = utils.strip_email_address(uid, strict=False)
            if address in recipients:
                keys.append(key["fingerprint"])

or maybe

def find_gpg_keys(recipients: list[str]) -> list[str]:
    keys = []
    for key in Gpg.list_keys():
        addresses = {utils.strip_email_address(uid, strict=False) for uid in key["uids"]}
        if addresses & recipients:
            keys.append(key["fingerprint"])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the strict parameter and used the util.strip_email_address instead. I will address the case where gnupg.list_keys emits malformed email addresses later, together with your suggestions above. Note that the behavior of util.strip_email_address changes in python 3.12.6 but I think this will not have a big impact on dodo because, at first sight, notmuch transforms mail addresses such as mail@example.com <mail@example.com> to "mail@example.com" <mail@example.com> which are parsed correctly by the new email.utils.parseaddr (and also by the golang validation mentioned on issue #102988)

hbog and others added 2 commits October 7, 2024 09:02
The strict parameter for email.utils.parseaddr is only available in
python version 3.12.6 and above.
@akissinger akissinger merged commit 8390384 into akissinger:master Nov 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Weird regex matching on UIDs for encryption
3 participants