Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2025 - Nym Technologies SA <[email protected]>
* SPDX-License-Identifier: GPL-3.0-only
*/

-- make aes256gcm column non-nullable and drop any gateways that still use the legacy keys
-- (since they'd be unusable after this change)
CREATE TABLE remote_gateway_details_tmp
(
gateway_id_bs58 TEXT NOT NULL UNIQUE PRIMARY KEY REFERENCES registered_gateway (gateway_id_bs58),
derived_aes256_gcm_siv_key BLOB NOT NULL,
gateway_owner_address TEXT,
gateway_listener TEXT NOT NULL
);

INSERT INTO remote_gateway_details_tmp (gateway_id_bs58, derived_aes256_gcm_siv_key, gateway_owner_address,
gateway_listener)
SELECT gateway_id_bs58, derived_aes256_gcm_siv_key, gateway_owner_address, gateway_listener
FROM remote_gateway_details
WHERE derived_aes256_gcm_siv_key IS NOT NULL;

DROP TABLE remote_gateway_details;
ALTER TABLE remote_gateway_details_tmp
RENAME TO remote_gateway_details;
Original file line number Diff line number Diff line change
Expand Up @@ -156,48 +156,26 @@ impl StorageManager {

pub(crate) async fn set_remote_gateway_details(
&self,
remote: &RawRemoteGatewayDetails,
gateway_id_bs58: String,
derived_aes256_gcm_siv_key: &[u8],
gateway_owner_address: Option<String>,
gateway_listener: String,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
INSERT INTO remote_gateway_details(gateway_id_bs58, derived_aes128_ctr_blake3_hmac_keys_bs58, derived_aes256_gcm_siv_key, gateway_owner_address, gateway_listener)
VALUES (?, ?, ?, ?, ?)
INSERT INTO remote_gateway_details(gateway_id_bs58, derived_aes256_gcm_siv_key, gateway_owner_address, gateway_listener)
VALUES (?, ?, ?, ?)
"#,
remote.gateway_id_bs58,
remote.derived_aes128_ctr_blake3_hmac_keys_bs58,
remote.derived_aes256_gcm_siv_key,
remote.gateway_owner_address,
remote.gateway_listener,
gateway_id_bs58,
derived_aes256_gcm_siv_key,
gateway_owner_address,
gateway_listener,
)
.execute(&self.connection_pool)
.await?;
Ok(())
}

pub(crate) async fn update_remote_gateway_key(
&self,
gateway_id_bs58: &str,
derived_aes128_ctr_blake3_hmac_keys_bs58: Option<&str>,
derived_aes256_gcm_siv_key: Option<&[u8]>,
) -> Result<(), sqlx::Error> {
sqlx::query!(
r#"
UPDATE remote_gateway_details
SET
derived_aes128_ctr_blake3_hmac_keys_bs58 = ?,
derived_aes256_gcm_siv_key = ?
WHERE gateway_id_bs58 = ?
"#,
derived_aes128_ctr_blake3_hmac_keys_bs58,
derived_aes256_gcm_siv_key,
gateway_id_bs58
)
.execute(&self.connection_pool)
.await?;

Ok(())
}

pub(crate) async fn remove_remote_gateway_details(
&self,
gateway_id: &str,
Expand Down
27 changes: 9 additions & 18 deletions common/client-core/gateways-storage/src/backend/fs_backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::{
use async_trait::async_trait;
use manager::StorageManager;
use nym_crypto::asymmetric::ed25519;
use nym_gateway_requests::SharedSymmetricKey;
use std::path::Path;

pub mod error;
Expand Down Expand Up @@ -119,9 +118,16 @@ impl GatewaysDetailsStore for OnDiskGatewaysDetails {

match &details.details {
GatewayDetails::Remote(remote_details) => {
let raw_details = remote_details.into();
self.manager
.set_remote_gateway_details(&raw_details)
.set_remote_gateway_details(
remote_details.gateway_id.to_base58_string(),
remote_details.shared_key.as_bytes(),
remote_details
.gateway_owner_address
.as_ref()
.map(|o| o.to_string()),
remote_details.gateway_listener.to_string(),
)
.await?;
}
GatewayDetails::Custom(custom_details) => {
Expand All @@ -134,21 +140,6 @@ impl GatewaysDetailsStore for OnDiskGatewaysDetails {
Ok(())
}

async fn upgrade_stored_remote_gateway_key(
&self,
gateway_id: ed25519::PublicKey,
updated_key: &SharedSymmetricKey,
) -> Result<(), Self::StorageError> {
self.manager
.update_remote_gateway_key(
&gateway_id.to_base58_string(),
None,
Some(updated_key.as_bytes()),
)
.await?;
Ok(())
}

// ideally all of those should be run under a storage tx to ensure storage consistency,
// but at that point it's fine
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
Expand Down
27 changes: 1 addition & 26 deletions common/client-core/gateways-storage/src/backend/mem_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
// SPDX-License-Identifier: Apache-2.0

use crate::types::{ActiveGateway, GatewayRegistration};
use crate::{BadGateway, GatewayDetails, GatewaysDetailsStore};
use crate::{BadGateway, GatewaysDetailsStore};
use async_trait::async_trait;
use nym_crypto::asymmetric::ed25519::PublicKey;
use nym_gateway_requests::{SharedGatewayKey, SharedSymmetricKey};
use std::collections::HashMap;
use std::sync::Arc;
use thiserror::Error;
Expand Down Expand Up @@ -96,29 +94,6 @@ impl GatewaysDetailsStore for InMemGatewaysDetails {
Ok(())
}

async fn upgrade_stored_remote_gateway_key(
&self,
gateway_id: PublicKey,
updated_key: &SharedSymmetricKey,
) -> Result<(), Self::StorageError> {
let mut guard = self.inner.write().await;

#[allow(clippy::unwrap_used)]
if let Some(target) = guard.gateways.get_mut(&gateway_id.to_string()) {
let GatewayDetails::Remote(details) = &mut target.details else {
return Ok(());
};
assert_eq!(Arc::strong_count(&details.shared_key), 1);

// eh. that's nasty, but it's only ever used for ephemeral clients so should be fine for now...
details.shared_key = Arc::new(SharedGatewayKey::Current(
SharedSymmetricKey::try_from_bytes(updated_key.as_bytes()).unwrap(),
))
}

Ok(())
}

async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError> {
let mut guard = self.inner.write().await;
if let Some(active) = guard.active_gateway.as_ref() {
Expand Down
7 changes: 0 additions & 7 deletions common/client-core/gateways-storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use async_trait::async_trait;
use nym_crypto::asymmetric::identity;
use nym_gateway_requests::SharedSymmetricKey;
use std::error::Error;

pub mod backend;
Expand Down Expand Up @@ -62,12 +61,6 @@ pub trait GatewaysDetailsStore {
details: &GatewayRegistration,
) -> Result<(), Self::StorageError>;

async fn upgrade_stored_remote_gateway_key(
&self,
gateway_id: identity::PublicKey,
updated_key: &SharedSymmetricKey,
) -> Result<(), Self::StorageError>;

/// Remove given gateway details from the underlying store.
async fn remove_gateway_details(&self, gateway_id: &str) -> Result<(), Self::StorageError>;
}
64 changes: 10 additions & 54 deletions common/client-core/gateways-storage/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
use crate::BadGateway;
use cosmrs::AccountId;
use nym_crypto::asymmetric::identity;
use nym_gateway_requests::shared_key::{LegacySharedKeys, SharedGatewayKey, SharedSymmetricKey};
use nym_gateway_requests::shared_key::SharedSymmetricKey;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
use time::OffsetDateTime;
Expand Down Expand Up @@ -65,7 +64,7 @@ impl From<GatewayDetails> for GatewayRegistration {
impl GatewayDetails {
pub fn new_remote(
gateway_id: identity::PublicKey,
shared_key: Arc<SharedGatewayKey>,
shared_key: Arc<SharedSymmetricKey>,
gateway_owner_address: Option<AccountId>,
gateway_listener: Url,
) -> Self {
Expand All @@ -88,7 +87,7 @@ impl GatewayDetails {
}
}

pub fn shared_key(&self) -> Option<&SharedGatewayKey> {
pub fn shared_key(&self) -> Option<&SharedSymmetricKey> {
match self {
GatewayDetails::Remote(details) => Some(&details.shared_key),
GatewayDetails::Custom(_) => None,
Expand Down Expand Up @@ -168,8 +167,7 @@ pub struct RegisteredGateway {
#[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))]
pub struct RawRemoteGatewayDetails {
pub gateway_id_bs58: String,
pub derived_aes128_ctr_blake3_hmac_keys_bs58: Option<String>,
pub derived_aes256_gcm_siv_key: Option<Vec<u8>>,
pub derived_aes256_gcm_siv_key: Vec<u8>,
pub gateway_owner_address: Option<String>,
pub gateway_listener: String,
}
Expand All @@ -186,35 +184,11 @@ impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
}
})?;

let shared_key =
match (
&value.derived_aes256_gcm_siv_key,
&value.derived_aes128_ctr_blake3_hmac_keys_bs58,
) {
(None, None) => {
return Err(BadGateway::MissingSharedKey {
gateway_id: value.gateway_id_bs58.clone(),
})
}
(Some(aes256gcm_siv), _) => {
let current_key =
SharedSymmetricKey::try_from_bytes(aes256gcm_siv).map_err(|source| {
BadGateway::MalformedSharedKeys {
gateway_id: value.gateway_id_bs58.clone(),
source,
}
})?;
SharedGatewayKey::Current(current_key)
}
(None, Some(aes128ctr_hmac)) => {
let legacy_key = LegacySharedKeys::try_from_base58_string(aes128ctr_hmac)
.map_err(|source| BadGateway::MalformedSharedKeys {
gateway_id: value.gateway_id_bs58.clone(),
source,
})?;
SharedGatewayKey::Legacy(legacy_key)
}
};
let shared_key = SharedSymmetricKey::try_from_bytes(&value.derived_aes256_gcm_siv_key)
.map_err(|source| BadGateway::MalformedSharedKeys {
gateway_id: value.gateway_id_bs58.clone(),
source,
})?;

let gateway_owner_address = value
.gateway_owner_address
Expand Down Expand Up @@ -247,29 +221,11 @@ impl TryFrom<RawRemoteGatewayDetails> for RemoteGatewayDetails {
}
}

impl<'a> From<&'a RemoteGatewayDetails> for RawRemoteGatewayDetails {
fn from(value: &'a RemoteGatewayDetails) -> Self {
let (derived_aes128_ctr_blake3_hmac_keys_bs58, derived_aes256_gcm_siv_key) =
match value.shared_key.deref() {
SharedGatewayKey::Current(key) => (None, Some(key.to_bytes())),
SharedGatewayKey::Legacy(key) => (Some(key.to_base58_string()), None),
};

RawRemoteGatewayDetails {
gateway_id_bs58: value.gateway_id.to_base58_string(),
derived_aes128_ctr_blake3_hmac_keys_bs58,
derived_aes256_gcm_siv_key,
gateway_owner_address: value.gateway_owner_address.as_ref().map(|o| o.to_string()),
gateway_listener: value.gateway_listener.to_string(),
}
}
}

#[derive(Debug, Clone)]
pub struct RemoteGatewayDetails {
pub gateway_id: identity::PublicKey,

pub shared_key: Arc<SharedGatewayKey>,
pub shared_key: Arc<SharedSymmetricKey>,

pub gateway_owner_address: Option<AccountId>,

Expand Down
29 changes: 2 additions & 27 deletions common/client-core/src/client/base_client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ where
config: &Config,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
details_store: &S::GatewaysDetailsStore,
packet_router: PacketRouter,
stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
Expand All @@ -403,7 +402,6 @@ where
where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{
let managed_keys = initialisation_result.client_keys;
let GatewayDetails::Remote(details) = initialisation_result.gateway_registration.details
Expand Down Expand Up @@ -458,31 +456,13 @@ where
// we need to:
// - perform handshake (reg or auth)
// - check for key upgrade
// - maybe perform another upgrade handshake
// - check for bandwidth
// - start background tasks
let auth_res = gateway_client
let _auth_res = gateway_client
.perform_initial_authentication()
.await
.map_err(gateway_failure)?;

if auth_res.requires_key_upgrade {
// drop the shared_key arc because we don't need it and we can't hold it for the purposes of upgrade
drop(auth_res);

let updated_key = gateway_client
.upgrade_key_authenticated()
.await
.map_err(gateway_failure)?;

details_store
.upgrade_stored_remote_gateway_key(gateway_client.gateway_identity(), &updated_key)
.await.map_err(|err| {
error!("failed to store upgraded gateway key! this connection might be forever broken now: {err}");
ClientCoreError::GatewaysDetailsStoreError { source: Box::new(err) }
})?
}

gateway_client
.claim_initial_bandwidth()
.await
Expand All @@ -501,7 +481,6 @@ where
config: &Config,
initialisation_result: InitialisationResult,
bandwidth_controller: Option<BandwidthController<C, S::CredentialStore>>,
details_store: &S::GatewaysDetailsStore,
packet_router: PacketRouter,
stats_reporter: ClientStatsSender,
#[cfg(unix)] connection_fd_callback: Option<Arc<dyn Fn(RawFd) + Send + Sync>>,
Expand All @@ -510,7 +489,6 @@ where
where
<S::KeyStore as KeyStore>::StorageError: Send + Sync + 'static,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync + 'static,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
{
// if we have setup custom gateway sender and persisted details agree with it, return it
if let Some(mut custom_gateway_transceiver) = custom_gateway_transceiver {
Expand All @@ -533,7 +511,6 @@ where
config,
initialisation_result,
bandwidth_controller,
details_store,
packet_router,
stats_reporter,
#[cfg(unix)]
Expand Down Expand Up @@ -744,8 +721,7 @@ where
)
.await?;

let (reply_storage_backend, credential_store, details_store) =
self.client_store.into_runtime_stores();
let (reply_storage_backend, credential_store, _) = self.client_store.into_runtime_stores();

// channels for inter-component communication
// TODO: make the channels be internally created by the relevant components
Expand Down Expand Up @@ -826,7 +802,6 @@ where
&self.config,
init_res,
bandwidth_controller,
&details_store,
gateway_packet_router,
stats_reporter.clone(),
#[cfg(unix)]
Expand Down
Loading
Loading