From 901557ad04adbc6795757ac5fa3bda067f2be9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Wed, 25 Feb 2026 14:28:32 +0000 Subject: [PATCH] generate fresh x25519, mlkem768 and mceliece keys on config migration --- Cargo.lock | 162 +-- common/nym-kkt/Cargo.toml | 1 + common/nym-kkt/src/key_utils.rs | 1 - common/nym-kkt/src/keys.rs | 325 +++++- gateway/src/node/lp_listener/mod.rs | 15 - nym-node/Cargo.toml | 1 + nym-node/src/config/old_configs/mod.rs | 2 + .../src/config/old_configs/old_config_v11.rs | 415 +------ .../src/config/old_configs/old_config_v12.rs | 1001 +++++++++++++++++ nym-node/src/config/persistence.rs | 49 + nym-node/src/config/upgrade_helpers.rs | 3 +- nym-node/src/error.rs | 4 + nym-node/src/node/helpers.rs | 83 ++ 13 files changed, 1581 insertions(+), 481 deletions(-) create mode 100644 nym-node/src/config/old_configs/old_config_v12.rs diff --git a/Cargo.lock b/Cargo.lock index 61caa29dd0..7825a56079 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1351,10 +1351,11 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "classic-mceliece-rust" -version = "3.2.0" -source = "git+https://github.com/georgio/classic-mceliece-rust#f2f27048b621df103bbe64369a18174ffec04ae1" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62a9b6d27e553269a76625911aa8cf6afaa8659f1b0c85b410cb5f51a87183d9" dependencies = [ - "rand 0.9.2", + "rand 0.8.5", "sha3", "zeroize", ] @@ -1563,11 +1564,11 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-models" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.5" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "hax-lib", - "pastey", + "pastey 0.2.1", "rand 0.9.2", ] @@ -3307,9 +3308,9 @@ dependencies = [ [[package]] name = "hax-lib" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86" +checksum = "543f93241d32b3f00569201bfce9d7a93c92c6421b23c77864ac929dc947b9fc" dependencies = [ "hax-lib-macros", "num-bigint", @@ -3318,9 +3319,9 @@ dependencies = [ [[package]] name = "hax-lib-macros" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1" +checksum = "f8755751e760b11021765bb04cb4a6c4e24742688d9f3aa14c2079638f537b0f" dependencies = [ "hax-lib-macros-types", "proc-macro-error2", @@ -3331,9 +3332,9 @@ dependencies = [ [[package]] name = "hax-lib-macros-types" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515" +checksum = "f177c9ae8ea456e2f71ff3c1ea47bf4464f772a05133fcbba56cd5ba169035a2" dependencies = [ "proc-macro2", "quote", @@ -4121,6 +4122,8 @@ dependencies = [ "nym-credentials-interface", "nym-crypto", "nym-gateway", + "nym-kkt", + "nym-kkt-ciphersuite", "nym-lp-transport", "nym-registration-client", "nym-test-utils", @@ -4450,10 +4453,21 @@ version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +[[package]] +name = "libcrux-aesgcm" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-secrets", + "libcrux-traits", +] + [[package]] name = "libcrux-chacha20poly1305" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4464,8 +4478,8 @@ dependencies = [ [[package]] name = "libcrux-curve25519" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4475,8 +4489,8 @@ dependencies = [ [[package]] name = "libcrux-ecdh" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-curve25519", "libcrux-p256", @@ -4486,8 +4500,8 @@ dependencies = [ [[package]] name = "libcrux-ed25519" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4499,15 +4513,15 @@ dependencies = [ [[package]] name = "libcrux-hacl-rs" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-macros", ] [[package]] name = "libcrux-hkdf" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-hacl-rs", "libcrux-hmac", @@ -4516,8 +4530,8 @@ dependencies = [ [[package]] name = "libcrux-hmac" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4526,8 +4540,8 @@ dependencies = [ [[package]] name = "libcrux-intrinsics" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "core-models", "hax-lib", @@ -4535,8 +4549,8 @@ dependencies = [ [[package]] name = "libcrux-kem" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-curve25519", "libcrux-ecdh", @@ -4551,16 +4565,30 @@ dependencies = [ [[package]] name = "libcrux-macros" version = "0.0.3" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "quote", "syn 2.0.106", ] +[[package]] +name = "libcrux-ml-dsa" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" +dependencies = [ + "core-models", + "hax-lib", + "libcrux-intrinsics", + "libcrux-macros", + "libcrux-platform", + "libcrux-sha3", + "tls_codec", +] + [[package]] name = "libcrux-ml-kem" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "hax-lib", "libcrux-intrinsics", @@ -4574,8 +4602,8 @@ dependencies = [ [[package]] name = "libcrux-p256" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4586,8 +4614,8 @@ dependencies = [ [[package]] name = "libcrux-platform" -version = "0.0.2" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.3" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libc", ] @@ -4595,7 +4623,7 @@ dependencies = [ [[package]] name = "libcrux-poly1305" version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4603,34 +4631,38 @@ dependencies = [ [[package]] name = "libcrux-psq" -version = "0.0.5" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ + "classic-mceliece-rust", + "libcrux-aesgcm", "libcrux-chacha20poly1305", "libcrux-ecdh", "libcrux-ed25519", "libcrux-hkdf", "libcrux-hmac", "libcrux-kem", + "libcrux-ml-dsa", "libcrux-ml-kem", "libcrux-sha2", "libcrux-traits", + "rand 0.8.5", "rand 0.9.2", "tls_codec", ] [[package]] name = "libcrux-secrets" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.5" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "hax-lib", ] [[package]] name = "libcrux-sha2" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-hacl-rs", "libcrux-macros", @@ -4639,8 +4671,8 @@ dependencies = [ [[package]] name = "libcrux-sha3" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.7" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "hax-lib", "libcrux-intrinsics", @@ -4650,8 +4682,8 @@ dependencies = [ [[package]] name = "libcrux-traits" -version = "0.0.4" -source = "git+https://github.com/cryspen/libcrux#f63bb67ead59297560edf523a3b29b21489c17ea" +version = "0.0.6" +source = "git+https://github.com/cryspen/libcrux?rev=b17f8687b67cdcfc10b55aeecc998bbbca28f775#b17f8687b67cdcfc10b55aeecc998bbbca28f775" dependencies = [ "libcrux-secrets", "rand 0.9.2", @@ -5075,7 +5107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3176f18d11a1ae46053e59ec89d46ba318ae1343615bd3f8c908bfc84edae35c" dependencies = [ "byteorder", - "pastey", + "pastey 0.1.1", "thiserror 2.0.12", ] @@ -6198,6 +6230,7 @@ dependencies = [ "nym-sphinx-types", "nym-test-utils", "rand 0.8.5", + "rand 0.9.2", "rand_chacha 0.3.1", "serde", "serde_bytes", @@ -6792,16 +6825,17 @@ name = "nym-kkt" version = "0.1.0" dependencies = [ "anyhow", - "blake3", - "classic-mceliece-rust", "criterion", "libcrux-chacha20poly1305", "libcrux-ecdh", "libcrux-kem", + "libcrux-ml-kem", + "libcrux-psq", "num_enum", "nym-crypto", "nym-kkt-ciphersuite", "nym-kkt-context", + "nym-pemstore", "rand 0.9.2", "rand_chacha 0.9.0", "strum", @@ -6827,7 +6861,7 @@ version = "1.20.4" dependencies = [ "num_enum", "nym-kkt-ciphersuite", - "thiserror 2.0.18", + "thiserror 2.0.12", ] [[package]] @@ -6848,13 +6882,8 @@ dependencies = [ "anyhow", "bs58", "bytes", - "chacha20poly1305", "criterion", - "dashmap", - "libcrux-kem", "libcrux-psq", - "libcrux-traits", - "mock_instant", "num_enum", "nym-crypto", "nym-kkt", @@ -6863,14 +6892,8 @@ dependencies = [ "nym-lp-packet", "nym-lp-transport", "nym-test-utils", - "parking_lot", - "rand 0.8.5", "rand 0.9.2", - "serde", - "sha2 0.10.9", - "snow", "thiserror 2.0.12", - "tls_codec", "tokio", "tracing", "zeroize", @@ -6923,7 +6946,7 @@ dependencies = [ "num_enum", "nym-crypto", "nym-lp-common", - "thiserror 2.0.18", + "thiserror 2.0.12", "tracing", ] @@ -6935,7 +6958,7 @@ dependencies = [ "nym-kkt-context", "nym-lp-packet", "nym-test-utils", - "thiserror 2.0.18", + "thiserror 2.0.12", "tokio", "tracing", ] @@ -7202,6 +7225,7 @@ dependencies = [ "opentelemetry", "opentelemetry_sdk", "rand 0.8.5", + "rand 0.9.2", "rand_chacha 0.3.1", "serde", "serde_json", @@ -7535,13 +7559,15 @@ dependencies = [ "nym-credentials-interface", "nym-crypto", "nym-ip-packet-client", + "nym-kkt", "nym-lp", "nym-lp-transport", "nym-registration-common", "nym-sdk", + "nym-test-utils", "nym-validator-client", "nym-wireguard-types", - "rand 0.8.5", + "rand 0.9.2", "thiserror 2.0.12", "tokio", "tokio-util", @@ -8860,6 +8886,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" +[[package]] +name = "pastey" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" + [[package]] name = "peg" version = "0.8.5" diff --git a/common/nym-kkt/Cargo.toml b/common/nym-kkt/Cargo.toml index dfc11ff77d..6ed830dad0 100644 --- a/common/nym-kkt/Cargo.toml +++ b/common/nym-kkt/Cargo.toml @@ -15,6 +15,7 @@ strum = { workspace = true } nym-crypto = { path = "../crypto", features = ["hashing"] } nym-kkt-ciphersuite = { workspace = true, features = ["digests"] } nym-kkt-context = { path = "../nym-kkt-context" } +nym-pemstore = { workspace = true } libcrux-kem = { workspace = true } libcrux-ecdh = { workspace = true, features = ["codec"] } diff --git a/common/nym-kkt/src/key_utils.rs b/common/nym-kkt/src/key_utils.rs index 7c597dc63f..10b03ecdca 100644 --- a/common/nym-kkt/src/key_utils.rs +++ b/common/nym-kkt/src/key_utils.rs @@ -21,7 +21,6 @@ where libcrux_ml_kem::mlkem768::rand::generate_key_pair(rng) } -// (decapsulation_key, encapsulation_key) pub fn generate_keypair_mceliece(rng: &mut R) -> libcrux_psq::classic_mceliece::KeyPair where R: RngCore + CryptoRng, diff --git a/common/nym-kkt/src/keys.rs b/common/nym-kkt/src/keys.rs index 2f5bc4ff4c..0a0f4fdc6c 100644 --- a/common/nym-kkt/src/keys.rs +++ b/common/nym-kkt/src/keys.rs @@ -2,19 +2,21 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::KKTError; -use libcrux_ml_kem::mlkem768::{MlKem768KeyPair, MlKem768PrivateKey, MlKem768PublicKey}; -use libcrux_psq::classic_mceliece; use libcrux_psq::handshake::types::PQEncapsulationKey; -use nym_kkt_ciphersuite::{KEM, mceliece}; +use nym_kkt_ciphersuite::KEM; use std::fmt::{Debug, Formatter}; use std::sync::Arc; +pub use libcrux_ml_kem::mlkem768::{MlKem768KeyPair, MlKem768PrivateKey, MlKem768PublicKey}; +pub use libcrux_psq::classic_mceliece as mceliece; +pub use libcrux_psq::handshake::types::{DHKeyPair, DHPrivateKey, DHPublicKey}; + /// Wrapper around keys used for the KEM exchange /// with cheap clones thanks to Arc wrappers #[derive(Clone)] pub struct KEMKeys { - mc_eliece_pk: Arc, - mc_eliece_sk: Arc, + mc_eliece_pk: Arc, + mc_eliece_sk: Arc, ml_kem768_pk: Arc, ml_kem768_sk: Arc, } @@ -29,7 +31,7 @@ impl Debug for KEMKeys { } impl KEMKeys { - pub fn new(mc_eliece: classic_mceliece::KeyPair, ml_kem768: MlKem768KeyPair) -> Self { + pub fn new(mc_eliece: mceliece::KeyPair, ml_kem768: MlKem768KeyPair) -> Self { let (ml_kem768_sk, ml_kem768_pk) = ml_kem768.into_parts(); Self { mc_eliece_pk: Arc::new(mc_eliece.pk), @@ -55,7 +57,7 @@ impl KEMKeys { } } - pub fn mc_eliece_encapsulation_key(&self) -> &classic_mceliece::PublicKey { + pub fn mc_eliece_encapsulation_key(&self) -> &mceliece::PublicKey { &self.mc_eliece_pk } @@ -63,7 +65,7 @@ impl KEMKeys { self.ml_kem768_pk.as_ref() } - pub fn mc_eliece_decapsulation_key(&self) -> &classic_mceliece::SecretKey { + pub fn mc_eliece_decapsulation_key(&self) -> &mceliece::SecretKey { &self.mc_eliece_sk } @@ -74,7 +76,7 @@ impl KEMKeys { #[derive(Clone)] pub enum EncapsulationKey { - McEliece(Arc), + McEliece(Arc), MlKem768(Arc), } @@ -110,15 +112,16 @@ impl EncapsulationKey { })?, ))), KEM::McEliece => { - let boxed_array: Box<[u8; mceliece::PUBLIC_KEY_LENGTH]> = bytes - .into_boxed_slice() - .try_into() - .map_err(|_| KKTError::KEMError { - info: "mceliece key of invalid length", - })?; + let boxed_array: Box<[u8; nym_kkt_ciphersuite::mceliece::PUBLIC_KEY_LENGTH]> = + bytes + .into_boxed_slice() + .try_into() + .map_err(|_| KKTError::KEMError { + info: "mceliece key of invalid length", + })?; Ok(EncapsulationKey::McEliece(Arc::new( - classic_mceliece::PublicKey::from(boxed_array), + mceliece::PublicKey::from(boxed_array), ))) } } @@ -131,3 +134,293 @@ impl EncapsulationKey { } } } + +// storage helpers +pub mod storage_wrappers { + use nym_pemstore::traits::PemStorableKey; + use thiserror::Error; + + #[derive(Debug, Error)] + pub enum MalformedStoredKeyError { + #[error("{typ} stored key has an invalid length")] + InvalidKeyLength { typ: &'static str }, + + #[error("{typ} stored key is malformed: {message}")] + MalformedData { typ: &'static str, message: String }, + + #[error("attempted to take ownership of a stored {typ} key representation")] + IllegalStoredConversion { typ: &'static str }, + } + + pub trait StorableKey: Sized { + type StorableRepresentation<'a>: PemStorableKey + + From<&'a Self> + + TryInto + + Sized + where + Self: 'a; + + fn to_storable(&self) -> Self::StorableRepresentation<'_> { + self.into() + } + + fn from_storable( + repr: Self::StorableRepresentation<'_>, + ) -> Result { + repr.try_into() + } + } + + macro_rules! declare_key_wrappers { + ($pub_key_type:ty, $private_key_type:ty) => { + pub enum StorablePublicKey<'a> { + Owned(Box<$pub_key_type>), + Borrowed(&'a $pub_key_type), + } + + impl AsRef<$pub_key_type> for StorablePublicKey<'_> { + fn as_ref(&self) -> &$pub_key_type { + match self { + StorablePublicKey::Owned(k) => k, + StorablePublicKey::Borrowed(k) => k, + } + } + } + + pub enum StorablePrivateKey<'a> { + Owned(Box<$private_key_type>), + Borrowed(&'a $private_key_type), + } + + impl AsRef<$private_key_type> for StorablePrivateKey<'_> { + fn as_ref(&self) -> &$private_key_type { + match self { + StorablePrivateKey::Owned(k) => k, + StorablePrivateKey::Borrowed(k) => k, + } + } + } + + impl<'a> From<&'a $pub_key_type> for StorablePublicKey<'a> { + fn from(value: &'a $pub_key_type) -> Self { + StorablePublicKey::Borrowed(value) + } + } + + impl<'a> TryFrom> for $pub_key_type { + type Error = MalformedStoredKeyError; + + fn try_from(value: StorablePublicKey<'a>) -> Result { + match value { + StorablePublicKey::Owned(value) => Ok(*value), + StorablePublicKey::Borrowed(_) => { + Err(MalformedStoredKeyError::IllegalStoredConversion { + typ: ::pem_type(), + }) + } + } + } + } + + impl<'a> From<&'a $private_key_type> for StorablePrivateKey<'a> { + fn from(value: &'a $private_key_type) -> Self { + StorablePrivateKey::Borrowed(value) + } + } + + impl<'a> TryFrom> for $private_key_type { + type Error = MalformedStoredKeyError; + + fn try_from(value: StorablePrivateKey<'a>) -> Result { + match value { + StorablePrivateKey::Owned(value) => Ok(*value), + StorablePrivateKey::Borrowed(_) => { + Err(MalformedStoredKeyError::IllegalStoredConversion { + typ: ::pem_type(), + }) + } + } + } + } + + impl $crate::keys::storage_wrappers::StorableKey for $pub_key_type { + type StorableRepresentation<'a> = StorablePublicKey<'a>; + } + + impl $crate::keys::storage_wrappers::StorableKey for $private_key_type { + type StorableRepresentation<'a> = StorablePrivateKey<'a>; + } + }; + } + + pub mod mceliece { + use crate::keys::storage_wrappers::MalformedStoredKeyError; + use libcrux_psq::classic_mceliece; + use nym_pemstore::traits::PemStorableKey; + + declare_key_wrappers!(classic_mceliece::PublicKey, classic_mceliece::SecretKey); + + impl<'a> PemStorableKey for StorablePrivateKey<'a> { + type Error = MalformedStoredKeyError; + + fn pem_type() -> &'static str { + "MCELIECE PRIVATE KEY" + } + + fn to_bytes(&self) -> Vec { + self.as_ref().as_ref().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> Result { + let bytes: Box<[u8; nym_kkt_ciphersuite::mceliece::SECRET_KEY_LENGTH]> = + bytes.to_vec().into_boxed_slice().try_into().map_err(|_| { + MalformedStoredKeyError::InvalidKeyLength { + typ: Self::pem_type(), + } + })?; + + Ok(StorablePrivateKey::Owned(Box::new( + classic_mceliece::SecretKey::from(bytes), + ))) + } + } + + impl<'a> PemStorableKey for StorablePublicKey<'a> { + type Error = MalformedStoredKeyError; + + fn pem_type() -> &'static str { + "MCELIECE PUBLIC KEY" + } + + fn to_bytes(&self) -> Vec { + self.as_ref().as_ref().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> Result { + let bytes: Box<[u8; nym_kkt_ciphersuite::mceliece::PUBLIC_KEY_LENGTH]> = + bytes.to_vec().into_boxed_slice().try_into().map_err(|_| { + MalformedStoredKeyError::InvalidKeyLength { + typ: Self::pem_type(), + } + })?; + + Ok(StorablePublicKey::Owned(Box::new( + classic_mceliece::PublicKey::from(bytes), + ))) + } + } + } + + pub mod mlkem768 { + use crate::keys::storage_wrappers::MalformedStoredKeyError; + use libcrux_ml_kem::mlkem768::{MlKem768PrivateKey, MlKem768PublicKey}; + use nym_pemstore::traits::PemStorableKey; + + declare_key_wrappers!(MlKem768PublicKey, MlKem768PrivateKey); + + impl<'a> PemStorableKey for StorablePrivateKey<'a> { + type Error = MalformedStoredKeyError; + + fn pem_type() -> &'static str { + "MLKEM768 PRIVATE KEY" + } + + fn to_bytes(&self) -> Vec { + self.as_ref().as_slice().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> Result { + let inner = MlKem768PrivateKey::try_from(bytes).map_err(|message| { + MalformedStoredKeyError::MalformedData { + typ: Self::pem_type(), + message: message.to_string(), + } + })?; + Ok(StorablePrivateKey::Owned(Box::new(inner))) + } + } + + impl<'a> PemStorableKey for StorablePublicKey<'a> { + type Error = MalformedStoredKeyError; + + fn pem_type() -> &'static str { + "MLKEM768 PUBLIC KEY" + } + + fn to_bytes(&self) -> Vec { + self.as_ref().as_slice().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> Result { + let inner = MlKem768PublicKey::try_from(bytes).map_err(|message| { + MalformedStoredKeyError::MalformedData { + typ: Self::pem_type(), + message: message.to_string(), + } + })?; + Ok(StorablePublicKey::Owned(Box::new(inner))) + } + } + } + + pub mod x25519 { + use crate::keys::storage_wrappers::MalformedStoredKeyError; + use libcrux_psq::handshake::types::{DHPrivateKey, DHPublicKey}; + use nym_pemstore::traits::PemStorableKey; + + declare_key_wrappers!(DHPublicKey, DHPrivateKey); + + impl<'a> PemStorableKey for StorablePrivateKey<'a> { + type Error = MalformedStoredKeyError; + + fn pem_type() -> &'static str { + "LP X25519 PRIVATE KEY" + } + + fn to_bytes(&self) -> Vec { + self.as_ref().as_ref().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> Result { + let bytes = + bytes + .try_into() + .map_err(|_| MalformedStoredKeyError::InvalidKeyLength { + typ: Self::pem_type(), + })?; + Ok(StorablePrivateKey::Owned(Box::new( + DHPrivateKey::from_bytes(&bytes).map_err(|err| { + MalformedStoredKeyError::MalformedData { + typ: Self::pem_type(), + message: format!("{err:?}"), + } + })?, + ))) + } + } + + impl<'a> PemStorableKey for StorablePublicKey<'a> { + type Error = MalformedStoredKeyError; + + fn pem_type() -> &'static str { + "LP X25519 PUBLIC KEY" + } + + fn to_bytes(&self) -> Vec { + self.as_ref().as_ref().to_vec() + } + + fn from_bytes(bytes: &[u8]) -> Result { + let bytes = + bytes + .try_into() + .map_err(|_| MalformedStoredKeyError::InvalidKeyLength { + typ: Self::pem_type(), + })?; + Ok(StorablePublicKey::Owned(Box::new(DHPublicKey::from_bytes( + &bytes, + )))) + } + } + } +} diff --git a/gateway/src/node/lp_listener/mod.rs b/gateway/src/node/lp_listener/mod.rs index cfae5884ee..e8d7c69cdd 100644 --- a/gateway/src/node/lp_listener/mod.rs +++ b/gateway/src/node/lp_listener/mod.rs @@ -132,17 +132,6 @@ pub struct LpDebug { /// Maximum concurrent connections pub max_connections: usize, - /// Maximum acceptable age of ClientHello timestamp in seconds (default: 30) - /// - /// ClientHello messages with timestamps older than this will be rejected - /// to prevent replay attacks. Value should be: - /// - Large enough to account for clock skew and network latency - /// - Small enough to limit replay attack window - /// - /// Recommended: 30-60 seconds - #[serde(with = "humantime_serde")] - pub timestamp_tolerance: Duration, - /// Use mock ecash manager for testing (default: false) /// /// When enabled, the LP listener will use a mock ecash verifier that @@ -229,9 +218,6 @@ impl Default for LpConfig { impl LpDebug { pub const DEFAULT_MAX_CONNECTIONS: usize = 10000; - // 30 seconds - balances security vs clock skew tolerance - pub const DEFAULT_TIMESTAMP_TOLERANCE: Duration = Duration::from_secs(30); - // 90 seconds - handshakes should complete quickly pub const DEFAULT_HANDSHAKE_TTL: Duration = Duration::from_secs(90); @@ -249,7 +235,6 @@ impl Default for LpDebug { fn default() -> Self { LpDebug { max_connections: Self::DEFAULT_MAX_CONNECTIONS, - timestamp_tolerance: Self::DEFAULT_TIMESTAMP_TOLERANCE, use_mock_ecash: false, handshake_ttl: Self::DEFAULT_HANDSHAKE_TTL, session_ttl: Self::DEFAULT_SESSION_TTL, diff --git a/nym-node/Cargo.toml b/nym-node/Cargo.toml index 00340e45cb..756e2c6780 100644 --- a/nym-node/Cargo.toml +++ b/nym-node/Cargo.toml @@ -34,6 +34,7 @@ human-repr = { workspace = true } ipnetwork = { workspace = true } indicatif = { workspace = true } rand = { workspace = true } +rand09 = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json.workspace = true thiserror.workspace = true diff --git a/nym-node/src/config/old_configs/mod.rs b/nym-node/src/config/old_configs/mod.rs index c46a72d1a1..2116f94ec8 100644 --- a/nym-node/src/config/old_configs/mod.rs +++ b/nym-node/src/config/old_configs/mod.rs @@ -4,6 +4,7 @@ mod old_config_v1; mod old_config_v10; mod old_config_v11; +mod old_config_v12; mod old_config_v2; mod old_config_v3; mod old_config_v4; @@ -24,3 +25,4 @@ pub use old_config_v8::try_upgrade_config_v8; pub use old_config_v9::try_upgrade_config_v9; pub use old_config_v10::try_upgrade_config_v10; pub use old_config_v11::try_upgrade_config_v11; +pub use old_config_v12::try_upgrade_config_v12; diff --git a/nym-node/src/config/old_configs/old_config_v11.rs b/nym-node/src/config/old_configs/old_config_v11.rs index 6d3a380357..31e5577f4d 100644 --- a/nym-node/src/config/old_configs/old_config_v11.rs +++ b/nym-node/src/config/old_configs/old_config_v11.rs @@ -1,21 +1,9 @@ // Copyright 2025 - Nym Technologies SA // SPDX-License-Identifier: GPL-3.0-only -use crate::config::authenticator::{Authenticator, AuthenticatorDebug}; -use crate::config::gateway_tasks::{ - ClientBandwidthDebug, StaleMessageDebug, UpgradeModeWatcher, ZkNymTicketHandlerDebug, -}; -use crate::config::persistence::{ - AuthenticatorPaths, GatewayTasksPaths, IpPacketRouterPaths, KeysPaths, NetworkRequesterPaths, - NymNodePaths, ReplayProtectionPaths, ServiceProvidersPaths, WireguardPaths, -}; -use crate::config::service_providers::{ - IpPacketRouter, IpPacketRouterDebug, NetworkRequester, NetworkRequesterDebug, -}; -use crate::config::{ - Config, GatewayTasksConfig, Host, Http, KeyRotation, KeyRotationDebug, Mixnet, MixnetDebug, - NodeModes, ReplayProtection, ReplayProtectionDebug, ServiceProvidersConfig, Verloc, - VerlocDebug, Wireguard, gateway_tasks, service_providers, +use crate::config::old_configs::old_config_v12::{ + ConfigV12, DebugV12, GatewayTasksConfigDebugV12, GatewayTasksConfigV12, LpConfigV12, + UpgradeModeWatcherV12, WireguardV12, }; use crate::error::NymNodeError; use nym_bin_common::logging::LoggingSettings; @@ -185,7 +173,7 @@ impl ConfigV11 { pub async fn try_upgrade_config_v11>( path: P, prev_config: Option, -) -> Result { +) -> Result { debug!("attempting to load v11 config..."); let old_cfg = if let Some(prev_config) = prev_config { @@ -197,144 +185,16 @@ pub async fn try_upgrade_config_v11>( // for future reference: when creating v12 migration, // look at how v10 -> v11 is implemented // you might be able to create a bunch of type aliases again to save you some headache - let cfg = Config { + let cfg = ConfigV12 { save_path: old_cfg.save_path, id: old_cfg.id, - modes: NodeModes { - mixnode: old_cfg.modes.mixnode, - entry: old_cfg.modes.entry, - exit: old_cfg.modes.exit, - }, - host: Host { - public_ips: old_cfg.host.public_ips, - hostname: old_cfg.host.hostname, - location: old_cfg.host.location, - }, - mixnet: Mixnet { - bind_address: old_cfg.mixnet.bind_address, - announce_port: old_cfg.mixnet.announce_port, - nym_api_urls: old_cfg.mixnet.nym_api_urls, - nyxd_urls: old_cfg.mixnet.nyxd_urls, - replay_protection: ReplayProtection { - storage_paths: ReplayProtectionPaths { - current_bloomfilters_directory: old_cfg - .mixnet - .replay_protection - .storage_paths - .current_bloomfilters_directory, - }, - debug: ReplayProtectionDebug { - unsafe_disabled: old_cfg.mixnet.replay_protection.debug.unsafe_disabled, - maximum_replay_detection_deferral: old_cfg - .mixnet - .replay_protection - .debug - .maximum_replay_detection_deferral, - maximum_replay_detection_pending_packets: old_cfg - .mixnet - .replay_protection - .debug - .maximum_replay_detection_pending_packets, - false_positive_rate: old_cfg.mixnet.replay_protection.debug.false_positive_rate, - initial_expected_packets_per_second: old_cfg - .mixnet - .replay_protection - .debug - .initial_expected_packets_per_second, - bloomfilter_minimum_packets_per_second_size: old_cfg - .mixnet - .replay_protection - .debug - .bloomfilter_minimum_packets_per_second_size, - bloomfilter_size_multiplier: old_cfg - .mixnet - .replay_protection - .debug - .bloomfilter_size_multiplier, - bloomfilter_disk_flushing_rate: old_cfg - .mixnet - .replay_protection - .debug - .bloomfilter_disk_flushing_rate, - }, - }, - key_rotation: KeyRotation { - debug: KeyRotationDebug { - rotation_state_poling_interval: old_cfg - .mixnet - .key_rotation - .debug - .rotation_state_poling_interval, - }, - }, - debug: MixnetDebug { - maximum_forward_packet_delay: old_cfg.mixnet.debug.maximum_forward_packet_delay, - packet_forwarding_initial_backoff: old_cfg - .mixnet - .debug - .packet_forwarding_initial_backoff, - packet_forwarding_maximum_backoff: old_cfg - .mixnet - .debug - .packet_forwarding_maximum_backoff, - initial_connection_timeout: old_cfg.mixnet.debug.initial_connection_timeout, - maximum_connection_buffer_size: old_cfg.mixnet.debug.maximum_connection_buffer_size, - unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise, - use_legacy_packet_encoding: old_cfg.mixnet.debug.use_legacy_packet_encoding, - }, - }, - storage_paths: NymNodePaths { - keys: KeysPaths { - private_ed25519_identity_key_file: old_cfg - .storage_paths - .keys - .private_ed25519_identity_key_file, - public_ed25519_identity_key_file: old_cfg - .storage_paths - .keys - .public_ed25519_identity_key_file, - primary_x25519_sphinx_key_file: old_cfg - .storage_paths - .keys - .primary_x25519_sphinx_key_file, - private_x25519_noise_key_file: old_cfg - .storage_paths - .keys - .private_x25519_noise_key_file, - public_x25519_noise_key_file: old_cfg - .storage_paths - .keys - .public_x25519_noise_key_file, - secondary_x25519_sphinx_key_file: old_cfg - .storage_paths - .keys - .secondary_x25519_sphinx_key_file, - }, - description: old_cfg.storage_paths.description, - }, - http: Http { - bind_address: old_cfg.http.bind_address, - landing_page_assets_path: old_cfg.http.landing_page_assets_path, - access_token: old_cfg.http.access_token, - expose_system_info: old_cfg.http.expose_system_info, - expose_system_hardware: old_cfg.http.expose_system_hardware, - expose_crypto_hardware: old_cfg.http.expose_crypto_hardware, - node_load_cache_ttl: old_cfg.http.node_load_cache_ttl, - }, - verloc: Verloc { - bind_address: old_cfg.verloc.bind_address, - announce_port: old_cfg.verloc.announce_port, - debug: VerlocDebug { - packets_per_node: old_cfg.verloc.debug.packets_per_node, - connection_timeout: old_cfg.verloc.debug.connection_timeout, - packet_timeout: old_cfg.verloc.debug.packet_timeout, - delay_between_packets: old_cfg.verloc.debug.delay_between_packets, - tested_nodes_batch_size: old_cfg.verloc.debug.tested_nodes_batch_size, - testing_interval: old_cfg.verloc.debug.testing_interval, - retry_timeout: old_cfg.verloc.debug.retry_timeout, - }, - }, - wireguard: Wireguard { + modes: old_cfg.modes, + host: old_cfg.host, + mixnet: old_cfg.mixnet, + storage_paths: old_cfg.storage_paths, + http: old_cfg.http, + verloc: old_cfg.verloc, + wireguard: WireguardV12 { enabled: old_cfg.wireguard.enabled, bind_address: old_cfg.wireguard.bind_address, private_ipv4: old_cfg.wireguard.private_ipv4, @@ -343,259 +203,48 @@ pub async fn try_upgrade_config_v11>( announced_metadata_port: old_cfg.wireguard.announced_metadata_port, private_network_prefix_v4: old_cfg.wireguard.private_network_prefix_v4, private_network_prefix_v6: old_cfg.wireguard.private_network_prefix_v6, + // \/ ADDED use_userspace: false, - storage_paths: WireguardPaths { - private_diffie_hellman_key_file: old_cfg - .wireguard - .storage_paths - .private_diffie_hellman_key_file, - public_diffie_hellman_key_file: old_cfg - .wireguard - .storage_paths - .public_diffie_hellman_key_file, - }, + // /\ ADDED + storage_paths: old_cfg.wireguard.storage_paths, }, - gateway_tasks: GatewayTasksConfig { - storage_paths: GatewayTasksPaths { - clients_storage: old_cfg.gateway_tasks.storage_paths.clients_storage, - stats_storage: old_cfg.gateway_tasks.storage_paths.stats_storage, - cosmos_mnemonic: old_cfg.gateway_tasks.storage_paths.cosmos_mnemonic, - bridge_client_params: old_cfg.gateway_tasks.storage_paths.bridge_client_params, - }, + gateway_tasks: GatewayTasksConfigV12 { + storage_paths: old_cfg.gateway_tasks.storage_paths, enforce_zk_nyms: old_cfg.gateway_tasks.enforce_zk_nyms, ws_bind_address: old_cfg.gateway_tasks.ws_bind_address, announce_ws_port: old_cfg.gateway_tasks.announce_ws_port, announce_wss_port: old_cfg.gateway_tasks.announce_wss_port, // \/ ADDED - upgrade_mode: UpgradeModeWatcher::new() + upgrade_mode: UpgradeModeWatcherV12::new() .inspect_err(|_| { error!( "failed to set custom upgrade mode configuration - falling back to mainnet" ) }) - .unwrap_or(UpgradeModeWatcher::new_mainnet()), - lp: Default::default(), + .unwrap_or(UpgradeModeWatcherV12::new_mainnet()), + lp: LpConfigV12::default(), // /\ ADDED - debug: gateway_tasks::Debug { + debug: GatewayTasksConfigDebugV12 { message_retrieval_limit: old_cfg.gateway_tasks.debug.message_retrieval_limit, maximum_open_connections: old_cfg.gateway_tasks.debug.maximum_open_connections, minimum_mix_performance: old_cfg.gateway_tasks.debug.minimum_mix_performance, max_request_timestamp_skew: old_cfg.gateway_tasks.debug.max_request_timestamp_skew, - stale_messages: StaleMessageDebug { - cleaner_run_interval: old_cfg - .gateway_tasks - .debug - .stale_messages - .cleaner_run_interval, - max_age: old_cfg.gateway_tasks.debug.stale_messages.max_age, - }, - client_bandwidth: ClientBandwidthDebug { - max_flushing_rate: old_cfg - .gateway_tasks - .debug - .client_bandwidth - .max_flushing_rate, - max_delta_flushing_amount: old_cfg - .gateway_tasks - .debug - .client_bandwidth - .max_delta_flushing_amount, - }, - zk_nym_tickets: ZkNymTicketHandlerDebug { - revocation_bandwidth_penalty: old_cfg - .gateway_tasks - .debug - .zk_nym_tickets - .revocation_bandwidth_penalty, - pending_poller: old_cfg.gateway_tasks.debug.zk_nym_tickets.pending_poller, - minimum_api_quorum: old_cfg - .gateway_tasks - .debug - .zk_nym_tickets - .minimum_api_quorum, - minimum_redemption_tickets: old_cfg - .gateway_tasks - .debug - .zk_nym_tickets - .minimum_redemption_tickets, - maximum_time_between_redemption: old_cfg - .gateway_tasks - .debug - .zk_nym_tickets - .maximum_time_between_redemption, - }, + stale_messages: old_cfg.gateway_tasks.debug.stale_messages, + client_bandwidth: old_cfg.gateway_tasks.debug.client_bandwidth, + zk_nym_tickets: old_cfg.gateway_tasks.debug.zk_nym_tickets, + // \/ ADDED (be explicit about the value rather than using ..Default::default() - upgrade_mode_min_staleness_recheck: gateway_tasks::Debug::default() + upgrade_mode_min_staleness_recheck: GatewayTasksConfigDebugV12::default() .upgrade_mode_min_staleness_recheck, // /\ ADDED }, }, - service_providers: ServiceProvidersConfig { - storage_paths: ServiceProvidersPaths { - clients_storage: old_cfg.service_providers.storage_paths.clients_storage, - stats_storage: old_cfg.service_providers.storage_paths.stats_storage, - network_requester: NetworkRequesterPaths { - private_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .private_ed25519_identity_key_file, - public_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .public_ed25519_identity_key_file, - private_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .private_x25519_diffie_hellman_key_file, - public_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .public_x25519_diffie_hellman_key_file, - ack_key_file: old_cfg - .service_providers - .storage_paths - .network_requester - .ack_key_file, - reply_surb_database: old_cfg - .service_providers - .storage_paths - .network_requester - .reply_surb_database, - gateway_registrations: old_cfg - .service_providers - .storage_paths - .network_requester - .gateway_registrations, - }, - ip_packet_router: IpPacketRouterPaths { - private_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .private_ed25519_identity_key_file, - public_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .public_ed25519_identity_key_file, - private_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .private_x25519_diffie_hellman_key_file, - public_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .public_x25519_diffie_hellman_key_file, - ack_key_file: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .ack_key_file, - reply_surb_database: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .reply_surb_database, - gateway_registrations: old_cfg - .service_providers - .storage_paths - .ip_packet_router - .gateway_registrations, - }, - authenticator: AuthenticatorPaths { - private_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .private_ed25519_identity_key_file, - public_ed25519_identity_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .public_ed25519_identity_key_file, - private_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .private_x25519_diffie_hellman_key_file, - public_x25519_diffie_hellman_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .public_x25519_diffie_hellman_key_file, - ack_key_file: old_cfg - .service_providers - .storage_paths - .authenticator - .ack_key_file, - reply_surb_database: old_cfg - .service_providers - .storage_paths - .authenticator - .reply_surb_database, - gateway_registrations: old_cfg - .service_providers - .storage_paths - .authenticator - .gateway_registrations, - }, - }, - open_proxy: old_cfg.service_providers.open_proxy, - upstream_exit_policy_url: old_cfg.service_providers.upstream_exit_policy_url, - network_requester: NetworkRequester { - debug: NetworkRequesterDebug { - enabled: old_cfg.service_providers.network_requester.debug.enabled, - disable_poisson_rate: old_cfg - .service_providers - .network_requester - .debug - .disable_poisson_rate, - client_debug: old_cfg - .service_providers - .network_requester - .debug - .client_debug, - }, - }, - ip_packet_router: IpPacketRouter { - debug: IpPacketRouterDebug { - enabled: old_cfg.service_providers.ip_packet_router.debug.enabled, - disable_poisson_rate: old_cfg - .service_providers - .ip_packet_router - .debug - .disable_poisson_rate, - client_debug: old_cfg - .service_providers - .ip_packet_router - .debug - .client_debug, - }, - }, - authenticator: Authenticator { - debug: AuthenticatorDebug { - enabled: old_cfg.service_providers.authenticator.debug.enabled, - disable_poisson_rate: old_cfg - .service_providers - .authenticator - .debug - .disable_poisson_rate, - client_debug: old_cfg.service_providers.authenticator.debug.client_debug, - }, - }, - debug: service_providers::Debug { - message_retrieval_limit: old_cfg.service_providers.debug.message_retrieval_limit, - }, - }, - metrics: Default::default(), - logging: LoggingSettings {}, - debug: Default::default(), + service_providers: old_cfg.service_providers, + metrics: old_cfg.metrics, + logging: old_cfg.logging, + // \/ FIXED + debug: DebugV12::default(), + // /\ FIXED }; Ok(cfg) } diff --git a/nym-node/src/config/old_configs/old_config_v12.rs b/nym-node/src/config/old_configs/old_config_v12.rs new file mode 100644 index 0000000000..ebac866cde --- /dev/null +++ b/nym-node/src/config/old_configs/old_config_v12.rs @@ -0,0 +1,1001 @@ +// Copyright 2026 - Nym Technologies SA +// SPDX-License-Identifier: GPL-3.0-only + +use crate::config::authenticator::{Authenticator, AuthenticatorDebug}; +use crate::config::gateway_tasks::{ + ClientBandwidthDebug, StaleMessageDebug, UpgradeModeWatcher, UpgradeModeWatcherDebug, + ZkNymTicketHandlerDebug, +}; +use crate::config::helpers::log_error_and_return; +use crate::config::persistence::{ + AuthenticatorPaths, DEFAULT_MCELIECE_PRIVATE_KEY_FILENAME, + DEFAULT_MCELIECE_PUBLIC_KEY_FILENAME, DEFAULT_MLKEM768_PRIVATE_KEY_FILENAME, + DEFAULT_MLKEM768_PUBLIC_KEY_FILENAME, DEFAULT_X25519_PRIVATE_LP_KEY_FILENAME, + DEFAULT_X25519_PUBLIC_LP_KEY_FILENAME, GatewayTasksPaths, IpPacketRouterPaths, KeysPaths, + NetworkRequesterPaths, NymNodePaths, ReplayProtectionPaths, ServiceProvidersPaths, + WireguardPaths, +}; +use crate::config::service_providers::{ + IpPacketRouter, IpPacketRouterDebug, NetworkRequester, NetworkRequesterDebug, +}; +use crate::config::{ + Config, Debug, GatewayTasksConfig, Host, Http, KeyRotation, KeyRotationDebug, MetricsConfig, + Mixnet, MixnetDebug, NodeModes, ReplayProtection, ReplayProtectionDebug, + ServiceProvidersConfig, Verloc, VerlocDebug, Wireguard, gateway_tasks, metrics, + service_providers, +}; +use crate::error::NymNodeError; +use crate::node::helpers::{ + store_mceliece_keypair, store_mlkem768_keypair, store_x25519_lp_keypair, +}; +use nym_bin_common::logging::LoggingSettings; +use nym_config::defaults::{mainnet, var_names}; +use nym_config::read_config_from_toml_file; +use nym_config::serde_helpers::de_maybe_port; +use nym_crypto::asymmetric::ed25519; +use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey; +use nym_gateway::node::LpConfig; +use nym_gateway::node::lp_listener::LpDebug; +use nym_kkt::key_utils::{ + generate_keypair_mceliece, generate_keypair_mlkem, generate_keypair_x25519, +}; +use rand09::SeedableRng; +use serde::{Deserialize, Serialize}; +use std::env; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::path::{Path, PathBuf}; +use std::time::Duration; +use tracing::{debug, error, info, instrument}; +pub use unchanged_v12_types::*; +use url::Url; + +// (while some of those are technically unused, they might be needed in future migrations, +// thus allow them to exist) +#[allow(dead_code)] +pub mod unchanged_v12_types { + use crate::config::old_configs::old_config_v11::{ + AuthenticatorDebugV11, AuthenticatorPathsV11, AuthenticatorV11, ClientBandwidthDebugV11, + DebugV11, GatewayTasksPathsV11, HostV11, HttpV11, IpPacketRouterDebugV11, + IpPacketRouterPathsV11, IpPacketRouterV11, KeyRotationDebugV11, KeyRotationV11, + KeysPathsV11, LoggingSettingsV11, MetricsConfigV11, MetricsDebugV11, MixnetDebugV11, + MixnetV11, NetworkRequesterDebugV11, NetworkRequesterPathsV11, NetworkRequesterV11, + NodeModeV11, NodeModesV11, NymNodePathsV11, ReplayProtectionDebugV11, + ReplayProtectionPathsV11, ReplayProtectionV11, ServiceProvidersConfigDebugV11, + ServiceProvidersConfigV11, ServiceProvidersPathsV11, StaleMessageDebugV11, VerlocDebugV11, + VerlocV11, WireguardPathsV11, WireguardV11, ZkNymTicketHandlerDebugV11, + }; + + pub type WireguardPathsV12 = WireguardPathsV11; + pub type NodeModeV12 = NodeModeV11; + pub type NodeModesV12 = NodeModesV11; + pub type HostV12 = HostV11; + pub type KeyRotationDebugV12 = KeyRotationDebugV11; + pub type KeyRotationV12 = KeyRotationV11; + pub type MixnetDebugV12 = MixnetDebugV11; + pub type MixnetV12 = MixnetV11; + pub type ReplayProtectionV12 = ReplayProtectionV11; + pub type ReplayProtectionPathsV12 = ReplayProtectionPathsV11; + pub type ReplayProtectionDebugV12 = ReplayProtectionDebugV11; + pub type KeysPathsV12 = KeysPathsV11; + pub type NymNodePathsV12 = NymNodePathsV11; + pub type HttpV12 = HttpV11; + pub type VerlocDebugV12 = VerlocDebugV11; + pub type VerlocV12 = VerlocV11; + pub type ZkNymTicketHandlerDebugV12 = ZkNymTicketHandlerDebugV11; + pub type NetworkRequesterPathsV12 = NetworkRequesterPathsV11; + pub type IpPacketRouterPathsV12 = IpPacketRouterPathsV11; + pub type AuthenticatorPathsV12 = AuthenticatorPathsV11; + pub type AuthenticatorV12 = AuthenticatorV11; + pub type AuthenticatorDebugV12 = AuthenticatorDebugV11; + pub type IpPacketRouterDebugV12 = IpPacketRouterDebugV11; + pub type IpPacketRouterV12 = IpPacketRouterV11; + pub type NetworkRequesterDebugV12 = NetworkRequesterDebugV11; + pub type NetworkRequesterV12 = NetworkRequesterV11; + pub type GatewayTasksPathsV12 = GatewayTasksPathsV11; + pub type StaleMessageDebugV12 = StaleMessageDebugV11; + pub type ClientBandwidthDebugV12 = ClientBandwidthDebugV11; + pub type ServiceProvidersPathsV12 = ServiceProvidersPathsV11; + pub type ServiceProvidersConfigDebugV12 = ServiceProvidersConfigDebugV11; + pub type ServiceProvidersConfigV12 = ServiceProvidersConfigV11; + pub type MetricsConfigV12 = MetricsConfigV11; + pub type MetricsDebugV12 = MetricsDebugV11; + pub type LoggingSettingsV12 = LoggingSettingsV11; +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct WireguardV12 { + /// Specifies whether the wireguard service is enabled on this node. + pub enabled: bool, + + /// Socket address this node will use for binding its wireguard interface. + /// default: `[::]:51822` + pub bind_address: SocketAddr, + + /// Private IPv4 address of the wireguard gateway. + /// default: `10.1.0.1` + pub private_ipv4: Ipv4Addr, + + /// Private IPv6 address of the wireguard gateway. + /// default: `fc01::1` + pub private_ipv6: Ipv6Addr, + + /// Tunnel port announced to external clients wishing to connect to the wireguard interface. + /// Useful in the instances where the node is behind a proxy. + pub announced_tunnel_port: u16, + + /// Metadata port announced to external clients wishing to connect to the metadata endpoint. + /// Useful in the instances where the node is behind a proxy. + pub announced_metadata_port: u16, + + /// The prefix denoting the maximum number of the clients that can be connected via Wireguard using IPv4. + /// The maximum value for IPv4 is 32 + pub private_network_prefix_v4: u8, + + /// The prefix denoting the maximum number of the clients that can be connected via Wireguard using IPv6. + /// The maximum value for IPv6 is 128 + pub private_network_prefix_v6: u8, + + /// Use userspace implementation of WireGuard (wireguard-go) instead of kernel module. + /// Useful in containerized environments without kernel WireGuard support. + /// default: `false` + #[serde(default)] + pub use_userspace: bool, + + /// Paths for wireguard keys, client registries, etc. + pub storage_paths: WireguardPathsV12, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct GatewayTasksConfigDebugV12 { + /// Number of messages from offline client that can be pulled at once (i.e. with a single SQL query) from the storage. + pub message_retrieval_limit: i64, + + /// The maximum number of client connections the gateway will keep open at once. + pub maximum_open_connections: usize, + + /// Specifies the minimum performance of mixnodes in the network that are to be used in internal topologies + /// of the services providers + pub minimum_mix_performance: u8, + + /// Defines the timestamp skew of a signed authentication request before it's deemed too excessive to process. + #[serde(alias = "maximum_auth_request_age")] + pub max_request_timestamp_skew: Duration, + + /// The minimum duration since the last explicit check for the upgrade mode to allow creation of new requests. + #[serde(with = "humantime_serde")] + pub upgrade_mode_min_staleness_recheck: Duration, + + pub stale_messages: StaleMessageDebugV12, + + pub client_bandwidth: ClientBandwidthDebugV12, + + pub zk_nym_tickets: ZkNymTicketHandlerDebugV12, +} + +impl GatewayTasksConfigDebugV12 { + pub const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: i64 = 100; + pub const DEFAULT_MINIMUM_MIX_PERFORMANCE: u8 = 50; + pub const DEFAULT_MAXIMUM_AUTH_REQUEST_TIMESTAMP_SKEW: Duration = Duration::from_secs(120); + pub const DEFAULT_MAXIMUM_OPEN_CONNECTIONS: usize = 8192; + pub const DEFAULT_UPGRADE_MODE_MIN_STALENESS_RECHECK: Duration = Duration::from_secs(30); +} + +impl Default for GatewayTasksConfigDebugV12 { + fn default() -> Self { + GatewayTasksConfigDebugV12 { + message_retrieval_limit: Self::DEFAULT_MESSAGE_RETRIEVAL_LIMIT, + maximum_open_connections: Self::DEFAULT_MAXIMUM_OPEN_CONNECTIONS, + max_request_timestamp_skew: Self::DEFAULT_MAXIMUM_AUTH_REQUEST_TIMESTAMP_SKEW, + minimum_mix_performance: Self::DEFAULT_MINIMUM_MIX_PERFORMANCE, + stale_messages: Default::default(), + client_bandwidth: Default::default(), + zk_nym_tickets: Default::default(), + upgrade_mode_min_staleness_recheck: Self::DEFAULT_UPGRADE_MODE_MIN_STALENESS_RECHECK, + } + } +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub struct UpgradeModeWatcherDebugV12 { + /// Default polling interval + #[serde(with = "humantime_serde")] + pub regular_polling_interval: Duration, + + /// Expedited polling interval for once upgrade mode is detected + #[serde(with = "humantime_serde")] + pub expedited_poll_interval: Duration, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UpgradeModeWatcherV12 { + /// Specifies whether this gateway watches for upgrade mode changes + /// via the published attestation file. + pub enabled: bool, + + /// Endpoint to query to retrieve current upgrade mode attestation. + pub attestation_url: Url, + + /// Expected public key of the attester providing the upgrade mode attestation + /// on the specified endpoint + #[serde(with = "bs58_ed25519_pubkey")] + pub attester_public_key: ed25519::PublicKey, + + #[serde(default)] + pub debug: UpgradeModeWatcherDebugV12, +} + +impl UpgradeModeWatcherV12 { + pub fn new_mainnet() -> UpgradeModeWatcherV12 { + info!("using mainnet configuration for the upgrade mode:"); + info!("\t- url: {}", mainnet::UPGRADE_MODE_ATTESTATION_URL); + info!( + "\t- attester public key: {}", + mainnet::UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY + ); + + // SAFETY: + // our hardcoded values should always be valid + #[allow(clippy::expect_used)] + let attestation_url = mainnet::UPGRADE_MODE_ATTESTATION_URL + .parse() + .expect("invalid default upgrade mode attestation URL"); + + #[allow(clippy::expect_used)] + let attester_public_key = mainnet::UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY + .parse() + .expect("invalid default upgrade mode attester public key"); + + UpgradeModeWatcherV12 { + enabled: true, + attestation_url, + attester_public_key, + debug: UpgradeModeWatcherDebugV12::default(), + } + } + + pub fn new() -> Result { + // if env is configured, extract relevant values from there, otherwise fallback to mainnet + if env::var(var_names::CONFIGURED).is_err() { + return Ok(Self::new_mainnet()); + } + + // if env is configured, the relevant values should be set + let Ok(env_attestation_url) = env::var(var_names::UPGRADE_MODE_ATTESTATION_URL) else { + return log_error_and_return(format!( + "'{}' is not set whilst the env is set to be configured", + var_names::UPGRADE_MODE_ATTESTATION_URL + )); + }; + + let Ok(env_attester_pubkey) = + env::var(var_names::UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY) + else { + return log_error_and_return(format!( + "'{}' is not set whilst the env is set to be configured", + var_names::UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY + )); + }; + + let attestation_url = match env_attestation_url.parse() { + Ok(url) => url, + Err(err) => { + return log_error_and_return(format!( + "provided attestation url {env_attestation_url} is invalid: {err}!" + )); + } + }; + + let attester_public_key = match env_attester_pubkey.parse() { + Ok(public_key) => public_key, + Err(err) => { + return log_error_and_return(format!( + "provided attester public key {env_attester_pubkey} is invalid: {err}!" + )); + } + }; + + Ok(UpgradeModeWatcherV12 { + enabled: true, + attestation_url, + attester_public_key, + debug: UpgradeModeWatcherDebugV12::default(), + }) + } +} + +impl UpgradeModeWatcherDebugV12 { + const DEFAULT_REGULAR_POLLING_INTERVAL: Duration = Duration::from_secs(15 * 60); + const DEFAULT_EXPEDITED_POLL_INTERVAL: Duration = Duration::from_secs(2 * 60); +} + +impl Default for UpgradeModeWatcherDebugV12 { + fn default() -> Self { + UpgradeModeWatcherDebugV12 { + regular_polling_interval: Self::DEFAULT_REGULAR_POLLING_INTERVAL, + expedited_poll_interval: Self::DEFAULT_EXPEDITED_POLL_INTERVAL, + } + } +} + +/// Configuration for LP listener +#[derive(Debug, Clone, Copy, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct LpConfigV12 { + pub control_bind_address: SocketAddr, + + pub data_bind_address: SocketAddr, + + #[serde(deserialize_with = "de_maybe_port")] + pub announce_control_port: Option, + + #[serde(deserialize_with = "de_maybe_port")] + pub announce_data_port: Option, + + #[serde(default)] + pub debug: LpDebugV12, +} + +#[derive(Debug, Clone, Copy, serde::Deserialize, serde::Serialize)] +#[serde(default)] +pub struct LpDebugV12 { + pub max_connections: usize, + #[serde(with = "humantime_serde")] + pub timestamp_tolerance: Duration, + pub use_mock_ecash: bool, + #[serde(with = "humantime_serde")] + pub handshake_ttl: Duration, + #[serde(with = "humantime_serde")] + pub session_ttl: Duration, + #[serde(with = "humantime_serde")] + pub state_cleanup_interval: Duration, + pub max_concurrent_forwards: usize, +} + +impl LpConfigV12 { + pub const DEFAULT_CONTROL_PORT: u16 = 41264; + pub const DEFAULT_DATA_PORT: u16 = 51264; +} + +impl Default for LpConfigV12 { + fn default() -> Self { + LpConfigV12 { + control_bind_address: SocketAddr::new( + IpAddr::V6(Ipv6Addr::UNSPECIFIED), + Self::DEFAULT_CONTROL_PORT, + ), + data_bind_address: SocketAddr::new( + IpAddr::V6(Ipv6Addr::UNSPECIFIED), + Self::DEFAULT_DATA_PORT, + ), + announce_control_port: None, + announce_data_port: None, + debug: Default::default(), + } + } +} + +impl LpDebugV12 { + pub const DEFAULT_MAX_CONNECTIONS: usize = 10000; + pub const DEFAULT_TIMESTAMP_TOLERANCE: Duration = Duration::from_secs(30); + pub const DEFAULT_HANDSHAKE_TTL: Duration = Duration::from_secs(90); + pub const DEFAULT_SESSION_TTL: Duration = Duration::from_secs(86400); + pub const DEFAULT_STATE_CLEANUP_INTERVAL: Duration = Duration::from_secs(300); + pub const DEFAULT_MAX_CONCURRENT_FORWARDS: usize = 1000; +} + +impl Default for LpDebugV12 { + fn default() -> Self { + LpDebugV12 { + max_connections: Self::DEFAULT_MAX_CONNECTIONS, + timestamp_tolerance: Self::DEFAULT_TIMESTAMP_TOLERANCE, + use_mock_ecash: false, + handshake_ttl: Self::DEFAULT_HANDSHAKE_TTL, + session_ttl: Self::DEFAULT_SESSION_TTL, + state_cleanup_interval: Self::DEFAULT_STATE_CLEANUP_INTERVAL, + max_concurrent_forwards: Self::DEFAULT_MAX_CONCURRENT_FORWARDS, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct GatewayTasksConfigV12 { + pub storage_paths: GatewayTasksPathsV12, + + /// Indicates whether this gateway is accepting only zk-nym credentials for accessing the mixnet + /// or if it also accepts non-paying clients + pub enforce_zk_nyms: bool, + + /// Socket address this node will use for binding its client websocket API. + /// default: `[::]:9000` + pub ws_bind_address: SocketAddr, + + /// Custom announced port for listening for websocket client traffic. + /// If unspecified, the value from the `bind_address` will be used instead + /// default: None + #[serde(deserialize_with = "de_maybe_port")] + pub announce_ws_port: Option, + + /// If applicable, announced port for listening for secure websocket client traffic. + /// (default: None) + #[serde(deserialize_with = "de_maybe_port")] + pub announce_wss_port: Option, + + pub upgrade_mode: UpgradeModeWatcherV12, + + #[serde(default)] + pub lp: LpConfigV12, + + #[serde(default)] + pub debug: GatewayTasksConfigDebugV12, +} + +#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct DebugV12 { + /// Specifies the time to live of the internal topology provider cache. + #[serde(with = "humantime_serde")] + pub topology_cache_ttl: Duration, + + /// Specifies the time between attempting to resolve any pending unknown nodes in the routing filter + #[serde(with = "humantime_serde")] + pub routing_nodes_check_interval: Duration, + + /// Specifies whether this node runs in testnet mode thus allowing it to route packets on local interfaces + pub testnet: bool, +} + +impl DebugV12 { + pub const DEFAULT_TOPOLOGY_CACHE_TTL: Duration = Duration::from_secs(10 * 60); + pub const DEFAULT_ROUTING_NODES_CHECK_INTERVAL: Duration = Duration::from_secs(5 * 60); +} + +impl Default for DebugV12 { + fn default() -> Self { + DebugV12 { + topology_cache_ttl: Self::DEFAULT_TOPOLOGY_CACHE_TTL, + routing_nodes_check_interval: Self::DEFAULT_ROUTING_NODES_CHECK_INTERVAL, + testnet: false, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigV12 { + // additional metadata holding on-disk location of this config file + #[serde(skip)] + pub(crate) save_path: Option, + + /// Human-readable ID of this particular node. + pub id: String, + + /// Current modes of this nym-node. + pub modes: NodeModesV12, + + pub host: HostV12, + + pub mixnet: MixnetV12, + + /// Storage paths to persistent nym-node data, such as its long term keys. + pub storage_paths: NymNodePathsV12, + + #[serde(default)] + pub http: HttpV12, + + #[serde(default)] + pub verloc: VerlocV12, + + pub wireguard: WireguardV12, + + #[serde(alias = "entry_gateway")] + pub gateway_tasks: GatewayTasksConfigV12, + + #[serde(alias = "exit_gateway")] + pub service_providers: ServiceProvidersConfigV12, + + #[serde(default)] + pub metrics: MetricsConfigV12, + + #[serde(default)] + pub logging: LoggingSettingsV12, + + #[serde(default)] + pub debug: DebugV12, +} + +impl ConfigV12 { + // simple wrapper that reads config file and assigns path location + fn read_from_path>(path: P) -> Result { + let path = path.as_ref(); + let mut loaded: ConfigV12 = + read_config_from_toml_file(path).map_err(|source| NymNodeError::ConfigLoadFailure { + path: path.to_path_buf(), + source, + })?; + loaded.save_path = Some(path.to_path_buf()); + debug!("loaded config file from {}", path.display()); + Ok(loaded) + } +} + +#[instrument(skip_all)] +pub async fn try_upgrade_config_v12>( + path: P, + prev_config: Option, +) -> Result { + debug!("attempting to load v12 config..."); + + let old_cfg = if let Some(prev_config) = prev_config { + prev_config + } else { + ConfigV12::read_from_path(&path)? + }; + + info!("migrating the old config (v12)..."); + let keys_dir = old_cfg + .storage_paths + .keys + .public_ed25519_identity_key_file + .parent() + .ok_or(NymNodeError::DataDirDerivationFailure)? + .to_path_buf(); + + let updated_keys = KeysPaths { + private_ed25519_identity_key_file: old_cfg + .storage_paths + .keys + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .storage_paths + .keys + .public_ed25519_identity_key_file, + primary_x25519_sphinx_key_file: old_cfg.storage_paths.keys.primary_x25519_sphinx_key_file, + secondary_x25519_sphinx_key_file: old_cfg + .storage_paths + .keys + .secondary_x25519_sphinx_key_file, + private_x25519_noise_key_file: old_cfg.storage_paths.keys.private_x25519_noise_key_file, + public_x25519_noise_key_file: old_cfg.storage_paths.keys.public_x25519_noise_key_file, + private_x25519_lp_key_file: keys_dir.join(DEFAULT_X25519_PRIVATE_LP_KEY_FILENAME), + public_x25519_lp_key_file: keys_dir.join(DEFAULT_X25519_PUBLIC_LP_KEY_FILENAME), + private_mlkem768_lp_key_file: keys_dir.join(DEFAULT_MLKEM768_PRIVATE_KEY_FILENAME), + public_mlkem768_lp_key_file: keys_dir.join(DEFAULT_MLKEM768_PUBLIC_KEY_FILENAME), + private_mceliece_lp_key_file: keys_dir.join(DEFAULT_MCELIECE_PRIVATE_KEY_FILENAME), + public_mceliece_lp_key_file: keys_dir.join(DEFAULT_MCELIECE_PUBLIC_KEY_FILENAME), + }; + + let mut rng = rand09::rngs::StdRng::from_os_rng(); + + // generate new keys for LP + info!("generating new LP x25519 DH keypair"); + let x25519 = generate_keypair_x25519(&mut rng); + let paths = updated_keys.x25519_lp_key_paths(); + store_x25519_lp_keypair(&x25519, &paths)?; + + info!("generating new mlkem768 keypair"); + let mlkem = generate_keypair_mlkem(&mut rng); + let paths = updated_keys.mlkem768_key_paths(); + store_mlkem768_keypair(&mlkem, &paths)?; + + info!("generating mceliece keypair"); + let mceliece = generate_keypair_mceliece(&mut rng); + let paths = updated_keys.mceliece_key_paths(); + store_mceliece_keypair(&mceliece, &paths)?; + + let cfg = Config { + save_path: old_cfg.save_path, + id: old_cfg.id, + modes: NodeModes { + mixnode: old_cfg.modes.mixnode, + entry: old_cfg.modes.entry, + exit: old_cfg.modes.exit, + }, + host: Host { + public_ips: old_cfg.host.public_ips, + hostname: old_cfg.host.hostname, + location: old_cfg.host.location, + }, + mixnet: Mixnet { + bind_address: old_cfg.mixnet.bind_address, + announce_port: old_cfg.mixnet.announce_port, + nym_api_urls: old_cfg.mixnet.nym_api_urls, + nyxd_urls: old_cfg.mixnet.nyxd_urls, + replay_protection: ReplayProtection { + storage_paths: ReplayProtectionPaths { + current_bloomfilters_directory: old_cfg + .mixnet + .replay_protection + .storage_paths + .current_bloomfilters_directory, + }, + debug: ReplayProtectionDebug { + unsafe_disabled: old_cfg.mixnet.replay_protection.debug.unsafe_disabled, + maximum_replay_detection_deferral: old_cfg + .mixnet + .replay_protection + .debug + .maximum_replay_detection_deferral, + maximum_replay_detection_pending_packets: old_cfg + .mixnet + .replay_protection + .debug + .maximum_replay_detection_pending_packets, + false_positive_rate: old_cfg.mixnet.replay_protection.debug.false_positive_rate, + initial_expected_packets_per_second: old_cfg + .mixnet + .replay_protection + .debug + .initial_expected_packets_per_second, + bloomfilter_minimum_packets_per_second_size: old_cfg + .mixnet + .replay_protection + .debug + .bloomfilter_minimum_packets_per_second_size, + bloomfilter_size_multiplier: old_cfg + .mixnet + .replay_protection + .debug + .bloomfilter_size_multiplier, + bloomfilter_disk_flushing_rate: old_cfg + .mixnet + .replay_protection + .debug + .bloomfilter_disk_flushing_rate, + }, + }, + key_rotation: KeyRotation { + debug: KeyRotationDebug { + rotation_state_poling_interval: old_cfg + .mixnet + .key_rotation + .debug + .rotation_state_poling_interval, + }, + }, + debug: MixnetDebug { + maximum_forward_packet_delay: old_cfg.mixnet.debug.maximum_forward_packet_delay, + packet_forwarding_initial_backoff: old_cfg + .mixnet + .debug + .packet_forwarding_initial_backoff, + packet_forwarding_maximum_backoff: old_cfg + .mixnet + .debug + .packet_forwarding_maximum_backoff, + initial_connection_timeout: old_cfg.mixnet.debug.initial_connection_timeout, + maximum_connection_buffer_size: old_cfg.mixnet.debug.maximum_connection_buffer_size, + unsafe_disable_noise: old_cfg.mixnet.debug.unsafe_disable_noise, + use_legacy_packet_encoding: old_cfg.mixnet.debug.use_legacy_packet_encoding, + }, + }, + storage_paths: NymNodePaths { + keys: updated_keys, + description: old_cfg.storage_paths.description, + }, + http: Http { + bind_address: old_cfg.http.bind_address, + landing_page_assets_path: old_cfg.http.landing_page_assets_path, + access_token: old_cfg.http.access_token, + expose_system_info: old_cfg.http.expose_system_info, + expose_system_hardware: old_cfg.http.expose_system_hardware, + expose_crypto_hardware: old_cfg.http.expose_crypto_hardware, + node_load_cache_ttl: old_cfg.http.node_load_cache_ttl, + }, + verloc: Verloc { + bind_address: old_cfg.verloc.bind_address, + announce_port: old_cfg.verloc.announce_port, + debug: VerlocDebug { + packets_per_node: old_cfg.verloc.debug.packets_per_node, + connection_timeout: old_cfg.verloc.debug.connection_timeout, + packet_timeout: old_cfg.verloc.debug.packet_timeout, + delay_between_packets: old_cfg.verloc.debug.delay_between_packets, + tested_nodes_batch_size: old_cfg.verloc.debug.tested_nodes_batch_size, + testing_interval: old_cfg.verloc.debug.testing_interval, + retry_timeout: old_cfg.verloc.debug.retry_timeout, + }, + }, + wireguard: Wireguard { + enabled: old_cfg.wireguard.enabled, + bind_address: old_cfg.wireguard.bind_address, + private_ipv4: old_cfg.wireguard.private_ipv4, + private_ipv6: old_cfg.wireguard.private_ipv6, + announced_tunnel_port: old_cfg.wireguard.announced_tunnel_port, + announced_metadata_port: old_cfg.wireguard.announced_metadata_port, + private_network_prefix_v4: old_cfg.wireguard.private_network_prefix_v4, + private_network_prefix_v6: old_cfg.wireguard.private_network_prefix_v6, + use_userspace: old_cfg.wireguard.use_userspace, + storage_paths: WireguardPaths { + private_diffie_hellman_key_file: old_cfg + .wireguard + .storage_paths + .private_diffie_hellman_key_file, + public_diffie_hellman_key_file: old_cfg + .wireguard + .storage_paths + .public_diffie_hellman_key_file, + }, + }, + gateway_tasks: GatewayTasksConfig { + storage_paths: GatewayTasksPaths { + clients_storage: old_cfg.gateway_tasks.storage_paths.clients_storage, + stats_storage: old_cfg.gateway_tasks.storage_paths.stats_storage, + cosmos_mnemonic: old_cfg.gateway_tasks.storage_paths.cosmos_mnemonic, + bridge_client_params: old_cfg.gateway_tasks.storage_paths.bridge_client_params, + }, + enforce_zk_nyms: old_cfg.gateway_tasks.enforce_zk_nyms, + ws_bind_address: old_cfg.gateway_tasks.ws_bind_address, + announce_ws_port: old_cfg.gateway_tasks.announce_ws_port, + announce_wss_port: old_cfg.gateway_tasks.announce_wss_port, + upgrade_mode: UpgradeModeWatcher { + enabled: old_cfg.gateway_tasks.upgrade_mode.enabled, + attestation_url: old_cfg.gateway_tasks.upgrade_mode.attestation_url, + attester_public_key: old_cfg.gateway_tasks.upgrade_mode.attester_public_key, + debug: UpgradeModeWatcherDebug { + regular_polling_interval: old_cfg + .gateway_tasks + .upgrade_mode + .debug + .regular_polling_interval, + expedited_poll_interval: old_cfg + .gateway_tasks + .upgrade_mode + .debug + .expedited_poll_interval, + }, + }, + lp: LpConfig { + control_bind_address: old_cfg.gateway_tasks.lp.control_bind_address, + data_bind_address: old_cfg.gateway_tasks.lp.data_bind_address, + announce_control_port: old_cfg.gateway_tasks.lp.announce_control_port, + announce_data_port: old_cfg.gateway_tasks.lp.announce_data_port, + debug: LpDebug { + max_connections: old_cfg.gateway_tasks.lp.debug.max_connections, + use_mock_ecash: old_cfg.gateway_tasks.lp.debug.use_mock_ecash, + handshake_ttl: old_cfg.gateway_tasks.lp.debug.handshake_ttl, + session_ttl: old_cfg.gateway_tasks.lp.debug.session_ttl, + state_cleanup_interval: old_cfg.gateway_tasks.lp.debug.state_cleanup_interval, + max_concurrent_forwards: old_cfg.gateway_tasks.lp.debug.max_concurrent_forwards, + }, + }, + debug: gateway_tasks::Debug { + message_retrieval_limit: old_cfg.gateway_tasks.debug.message_retrieval_limit, + maximum_open_connections: old_cfg.gateway_tasks.debug.maximum_open_connections, + minimum_mix_performance: old_cfg.gateway_tasks.debug.minimum_mix_performance, + max_request_timestamp_skew: old_cfg.gateway_tasks.debug.max_request_timestamp_skew, + stale_messages: StaleMessageDebug { + cleaner_run_interval: old_cfg + .gateway_tasks + .debug + .stale_messages + .cleaner_run_interval, + max_age: old_cfg.gateway_tasks.debug.stale_messages.max_age, + }, + client_bandwidth: ClientBandwidthDebug { + max_flushing_rate: old_cfg + .gateway_tasks + .debug + .client_bandwidth + .max_flushing_rate, + max_delta_flushing_amount: old_cfg + .gateway_tasks + .debug + .client_bandwidth + .max_delta_flushing_amount, + }, + zk_nym_tickets: ZkNymTicketHandlerDebug { + revocation_bandwidth_penalty: old_cfg + .gateway_tasks + .debug + .zk_nym_tickets + .revocation_bandwidth_penalty, + pending_poller: old_cfg.gateway_tasks.debug.zk_nym_tickets.pending_poller, + minimum_api_quorum: old_cfg + .gateway_tasks + .debug + .zk_nym_tickets + .minimum_api_quorum, + minimum_redemption_tickets: old_cfg + .gateway_tasks + .debug + .zk_nym_tickets + .minimum_redemption_tickets, + maximum_time_between_redemption: old_cfg + .gateway_tasks + .debug + .zk_nym_tickets + .maximum_time_between_redemption, + }, + upgrade_mode_min_staleness_recheck: old_cfg + .gateway_tasks + .debug + .upgrade_mode_min_staleness_recheck, + }, + }, + service_providers: ServiceProvidersConfig { + storage_paths: ServiceProvidersPaths { + clients_storage: old_cfg.service_providers.storage_paths.clients_storage, + stats_storage: old_cfg.service_providers.storage_paths.stats_storage, + network_requester: NetworkRequesterPaths { + private_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .service_providers + .storage_paths + .network_requester + .ack_key_file, + reply_surb_database: old_cfg + .service_providers + .storage_paths + .network_requester + .reply_surb_database, + gateway_registrations: old_cfg + .service_providers + .storage_paths + .network_requester + .gateway_registrations, + }, + ip_packet_router: IpPacketRouterPaths { + private_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .ack_key_file, + reply_surb_database: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .reply_surb_database, + gateway_registrations: old_cfg + .service_providers + .storage_paths + .ip_packet_router + .gateway_registrations, + }, + authenticator: AuthenticatorPaths { + private_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .private_ed25519_identity_key_file, + public_ed25519_identity_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .public_ed25519_identity_key_file, + private_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .private_x25519_diffie_hellman_key_file, + public_x25519_diffie_hellman_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .public_x25519_diffie_hellman_key_file, + ack_key_file: old_cfg + .service_providers + .storage_paths + .authenticator + .ack_key_file, + reply_surb_database: old_cfg + .service_providers + .storage_paths + .authenticator + .reply_surb_database, + gateway_registrations: old_cfg + .service_providers + .storage_paths + .authenticator + .gateway_registrations, + }, + }, + open_proxy: old_cfg.service_providers.open_proxy, + upstream_exit_policy_url: old_cfg.service_providers.upstream_exit_policy_url, + network_requester: NetworkRequester { + debug: NetworkRequesterDebug { + enabled: old_cfg.service_providers.network_requester.debug.enabled, + disable_poisson_rate: old_cfg + .service_providers + .network_requester + .debug + .disable_poisson_rate, + client_debug: old_cfg + .service_providers + .network_requester + .debug + .client_debug, + }, + }, + ip_packet_router: IpPacketRouter { + debug: IpPacketRouterDebug { + enabled: old_cfg.service_providers.ip_packet_router.debug.enabled, + disable_poisson_rate: old_cfg + .service_providers + .ip_packet_router + .debug + .disable_poisson_rate, + client_debug: old_cfg + .service_providers + .ip_packet_router + .debug + .client_debug, + }, + }, + authenticator: Authenticator { + debug: AuthenticatorDebug { + enabled: old_cfg.service_providers.authenticator.debug.enabled, + disable_poisson_rate: old_cfg + .service_providers + .authenticator + .debug + .disable_poisson_rate, + client_debug: old_cfg.service_providers.authenticator.debug.client_debug, + }, + }, + debug: service_providers::Debug { + message_retrieval_limit: old_cfg.service_providers.debug.message_retrieval_limit, + }, + }, + metrics: MetricsConfig { + debug: metrics::Debug { + log_stats_to_console: false, + aggregator_update_rate: Default::default(), + stale_mixnet_metrics_cleaner_rate: Default::default(), + global_prometheus_counters_update_rate: Default::default(), + pending_egress_packets_update_rate: Default::default(), + clients_sessions_update_rate: Default::default(), + console_logging_update_interval: Default::default(), + legacy_mixing_metrics_update_rate: Default::default(), + }, + }, + logging: LoggingSettings {}, + debug: Debug { + topology_cache_ttl: old_cfg.debug.topology_cache_ttl, + routing_nodes_check_interval: old_cfg.debug.routing_nodes_check_interval, + testnet: old_cfg.debug.testnet, + }, + }; + Ok(cfg) +} diff --git a/nym-node/src/config/persistence.rs b/nym-node/src/config/persistence.rs index 1870d69203..4d0a5c645c 100644 --- a/nym-node/src/config/persistence.rs +++ b/nym-node/src/config/persistence.rs @@ -20,6 +20,8 @@ pub const DEFAULT_X25519_PUBLIC_NOISE_KEY_FILENAME: &str = "x25519_noise.pub"; pub const DEFAULT_NYMNODE_DESCRIPTION_FILENAME: &str = "description.toml"; // Global/LP: +pub const DEFAULT_X25519_PRIVATE_LP_KEY_FILENAME: &str = "x25519_lp"; +pub const DEFAULT_X25519_PUBLIC_LP_KEY_FILENAME: &str = "x25519_lp.pub"; pub const DEFAULT_MLKEM768_PRIVATE_KEY_FILENAME: &str = "mlkem768"; pub const DEFAULT_MLKEM768_PUBLIC_KEY_FILENAME: &str = "mlkem768.pub"; pub const DEFAULT_MCELIECE_PRIVATE_KEY_FILENAME: &str = "mceliece"; @@ -105,6 +107,26 @@ pub struct KeysPaths { /// Path to file containing x25519 noise public key. pub public_x25519_noise_key_file: PathBuf, + + // >> LP KEYS START: + /// Path to file containing x25519 lp private key. + pub private_x25519_lp_key_file: PathBuf, + + /// Path to file containing x25519 lp public key. + pub public_x25519_lp_key_file: PathBuf, + + /// Path to file containing mlkem768 lp private key. + pub private_mlkem768_lp_key_file: PathBuf, + + /// Path to file containing mlkem768 lp public key. + pub public_mlkem768_lp_key_file: PathBuf, + + /// Path to file containing mceliece lp private key. + pub private_mceliece_lp_key_file: PathBuf, + + /// Path to file containing mceliece lp public key. + pub public_mceliece_lp_key_file: PathBuf, + // >> LP KEYS END } impl KeysPaths { @@ -122,6 +144,12 @@ impl KeysPaths { .join(DEFAULT_SECONDARY_X25519_SPHINX_KEY_FILENAME), private_x25519_noise_key_file: data_dir.join(DEFAULT_X25519_PRIVATE_NOISE_KEY_FILENAME), public_x25519_noise_key_file: data_dir.join(DEFAULT_X25519_PUBLIC_NOISE_KEY_FILENAME), + private_x25519_lp_key_file: data_dir.join(DEFAULT_X25519_PRIVATE_LP_KEY_FILENAME), + public_x25519_lp_key_file: data_dir.join(DEFAULT_X25519_PUBLIC_LP_KEY_FILENAME), + private_mlkem768_lp_key_file: data_dir.join(DEFAULT_MLKEM768_PRIVATE_KEY_FILENAME), + public_mlkem768_lp_key_file: data_dir.join(DEFAULT_MLKEM768_PUBLIC_KEY_FILENAME), + private_mceliece_lp_key_file: data_dir.join(DEFAULT_MCELIECE_PRIVATE_KEY_FILENAME), + public_mceliece_lp_key_file: data_dir.join(DEFAULT_MCELIECE_PUBLIC_KEY_FILENAME), } } @@ -138,6 +166,27 @@ impl KeysPaths { &self.public_x25519_noise_key_file, ) } + + pub fn x25519_lp_key_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_x25519_lp_key_file, + &self.public_x25519_lp_key_file, + ) + } + + pub fn mlkem768_key_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_mlkem768_lp_key_file, + &self.public_mlkem768_lp_key_file, + ) + } + + pub fn mceliece_key_paths(&self) -> nym_pemstore::KeyPairPath { + nym_pemstore::KeyPairPath::new( + &self.private_mceliece_lp_key_file, + &self.public_mceliece_lp_key_file, + ) + } } #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize)] diff --git a/nym-node/src/config/upgrade_helpers.rs b/nym-node/src/config/upgrade_helpers.rs index b2b8018231..8c556b409d 100644 --- a/nym-node/src/config/upgrade_helpers.rs +++ b/nym-node/src/config/upgrade_helpers.rs @@ -18,7 +18,8 @@ async fn try_upgrade_config(path: &Path) -> Result<(), NymNodeError> { let cfg = try_upgrade_config_v8(path, cfg).await.ok(); let cfg = try_upgrade_config_v9(path, cfg).await.ok(); let cfg = try_upgrade_config_v10(path, cfg).await.ok(); - match try_upgrade_config_v11(path, cfg).await { + let cfg = try_upgrade_config_v11(path, cfg).await.ok(); + match try_upgrade_config_v12(path, cfg).await { Ok(cfg) => cfg.save(), Err(e) => { tracing::error!("Failed to finish upgrade: {e}"); diff --git a/nym-node/src/error.rs b/nym-node/src/error.rs index abe336dc44..ca6f0c7c65 100644 --- a/nym-node/src/error.rs +++ b/nym-node/src/error.rs @@ -5,6 +5,7 @@ use crate::node::http::error::NymNodeHttpError; use crate::wireguard::error::WireguardError; use nym_http_api_client::HttpClientError; use nym_ip_packet_router::error::ClientCoreError; +use nym_kkt::keys::storage_wrappers::MalformedStoredKeyError; use nym_validator_client::ValidatorClientError; use nym_validator_client::nyxd::error::NyxdError; use std::io; @@ -231,6 +232,9 @@ pub enum NymNodeError { #[error("failed upgrade")] FailedUpgrade, + + #[error(transparent)] + MalformedStoredKey(#[from] MalformedStoredKeyError), } impl From for NymNodeError { diff --git a/nym-node/src/node/helpers.rs b/nym-node/src/node/helpers.rs index b0112a7004..dfe41d67ad 100644 --- a/nym-node/src/node/helpers.rs +++ b/nym-node/src/node/helpers.rs @@ -6,6 +6,10 @@ use crate::error::{KeyIOFailure, NymNodeError}; use crate::node::key_rotation::key::{SphinxPrivateKey, SphinxPublicKey}; use crate::node::nym_apis_client::NymApisClient; use nym_crypto::asymmetric::{ed25519, x25519}; +use nym_kkt::keys::storage_wrappers::StorableKey; +use nym_kkt::keys::{ + DHKeyPair, DHPrivateKey, MlKem768KeyPair, MlKem768PrivateKey, MlKem768PublicKey, mceliece, +}; use nym_node_requests::api::v1::node::models::NodeDescription; use nym_pemstore::KeyPairPath; use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair}; @@ -169,6 +173,34 @@ pub(crate) fn load_x25519_wireguard_keypair( Ok(load_keypair(paths, "x25519-wireguard")?) } +fn load_lp_key(path: P, name: &'static str) -> Result +where + P: AsRef, + S: StorableKey, +{ + let repr = load_key::<::StorableRepresentation<'_>, _>(path, name)?; + Ok(S::from_storable(repr)?) +} + +pub(crate) fn load_x25519_lp_keypair(paths: &KeyPairPath) -> Result { + let pk: DHPrivateKey = load_lp_key(&paths.private_key_path, "x25519-lp-private-key")?; + Ok(DHKeyPair::from(pk)) +} + +pub(crate) fn load_mlkem768_keypair(paths: &KeyPairPath) -> Result { + let sk: MlKem768PrivateKey = load_lp_key(&paths.private_key_path, "mlkem768-private-key")?; + let pk: MlKem768PublicKey = load_lp_key(&paths.public_key_path, "mlkem768-public-key")?; + Ok(MlKem768KeyPair::from(sk, pk)) +} + +pub(crate) fn load_mceliece_keypair( + paths: &KeyPairPath, +) -> Result { + let sk: mceliece::SecretKey = load_lp_key(&paths.private_key_path, "mceliece-private-key")?; + let pk: mceliece::PublicKey = load_lp_key(&paths.public_key_path, "mceliece-public-key")?; + Ok(mceliece::KeyPair { sk, pk }) +} + pub(crate) fn store_ed25519_identity_keypair( keys: &ed25519::KeyPair, paths: &KeyPairPath, @@ -183,6 +215,57 @@ pub(crate) fn store_x25519_noise_keypair( Ok(store_keypair(keys, paths, "x25519-noise")?) } +pub(crate) fn store_x25519_lp_keypair( + keys: &DHKeyPair, + paths: &KeyPairPath, +) -> Result<(), NymNodeError> { + store_key( + &keys.pk.to_storable(), + &paths.public_key_path, + "x25519-lp-public-key", + )?; + store_key( + &keys.sk().to_storable(), + &paths.private_key_path, + "x25519-lp-private-key", + )?; + Ok(()) +} + +pub(crate) fn store_mlkem768_keypair( + keys: &MlKem768KeyPair, + paths: &KeyPairPath, +) -> Result<(), NymNodeError> { + store_key( + &keys.public_key().to_storable(), + &paths.public_key_path, + "mlkem768-public-key", + )?; + store_key( + &keys.private_key().to_storable(), + &paths.private_key_path, + "mlkem768-private-key", + )?; + Ok(()) +} + +pub(crate) fn store_mceliece_keypair( + keys: &mceliece::KeyPair, + paths: &KeyPairPath, +) -> Result<(), NymNodeError> { + store_key( + &keys.pk.to_storable(), + &paths.public_key_path, + "mceliece-public-key", + )?; + store_key( + &keys.sk.to_storable(), + &paths.private_key_path, + "mceliece-private-key", + )?; + Ok(()) +} + pub(crate) async fn get_current_rotation_id( nym_apis: &[Url], fallback_nyxd: &[Url],