Skip to content

Commit

Permalink
Finalize seedable PR
Browse files Browse the repository at this point in the history
  • Loading branch information
durch committed Feb 13, 2025
1 parent 8c3d1cc commit 858bce5
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 4 deletions.
22 changes: 21 additions & 1 deletion common/client-core/src/client/base_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use nym_client_core_config_types::ForgetMe;
use nym_client_core_gateways_storage::{GatewayDetails, GatewaysDetailsStore};
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_crypto::asymmetric::{encryption, identity};
use nym_crypto::hkdf::DerivationMaterial;
use nym_gateway_client::client::config::GatewayClientConfig;
use nym_gateway_client::{
AcknowledgementReceiver, GatewayClient, GatewayConfig, MixnetMessageReceiver, PacketRouter,
Expand Down Expand Up @@ -192,6 +193,8 @@ pub struct BaseClientBuilder<C, S: MixnetClientStorage> {

#[cfg(unix)]
connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,

derivation_material: Option<DerivationMaterial>,
}

impl<C, S> BaseClientBuilder<C, S>
Expand All @@ -216,9 +219,19 @@ where
setup_method: GatewaySetup::MustLoad { gateway_id: None },
#[cfg(unix)]
connection_fd_callback: None,
derivation_material: None,
}
}

#[must_use]
pub fn with_derivation_material(
mut self,
derivation_material: Option<DerivationMaterial>,
) -> Self {
self.derivation_material = derivation_material;
self
}

#[must_use]
pub fn with_forget_me(mut self, forget_me: &ForgetMe) -> Self {
self.config.debug.forget_me = *forget_me;
Expand Down Expand Up @@ -684,6 +697,7 @@ where
setup_method: GatewaySetup,
key_store: &S::KeyStore,
details_store: &S::GatewaysDetailsStore,
derivation_material: Option<DerivationMaterial>,
) -> Result<InitialisationResult, ClientCoreError>
where
<S::KeyStore as KeyStore>::StorageError: Sync + Send,
Expand All @@ -693,7 +707,12 @@ where
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);
let keys = if let Some(derivation_material) = derivation_material {
ClientKeys::from_master_key(&mut rng, &derivation_material)
.map_err(|_| ClientCoreError::HkdfDerivationError {})?
} else {
ClientKeys::generate_new(&mut rng)
};
store_client_keys(keys, key_store).await?;
}

Expand All @@ -715,6 +734,7 @@ where
self.setup_method,
self.client_store.key_store(),
self.client_store.gateway_details_store(),
self.derivation_material,
)
.await?;

Expand Down
3 changes: 3 additions & 0 deletions common/client-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ pub enum ClientCoreError {
"fresh registration with gateway {gateway_id} somehow requires an additional key upgrade!"
)]
UnexpectedKeyUpgrade { gateway_id: String },

#[error("failed to derive keys from master key")]
HkdfDerivationError {},
}

/// Set of messages that the client can send to listeners via the task manager
Expand Down
2 changes: 1 addition & 1 deletion common/crypto/src/asymmetric/identity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl KeyPair {
KeyPair {
private_key: PrivateKey(ed25519_signing_key.to_bytes()),
public_key: PublicKey(ed25519_signing_key.verifying_key()),
index: index,
index,
}
}

Expand Down
33 changes: 33 additions & 0 deletions common/crypto/src/hkdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ where
Ok(okm)
}

/// `DerivationMaterial` encapsulates parameters for deterministic key derivation using
/// HKDF (SHA-512).
///
/// It consists of:
/// - A master key (`master_key`): the base secret.
/// - An index (`index`): ensures unique derivations.
/// - A salt (`salt`): adds additional uniqueness.
///
/// Use the `derive_secret()` method to generate a 32-byte secret. To prepare for a new derivation,
/// call the `next()` method, which increments the index. **It is the caller's responsibility to
/// track and persist the derivation index if keys need to be rederived.**
///
/// # Example
///
/// ```rust
/// let master_key = [0u8; 32]; // your secret master key
/// let salt = "unique-salt-value".to_string();
/// let material = DerivationMaterial::new(master_key, 0, salt);
///
/// // Derive a secret
/// let secret = material.derive_secret().expect("Failed to derive secret");
///
/// // Prepare for the next derivation
/// let next_material = material.next();
/// ```
pub struct DerivationMaterial {
master_key: [u8; 32],
index: u32,
Expand All @@ -58,4 +83,12 @@ impl DerivationMaterial {
salt,
}
}

pub fn next(&self) -> Self {
Self {
master_key: self.master_key,
index: self.index + 1,
salt: self.salt.clone(),
}
}
}
23 changes: 21 additions & 2 deletions sdk/rust/nym-sdk/src/mixnet/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use nym_client_core::init::helpers::gateways_for_init;
use nym_client_core::init::setup_gateway;
use nym_client_core::init::types::{GatewaySelectionSpecification, GatewaySetup};
use nym_credentials_interface::TicketType;
use nym_crypto::hkdf::DerivationMaterial;
use nym_socks5_client_core::config::Socks5;
use nym_task::{TaskClient, TaskHandle, TaskStatus};
use nym_topology::provider_trait::TopologyProvider;
Expand Down Expand Up @@ -64,6 +65,7 @@ pub struct MixnetClientBuilder<S: MixnetClientStorage = Ephemeral> {

storage: S,
forget_me: ForgetMe,
derivation_material: Option<DerivationMaterial>,
}

impl MixnetClientBuilder<Ephemeral> {
Expand Down Expand Up @@ -101,6 +103,7 @@ impl MixnetClientBuilder<OnDiskPersistent> {
#[cfg(unix)]
connection_fd_callback: None,
forget_me: Default::default(),
derivation_material: None,
})
}
}
Expand Down Expand Up @@ -133,6 +136,7 @@ where
gateway_endpoint_config_path: None,
storage,
forget_me: Default::default(),
derivation_material: None,
}
}

Expand All @@ -154,9 +158,16 @@ where
gateway_endpoint_config_path: self.gateway_endpoint_config_path,
storage,
forget_me: self.forget_me,
derivation_material: self.derivation_material,
}
}

#[must_use]
pub fn with_derivation_material(mut self, derivation_material: DerivationMaterial) -> Self {
self.derivation_material = Some(derivation_material);
self
}

/// Change the underlying storage of this builder to use default implementation of on-disk disk_persistence.
#[must_use]
pub fn set_default_storage(
Expand Down Expand Up @@ -312,6 +323,7 @@ where
client.connection_fd_callback = self.connection_fd_callback;
}
client.forget_me = self.forget_me;
client.derivation_material = self.derivation_material;
Ok(client)
}
}
Expand Down Expand Up @@ -366,6 +378,9 @@ where
connection_fd_callback: Option<Arc<dyn Fn(std::os::fd::RawFd) + Send + Sync>>,

forget_me: ForgetMe,

/// The derivation material to use for the client keys, its up to the caller to save this for rederivation later
derivation_material: Option<DerivationMaterial>,
}

impl<S> DisconnectedMixnetClient<S>
Expand Down Expand Up @@ -403,6 +418,8 @@ where
None
};

let forget_me = config.debug_config.forget_me;

Ok(DisconnectedMixnetClient {
config,
socks5_config,
Expand All @@ -417,7 +434,8 @@ where
user_agent: None,
#[cfg(unix)]
connection_fd_callback: None,
forget_me: Default::default(),
forget_me,
derivation_material: None,
})
}

Expand Down Expand Up @@ -641,7 +659,8 @@ where
let mut base_builder: BaseClientBuilder<_, _> =
BaseClientBuilder::new(base_config, self.storage, self.dkg_query_client)
.with_wait_for_gateway(self.wait_for_gateway)
.with_forget_me(&self.forget_me);
.with_forget_me(&self.forget_me)
.with_derivation_material(self.derivation_material);

if let Some(user_agent) = self.user_agent {
base_builder = base_builder.with_user_agent(user_agent);
Expand Down

0 comments on commit 858bce5

Please sign in to comment.