Skip to content

Commit ae14180

Browse files
simonwickyjstuczyn
authored andcommitted
backwards compatibility for mixnodes announced keys
1 parent 79ddbc3 commit ae14180

File tree

5 files changed

+158
-22
lines changed

5 files changed

+158
-22
lines changed

nym-api/nym-api-requests/src/models.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -887,15 +887,15 @@ pub struct HostKeys {
887887
pub x25519: x25519::PublicKey,
888888

889889
#[serde(default)]
890-
pub x25519_noise: Option<VersionedNoiseKey>,
890+
pub x25519_versioned_noise: Option<VersionedNoiseKey>,
891891
}
892892

893893
impl From<nym_node_requests::api::v1::node::models::HostKeys> for HostKeys {
894894
fn from(value: nym_node_requests::api::v1::node::models::HostKeys) -> Self {
895895
HostKeys {
896896
ed25519: value.ed25519_identity,
897897
x25519: value.x25519_sphinx,
898-
x25519_noise: value.x25519_noise,
898+
x25519_versioned_noise: value.x25519_versioned_noise,
899899
}
900900
}
901901
}
@@ -1050,7 +1050,11 @@ impl NymNodeDescription {
10501050

10511051
SemiSkimmedNode {
10521052
basic: skimmed_node,
1053-
x25519_noise_versioned_key: self.description.host_information.keys.x25519_noise,
1053+
x25519_noise_versioned_key: self
1054+
.description
1055+
.host_information
1056+
.keys
1057+
.x25519_versioned_noise,
10541058
}
10551059
}
10561060
}

nym-api/src/nym_nodes/handlers/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ async fn nodes_noise(
170170
n.description
171171
.host_information
172172
.keys
173-
.x25519_noise
173+
.x25519_versioned_noise
174174
.map(|noise_key| (noise_key, n))
175175
})
176176
.map(|(noise_key, node)| NoiseDetails {

nym-node/nym-node-requests/src/api/mod.rs

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use schemars::JsonSchema;
88
use serde::{Deserialize, Serialize};
99
use std::fmt::{Display, Formatter};
1010
use std::ops::Deref;
11+
use v1::node::models::LegacyHostInformationV3;
1112

1213
#[cfg(feature = "client")]
1314
pub mod client;
@@ -68,9 +69,18 @@ impl SignedHostInformation {
6869

6970
// TODO: @JS: to remove downgrade support in future release(s)
7071

72+
let legacy_v3 = SignedData {
73+
data: LegacyHostInformationV3::from(self.data.clone()),
74+
signature: self.signature.clone(),
75+
};
76+
77+
if legacy_v3.verify(&self.keys.ed25519_identity) {
78+
return true;
79+
}
80+
7181
// attempt to verify legacy signatures
7282
let legacy_v2 = SignedData {
73-
data: LegacyHostInformationV2::from(self.data.clone()),
83+
data: LegacyHostInformationV2::from(legacy_v3.data),
7484
signature: self.signature.clone(),
7585
};
7686

@@ -119,7 +129,7 @@ mod tests {
119129
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
120130
let ed22519 = ed25519::KeyPair::new(&mut rng);
121131
let x25519_sphinx = x25519::KeyPair::new(&mut rng);
122-
let x25519_noise = VersionedNoiseKey {
132+
let x25519_versioned_noise = VersionedNoiseKey {
123133
version: NoiseVersion::V1,
124134
x25519_pubkey: *x25519::KeyPair::new(&mut rng).public_key(),
125135
};
@@ -130,7 +140,7 @@ mod tests {
130140
keys: crate::api::v1::node::models::HostKeys {
131141
ed25519_identity: *ed22519.public_key(),
132142
x25519_sphinx: *x25519_sphinx.public_key(),
133-
x25519_noise: None,
143+
x25519_versioned_noise: None,
134144
},
135145
};
136146

@@ -144,7 +154,7 @@ mod tests {
144154
keys: crate::api::v1::node::models::HostKeys {
145155
ed25519_identity: *ed22519.public_key(),
146156
x25519_sphinx: *x25519_sphinx.public_key(),
147-
x25519_noise: Some(x25519_noise),
157+
x25519_versioned_noise: Some(x25519_versioned_noise),
148158
},
149159
};
150160

@@ -154,6 +164,84 @@ mod tests {
154164
assert!(signed_info.verify_host_information());
155165
}
156166

167+
#[test]
168+
fn dummy_legacy_v3_signed_host_verification() {
169+
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
170+
let ed22519 = ed25519::KeyPair::new(&mut rng);
171+
let x25519_sphinx = x25519::KeyPair::new(&mut rng);
172+
let x25519_noise = x25519::KeyPair::new(&mut rng);
173+
174+
let legacy_info_no_noise = crate::api::v1::node::models::LegacyHostInformationV3 {
175+
ip_address: vec!["1.1.1.1".parse().unwrap()],
176+
hostname: Some("foomp.com".to_string()),
177+
keys: crate::api::v1::node::models::LegacyHostKeysV3 {
178+
ed25519_identity: *ed22519.public_key(),
179+
x25519_sphinx: *x25519_sphinx.public_key(),
180+
x25519_noise: None,
181+
},
182+
};
183+
184+
//technically this variant should never happen
185+
let legacy_info_noise = crate::api::v1::node::models::LegacyHostInformationV3 {
186+
ip_address: vec!["1.1.1.1".parse().unwrap()],
187+
hostname: Some("foomp.com".to_string()),
188+
keys: crate::api::v1::node::models::LegacyHostKeysV3 {
189+
ed25519_identity: *ed22519.public_key(),
190+
x25519_sphinx: *x25519_sphinx.public_key(),
191+
x25519_noise: Some(*x25519_noise.public_key()),
192+
},
193+
};
194+
195+
let host_info_no_noise = crate::api::v1::node::models::HostInformation {
196+
ip_address: legacy_info_no_noise.ip_address.clone(),
197+
hostname: legacy_info_no_noise.hostname.clone(),
198+
keys: crate::api::v1::node::models::HostKeys {
199+
ed25519_identity: legacy_info_no_noise.keys.ed25519_identity,
200+
x25519_sphinx: legacy_info_no_noise.keys.x25519_sphinx,
201+
x25519_versioned_noise: None,
202+
},
203+
};
204+
205+
let host_info_noise = crate::api::v1::node::models::HostInformation {
206+
ip_address: legacy_info_noise.ip_address.clone(),
207+
hostname: legacy_info_noise.hostname.clone(),
208+
keys: crate::api::v1::node::models::HostKeys {
209+
ed25519_identity: legacy_info_noise.keys.ed25519_identity,
210+
x25519_sphinx: legacy_info_noise.keys.x25519_sphinx,
211+
x25519_versioned_noise: Some(VersionedNoiseKey {
212+
version: NoiseVersion::V1,
213+
x25519_pubkey: legacy_info_noise.keys.x25519_noise.unwrap(),
214+
}),
215+
},
216+
};
217+
218+
// signature on legacy data
219+
let signature_no_noise = SignedData::new(legacy_info_no_noise, ed22519.private_key())
220+
.unwrap()
221+
.signature;
222+
223+
let signature_noise = SignedData::new(legacy_info_noise, ed22519.private_key())
224+
.unwrap()
225+
.signature;
226+
227+
// signed blob with the 'current' structure
228+
let current_struct_no_noise = SignedData {
229+
data: host_info_no_noise,
230+
signature: signature_no_noise,
231+
};
232+
233+
let current_struct_noise = SignedData {
234+
data: host_info_noise,
235+
signature: signature_noise,
236+
};
237+
238+
assert!(!current_struct_no_noise.verify(ed22519.public_key()));
239+
assert!(current_struct_no_noise.verify_host_information());
240+
241+
assert!(!current_struct_noise.verify(ed22519.public_key()));
242+
assert!(current_struct_noise.verify_host_information())
243+
}
244+
157245
#[test]
158246
fn dummy_legacy_v2_signed_host_verification() {
159247
let mut rng = rand_chacha::ChaCha20Rng::from_seed([0u8; 32]);
@@ -187,7 +275,7 @@ mod tests {
187275
keys: crate::api::v1::node::models::HostKeys {
188276
ed25519_identity: legacy_info_no_noise.keys.ed25519_identity.parse().unwrap(),
189277
x25519_sphinx: legacy_info_no_noise.keys.x25519_sphinx.parse().unwrap(),
190-
x25519_noise: None,
278+
x25519_versioned_noise: None,
191279
},
192280
};
193281

@@ -197,7 +285,7 @@ mod tests {
197285
keys: crate::api::v1::node::models::HostKeys {
198286
ed25519_identity: legacy_info_noise.keys.ed25519_identity.parse().unwrap(),
199287
x25519_sphinx: legacy_info_noise.keys.x25519_sphinx.parse().unwrap(),
200-
x25519_noise: Some(VersionedNoiseKey {
288+
x25519_versioned_noise: Some(VersionedNoiseKey {
201289
version: NoiseVersion::V1,
202290
x25519_pubkey: legacy_info_noise.keys.x25519_noise.parse().unwrap(),
203291
}),
@@ -244,7 +332,7 @@ mod tests {
244332
keys: crate::api::v1::node::models::HostKeys {
245333
ed25519_identity: *ed22519.public_key(),
246334
x25519_sphinx: *x25519_sphinx.public_key(),
247-
x25519_noise: None,
335+
x25519_versioned_noise: None,
248336
},
249337
};
250338

@@ -254,7 +342,7 @@ mod tests {
254342
keys: crate::api::v1::node::models::HostKeys {
255343
ed25519_identity: *ed22519.public_key(),
256344
x25519_sphinx: *x25519_sphinx.public_key(),
257-
x25519_noise: Some(VersionedNoiseKey {
345+
x25519_versioned_noise: Some(VersionedNoiseKey {
258346
version: NoiseVersion::V1,
259347
x25519_pubkey: *x25519_noise.public_key(),
260348
}),
@@ -295,7 +383,7 @@ mod tests {
295383
keys: crate::api::v1::node::models::HostKeys {
296384
ed25519_identity: legacy_info.keys.ed25519.parse().unwrap(),
297385
x25519_sphinx: legacy_info.keys.x25519.parse().unwrap(),
298-
x25519_noise: None,
386+
x25519_versioned_noise: None,
299387
},
300388
};
301389

nym-node/nym-node-requests/src/api/v1/node/models.rs

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
use celes::Country;
55
use nym_crypto::asymmetric::ed25519::{self, serde_helpers::bs58_ed25519_pubkey};
6-
use nym_crypto::asymmetric::x25519::{self, serde_helpers::bs58_x25519_pubkey};
6+
use nym_crypto::asymmetric::x25519::{
7+
self, serde_helpers::bs58_x25519_pubkey, serde_helpers::option_bs58_x25519_pubkey,
8+
};
79
use nym_noise_keys::VersionedNoiseKey;
810
use schemars::JsonSchema;
911
use serde::{Deserialize, Serialize};
@@ -68,6 +70,13 @@ impl HostInformation {
6870
}
6971
}
7072

73+
#[derive(Serialize)]
74+
pub struct LegacyHostInformationV3 {
75+
pub ip_address: Vec<IpAddr>,
76+
pub hostname: Option<String>,
77+
pub keys: LegacyHostKeysV3,
78+
}
79+
7180
#[derive(Serialize)]
7281
pub struct LegacyHostInformationV2 {
7382
pub ip_address: Vec<IpAddr>,
@@ -82,8 +91,18 @@ pub struct LegacyHostInformation {
8291
pub keys: LegacyHostKeys,
8392
}
8493

85-
impl From<HostInformation> for LegacyHostInformationV2 {
94+
impl From<HostInformation> for LegacyHostInformationV3 {
8695
fn from(value: HostInformation) -> Self {
96+
LegacyHostInformationV3 {
97+
ip_address: value.ip_address,
98+
hostname: value.hostname,
99+
keys: value.keys.into(),
100+
}
101+
}
102+
}
103+
104+
impl From<LegacyHostInformationV3> for LegacyHostInformationV2 {
105+
fn from(value: LegacyHostInformationV3) -> Self {
87106
LegacyHostInformationV2 {
88107
ip_address: value.ip_address,
89108
hostname: value.hostname,
@@ -122,7 +141,22 @@ pub struct HostKeys {
122141

123142
/// Base58-encoded x25519 public key of this node used for the noise protocol.
124143
#[serde(default)]
125-
pub x25519_noise: Option<VersionedNoiseKey>,
144+
pub x25519_versioned_noise: Option<VersionedNoiseKey>,
145+
}
146+
147+
#[derive(Serialize)]
148+
pub struct LegacyHostKeysV3 {
149+
#[serde(alias = "ed25519")]
150+
#[serde(with = "bs58_ed25519_pubkey")]
151+
pub ed25519_identity: ed25519::PublicKey,
152+
153+
#[serde(alias = "x25519")]
154+
#[serde(with = "bs58_x25519_pubkey")]
155+
pub x25519_sphinx: x25519::PublicKey,
156+
157+
#[serde(default)]
158+
#[serde(with = "option_bs58_x25519_pubkey")]
159+
pub x25519_noise: Option<x25519::PublicKey>,
126160
}
127161

128162
#[derive(Serialize)]
@@ -138,14 +172,24 @@ pub struct LegacyHostKeys {
138172
pub x25519: String,
139173
}
140174

141-
impl From<HostKeys> for LegacyHostKeysV2 {
175+
impl From<HostKeys> for LegacyHostKeysV3 {
142176
fn from(value: HostKeys) -> Self {
177+
LegacyHostKeysV3 {
178+
ed25519_identity: value.ed25519_identity,
179+
x25519_sphinx: value.x25519_sphinx,
180+
x25519_noise: value.x25519_versioned_noise.map(|k| k.x25519_pubkey),
181+
}
182+
}
183+
}
184+
185+
impl From<LegacyHostKeysV3> for LegacyHostKeysV2 {
186+
fn from(value: LegacyHostKeysV3) -> Self {
143187
LegacyHostKeysV2 {
144188
ed25519_identity: value.ed25519_identity.to_base58_string(),
145189
x25519_sphinx: value.x25519_sphinx.to_base58_string(),
146190
x25519_noise: value
147191
.x25519_noise
148-
.map(|k| k.x25519_pubkey.to_base58_string())
192+
.map(|k| k.to_base58_string())
149193
.unwrap_or_default(),
150194
}
151195
}

nym-node/src/node/http/helpers/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ pub mod system_info;
1414
pub(crate) fn sign_host_details(
1515
config: &Config,
1616
x22519_sphinx: &x25519::PublicKey,
17-
x25519_noise: &VersionedNoiseKey,
17+
x25519_versioned_noise: &VersionedNoiseKey,
1818
ed22519_identity: &ed25519::KeyPair,
1919
) -> Result<SignedHostInformation, NymNodeError> {
20-
let x25519_noise = if config.mixnet.debug.unsafe_disable_noise {
20+
let x25519_versioned_noise = if config.mixnet.debug.unsafe_disable_noise {
2121
None
2222
} else {
23-
Some(*x25519_noise)
23+
Some(*x25519_versioned_noise)
2424
};
2525

2626
let host_info = api_requests::v1::node::models::HostInformation {
@@ -29,7 +29,7 @@ pub(crate) fn sign_host_details(
2929
keys: api_requests::v1::node::models::HostKeys {
3030
ed25519_identity: *ed22519_identity.public_key(),
3131
x25519_sphinx: *x22519_sphinx,
32-
x25519_noise,
32+
x25519_versioned_noise,
3333
},
3434
};
3535

0 commit comments

Comments
 (0)