The Rust SDK removed the low-level SecretStoreWrapper.putSecret/getSecret
API between 26.03.x and 26.04.x — it was an escape hatch we were using
to pin arbitrary bytes into a Matrix 4S slot. The SDK maintainers never
contracted that primitive; locking it down lets their recovery code
evolve without worrying about third-party storage.
This commit replaces that dependency with a self-contained design we
own end-to-end, so future SDK moves no longer break our backup flow.
### Design
- Slot: `com.sulkta.wallet.seed.v1` in Matrix account data.
Our namespace, not a Matrix-spec 4S slot — we are NOT impersonating
Matrix secret storage, we are holding our own opaque blob.
- Envelope (JSON): version tag, algorithm tag, random 12-byte IV, GCM
output (ciphertext || tag), AAD = slot name. AES-256-GCM via stock
javax.crypto. AAD binds a blob to its slot so a blob can't be lifted
from one namespace and successfully opened in another.
- Key: derived from the user's existing Matrix recovery key via
HKDF-SHA256 with info label "sulkta.wallet.seed.v1". The info label
guarantees we never produce the same key bytes Matrix uses for its
own crypto — same secret, different domain.
- I/O: client.setAccountData(key, json) + client.accountData(key)
via the SDK; the homeserver only ever sees the opaque encrypted blob.
### Files
- api/walletsecretstorage/WalletSecretStorage.kt — new interface
- impl/walletsecretstorage/WalletSecretEnvelope.kt — AES-GCM envelope
(with unit tests: round-trip, wrong key, tampered ct, tampered iv,
wrong AAD, wrong version, malformed JSON)
- impl/walletsecretstorage/RecoveryKeyDerivation.kt — base58 decode
+ parity check + HKDF-SHA256 (with unit tests: determinism,
whitespace tolerance, distinct info labels → distinct keys)
- impl/walletsecretstorage/MatrixAccountDataWalletSecretStorage.kt —
WalletSecretStorage impl wrapping Client account data
- test/walletsecretstorage/FakeWalletSecretStorage.kt — in-memory fake
- api/MatrixClient.kt: old .secretStorage → .walletSecretStorage
- features/wallet/.../WalletBackupServiceImpl.kt — rewired to use the
new interface; hasBackupWithoutKey now goes through the same path
instead of manually poking the raw Matrix HTTP API.
- DELETED: api/secretstorage/SecretStorage.kt, SecretStore.kt, impl/
secretstorage/RustSecretStorage.kt — the old SDK-dependent path.
### Backward compat note
Users who backed up a wallet seed on the OLD SDK have a blob in Matrix's
4S at `com.sulkta.cardano.wallet_seed`. This branch cannot read those.
Since the prior integration was only tested internally, acceptable
today — anyone with an old backup re-enters their mnemonic.