updated readme + example

This commit is contained in:
nvrl committed 2026-06-25 17:26:02 +02:00
1 parent 5d4e20d4c2
commit b0eeb8cf0f
2 files changed
+190 -27

No files matched your search

+56 -27
View File
@@ -2,7 +2,8 @@
A pure-Rust reimplementation of the [Proton Drive SDK](https://github.com/ProtonDriveApps/sdk).
No dependency on the official NativeAOT core — it talks to the Proton Drive API
and does the OpenPGP crypto directly.
and does the OpenPGP crypto directly. The canonical reference is the upstream
**C#** implementation; behavior is matched to it where the protocol is unclear.
> Unofficial, third-party project. Not affiliated with or endorsed by Proton.
> If you build on it, follow the operational requirements in the upstream SDK
@@ -10,18 +11,37 @@ and does the OpenPGP crypto directly.
## Status
**Milestone 1 — session + read operations.** Implemented:
**Milestone 2 — authenticated read/write.** Implemented:
- Core: typed IDs, API response envelope, config, reqwest HTTP client with
`x-pm-uid` / `x-pm-appversion` / bearer auth and transparent 401 token refresh.
- Session: `resume` from existing tokens, `end`.
- Account: user keys, address keys, and Proton's bcrypt key-passphrase
derivation (the mailbox password is required even for read — see below).
- Drive read: `get_my_files_folder`, `get_node`, `enumerate_folder_children`,
with share and node (link) decryption and recursive parent-key resolution.
- **Core / HTTP**: typed IDs, API response envelope, config, reqwest client with
`x-pm-uid` / `x-pm-appversion` / bearer auth, transparent 401 refresh,
retry/backoff (408/429/502/503/504 + transient transport, `Retry-After` +
jitter), and per-request telemetry.
- **Session**: SRP-6a password login (`begin`), TOTP 2FA
(`apply_second_factor_code`), scope refresh, `resume` from tokens, `end`.
- **Account**: user keys, address keys, bcrypt key-passphrase derivation,
public-key resolution for authorship verification (`core/v4/keys/all`).
- **Drive read**: `get_my_files_folder`, `get_node`, `enumerate_folder_children`,
`enumerate_nodes`, `enumerate_trash`, with share/node decryption and recursive
parent-key resolution.
- **Download**: `download_file` / `download_file_to` (streaming), thumbnails,
content-manifest integrity + non-fatal signature verification.
- **Upload**: `upload_file` / `upload_file_from` (streaming `Read`), new
revisions, caller-supplied thumbnails, and AEAD blocks (SEIPDv2 / AES-256-GCM)
alongside legacy SEIPDv1.
- **Folder / node ops**: create, rename, trash, restore, delete, empty-trash,
and same-volume `move_node`.
- **Events**: `main_volume_id`, `latest_event_id`, `enumerate_events`.
- **Photos**: `ProtonPhotosClient` — timeline, photo get/download, upload.
- **Caching**: pluggable entity cache (`with_entity_cache`); secrets stay
in-memory.
- **Telemetry**: pluggable `Telemetry` observer (`with_telemetry`).
Not yet implemented: SRP password login (`begin`), uploads/downloads, sharing,
events, trash, photos, signature-verification enforcement, persistent caching.
Not yet implemented: cross-volume move (needs `NewShareID` + re-signing),
sharing, signature-verification *enforcement* (it is non-fatal metadata).
Crypto paths have offline round-trip tests; the write/move paths still need live
validation against a real account.
## Workspace
@@ -30,7 +50,7 @@ events, trash, photos, signature-verification enforcement, persistent caching.
| `crates/proton-sdk` | Core account/session/crypto (`Proton.Sdk` analogue) |
| `crates/proton-drive-sdk` | High-level Drive client (`Proton.Drive.Sdk` analogue) |
OpenPGP is provided by the [`pgp`](https://crates.io/crates/pgp) crate (rPGP).
OpenPGP is provided by the [`pgp`](https://crates.io/crates/pgp) crate (rPGP 0.16).
## The mailbox-password requirement
@@ -43,28 +63,26 @@ mailbox password + key salt --bcrypt--> key passphrase
-> decrypt share/node passphrase -> unlock node key -> decrypt names
```
`ProtonApiSession::resume` takes the tokens; `ProtonDriveClient::new` takes the
mailbox password.
`ProtonApiSession::begin` / `resume` handle the tokens; `ProtonDriveClient::new`
takes the mailbox password.
## Usage
SRP login (with 2FA), then read the my-files root:
```rust,no_run
use proton_sdk::config::ProtonClientConfiguration;
use proton_sdk::session::{PasswordMode, ProtonApiSession, ResumeParameters};
use proton_sdk::session::ProtonApiSession;
use proton_drive_sdk::ProtonDriveClient;
# async fn run() -> proton_sdk::error::Result<()> {
let config = ProtonClientConfiguration::new("external-drive-myapp@0.1.0-alpha");
let session = ProtonApiSession::resume(config, ResumeParameters {
session_id: "uid".into(),
username: "user@proton.me".into(),
user_id: "user-id".into(),
access_token: "access".into(),
refresh_token: "refresh".into(),
scopes: vec![],
is_waiting_for_second_factor_code: false,
password_mode: PasswordMode::Single,
})?;
let mut session =
ProtonApiSession::begin(config, "user@proton.me", b"mailbox-password").await?;
if session.is_waiting_for_second_factor() {
session.apply_second_factor_code("123456").await?;
}
let drive = ProtonDriveClient::new(&session, b"mailbox-password".to_vec());
let root = drive.get_my_files_folder().await?;
@@ -75,11 +93,22 @@ for child in drive.enumerate_folder_children(&root.uid).await? {
# }
```
Already have tokens? Use `ProtonApiSession::resume(config, ResumeParameters {..})`
instead of `begin`.
A live smoke test lives in `crates/proton-drive-sdk/examples/live_login.rs`
(reads `username` / `password` from a repo-root `.env`, TOTP from
`PROTON_TOTP_SECRET`):
```bash
PROTON_TOTP_SECRET=... cargo run -p proton-drive-sdk --example live_login
```
## Build & test
```bash
cargo build
cargo test
cargo build # 0 warnings / 0 errors
cargo test # offline crypto + derivation round-trip tests
```
## License