diff --git a/server/Cargo.lock b/server/Cargo.lock index 2e10b79..3e8ae9e 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -59,14 +59,21 @@ version = "0.1.0" name = "arbiter-proto" version = "0.1.0" dependencies = [ + "base64", "futures", - "hex", "kameo", + "miette", "prost", + "rand", + "rcgen", + "rstest", + "rustls-pki-types", + "thiserror", "tokio", "tonic", "tonic-prost", "tonic-prost-build", + "url", ] [[package]] @@ -88,6 +95,7 @@ dependencies = [ "kameo", "memsafe", "miette", + "pem", "rand", "rcgen", "restructed", @@ -109,6 +117,16 @@ dependencies = [ [[package]] name = "arbiter-useragent" version = "0.1.0" +dependencies = [ + "arbiter-proto", + "ed25519-dalek", + "kameo", + "smlang", + "tokio", + "tonic", + "tracing", + "x25519-dalek", +] [[package]] name = "argon2" @@ -859,6 +877,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -936,6 +963,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.32" @@ -1006,6 +1039,12 @@ version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "h2" version = "0.4.13" @@ -1055,12 +1094,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "http" version = "1.4.0" @@ -1195,6 +1228,87 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "id-arena" version = "2.3.0" @@ -1207,6 +1321,27 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -1342,6 +1477,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + [[package]] name = "lock_api" version = "0.4.14" @@ -1684,6 +1825,15 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1700,6 +1850,15 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1900,6 +2059,12 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "restructed" version = "0.2.2" @@ -1936,6 +2101,35 @@ dependencies = [ "thiserror", ] +[[package]] +name = "rstest" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", +] + +[[package]] +name = "rstest_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.115", + "unicode-ident", +] + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -2206,6 +2400,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "string_morph" version = "0.1.0" @@ -2419,6 +2619,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.49.0" @@ -2505,6 +2715,18 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + [[package]] name = "toml_parser" version = "1.0.8+spec-1.1.0" @@ -2747,6 +2969,24 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.21.0" @@ -3138,6 +3378,9 @@ name = "winnow" version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" @@ -3227,6 +3470,12 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + [[package]] name = "x25519-dalek" version = "2.0.1" @@ -3266,6 +3515,50 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.2" @@ -3286,6 +3579,39 @@ dependencies = [ "syn 2.0.115", ] +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.115", +] + [[package]] name = "zmij" version = "1.0.21" diff --git a/server/Cargo.toml b/server/Cargo.toml index d70e366..5e68948 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -23,3 +23,12 @@ async-trait = "0.1.89" futures = "0.3.31" tokio-stream = { version = "0.1.18", features = ["full"] } kameo = "0.19.2" +x25519-dalek = { version = "2.0.1", features = ["getrandom"] } +rstest = "0.26.1" +rustls-pki-types = "1.14.0" +rcgen = { version = "0.14.7", features = [ + "aws_lc_rs", + "pem", + "x509-parser", + "zeroize", +], default-features = false } diff --git a/server/crates/arbiter-proto/Cargo.toml b/server/crates/arbiter-proto/Cargo.toml index 24ee308..cffd431 100644 --- a/server/crates/arbiter-proto/Cargo.toml +++ b/server/crates/arbiter-proto/Cargo.toml @@ -9,12 +9,22 @@ license = "Apache-2.0" tonic.workspace = true tokio.workspace = true futures.workspace = true -hex = "0.4.3" tonic-prost = "0.14.3" prost = "0.14.3" kameo.workspace = true +url = "2.5.8" +miette.workspace = true +thiserror.workspace = true +rustls-pki-types.workspace = true +base64 = "0.22.1" + [build-dependencies] tonic-prost-build = "0.14.3" +[dev-dependencies] +rstest.workspace = true +rand.workspace = true +rcgen.workspace = true + diff --git a/server/crates/arbiter-proto/src/lib.rs b/server/crates/arbiter-proto/src/lib.rs index a738e8f..aabd7c4 100644 --- a/server/crates/arbiter-proto/src/lib.rs +++ b/server/crates/arbiter-proto/src/lib.rs @@ -1,3 +1,8 @@ +pub mod transport; +pub mod url; + +use base64::{Engine, prelude::BASE64_STANDARD}; + use crate::proto::auth::AuthChallenge; pub mod proto { @@ -8,9 +13,7 @@ pub mod proto { } } -pub mod transport; - -pub static BOOTSTRAP_TOKEN_PATH: &str = "bootstrap_token"; +pub static BOOTSTRAP_PATH: &str = "bootstrap_token"; pub fn home_path() -> Result { static ARBITER_HOME: &str = ".arbiter"; @@ -26,6 +29,6 @@ pub fn home_path() -> Result { } pub fn format_challenge(challenge: &AuthChallenge) -> Vec { - let concat_form = format!("{}:{}", challenge.nonce, hex::encode(&challenge.pubkey)); + let concat_form = format!("{}:{}", challenge.nonce, BASE64_STANDARD.encode(&challenge.pubkey)); concat_form.into_bytes().to_vec() } diff --git a/server/crates/arbiter-proto/src/url.rs b/server/crates/arbiter-proto/src/url.rs new file mode 100644 index 0000000..adfd45d --- /dev/null +++ b/server/crates/arbiter-proto/src/url.rs @@ -0,0 +1,128 @@ +use std::fmt::Display; + +use base64::{Engine as _, prelude::BASE64_URL_SAFE}; +use rustls_pki_types::CertificateDer; + +const ARBITER_URL_SCHEME: &str = "arbiter"; +const CERT_QUERY_KEY: &str = "cert"; +const BOOTSTRAP_TOKEN_QUERY_KEY: &str = "bootstrap_token"; + +pub struct ArbiterUrl { + pub host: String, + pub port: u16, + pub ca_cert: CertificateDer<'static>, + pub bootstrap_token: Option, +} + +impl Display for ArbiterUrl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut base = format!( + "{ARBITER_URL_SCHEME}://{}:{}?{CERT_QUERY_KEY}={}", + self.host, + self.port, + BASE64_URL_SAFE.encode(self.ca_cert.to_vec()) + ); + if let Some(token) = &self.bootstrap_token { + base.push_str(&format!("&{BOOTSTRAP_TOKEN_QUERY_KEY}={}", token)); + } + f.write_str(&base) + } +} + +#[derive(Debug, thiserror::Error, miette::Diagnostic)] +pub enum Error { + #[error("Invalid URL scheme, expected '{ARBITER_URL_SCHEME}://'")] + #[diagnostic( + code(arbiter::url::invalid_scheme), + help("The URL must start with '{ARBITER_URL_SCHEME}://'") + )] + InvalidScheme, + #[error("Missing host in URL")] + #[diagnostic( + code(arbiter::url::missing_host), + help("The URL must include a host, e.g., '{ARBITER_URL_SCHEME}://127.0.0.1:'") + )] + MissingHost, + #[error("Missing port in URL")] + #[diagnostic( + code(arbiter::url::missing_port), + help("The URL must include a port, e.g., '{ARBITER_URL_SCHEME}://127.0.0.1:1234'") + )] + MissingPort, + #[error("Missing 'cert' query parameter in URL")] + #[diagnostic( + code(arbiter::url::missing_cert), + help("The URL must include a 'cert' query parameter") + )] + MissingCert, + #[error("Invalid base64 in 'cert' query parameter: {0}")] + #[diagnostic(code(arbiter::url::invalid_cert_base64))] + InvalidCertBase64(#[from] base64::DecodeError), +} + +impl<'a> TryFrom<&'a str> for ArbiterUrl { + type Error = Error; + + fn try_from(value: &'a str) -> Result { + let url = url::Url::parse(value).map_err(|_| Error::InvalidScheme)?; + + if url.scheme() != ARBITER_URL_SCHEME { + return Err(Error::InvalidScheme); + } + + let host = url.host_str().ok_or(Error::MissingHost)?.to_string(); + let port = url.port().ok_or(Error::MissingPort)?; + let cert_str = url + .query_pairs() + .find(|(k, _)| k == CERT_QUERY_KEY) + .ok_or(Error::MissingCert)? + .1; + + let cert = BASE64_URL_SAFE.decode(cert_str.as_ref())?; + let cert = CertificateDer::from_slice(&cert).into_owned(); + + let bootstrap_token = url + .query_pairs() + .find(|(k, _)| k == BOOTSTRAP_TOKEN_QUERY_KEY) + .map(|(_, v)| v.to_string()); + + Ok(ArbiterUrl { + host, + port, + ca_cert: cert, + bootstrap_token, + }) + } +} + +#[cfg(test)] +mod tests { + use rcgen::generate_simple_self_signed; + use rstest::rstest; + + use super::*; + + #[rstest] + + fn test_parsing_correctness( + #[values("127.0.0.1", "localhost", "192.168.1.1", "some.domain.com")] host: &str, + + #[values(None, Some("token123".to_string()))] bootstrap_token: Option, + ) { + let cert = generate_simple_self_signed(&["Arbiter CA".into()]).unwrap(); + let cert = cert.cert.der(); + + let url = ArbiterUrl { + host: host.to_string(), + port: 1234, + ca_cert: cert.clone().into_owned(), + bootstrap_token, + }; + let url_str = url.to_string(); + let parsed_url = ArbiterUrl::try_from(url_str.as_str()).unwrap(); + assert_eq!(url.host, parsed_url.host); + assert_eq!(url.port, parsed_url.port); + assert_eq!(url.ca_cert.to_vec(), parsed_url.ca_cert.to_vec()); + assert_eq!(url.bootstrap_token, parsed_url.bootstrap_token); + } +} diff --git a/server/crates/arbiter-server/Cargo.toml b/server/crates/arbiter-server/Cargo.toml index edae115..4830b67 100644 --- a/server/crates/arbiter-server/Cargo.toml +++ b/server/crates/arbiter-server/Cargo.toml @@ -18,6 +18,7 @@ arbiter-proto.path = "../arbiter-proto" tracing.workspace = true tracing-subscriber = { version = "0.3", features = ["env-filter"] } tonic.workspace = true +tonic.features = ["tls-aws-lc"] tokio.workspace = true rustls.workspace = true smlang.workspace = true @@ -30,21 +31,17 @@ futures.workspace = true tokio-stream.workspace = true dashmap = "6.1.0" rand.workspace = true -rcgen = { version = "0.14.7", features = [ - "aws_lc_rs", - "pem", - "x509-parser", - "zeroize", -], default-features = false } +rcgen.workspace = true chrono.workspace = true memsafe = "0.4.0" zeroize = { version = "1.8.2", features = ["std", "simd"] } kameo.workspace = true -x25519-dalek = { version = "2.0.1", features = ["getrandom"] } +x25519-dalek.workspace = true chacha20poly1305 = { version = "0.10.1", features = ["std"] } argon2 = { version = "0.5.3", features = ["zeroize"] } restructed = "0.2.2" strum = { version = "0.27.2", features = ["derive"] } +pem = "3.0.6" [dev-dependencies] insta = "1.46.3" diff --git a/server/crates/arbiter-server/migrations/2026-02-14-171124-0000_init/up.sql b/server/crates/arbiter-server/migrations/2026-02-14-171124-0000_init/up.sql index f673de7..b2b497e 100644 --- a/server/crates/arbiter-server/migrations/2026-02-14-171124-0000_init/up.sql +++ b/server/crates/arbiter-server/migrations/2026-02-14-171124-0000_init/up.sql @@ -24,14 +24,24 @@ create unique index if not exists uniq_nonce_per_root_key on aead_encrypted ( associated_root_key_id ); +create table if not exists tls_history ( + id INTEGER not null PRIMARY KEY, + cert text not null, + cert_key text not null, -- PEM Encoded private key + ca_cert text not null, + ca_key text not null, -- PEM Encoded private key + created_at integer not null default(unixepoch ('now')) +) STRICT; + -- This is a singleton create table if not exists arbiter_settings ( id INTEGER not null PRIMARY KEY CHECK (id = 1), -- singleton row, id must be 1 root_key_id integer references root_key_history (id) on delete RESTRICT, -- if null, means wasn't bootstrapped yet - cert_key blob not null, - cert blob not null + tls_id integer references tls_history (id) on delete RESTRICT ) STRICT; +insert into arbiter_settings (id) values (1) on conflict do nothing; -- ensure singleton row exists + create table if not exists useragent_client ( id integer not null primary key, nonce integer not null default(1), -- used for auth challenge diff --git a/server/crates/arbiter-server/src/actors/bootstrap.rs b/server/crates/arbiter-server/src/actors/bootstrap.rs index 7119084..366a91a 100644 --- a/server/crates/arbiter-server/src/actors/bootstrap.rs +++ b/server/crates/arbiter-server/src/actors/bootstrap.rs @@ -1,28 +1,31 @@ -use arbiter_proto::{BOOTSTRAP_TOKEN_PATH, home_path}; +use arbiter_proto::{BOOTSTRAP_PATH, home_path}; use diesel::QueryDsl; use diesel_async::RunQueryDsl; use kameo::{Actor, messages}; use miette::Diagnostic; -use rand::{RngExt, distr::StandardUniform, make_rng, rngs::StdRng}; +use rand::{ + RngExt, + distr::{Alphanumeric}, + make_rng, + rngs::StdRng, +}; use thiserror::Error; -use tracing::info; use crate::db::{self, DatabasePool, schema}; - const TOKEN_LENGTH: usize = 64; pub async fn generate_token() -> Result { let rng: StdRng = make_rng(); - let token: String = rng - .sample_iter::(StandardUniform) - .take(TOKEN_LENGTH) - .fold(Default::default(), |mut accum, char| { + let token: String = rng.sample_iter(Alphanumeric).take(TOKEN_LENGTH).fold( + Default::default(), + |mut accum, char| { accum += char.to_string().as_str(); accum - }); + }, + ); - tokio::fs::write(home_path()?.join(BOOTSTRAP_TOKEN_PATH), token.as_str()).await?; + tokio::fs::write(home_path()?.join(BOOTSTRAP_PATH), token.as_str()).await?; Ok(token) } @@ -58,10 +61,9 @@ impl Bootstrapper { drop(conn); + let token = if row_count == 0 { let token = generate_token().await?; - info!(%token, "Generated bootstrap token"); - tokio::fs::write(home_path()?.join(BOOTSTRAP_TOKEN_PATH), token.as_str()).await?; Some(token) } else { None diff --git a/server/crates/arbiter-server/src/actors/keyholder/mod.rs b/server/crates/arbiter-server/src/actors/keyholder/mod.rs index aab15e3..6008a7e 100644 --- a/server/crates/arbiter-server/src/actors/keyholder/mod.rs +++ b/server/crates/arbiter-server/src/actors/keyholder/mod.rs @@ -345,30 +345,15 @@ impl KeyHolder { #[cfg(test)] mod tests { use diesel::SelectableHelper; - use diesel::dsl::insert_into; + use diesel_async::RunQueryDsl; use memsafe::MemSafe; - use crate::db::{self, models::ArbiterSetting}; + use crate::db::{self}; use super::*; - async fn seed_settings(pool: &db::DatabasePool) { - let mut conn = pool.get().await.unwrap(); - insert_into(schema::arbiter_settings::table) - .values(&ArbiterSetting { - id: 1, - root_key_id: None, - cert_key: vec![], - cert: vec![], - }) - .execute(&mut conn) - .await - .unwrap(); - } - async fn bootstrapped_actor(db: &db::DatabasePool) -> KeyHolder { - seed_settings(db).await; let mut actor = KeyHolder::new(db.clone()).await.unwrap(); let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); actor.bootstrap(seal_key).await.unwrap(); diff --git a/server/crates/arbiter-server/src/context/mod.rs b/server/crates/arbiter-server/src/context/mod.rs index 01cd2ed..9867cf1 100644 --- a/server/crates/arbiter-server/src/context/mod.rs +++ b/server/crates/arbiter-server/src/context/mod.rs @@ -1,14 +1,12 @@ use std::sync::Arc; -use diesel::OptionalExtension as _; -use diesel_async::RunQueryDsl as _; use miette::Diagnostic; use thiserror::Error; use crate::{ actors::GlobalActors, - context::tls::{TlsDataRaw, TlsManager}, - db::{self, models::ArbiterSetting, schema::arbiter_settings}, + context::tls::TlsManager, + db::{self}, }; pub mod tls; @@ -29,7 +27,7 @@ pub enum InitError { #[error("TLS initialization failed: {0}")] #[diagnostic(code(arbiter_server::init::tls_init))] - Tls(#[from] tls::TlsInitError), + Tls(#[from] tls::InitError), #[error("Actor spawn failed: {0}")] #[diagnostic(code(arbiter_server::init::actor_spawn))] @@ -57,54 +55,11 @@ impl std::ops::Deref for ServerContext { } impl ServerContext { - async fn load_tls( - db: &mut db::DatabaseConnection, - settings: Option<&ArbiterSetting>, - ) -> Result { - match &settings { - Some(settings) => { - let tls_data_raw = TlsDataRaw { - cert: settings.cert.clone(), - key: settings.cert_key.clone(), - }; - - Ok(TlsManager::new(Some(tls_data_raw)).await?) - } - None => { - let tls = TlsManager::new(None).await?; - let tls_data_raw = tls.bytes(); - - diesel::insert_into(arbiter_settings::table) - .values(&ArbiterSetting { - id: 1, - root_key_id: None, - cert_key: tls_data_raw.key, - cert: tls_data_raw.cert, - }) - .execute(db) - .await?; - - Ok(tls) - } - } - } - pub async fn new(db: db::DatabasePool) -> Result { - let mut conn = db.get().await?; - - let settings = arbiter_settings::table - .first::(&mut conn) - .await - .optional()?; - - let tls = Self::load_tls(&mut conn, settings.as_ref()).await?; - - drop(conn); - Ok(Self(Arc::new(_ServerContextInner { actors: GlobalActors::spawn(db.clone()).await?, + tls: TlsManager::new(db.clone()).await?, db, - tls, }))) } } diff --git a/server/crates/arbiter-server/src/context/tls.rs b/server/crates/arbiter-server/src/context/tls.rs index 424a580..4a27764 100644 --- a/server/crates/arbiter-server/src/context/tls.rs +++ b/server/crates/arbiter-server/src/context/tls.rs @@ -1,12 +1,36 @@ use std::string::FromUtf8Error; +use diesel::{ExpressionMethods as _, QueryDsl, SelectableHelper as _}; +use diesel_async::{AsyncConnection, RunQueryDsl}; use miette::Diagnostic; -use rcgen::{Certificate, KeyPair}; -use rustls::pki_types::CertificateDer; +use pem::Pem; +use rcgen::{ + BasicConstraints, Certificate, CertificateParams, CertifiedIssuer, DistinguishedName, DnType, + IsCa, Issuer, KeyPair, KeyUsagePurpose, +}; +use rustls::pki_types::{pem::PemObject}; use thiserror::Error; +use tonic::transport::CertificateDer; + +use crate::db::{ + self, + models::{NewTlsHistory, TlsHistory}, + schema::{ + arbiter_settings, + tls_history::{self}, + }, +}; + +const ENCODE_CONFIG: pem::EncodeConfig = { + let line_ending = match cfg!(target_family = "windows") { + true => pem::LineEnding::CRLF, + false => pem::LineEnding::LF, + }; + pem::EncodeConfig::new().set_line_ending(line_ending) +}; #[derive(Error, Debug, Diagnostic)] -pub enum TlsInitError { +pub enum InitError { #[error("Key generation error during TLS initialization: {0}")] #[diagnostic(code(arbiter_server::tls_init::key_generation))] KeyGeneration(#[from] rcgen::Error), @@ -18,68 +42,211 @@ pub enum TlsInitError { #[error("Key deserialization error: {0}")] #[diagnostic(code(arbiter_server::tls_init::key_deserialization))] KeyDeserializationError(rcgen::Error), + + #[error("Database error during TLS initialization: {0}")] + #[diagnostic(code(arbiter_server::tls_init::database_error))] + DatabaseError(#[from] diesel::result::Error), + + #[error("Pem deserialization error during TLS initialization: {0}")] + #[diagnostic(code(arbiter_server::tls_init::pem_deserialization))] + PemDeserializationError(#[from] rustls::pki_types::pem::Error), + + #[error("Database pool acquire error during TLS initialization: {0}")] + #[diagnostic(code(arbiter_server::tls_init::database_pool_acquire))] + DatabasePoolAcquire(#[from] db::PoolError), } -pub struct TlsData { - pub cert: CertificateDer<'static>, - pub keypair: KeyPair, +pub type PemCert = String; + +pub fn encode_cert_to_pem(cert: &CertificateDer) -> PemCert { + pem::encode_config( + &Pem::new("CERTIFICATE", cert.to_vec()), + ENCODE_CONFIG, + ) } -pub struct TlsDataRaw { - pub cert: Vec, - pub key: Vec, +#[allow(unused)] +struct SerializedTls { + cert_pem: PemCert, + cert_key_pem: String, } -impl TlsDataRaw { - pub fn serialize(cert: &TlsData) -> Self { - Self { - cert: cert.cert.as_ref().to_vec(), - key: cert.keypair.serialize_pem().as_bytes().to_vec(), - } + +struct TlsCa { + issuer: Issuer<'static, KeyPair>, + cert: CertificateDer<'static>, +} + +impl TlsCa { + fn generate() -> Result { + let keypair = KeyPair::generate()?; + let mut params = CertificateParams::new(["Arbiter Instance CA".into()])?; + params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); + params.key_usages = vec![ + KeyUsagePurpose::KeyCertSign, + KeyUsagePurpose::CrlSign, + KeyUsagePurpose::DigitalSignature, + ]; + + let mut dn = DistinguishedName::new(); + dn.push(DnType::CommonName, "Arbiter Instance CA"); + params.distinguished_name = dn; + let certified_issuer = CertifiedIssuer::self_signed(params, keypair)?; + + let cert_key_pem = certified_issuer.key().serialize_pem(); + + let issuer = Issuer::from_ca_cert_pem( + &certified_issuer.pem(), + KeyPair::from_pem(cert_key_pem.as_ref()).unwrap(), + ) + .unwrap(); + + Ok(Self { + issuer, + cert: certified_issuer.der().clone(), + }) + } + fn generate_leaf(&self) -> Result { + let cert_key = KeyPair::generate()?; + let mut params = CertificateParams::new(["Arbiter Instance Leaf".into()])?; + params.is_ca = IsCa::NoCa; + params.key_usages = vec![ + KeyUsagePurpose::DigitalSignature, + KeyUsagePurpose::KeyEncipherment, + ]; + + let mut dn = DistinguishedName::new(); + dn.push(DnType::CommonName, "Arbiter Instance Leaf"); + params.distinguished_name = dn; + + let new_cert = params.signed_by(&cert_key, &self.issuer)?; + + Ok(TlsCert { + cert: new_cert, + cert_key, + }) } - pub fn deserialize(&self) -> Result { - let cert = CertificateDer::from_slice(&self.cert).into_owned(); + #[allow(unused)] + fn serialize(&self) -> Result { + let cert_key_pem = self.issuer.key().serialize_pem(); + Ok(SerializedTls { + cert_pem: encode_cert_to_pem(&self.cert), + cert_key_pem, + }) + } - let key = String::from_utf8(self.key.clone()).map_err(TlsInitError::KeyInvalidFormat)?; - - let keypair = KeyPair::from_pem(&key).map_err(TlsInitError::KeyDeserializationError)?; - - Ok(TlsData { cert, keypair }) + #[allow(unused)] + fn try_deserialize(cert_pem: &str, cert_key_pem: &str) -> Result { + let keypair = + KeyPair::from_pem(cert_key_pem).map_err(InitError::KeyDeserializationError)?; + let issuer = Issuer::from_ca_cert_pem(cert_pem, keypair)?; + Ok(Self { + issuer, + cert: CertificateDer::from_pem_slice(cert_pem.as_bytes())?, + }) } } -fn generate_cert(key: &KeyPair) -> Result { - let params = - rcgen::CertificateParams::new(vec!["arbiter.local".to_string(), "localhost".to_string()])?; - - params.self_signed(key) +struct TlsCert { + cert: Certificate, + cert_key: KeyPair, } // TODO: Implement cert rotation pub struct TlsManager { - data: TlsData, + cert: CertificateDer<'static>, + keypair: KeyPair, + ca_cert: CertificateDer<'static>, + _db: db::DatabasePool, } impl TlsManager { - pub async fn new(data: Option) -> Result { - match data { - Some(raw) => { - let tls_data = raw.deserialize()?; - Ok(Self { data: tls_data }) - } - None => { - let keypair = KeyPair::generate()?; - let cert = generate_cert(&keypair)?; - let tls_data = TlsData { - cert: cert.der().clone(), - keypair, + pub async fn generate_new(db: &db::DatabasePool) -> Result { + let ca = TlsCa::generate()?; + let new_cert = ca.generate_leaf()?; + + { + let mut conn = db.get().await?; + conn.transaction(|conn| { + Box::pin(async { + let new_tls_history = NewTlsHistory { + cert: new_cert.cert.pem(), + cert_key: new_cert.cert_key.serialize_pem(), + ca_cert: encode_cert_to_pem(&ca.cert), + ca_key: ca.issuer.key().serialize_pem(), + }; + + let inserted_tls_history: i32 = diesel::insert_into(tls_history::table) + .values(&new_tls_history) + .returning(tls_history::id) + .get_result(conn) + .await?; + + diesel::update(arbiter_settings::table) + .set(arbiter_settings::tls_id.eq(inserted_tls_history)) + .execute(conn) + .await?; + + Result::<_, diesel::result::Error>::Ok(()) + }) + }) + .await?; + } + + Ok(Self { + cert: new_cert.cert.der().clone(), + keypair: new_cert.cert_key, + ca_cert: ca.cert, + _db: db.clone(), + }) + } + + pub async fn new(db: db::DatabasePool) -> Result { + let cert_data: Option = { + let mut conn = db.get().await?; + arbiter_settings::table + .left_join(tls_history::table) + .select(Option::::as_select()) + .first(&mut conn) + .await? + }; + + match cert_data { + Some(data) => { + let try_load = || -> Result<_, Box> { + let keypair = KeyPair::from_pem(&data.cert_key)?; + let cert = CertificateDer::from_pem_slice(data.cert.as_bytes())?; + let ca_cert = CertificateDer::from_pem_slice(data.ca_cert.as_bytes())?; + Ok(Self { + cert, + keypair, + ca_cert, + _db: db.clone(), + }) }; - Ok(Self { data: tls_data }) + match try_load() { + Ok(manager) => Ok(manager), + Err(e) => { + eprintln!("Failed to load existing TLS certs: {e}. Generating new ones."); + Self::generate_new(&db).await + } + } } + None => Self::generate_new(&db).await, } } - pub fn bytes(&self) -> TlsDataRaw { - TlsDataRaw::serialize(&self.data) + pub fn cert(&self) -> &CertificateDer<'static> { + &self.cert + } + pub fn ca_cert(&self) -> &CertificateDer<'static> { + &self.ca_cert + } + + pub fn cert_pem(&self) -> PemCert { + encode_cert_to_pem(&self.cert) + } + pub fn key_pem(&self) -> String { + self.keypair.serialize_pem() } } diff --git a/server/crates/arbiter-server/src/db/mod.rs b/server/crates/arbiter-server/src/db/mod.rs index a15398e..69d7539 100644 --- a/server/crates/arbiter-server/src/db/mod.rs +++ b/server/crates/arbiter-server/src/db/mod.rs @@ -24,23 +24,23 @@ const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations"); #[derive(Error, Diagnostic, Debug)] pub enum DatabaseSetupError { #[error("Failed to determine home directory")] - #[diagnostic(code(arbiter::db::home_dir_error))] + #[diagnostic(code(arbiter::db::home_dir))] HomeDir(std::io::Error), #[error(transparent)] - #[diagnostic(code(arbiter::db::connection_error))] + #[diagnostic(code(arbiter::db::connection))] Connection(diesel::ConnectionError), #[error(transparent)] - #[diagnostic(code(arbiter::db::concurrency_error))] + #[diagnostic(code(arbiter::db::concurrency))] ConcurrencySetup(diesel::result::Error), #[error(transparent)] - #[diagnostic(code(arbiter::db::migration_error))] + #[diagnostic(code(arbiter::db::migration))] Migration(Box), #[error(transparent)] - #[diagnostic(code(arbiter::db::pool_error))] + #[diagnostic(code(arbiter::db::pool))] Pool(#[from] PoolInitError), } @@ -91,12 +91,12 @@ fn initialize_database(url: &str) -> Result<(), DatabaseSetupError> { #[tracing::instrument(level = "info")] pub async fn create_pool(url: Option<&str>) -> Result { - let database_url = url.map(String::from).unwrap_or(format!( - "{}?mode=rwc", - (database_path()? + let database_url = url.map(String::from).unwrap_or( + database_path()? .to_str() - .expect("database path is not valid UTF-8")) - )); + .expect("database path is not valid UTF-8") + .to_string(), + ); initialize_database(&database_url)?; diff --git a/server/crates/arbiter-server/src/db/models.rs b/server/crates/arbiter-server/src/db/models.rs index a3a35dd..453e071 100644 --- a/server/crates/arbiter-server/src/db/models.rs +++ b/server/crates/arbiter-server/src/db/models.rs @@ -1,7 +1,7 @@ #![allow(unused)] #![allow(clippy::all)] -use crate::db::schema::{self, aead_encrypted, arbiter_settings, root_key_history}; +use crate::db::schema::{self, aead_encrypted, arbiter_settings, root_key_history, tls_history}; use diesel::{prelude::*, sqlite::Sqlite}; use restructed::Models; @@ -46,13 +46,29 @@ pub struct RootKeyHistory { pub salt: Vec, } -#[derive(Queryable, Debug, Insertable)] +#[derive(Models, Queryable, Debug, Insertable, Selectable)] +#[diesel(table_name = tls_history, check_for_backend(Sqlite))] +#[view( + NewTlsHistory, + derive(Insertable), + omit(id, created_at), + attributes_with = "deriveless" +)] +pub struct TlsHistory { + pub id: i32, + pub cert: String, + pub cert_key: String, // PEM Encoded private key + pub ca_cert: String, // PEM Encoded certificate for cert signing + pub ca_key: String, // PEM Encoded public key for cert signing + pub created_at: i32, +} + +#[derive(Queryable, Debug, Insertable, Selectable)] #[diesel(table_name = arbiter_settings, check_for_backend(Sqlite))] -pub struct ArbiterSetting { +pub struct ArbiterSettings { pub id: i32, pub root_key_id: Option, // references root_key_history.id - pub cert_key: Vec, - pub cert: Vec, + pub tls_id: Option, // references tls_history.id } #[derive(Queryable, Debug)] diff --git a/server/crates/arbiter-server/src/db/schema.rs b/server/crates/arbiter-server/src/db/schema.rs index 7ba8193..28cb4dc 100644 --- a/server/crates/arbiter-server/src/db/schema.rs +++ b/server/crates/arbiter-server/src/db/schema.rs @@ -16,8 +16,7 @@ diesel::table! { arbiter_settings (id) { id -> Integer, root_key_id -> Nullable, - cert_key -> Binary, - cert -> Binary, + tls_id -> Nullable, } } @@ -43,6 +42,17 @@ diesel::table! { } } +diesel::table! { + tls_history (id) { + id -> Integer, + cert -> Text, + cert_key -> Text, + ca_cert -> Text, + ca_key -> Text, + created_at -> Integer, + } +} + diesel::table! { useragent_client (id) { id -> Integer, @@ -55,11 +65,13 @@ diesel::table! { diesel::joinable!(aead_encrypted -> root_key_history (associated_root_key_id)); diesel::joinable!(arbiter_settings -> root_key_history (root_key_id)); +diesel::joinable!(arbiter_settings -> tls_history (tls_id)); diesel::allow_tables_to_appear_in_same_query!( aead_encrypted, arbiter_settings, program_client, root_key_history, + tls_history, useragent_client, ); diff --git a/server/crates/arbiter-server/src/main.rs b/server/crates/arbiter-server/src/main.rs index 5e3a3b9..1f81c7b 100644 --- a/server/crates/arbiter-server/src/main.rs +++ b/server/crates/arbiter-server/src/main.rs @@ -1,7 +1,13 @@ -use arbiter_proto::proto::arbiter_service_server::ArbiterServiceServer; -use arbiter_server::{Server, context::ServerContext, db}; +use std::net::SocketAddr; + +use arbiter_proto::{proto::arbiter_service_server::ArbiterServiceServer, url::ArbiterUrl}; +use arbiter_server::{Server, actors::bootstrap::GetToken, context::ServerContext, db}; +use miette::miette; +use tonic::transport::{Identity, ServerTlsConfig}; use tracing::info; +const PORT: u16 = 50051; + #[tokio::main] async fn main() -> miette::Result<()> { tracing_subscriber::fmt() @@ -13,18 +19,31 @@ async fn main() -> miette::Result<()> { info!("Starting arbiter server"); - info!("Initializing database"); let db = db::create_pool(None).await?; info!("Database ready"); - info!("Initializing server context"); let context = ServerContext::new(db).await?; - info!("Server context ready"); - let addr = "[::1]:50051".parse().expect("valid address"); + let addr: SocketAddr = format!("127.0.0.1:{PORT}").parse().expect("valid address"); info!(%addr, "Starting gRPC server"); + let url = ArbiterUrl { + host: addr.ip().to_string(), + port: addr.port(), + ca_cert: context.tls.ca_cert().clone().into_owned(), + bootstrap_token: context.actors.bootstrapper.ask(GetToken).await.unwrap(), + }; + + info!(%url, "Server URL"); + + let tls = ServerTlsConfig::new().identity(Identity::from_pem( + context.tls.cert_pem(), + context.tls.key_pem(), + )); + tonic::transport::Server::builder() + .tls_config(tls) + .map_err(|err| miette!("Faild to setup TLS: {err}"))? .add_service(ArbiterServiceServer::new(Server::new(context))) .serve(addr) .await diff --git a/server/crates/arbiter-server/tests/common/mod.rs b/server/crates/arbiter-server/tests/common/mod.rs index 7269edf..ce01412 100644 --- a/server/crates/arbiter-server/tests/common/mod.rs +++ b/server/crates/arbiter-server/tests/common/mod.rs @@ -1,28 +1,13 @@ use arbiter_server::{ actors::keyholder::KeyHolder, - db::{self, models::ArbiterSetting, schema}, + db::{self, schema}, }; -use diesel::{QueryDsl, insert_into}; +use diesel::QueryDsl; use diesel_async::RunQueryDsl; use memsafe::MemSafe; -pub async fn seed_settings(pool: &db::DatabasePool) { - let mut conn = pool.get().await.unwrap(); - insert_into(schema::arbiter_settings::table) - .values(&ArbiterSetting { - id: 1, - root_key_id: None, - cert_key: vec![], - cert: vec![], - }) - .execute(&mut conn) - .await - .unwrap(); -} - #[allow(dead_code)] pub async fn bootstrapped_keyholder(db: &db::DatabasePool) -> KeyHolder { - seed_settings(db).await; let mut actor = KeyHolder::new(db.clone()).await.unwrap(); actor .bootstrap(MemSafe::new(b"test-seal-key".to_vec()).unwrap()) diff --git a/server/crates/arbiter-server/tests/keyholder/lifecycle.rs b/server/crates/arbiter-server/tests/keyholder/lifecycle.rs index 7c633fc..2cf1f55 100644 --- a/server/crates/arbiter-server/tests/keyholder/lifecycle.rs +++ b/server/crates/arbiter-server/tests/keyholder/lifecycle.rs @@ -12,7 +12,6 @@ use crate::common; #[test_log::test] async fn test_bootstrap() { let db = db::create_test_pool().await; - common::seed_settings(&db).await; let mut actor = KeyHolder::new(db.clone()).await.unwrap(); let seal_key = MemSafe::new(b"test-seal-key".to_vec()).unwrap(); @@ -53,7 +52,6 @@ async fn test_bootstrap_rejects_double() { #[test_log::test] async fn test_create_new_before_bootstrap_fails() { let db = db::create_test_pool().await; - common::seed_settings(&db).await; let mut actor = KeyHolder::new(db).await.unwrap(); let err = actor @@ -67,7 +65,6 @@ async fn test_create_new_before_bootstrap_fails() { #[test_log::test] async fn test_decrypt_before_bootstrap_fails() { let db = db::create_test_pool().await; - common::seed_settings(&db).await; let mut actor = KeyHolder::new(db).await.unwrap(); let err = actor.decrypt(1).await.unwrap_err(); diff --git a/server/crates/arbiter-server/tests/user_agent/auth.rs b/server/crates/arbiter-server/tests/user_agent/auth.rs index 98d921d..c79d616 100644 --- a/server/crates/arbiter-server/tests/user_agent/auth.rs +++ b/server/crates/arbiter-server/tests/user_agent/auth.rs @@ -20,7 +20,6 @@ use kameo::actor::Spawn; #[test_log::test] pub async fn test_bootstrap_token_auth() { let db =db::create_test_pool().await; - crate::common::seed_settings(&db).await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); let token = actors.bootstrapper.ask(GetToken).await.unwrap().unwrap(); @@ -67,7 +66,6 @@ pub async fn test_bootstrap_token_auth() { #[test_log::test] pub async fn test_bootstrap_invalid_token_auth() { let db = db::create_test_pool().await; - crate::common::seed_settings(&db).await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); let user_agent = @@ -110,7 +108,6 @@ pub async fn test_bootstrap_invalid_token_auth() { #[test_log::test] pub async fn test_challenge_auth() { let db = db::create_test_pool().await; - crate::common::seed_settings(&db).await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); let user_agent = diff --git a/server/crates/arbiter-server/tests/user_agent/unseal.rs b/server/crates/arbiter-server/tests/user_agent/unseal.rs index 7120935..9a7c85f 100644 --- a/server/crates/arbiter-server/tests/user_agent/unseal.rs +++ b/server/crates/arbiter-server/tests/user_agent/unseal.rs @@ -23,7 +23,6 @@ async fn setup_authenticated_user_agent( seal_key: &[u8], ) -> (arbiter_server::db::DatabasePool, ActorRef) { let db = db::create_test_pool().await; - crate::common::seed_settings(&db).await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); actors @@ -167,7 +166,6 @@ pub async fn test_unseal_corrupted_ciphertext() { #[test_log::test] pub async fn test_unseal_start_without_auth_fails() { let db = db::create_test_pool().await; - crate::common::seed_settings(&db).await; let actors = GlobalActors::spawn(db.clone()).await.unwrap(); let user_agent = diff --git a/server/crates/arbiter-useragent/Cargo.toml b/server/crates/arbiter-useragent/Cargo.toml index 5fea561..16eb12d 100644 --- a/server/crates/arbiter-useragent/Cargo.toml +++ b/server/crates/arbiter-useragent/Cargo.toml @@ -5,3 +5,11 @@ edition = "2024" license = "Apache-2.0" [dependencies] +arbiter-proto.path = "../arbiter-proto" +kameo.workspace = true +tokio = {workspace = true, features = ["net"]} +tonic.workspace = true +tracing.workspace = true +ed25519-dalek.workspace = true +smlang.workspace = true +x25519-dalek.workspace = true \ No newline at end of file diff --git a/server/crates/arbiter-useragent/src/lib.rs b/server/crates/arbiter-useragent/src/lib.rs index b93cf3f..e69de29 100644 --- a/server/crates/arbiter-useragent/src/lib.rs +++ b/server/crates/arbiter-useragent/src/lib.rs @@ -1,14 +0,0 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -}