Files
proton-sdk-rs/Cargo.toml
T
nvrl 325c326c49 feat(account): hold key passphrases as wiped secrets and report a locked account as KeysLocked
A wrong mailbox password surfaced as InvalidOperation("no active user key could
be unlocked"), which a caller could only recognise by its text. It is now
ProtonError::KeysLocked, and AccountClient::unlock() checks a secret with a
single core/v4/users request instead of loading every address.

Derived passphrases, the mailbox password and the cache encryption key were
plain Vec<u8>s, cloned freely and never wiped. They are Zeroizing now, and the
passphrase map is KeyPassphrases: wiped on drop, redacted in Debug, with
derive_secret() for a client that wants a key only an unlocked account can
reproduce. with_key_passphrases takes it.

Breaking: ProtonError gains a variant, key_passphrases() and
with_key_passphrases() change type, and derive_key_passphrase returns
Zeroizing<Vec<u8>>.
2026-10-07 02:24:51 +02:00

61 lines
2.2 KiB
TOML

[workspace]
resolver = "2"
members = ["crates/proton-sdk", "crates/proton-drive-rs"]
[workspace.package]
version = "0.8.0"
edition = "2024"
license = "MIT"
repository = "https://github.com/narrrl/proton-sdk-rs"
homepage = "https://github.com/narrrl/proton-sdk-rs"
rust-version = "1.96"
authors = ["Nils Pukropp <contact@narl.io>"]
keywords = ["proton", "drive", "openpgp", "encryption", "sdk"]
categories = ["api-bindings", "cryptography", "filesystem"]
# Dependencies build optimized even in dev/test. All of the CPU cost on the
# transfer paths — rPGP's AES/SHA, bcrypt, the S2K derivations — lives in these
# crates, and unoptimized rPGP is roughly two orders of magnitude slower, which
# made debug-profile upload benchmarks measure the compiler rather than the link
# (~2 MiB/s, CPU-bound, where release is network-bound). Workspace crates stay at
# `opt-level = 0` so incremental rebuilds and backtraces are unaffected.
[profile.dev.package."*"]
opt-level = 2
[workspace.dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "fs", "io-util", "time"] }
# `http2` is in reqwest's default set, which `default-features = false` drops:
# without it every concurrent block download pays its own TCP+TLS handshake
# instead of multiplexing over one connection.
reqwest = { version = "0.13", default-features = false, features = ["json", "rustls", "webpki-roots", "stream", "multipart", "gzip", "http2"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_repr = "0.1"
thiserror = "2"
async-trait = "0.1"
futures = "0.3"
lru = "0.18"
bytes = "1"
base64 = "0.23"
hex = "0.4"
hmac = "0.13"
sha1 = "0.11"
sha2 = "0.11"
aes-gcm = "0.11"
hkdf = "0.13"
zeroize = "1"
bcrypt = "0.19"
pgp = "0.21"
# Match the chrono line rPGP pulls in; used to stamp detached-signature subpackets.
chrono = "0.4"
tracing = "0.1"
num-bigint = "0.5"
num-traits = "0.2"
getrandom = "0.4"
# General-purpose RNG (HTTP retry jitter, etc.).
rand = "0.10"
# rPGP (pgp 0.20) still depends on rand 0.8 and passes its CryptoRng/Rng traits
# across its public API, so the RNG we hand to keygen/encryption must be rand 0.8.
# Kept under an alias alongside rand 0.10; crypto/ uses `rand_08`, the rest `rand`.
rand_08 = { package = "rand", version = "0.8" }