Move Ephemeral registry to submodule (#136)

This commit is contained in:
Jazz Turner-Baggs 2026-06-17 08:27:39 -07:00 committed by GitHub
parent 960d0bc119
commit e163980715
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 114 additions and 114 deletions

View File

@ -1,114 +1,2 @@
use std::{
collections::HashMap,
fmt::Debug,
sync::{Arc, Mutex},
};
use crypto::Ed25519VerifyingKey;
use libchat::{
AccountDirectory, DeviceSet, IdentityProvider, RegistrationService, SignedDeviceBundle,
verify_bundle,
};
pub mod ephemeral;
pub mod http;
/// A Contact Registry used for Tests.
/// This implementation stores bundle bytes and then returns them when
/// retrieved.
///
/// Like the real `keypackage-registry`, one object serves both roles: a
/// keypackage store ([`RegistrationService`]) keyed by `device_id`, and an
/// account → device directory ([`AccountDirectory`]) keyed by the hex account key.
#[derive(Clone, Default)]
pub struct EphemeralRegistry {
key_packages: Arc<Mutex<HashMap<String, Vec<u8>>>>,
installations: Arc<Mutex<HashMap<String, SignedDeviceBundle>>>,
}
impl EphemeralRegistry {
pub fn new() -> Self {
Self::default()
}
}
impl Debug for EphemeralRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let registry = self.key_packages.lock().unwrap();
let truncated: Vec<(&String, String)> = registry
.iter()
.map(|(k, v)| {
let hex = if v.len() <= 8 {
hex::encode(v)
} else {
format!(
"{}..{}",
hex::encode(&v[..4]),
hex::encode(&v[v.len() - 4..])
)
};
(k, hex)
})
.collect();
f.debug_struct("EphemeralRegistry")
.field("registry", &truncated)
.finish()
}
}
impl RegistrationService for EphemeralRegistry {
type Error = String;
fn register(
&mut self,
identity: &dyn IdentityProvider,
key_bundle: Vec<u8>,
) -> Result<(), <Self as RegistrationService>::Error> {
self.key_packages
.lock()
.unwrap()
.insert(identity.id().to_string(), key_bundle);
Ok(())
}
fn retrieve(
&self,
device_id: &str,
) -> Result<Option<Vec<u8>>, <Self as RegistrationService>::Error> {
Ok(self.key_packages.lock().unwrap().get(device_id).cloned())
}
}
/// Account → device directory, verifying each bundle on `fetch` exactly as the
/// HTTP client does so callers exercise the same trust path without a server.
impl AccountDirectory for EphemeralRegistry {
type Error = String;
fn publish(
&mut self,
bundle: &SignedDeviceBundle,
) -> Result<(), <Self as AccountDirectory>::Error> {
self.installations
.lock()
.unwrap()
.insert(hex::encode(bundle.account_pub.as_ref()), bundle.clone());
Ok(())
}
fn fetch(
&self,
account: &Ed25519VerifyingKey,
) -> Result<Option<DeviceSet>, <Self as AccountDirectory>::Error> {
let Some(bundle) = self
.installations
.lock()
.unwrap()
.get(&hex::encode(account.as_ref()))
.cloned()
else {
return Ok(None);
};
verify_bundle(account, &bundle)
.map(Some)
.map_err(|e| e.to_string())
}
}

View File

@ -0,0 +1,112 @@
use std::{
collections::HashMap,
fmt::Debug,
sync::{Arc, Mutex},
};
use crypto::Ed25519VerifyingKey;
use libchat::{
AccountDirectory, DeviceSet, IdentityProvider, RegistrationService, SignedDeviceBundle,
verify_bundle,
};
/// A Contact Registry used for Tests.
/// This implementation stores bundle bytes and then returns them when
/// retrieved.
///
/// Like the real `keypackage-registry`, one object serves both roles: a
/// keypackage store ([`RegistrationService`]) keyed by `device_id`, and an
/// account → device directory ([`AccountDirectory`]) keyed by the hex account key.
#[derive(Clone, Default)]
pub struct EphemeralRegistry {
key_packages: Arc<Mutex<HashMap<String, Vec<u8>>>>,
installations: Arc<Mutex<HashMap<String, SignedDeviceBundle>>>,
}
impl EphemeralRegistry {
pub fn new() -> Self {
Self::default()
}
}
impl Debug for EphemeralRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let registry = self.key_packages.lock().unwrap();
let truncated: Vec<(&String, String)> = registry
.iter()
.map(|(k, v)| {
let hex = if v.len() <= 8 {
hex::encode(v)
} else {
format!(
"{}..{}",
hex::encode(&v[..4]),
hex::encode(&v[v.len() - 4..])
)
};
(k, hex)
})
.collect();
f.debug_struct("EphemeralRegistry")
.field("registry", &truncated)
.finish()
}
}
impl RegistrationService for EphemeralRegistry {
type Error = String;
fn register(
&mut self,
identity: &dyn IdentityProvider,
key_bundle: Vec<u8>,
) -> Result<(), <Self as RegistrationService>::Error> {
self.key_packages
.lock()
.unwrap()
.insert(identity.id().to_string(), key_bundle);
Ok(())
}
fn retrieve(
&self,
device_id: &str,
) -> Result<Option<Vec<u8>>, <Self as RegistrationService>::Error> {
Ok(self.key_packages.lock().unwrap().get(device_id).cloned())
}
}
/// Account → device directory, verifying each bundle on `fetch` exactly as the
/// HTTP client does so callers exercise the same trust path without a server.
impl AccountDirectory for EphemeralRegistry {
type Error = String;
fn publish(
&mut self,
bundle: &SignedDeviceBundle,
) -> Result<(), <Self as AccountDirectory>::Error> {
self.installations
.lock()
.unwrap()
.insert(hex::encode(bundle.account_pub.as_ref()), bundle.clone());
Ok(())
}
fn fetch(
&self,
account: &Ed25519VerifyingKey,
) -> Result<Option<DeviceSet>, <Self as AccountDirectory>::Error> {
let Some(bundle) = self
.installations
.lock()
.unwrap()
.get(&hex::encode(account.as_ref()))
.cloned()
else {
return Ok(None);
};
verify_bundle(account, &bundle)
.map(Some)
.map_err(|e| e.to_string())
}
}

View File

@ -3,7 +3,7 @@ mod delivery;
mod storage;
mod wakeup;
pub use contact_registry::EphemeralRegistry;
pub use contact_registry::ephemeral::EphemeralRegistry;
pub use contact_registry::http::{HttpRegistry, HttpRegistryError};
pub use delivery::*;
pub use storage::*;