[Fix] Stability

This commit is contained in:
Alex Emmet 2026-08-28 13:20:20 +02:00
commit cf9607b15e
No known key found for this signature in database
5 changed files with 55 additions and 54 deletions

View file

@ -31,4 +31,3 @@ uuid = { version = "1.24.0", features = ["v4", "v7"] }
thiserror = "2.0.19"
serde = { version = "1.0.229", features = ["derive"] }
serde_json = "1.0.151"
zeroize = "1.9"

View file

@ -54,6 +54,14 @@ pub async fn register_complete_iota(id: IotaId, public_key: PublicKeyBundle) ->
Ok(())
}
pub async fn change_iota_key(id: IotaId, key: PublicKeyBundle) -> Result<()> {
sqlx::query("UPDATE iotas SET public_key = ? WHERE id = ?")
.bind(key.try_as_bytes()?)
.bind(id.0)
.execute(&pool().await?)
.await?;
Ok(())
}
pub async fn delete_iota(id: IotaId) -> Result<()> {
sqlx::query("DELETE FROM iotas WHERE id = ?")
.bind(id.0)

View file

@ -1,7 +1,8 @@
use crate::error::{IdentityError, Result};
use mtp::crypto::{Keyring, PublicKeyBundle};
use mtp::files::{
FileError, load_keyring, load_public_key_bundle, save_keyring, save_public_key_bundle,
FileError, load_keyring_raw, load_public_key_bundle, save_keyring_raw,
save_public_key_bundle,
};
use std::{
fs,
@ -22,25 +23,20 @@ pub struct OmegaIdentity {
static PUBLIC_BUNDLE_TEMP_COUNTER: AtomicU64 = AtomicU64::new(0);
impl OmegaIdentity {
pub fn load_or_create(passphrase: &[u8]) -> Result<Self> {
Self::load_or_create_at(
Path::new(KEYRING_PATH),
Path::new(PUBLIC_KEY_PATH),
passphrase,
)
pub fn load_or_create() -> Result<Self> {
Self::load_or_create_at(Path::new(KEYRING_PATH), Path::new(PUBLIC_KEY_PATH))
}
pub(crate) fn load_or_create_at(
keyring_path: impl AsRef<Path>,
public_key_path: impl AsRef<Path>,
passphrase: &[u8],
) -> Result<Self> {
let keyring_path = keyring_path.as_ref();
let public_key_path = public_key_path.as_ref();
let keyring = match load_keyring(keyring_path, passphrase) {
let keyring = match load_keyring_raw(keyring_path) {
Ok(keyring) => keyring,
Err(FileError::Io(error)) if error.kind() == std::io::ErrorKind::NotFound => {
return Self::create_at(keyring_path, public_key_path, passphrase);
return Self::create_at(keyring_path, public_key_path);
}
Err(error) => {
return Err(IdentityError::Storage {
@ -72,9 +68,9 @@ impl OmegaIdentity {
Ok(identity)
}
fn create_at(keyring_path: &Path, public_key_path: &Path, passphrase: &[u8]) -> Result<Self> {
fn create_at(keyring_path: &Path, public_key_path: &Path) -> Result<Self> {
let keyring = Keyring::generate();
save_keyring(&keyring, keyring_path, passphrase).map_err(|error| {
save_keyring_raw(&keyring, keyring_path).map_err(|error| {
IdentityError::Storage {
path: keyring_path.to_path_buf(),
source: error,
@ -188,9 +184,7 @@ mod tests {
let directory = test_directory();
let keyring_path = directory.join("omega.mk");
let public_key_path = directory.join("omega.mpkb");
let passphrase = b"test-passphrase";
let first = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase)
let first = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path)
.expect("create identity");
let first_bundle = first.public_key_bundle().try_as_bytes().expect("bundle");
@ -199,9 +193,8 @@ mod tests {
assert_eq!(&keyring_bytes[..4], b"MTMK");
assert_eq!(&bundle_bytes[..4], b"MPKB");
let restarted =
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase)
.expect("reload identity");
let restarted = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path)
.expect("reload identity");
assert_eq!(
restarted
.public_key_bundle()
@ -227,8 +220,7 @@ mod tests {
let public_key_path = directory.join("omega.mpkb");
fs::write(&keyring_path, b"not-a-keyring").expect("write invalid keyring");
let result =
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, b"test-passphrase");
let result = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path);
assert!(result.is_err());
assert!(!public_key_path.exists());
@ -240,16 +232,13 @@ mod tests {
let directory = test_directory();
let keyring_path = directory.join("omega.mk");
let public_key_path = directory.join("omega.mpkb");
let passphrase = b"test-passphrase";
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase)
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path)
.expect("create identity");
let original_keyring = fs::read(&keyring_path).expect("read keyring");
fs::remove_file(&public_key_path).expect("remove bundle");
let repaired =
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase)
.expect("repair bundle");
let repaired = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path)
.expect("repair bundle");
assert_eq!(
fs::read(&keyring_path).expect("read keyring"),
@ -271,16 +260,14 @@ mod tests {
let directory = test_directory();
let keyring_path = directory.join("omega.mk");
let public_key_path = directory.join("omega.mpkb");
let passphrase = b"test-passphrase";
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase)
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path)
.expect("create identity");
let other_keyring = Keyring::generate();
save_public_key_bundle(&other_keyring.public_key_bundle(), &public_key_path)
.expect("save mismatched bundle");
assert!(matches!(
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase),
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path),
Err(crate::OmegaError::Identity(
IdentityError::PublicBundleMismatch { .. }
))
@ -290,24 +277,28 @@ mod tests {
}
#[test]
fn wrong_passphrase_does_not_replace_existing_keyring() {
fn existing_raw_keyring_is_reloaded_without_a_passphrase() {
let directory = test_directory();
let keyring_path = directory.join("omega.mk");
let public_key_path = directory.join("omega.mpkb");
let passphrase = b"test-passphrase";
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, passphrase)
let first = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path)
.expect("create identity");
let original_keyring = fs::read(&keyring_path).expect("read keyring");
assert!(
OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path, b"wrong-passphrase")
.is_err()
);
let reloaded = OmegaIdentity::load_or_create_at(&keyring_path, &public_key_path)
.expect("reload identity");
assert_eq!(
fs::read(&keyring_path).expect("read keyring"),
original_keyring
);
assert_eq!(
reloaded
.public_key_bundle()
.try_as_bytes()
.expect("bundle"),
first.public_key_bundle().try_as_bytes().expect("bundle")
);
fs::remove_dir_all(directory).expect("remove test directory");
}

View file

@ -25,7 +25,6 @@ use std::env;
use std::path::Path;
use std::time::Duration;
use tokio::time::interval;
use zeroize::Zeroizing;
#[tokio::main]
async fn main() {
@ -38,18 +37,6 @@ async fn main() {
log_in!("Incoming messages");
log_out!("Outgoing messages");
let identity_secret = match env::var("OMEGA_IDENTITY_SECRET") {
Ok(secret) if !secret.is_empty() => secret,
Ok(_) => {
log!("[FATAL] OMEGA_IDENTITY_SECRET must not be empty");
return;
}
Err(error) => {
log!("[FATAL] Unable to load OMEGA_IDENTITY_SECRET: {}", error);
return;
}
};
let identity_secret = Zeroizing::new(identity_secret);
let config = match OmegaConfig::from_env() {
Ok(config) => config,
Err(error) => {
@ -61,14 +48,13 @@ async fn main() {
log!("[FATAL] Omega rate-limit configuration was initialized more than once");
return;
}
let identity = match identity::OmegaIdentity::load_or_create(identity_secret.as_bytes()) {
let identity = match identity::OmegaIdentity::load_or_create() {
Ok(identity) => identity,
Err(error) => {
log!("[FATAL] Omega identity initialization failed: {}", error);
return;
}
};
drop(identity_secret);
let state = OmegaState::new(identity, config);
log!("Started");

View file

@ -764,8 +764,9 @@ pub async fn start(port: u16, state: Arc<OmegaState>) -> Result<(), Box<dyn std:
#[cfg(test)]
mod tests {
use super::{DispatchClass, OmikronConnection};
use super::{DispatchClass, OmikronConnection, parse_bind_address};
use mtp::codec::{CommunicationType, CommunicationValue};
use std::net::{IpAddr, Ipv4Addr};
#[test]
fn relay_dispatch_is_not_on_the_ordered_state_lane() {
@ -776,6 +777,14 @@ mod tests {
);
}
#[test]
fn parses_configured_bind_address() {
assert_eq!(
parse_bind_address(Some("10.200.2.0")),
Ok(IpAddr::V4(Ipv4Addr::new(10, 200, 2, 0)))
);
}
#[test]
fn presence_lifecycle_dispatch_is_ordered() {
let value = CommunicationValue::new(CommunicationType::UserConnected);
@ -806,4 +815,12 @@ mod tests {
&CommunicationValue::new(CommunicationType::GetUserData).with_id(1)
));
}
#[test]
fn defaults_bind_address_to_all_interfaces() {
assert_eq!(
parse_bind_address(None),
Ok(IpAddr::V4(Ipv4Addr::UNSPECIFIED))
);
}
}