Files
qbz/crates/qbz-secrets/Cargo.toml
T
Afonso Jorge Ramos 7814d4d2d5 fix(secrets): keep the unit tests off the OS keyring
Asking for real keyring backends made the four round-trip tests write a genuine
login-Keychain item, and macOS binds an item's ACL to the exact binary that
created it. So every run after the first, with any source edit in between, was
a new binary against an existing item, and the OS blocked all four tests on an
authorization dialog that a headless run can never answer. Reproduced: four
tests hanging past 60s, where a fresh keychain passes in 0.39s.

The comment claiming the sandboxed tempdir guaranteed a fresh state was the
thing that hid it. try_open_keyring ignores storage_dir entirely and looks the
entry up by service name alone, so the tempdir bought nothing.

Backend::kdf_only pins them to the path they were always documented as taking.
What they assert, wrap/unwrap round-trips and tamper detection, is identical on
either backend, so nothing is lost. survives_a_restart.rs still exercises the
real backend, which is where it belongs: persistence across processes is what
that test is about.

Also names Linux's backend here instead of inheriting it. qbz-credentials
enables async-secret-service and Cargo unifies features, which holds for every
shipping build but makes this crate's correctness depend on a sibling: built on
its own, cargo tree showed keyring with no secret-service, i.e. the same silent
mock this PR exists to remove. Verified: secret-service v4.0.0 on Linux,
security-framework on macOS, and still zero keyring on iOS and Android.
2026-08-12 03:21:26 +02:00

65 lines
2.5 KiB
TOML

[package]
name = "qbz-secrets"
version.workspace = true
edition.workspace = true
license.workspace = true
description = "Device-bound secret wrapping for QBZ (offline cache keys, tokens)"
[dependencies]
# AES-256-GCM for the at-rest wrap/unwrap
aes-gcm = "0.10"
# KDF for the fallback path when OS keyring isn't available
hkdf = "0.12"
sha2 = "0.10"
# RNG for nonces + install UUID generation
rand = "0.9"
# UUID for the persistent install identifier (salt component for fallback)
uuid = { version = "1", features = ["v4", "serde"] }
# Serde for the on-disk wrapper blob envelope
serde = { workspace = true }
# Base64 for keyring-roundtripped master key (secret-service expects a UTF-8 string)
base64 = { workspace = true }
# Errors
thiserror = { workspace = true }
log = { workspace = true }
[dev-dependencies]
tempfile = "3"
# OS keyring backends: secret-service (Linux), Keychain (macOS), Credential
# Manager (Windows). **Desktop only, deliberately** — see the target gate below.
#
# Every backend is opt-in. keyring 3.x ships no `default` feature contents at
# all, and each platform arm reads `not(feature = "…-native")` → `pub use mock
# as default`. That mock is an in-memory store whose writes *succeed*, so
# depending on it does not fail loudly: it hands back a master key that never
# outlives the process, and every blob wrapped in one run is undecryptable in
# the next. `Backend::new` still reports `BackendKind::Keyring`, so the logs
# say nothing is wrong.
#
# Linux names its backend here too, rather than relying on qbz-credentials
# enabling `async-secret-service` and Cargo unifying features across the graph.
# That does hold for any build containing qbz-credentials, which today is every
# shipping one, but it makes this crate's correctness depend on a sibling: build
# qbz-secrets on its own and `cargo tree` shows keyring with no secret-service,
# which is the silent mock described above. A crate meant to be reusable should
# not be one dependency-graph change away from storing secrets in a hashmap.
#
# `async-secret-service` and not `sync-secret-service`: keyring's lib.rs has a
# `compile_error!` for enabling both halves of a store, and qbz-credentials
# already picked the async one. `crypto-rust` is not optional, secret-service
# does not build without a crypto backend selected.
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
keyring = { version = "3", features = [
"apple-native",
"windows-native",
"async-secret-service",
"crypto-rust",
] }