Skip to main content

Module keystore

Module keystore 

Source
Expand description

Overridable, per-platform secure storage for a node key.

Where a node key is kept at rest:

Every store keeps the key’s private part only, in one write: the ML-DSA-87 seed, and in pq_hybrid the RSA-PSS key as PKCS #1, at most 2,431 bytes, within Credential Manager’s 2,560-byte limit on one secret. The public keys are derived again on load. A key kept by macula-rust 0.7.0, in the key-file form, no longer loads; create the identity again.

This module is a small KeyStore trait plus KeyringStore, a default implementation backed by the keyring crate, which selects the actual native secure store per target automatically — Keychain (Security.framework) on macOS and iOS, Secret Service (D-Bus) on Linux, Credential Manager on Windows, and the Android Keystore (via JNI) on Android. Nothing in this crate branches on target platform itself; keyring’s own Cargo.toml does that selection via per-target optional dependencies (see keyring = 4.2.0’s manifest), and the backend that ends up linked is what actually runs.

KeyStore itself is deliberately not tied to keyring at all — a caller with a different secure-storage requirement (a hardware security module, a different vault) can implement the trait directly and hand it to NodeKey::save_to_keystore/ load_from_keystore — “overridable per target platform” is a property of the trait boundary, not something wired into this crate’s own logic.

§Android setup, required once per app, not something this crate can do for you

android-native-keyring-store (the backend keyring links in for Android, confirmed via its own Cargo.toml: jni + ndk-context) needs the embedding app to hand it a JNI Context once at startup, because Android’s Keystore is a Java API with no NDK surface — there is no way for Rust code alone to reach it. That crate ships its own JNI export for exactly this (confirmed in its README, not assumed): once this crate is linked into an Android .so, that export is present automatically, and the Kotlin side calls it once, e.g. from MainActivity.onCreate:

package io.crates.keyring
class Keyring {
    companion object {
        init { System.loadLibrary("your_actual_library_name") }
        external fun initializeNdkContext(context: Context)
    }
}
// in onCreate:
Keyring.initializeNdkContext(this.applicationContext)

No custom UniFFI foreign-trait bridge is needed for Android or iOS — both have first-party keyring backends, confirmed by reading keyring 4.2.0’s own Cargo.toml target-cfg dependency blocks directly, not assumed from the crate’s name.

§A second Linux backend, LinuxKeyutilsStore

KeyringStore’s Linux path (keyring’s v1 API) unconditionally uses the D-Bus Secret Service, which requires a running provider (gnome-keyring, KWallet) — absent on headless boxes, containers, and this crate’s own dev sandbox (confirmed directly: a D-Bus session socket exists here, but no org.freedesktop.secrets provider is listening on it, so KeyringStore::new returns KeyStoreError::Backend with NoDefaultStore). LinuxKeyutilsStore uses the kernel’s own keyutils facility instead, always available on Linux — this is exactly what lets this module’s own tests verify a real save/load/delete round trip in this environment.

Structs§

KeyringStore
The default KeyStore: the platform-native secure store keyring selects for the current target (see this module’s own doc). On Windows a Credential Manager credential persisted on this machine only (CRED_PERSIST_LOCAL_MACHINE): the default, Enterprise persistence, roams with a domain user’s profile, and a node’s key does not leave the node (macula-rust#19). service and account are the same two strings every keyring consumer already uses to address one credential — pick values scoped to this application, e.g. ("com.example.myapp", "macula-identity"), since the underlying stores are shared OS-wide facilities, not sandboxed to this crate.
LinuxKeyutilsStore
A second, independently-selectable KeyStore backend — proof that the trait boundary is genuinely overridable, not merely declared to be: the kernel’s own keyutils facility, no D-Bus/secret-service daemon required. This is what KeyringStore’s underlying keyring dependency itself recommends for headless Linux (containers, CI, a sandboxed dev box with no gnome-keyring/kwalletd running) — see linux-keyutils-keyring-store’s own module doc, which states this outright.

Enums§

KeyStoreError

Traits§

KeyStore
Secure storage for one node key, as the bytes of its key file (the seed form: the ML-DSA-87 seed, and in pq_hybrid the RSA-PSS key too, a few KiB). Implement this directly for a backend other than KeyringStore (a hardware security module, a different vault) — this is the override point “per target platform” hangs off, not a platform enum this crate switches on internally.