Holehe & The Secret Email — What a ProtonMail PGP Key Date Can Prove
ProtonMail never publishes account creation dates. It publishes a PGP key for every address, and that key carries a timestamp. One HKP query away, and less proof than it looks.
Platform: OSINT Industries CTF (ctf.osint.industries)
Category: OSINT
Flag format: OSINT{DD-MM-YYYY}
Correction, 25 September 2026. The first version of this writeup treated the PGP key’s creation date as the date the account was created. It is not. Proton lets users import keys, generate new ones and delete old ones, a password reset replaces the keys, and every address added later gets its own key. The date is the creation date of the key the keyserver returns today, nothing more. The flag is unchanged; the reasoning around it is corrected below.
The Challenge
You are investigating the origins of a popular OSINT tool used to pivot from email addresses to online accounts: Holehe.
Behind every tool, there is a creator — and behind that creator, there is an email address. Your mission is to trace back the very first appearance of the creator’s email address online and determine the exact date it was created.
Constraints were explicit: legal, open-source information only. No contacting the developer, no intrusion. Everything solvable through code repositories, public profiles and archives.
Two things need to be found: the creator’s public email address, and the creation date of that address. The second half is where the challenge actually lives, because ProtonMail does not publish account creation dates anywhere in its UI.
Result up front: the address is megadose@protonmail.com, its current PGP key was generated 14 January 2021 at 09:41:06 UTC, and the flag is OSINT{14-01-2021}. One HKP keyserver query gets you there. The rest of this is what that date does and does not prove, and where it can quietly hand you the wrong day.
Step 1 — The repo owner is megadose, not a fork
Holehe is a well-known tool, so this part is mechanical. A search leads straight to the canonical repository:
https://github.com/megadose/holehe
The repository owner is the GitHub user megadose. Commit history confirms authorship rather than a fork or a mirror — worth checking, since several clones of Holehe exist under other accounts and one of them would send you looking at the wrong person entirely.
Step 2 — The email sits in setup.py, and again in a second repo
The email is present in the Holehe repository itself, in setup.py:
setup(
name='holehe',
version="1.61",
author="megadose",
author_email="megadose@protonmail.com",
...
)
Packaging metadata is a reliable place to look for maintainer contact details across the Python ecosystem — the same value is mirrored on PyPI, so it survives even if the repository changes.
For independent confirmation I pivoted to another repository under the same account, toutatis, and checked its CODE_OF_CONDUCT.md:
https://github.com/megadose/toutatis/blob/master/CODE_OF_CONDUCT.md
The Contributor Covenant boilerplate requires an enforcement contact, and this one is filled in with the same address. Two independent repositories, two independent contexts, same email:
megadose@protonmail.com
Cross-verification across repositories is not busywork. A single occurrence in a single file could be stale, copied from a template, or belong to a different maintainer. Two makes it an attributed identity.
Step 3 — ProtonMail publishes no registration date anywhere
ProtonMail is a privacy-focused provider. It does not expose account registration dates through its web interface, through any public profile, or through any documented API endpoint intended for that purpose.
The way through is indirect: Proton generates an OpenPGP key pair when an account is created and whenever a new address is added, and the public key is published on Proton’s own keyserver so other people can send encrypted mail. OpenPGP keys carry a creation timestamp inside the key material as a matter of protocol. Query the keyserver, read the timestamp.
That timestamp belongs to the key, not to the account. The key a keyserver returns today is not necessarily the first one the address ever had: users can generate a new key and make it primary, import a key made elsewhere (with whatever creation date it carries), and delete old keys. A password reset without recovery sets the old keys inactive and creates new ones.
Step 4 — Every ProtonMail address ships a dated PGP key
Proton exposes a standard HKP (HTTP Keyserver Protocol) interface. The op=index operator returns machine-readable metadata rather than the key itself:
https://api.protonmail.ch/pks/lookup?op=index&search=megadose%40protonmail.com
Response:
info:1:1
pub:b2e401ef07705693a0ccd8d1b0e29d2cac738595:22::1610617266::
uid:megadose@protonmail.com <megadose@protonmail.com>:1610617266::
No tooling required — this runs in a browser address bar.

Re-run 2026-08-06 — the response is byte-identical to the one above, so the key has not been rotated in the years since.
Parsing the response
The HKP draft specification defines the machine-readable index format. Each line matters here.
info:1:1 — format version 1, followed by the count of keys returned. The count is 1: one key is published for this address today. That removes the question of which timestamp to read, but it says nothing about keys that came before. A deleted key leaves no trace in this response. With several pub: entries the oldest one is the better candidate, but it is still only the oldest key that survived.
pub: line — colon-delimited, fields in order:
| Field | Value | Meaning |
|---|---|---|
| 1 | pub | record type |
| 2 | b2e401ef07705693a0ccd8d1b0e29d2cac738595 | key fingerprint |
| 3 | 22 | algorithm ID |
| 4 | (empty) | key length |
| 5 | 1610617266 | creation date, Unix epoch |
| 6 | (empty) | expiration date |
| 7 | (empty) | flags (revoked/disabled/expired) |
Field 5 is the answer. Field 6 being empty means the key carries no expiration. Field 7 being empty means the key is neither revoked nor disabled — the address is live.
Algorithm ID 22 is EdDSA (Curve25519), per the IANA OpenPGP registry. Proton generates Curve25519 keys by default; its support pages note that accounts created before 2021 may still have RSA keys. An EdDSA key fits a 2021 account, but it also fits any key generated later, so it is consistent with the date without supporting it.
uid: line — carries the same timestamp, 1610617266. This is the self-signature creation date of the User ID packet: the moment the key was bound to that email address. Key creation and UID binding at the identical second show that both happened in one automated operation. The first version of this writeup read that as evidence for account creation. It is not: Proton produces the same pattern every time it generates a key, at signup, for a new address, on “Generate key” and after a password reset.
Converting the timestamp
1610617266 → Thu, 14 Jan 2021 09:41:06 UTC
The timestamp is UTC. Most online converters helpfully render epoch values in the browser’s local timezone, and if you are not paying attention you will submit whatever your machine decided. At 09:41 UTC this is harmless. Had the key been generated near midnight UTC, a local offset would have shifted the date by a full day and produced a wrong flag that looks entirely correct. Convert explicitly:
date -u -d @1610617266
Flag
OSINT{14-01-2021}Notes
The claim being made. The evidence establishes that the OpenPGP key published today for megadose@protonmail.com was created on 14 January 2021, and that the address was bound to it in the same operation. It does not establish when the account or the address was created:
- If Proton generated the key, the address existed on that date at the latest. It may be older, if the key replaced an earlier one.
- If the key was imported, its date can even predate the address.
The challenge accepts this date, and for a CTF that is the intended reading. In an investigation, report it as “PGP key created on” and look for independent traces before calling it an account creation date: first appearances in commits, package metadata or archived pages.
Wrong flag, right answer. I burned a submission on this one. My first attempt was rejected despite containing the correct date, because I had assembled the flag from text that had passed through an editor with typographic substitution enabled. The separators were en dashes (–, U+2013), not hyphens (-, U+002D). Visually near-identical in a proportional font, completely different bytes.
Worth building the reflex, because it costs points on challenges you have already solved:
print([hex(ord(c)) for c in flag])
Type flags by hand, or launder them through a plaintext field first. Markdown editors, note-taking apps, word processors and most CMS platforms will silently rewrite hyphens, quotes and ellipses.
Generalising the technique. The HKP query works against any keyserver, not just Proton’s — keys.openpgp.org/pks/lookup?op=index&search= follows the same syntax. The difference is coverage: Proton generates a key for every address automatically, so a lookup is effectively an existence check plus the date of the current key for the entire protonmail.com, protonmail.ch, pm.me and proton.me namespace. On general-purpose keyservers, only people who deliberately uploaded a key are present, and the creation date reflects when they made that key rather than when they made the account.
Fetching the full key instead of the index uses the same endpoint with a different operator:
curl -s "https://api.protonmail.ch/pks/lookup?op=get&search=megadose@protonmail.com" > key.asc
gpg --show-keys --with-colons key.asc
For this challenge it adds nothing the index did not already provide, but it is the route to take when you need subkey structure or signature details.
Sources
- github.com/megadose/holehe
- github.com/megadose/holehe — setup.py
- github.com/megadose/toutatis — CODE_OF_CONDUCT.md
- api.protonmail.ch HKP lookup
- Proton Support — Key management
- Proton Support — How to create new encryption keys
- RFC 4880 — OpenPGP Message Format
- HKP draft specification — machine readable indexes