wip: putting new sphinx keys to self described endpoints
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::node::http::api::api_requests;
|
||||
use crate::node::http::state::AppState;
|
||||
use crate::node::key_rotation::active_keys::SphinxKeyGuard;
|
||||
use axum::extract::{Query, State};
|
||||
use nym_http_api_common::{FormattedResponse, OutputParams};
|
||||
use nym_node_requests::api::{v1::node::models::SignedHostInformation, SignedDataHostInfo};
|
||||
@@ -27,13 +28,25 @@ pub(crate) async fn host_information(
|
||||
) -> HostInformationResponse {
|
||||
let output = output.output.unwrap_or_default();
|
||||
|
||||
let primary_key = state.x25519_sphinx_keys.primary();
|
||||
let pre_announced = match state.x25519_sphinx_keys.secondary() {
|
||||
None => None,
|
||||
Some(secondary_key) => {
|
||||
todo!()
|
||||
}
|
||||
};
|
||||
|
||||
let host_info = api_requests::v1::node::models::HostInformation {
|
||||
ip_address: state.static_information.ip_addresses.clone(),
|
||||
hostname: state.static_information.hostname.clone(),
|
||||
keys: api_requests::v1::node::models::HostKeys {
|
||||
ed25519_identity: *state.static_information.ed25519_identity_keys.public_key(),
|
||||
x25519_sphinx: state.x25519_sphinx_keys.primary().x25519_pubkey(),
|
||||
current_x25519_sphinx_key: api_requests::v1::node::models::SphinxKey {
|
||||
rotation_id: primary_key.rotation_id(),
|
||||
public_key: primary_key.x25519_pubkey(),
|
||||
},
|
||||
x25519_noise: state.static_information.x25519_noise_key,
|
||||
pre_announced_x25519_sphinx_key: pre_announced,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -13,8 +13,14 @@ pub(crate) struct ActiveSphinxKeys {
|
||||
}
|
||||
|
||||
struct ActiveSphinxKeysInner {
|
||||
/// Key that's currently used as the default when processing packets with no explicit rotation information
|
||||
primary_key: ArcSwap<SphinxPrivateKey>,
|
||||
|
||||
/// Optionally, a key from the previous rotation during the overlap period when the keys are rotated.
|
||||
secondary_key: ArcSwapOption<SphinxPrivateKey>,
|
||||
|
||||
/// Optionally, a key for the upcoming rotation that's being pre-announced to other network entities
|
||||
pre_announced_key: ArcSwapOption<SphinxPrivateKey>,
|
||||
}
|
||||
|
||||
impl ActiveSphinxKeys {
|
||||
|
||||
@@ -2,25 +2,54 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::node::key_rotation::manager::SphinxKeyManager;
|
||||
use crate::node::nym_apis_client::NymApisClient;
|
||||
use nym_task::ShutdownToken;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use std::time::Duration;
|
||||
use tokio::time::interval;
|
||||
use tracing::{info, trace};
|
||||
|
||||
pub(crate) struct KeyRotationController {
|
||||
// regular polling rate to catch any changes in the system config. they shouldn't happen too often
|
||||
// so the requests can be sent quite infrequently
|
||||
regular_polling_interval: Duration,
|
||||
|
||||
client: NymApisClient,
|
||||
managed_keys: SphinxKeyManager,
|
||||
shutdown_token: ShutdownToken,
|
||||
}
|
||||
|
||||
enum KeyRotationActionState {
|
||||
// perform key-rotation and pre-announce new key to the nym-api(s)
|
||||
PreAnnounce,
|
||||
|
||||
// remove the old key and purge associated data like the replay detection bloomfilter
|
||||
PurgeOld,
|
||||
}
|
||||
|
||||
impl KeyRotationController {
|
||||
pub(crate) fn new(client: NymApisClient, shutdown_token: ShutdownToken) -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn regular_poll(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&self) {
|
||||
info!("starting sphinx key rotation controller");
|
||||
|
||||
let mut polling_interval = interval(self.regular_polling_interval);
|
||||
polling_interval.reset();
|
||||
|
||||
while !self.shutdown_token.is_cancelled() {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = self.shutdown_token.cancelled() => {
|
||||
trace!("KeyRotationController: Received shutdown");
|
||||
}
|
||||
_ = polling_interval.tick() => {
|
||||
self.regular_poll().await;
|
||||
}
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ use crate::error::{KeyIOFailure, NymNodeError};
|
||||
use crate::node::helpers::{load_key, store_key};
|
||||
use crate::node::key_rotation::active_keys::ActiveSphinxKeys;
|
||||
use crate::node::key_rotation::key::SphinxPrivateKey;
|
||||
use nym_crypto::aes::cipher::crypto_common::rand_core::{CryptoRng, RngCore};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing::{trace, warn};
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
pub(crate) mod active_keys;
|
||||
mod controller;
|
||||
pub(crate) mod controller;
|
||||
pub(crate) mod key;
|
||||
pub(crate) mod manager;
|
||||
|
||||
@@ -12,12 +12,19 @@ use nym_validator_client::nym_api::error::NymAPIError;
|
||||
use nym_validator_client::NymApiClient;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::timeout;
|
||||
use tracing::warn;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NymApisClient {
|
||||
inner: Arc<RwLock<InnerClient>>,
|
||||
}
|
||||
|
||||
struct InnerClient {
|
||||
active_client: NymApiClient,
|
||||
available_urls: Vec<Url>,
|
||||
currently_used_api: usize,
|
||||
@@ -38,24 +45,57 @@ impl NymApisClient {
|
||||
.build()?;
|
||||
|
||||
Ok(NymApisClient {
|
||||
active_client: NymApiClient {
|
||||
nym_api: active_client,
|
||||
},
|
||||
available_urls: urls,
|
||||
currently_used_api: 0,
|
||||
inner: Arc::new(RwLock::new(InnerClient {
|
||||
active_client: NymApiClient {
|
||||
nym_api: active_client,
|
||||
},
|
||||
available_urls: urls,
|
||||
currently_used_api: 0,
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
fn use_next_endpoint(&mut self) {
|
||||
if self.available_urls.len() == 1 {
|
||||
async fn use_next_endpoint(&mut self) {
|
||||
let mut guard = self.inner.write().await;
|
||||
if guard.available_urls.len() == 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
self.currently_used_api = (self.currently_used_api + 1) % self.available_urls.len();
|
||||
self.active_client
|
||||
.change_nym_api(self.available_urls[self.currently_used_api].clone())
|
||||
let next_index = (guard.currently_used_api + 1) % guard.available_urls.len();
|
||||
let next = guard.available_urls[next_index].clone();
|
||||
guard.currently_used_api = next_index;
|
||||
guard.active_client.change_nym_api(next)
|
||||
}
|
||||
|
||||
pub(crate) async fn query_exhaustively<R, T>(
|
||||
&self,
|
||||
req: R,
|
||||
timeout_duration: Duration,
|
||||
) -> Result<T, NymNodeError>
|
||||
where
|
||||
R: AsyncFn(Client) -> Result<T, NymAPIError>,
|
||||
{
|
||||
self.inner
|
||||
.read()
|
||||
.await
|
||||
.query_exhaustively(req, timeout_duration)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn broadcast_force_refresh(&self, private_key: &ed25519::PrivateKey) {
|
||||
self.inner
|
||||
.read()
|
||||
.await
|
||||
.broadcast_force_refresh(private_key)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(crate) async fn broadcast_key_rotation(&self) {
|
||||
self.inner.read().await.broadcast_key_rotation().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl InnerClient {
|
||||
// currently there are no cases without json body, but for those we'd just need to slightly adjust the signature
|
||||
async fn broadcast<B, R>(&self, request_body: &B, req: R, timeout_duration: Duration)
|
||||
where
|
||||
@@ -115,11 +155,11 @@ impl NymApisClient {
|
||||
}
|
||||
|
||||
pub(crate) async fn broadcast_key_rotation(&self) {
|
||||
//
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<NymApiClient> for NymApisClient {
|
||||
impl AsRef<NymApiClient> for InnerClient {
|
||||
fn as_ref(&self) -> &NymApiClient {
|
||||
&self.active_client
|
||||
}
|
||||
|
||||
@@ -33,41 +33,41 @@ struct ReplayProtectionBackgroundTaskConfig {
|
||||
|
||||
impl From<&Config> for ReplayProtectionBackgroundTaskConfig {
|
||||
fn from(config: &Config) -> Self {
|
||||
ReplayProtectionBackgroundTaskConfig {
|
||||
current_bloomfilter_path: config
|
||||
.mixnet
|
||||
.replay_protection
|
||||
.storage_paths
|
||||
.current_bloomfilter_filepath(),
|
||||
current_bloomfilter_temp_flush_path: config
|
||||
.mixnet
|
||||
.replay_protection
|
||||
.storage_paths
|
||||
.current_bloomfilter_being_flushed_filepath(),
|
||||
false_positive_rate: config.mixnet.replay_protection.debug.false_positive_rate,
|
||||
filter_reset_rate: config.mixnet.replay_protection.debug.bloomfilter_reset_rate,
|
||||
disk_flushing_rate: config
|
||||
.mixnet
|
||||
.replay_protection
|
||||
.debug
|
||||
.bloomfilter_disk_flushing_rate,
|
||||
bloomfilter_size_multiplier: config
|
||||
.mixnet
|
||||
.replay_protection
|
||||
.debug
|
||||
.bloomfilter_size_multiplier,
|
||||
minimum_bloomfilter_packets_per_second: config
|
||||
.mixnet
|
||||
.replay_protection
|
||||
.debug
|
||||
.bloomfilter_minimum_packets_per_second_size,
|
||||
}
|
||||
todo!()
|
||||
// ReplayProtectionBackgroundTaskConfig {
|
||||
// current_bloomfilter_path: config
|
||||
// .mixnet
|
||||
// .replay_protection
|
||||
// .storage_paths
|
||||
// .current_bloomfilter_filepath(),
|
||||
// current_bloomfilter_temp_flush_path: config
|
||||
// .mixnet
|
||||
// .replay_protection
|
||||
// .storage_paths
|
||||
// .current_bloomfilter_being_flushed_filepath(),
|
||||
// false_positive_rate: config.mixnet.replay_protection.debug.false_positive_rate,
|
||||
// filter_reset_rate: config.mixnet.replay_protection.debug.bloomfilter_reset_rate,
|
||||
// disk_flushing_rate: config
|
||||
// .mixnet
|
||||
// .replay_protection
|
||||
// .debug
|
||||
// .bloomfilter_disk_flushing_rate,
|
||||
// bloomfilter_size_multiplier: config
|
||||
// .mixnet
|
||||
// .replay_protection
|
||||
// .debug
|
||||
// .bloomfilter_size_multiplier,
|
||||
// minimum_bloomfilter_packets_per_second: config
|
||||
// .mixnet
|
||||
// .replay_protection
|
||||
// .debug
|
||||
// .bloomfilter_minimum_packets_per_second_size,
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// background task responsible for periodically flushing the bloomfilter to disk
|
||||
// as well as clearing it up on the specified timer
|
||||
// (in the future this will be enforced by key rotation)
|
||||
// background task responsible for periodically flushing the bloomfilters to disk
|
||||
// it no longer removes them on the timer as it's now responsibility of the key rotation controller
|
||||
pub struct ReplayProtectionBackgroundTask {
|
||||
config: ReplayProtectionBackgroundTaskConfig,
|
||||
last_reset: LastResetData,
|
||||
|
||||
Reference in New Issue
Block a user