app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit 426fba9fdf84d95105d6c055e9c55cbf34358aa0
parent 50c99cefdb531830b99fdee3aa374d7e83be804a
Author: triesap <triesap@radroots.dev>
Date:   Mon, 19 Jan 2026 01:45:44 +0000

app-core: add device key material provider

- implement device key material provider with registry storage
- return crypto undefined for non-wasm key material
- expose provider from crypto module exports
- add provider id and non-wasm error tests

Diffstat:
Mcrates/core/src/crypto/mod.rs | 2++
Acrates/core/src/crypto/provider.rs | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+), 0 deletions(-)

diff --git a/crates/core/src/crypto/mod.rs b/crates/core/src/crypto/mod.rs @@ -5,6 +5,7 @@ pub mod random; pub mod keys; pub mod kdf; pub mod registry; +pub mod provider; pub use error::{RadrootsClientCryptoError, RadrootsClientCryptoErrorMessage}; pub use types::{ @@ -20,6 +21,7 @@ pub use types::{ RadrootsClientLegacyKeyConfig, RadrootsClientWebCryptoService, }; +pub use provider::RadrootsClientDeviceKeyMaterialProvider; pub use envelope::{crypto_envelope_decode, crypto_envelope_encode}; pub use keys::crypto_key_id_create; pub use kdf::{crypto_kdf_iterations_default, crypto_kdf_salt_create}; diff --git a/crates/core/src/crypto/provider.rs b/crates/core/src/crypto/provider.rs @@ -0,0 +1,63 @@ +use async_trait::async_trait; + +#[cfg(target_arch = "wasm32")] +use crate::crypto::random::fill_random; +#[cfg(target_arch = "wasm32")] +use crate::crypto::registry::{ + crypto_registry_get_device_material, + crypto_registry_set_device_material, +}; + +use super::{RadrootsClientCryptoError, RadrootsClientKeyMaterialProvider}; + +const DEVICE_PROVIDER_ID: &str = "device"; +#[cfg(target_arch = "wasm32")] +const DEVICE_MATERIAL_BYTES: usize = 32; + +pub struct RadrootsClientDeviceKeyMaterialProvider; + +#[async_trait(?Send)] +impl RadrootsClientKeyMaterialProvider for RadrootsClientDeviceKeyMaterialProvider { + async fn get_key_material(&self) -> Result<Vec<u8>, RadrootsClientCryptoError> { + #[cfg(not(target_arch = "wasm32"))] + { + return Err(RadrootsClientCryptoError::CryptoUndefined); + } + #[cfg(target_arch = "wasm32")] + { + if let Some(existing) = crypto_registry_get_device_material().await? { + return Ok(existing); + } + let mut material = vec![0u8; DEVICE_MATERIAL_BYTES]; + fill_random(&mut material)?; + crypto_registry_set_device_material(&material).await?; + Ok(material) + } + } + + async fn get_provider_id(&self) -> Result<String, RadrootsClientCryptoError> { + Ok(String::from(DEVICE_PROVIDER_ID)) + } +} + +#[cfg(test)] +mod tests { + use super::RadrootsClientDeviceKeyMaterialProvider; + use crate::crypto::{RadrootsClientCryptoError, RadrootsClientKeyMaterialProvider}; + + #[test] + fn provider_id_is_device() { + let provider = RadrootsClientDeviceKeyMaterialProvider; + let id = futures::executor::block_on(provider.get_provider_id()) + .expect("provider id"); + assert_eq!(id, "device"); + } + + #[test] + fn non_wasm_material_errors() { + let provider = RadrootsClientDeviceKeyMaterialProvider; + let err = futures::executor::block_on(provider.get_key_material()) + .expect_err("missing crypto"); + assert_eq!(err, RadrootsClientCryptoError::CryptoUndefined); + } +}