additional fixes to key creation

This commit is contained in:
Jędrzej Stuczyński
2024-03-07 14:58:06 +00:00
parent c0ae924c58
commit 5bb96bf42c
23 changed files with 148 additions and 739 deletions
-3
View File
@@ -12,9 +12,6 @@ use clap::{Parser, Subcommand};
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::base_client::storage::OnDiskGatewaysDetails;
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
-3
View File
@@ -13,9 +13,6 @@ use clap::{Parser, Subcommand};
use log::{error, info};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::topology_control::geo_aware_provider::CountryGroup;
use nym_client_core::config::{GatewayEndpointConfig, GroupBy, TopologyStructure};
@@ -1,7 +1,7 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::types::{GatewayDetails, GatewayRegistration};
use crate::types::GatewayRegistration;
use crate::{GatewaysDetailsStore, StorageError};
use async_trait::async_trait;
use manager::StorageManager;
@@ -26,6 +26,7 @@ impl OnDiskGatewaysDetails {
#[async_trait]
impl GatewaysDetailsStore for OnDiskGatewaysDetails {
type StorageError = error::StorageError;
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError> {
todo!()
}
@@ -26,52 +26,81 @@ pub struct InMemGatewaysDetails {
#[derive(Debug, Default)]
struct InMemStorageInner {
active_gateway: Option<String>,
gateways: HashMap<String, GatewayDetails>,
gateways: HashMap<String, GatewayRegistration>,
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewaysDetailsStore for InMemGatewaysDetails {
type StorageError = InMemStorageError;
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError> {
todo!()
}
async fn active_gateway(&self) -> Result<Option<GatewayRegistration>, Self::StorageError> {
// let guard = self.inner.read().await;
//
// let foo = guard.active_gateway.map(|id| {
// // SAFETY: if particular gateway is set as active, its details MUST exist
// #[allow(clippy::unwrap_used)]
// guard.gateways.get(&id).unwrap()
// });
todo!()
// foo.cloned()
async fn has_gateway_details(&self, gateway_id: &str) -> Result<bool, Self::StorageError> {
Ok(self.inner.read().await.gateways.contains_key(gateway_id))
}
async fn active_gateway(&self) -> Result<Option<GatewayRegistration>, Self::StorageError> {
let guard = self.inner.read().await;
Ok(guard.active_gateway.as_ref().map(|id| {
// SAFETY: if particular gateway is set as active, its details MUST exist
#[allow(clippy::unwrap_used)]
guard.gateways.get(id).unwrap().clone()
}))
}
async fn set_active_gateway(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
todo!()
// ensure the gateway with provided id exists
let mut guard = self.inner.write().await;
if !guard.gateways.contains_key(gateway_id) {
return Err(InMemStorageError::GatewayDoesNotExist {
gateway_id: gateway_id.to_string(),
});
}
guard.active_gateway = Some(gateway_id.to_string());
Ok(())
}
async fn all_gateways(&self) -> Result<Vec<GatewayRegistration>, Self::StorageError> {
todo!()
Ok(self.inner.read().await.gateways.values().cloned().collect())
}
async fn load_gateway_details(
&self,
gateway_id: &str,
) -> Result<GatewayRegistration, Self::StorageError> {
todo!()
self.inner
.read()
.await
.gateways
.get(gateway_id)
.cloned()
.ok_or(InMemStorageError::GatewayDoesNotExist {
gateway_id: gateway_id.to_string(),
})
}
async fn store_gateway_details(
&self,
details: &GatewayRegistration,
) -> Result<(), Self::StorageError> {
todo!()
self.inner.write().await.gateways.insert(
details.details.gateway_id().to_base58_string(),
details.clone(),
);
Ok(())
}
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
todo!()
let mut guard = self.inner.write().await;
if let Some(active) = guard.active_gateway.as_ref() {
if active == gateway_id {
guard.active_gateway = None
}
}
guard.gateways.remove(gateway_id);
Ok(())
}
}
@@ -16,13 +16,13 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
pub const REMOTE_GATEWAY_TYPE: &str = "remote";
pub const CUSTOM_GATEWAY_TYPE: &str = "custom";
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct GatewayRegistration {
pub details: GatewayDetails,
pub registration_timestamp: OffsetDateTime,
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum GatewayDetails {
/// Standard details of a remote gateway
Remote(RemoteGatewayDetails),
@@ -197,7 +197,7 @@ impl From<RemoteGatewayDetails> for RawRemoteGatewayDetails {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct RemoteGatewayDetails {
pub gateway_id: identity::PublicKey,
@@ -245,7 +245,7 @@ impl From<CustomGatewayDetails> for RawCustomGatewayDetails {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct CustomGatewayDetails {
pub gateway_id: identity::PublicKey,
pub data: Option<Vec<u8>>,
@@ -4,10 +4,12 @@
use super::packet_statistics_control::PacketStatisticsReporter;
use super::received_buffer::ReceivedBufferMessage;
use super::topology_control::geo_aware_provider::GeoAwareTopologyProvider;
use crate::client::base_client::storage::helpers::store_client_keys;
use crate::client::base_client::storage::MixnetClientStorage;
use crate::client::cover_traffic_stream::LoopCoverTrafficStream;
use crate::client::inbound_messages::{InputMessage, InputMessageReceiver, InputMessageSender};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::ClientKeys;
use crate::client::mix_traffic::transceiver::{GatewayReceiver, GatewayTransceiver, RemoteGateway};
use crate::client::mix_traffic::{BatchMixMessageSender, MixTrafficController};
use crate::client::packet_statistics_control::PacketStatisticsControl;
@@ -51,6 +53,7 @@ use nym_task::{TaskClient, TaskHandle};
use nym_topology::provider_trait::TopologyProvider;
use nym_topology::HardcodedTopologyProvider;
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
use rand::rngs::OsRng;
use std::fmt::Debug;
use std::os::raw::c_int as RawFd;
use std::path::Path;
@@ -577,6 +580,14 @@ where
<S::KeyStore as KeyStore>::StorageError: Sync + Send,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{
// if client keys do not exist already, create and persist them
if key_store.load_keys().await.is_err() {
info!("could not find valid client keys - a new set will be generated");
let mut rng = OsRng;
let keys = ClientKeys::generate_new(&mut rng);
store_client_keys(keys, key_store).await?;
}
setup_gateway(setup_method, key_store, details_store).await
}
@@ -1,282 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use async_trait::async_trait;
use log::error;
use nym_gateway_requests::registration::handshake::SharedKeys;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::error::Error;
use std::ops::Deref;
use tokio::sync::Mutex;
use zeroize::Zeroizing;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait GatewayDetailsStore {
type StorageError: Error;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError>;
async fn store_gateway_details(
&self,
details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PersistedGatewayDetails {
/// Standard details of a remote gateway
Default(PersistedGatewayConfig),
/// Custom gateway setup, such as for a client embedded inside gateway itself
Custom(PersistedCustomGatewayDetails),
}
impl PersistedGatewayDetails {
// TODO: this should probably allow for custom verification over T
pub fn validate(&self, shared_key: Option<&SharedKeys>) -> Result<(), ClientCoreError> {
match self {
PersistedGatewayDetails::Default(details) => {
if !details.verify(shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?) {
Err(ClientCoreError::MismatchedGatewayDetails {
gateway_id: details.details.gateway_id.clone(),
})
} else {
Ok(())
}
}
PersistedGatewayDetails::Custom(_) => {
if shared_key.is_some() {
error!("using custom persisted gateway setup with shared key present - are you sure that's what you want?");
// but technically we could still continue. just ignore the key
}
Ok(())
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PersistedGatewayConfig {
// TODO: should we also verify correctness of the details themselves?
// i.e. we could include a checksum or tag (via the shared keys)
// counterargument: if we wanted to modify, say, the host information in the stored file on disk,
// in order to actually use it, we'd have to recompute the whole checksum which would be a huge pain.
/// The hash of the shared keys to ensure the correct ones are used with those gateway details.
#[serde(with = "base64")]
key_hash: Vec<u8>,
/// Actual gateway details being persisted.
pub details: GatewayEndpointConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistedCustomGatewayDetails {
// whatever custom method is used, gateway's identity must be known
pub gateway_id: String,
#[serde(flatten)]
pub additional_data: Vec<u8>,
}
impl PersistedGatewayConfig {
pub fn new(details: GatewayEndpointConfig, shared_key: &SharedKeys) -> Self {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize().to_vec();
PersistedGatewayConfig { key_hash, details }
}
pub fn verify(&self, shared_key: &SharedKeys) -> bool {
let key_bytes = Zeroizing::new(shared_key.to_bytes());
let mut key_hasher = Sha256::new();
key_hasher.update(&key_bytes);
let key_hash = key_hasher.finalize();
self.key_hash == key_hash.deref()
}
}
impl PersistedGatewayDetails {
// pub fn new(
// details: GatewayDetails,
// shared_key: Option<&SharedKeys>,
// ) -> Result<Self, ClientCoreError> {
// match details {
// GatewayDetails::Configured(cfg) => {
// let shared_key = shared_key.ok_or(ClientCoreError::UnavailableSharedKey)?;
// Ok(PersistedGatewayDetails::Default(
// PersistedGatewayConfig::new(cfg, shared_key),
// ))
// }
// GatewayDetails::Custom(custom) => Ok(PersistedGatewayDetails::Custom(custom.into())),
// }
// }
//
// pub fn is_custom(&self) -> bool {
// matches!(self, PersistedGatewayDetails::Custom(..))
// }
//
// pub fn matches(&self, other: &GatewayDetails) -> bool {
// match self {
// PersistedGatewayDetails::Default(default) => {
// if let GatewayDetails::Configured(other_configured) = other {
// &default.details == other_configured
// } else {
// false
// }
// }
// PersistedGatewayDetails::Custom(custom) => {
// if let GatewayDetails::Custom(other_custom) = other {
// custom.gateway_id == other_custom.gateway_id
// && custom.additional_data == other_custom.additional_data
// } else {
// false
// }
// }
// }
// }
}
// helper to make Vec<u8> serialization use base64 representation to make it human readable
// so that it would be easier for users to copy contents from the disk if they wanted to use it elsewhere
mod base64 {
use base64::{engine::general_purpose::STANDARD, Engine as _};
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&STANDARD.encode(bytes))
}
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<u8>, D::Error> {
let s = <String>::deserialize(deserializer)?;
STANDARD.decode(s).map_err(serde::de::Error::custom)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug, thiserror::Error)]
pub enum OnDiskGatewayDetailsError {
#[error("JSON failure: {0}")]
SerializationFailure(#[from] serde_json::Error),
#[error("failed to store gateway details to {path}: {err}")]
StoreFailure {
path: String,
#[source]
err: std::io::Error,
},
#[error("failed to load gateway details from {path}: {err}")]
LoadFailure {
path: String,
#[source]
err: std::io::Error,
},
}
#[cfg(not(target_arch = "wasm32"))]
pub struct OnDiskGatewayDetails {
file_location: std::path::PathBuf,
}
#[cfg(not(target_arch = "wasm32"))]
impl OnDiskGatewayDetails {
pub fn new<P: AsRef<std::path::Path>>(path: P) -> Self {
OnDiskGatewayDetails {
file_location: path.as_ref().to_owned(),
}
}
pub fn load_from_disk(&self) -> Result<PersistedGatewayDetails, OnDiskGatewayDetailsError> {
let file = std::fs::File::open(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::LoadFailure {
path: self.file_location.display().to_string(),
err,
}
})?;
Ok(serde_json::from_reader(file)?)
}
pub fn store_to_disk(
&self,
details: &PersistedGatewayDetails,
) -> Result<(), OnDiskGatewayDetailsError> {
// ensure the whole directory structure exists
if let Some(parent_dir) = &self.file_location.parent() {
std::fs::create_dir_all(parent_dir).map_err(|err| {
OnDiskGatewayDetailsError::StoreFailure {
path: self.file_location.display().to_string(),
err,
}
})?
}
let file = std::fs::File::create(&self.file_location).map_err(|err| {
OnDiskGatewayDetailsError::StoreFailure {
path: self.file_location.display().to_string(),
err,
}
})?;
Ok(serde_json::to_writer_pretty(file, details)?)
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewayDetailsStore for OnDiskGatewayDetails {
type StorageError = OnDiskGatewayDetailsError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.load_from_disk()
}
async fn store_gateway_details(
&self,
gateway_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
self.store_to_disk(gateway_details)
}
}
#[derive(Default)]
pub struct InMemGatewayDetails {
details: Mutex<Option<PersistedGatewayDetails>>,
}
#[derive(Debug, thiserror::Error)]
#[error("old ephemeral gateway details can't be loaded from storage")]
pub struct EphemeralGatewayDetailsError;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl GatewayDetailsStore for InMemGatewayDetails {
type StorageError = EphemeralGatewayDetailsError;
async fn load_gateway_details(&self) -> Result<PersistedGatewayDetails, Self::StorageError> {
self.details
.lock()
.await
.clone()
.ok_or(EphemeralGatewayDetailsError)
}
async fn store_gateway_details(
&self,
gateway_details: &PersistedGatewayDetails,
) -> Result<(), Self::StorageError> {
*self.details.lock().await = Some(gateway_details.clone());
Ok(())
}
}
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::KeyManager;
use crate::client::key_manager::ClientKeys;
use crate::error::ClientCoreError;
use nym_client_core_gateways_storage::{GatewayRegistration, GatewaysDetailsStore};
@@ -88,12 +88,24 @@ where
})
}
pub async fn load_client_keys<K>(key_store: &K) -> Result<KeyManager, ClientCoreError>
pub async fn load_client_keys<K>(key_store: &K) -> Result<ClientKeys, ClientCoreError>
where
K: KeyStore,
K::StorageError: Send + Sync + 'static,
{
KeyManager::load_keys(key_store)
ClientKeys::load_keys(key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
})
}
pub async fn store_client_keys<K>(keys: ClientKeys, key_store: &K) -> Result<(), ClientCoreError>
where
K: KeyStore,
K::StorageError: Send + Sync + 'static,
{
keys.persist_keys(key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
source: Box::new(source),
@@ -4,9 +4,6 @@
// TODO: combine those more closely. Perhaps into a single underlying store.
// Like for persistent, on-disk, storage, what's the point of having 3 different databases?
// use crate::client::base_client::storage::gateway_details::{
// GatewayDetailsStore, InMemGatewayDetails,
// };
use crate::client::key_manager::persistence::{InMemEphemeralKeys, KeyStore};
use crate::client::replies::reply_storage;
use crate::client::replies::reply_storage::ReplyStorageBackend;
@@ -18,30 +15,24 @@ use nym_credential_storage::storage::Storage as CredentialStorage;
feature = "fs-surb-storage",
feature = "fs-gateways-storage"
))]
use crate::client::base_client::non_wasm_helpers;
// #[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
// use crate::client::base_client::storage::gateway_details::OnDiskGatewayDetails;
#[cfg(not(target_arch = "wasm32"))]
use crate::client::key_manager::persistence::OnDiskKeys;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::client::replies::reply_storage::fs_backend;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::config::{self, disk_persistence::CommonClientPaths};
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use crate::error::ClientCoreError;
use crate::{
client::{
base_client::non_wasm_helpers, key_manager::persistence::OnDiskKeys,
replies::reply_storage::fs_backend,
},
config::{self, disk_persistence::CommonClientPaths},
error::ClientCoreError,
};
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
use nym_credential_storage::persistent_storage::PersistentStorage as PersistentCredentialStorage;
// fs-gateways
pub use nym_client_core_gateways_storage::{GatewaysDetailsStore, InMemGatewaysDetails};
#[deprecated]
pub mod gateway_details;
pub mod helpers;
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-gateways-storage"))]
pub use nym_client_core_gateways_storage::{OnDiskGatewaysDetails, StorageError};
pub mod helpers;
// TODO: ideally this should be changed into
// `MixnetClientStorage: KeyStore + ReplyStorageBackend + CredentialStorage + GatewayDetailsStore`
pub trait MixnetClientStorage {
@@ -6,230 +6,11 @@ use nym_crypto::asymmetric::{encryption, identity};
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::acknowledgements::AckKey;
use rand::{CryptoRng, RngCore};
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
use zeroize::ZeroizeOnDrop;
pub mod persistence;
pub struct ManagedKeys {
client_keys: KeyManager,
active_gateway_keys: Option<Arc<SharedKeys>>,
}
//
// pub enum ManagedKeys {
// Initial(KeyManagerBuilder),
// FullyDerived(KeyManager),
//
// // I really hate the existence of this variant, but I couldn't come up with a better way to handle
// // `Self::deal_with_gateway_key` otherwise.
// Invalidated,
// }
//
// impl Debug for ManagedKeys {
// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// match self {
// ManagedKeys::Initial(_) => write!(f, "initial"),
// ManagedKeys::FullyDerived(_) => write!(f, "fully derived"),
// ManagedKeys::Invalidated => write!(f, "invalidated"),
// }
// }
// }
//
// impl From<KeyManagerBuilder> for ManagedKeys {
// fn from(value: KeyManagerBuilder) -> Self {
// ManagedKeys::Initial(value)
// }
// }
//
// impl From<KeyManager> for ManagedKeys {
// fn from(value: KeyManager) -> Self {
// ManagedKeys::FullyDerived(value)
// }
// }
//
impl ManagedKeys {
// pub fn is_valid(&self) -> bool {
// !matches!(self, ManagedKeys::Invalidated)
// }
//
// pub async fn try_load<S: KeyStore>(key_store: &S) -> Result<Self, S::StorageError> {
// Ok(ManagedKeys::FullyDerived(
// KeyManager::load_keys(key_store).await?,
// ))
// }
//
// pub fn generate_new<R>(rng: &mut R) -> Self
// where
// R: RngCore + CryptoRng,
// {
// ManagedKeys::Initial(KeyManagerBuilder::new(rng))
// }
//
// pub async fn load_or_generate<R, S>(rng: &mut R, key_store: &S) -> Self
// where
// R: RngCore + CryptoRng,
// S: KeyStore,
// {
// Self::try_load(key_store)
// .await
// .unwrap_or_else(|_| Self::generate_new(rng))
// }
//
// pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
// match self {
// ManagedKeys::Initial(keys) => keys.identity_keypair(),
// ManagedKeys::FullyDerived(keys) => keys.identity_keypair(),
// ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
// }
// }
//
// pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
// match self {
// ManagedKeys::Initial(keys) => keys.encryption_keypair(),
// ManagedKeys::FullyDerived(keys) => keys.encryption_keypair(),
// ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
// }
// }
//
// pub fn ack_key(&self) -> Arc<AckKey> {
// match self {
// ManagedKeys::Initial(keys) => keys.ack_key(),
// ManagedKeys::FullyDerived(keys) => keys.ack_key(),
// ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
// }
// }
//
// pub fn must_get_gateway_shared_key(&self) -> Arc<SharedKeys> {
// self.gateway_shared_key()
// .expect("failed to extract gateway shared key")
// }
//
// pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
// match self {
// ManagedKeys::Initial(_) => None,
// ManagedKeys::FullyDerived(keys) => keys.gateway_shared_key(),
// ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
// }
// }
//
// pub fn identity_public_key(&self) -> &identity::PublicKey {
// match self {
// ManagedKeys::Initial(keys) => keys.identity_keypair.public_key(),
// ManagedKeys::FullyDerived(keys) => keys.identity_keypair.public_key(),
// ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
// }
// }
//
// pub fn encryption_public_key(&self) -> &encryption::PublicKey {
// match self {
// ManagedKeys::Initial(keys) => keys.encryption_keypair.public_key(),
// ManagedKeys::FullyDerived(keys) => keys.encryption_keypair.public_key(),
// ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
// }
// }
//
// pub fn ensure_gateway_key(&self, gateway_shared_key: Option<Arc<SharedKeys>>) {
// if let ManagedKeys::FullyDerived(key_manager) = &self {
// if self.gateway_shared_key().is_none() && gateway_shared_key.is_none() {
// // the key doesn't exist in either state
// return;
// }
//
// if gateway_shared_key.is_some() && self.gateway_shared_key().is_none()
// || gateway_shared_key.is_none() && self.gateway_shared_key().is_some()
// {
// // if one is provided whilst the other is not...
// // TODO: should this actually panic or return an error? would this branch be possible
// // under normal operation?
// panic!("inconsistent re-derived gateway key")
// }
//
// // here we know both keys MUST exist
// let provided = gateway_shared_key.unwrap();
// if !Arc::ptr_eq(key_manager.must_get_gateway_shared_key(), &provided)
// || *key_manager.must_get_gateway_shared_key() != provided
// {
// // this should NEVER happen thus panic here
// panic!("derived fresh gateway shared key whilst already holding one!")
// }
// }
// }
//
// pub async fn deal_with_gateway_key<S: KeyStore>(
// &mut self,
// gateway_shared_key: Option<Arc<SharedKeys>>,
// key_store: &S,
// ) -> Result<(), S::StorageError> {
// let key_manager = match std::mem::replace(self, ManagedKeys::Invalidated) {
// ManagedKeys::Initial(keys) => {
// let key_manager = keys.insert_maybe_gateway_shared_key(gateway_shared_key);
// key_manager.persist_keys(key_store).await?;
// key_manager
// }
// ManagedKeys::FullyDerived(key_manager) => {
// self.ensure_gateway_key(gateway_shared_key);
// key_manager
// }
// ManagedKeys::Invalidated => unreachable!("the managed keys got invalidated"),
// };
//
// *self = ManagedKeys::FullyDerived(key_manager);
// Ok(())
// }
}
//
// // all of the keys really shouldn't be wrapped in `Arc`, but due to how the gateway client is currently
// // constructed, changing that would require more work than what it's worth
// pub struct KeyManagerBuilder {
// /// identity key associated with the client instance.
// identity_keypair: Arc<identity::KeyPair>,
//
// /// encryption key associated with the client instance.
// encryption_keypair: Arc<encryption::KeyPair>,
//
// /// key used for producing and processing acknowledgement packets.
// ack_key: Arc<AckKey>,
// }
//
// impl KeyManagerBuilder {
// /// Creates new instance of a [`KeyManager`]
// pub fn new<R>(rng: &mut R) -> Self
// where
// R: RngCore + CryptoRng,
// {
// KeyManagerBuilder {
// identity_keypair: Arc::new(identity::KeyPair::new(rng)),
// encryption_keypair: Arc::new(encryption::KeyPair::new(rng)),
// ack_key: Arc::new(AckKey::new(rng)),
// }
// }
//
// pub fn insert_maybe_gateway_shared_key(
// self,
// gateway_shared_key: Option<Arc<SharedKeys>>,
// ) -> KeyManager {
// KeyManager {
// identity_keypair: self.identity_keypair,
// encryption_keypair: self.encryption_keypair,
// gateway_shared_key,
// ack_key: self.ack_key,
// }
// }
//
// pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
// Arc::clone(&self.identity_keypair)
// }
//
// pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
// Arc::clone(&self.encryption_keypair)
// }
//
// pub fn ack_key(&self) -> Arc<AckKey> {
// Arc::clone(&self.ack_key)
// }
// }
// Note: to support key rotation in the future, all keys will require adding an extra smart pointer,
// most likely an AtomicCell, or if it doesn't work as I think it does, a Mutex. Although I think
// AtomicCell includes a Mutex implicitly if the underlying type does not work atomically.
@@ -238,30 +19,24 @@ impl ManagedKeys {
// Remember that Arc<T> has Deref implementation for T
#[derive(Clone)]
pub struct KeyManager {
pub struct ClientKeys {
/// identity key associated with the client instance.
identity_keypair: Arc<identity::KeyPair>,
/// encryption key associated with the client instance.
encryption_keypair: Arc<encryption::KeyPair>,
// /// shared key derived with the gateway during "registration handshake"
// // I'm not a fan of how we broke the nice transition of `KeyManagerBuilder` -> `KeyManager`
// // by making this field optional.
// // However, it has to be optional for when we use embedded NR inside a gateway,
// // since it won't have a shared key (because why would it?)
// gateway_shared_key: Option<Arc<SharedKeys>>,
/// key used for producing and processing acknowledgement packets.
ack_key: Arc<AckKey>,
}
impl KeyManager {
/// Creates new instance of a [`KeyManager`]
impl ClientKeys {
/// Creates new instance of a [`ClientKeys`]
pub fn generate_new<R>(rng: &mut R) -> Self
where
R: RngCore + CryptoRng,
{
KeyManager {
ClientKeys {
identity_keypair: Arc::new(identity::KeyPair::new(rng)),
encryption_keypair: Arc::new(encryption::KeyPair::new(rng)),
ack_key: Arc::new(AckKey::new(rng)),
@@ -271,13 +46,11 @@ impl KeyManager {
pub fn from_keys(
id_keypair: identity::KeyPair,
enc_keypair: encryption::KeyPair,
// gateway_shared_key: Option<SharedKeys>,
ack_key: AckKey,
) -> Self {
Self {
identity_keypair: Arc::new(id_keypair),
encryption_keypair: Arc::new(enc_keypair),
// gateway_shared_key: gateway_shared_key.map(Arc::new),
ack_key: Arc::new(ack_key),
}
}
@@ -303,32 +76,6 @@ impl KeyManager {
pub fn ack_key(&self) -> Arc<AckKey> {
Arc::clone(&self.ack_key)
}
// fn must_get_gateway_shared_key(&self) -> &Arc<SharedKeys> {
// self.gateway_shared_key
// .as_ref()
// .expect("gateway shared key is unavailable")
// }
//
// pub fn uses_custom_gateway(&self) -> bool {
// self.gateway_shared_key.is_none()
// }
//
// /// Gets an atomically reference counted pointer to [`SharedKey`].
// pub fn gateway_shared_key(&self) -> Option<Arc<SharedKeys>> {
// self.gateway_shared_key.as_ref().map(Arc::clone)
// }
// pub fn remove_gateway_key(self) -> KeyManagerBuilder {
// if Arc::strong_count(self.must_get_gateway_shared_key()) > 1 {
// panic!("attempted to remove gateway key whilst still holding multiple references!")
// }
// KeyManagerBuilder {
// identity_keypair: self.identity_keypair,
// encryption_keypair: self.encryption_keypair,
// ack_key: self.ack_key,
// }
// }
}
fn _assert_keys_zeroize_on_drop() {
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::key_manager::KeyManager;
use crate::client::key_manager::ClientKeys;
use async_trait::async_trait;
use std::error::Error;
use tokio::sync::Mutex;
@@ -25,9 +25,9 @@ use nym_sphinx::acknowledgements::AckKey;
pub trait KeyStore {
type StorageError: Error;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError>;
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError>;
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError>;
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError>;
}
#[cfg(not(target_arch = "wasm32"))]
@@ -148,19 +148,19 @@ impl OnDiskKeys {
})
}
fn load_keys(&self) -> Result<KeyManager, OnDiskKeysError> {
fn load_keys(&self) -> Result<ClientKeys, OnDiskKeysError> {
let identity_keypair = self.load_identity_keypair()?;
let encryption_keypair = self.load_encryption_keypair()?;
let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
Ok(KeyManager::from_keys(
Ok(ClientKeys::from_keys(
identity_keypair,
encryption_keypair,
ack_key,
))
}
fn store_keys(&self, keys: &KeyManager) -> Result<(), OnDiskKeysError> {
fn store_keys(&self, keys: &ClientKeys) -> Result<(), OnDiskKeysError> {
let identity_paths = self.paths.identity_key_pair_path();
let encryption_paths = self.paths.encryption_key_pair_path();
@@ -186,18 +186,18 @@ impl OnDiskKeys {
impl KeyStore for OnDiskKeys {
type StorageError = OnDiskKeysError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
self.load_keys()
}
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
self.store_keys(keys)
}
}
#[derive(Default)]
pub struct InMemEphemeralKeys {
keys: Mutex<Option<KeyManager>>,
keys: Mutex<Option<ClientKeys>>,
}
#[derive(Debug, thiserror::Error)]
@@ -209,11 +209,11 @@ pub struct EphemeralKeysError;
impl KeyStore for InMemEphemeralKeys {
type StorageError = EphemeralKeysError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
self.keys.lock().await.clone().ok_or(EphemeralKeysError)
}
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
*self.keys.lock().await = Some(keys.clone());
Ok(())
}
-1
View File
@@ -1,7 +1,6 @@
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::GatewayEndpointConfig;
use crate::error::ClientCoreError;
use crate::init::types::RegistrationResult;
use futures::{SinkExt, StreamExt};
+5 -5
View File
@@ -8,7 +8,7 @@ use crate::client::base_client::storage::helpers::{
store_gateway_details,
};
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::KeyManager;
use crate::client::key_manager::ClientKeys;
use crate::error::ClientCoreError;
use crate::init::helpers::{
choose_gateway_by_latency, get_specified_gateway, uniformly_random_gateway,
@@ -38,7 +38,7 @@ where
K: KeyStore,
K::StorageError: Send + Sync + 'static,
{
KeyManager::generate_new(rng)
ClientKeys::generate_new(rng)
.persist_keys(key_store)
.await
.map_err(|source| ClientCoreError::KeyStoreError {
@@ -176,7 +176,7 @@ where
fn reuse_gateway_connection(
authenticated_ephemeral_client: InitGatewayClient,
gateway_registration: GatewayRegistration,
client_keys: KeyManager,
client_keys: ClientKeys,
) -> InitialisationResult {
InitialisationResult {
gateway_registration,
@@ -218,10 +218,10 @@ where
GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
gateway_details,
managed_keys,
client_keys: managed_keys,
} => Ok(reuse_gateway_connection(
authenticated_ephemeral_client,
gateway_details,
*gateway_details,
managed_keys,
)),
}
+18 -105
View File
@@ -2,23 +2,21 @@
// SPDX-License-Identifier: Apache-2.0
use crate::client::key_manager::persistence::KeyStore;
use crate::client::key_manager::{KeyManager, ManagedKeys};
use crate::config::{Config, GatewayEndpointConfig};
use crate::client::key_manager::ClientKeys;
use crate::config::Config;
use crate::error::ClientCoreError;
use crate::init::{setup_gateway, use_loaded_gateway_details};
use nym_client_core_gateways_storage::{
BadGateway, GatewayRegistration, GatewaysDetailsStore, RemoteGatewayDetails,
GatewayRegistration, GatewaysDetailsStore, RemoteGatewayDetails,
};
use nym_crypto::asymmetric::identity;
use nym_gateway_client::client::InitGatewayClient;
use nym_gateway_requests::registration::handshake::SharedKeys;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::addressing::nodes::NodeIdentity;
use nym_topology::gateway;
use nym_validator_client::client::IdentityKey;
use nym_validator_client::nyxd::AccountId;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use std::fmt::Display;
use std::str::FromStr;
use std::sync::Arc;
@@ -111,12 +109,12 @@ pub struct RegistrationResult {
/// if this was the first time this client registered
pub struct InitialisationResult {
pub gateway_registration: GatewayRegistration,
pub client_keys: KeyManager,
pub client_keys: ClientKeys,
pub authenticated_ephemeral_client: Option<InitGatewayClient>,
}
impl InitialisationResult {
pub fn new_loaded(gateway_registration: GatewayRegistration, client_keys: KeyManager) -> Self {
pub fn new_loaded(gateway_registration: GatewayRegistration, client_keys: ClientKeys) -> Self {
InitialisationResult {
gateway_registration,
client_keys,
@@ -144,89 +142,6 @@ impl InitialisationResult {
)
}
}
//
// /// Details of particular gateway client got registered with
// #[derive(Debug, Clone)]
// pub enum GatewayDetails {
// /// Standard details of a remote gateway
// Configured(GatewayEndpointConfig),
//
// /// Custom gateway setup, such as for a client embedded inside gateway itself
// Custom(CustomGatewayDetails),
// }
//
// #[derive(Debug, Clone, Serialize, Deserialize)]
// pub struct CustomGatewayDetails {
// // whatever custom method is used, gateway's identity must be known
// pub gateway_id: String,
//
// pub additional_data: Vec<u8>,
// }
//
// impl CustomGatewayDetails {
// pub fn new(gateway_id: String, additional_data: Vec<u8>) -> Self {
// Self {
// gateway_id,
// additional_data,
// }
// }
// }
//
// impl GatewayDetails {
// pub fn try_get_configured_endpoint(&self) -> Option<&GatewayEndpointConfig> {
// if let GatewayDetails::Configured(endpoint) = &self {
// Some(endpoint)
// } else {
// None
// }
// }
//
// pub fn is_custom(&self) -> bool {
// matches!(self, GatewayDetails::Custom(_))
// }
//
// pub fn gateway_id(&self) -> &str {
// match self {
// GatewayDetails::Configured(cfg) => &cfg.gateway_id,
// GatewayDetails::Custom(custom) => &custom.gateway_id,
// }
// }
// }
//
// impl From<GatewayEndpointConfig> for GatewayDetails {
// fn from(value: GatewayEndpointConfig) -> Self {
// GatewayDetails::Configured(value)
// }
// }
//
// impl From<PersistedCustomGatewayDetails> for CustomGatewayDetails {
// fn from(value: PersistedCustomGatewayDetails) -> Self {
// CustomGatewayDetails {
// gateway_id: value.gateway_id,
// additional_data: value.additional_data,
// }
// }
// }
//
// impl From<CustomGatewayDetails> for PersistedCustomGatewayDetails {
// fn from(value: CustomGatewayDetails) -> Self {
// PersistedCustomGatewayDetails {
// gateway_id: value.gateway_id,
// additional_data: value.additional_data,
// }
// }
// }
//
// impl From<PersistedGatewayDetails> for GatewayDetails {
// fn from(value: PersistedGatewayDetails) -> Self {
// match value {
// PersistedGatewayDetails::Default(default) => {
// GatewayDetails::Configured(default.details)
// }
// PersistedGatewayDetails::Custom(custom) => GatewayDetails::Custom(custom.into()),
// }
// }
// }
#[derive(Clone, Debug)]
pub enum GatewaySelectionSpecification {
@@ -301,24 +216,23 @@ pub enum GatewaySetup {
authenticated_ephemeral_client: InitGatewayClient,
// Details of this pre-initialised client (i.e. gateway and keys)
gateway_details: GatewayRegistration,
gateway_details: Box<GatewayRegistration>,
managed_keys: KeyManager,
client_keys: ClientKeys,
},
}
impl GatewaySetup {
pub fn try_reuse_connection(init_res: InitialisationResult) -> Result<Self, ClientCoreError> {
todo!()
// if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
// Ok(GatewaySetup::ReuseConnection {
// authenticated_ephemeral_client,
// gateway_details: init_res.gateway_details,
// managed_keys: init_res.managed_keys,
// })
// } else {
// Err(ClientCoreError::NoInitClientPresent)
// }
if let Some(authenticated_ephemeral_client) = init_res.authenticated_ephemeral_client {
Ok(GatewaySetup::ReuseConnection {
authenticated_ephemeral_client,
gateway_details: Box::new(init_res.gateway_registration),
client_keys: init_res.client_keys,
})
} else {
Err(ClientCoreError::NoInitClientPresent)
}
}
pub async fn try_setup<K, D>(
@@ -332,8 +246,7 @@ impl GatewaySetup {
K::StorageError: Send + Sync + 'static,
D::StorageError: Send + Sync + 'static,
{
todo!()
// setup_gateway(self, key_store, details_store).await
setup_gateway(self, key_store, details_store).await
}
pub fn is_must_load(&self) -> bool {
@@ -7,12 +7,9 @@ use crate::helpers::setup_reply_surb_storage_backend;
use crate::storage::wasm_client_traits::WasmClientStorage;
use crate::storage::ClientStorage;
use async_trait::async_trait;
use nym_client_core::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedGatewayDetails,
};
use nym_client_core::client::base_client::storage::MixnetClientStorage;
use nym_client_core::client::key_manager::persistence::KeyStore;
use nym_client_core::client::key_manager::KeyManager;
use nym_client_core::client::key_manager::ClientKeys;
use nym_client_core::client::replies::reply_storage::browser_backend;
use nym_credential_storage::ephemeral_storage::EphemeralStorage as EphemeralCredentialStorage;
use wasm_utils::console_log;
@@ -68,7 +65,7 @@ impl MixnetClientStorage for FullWasmClientStorage {
impl KeyStore for ClientStorage {
type StorageError = WasmCoreError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
console_log!("attempting to load cryptographic keys...");
// all keys implement `ZeroizeOnDrop`, so if we return an Error, whatever was already loaded will be cleared
@@ -77,7 +74,7 @@ impl KeyStore for ClientStorage {
let ack_keypair = self.must_read_ack_key().await?;
let gateway_shared_key = self.must_read_gateway_shared_key().await?;
Ok(KeyManager::from_keys(
Ok(ClientKeys::from_keys(
identity_keypair,
encryption_keypair,
Some(gateway_shared_key),
@@ -85,7 +82,7 @@ impl KeyStore for ClientStorage {
))
}
async fn store_keys(&self, keys: &KeyManager) -> Result<(), Self::StorageError> {
async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
console_log!("attempting to store cryptographic keys...");
self.store_identity_keypair(&keys.identity_keypair())
@@ -7,7 +7,7 @@ use crate::{
state::State,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::client::key_manager::KeyManager;
use nym_client_core::client::key_manager::ClientKeys;
use nym_crypto::asymmetric::identity;
pub async fn get_identity_key(
@@ -23,7 +23,7 @@ pub async fn get_identity_key(
// wtf, why are we loading EVERYTHING to just get identity key??
let key_store = OnDiskKeys::from(paths);
let key_manager =
KeyManager::load_keys(&key_store)
ClientKeys::load_keys(&key_store)
.await
.map_err(|err| BackendError::UnableToLoadKeys {
source: Box::new(err),
@@ -0,0 +1,9 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
todo!()
}
@@ -1,8 +1,5 @@
use nym_client_core::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedGatewayDetails,
};
use nym_sdk::mixnet::{
self, EmptyReplyStorage, EphemeralCredentialStorage, KeyManager, KeyStore, MixnetClientStorage,
self, ClientKeys, EmptyReplyStorage, EphemeralCredentialStorage, KeyStore, MixnetClientStorage,
MixnetMessageSender,
};
use nym_topology::provider_trait::async_trait;
@@ -93,13 +90,13 @@ struct MockKeyStore;
impl KeyStore for MockKeyStore {
type StorageError = MyError;
async fn load_keys(&self) -> Result<KeyManager, Self::StorageError> {
async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
println!("loading stored keys");
Err(MyError)
}
async fn store_keys(&self, _keys: &KeyManager) -> Result<(), Self::StorageError> {
async fn store_keys(&self, _keys: &ClientKeys) -> Result<(), Self::StorageError> {
println!("storing keys");
Ok(())
+1 -1
View File
@@ -12,7 +12,7 @@ async fn main() {
let our_address = client.nym_address();
println!("Our client nym address is: {our_address}");
// Send a message throught the mixnet to ourselves
// Send a message through the mixnet to ourselves
client
.send_plain_message(*our_address, "hello there")
.await
+1 -1
View File
@@ -48,7 +48,7 @@ pub use nym_client_core::{
inbound_messages::InputMessage,
key_manager::{
persistence::{InMemEphemeralKeys, KeyStore, OnDiskKeys},
KeyManager,
ClientKeys,
},
replies::reply_storage::{
fs_backend::Backend as ReplyStorage, CombinedReplyStorage, Empty as EmptyReplyStorage,
-3
View File
@@ -11,9 +11,6 @@ use crate::{Error, Result};
use futures::channel::mpsc;
use futures::StreamExt;
use log::warn;
use nym_client_core::client::base_client::storage::gateway_details::{
GatewayDetailsStore, PersistedGatewayDetails,
};
use nym_client_core::client::base_client::storage::{
Ephemeral, GatewaysDetailsStore, MixnetClientStorage, OnDiskPersistent,
};
@@ -2,9 +2,6 @@ use clap::{CommandFactory, Parser, Subcommand};
use log::{error, trace};
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_bin_common::{bin_info, version_checker};
use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;
@@ -14,9 +14,6 @@ use log::{error, info, trace};
use nym_bin_common::bin_info;
use nym_bin_common::completions::{fig_generate, ArgShell};
use nym_bin_common::version_checker;
use nym_client_core::client::base_client::storage::gateway_details::{
OnDiskGatewayDetails, PersistedGatewayDetails,
};
use nym_client_core::client::key_manager::persistence::OnDiskKeys;
use nym_client_core::config::GatewayEndpointConfig;
use nym_client_core::error::ClientCoreError;