
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
- Made a test account [email protected] at 14:44 UTC, 12-Sep-2024
- 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.
Element | keyid | creation 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 #3Binds 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::

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:
- 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. - 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.