backwards compatibility for mixnodes announced keys

This commit is contained in:
Simon Wicky
2025-05-21 09:54:20 +02:00
committed by Georgio Nicolas
parent a66852c0ae
commit afbdc107bf
4 changed files with 126 additions and 34 deletions
+6 -4
View File
@@ -896,7 +896,7 @@ pub struct HostKeys {
pub current_x25519_sphinx_key: SphinxKey,
#[serde(default)]
pub x25519_noise: Option<VersionedNoiseKey>,
pub x25519_versioned_noise: Option<VersionedNoiseKey>,
}
impl From<nym_node_requests::api::v1::node::models::HostKeys> for HostKeys {
@@ -904,8 +904,6 @@ impl From<nym_node_requests::api::v1::node::models::HostKeys> for HostKeys {
HostKeys {
ed25519: value.ed25519_identity,
x25519: value.x25519_sphinx,
current_x25519_sphinx_key: value.primary_x25519_sphinx_key.into(),
pre_announced_x25519_sphinx_key: value.pre_announced_x25519_sphinx_key.map(Into::into),
x25519_versioned_noise: value.x25519_versioned_noise,
}
}
@@ -1093,7 +1091,11 @@ impl NymNodeDescription {
SemiSkimmedNode {
basic: skimmed_node,
x25519_noise_versioned_key: self.description.host_information.keys.x25519_noise,
x25519_noise_versioned_key: self
.description
.host_information
.keys
.x25519_versioned_noise,
}
}
}
+95 -22
View File
@@ -10,6 +10,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use v1::node::models::LegacyHostInformationV3;
#[cfg(feature = "client")]
pub mod client;
@@ -70,6 +71,15 @@ impl SignedHostInformation {
// TODO: @JS: to remove downgrade support in future release(s)
let legacy_v3 = SignedData {
data: LegacyHostInformationV3::from(self.data.clone()),
signature: self.signature.clone(),
};
if legacy_v3.verify(&self.keys.ed25519_identity) {
return true;
}
// attempt to verify legacy signatures
let legacy_v3 = SignedData {
data: LegacyHostInformationV3::from(self.data.clone()),
@@ -131,7 +141,7 @@ mod tests {
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
let ed22519 = ed25519::KeyPair::new(&mut rng);
let x25519_sphinx = x25519::KeyPair::new(&mut rng);
let x25519_noise = VersionedNoiseKey {
let x25519_versioned_noise = VersionedNoiseKey {
version: NoiseVersion::V1,
x25519_pubkey: *x25519::KeyPair::new(&mut rng).public_key(),
};
@@ -145,11 +155,6 @@ mod tests {
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: *ed22519.public_key(),
x25519_sphinx: *x25519_sphinx.public_key(),
primary_x25519_sphinx_key: SphinxKey {
rotation_id: current_rotation_id,
public_key: *x25519_sphinx.public_key(),
},
pre_announced_x25519_sphinx_key: None,
x25519_versioned_noise: None,
},
};
@@ -164,7 +169,7 @@ mod tests {
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: *ed22519.public_key(),
x25519_sphinx: *x25519_sphinx.public_key(),
x25519_noise: Some(x25519_noise),
x25519_versioned_noise: Some(x25519_versioned_noise),
},
};
@@ -314,6 +319,84 @@ mod tests {
assert!(current_struct_noise.verify_host_information())
}
#[test]
fn dummy_legacy_v3_signed_host_verification() {
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
let ed22519 = ed25519::KeyPair::new(&mut rng);
let x25519_sphinx = x25519::KeyPair::new(&mut rng);
let x25519_noise = x25519::KeyPair::new(&mut rng);
let legacy_info_no_noise = crate::api::v1::node::models::LegacyHostInformationV3 {
ip_address: vec!["1.1.1.1".parse().unwrap()],
hostname: Some("foomp.com".to_string()),
keys: crate::api::v1::node::models::LegacyHostKeysV3 {
ed25519_identity: *ed22519.public_key(),
x25519_sphinx: *x25519_sphinx.public_key(),
x25519_noise: None,
},
};
//technically this variant should never happen
let legacy_info_noise = crate::api::v1::node::models::LegacyHostInformationV3 {
ip_address: vec!["1.1.1.1".parse().unwrap()],
hostname: Some("foomp.com".to_string()),
keys: crate::api::v1::node::models::LegacyHostKeysV3 {
ed25519_identity: *ed22519.public_key(),
x25519_sphinx: *x25519_sphinx.public_key(),
x25519_noise: Some(*x25519_noise.public_key()),
},
};
let host_info_no_noise = crate::api::v1::node::models::HostInformation {
ip_address: legacy_info_no_noise.ip_address.clone(),
hostname: legacy_info_no_noise.hostname.clone(),
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: legacy_info_no_noise.keys.ed25519_identity,
x25519_sphinx: legacy_info_no_noise.keys.x25519_sphinx,
x25519_versioned_noise: None,
},
};
let host_info_noise = crate::api::v1::node::models::HostInformation {
ip_address: legacy_info_noise.ip_address.clone(),
hostname: legacy_info_noise.hostname.clone(),
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: legacy_info_noise.keys.ed25519_identity,
x25519_sphinx: legacy_info_noise.keys.x25519_sphinx,
x25519_versioned_noise: Some(VersionedNoiseKey {
version: NoiseVersion::V1,
x25519_pubkey: legacy_info_noise.keys.x25519_noise.unwrap(),
}),
},
};
// signature on legacy data
let signature_no_noise = SignedData::new(legacy_info_no_noise, ed22519.private_key())
.unwrap()
.signature;
let signature_noise = SignedData::new(legacy_info_noise, ed22519.private_key())
.unwrap()
.signature;
// signed blob with the 'current' structure
let current_struct_no_noise = SignedData {
data: host_info_no_noise,
signature: signature_no_noise,
};
let current_struct_noise = SignedData {
data: host_info_noise,
signature: signature_noise,
};
assert!(!current_struct_no_noise.verify(ed22519.public_key()));
assert!(current_struct_no_noise.verify_host_information());
assert!(!current_struct_noise.verify(ed22519.public_key()));
assert!(current_struct_noise.verify_host_information())
}
#[test]
fn dummy_legacy_v2_signed_host_verification() {
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
@@ -347,12 +430,7 @@ mod tests {
hostname: legacy_info_no_noise.hostname.clone(),
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: legacy_info_no_noise.keys.ed25519_identity.parse().unwrap(),
x25519_sphinx: *x25519_sphinx.public_key(),
primary_x25519_sphinx_key: SphinxKey {
rotation_id: u32::MAX,
public_key: *x25519_sphinx.public_key(),
},
pre_announced_x25519_sphinx_key: None,
x25519_sphinx: legacy_info_no_noise.keys.x25519_sphinx.parse().unwrap(),
x25519_versioned_noise: None,
},
};
@@ -364,7 +442,7 @@ mod tests {
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: legacy_info_noise.keys.ed25519_identity.parse().unwrap(),
x25519_sphinx: legacy_info_noise.keys.x25519_sphinx.parse().unwrap(),
x25519_noise: Some(VersionedNoiseKey {
x25519_versioned_noise: Some(VersionedNoiseKey {
version: NoiseVersion::V1,
x25519_pubkey: legacy_info_noise.keys.x25519_noise.parse().unwrap(),
}),
@@ -411,7 +489,7 @@ mod tests {
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: *ed22519.public_key(),
x25519_sphinx: *x25519_sphinx.public_key(),
x25519_noise: None,
x25519_versioned_noise: None,
},
};
@@ -421,7 +499,7 @@ mod tests {
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: *ed22519.public_key(),
x25519_sphinx: *x25519_sphinx.public_key(),
x25519_noise: Some(VersionedNoiseKey {
x25519_versioned_noise: Some(VersionedNoiseKey {
version: NoiseVersion::V1,
x25519_pubkey: *x25519_noise.public_key(),
}),
@@ -462,12 +540,7 @@ mod tests {
hostname: legacy_info.hostname.clone(),
keys: crate::api::v1::node::models::HostKeys {
ed25519_identity: legacy_info.keys.ed25519.parse().unwrap(),
x25519_sphinx: *x25519_sphinx.public_key(),
primary_x25519_sphinx_key: SphinxKey {
rotation_id: u32::MAX,
public_key: *x25519_sphinx.public_key(),
},
pre_announced_x25519_sphinx_key: None,
x25519_sphinx: legacy_info.keys.x25519.parse().unwrap(),
x25519_versioned_noise: None,
},
};
@@ -3,7 +3,9 @@
use celes::Country;
use nym_crypto::asymmetric::ed25519::{self, serde_helpers::bs58_ed25519_pubkey};
use nym_crypto::asymmetric::x25519::{self, serde_helpers::bs58_x25519_pubkey};
use nym_crypto::asymmetric::x25519::{
self, serde_helpers::bs58_x25519_pubkey, serde_helpers::option_bs58_x25519_pubkey,
};
use nym_noise_keys::VersionedNoiseKey;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -144,7 +146,22 @@ pub struct HostKeys {
/// Base58-encoded x25519 public key of this node used for the noise protocol.
#[serde(default)]
pub x25519_noise: Option<VersionedNoiseKey>,
pub x25519_versioned_noise: Option<VersionedNoiseKey>,
}
#[derive(Serialize)]
pub struct LegacyHostKeysV3 {
#[serde(alias = "ed25519")]
#[serde(with = "bs58_ed25519_pubkey")]
pub ed25519_identity: ed25519::PublicKey,
#[serde(alias = "x25519")]
#[serde(with = "bs58_x25519_pubkey")]
pub x25519_sphinx: x25519::PublicKey,
#[serde(default)]
#[serde(with = "option_bs58_x25519_pubkey")]
pub x25519_noise: Option<x25519::PublicKey>,
}
#[derive(Serialize)]
@@ -164,7 +181,7 @@ impl From<HostKeys> for LegacyHostKeysV3 {
fn from(value: HostKeys) -> Self {
LegacyHostKeysV3 {
ed25519_identity: value.ed25519_identity,
x25519_sphinx: value.primary_x25519_sphinx_key.public_key,
x25519_sphinx: value.x25519_sphinx,
x25519_noise: value.x25519_versioned_noise.map(|k| k.x25519_pubkey),
}
}
@@ -177,7 +194,7 @@ impl From<LegacyHostKeysV3> for LegacyHostKeysV2 {
x25519_sphinx: value.x25519_sphinx.to_base58_string(),
x25519_noise: value
.x25519_noise
.map(|k| k.x25519_pubkey.to_base58_string())
.map(|k| k.to_base58_string())
.unwrap_or_default(),
}
}
+4 -4
View File
@@ -14,13 +14,13 @@ pub mod system_info;
pub(crate) fn sign_host_details(
config: &Config,
x22519_sphinx: &x25519::PublicKey,
x25519_noise: &VersionedNoiseKey,
x25519_versioned_noise: &VersionedNoiseKey,
ed22519_identity: &ed25519::KeyPair,
) -> Result<SignedHostInformation, NymNodeError> {
let x25519_noise = if config.mixnet.debug.unsafe_disable_noise {
let x25519_versioned_noise = if config.mixnet.debug.unsafe_disable_noise {
None
} else {
Some(*x25519_noise)
Some(*x25519_versioned_noise)
};
let host_info = api_requests::v1::node::models::HostInformation {
@@ -29,7 +29,7 @@ pub(crate) fn sign_host_details(
keys: api_requests::v1::node::models::HostKeys {
ed25519_identity: *ed22519_identity.public_key(),
x25519_sphinx: *x22519_sphinx,
x25519_noise,
x25519_versioned_noise,
},
};