refactor(server): actors reorganization & linter fixes
This commit is contained in:
@@ -10,10 +10,10 @@ pub mod proto {
|
|||||||
|
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
pub static BOOTSTRAP_TOKEN_PATH: &'static str = "bootstrap_token";
|
pub static BOOTSTRAP_TOKEN_PATH: &str = "bootstrap_token";
|
||||||
|
|
||||||
pub fn home_path() -> Result<std::path::PathBuf, std::io::Error> {
|
pub fn home_path() -> Result<std::path::PathBuf, std::io::Error> {
|
||||||
static ARBITER_HOME: &'static str = ".arbiter";
|
static ARBITER_HOME: &str = ".arbiter";
|
||||||
let home_dir = std::env::home_dir().ok_or(std::io::Error::new(
|
let home_dir = std::env::home_dir().ok_or(std::io::Error::new(
|
||||||
std::io::ErrorKind::PermissionDenied,
|
std::io::ErrorKind::PermissionDenied,
|
||||||
"can not get home directory",
|
"can not get home directory",
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod v1;
|
||||||
@@ -42,12 +42,12 @@ impl<'a> TryFrom<&'a [u8]> for Nonce {
|
|||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
let mut nonce = [0u8; NONCE_LENGTH];
|
let mut nonce = [0u8; NONCE_LENGTH];
|
||||||
nonce.copy_from_slice(&value);
|
nonce.copy_from_slice(value);
|
||||||
Ok(Self(nonce))
|
Ok(Self(nonce))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct KeyCell(pub(super) MemSafe<Key>);
|
pub struct KeyCell(pub MemSafe<Key>);
|
||||||
impl From<MemSafe<Key>> for KeyCell {
|
impl From<MemSafe<Key>> for KeyCell {
|
||||||
fn from(value: MemSafe<Key>) -> Self {
|
fn from(value: MemSafe<Key>) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
@@ -85,10 +85,6 @@ impl KeyCell {
|
|||||||
key.into()
|
key.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self) -> MemSafe<Key> {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encrypt_in_place(
|
pub fn encrypt_in_place(
|
||||||
&mut self,
|
&mut self,
|
||||||
nonce: &Nonce,
|
nonce: &Nonce,
|
||||||
@@ -130,7 +126,7 @@ impl KeyCell {
|
|||||||
|
|
||||||
|
|
||||||
let ciphertext = cipher.encrypt(
|
let ciphertext = cipher.encrypt(
|
||||||
&nonce,
|
nonce,
|
||||||
Payload {
|
Payload {
|
||||||
msg: plaintext.as_ref(),
|
msg: plaintext.as_ref(),
|
||||||
aad: associated_data,
|
aad: associated_data,
|
||||||
@@ -142,7 +138,7 @@ impl KeyCell {
|
|||||||
|
|
||||||
pub type Salt = [u8; ArgonSalt::RECOMMENDED_LENGTH];
|
pub type Salt = [u8; ArgonSalt::RECOMMENDED_LENGTH];
|
||||||
|
|
||||||
pub(super) fn generate_salt() -> Salt {
|
pub fn generate_salt() -> Salt {
|
||||||
let mut salt = Salt::default();
|
let mut salt = Salt::default();
|
||||||
let mut rng = StdRng::try_from_rng(&mut SysRng).unwrap();
|
let mut rng = StdRng::try_from_rng(&mut SysRng).unwrap();
|
||||||
rng.fill_bytes(&mut salt);
|
rng.fill_bytes(&mut salt);
|
||||||
@@ -151,7 +147,7 @@ pub(super) fn generate_salt() -> Salt {
|
|||||||
|
|
||||||
/// User password might be of different length, have not enough entropy, etc...
|
/// User password might be of different length, have not enough entropy, etc...
|
||||||
/// Derive a fixed-length key from the password using Argon2id, which is designed for password hashing and key derivation.
|
/// Derive a fixed-length key from the password using Argon2id, which is designed for password hashing and key derivation.
|
||||||
pub(super) fn derive_seal_key(mut password: MemSafe<Vec<u8>>, salt: &Salt) -> KeyCell {
|
pub fn derive_seal_key(mut password: MemSafe<Vec<u8>>, salt: &Salt) -> KeyCell {
|
||||||
let params = argon2::Params::new(262_144, 3, 4, None).unwrap();
|
let params = argon2::Params::new(262_144, 3, 4, None).unwrap();
|
||||||
let hasher = Argon2::new(Algorithm::Argon2id, argon2::Version::V0x13, params);
|
let hasher = Argon2::new(Algorithm::Argon2id, argon2::Version::V0x13, params);
|
||||||
let mut key = MemSafe::new(Key::default()).unwrap();
|
let mut key = MemSafe::new(Key::default()).unwrap();
|
||||||
@@ -9,15 +9,16 @@ use strum::{EnumDiscriminants, IntoDiscriminant};
|
|||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
actors::keyholder::v1::{KeyCell, Nonce},
|
|
||||||
db::{
|
db::{
|
||||||
self,
|
self,
|
||||||
models::{self, RootKeyHistory},
|
models::{self, RootKeyHistory},
|
||||||
schema::{self},
|
schema::{self},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use encryption::v1::{self, KeyCell, Nonce};
|
||||||
|
|
||||||
|
pub mod encryption;
|
||||||
|
|
||||||
pub mod v1;
|
|
||||||
|
|
||||||
#[derive(Default, EnumDiscriminants)]
|
#[derive(Default, EnumDiscriminants)]
|
||||||
#[strum_discriminants(derive(Reply), vis(pub))]
|
#[strum_discriminants(derive(Reply), vis(pub))]
|
||||||
@@ -10,7 +10,7 @@ use arbiter_proto::proto::{
|
|||||||
};
|
};
|
||||||
use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit};
|
use chacha20poly1305::{AeadInPlace, XChaCha20Poly1305, XNonce, aead::KeyInit};
|
||||||
use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl, dsl::update};
|
use diesel::{ExpressionMethods as _, OptionalExtension as _, QueryDsl, dsl::update};
|
||||||
use diesel_async::{AsyncConnection, RunQueryDsl};
|
use diesel_async::{RunQueryDsl};
|
||||||
use ed25519_dalek::VerifyingKey;
|
use ed25519_dalek::VerifyingKey;
|
||||||
use kameo::{Actor, actor::ActorRef, error::SendError, messages};
|
use kameo::{Actor, actor::ActorRef, error::SendError, messages};
|
||||||
use memsafe::MemSafe;
|
use memsafe::MemSafe;
|
||||||
@@ -25,7 +25,7 @@ use crate::{
|
|||||||
bootstrap::{Bootstrapper, ConsumeToken},
|
bootstrap::{Bootstrapper, ConsumeToken},
|
||||||
keyholder::{self, KeyHolder, TryUnseal},
|
keyholder::{self, KeyHolder, TryUnseal},
|
||||||
user_agent::state::{
|
user_agent::state::{
|
||||||
AuthRequestContext, ChallengeContext, DummyContext, UnsealContext, UserAgentEvents,
|
ChallengeContext, DummyContext, UnsealContext, UserAgentEvents,
|
||||||
UserAgentStateMachine, UserAgentStates,
|
UserAgentStateMachine, UserAgentStates,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -129,7 +129,7 @@ impl UserAgentActor {
|
|||||||
let nonce: Option<i32> = {
|
let nonce: Option<i32> = {
|
||||||
let mut db_conn = self.db.get().await.to_status()?;
|
let mut db_conn = self.db.get().await.to_status()?;
|
||||||
db_conn
|
db_conn
|
||||||
.transaction(|conn| {
|
.exclusive_transaction(|conn| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let current_nonce = schema::useragent_client::table
|
let current_nonce = schema::useragent_client::table
|
||||||
.filter(
|
.filter(
|
||||||
@@ -162,7 +162,7 @@ impl UserAgentActor {
|
|||||||
|
|
||||||
let challenge = auth::AuthChallenge {
|
let challenge = auth::AuthChallenge {
|
||||||
pubkey: pubkey_bytes,
|
pubkey: pubkey_bytes,
|
||||||
nonce: nonce,
|
nonce,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.transition(UserAgentEvents::SentChallenge(ChallengeContext {
|
self.transition(UserAgentEvents::SentChallenge(ChallengeContext {
|
||||||
@@ -237,7 +237,6 @@ impl UserAgentActor {
|
|||||||
let client_public_key = PublicKey::from(client_pubkey_bytes);
|
let client_public_key = PublicKey::from(client_pubkey_bytes);
|
||||||
|
|
||||||
self.transition(UserAgentEvents::UnsealRequest(UnsealContext {
|
self.transition(UserAgentEvents::UnsealRequest(UnsealContext {
|
||||||
server_public_key: public_key,
|
|
||||||
secret: Mutex::new(Some(secret)),
|
secret: Mutex::new(Some(secret)),
|
||||||
client_public_key,
|
client_public_key,
|
||||||
}))?;
|
}))?;
|
||||||
@@ -325,9 +324,9 @@ impl UserAgentActor {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(?err, "Failed to decrypt unseal key");
|
error!(?err, "Failed to decrypt unseal key");
|
||||||
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
self.transition(UserAgentEvents::ReceivedInvalidKey)?;
|
||||||
return Ok(unseal_response(UserAgentResponsePayload::UnsealResult(
|
Ok(unseal_response(UserAgentResponsePayload::UnsealResult(
|
||||||
UnsealResult::InvalidKey.into(),
|
UnsealResult::InvalidKey.into(),
|
||||||
)));
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,10 +341,7 @@ impl UserAgentActor {
|
|||||||
Status::invalid_argument("Failed to convert pubkey to VerifyingKey")
|
Status::invalid_argument("Failed to convert pubkey to VerifyingKey")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.transition(UserAgentEvents::AuthRequest(AuthRequestContext {
|
self.transition(UserAgentEvents::AuthRequest)?;
|
||||||
pubkey,
|
|
||||||
bootstrap_token: req.bootstrap_token.clone(),
|
|
||||||
}))?;
|
|
||||||
|
|
||||||
match req.bootstrap_token {
|
match req.bootstrap_token {
|
||||||
Some(token) => self.auth_with_bootstrap_token(pubkey, token).await,
|
Some(token) => self.auth_with_bootstrap_token(pubkey, token).await,
|
||||||
@@ -12,17 +12,9 @@ pub struct ChallengeContext {
|
|||||||
pub key: VerifyingKey,
|
pub key: VerifyingKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request context with deserialized public key for state machine.
|
|
||||||
// This intermediate struct is needed because the state machine branches depending on presence of bootstrap token,
|
|
||||||
// but we want to have the deserialized key in both branches.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct AuthRequestContext {
|
|
||||||
pub pubkey: VerifyingKey,
|
|
||||||
pub bootstrap_token: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UnsealContext {
|
pub struct UnsealContext {
|
||||||
pub server_public_key: PublicKey,
|
|
||||||
pub client_public_key: PublicKey,
|
pub client_public_key: PublicKey,
|
||||||
pub secret: Mutex<Option<EphemeralSecret>>,
|
pub secret: Mutex<Option<EphemeralSecret>>,
|
||||||
}
|
}
|
||||||
@@ -33,10 +25,10 @@ smlang::statemachine!(
|
|||||||
name: UserAgent,
|
name: UserAgent,
|
||||||
custom_error: false,
|
custom_error: false,
|
||||||
transitions: {
|
transitions: {
|
||||||
*Init + AuthRequest(AuthRequestContext) / auth_request_context = ReceivedAuthRequest(AuthRequestContext),
|
*Init + AuthRequest = ReceivedAuthRequest,
|
||||||
ReceivedAuthRequest(AuthRequestContext) + ReceivedBootstrapToken = Idle,
|
ReceivedAuthRequest + ReceivedBootstrapToken = Idle,
|
||||||
|
|
||||||
ReceivedAuthRequest(AuthRequestContext) + SentChallenge(ChallengeContext) / move_challenge = WaitingForChallengeSolution(ChallengeContext),
|
ReceivedAuthRequest + SentChallenge(ChallengeContext) / move_challenge = WaitingForChallengeSolution(ChallengeContext),
|
||||||
|
|
||||||
WaitingForChallengeSolution(ChallengeContext) + ReceivedGoodSolution = Idle,
|
WaitingForChallengeSolution(ChallengeContext) + ReceivedGoodSolution = Idle,
|
||||||
WaitingForChallengeSolution(ChallengeContext) + ReceivedBadSolution = AuthError, // block further transitions, but connection should close anyway
|
WaitingForChallengeSolution(ChallengeContext) + ReceivedBadSolution = AuthError, // block further transitions, but connection should close anyway
|
||||||
@@ -49,28 +41,15 @@ smlang::statemachine!(
|
|||||||
|
|
||||||
pub struct DummyContext;
|
pub struct DummyContext;
|
||||||
impl UserAgentStateMachineContext for DummyContext {
|
impl UserAgentStateMachineContext for DummyContext {
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[allow(clippy::unused_unit)]
|
|
||||||
fn move_challenge(
|
|
||||||
&mut self,
|
|
||||||
_state_data: &AuthRequestContext,
|
|
||||||
event_data: ChallengeContext,
|
|
||||||
) -> Result<ChallengeContext, ()> {
|
|
||||||
Ok(event_data)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
#[allow(clippy::unused_unit)]
|
|
||||||
fn auth_request_context(
|
|
||||||
&mut self,
|
|
||||||
event_data: AuthRequestContext,
|
|
||||||
) -> Result<AuthRequestContext, ()> {
|
|
||||||
Ok(event_data)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[allow(clippy::unused_unit)]
|
#[allow(clippy::unused_unit)]
|
||||||
fn generate_temp_keypair(&mut self, event_data: UnsealContext) -> Result<UnsealContext, ()> {
|
fn generate_temp_keypair(&mut self, event_data: UnsealContext) -> Result<UnsealContext, ()> {
|
||||||
Ok(event_data)
|
Ok(event_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[allow(clippy::unused_unit)]
|
||||||
|
fn move_challenge< >(&mut self,event_data:ChallengeContext) -> Result<ChallengeContext,()> {
|
||||||
|
Ok(event_data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub type DatabasePool = diesel_async::pooled_connection::bb8::Pool<DatabaseConne
|
|||||||
pub type PoolInitError = diesel_async::pooled_connection::PoolError;
|
pub type PoolInitError = diesel_async::pooled_connection::PoolError;
|
||||||
pub type PoolError = diesel_async::pooled_connection::bb8::RunError;
|
pub type PoolError = diesel_async::pooled_connection::bb8::RunError;
|
||||||
|
|
||||||
static DB_FILE: &'static str = "arbiter.sqlite";
|
static DB_FILE: &str = "arbiter.sqlite";
|
||||||
|
|
||||||
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user