Investigating the ProtonMail API


A recent investigation into a ProtonMail burner address raised some interesting questions around use of the API in OSINT. Attempting to identify the account creation date from the time of minting for public PGP key prompted a closer look.

The Proton API

Documentation can be found in a few sources, but is no longer current. This page is useful, but the live version has since been removed.

https://api.protonmail.ch/pks/lookup\?op\=get\&search\={email address}

Public Keys can be obtained for an email address using the pks api

https://api.protonmail.ch/pks/lookup\?op\=index\&search\={email address}

A higher level view can be obtained with the index option

Taking a look

  1. Made a test account [email protected] at 14:44 UTC, 12-Sep-2024
  2. We can then look at the PGP public key packet information:
curl https://api.protonmail.ch/pks/lookup\?op\=get\&search\=[email protected] | gpg --list-packets

This shows creation times for the public key packet, public sub key packet and signature packet. For each account tested, the creation dates (public key pack, public sub key packet, signature packets for each) for the email were consistent, with signature packets associated with certified with the ProtonCA key ID calculated up to a minute after the associated key.

Elementkeyidcreation date
public key packet
Contains the public key data used for signing messages

   AFCB659191746D6B Thu Sep 12 14:44:56 UTC 2024
signature packet #1
Binds the user ID to the key via a signature

   AFCB659191746D6B   Thu Sep 12 14:44:56 UTC 2024
signature packet #2
Binds the user ID to the key via a second party (ProtonCA) signature

   D806C1AF5978E8C7   Thu Sep 12 14:45:57 UTC 2024
public sub key packet
Contains the public subkey used for encryption

   BE405612DF014AE4   Thu Sep 12 14:44:56 UTC 2024
signature packet #3
Binds the subkey to the primary key

   AFCB659191746D6B   Thu Sep 12 14:44:56 UTC 2024



Note that all packets except for signature packet are generated at the same timestamp. This common time is also shared in the op=info API query:

info:1:1
pub:ae073692147c9da2fe24d855afcb659191746d6b:22::1726152296::
uid:[email protected] <[email protected]>:1726152296::

Interestingly, the info:1:1 element is used in some open source tools as confirmation that the account exists. However, at the time of writing this appears no longer to function, and tests with an non-legitimate account names also returns a confirmation.

info:1:1
pub:57381f5e0bed8f8b6ba77fe0340f3eec8d936a1a:22::1702143764::
uid:dghpcybpcybhigxvbmcgyw5kignvbxbsawnhdgvkihjhbmrvbsbwahjhc2u@proton.me <dghpcybpcybhigxvbmcgyw5kignvbxbsawnhdgvkihjhbmrvbsbwahjhc2u@proton.me>:1702143764::
image showing name too many characters to be a legitimate protonmail account

Closer inspection shows that ‘impossible’ or otherwise unregistered accounts return a PGP key creation time when queried, but do not carry the additional ProtonCA signature packet. Existence of the packet can therefore be used in account enumeration.

So what?

We have demonstrated use of publicly available information from ProtonMail to quickly identify the time of public key creation, a useful proxy for account creation date. We have also identified another mechanism to parse legitimate / unregistered accounts that does not appear in use in the tools reviewed. There are some unanswered questions, specifically (i) how are PGP creation times for unregistered accounts generated, and (ii) how does this differ for accounts after deletion, if at all. The latter could be of interest for OSINT against burner accounts, and we will give an update once the test account has been completely deleted by Proton.


Update

Repeating the lookup after the account is deleted shows the same pattern as non-existent accounts:

  1. There is no signature packet #2 (binding the UserID to the signature via a second party signing) for the public key of the deleted account.
  2. The curl request for the deleted account returns a lowercase user ID packet. When the account existed, the account exists the case returned is accurate, even if the lookup is for an incorrectly capitalised string.

When using the API for OSINT against ProtonMail accounts, consider parsing based on the number of signature packets, and, if required, the existence of capitalization in API requests.