https://github.com/wenceslas-sanchez/proton-drive
Async Proton Drive client with end-to-end encryption, SRP authentication and OpenPGP key management
https://github.com/wenceslas-sanchez/proton-drive
async cloud-storage e2ee end-to-end-encryption openpgp pgp privacy proton-drive proton-mail protonmail python self-hosted srp
Last synced: 4 months ago
JSON representation
Async Proton Drive client with end-to-end encryption, SRP authentication and OpenPGP key management
- Host: GitHub
- URL: https://github.com/wenceslas-sanchez/proton-drive
- Owner: wenceslas-sanchez
- License: gpl-3.0
- Created: 2025-12-24T20:09:32.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-03-04T20:24:37.000Z (4 months ago)
- Last Synced: 2026-03-05T02:18:53.845Z (4 months ago)
- Topics: async, cloud-storage, e2ee, end-to-end-encryption, openpgp, pgp, privacy, proton-drive, proton-mail, protonmail, python, self-hosted, srp
- Language: Python
- Homepage:
- Size: 411 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# proton-drive-client
A Python async client to access your Proton Drive programmatically, browse the tree, download files, and decrypt them on the fly.
## Install
```bash
git clone https://github.com/wenceslas-sanchez/proton-drive.git
cd proton-drive
uv sync
```
## Quick start
```python
import asyncio
from proton_drive import ProtonDriveClient
async def main():
async with ProtonDriveClient() as client:
await client.authenticate("user@proton.me", "password")
if client.requires_2fa:
await client.provide_2fa(input("2FA code: "))
# Browse
root = await client.build_tree()
print(root)
# List a directory
nodes = await client.list_directory("/Documents")
# Download a file
async for chunk in client.download_file("/Documents/report.pdf"):
...
# Download to disk
await client.download_to_file("/Documents/report.pdf", "./report.pdf")
asyncio.run(main())
```
## API
### `ProtonDriveClient`
| Method | Description |
|---|---|
| `authenticate(username, password)` | Log in |
| `provide_2fa(code)` | Submit TOTP/2FA code |
| `logout()` | Log out |
| `build_tree(max_depth=50)` | Build the full folder tree |
| `list_directory(path="/")` | List direct children of a folder |
| `get_node(path)` | Get a node by path, or `None` |
| `download_file(path)` | Async generator yielding decrypted bytes |
| `download_to_file(path, destination)` | Download and write to disk |
## How encryption works
Proton Drive is end-to-end encrypted. Everything revolves around a **key hierarchy** and **per-file session keys**.
### Key hierarchy
```
User Key (unlocked from your password via SRP)
└── Address Key (passphrase encrypted with User Key)
└── Share Key (passphrase encrypted with Address Key)
└── Node Key per folder/file (passphrase encrypted with parent Node Key)
└── Session Key (stored in ContentKeyPacket, encrypted with Node Key)
```
To decrypt any file, the client walks this chain top-down, unlocking each level with the one above.
### File blocks
Each file is split into blocks (≤ 4 MB). Every block is an **OpenPGP SEIPD v1** (tag 18) packet:
```
[ version: 1 byte ]
[ CFB-encrypted payload ]
├── prefix: block_size + 2 random bytes (CFB IV check)
├── Literal Data packet (tag 11) (actual file content)
└── MDC packet (0xd3 0x14 + SHA-1) (integrity check)
```
Decryption steps per block:
1. Verify the block SHA-256 hash against the API-provided value
2. Decrypt the full ciphertext with AES-CFB (zero IV, session key)
3. Verify the MDC SHA-1 (covers prefix + literal data + `0xd3 0x14`)
4. Strip the prefix and parse the Literal Data packet to get raw bytes
### Session key extraction
The `ContentKeyPacket` is a **PKESK** (tag 1) packet. The session key is ECDH-encrypted with the file's Node Key:
```
ECDH decrypt(Node Key, PKESK) → [ algo (1 byte) | key (16/24/32 bytes) | checksum (2 bytes) ]
```
The checksum is `sum(key_bytes) mod 65536`. Currently handled by `pgpy` + `cryptography` (see TODO).
## TODO
- [ ] Upload / POST files
- [ ] Filter the tree with a regex (hide matching paths)
- [ ] Remove `pgpy` dependency — implement ECDH session key extraction natively with `cryptography`
## License
GPL-3.0