Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c0babf35a | |||
| b6f234259c | |||
| 7d8d1e9d6d | |||
| 3b75af34e8 | |||
| 1a3c1fa466 |
Generated
+757
-446
File diff suppressed because it is too large
Load Diff
+1
-2
@@ -256,8 +256,7 @@ ctr = "0.9.1"
|
|||||||
cupid = "0.6.1"
|
cupid = "0.6.1"
|
||||||
curve25519-dalek = "4.1.3"
|
curve25519-dalek = "4.1.3"
|
||||||
dashmap = "5.5.3"
|
dashmap = "5.5.3"
|
||||||
# We want https://github.com/DefGuard/wireguard-rs/pull/64 , but there's no crates.io release being pushed out anymore
|
defguard_wireguard_rs = "0.8.0"
|
||||||
defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.4.7" }
|
|
||||||
digest = "0.10.7"
|
digest = "0.10.7"
|
||||||
dirs = "6.0"
|
dirs = "6.0"
|
||||||
dotenvy = "0.15.6"
|
dotenvy = "0.15.6"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.1.69"
|
version = "1.1.68"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||||
description = "Implementation of the Nym Client"
|
description = "Implementation of the Nym Client"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.1.69"
|
version = "1.1.68"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub use serde_helpers::*;
|
|||||||
#[cfg(feature = "sphinx")]
|
#[cfg(feature = "sphinx")]
|
||||||
use nym_sphinx_types::{DESTINATION_ADDRESS_LENGTH, DestinationAddressBytes};
|
use nym_sphinx_types::{DESTINATION_ADDRESS_LENGTH, DestinationAddressBytes};
|
||||||
|
|
||||||
|
use crate::asymmetric::x25519;
|
||||||
#[cfg(feature = "rand")]
|
#[cfg(feature = "rand")]
|
||||||
use rand::{CryptoRng, Rng, RngCore};
|
use rand::{CryptoRng, Rng, RngCore};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
@@ -110,6 +111,18 @@ impl KeyPair {
|
|||||||
index: fake_index(pub_bytes),
|
index: fake_index(pub_bytes),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts this Ed25519 keypair to an X25519 keypair for ECDH.
|
||||||
|
///
|
||||||
|
/// Uses the standard ed25519→x25519 conversion via SHA-512 hash and clamping.
|
||||||
|
/// This is the same approach as libsodium's `crypto_sign_ed25519_sk_to_curve25519`.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// The converted X25519 keypair
|
||||||
|
pub fn to_x25519(&self) -> x25519::KeyPair {
|
||||||
|
let private_key = self.private_key.to_x25519();
|
||||||
|
x25519::KeyPair::from(private_key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reduces a byte slice into a u32 value by XOR-ing all its bytes into a 4-byte accumulator.
|
/// Reduces a byte slice into a u32 value by XOR-ing all its bytes into a 4-byte accumulator.
|
||||||
@@ -136,6 +149,16 @@ impl From<PrivateKey> for KeyPair {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<(PrivateKey, PublicKey)> for KeyPair {
|
||||||
|
fn from((private_key, public_key): (PrivateKey, PublicKey)) -> Self {
|
||||||
|
KeyPair {
|
||||||
|
private_key,
|
||||||
|
public_key,
|
||||||
|
index: fake_index(public_key.to_bytes().as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PemStorableKeyPair for KeyPair {
|
impl PemStorableKeyPair for KeyPair {
|
||||||
type PrivatePemKey = PrivateKey;
|
type PrivatePemKey = PrivateKey;
|
||||||
type PublicPemKey = PublicKey;
|
type PublicPemKey = PublicKey;
|
||||||
@@ -185,14 +208,25 @@ impl PublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this public key to a byte array.
|
/// Convert this public key to a byte array.
|
||||||
|
#[inline]
|
||||||
pub fn to_bytes(self) -> [u8; PUBLIC_KEY_LENGTH] {
|
pub fn to_bytes(self) -> [u8; PUBLIC_KEY_LENGTH] {
|
||||||
self.0.to_bytes()
|
self.0.to_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// View this public key as a byte array.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
|
||||||
|
self.0.as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn from_bytes(b: &[u8]) -> Result<Self, Ed25519RecoveryError> {
|
pub fn from_bytes(b: &[u8]) -> Result<Self, Ed25519RecoveryError> {
|
||||||
Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(
|
Self::from_byte_array(b.try_into()?)
|
||||||
b.try_into()?,
|
}
|
||||||
)?))
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_byte_array(b: &[u8; PUBLIC_KEY_LENGTH]) -> Result<Self, Ed25519RecoveryError> {
|
||||||
|
Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(b)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_base58_string(self) -> String {
|
pub fn to_base58_string(self) -> String {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||||
use std::fmt::{self, Debug, Display, Formatter};
|
use std::fmt::{self, Debug, Display, Formatter};
|
||||||
|
use std::ops::Deref;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
@@ -56,6 +57,15 @@ pub struct KeyPair {
|
|||||||
pub(crate) public_key: PublicKey,
|
pub(crate) public_key: PublicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for KeyPair {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("KeyPair")
|
||||||
|
.field("private_key", &"<redacted>")
|
||||||
|
.field("public_key", &self.public_key.to_base58_string())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl KeyPair {
|
impl KeyPair {
|
||||||
#[cfg(feature = "rand")]
|
#[cfg(feature = "rand")]
|
||||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||||
@@ -93,6 +103,15 @@ impl From<PrivateKey> for KeyPair {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<(PrivateKey, PublicKey)> for KeyPair {
|
||||||
|
fn from((private_key, public_key): (PrivateKey, PublicKey)) -> Self {
|
||||||
|
KeyPair {
|
||||||
|
private_key,
|
||||||
|
public_key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PemStorableKeyPair for KeyPair {
|
impl PemStorableKeyPair for KeyPair {
|
||||||
type PrivatePemKey = PrivateKey;
|
type PrivatePemKey = PrivateKey;
|
||||||
type PublicPemKey = PublicKey;
|
type PublicPemKey = PublicKey;
|
||||||
@@ -116,6 +135,13 @@ impl PemStorableKeyPair for KeyPair {
|
|||||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||||
pub struct PublicKey(x25519_dalek::PublicKey);
|
pub struct PublicKey(x25519_dalek::PublicKey);
|
||||||
|
|
||||||
|
impl Deref for PublicKey {
|
||||||
|
type Target = x25519_dalek::PublicKey;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for PublicKey {
|
impl Display for PublicKey {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
Display::fmt(&self.to_base58_string(), f)
|
Display::fmt(&self.to_base58_string(), f)
|
||||||
@@ -129,14 +155,17 @@ impl Debug for PublicKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
|
#[inline]
|
||||||
pub fn to_bytes(self) -> [u8; PUBLIC_KEY_SIZE] {
|
pub fn to_bytes(self) -> [u8; PUBLIC_KEY_SIZE] {
|
||||||
*self.0.as_bytes()
|
*self.0.as_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] {
|
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] {
|
||||||
self.0.as_bytes()
|
self.0.as_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn from_bytes(b: &[u8]) -> Result<Self, KeyRecoveryError> {
|
pub fn from_bytes(b: &[u8]) -> Result<Self, KeyRecoveryError> {
|
||||||
if b.len() != PUBLIC_KEY_SIZE {
|
if b.len() != PUBLIC_KEY_SIZE {
|
||||||
return Err(KeyRecoveryError::InvalidSizePublicKey {
|
return Err(KeyRecoveryError::InvalidSizePublicKey {
|
||||||
@@ -146,7 +175,12 @@ impl PublicKey {
|
|||||||
}
|
}
|
||||||
let mut bytes = [0; PUBLIC_KEY_SIZE];
|
let mut bytes = [0; PUBLIC_KEY_SIZE];
|
||||||
bytes.copy_from_slice(&b[..PUBLIC_KEY_SIZE]);
|
bytes.copy_from_slice(&b[..PUBLIC_KEY_SIZE]);
|
||||||
Ok(Self(x25519_dalek::PublicKey::from(bytes)))
|
Ok(Self::from_byte_array(&bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn from_byte_array(b: &[u8; PUBLIC_KEY_SIZE]) -> Self {
|
||||||
|
Self(x25519_dalek::PublicKey::from(*b))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_base58_string(self) -> String {
|
pub fn to_base58_string(self) -> String {
|
||||||
@@ -174,6 +208,12 @@ impl PublicKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<[u8; PUBLIC_KEY_SIZE]> for PublicKey {
|
||||||
|
fn from(bytes: [u8; PUBLIC_KEY_SIZE]) -> Self {
|
||||||
|
PublicKey(x25519_dalek::PublicKey::from(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for PublicKey {
|
impl FromStr for PublicKey {
|
||||||
type Err = KeyRecoveryError;
|
type Err = KeyRecoveryError;
|
||||||
|
|
||||||
@@ -296,6 +336,10 @@ impl PrivateKey {
|
|||||||
Ok(Self(x25519_dalek::StaticSecret::from(bytes)))
|
Ok(Self(x25519_dalek::StaticSecret::from(bytes)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_secret(secret: [u8; PRIVATE_KEY_SIZE]) -> Self {
|
||||||
|
Self(x25519_dalek::StaticSecret::from(secret))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_base58_string(&self) -> String {
|
pub fn to_base58_string(&self) -> String {
|
||||||
bs58::encode(&self.to_bytes()).into_string()
|
bs58::encode(&self.to_bytes()).into_string()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ dotenvy = { workspace = true, optional = true }
|
|||||||
log = { workspace = true, optional = true }
|
log = { workspace = true, optional = true }
|
||||||
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||||
serde = { workspace = true, features = ["derive"], optional = true }
|
serde = { workspace = true, features = ["derive"], optional = true }
|
||||||
serde_json = {workspace = true, optional = true }
|
|
||||||
tracing = {workspace = true, optional = true }
|
|
||||||
url = { workspace = true, optional = true }
|
url = { workspace = true, optional = true }
|
||||||
utoipa = { workspace = true, optional = true }
|
utoipa = { workspace = true, optional = true }
|
||||||
|
|
||||||
@@ -22,7 +20,7 @@ utoipa = { workspace = true, optional = true }
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["env", "network"]
|
default = ["env", "network"]
|
||||||
env = ["dotenvy", "log", "serde_json", "tracing"]
|
env = ["dotenvy", "log"]
|
||||||
network = ["schemars", "serde", "url"]
|
network = ["schemars", "serde", "url"]
|
||||||
utoipa = [ "dep:utoipa" ]
|
utoipa = [ "dep:utoipa" ]
|
||||||
|
|
||||||
|
|||||||
@@ -72,13 +72,6 @@ pub const NYM_VPN_APIS: &[ApiUrlConst] = &[
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
#[cfg(feature = "env")]
|
|
||||||
fn serialize_api_urls(urls: &[ApiUrlConst]) -> String {
|
|
||||||
serde_json::to_string(urls)
|
|
||||||
.inspect_err(|e| tracing::warn!("failed to serialize nym_api_urls for env: {e}"))
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
|
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
|
||||||
pub const EXIT_POLICY_URL: &str =
|
pub const EXIT_POLICY_URL: &str =
|
||||||
"https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
|
"https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
|
||||||
@@ -169,11 +162,9 @@ pub fn export_to_env() {
|
|||||||
);
|
);
|
||||||
set_var_to_default(var_names::NYXD, NYXD_URL);
|
set_var_to_default(var_names::NYXD, NYXD_URL);
|
||||||
set_var_to_default(var_names::NYM_API, NYM_API);
|
set_var_to_default(var_names::NYM_API, NYM_API);
|
||||||
set_var_to_default(var_names::NYM_APIS, &serialize_api_urls(NYM_APIS));
|
|
||||||
set_var_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
|
set_var_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
|
||||||
set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
||||||
set_var_to_default(var_names::NYM_VPN_API, NYM_VPN_API);
|
set_var_to_default(var_names::NYM_VPN_API, NYM_VPN_API);
|
||||||
set_var_to_default(var_names::NYM_VPN_APIS, &serialize_api_urls(NYM_VPN_APIS));
|
|
||||||
set_var_to_default(
|
set_var_to_default(
|
||||||
var_names::UPGRADE_MODE_ATTESTATION_URL,
|
var_names::UPGRADE_MODE_ATTESTATION_URL,
|
||||||
UPGRADE_MODE_ATTESTATION_URL,
|
UPGRADE_MODE_ATTESTATION_URL,
|
||||||
@@ -220,9 +211,6 @@ pub fn export_to_env_if_not_set() {
|
|||||||
);
|
);
|
||||||
set_var_conditionally_to_default(var_names::NYXD, NYXD_URL);
|
set_var_conditionally_to_default(var_names::NYXD, NYXD_URL);
|
||||||
set_var_conditionally_to_default(var_names::NYM_API, NYM_API);
|
set_var_conditionally_to_default(var_names::NYM_API, NYM_API);
|
||||||
set_var_conditionally_to_default(var_names::NYM_APIS, &serialize_api_urls(NYM_APIS));
|
|
||||||
set_var_conditionally_to_default(var_names::NYM_VPN_API, NYM_VPN_API);
|
|
||||||
set_var_conditionally_to_default(var_names::NYM_VPN_APIS, &serialize_api_urls(NYM_VPN_APIS));
|
|
||||||
set_var_conditionally_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
|
set_var_conditionally_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
|
||||||
set_var_conditionally_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
set_var_conditionally_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
||||||
set_var_conditionally_to_default(
|
set_var_conditionally_to_default(
|
||||||
|
|||||||
@@ -7,13 +7,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::ops::Not;
|
use std::ops::Not;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[cfg(feature = "env")]
|
|
||||||
use crate::var_names;
|
|
||||||
#[cfg(feature = "env")]
|
|
||||||
use std::env::{VarError, var};
|
|
||||||
#[cfg(feature = "env")]
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||||
pub struct ChainDetails {
|
pub struct ChainDetails {
|
||||||
@@ -62,7 +55,7 @@ pub struct ApiUrl {
|
|||||||
pub front_hosts: Option<Vec<String>>,
|
pub front_hosts: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct ApiUrlConst<'a> {
|
pub struct ApiUrlConst<'a> {
|
||||||
pub url: &'a str,
|
pub url: &'a str,
|
||||||
pub front_hosts: Option<&'a [&'a str]>,
|
pub front_hosts: Option<&'a [&'a str]>,
|
||||||
@@ -113,6 +106,10 @@ impl NymNetworkDetails {
|
|||||||
|
|
||||||
#[cfg(feature = "env")]
|
#[cfg(feature = "env")]
|
||||||
pub fn new_from_env() -> Self {
|
pub fn new_from_env() -> Self {
|
||||||
|
use crate::var_names;
|
||||||
|
use std::env::{VarError, var};
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
fn get_optional_env<K: AsRef<OsStr>>(env: K) -> Option<String> {
|
fn get_optional_env<K: AsRef<OsStr>>(env: K) -> Option<String> {
|
||||||
match var(env) {
|
match var(env) {
|
||||||
Ok(var) => {
|
Ok(var) => {
|
||||||
@@ -128,11 +125,6 @@ impl NymNetworkDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let nym_api = var(var_names::NYM_API).expect("nym api not set");
|
let nym_api = var(var_names::NYM_API).expect("nym api not set");
|
||||||
let nym_api_urls = try_parse_api_urls(var_names::NYM_APIS).unwrap_or(vec![ApiUrl {
|
|
||||||
url: nym_api.clone(),
|
|
||||||
front_hosts: None,
|
|
||||||
}]);
|
|
||||||
let nym_vpn_api_urls = try_parse_api_urls(var_names::NYM_VPN_APIS);
|
|
||||||
|
|
||||||
NymNetworkDetails::new_empty()
|
NymNetworkDetails::new_empty()
|
||||||
.with_network_name(var(var_names::NETWORK_NAME).expect("network name not set"))
|
.with_network_name(var(var_names::NETWORK_NAME).expect("network name not set"))
|
||||||
@@ -169,8 +161,10 @@ impl NymNetworkDetails {
|
|||||||
.with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS))
|
.with_multisig_contract(get_optional_env(var_names::MULTISIG_CONTRACT_ADDRESS))
|
||||||
.with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS))
|
.with_coconut_dkg_contract(get_optional_env(var_names::COCONUT_DKG_CONTRACT_ADDRESS))
|
||||||
.with_nym_vpn_api_url(get_optional_env(var_names::NYM_VPN_API))
|
.with_nym_vpn_api_url(get_optional_env(var_names::NYM_VPN_API))
|
||||||
.with_nym_vpn_api_urls(nym_vpn_api_urls)
|
.with_nym_api_urls(Some(vec![ApiUrl {
|
||||||
.with_nym_api_urls(nym_api_urls)
|
url: nym_api,
|
||||||
|
front_hosts: None,
|
||||||
|
}]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_mainnet() -> Self {
|
pub fn new_mainnet() -> Self {
|
||||||
@@ -224,9 +218,6 @@ impl NymNetworkDetails {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let nym_api_urls = self.nym_api_urls();
|
|
||||||
let nym_vpn_api_urls = self.nym_vpn_api_urls();
|
|
||||||
|
|
||||||
set_var(var_names::NETWORK_NAME, self.network_name);
|
set_var(var_names::NETWORK_NAME, self.network_name);
|
||||||
set_var(var_names::BECH32_PREFIX, self.chain_details.bech32_account_prefix);
|
set_var(var_names::BECH32_PREFIX, self.chain_details.bech32_account_prefix);
|
||||||
|
|
||||||
@@ -252,10 +243,9 @@ impl NymNetworkDetails {
|
|||||||
set_optional_var(var_names::COCONUT_DKG_CONTRACT_ADDRESS, self.contracts.coconut_dkg_contract_address);
|
set_optional_var(var_names::COCONUT_DKG_CONTRACT_ADDRESS, self.contracts.coconut_dkg_contract_address);
|
||||||
|
|
||||||
set_optional_var(var_names::NYM_VPN_API, self.nym_vpn_api_url);
|
set_optional_var(var_names::NYM_VPN_API, self.nym_vpn_api_url);
|
||||||
set_optional_var(var_names::NYM_VPN_APIS, nym_vpn_api_urls);
|
|
||||||
set_optional_var(var_names::NYM_APIS, nym_api_urls);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_gas_price_amount(&self) -> f64 {
|
pub fn default_gas_price_amount(&self) -> f64 {
|
||||||
@@ -365,14 +355,8 @@ impl NymNetworkDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_nym_api_urls(mut self, urls: Vec<ApiUrl>) -> Self {
|
pub fn with_nym_api_urls(mut self, urls: Option<Vec<ApiUrl>>) -> Self {
|
||||||
self.nym_api_urls = Some(urls);
|
self.nym_api_urls = urls;
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_nym_vpn_api_urls(mut self, urls: Option<Vec<ApiUrl>>) -> Self {
|
|
||||||
self.nym_vpn_api_urls = urls;
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,28 +366,6 @@ impl NymNetworkDetails {
|
|||||||
.expect("the provided nym-vpn api url is invalid!")
|
.expect("the provided nym-vpn api url is invalid!")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "env")]
|
|
||||||
fn nym_api_urls(&self) -> Option<String> {
|
|
||||||
serde_json::to_string(self.nym_api_urls.as_deref()?)
|
|
||||||
.inspect_err(|e| tracing::warn!("failed to serialize nym_api_urls for env: {e}"))
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "env")]
|
|
||||||
fn nym_vpn_api_urls(&self) -> Option<String> {
|
|
||||||
serde_json::to_string(self.nym_vpn_api_urls.as_deref()?)
|
|
||||||
.inspect_err(|e| tracing::warn!("failed to serialize nym_vpn_api_urls for env: {e}"))
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "env")]
|
|
||||||
fn try_parse_api_urls(k: impl AsRef<OsStr>) -> Option<Vec<ApiUrl>> {
|
|
||||||
let raw = var(k).ok()?;
|
|
||||||
serde_json::from_str(&raw)
|
|
||||||
.inspect_err(|e| tracing::warn!("failed to parse api urls from env \"{raw:?}\": {e}"))
|
|
||||||
.ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Serialize, Deserialize, Clone, PartialEq, Eq)]
|
||||||
|
|||||||
@@ -21,11 +21,9 @@ pub const COCONUT_DKG_CONTRACT_ADDRESS: &str = "COCONUT_DKG_CONTRACT_ADDRESS";
|
|||||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS";
|
pub const REWARDING_VALIDATOR_ADDRESS: &str = "REWARDING_VALIDATOR_ADDRESS";
|
||||||
pub const NYXD: &str = "NYXD";
|
pub const NYXD: &str = "NYXD";
|
||||||
pub const NYM_API: &str = "NYM_API";
|
pub const NYM_API: &str = "NYM_API";
|
||||||
pub const NYM_APIS: &str = "NYM_APIS";
|
|
||||||
pub const NYXD_WEBSOCKET: &str = "NYXD_WS";
|
pub const NYXD_WEBSOCKET: &str = "NYXD_WS";
|
||||||
pub const EXIT_POLICY_URL: &str = "EXIT_POLICY";
|
pub const EXIT_POLICY_URL: &str = "EXIT_POLICY";
|
||||||
pub const NYM_VPN_API: &str = "NYM_VPN_API";
|
pub const NYM_VPN_API: &str = "NYM_VPN_API";
|
||||||
pub const NYM_VPN_APIS: &str = "NYM_VPN_APIS";
|
|
||||||
pub const CLIENT_STATS_COLLECTION_PROVIDER: &str = "CLIENT_STATS_COLLECTION_PROVIDER";
|
pub const CLIENT_STATS_COLLECTION_PROVIDER: &str = "CLIENT_STATS_COLLECTION_PROVIDER";
|
||||||
pub const UPGRADE_MODE_ATTESTATION_URL: &str = "UPGRADE_MODE_ATTESTATION_URL";
|
pub const UPGRADE_MODE_ATTESTATION_URL: &str = "UPGRADE_MODE_ATTESTATION_URL";
|
||||||
pub const UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY: &str = "UPGRADE_MODE_ATTESTER_ED25519_PUBKEY";
|
pub const UPGRADE_MODE_ATTESTER_ED25519_BS58_PUBKEY: &str = "UPGRADE_MODE_ATTESTER_ED25519_PUBKEY";
|
||||||
|
|||||||
@@ -8,24 +8,29 @@ license.workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
blake3 = { workspace = true }
|
blake3 = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
|
num_enum = { workspace = true }
|
||||||
|
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
|
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
|
||||||
|
|
||||||
libcrux-traits = { git = "https://github.com/cryspen/libcrux" }
|
|
||||||
libcrux-kem = { git = "https://github.com/cryspen/libcrux" }
|
libcrux-kem = { git = "https://github.com/cryspen/libcrux" }
|
||||||
libcrux-psq = { git = "https://github.com/cryspen/libcrux", features = ["test-utils"] }
|
|
||||||
libcrux-sha3 = { git = "https://github.com/cryspen/libcrux" }
|
libcrux-sha3 = { git = "https://github.com/cryspen/libcrux" }
|
||||||
libcrux-ml-kem = { git = "https://github.com/cryspen/libcrux" }
|
|
||||||
libcrux-ecdh = { git = "https://github.com/cryspen/libcrux", features = ["codec"] }
|
libcrux-ecdh = { git = "https://github.com/cryspen/libcrux", features = ["codec"] }
|
||||||
|
libcrux-chacha20poly1305 = { git = "https://github.com/cryspen/libcrux" }
|
||||||
|
|
||||||
|
#rand = "0.9.2"
|
||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
|
zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||||
classic-mceliece-rust = { git = "https://github.com/georgio/classic-mceliece-rust", features = ["mceliece460896f", "zeroize"] }
|
classic-mceliece-rust = { git = "https://github.com/georgio/classic-mceliece-rust", features = ["mceliece460896f", "zeroize"] }
|
||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
rand_chacha = "0.9.0"
|
||||||
|
anyhow = { workspace = true }
|
||||||
criterion = { workspace = true }
|
criterion = { workspace = true }
|
||||||
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benches"
|
name = "benches"
|
||||||
harness = false
|
harness = false
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
let mut secret_responder: [u8; 32] = [0u8; 32];
|
let mut secret_responder: [u8; 32] = [0u8; 32];
|
||||||
rng.fill_bytes(&mut secret_responder);
|
rng.fill_bytes(&mut secret_responder);
|
||||||
|
|
||||||
let responder_ed25519_keypair = ed25519::KeyPair::from_secret(secret_responder, 1);
|
let responder_ed25519_keypair = ed25519::KeyPair::from_secret(secret_responder, 1);
|
||||||
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
|
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
|
||||||
for hash_function in [
|
for hash_function in [
|
||||||
@@ -104,10 +105,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
// Anonymous Initiator, OneWay
|
// Anonymous Initiator, OneWay
|
||||||
{
|
{
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Anonymous Initiator: Generate Request",),
|
||||||
"{}, {} | Anonymous Initiator: Generate Request",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| anonymous_initiator_process(&mut rng, ciphersuite).unwrap());
|
b.iter(|| anonymous_initiator_process(&mut rng, ciphersuite).unwrap());
|
||||||
},
|
},
|
||||||
@@ -118,8 +116,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Anonymous Initiator: Encode Frame - Request",
|
"{kem}, {hash_function} | Anonymous Initiator: Encode Frame - Request",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| b.iter(|| i_frame.to_bytes()),
|
|b| b.iter(|| i_frame.to_bytes()),
|
||||||
);
|
);
|
||||||
@@ -128,8 +125,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Anonymous Initiator: Decode Frame - Request",
|
"{kem}, {hash_function} | Anonymous Initiator: Decode Frame - Request",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap()),
|
|b| b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap()),
|
||||||
);
|
);
|
||||||
@@ -138,8 +134,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Anonymous Initiator: Responder Ingest Frame",
|
"{kem}, {hash_function} | Anonymous Initiator: Responder Ingest Frame",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
@@ -153,14 +148,13 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Anonymous Initiator: Responder Generate Response",
|
"{kem}, {hash_function} | Anonymous Initiator: Responder Generate Response",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
responder_process(
|
responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
@@ -170,7 +164,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
);
|
);
|
||||||
let r_frame = responder_process(
|
let r_frame = responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
@@ -178,26 +172,23 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Anonymous Initiator: Responder Encode Frame",
|
"{kem}, {hash_function} | Anonymous Initiator: Responder Encode Frame",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| b.iter(|| r_frame.to_bytes()),
|
|b| b.iter(|| r_frame.to_bytes()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let r_bytes = r_frame.to_bytes();
|
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Anonymous Initiator: Initiator Ingest Response",
|
"{kem}, {hash_function} | Anonymous Initiator: Initiator Ingest Response",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
initiator_ingest_response(
|
initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&r_frame,
|
||||||
|
&r_frame.context().unwrap(),
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
@@ -206,9 +197,10 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
let obtained_key = initiator_ingest_response(
|
let obtained_key = initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&r_frame,
|
||||||
|
&r_frame.context().unwrap(),
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -226,10 +218,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator OneWay: Generate Request",),
|
||||||
"{}, {} | Initiator OneWay: Generate Request",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
initiator_process(
|
initiator_process(
|
||||||
@@ -245,30 +234,21 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator OneWay: Encode Frame - Request",),
|
||||||
"{}, {} | Initiator OneWay: Encode Frame - Request",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| b.iter(|| i_frame.to_bytes()),
|
|b| b.iter(|| i_frame.to_bytes()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let i_frame_bytes = i_frame.to_bytes();
|
let i_frame_bytes = i_frame.to_bytes();
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator OneWay: Decode Frame - Request",),
|
||||||
"{}, {} | Initiator OneWay: Decode Frame - Request",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap()),
|
|b| b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator OneWay: Responder Ingest Frame",),
|
||||||
"{}, {} | Initiator OneWay: Responder Ingest Frame",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
responder_ingest_message(
|
responder_ingest_message(
|
||||||
@@ -294,14 +274,13 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Initiator OneWay: Responder Generate Response",
|
"{kem}, {hash_function} | Initiator OneWay: Responder Generate Response",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
responder_process(
|
responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
@@ -312,36 +291,31 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
let r_frame = responder_process(
|
let r_frame = responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator OneWay: Responder Encode Frame",),
|
||||||
"{}, {} | Initiator OneWay: Responder Encode Frame",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| r_frame.to_bytes());
|
b.iter(|| r_frame.to_bytes());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let r_bytes = r_frame.to_bytes();
|
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Initiator OneWay: Initiator Ingest Response",
|
"{kem}, {hash_function} | Initiator OneWay: Initiator Ingest Response",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
initiator_ingest_response(
|
initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&r_frame,
|
||||||
|
&r_frame.context().unwrap(),
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
@@ -350,9 +324,10 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
let i_obtained_key = initiator_ingest_response(
|
let i_obtained_key = initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&r_frame,
|
||||||
|
&r_frame.context().unwrap(),
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -362,10 +337,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
// Initiator, Mutual
|
// Initiator, Mutual
|
||||||
{
|
{
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator Mutual: Generate Request",),
|
||||||
"{}, {} | Initiator Mutual: Generate Request",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
initiator_process(
|
initiator_process(
|
||||||
@@ -390,10 +362,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator Mutual: Encode Frame - Request",),
|
||||||
"{}, {} | Initiator Mutual: Encode Frame - Request",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| i_frame.to_bytes());
|
b.iter(|| i_frame.to_bytes());
|
||||||
},
|
},
|
||||||
@@ -402,10 +371,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
let i_frame_bytes = i_frame.to_bytes();
|
let i_frame_bytes = i_frame.to_bytes();
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator Mutual: Decode Frame - Request",),
|
||||||
"{}, {} | Initiator Mutual: Decode Frame - Request",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap());
|
b.iter(|| KKTFrame::from_bytes(&i_frame_bytes).unwrap());
|
||||||
},
|
},
|
||||||
@@ -414,10 +380,7 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator Mutual: Responder Ingest Frame",),
|
||||||
"{}, {} | Initiator Mutual: Responder Ingest Frame",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
responder_ingest_message(
|
responder_ingest_message(
|
||||||
@@ -443,14 +406,13 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Initiator Mutual: Responder Generate Response",
|
"{kem}, {hash_function} | Initiator Mutual: Responder Generate Response",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
responder_process(
|
responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
@@ -461,17 +423,14 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
let r_frame = responder_process(
|
let r_frame = responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!("{kem}, {hash_function} | Initiator Mutual: Responder Encode Frame",),
|
||||||
"{}, {} | Initiator Mutual: Responder Encode Frame",
|
|
||||||
kem, hash_function
|
|
||||||
),
|
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
r_frame.to_bytes();
|
r_frame.to_bytes();
|
||||||
@@ -479,20 +438,18 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let r_bytes = r_frame.to_bytes();
|
|
||||||
|
|
||||||
c.bench_function(
|
c.bench_function(
|
||||||
&format!(
|
&format!(
|
||||||
"{}, {} | Initiator Mutual: Initiator Ingest Response",
|
"{kem}, {hash_function} | Initiator Mutual: Initiator Ingest Response",
|
||||||
kem, hash_function
|
|
||||||
),
|
),
|
||||||
|b| {
|
|b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
initiator_ingest_response(
|
initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&r_frame,
|
||||||
|
&r_frame.context().unwrap(),
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
@@ -501,9 +458,10 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
|||||||
|
|
||||||
let obtained_key = initiator_ingest_response(
|
let obtained_key = initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&r_frame,
|
||||||
|
&r_frame.context().unwrap(),
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ use nym_crypto::asymmetric::ed25519;
|
|||||||
|
|
||||||
use crate::error::KKTError;
|
use crate::error::KKTError;
|
||||||
|
|
||||||
pub const HASH_LEN_256: u8 = 32;
|
pub const HASH_LEN_256: usize = 32;
|
||||||
pub const CIPHERSUITE_ENCODING_LEN: usize = 4;
|
pub const CIPHERSUITE_ENCODING_LEN: usize = 4;
|
||||||
|
|
||||||
pub const CURVE25519_KEY_LEN: usize = 32;
|
pub const CURVE25519_KEY_LEN: usize = 32;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum HashFunction {
|
pub enum HashFunction {
|
||||||
Blake3,
|
Blake3,
|
||||||
SHAKE128,
|
SHAKE128,
|
||||||
@@ -87,7 +87,7 @@ impl<'a> EncapsulationKey<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum SignatureScheme {
|
pub enum SignatureScheme {
|
||||||
Ed25519,
|
Ed25519,
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ impl Display for SignatureScheme {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum KEM {
|
pub enum KEM {
|
||||||
MlKem768,
|
MlKem768,
|
||||||
XWing,
|
XWing,
|
||||||
@@ -118,7 +118,7 @@ impl Display for KEM {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Ciphersuite {
|
pub struct Ciphersuite {
|
||||||
hash_function: HashFunction,
|
hash_function: HashFunction,
|
||||||
signature_scheme: SignatureScheme,
|
signature_scheme: SignatureScheme,
|
||||||
@@ -172,7 +172,7 @@ impl Ciphersuite {
|
|||||||
l
|
l
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => HASH_LEN_256,
|
None => HASH_LEN_256 as u8,
|
||||||
};
|
};
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
hash_function,
|
hash_function,
|
||||||
@@ -203,7 +203,7 @@ impl Ciphersuite {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn encode(&self) -> [u8; 4] {
|
pub fn encode(&self) -> [u8; CIPHERSUITE_ENCODING_LEN] {
|
||||||
// [kem, hash, hashlen, sig]
|
// [kem, hash, hashlen, sig]
|
||||||
[
|
[
|
||||||
match self.kem {
|
match self.kem {
|
||||||
@@ -218,8 +218,8 @@ impl Ciphersuite {
|
|||||||
HashFunction::SHAKE128 => 2,
|
HashFunction::SHAKE128 => 2,
|
||||||
HashFunction::SHA256 => 3,
|
HashFunction::SHA256 => 3,
|
||||||
},
|
},
|
||||||
match self.hash_length {
|
match self.hash_length as usize {
|
||||||
HASH_LEN_256 => 0,
|
HASH_LEN_256 => 0u8,
|
||||||
_ => self.hash_length,
|
_ => self.hash_length,
|
||||||
},
|
},
|
||||||
match self.signature_scheme {
|
match self.signature_scheme {
|
||||||
@@ -227,55 +227,45 @@ impl Ciphersuite {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
pub fn decode(encoding: &[u8]) -> Result<Self, KKTError> {
|
pub fn decode(encoding: [u8; CIPHERSUITE_ENCODING_LEN]) -> Result<Self, KKTError> {
|
||||||
if encoding.len() == 4 {
|
let kem = match encoding[0] {
|
||||||
let kem = match encoding[0] {
|
0 => KEM::XWing,
|
||||||
0 => KEM::XWing,
|
1 => KEM::MlKem768,
|
||||||
1 => KEM::MlKem768,
|
2 => KEM::McEliece,
|
||||||
2 => KEM::McEliece,
|
255 => KEM::X25519,
|
||||||
255 => KEM::X25519,
|
_ => {
|
||||||
_ => {
|
return Err(KKTError::CiphersuiteDecodingError {
|
||||||
return Err(KKTError::CiphersuiteDecodingError {
|
info: format!("Undefined KEM: {}", encoding[0]),
|
||||||
info: format!("Undefined KEM: {}", encoding[0]),
|
});
|
||||||
});
|
}
|
||||||
}
|
};
|
||||||
};
|
let hash_function = match encoding[1] {
|
||||||
let hash_function = match encoding[1] {
|
0 => HashFunction::Blake3,
|
||||||
0 => HashFunction::Blake3,
|
1 => HashFunction::SHAKE256,
|
||||||
1 => HashFunction::SHAKE256,
|
2 => HashFunction::SHAKE128,
|
||||||
2 => HashFunction::SHAKE128,
|
3 => HashFunction::SHA256,
|
||||||
3 => HashFunction::SHA256,
|
_ => {
|
||||||
_ => {
|
return Err(KKTError::CiphersuiteDecodingError {
|
||||||
return Err(KKTError::CiphersuiteDecodingError {
|
info: format!("Undefined Hash Function: {}", encoding[1]),
|
||||||
info: format!("Undefined Hash Function: {}", encoding[1]),
|
});
|
||||||
});
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let custom_hash_length = match encoding[2] {
|
let custom_hash_length = match encoding[2] {
|
||||||
0 => None,
|
0 => None,
|
||||||
_ => Some(encoding[2]),
|
_ => Some(encoding[2]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let signature_scheme = match encoding[3] {
|
let signature_scheme = match encoding[3] {
|
||||||
0 => SignatureScheme::Ed25519,
|
0 => SignatureScheme::Ed25519,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(KKTError::CiphersuiteDecodingError {
|
return Err(KKTError::CiphersuiteDecodingError {
|
||||||
info: format!("Undefined Signature Scheme: {}", encoding[3]),
|
info: format!("Undefined Signature Scheme: {}", encoding[3]),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::resolve_ciphersuite(kem, hash_function, signature_scheme, custom_hash_length)
|
Self::resolve_ciphersuite(kem, hash_function, signature_scheme, custom_hash_length)
|
||||||
} else {
|
|
||||||
Err(KKTError::CiphersuiteDecodingError {
|
|
||||||
info: format!(
|
|
||||||
"Incorrect Encoding Length: actual: {} != expected: {}",
|
|
||||||
encoding.len(),
|
|
||||||
CIPHERSUITE_ENCODING_LEN
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+102
-119
@@ -1,22 +1,25 @@
|
|||||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use std::fmt::Display;
|
use crate::ciphersuite::CIPHERSUITE_ENCODING_LEN;
|
||||||
|
|
||||||
use crate::{KKT_VERSION, ciphersuite::Ciphersuite, error::KKTError, frame::KKT_SESSION_ID_LEN};
|
use crate::{KKT_VERSION, ciphersuite::Ciphersuite, error::KKTError, frame::KKT_SESSION_ID_LEN};
|
||||||
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
pub const KKT_CONTEXT_LEN: usize = 7;
|
pub const KKT_CONTEXT_LEN: usize = 7;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
// bitmask used: 0b1110_0000
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug, IntoPrimitive, TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum KKTStatus {
|
pub enum KKTStatus {
|
||||||
Ok,
|
Ok = 0b0000_0000,
|
||||||
InvalidRequestFormat,
|
InvalidRequestFormat = 0b0010_0000,
|
||||||
InvalidResponseFormat,
|
InvalidResponseFormat = 0b0100_0000,
|
||||||
InvalidSignature,
|
InvalidSignature = 0b0110_0000,
|
||||||
UnsupportedCiphersuite,
|
UnsupportedCiphersuite = 0b1000_0000,
|
||||||
UnsupportedKKTVersion,
|
UnsupportedKKTVersion = 0b1010_0000,
|
||||||
InvalidKey,
|
InvalidKey = 0b1100_0000,
|
||||||
Timeout,
|
Timeout = 0b1110_0000,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for KKTStatus {
|
impl Display for KKTStatus {
|
||||||
@@ -33,20 +36,25 @@ impl Display for KKTStatus {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
|
||||||
|
// bitmask used: 0b0000_0011
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug, IntoPrimitive, TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum KKTRole {
|
pub enum KKTRole {
|
||||||
Initiator,
|
Initiator = 0b0000_0000,
|
||||||
AnonymousInitiator,
|
Responder = 0b0000_0001,
|
||||||
Responder,
|
AnonymousInitiator = 0b0000_0010,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
// bitmask used: 0b0001_1100
|
||||||
|
#[derive(Clone, Copy, PartialEq, Debug, IntoPrimitive, TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum KKTMode {
|
pub enum KKTMode {
|
||||||
OneWay,
|
OneWay = 0b0000_0000,
|
||||||
Mutual,
|
Mutual = 0b0000_0100,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct KKTContext {
|
pub struct KKTContext {
|
||||||
version: u8,
|
version: u8,
|
||||||
message_sequence: u8,
|
message_sequence: u8,
|
||||||
@@ -127,11 +135,14 @@ impl KKTContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn header_len(&self) -> usize {
|
pub const fn header_len(&self) -> usize {
|
||||||
KKT_CONTEXT_LEN
|
KKT_CONTEXT_LEN
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn session_id_len(&self) -> usize {
|
pub const fn session_id_len(&self) -> usize {
|
||||||
|
// note: if anyone decides to update this function and changes the constant value,
|
||||||
|
// you will have to adjust encoding/decoding functions
|
||||||
|
|
||||||
// match self.role {
|
// match self.role {
|
||||||
// KKTRole::Initiator | KKTRole::Responder => SESSION_ID_LENGTH,
|
// KKTRole::Initiator | KKTRole::Responder => SESSION_ID_LENGTH,
|
||||||
// It doesn't make sense to send a session_id if we send messages in the clear
|
// It doesn't make sense to send a session_id if we send messages in the clear
|
||||||
@@ -144,115 +155,87 @@ impl KKTContext {
|
|||||||
self.body_len() + self.signature_len() + self.header_len() + self.session_id_len()
|
self.body_len() + self.signature_len() + self.header_len() + self.session_id_len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode(&self) -> Result<Vec<u8>, KKTError> {
|
pub fn encode(&self) -> Result<[u8; KKT_CONTEXT_LEN], KKTError> {
|
||||||
let mut header_bytes: Vec<u8> = Vec::with_capacity(KKT_CONTEXT_LEN);
|
let mut header_bytes = [0u8; KKT_CONTEXT_LEN];
|
||||||
if self.message_sequence >= 1 << 4 {
|
if self.message_sequence >= 1 << 4 {
|
||||||
return Err(KKTError::MessageCountLimitReached);
|
return Err(KKTError::MessageCountLimitReached);
|
||||||
}
|
}
|
||||||
|
|
||||||
header_bytes.push((KKT_VERSION << 4) + self.message_sequence);
|
let ciphersuite_bytes = self.ciphersuite.encode();
|
||||||
|
|
||||||
header_bytes.push(
|
header_bytes[0] = (KKT_VERSION << 4) + self.message_sequence;
|
||||||
match self.status {
|
header_bytes[1] = u8::from(self.status) + u8::from(self.mode) + u8::from(self.role);
|
||||||
KKTStatus::Ok => 0,
|
|
||||||
KKTStatus::InvalidRequestFormat => 0b0010_0000,
|
|
||||||
KKTStatus::InvalidResponseFormat => 0b0100_0000,
|
|
||||||
KKTStatus::InvalidSignature => 0b0110_0000,
|
|
||||||
KKTStatus::UnsupportedCiphersuite => 0b1000_0000,
|
|
||||||
KKTStatus::UnsupportedKKTVersion => 0b1010_0000,
|
|
||||||
KKTStatus::InvalidKey => 0b1100_0000,
|
|
||||||
KKTStatus::Timeout => 0b1110_0000,
|
|
||||||
} + match self.mode {
|
|
||||||
KKTMode::OneWay => 0,
|
|
||||||
KKTMode::Mutual => 0b0000_0100,
|
|
||||||
} + match self.role {
|
|
||||||
KKTRole::Initiator => 0,
|
|
||||||
KKTRole::Responder => 1,
|
|
||||||
KKTRole::AnonymousInitiator => 2,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
header_bytes.extend_from_slice(&self.ciphersuite.encode());
|
let mut i = 2;
|
||||||
header_bytes.push(0);
|
for b in ciphersuite_bytes.into_iter() {
|
||||||
|
header_bytes[i] = b;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
header_bytes[i] = 0;
|
||||||
Ok(header_bytes)
|
Ok(header_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_decode(header_bytes: &[u8]) -> Result<Self, KKTError> {
|
pub fn try_decode(header_bytes: [u8; KKT_CONTEXT_LEN]) -> Result<Self, KKTError> {
|
||||||
if header_bytes.len() == KKT_CONTEXT_LEN {
|
let kkt_version = (header_bytes[0] & 0b1111_0000) >> 4;
|
||||||
let kkt_version = header_bytes[0] & 0b1111_0000;
|
let message_sequence_counter = header_bytes[0] & 0b0000_1111;
|
||||||
|
|
||||||
let message_sequence_counter = header_bytes[0] & 0b0000_1111;
|
// We only check if stuff is valid here, not necessarily if it's compatible
|
||||||
|
|
||||||
// We only check if stuff is valid here, not necessarily if it's compatible
|
if kkt_version > KKT_VERSION {
|
||||||
|
return Err(KKTError::FrameDecodingError {
|
||||||
if (kkt_version >> 4) > KKT_VERSION {
|
info: format!("Header - Invalid KKT Version: {kkt_version}"),
|
||||||
return Err(KKTError::FrameDecodingError {
|
});
|
||||||
info: format!("Header - Invalid KKT Version: {}", kkt_version >> 4),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let status = match header_bytes[1] & 0b1110_0000 {
|
|
||||||
0 => KKTStatus::Ok,
|
|
||||||
0b0010_0000 => KKTStatus::InvalidRequestFormat,
|
|
||||||
0b0100_0000 => KKTStatus::InvalidResponseFormat,
|
|
||||||
0b0110_0000 => KKTStatus::InvalidSignature,
|
|
||||||
0b1000_0000 => KKTStatus::UnsupportedCiphersuite,
|
|
||||||
0b1010_0000 => KKTStatus::UnsupportedKKTVersion,
|
|
||||||
0b1100_0000 => KKTStatus::InvalidKey,
|
|
||||||
0b1110_0000 => KKTStatus::Timeout,
|
|
||||||
_ => {
|
|
||||||
return Err(KKTError::FrameDecodingError {
|
|
||||||
info: format!(
|
|
||||||
"Header - Invalid KKT Status: {}",
|
|
||||||
header_bytes[1] & 0b1110_0000
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let role = match header_bytes[1] & 0b0000_0011 {
|
|
||||||
0 => KKTRole::Initiator,
|
|
||||||
1 => KKTRole::Responder,
|
|
||||||
2 => KKTRole::AnonymousInitiator,
|
|
||||||
_ => {
|
|
||||||
return Err(KKTError::FrameDecodingError {
|
|
||||||
info: format!(
|
|
||||||
"Header - Invalid KKT Role: {}",
|
|
||||||
header_bytes[1] & 0b0000_0011
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mode = match (header_bytes[1] & 0b0001_1100) >> 2 {
|
|
||||||
0 => KKTMode::OneWay,
|
|
||||||
1 => KKTMode::Mutual,
|
|
||||||
_ => {
|
|
||||||
return Err(KKTError::FrameDecodingError {
|
|
||||||
info: format!(
|
|
||||||
"Header - Invalid KKT Mode: {}",
|
|
||||||
(header_bytes[1] & 0b0001_1100) >> 2
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(KKTContext {
|
|
||||||
version: kkt_version,
|
|
||||||
status,
|
|
||||||
mode,
|
|
||||||
role,
|
|
||||||
ciphersuite: Ciphersuite::decode(&header_bytes[2..6])?,
|
|
||||||
message_sequence: message_sequence_counter,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(KKTError::FrameDecodingError {
|
|
||||||
info: format!(
|
|
||||||
"Header - Invalid Header Length: actual: {} != expected: {}",
|
|
||||||
header_bytes.len(),
|
|
||||||
KKT_CONTEXT_LEN
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let raw_kkt_status = header_bytes[1] & 0b1110_0000;
|
||||||
|
let raw_kkt_role = header_bytes[1] & 0b0000_0011;
|
||||||
|
let raw_kkt_mode = header_bytes[1] & 0b0001_1100;
|
||||||
|
|
||||||
|
let status =
|
||||||
|
KKTStatus::try_from(raw_kkt_status).map_err(|_| KKTError::FrameDecodingError {
|
||||||
|
info: format!("Header - Invalid KKT Status: {raw_kkt_status}"),
|
||||||
|
})?;
|
||||||
|
let role = KKTRole::try_from(raw_kkt_role).map_err(|_| KKTError::FrameDecodingError {
|
||||||
|
info: format!("Header - Invalid KKT Role: {raw_kkt_role}"),
|
||||||
|
})?;
|
||||||
|
let mode = KKTMode::try_from(raw_kkt_mode).map_err(|_| KKTError::FrameDecodingError {
|
||||||
|
info: format!("Header - Invalid KKT Mode: {raw_kkt_mode}"),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let ciphersuite_bytes = header_bytes[2..6].try_into().map_err(|_| {
|
||||||
|
KKTError::CiphersuiteDecodingError {
|
||||||
|
info: format!(
|
||||||
|
"Incorrect Encoding Length: actual: 4 != expected: {CIPHERSUITE_ENCODING_LEN}",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(KKTContext {
|
||||||
|
version: kkt_version,
|
||||||
|
status,
|
||||||
|
mode,
|
||||||
|
role,
|
||||||
|
ciphersuite: Ciphersuite::decode(ciphersuite_bytes)?,
|
||||||
|
message_sequence: message_sequence_counter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn kkt_context_encoding() {
|
||||||
|
let valid_context = KKTContext::new(
|
||||||
|
KKTRole::Initiator,
|
||||||
|
KKTMode::Mutual,
|
||||||
|
Ciphersuite::decode([255, 1, 0, 0]).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let encoded = valid_context.encode().unwrap();
|
||||||
|
let decoded = KKTContext::try_decode(encoded).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(decoded, valid_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,95 +1,257 @@
|
|||||||
use core::hash;
|
// Copyright 2025-2026 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use blake3::{Hash, Hasher};
|
use crate::kkt::KKT_INITIAL_FRAME_AAD;
|
||||||
use curve25519_dalek::digest::DynDigest;
|
use crate::{
|
||||||
use libcrux_psq::traits::Ciphertext;
|
ciphersuite::CURVE25519_KEY_LEN, context::KKTContext, error::KKTError, frame::KKTFrame,
|
||||||
use nym_crypto::symmetric::aead::{AeadKey, Nonce};
|
|
||||||
use nym_crypto::{
|
|
||||||
aes::Aes256,
|
|
||||||
asymmetric::x25519::{self, PrivateKey, PublicKey},
|
|
||||||
generic_array::GenericArray,
|
|
||||||
Aes256GcmSiv,
|
|
||||||
};
|
};
|
||||||
// use rand::{CryptoRng, RngCore};
|
use blake3::Hasher;
|
||||||
|
use libcrux_chacha20poly1305::{NONCE_LEN, TAG_LEN};
|
||||||
|
use nym_crypto::asymmetric::x25519;
|
||||||
|
use rand::{CryptoRng, RngCore};
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use nym_crypto::aes::cipher::crypto_common::rand_core::{CryptoRng, RngCore};
|
#[derive(Clone, Copy, Zeroize)]
|
||||||
|
pub struct KKTSessionSecret([u8; 32]);
|
||||||
|
|
||||||
use crate::error::KKTError;
|
impl KKTSessionSecret {
|
||||||
|
pub fn new<R>(rng: &mut R, remote_public_key: &x25519::PublicKey) -> (Self, x25519::PublicKey)
|
||||||
|
where
|
||||||
|
R: RngCore + CryptoRng,
|
||||||
|
{
|
||||||
|
let mut private_key_bytes = [0u8; x25519::PRIVATE_KEY_SIZE];
|
||||||
|
rng.fill_bytes(&mut private_key_bytes);
|
||||||
|
|
||||||
fn generate_round_trip_symmetric_key<R>(
|
let ephemeral_private_key = x25519::PrivateKey::from_secret(private_key_bytes);
|
||||||
rng: &mut R,
|
let ephemeral_public_key = x25519::PublicKey::from(&ephemeral_private_key);
|
||||||
remote_public_key: &PublicKey,
|
|
||||||
) -> ([u8; 64], [u8; 32])
|
|
||||||
where
|
|
||||||
R: CryptoRng + RngCore,
|
|
||||||
{
|
|
||||||
let mut s = x25519::PrivateKey::new(rng);
|
|
||||||
let gs = s.public_key();
|
|
||||||
|
|
||||||
let mut gbs = s.diffie_hellman(remote_public_key);
|
(
|
||||||
s.zeroize();
|
Self::derive(&ephemeral_private_key, remote_public_key),
|
||||||
|
ephemeral_public_key,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn from_bytes(secret: [u8; 32]) -> Self {
|
||||||
|
Self(secret)
|
||||||
|
}
|
||||||
|
|
||||||
let mut message: [u8; 64] = [0u8; 64];
|
fn try_derive(private_key: &x25519::PrivateKey, public_key: &[u8]) -> Result<Self, KKTError> {
|
||||||
message[0..32].clone_from_slice(gs.as_bytes());
|
let mut pub_key: [u8; 32] = [0u8; 32];
|
||||||
|
pub_key.copy_from_slice(&public_key[0..CURVE25519_KEY_LEN]);
|
||||||
|
|
||||||
let mut hasher = Hasher::new();
|
// Todo: check validity of pk...
|
||||||
|
let pk = x25519::PublicKey::from(pub_key);
|
||||||
|
Ok(Self::derive(private_key, &pk))
|
||||||
|
}
|
||||||
|
|
||||||
hasher.update(&gbs);
|
pub fn derive(private_key: &x25519::PrivateKey, public_key: &x25519::PublicKey) -> Self {
|
||||||
gbs.zeroize();
|
let mut shared_secret = private_key.diffie_hellman(public_key);
|
||||||
let key: [u8; 32] = hasher.finalize().as_bytes().to_owned();
|
|
||||||
|
|
||||||
hasher.update(remote_public_key.as_bytes());
|
let mut hasher = Hasher::new();
|
||||||
hasher.update(gs.as_bytes());
|
|
||||||
|
|
||||||
hasher.finalize_into_reset(&mut message[32..64]);
|
hasher.update(&shared_secret);
|
||||||
|
shared_secret.zeroize();
|
||||||
|
|
||||||
(message, key)
|
Self(hasher.finalize().as_bytes().to_owned())
|
||||||
}
|
}
|
||||||
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
||||||
fn extract_shared_secret(b: &PrivateKey, message: &[u8; 64]) -> Result<[u8; 32], KKTError> {
|
&self.0
|
||||||
let gs = PublicKey::from_bytes(&message[0..32])?;
|
|
||||||
|
|
||||||
let mut gsb = b.diffie_hellman(&gs);
|
|
||||||
|
|
||||||
let mut hasher = Hasher::new();
|
|
||||||
hasher.update(&gsb);
|
|
||||||
gsb.zeroize();
|
|
||||||
let key: [u8; 32] = hasher.finalize().as_bytes().to_owned();
|
|
||||||
|
|
||||||
hasher.update(b.public_key().as_bytes());
|
|
||||||
hasher.update(gs.as_bytes());
|
|
||||||
|
|
||||||
// This runs in constant time
|
|
||||||
if hasher.finalize() == message[32..64] {
|
|
||||||
Ok(key)
|
|
||||||
} else {
|
|
||||||
Err(KKTError::X25519Error {
|
|
||||||
info: format!("Symmetric Key Hash Validation Error"),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt(mut key: [u8; 32], message: &[u8]) -> Result<Vec<u8>, KKTError> {
|
pub fn encrypt_initial_kkt_frame<R>(
|
||||||
// The empty nonce is fine since we use the key once.
|
rng: &mut R,
|
||||||
let nonce = Nonce::<Aes256GcmSiv>::from_slice(&[]);
|
remote_public_key: &x25519::PublicKey,
|
||||||
|
kkt_frame: &KKTFrame,
|
||||||
|
) -> Result<(KKTSessionSecret, Vec<u8>), KKTError>
|
||||||
|
where
|
||||||
|
R: CryptoRng + RngCore,
|
||||||
|
{
|
||||||
|
let (session_secret_key, ephemeral_public_key) = KKTSessionSecret::new(rng, remote_public_key);
|
||||||
|
|
||||||
let ciphertext =
|
let mut encrypted_frame =
|
||||||
nym_crypto::symmetric::aead::encrypt::<Aes256GcmSiv>(&key.into(), nonce, message)?;
|
encrypt_kkt_frame(rng, &session_secret_key, kkt_frame, KKT_INITIAL_FRAME_AAD)?;
|
||||||
|
|
||||||
key.zeroize();
|
let mut output_buffer = Vec::with_capacity(encrypted_frame.len() + CURVE25519_KEY_LEN);
|
||||||
|
output_buffer.extend_from_slice(ephemeral_public_key.as_bytes());
|
||||||
|
output_buffer.append(&mut encrypted_frame);
|
||||||
|
|
||||||
Ok(ciphertext)
|
// [ 32 | 12 | ciphertext | 16];
|
||||||
|
// [eph_pub_key | nonce | ciphertext | tag];
|
||||||
|
Ok((session_secret_key, output_buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt(key: [u8; 32], ciphertext: Vec<u8>) -> Vec<u8> {
|
pub fn decrypt_initial_kkt_frame(
|
||||||
// The empty nonce is fine since we use the key once.
|
responder_private_key: &x25519::PrivateKey,
|
||||||
let nonce = Nonce::<Aes256>::from_slice(&[]);
|
encrypted_frame_bytes: &[u8],
|
||||||
|
) -> Result<(KKTSessionSecret, KKTFrame, KKTContext), KKTError> {
|
||||||
|
if encrypted_frame_bytes.len() < CURVE25519_KEY_LEN + TAG_LEN + NONCE_LEN {
|
||||||
|
Err(KKTError::AEADError {
|
||||||
|
info: "Encrypted KKT Frame is too short.",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let shared_secret = KKTSessionSecret::try_derive(
|
||||||
|
responder_private_key,
|
||||||
|
&encrypted_frame_bytes[0..CURVE25519_KEY_LEN],
|
||||||
|
)?;
|
||||||
|
|
||||||
let ciphertext =
|
let (kkt_frame, kkt_context) = decrypt_kkt_frame(
|
||||||
nym_crypto::symmetric::aead::encrypt::<Aes256GcmSiv>(&key.into(), nonce, message)?;
|
&shared_secret,
|
||||||
|
&encrypted_frame_bytes[CURVE25519_KEY_LEN..],
|
||||||
key.zeroize();
|
KKT_INITIAL_FRAME_AAD,
|
||||||
|
)?;
|
||||||
Ok(ciphertext)
|
Ok((shared_secret, kkt_frame, kkt_context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt_kkt_frame<R>(
|
||||||
|
rng: &mut R,
|
||||||
|
secret_key: &KKTSessionSecret,
|
||||||
|
kkt_frame: &KKTFrame,
|
||||||
|
aad: &[u8],
|
||||||
|
) -> Result<Vec<u8>, KKTError>
|
||||||
|
where
|
||||||
|
R: CryptoRng + RngCore,
|
||||||
|
{
|
||||||
|
let kkt_frame_bytes = kkt_frame.to_bytes();
|
||||||
|
|
||||||
|
// generate nonce
|
||||||
|
let mut nonce: [u8; NONCE_LEN] = [0u8; NONCE_LEN];
|
||||||
|
rng.fill_bytes(&mut nonce);
|
||||||
|
|
||||||
|
let mut ciphertext = encrypt(secret_key.as_bytes(), &kkt_frame_bytes, aad, &nonce)?;
|
||||||
|
|
||||||
|
// [ 12 | ciphertext | 16];
|
||||||
|
// [nonce | ciphertext | tag];
|
||||||
|
let mut output_buffer: Vec<u8> =
|
||||||
|
Vec::with_capacity(NONCE_LEN + kkt_frame_bytes.len() + TAG_LEN);
|
||||||
|
|
||||||
|
output_buffer.extend_from_slice(&nonce);
|
||||||
|
output_buffer.append(&mut ciphertext);
|
||||||
|
|
||||||
|
Ok(output_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// kkt_frame_bytes should look like this
|
||||||
|
// [ 12 | ciphertext | 16];
|
||||||
|
// [nonce | ciphertext | tag];
|
||||||
|
pub fn decrypt_kkt_frame(
|
||||||
|
secret_key: &KKTSessionSecret,
|
||||||
|
kkt_frame_bytes: &[u8],
|
||||||
|
aad: &[u8],
|
||||||
|
) -> Result<(KKTFrame, KKTContext), KKTError> {
|
||||||
|
let mut nonce: [u8; NONCE_LEN] = [0u8; NONCE_LEN];
|
||||||
|
nonce.copy_from_slice(&kkt_frame_bytes[0..NONCE_LEN]);
|
||||||
|
|
||||||
|
let plaintext = decrypt(
|
||||||
|
secret_key.as_bytes(),
|
||||||
|
&kkt_frame_bytes[NONCE_LEN..],
|
||||||
|
aad,
|
||||||
|
&nonce,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
KKTFrame::from_bytes(&plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encrypt(
|
||||||
|
secret_key: &[u8; 32],
|
||||||
|
plaintext: &[u8],
|
||||||
|
aad: &[u8],
|
||||||
|
nonce: &[u8; NONCE_LEN],
|
||||||
|
) -> Result<Vec<u8>, KKTError> {
|
||||||
|
let mut output_buffer = vec![0; plaintext.len() + TAG_LEN];
|
||||||
|
libcrux_chacha20poly1305::encrypt(secret_key, plaintext, &mut output_buffer, aad, nonce)?;
|
||||||
|
Ok(output_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt(
|
||||||
|
secret_key: &[u8; 32],
|
||||||
|
ciphertext: &[u8],
|
||||||
|
aad: &[u8],
|
||||||
|
nonce: &[u8; NONCE_LEN],
|
||||||
|
) -> Result<Vec<u8>, KKTError> {
|
||||||
|
let mut output_buffer = vec![0; ciphertext.len() - TAG_LEN];
|
||||||
|
libcrux_chacha20poly1305::decrypt(secret_key, &mut output_buffer, ciphertext, aad, nonce)?;
|
||||||
|
Ok(output_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::ciphersuite::Ciphersuite;
|
||||||
|
use crate::context::{KKTContext, KKTMode, KKTRole};
|
||||||
|
use crate::encryption::{decrypt_kkt_frame, encrypt_kkt_frame};
|
||||||
|
use crate::frame::{KKT_SESSION_ID_LEN, KKTFrame};
|
||||||
|
use crate::{
|
||||||
|
ciphersuite::HASH_LEN_256,
|
||||||
|
encryption::{KKTSessionSecret, decrypt, encrypt},
|
||||||
|
key_utils::generate_keypair_x25519,
|
||||||
|
};
|
||||||
|
use rand::{RngCore, SeedableRng, rng};
|
||||||
|
use rand_chacha::ChaCha20Rng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keygen() {
|
||||||
|
let mut rng = rng();
|
||||||
|
let responder_x25519_keypair = generate_keypair_x25519(&mut rng);
|
||||||
|
|
||||||
|
let (session_secret_key, ephemeral_public_key) =
|
||||||
|
KKTSessionSecret::new(&mut rng, responder_x25519_keypair.public_key());
|
||||||
|
|
||||||
|
let shared_secret = KKTSessionSecret::try_derive(
|
||||||
|
responder_x25519_keypair.private_key(),
|
||||||
|
ephemeral_public_key.as_bytes().as_slice(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(shared_secret.as_bytes(), session_secret_key.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encryption() {
|
||||||
|
let mut rng = rng();
|
||||||
|
|
||||||
|
let mut secret_key = [0u8; HASH_LEN_256];
|
||||||
|
rng.fill_bytes(&mut secret_key);
|
||||||
|
|
||||||
|
let mut plaintext = vec![0; 100];
|
||||||
|
rng.fill_bytes(&mut plaintext);
|
||||||
|
|
||||||
|
let mut nonce = [0; 12];
|
||||||
|
rng.fill_bytes(&mut nonce);
|
||||||
|
|
||||||
|
let mut aad = vec![0; 124];
|
||||||
|
rng.fill_bytes(&mut aad);
|
||||||
|
|
||||||
|
let ciphertext = encrypt(&secret_key, &plaintext, &aad, &nonce).unwrap();
|
||||||
|
|
||||||
|
let o_plaintext = decrypt(&secret_key, &ciphertext, &aad, &nonce).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(o_plaintext, plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn kkt_frame_encryption() -> anyhow::Result<()> {
|
||||||
|
let mut rng = ChaCha20Rng::seed_from_u64(42);
|
||||||
|
let session_key = KKTSessionSecret::from_bytes([42u8; 32]);
|
||||||
|
let aad = b"my-amazing-aad";
|
||||||
|
|
||||||
|
let valid_context = KKTContext::new(
|
||||||
|
KKTRole::Initiator,
|
||||||
|
KKTMode::Mutual,
|
||||||
|
Ciphersuite::decode([255, 1, 0, 0])?,
|
||||||
|
)?;
|
||||||
|
let dummy_frame = KKTFrame::new(
|
||||||
|
valid_context.encode()?,
|
||||||
|
&[2u8; 32],
|
||||||
|
[3u8; KKT_SESSION_ID_LEN],
|
||||||
|
&[4u8; 64],
|
||||||
|
);
|
||||||
|
|
||||||
|
let ciphertext = encrypt_kkt_frame(&mut rng, &session_key, &dummy_frame, aad.as_slice())?;
|
||||||
|
|
||||||
|
let (frame, context) = decrypt_kkt_frame(&session_key, &ciphertext, aad.as_slice())?;
|
||||||
|
|
||||||
|
assert_eq!(dummy_frame, frame);
|
||||||
|
assert_eq!(context, valid_context);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::context::KKTStatus;
|
use crate::context::KKTStatus;
|
||||||
@@ -44,6 +45,9 @@ pub enum KKTError {
|
|||||||
#[error("{}", info)]
|
#[error("{}", info)]
|
||||||
X25519Error { info: &'static str },
|
X25519Error { info: &'static str },
|
||||||
|
|
||||||
|
#[error("{}", info)]
|
||||||
|
AEADError { info: &'static str },
|
||||||
|
|
||||||
#[error("Generic libcrux error")]
|
#[error("Generic libcrux error")]
|
||||||
LibcruxError,
|
LibcruxError,
|
||||||
}
|
}
|
||||||
@@ -87,3 +91,27 @@ impl From<libcrux_ecdh::Error> for KKTError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<libcrux_chacha20poly1305::AeadError> for KKTError {
|
||||||
|
fn from(err: libcrux_chacha20poly1305::AeadError) -> Self {
|
||||||
|
KKTError::KEMError {
|
||||||
|
info: match err {
|
||||||
|
libcrux_chacha20poly1305::AeadError::PlaintextTooLarge => {
|
||||||
|
"Plaintext is longer than u32::MAX"
|
||||||
|
}
|
||||||
|
libcrux_chacha20poly1305::AeadError::CiphertextTooLarge => {
|
||||||
|
"Ciphertext is longer than u32::MAX"
|
||||||
|
}
|
||||||
|
libcrux_chacha20poly1305::AeadError::AadTooLarge => "Aad is longer than u32::MAX",
|
||||||
|
libcrux_chacha20poly1305::AeadError::CiphertextTooShort => {
|
||||||
|
"The provided destination ciphertext does not fit the ciphertext and tag"
|
||||||
|
}
|
||||||
|
libcrux_chacha20poly1305::AeadError::PlaintextTooShort => {
|
||||||
|
"The provided destination plaintext is too short to fit the decrypted plaintext"
|
||||||
|
}
|
||||||
|
libcrux_chacha20poly1305::AeadError::InvalidCiphertext => {
|
||||||
|
"The ciphertext is not a valid encryption under the given key and nonce."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+79
-53
@@ -14,9 +14,12 @@ use crate::{
|
|||||||
|
|
||||||
pub const KKT_SESSION_ID_LEN: usize = 16;
|
pub const KKT_SESSION_ID_LEN: usize = 16;
|
||||||
|
|
||||||
|
pub type KKTSessionId = [u8; KKT_SESSION_ID_LEN];
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct KKTFrame {
|
pub struct KKTFrame {
|
||||||
context: Vec<u8>,
|
context: [u8; KKT_CONTEXT_LEN],
|
||||||
session_id: Vec<u8>,
|
session_id: KKTSessionId,
|
||||||
body: Vec<u8>,
|
body: Vec<u8>,
|
||||||
signature: Vec<u8>,
|
signature: Vec<u8>,
|
||||||
}
|
}
|
||||||
@@ -27,20 +30,31 @@ pub struct KKTFrame {
|
|||||||
// if coming from responder => body has the responder's kem public key and the signature is over the context + body + session_id.
|
// if coming from responder => body has the responder's kem public key and the signature is over the context + body + session_id.
|
||||||
|
|
||||||
impl KKTFrame {
|
impl KKTFrame {
|
||||||
pub fn new(context: &[u8], body: &[u8], session_id: &[u8], signature: &[u8]) -> Self {
|
pub fn new(
|
||||||
|
context: [u8; KKT_CONTEXT_LEN],
|
||||||
|
body: &[u8],
|
||||||
|
session_id: [u8; KKT_SESSION_ID_LEN],
|
||||||
|
signature: &[u8],
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
context: Vec::from(context),
|
context,
|
||||||
body: Vec::from(body),
|
body: Vec::from(body),
|
||||||
session_id: Vec::from(session_id),
|
session_id,
|
||||||
signature: Vec::from(signature),
|
signature: Vec::from(signature),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn context_ref(&self) -> &[u8] {
|
pub fn context_ref(&self) -> &[u8] {
|
||||||
&self.context
|
&self.context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn context(&self) -> Result<KKTContext, KKTError> {
|
||||||
|
KKTContext::try_decode(self.context)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn signature_ref(&self) -> &[u8] {
|
pub fn signature_ref(&self) -> &[u8] {
|
||||||
&self.signature
|
&self.signature
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body_ref(&self) -> &[u8] {
|
pub fn body_ref(&self) -> &[u8] {
|
||||||
&self.body
|
&self.body
|
||||||
}
|
}
|
||||||
@@ -48,6 +62,10 @@ impl KKTFrame {
|
|||||||
pub fn session_id_ref(&self) -> &[u8] {
|
pub fn session_id_ref(&self) -> &[u8] {
|
||||||
&self.session_id
|
&self.session_id
|
||||||
}
|
}
|
||||||
|
pub fn session_id(&self) -> [u8; KKT_SESSION_ID_LEN] {
|
||||||
|
self.session_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn signature_mut(&mut self) -> &mut [u8] {
|
pub fn signature_mut(&mut self) -> &mut [u8] {
|
||||||
&mut self.signature
|
&mut self.signature
|
||||||
}
|
}
|
||||||
@@ -73,57 +91,65 @@ impl KKTFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<(Self, KKTContext), KKTError> {
|
pub fn from_bytes(bytes: &[u8]) -> Result<(Self, KKTContext), KKTError> {
|
||||||
|
let len = bytes.len();
|
||||||
if bytes.len() < KKT_CONTEXT_LEN {
|
if bytes.len() < KKT_CONTEXT_LEN {
|
||||||
Err(KKTError::FrameDecodingError {
|
return Err(KKTError::FrameDecodingError {
|
||||||
info: format!(
|
info: format!(
|
||||||
"Frame is shorter than expected context length: actual {} != expected {}",
|
"Frame is shorter than expected context length: actual {len} != expected {KKT_CONTEXT_LEN}",
|
||||||
bytes.len(),
|
|
||||||
KKT_CONTEXT_LEN
|
|
||||||
),
|
),
|
||||||
})
|
});
|
||||||
} else {
|
|
||||||
let context_bytes = Vec::from(&bytes[0..KKT_CONTEXT_LEN]);
|
|
||||||
|
|
||||||
let context = KKTContext::try_decode(&context_bytes)?;
|
|
||||||
|
|
||||||
let (mut session_id, mut body, mut signature): (Vec<u8>, Vec<u8>, Vec<u8>) =
|
|
||||||
(vec![], vec![], vec![]);
|
|
||||||
|
|
||||||
if bytes.len() == context.full_message_len() {
|
|
||||||
if context.body_len() > 0 {
|
|
||||||
body.extend_from_slice(
|
|
||||||
&bytes[KKT_CONTEXT_LEN..KKT_CONTEXT_LEN + context.body_len()],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if context.session_id_len() > 0 {
|
|
||||||
session_id.extend_from_slice(
|
|
||||||
&bytes[KKT_CONTEXT_LEN + context.body_len()
|
|
||||||
..KKT_CONTEXT_LEN + context.body_len() + context.session_id_len()],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if context.signature_len() > 0 {
|
|
||||||
signature.extend_from_slice(
|
|
||||||
&bytes[KKT_CONTEXT_LEN + context.body_len() + context.session_id_len()
|
|
||||||
..KKT_CONTEXT_LEN
|
|
||||||
+ context.body_len()
|
|
||||||
+ context.session_id_len()
|
|
||||||
+ context.signature_len()],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
KKTFrame::new(&context_bytes, &body, &session_id, &signature),
|
|
||||||
context,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Err(KKTError::FrameDecodingError {
|
|
||||||
info: format!(
|
|
||||||
"Frame is shorter than expected: actual {} != expected {}",
|
|
||||||
bytes.len(),
|
|
||||||
context.full_message_len()
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: we're using exactly KKT_CONTEXT_LEN bytes
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let context_bytes = bytes[0..KKT_CONTEXT_LEN].try_into().unwrap();
|
||||||
|
let context = KKTContext::try_decode(context_bytes)?;
|
||||||
|
|
||||||
|
if bytes.len() != context.full_message_len() {
|
||||||
|
return Err(KKTError::FrameDecodingError {
|
||||||
|
info: format!(
|
||||||
|
"Frame is shorter than expected: actual {len} != expected {}",
|
||||||
|
context.full_message_len()
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body = Vec::new();
|
||||||
|
let mut signature = Vec::new();
|
||||||
|
|
||||||
|
// decode body
|
||||||
|
if context.body_len() > 0 {
|
||||||
|
let body_bytes = &bytes[KKT_CONTEXT_LEN..KKT_CONTEXT_LEN + context.body_len()];
|
||||||
|
body.extend_from_slice(body_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
let session_bytes = &bytes[KKT_CONTEXT_LEN + context.body_len()
|
||||||
|
..KKT_CONTEXT_LEN + context.body_len() + KKT_SESSION_ID_LEN];
|
||||||
|
// SAFETY: we're using exactly KKT_SESSION_ID_LEN bytes and we checked for sufficient bytes
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let session_id = session_bytes.try_into().unwrap();
|
||||||
|
|
||||||
|
// // old code left for reference if session id becomes variable in length:
|
||||||
|
// if context.session_id_len() > 0 {
|
||||||
|
// session_id.extend_from_slice(
|
||||||
|
// &bytes[KKT_CONTEXT_LEN + context.body_len()
|
||||||
|
// ..KKT_CONTEXT_LEN + context.body_len() + context.session_id_len()],
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// decode signature
|
||||||
|
if context.signature_len() > 0 {
|
||||||
|
let signature_bytes = &bytes[KKT_CONTEXT_LEN + context.body_len() + KKT_SESSION_ID_LEN
|
||||||
|
..KKT_CONTEXT_LEN
|
||||||
|
+ context.body_len()
|
||||||
|
+ KKT_SESSION_ID_LEN
|
||||||
|
+ context.signature_len()];
|
||||||
|
signature.extend_from_slice(signature_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
KKTFrame::new(context_bytes, &body, session_id, &signature),
|
||||||
|
context,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,53 @@
|
|||||||
use crate::{
|
use crate::ciphersuite::HashFunction;
|
||||||
ciphersuite::{HashFunction, KEM},
|
|
||||||
error::KKTError,
|
|
||||||
};
|
|
||||||
|
|
||||||
use classic_mceliece_rust::keypair_boxed;
|
use classic_mceliece_rust::keypair_boxed;
|
||||||
use libcrux_kem::{Algorithm, key_gen};
|
|
||||||
|
|
||||||
use libcrux_sha3;
|
use libcrux_sha3;
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
|
pub fn generate_keypair_ed25519<R>(
|
||||||
|
rng: &mut R,
|
||||||
|
index: Option<u32>,
|
||||||
|
) -> nym_crypto::asymmetric::ed25519::KeyPair
|
||||||
|
where
|
||||||
|
R: RngCore + CryptoRng,
|
||||||
|
{
|
||||||
|
let mut secret_initiator: [u8; 32] = [0u8; 32];
|
||||||
|
rng.fill_bytes(&mut secret_initiator);
|
||||||
|
nym_crypto::asymmetric::ed25519::KeyPair::from_secret(secret_initiator, index.unwrap_or(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_keypair_x25519<R>(rng: &mut R) -> nym_crypto::asymmetric::x25519::KeyPair
|
||||||
|
where
|
||||||
|
R: RngCore + CryptoRng,
|
||||||
|
{
|
||||||
|
let mut secret_initiator: [u8; 32] = [0u8; 32];
|
||||||
|
rng.fill_bytes(&mut secret_initiator);
|
||||||
|
|
||||||
|
let private_key = nym_crypto::asymmetric::x25519::PrivateKey::from_secret(secret_initiator);
|
||||||
|
private_key.into()
|
||||||
|
}
|
||||||
|
|
||||||
// (decapsulation_key, encapsulation_key)
|
// (decapsulation_key, encapsulation_key)
|
||||||
pub fn generate_keypair_libcrux<R>(
|
pub fn generate_keypair_libcrux<R>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
kem: KEM,
|
kem: crate::ciphersuite::KEM,
|
||||||
) -> Result<(libcrux_kem::PrivateKey, libcrux_kem::PublicKey), KKTError>
|
) -> Result<(libcrux_kem::PrivateKey, libcrux_kem::PublicKey), crate::error::KKTError>
|
||||||
where
|
where
|
||||||
R: RngCore + CryptoRng,
|
R: RngCore + CryptoRng,
|
||||||
{
|
{
|
||||||
match kem {
|
match kem {
|
||||||
KEM::MlKem768 => Ok(key_gen(Algorithm::MlKem768, rng)?),
|
crate::ciphersuite::KEM::MlKem768 => {
|
||||||
KEM::XWing => Ok(key_gen(Algorithm::XWingKemDraft06, rng)?),
|
Ok(libcrux_kem::key_gen(libcrux_kem::Algorithm::MlKem768, rng)?)
|
||||||
KEM::X25519 => Ok(key_gen(Algorithm::X25519, rng)?),
|
}
|
||||||
_ => Err(KKTError::KEMError {
|
crate::ciphersuite::KEM::XWing => Ok(libcrux_kem::key_gen(
|
||||||
|
libcrux_kem::Algorithm::XWingKemDraft06,
|
||||||
|
rng,
|
||||||
|
)?),
|
||||||
|
crate::ciphersuite::KEM::X25519 => {
|
||||||
|
Ok(libcrux_kem::key_gen(libcrux_kem::Algorithm::X25519, rng)?)
|
||||||
|
}
|
||||||
|
_ => Err(crate::error::KKTError::KEMError {
|
||||||
info: "Key Generation Error: Unsupported Libcrux Algorithm",
|
info: "Key Generation Error: Unsupported Libcrux Algorithm",
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|||||||
+140
-42
@@ -8,14 +8,14 @@
|
|||||||
//!
|
//!
|
||||||
//! The underlying KKT protocol is implemented in the `session` module.
|
//! The underlying KKT protocol is implemented in the `session` module.
|
||||||
|
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ciphersuite::{Ciphersuite, EncapsulationKey},
|
ciphersuite::{Ciphersuite, EncapsulationKey},
|
||||||
context::{KKTContext, KKTMode},
|
context::{KKTContext, KKTMode},
|
||||||
|
encryption::{decrypt_initial_kkt_frame, decrypt_kkt_frame, encrypt_kkt_frame},
|
||||||
error::KKTError,
|
error::KKTError,
|
||||||
frame::KKTFrame,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Re-export core session functions for advanced use cases
|
// Re-export core session functions for advanced use cases
|
||||||
@@ -24,7 +24,13 @@ pub use crate::session::{
|
|||||||
responder_ingest_message, responder_process,
|
responder_ingest_message, responder_process,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Request a KEM public key from a responder (OneWay mode).
|
use crate::encryption::{KKTSessionSecret, encrypt_initial_kkt_frame};
|
||||||
|
use crate::frame::KKTFrame;
|
||||||
|
|
||||||
|
pub(crate) const KKT_RESPONSE_AAD: &[u8] = b"KKT_Response";
|
||||||
|
pub(crate) const KKT_INITIAL_FRAME_AAD: &[u8] = b"KKT_INITIAL_FRAME";
|
||||||
|
|
||||||
|
/// Perform an *Encrypted* request for a KEM public key from a responder (OneWay mode).
|
||||||
///
|
///
|
||||||
/// This is the client-side operation that initiates a KKT exchange.
|
/// This is the client-side operation that initiates a KKT exchange.
|
||||||
/// The request will be signed with the provided signing key.
|
/// The request will be signed with the provided signing key.
|
||||||
@@ -33,17 +39,20 @@ pub use crate::session::{
|
|||||||
/// * `rng` - Random number generator
|
/// * `rng` - Random number generator
|
||||||
/// * `ciphersuite` - Negotiated ciphersuite (KEM, hash, signature algorithms)
|
/// * `ciphersuite` - Negotiated ciphersuite (KEM, hash, signature algorithms)
|
||||||
/// * `signing_key` - Client's Ed25519 signing key for authentication
|
/// * `signing_key` - Client's Ed25519 signing key for authentication
|
||||||
|
/// * `responder_dh_public_key` - Responder's long-term x25519 Diffie-Hellman public key
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
|
/// * `KKTSessionSecret` - Session Secret Key to use when decrypting responses
|
||||||
/// * `KKTContext` - Context to use when validating the response
|
/// * `KKTContext` - Context to use when validating the response
|
||||||
/// * `KKTFrame` - Signed request frame to send to responder
|
/// * `Vec<u8>` - Contains the client's ephemeral public key and encrypted and signed bytes to send to responder
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// let (context, request_frame) = request_kem_key(
|
/// let (session_secret, context, request_frame) = request_kem_key(
|
||||||
/// &mut rng,
|
/// &mut rng,
|
||||||
/// ciphersuite,
|
/// ciphersuite,
|
||||||
/// client_signing_key,
|
/// client_signing_key,
|
||||||
|
/// responder_dh_public_key,
|
||||||
/// )?;
|
/// )?;
|
||||||
/// // Send request_frame to gateway
|
/// // Send request_frame to gateway
|
||||||
/// ```
|
/// ```
|
||||||
@@ -51,13 +60,21 @@ pub fn request_kem_key<R: CryptoRng + RngCore>(
|
|||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
ciphersuite: Ciphersuite,
|
ciphersuite: Ciphersuite,
|
||||||
signing_key: &ed25519::PrivateKey,
|
signing_key: &ed25519::PrivateKey,
|
||||||
) -> Result<(KKTContext, KKTFrame), KKTError> {
|
responder_dh_public_key: &x25519::PublicKey,
|
||||||
|
) -> Result<(KKTSessionSecret, KKTContext, Vec<u8>), KKTError> {
|
||||||
// OneWay mode: client only wants responder's KEM key
|
// OneWay mode: client only wants responder's KEM key
|
||||||
// None: client doesn't send their own KEM key
|
// None: client doesn't send their own KEM key
|
||||||
initiator_process(rng, KKTMode::OneWay, ciphersuite, signing_key, None)
|
let (initiator_context, initiator_frame) =
|
||||||
|
initiator_process(rng, KKTMode::OneWay, ciphersuite, signing_key, None)?;
|
||||||
|
|
||||||
|
// Generate the session's shared secret and encrypt the Initiator's request
|
||||||
|
let (session_secret, encrypted_request_bytes) =
|
||||||
|
encrypt_initial_kkt_frame(rng, responder_dh_public_key, &initiator_frame)?;
|
||||||
|
|
||||||
|
Ok((session_secret, initiator_context, encrypted_request_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validate a KKT response and extract the responder's KEM public key.
|
/// Decrypt, validate an *Encrypted* KKT response and extract the responder's KEM public key.
|
||||||
///
|
///
|
||||||
/// This is the client-side operation that processes the gateway's response.
|
/// This is the client-side operation that processes the gateway's response.
|
||||||
/// It verifies the signature and validates the key hash against the expected value
|
/// It verifies the signature and validates the key hash against the expected value
|
||||||
@@ -65,6 +82,7 @@ pub fn request_kem_key<R: CryptoRng + RngCore>(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `context` - Context from the initial request
|
/// * `context` - Context from the initial request
|
||||||
|
/// * `session_secret` - Session Secret Key (generated with request)
|
||||||
/// * `responder_vk` - Responder's Ed25519 verification key (from directory)
|
/// * `responder_vk` - Responder's Ed25519 verification key (from directory)
|
||||||
/// * `expected_key_hash` - Expected hash of responder's KEM key (from directory)
|
/// * `expected_key_hash` - Expected hash of responder's KEM key (from directory)
|
||||||
/// * `response_bytes` - Serialized response frame from responder
|
/// * `response_bytes` - Serialized response frame from responder
|
||||||
@@ -76,7 +94,8 @@ pub fn request_kem_key<R: CryptoRng + RngCore>(
|
|||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// let gateway_kem_key = validate_kem_response(
|
/// let gateway_kem_key = validate_kem_response(
|
||||||
/// &mut context,
|
/// &mut context,
|
||||||
/// gateway_verification_key,
|
/// &session_secret,
|
||||||
|
/// &gateway_verification_key,
|
||||||
/// &expected_hash_from_directory,
|
/// &expected_hash_from_directory,
|
||||||
/// &response_bytes,
|
/// &response_bytes,
|
||||||
/// )?;
|
/// )?;
|
||||||
@@ -84,23 +103,44 @@ pub fn request_kem_key<R: CryptoRng + RngCore>(
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn validate_kem_response<'a>(
|
pub fn validate_kem_response<'a>(
|
||||||
context: &mut KKTContext,
|
context: &mut KKTContext,
|
||||||
|
session_secret: &KKTSessionSecret,
|
||||||
responder_vk: &ed25519::PublicKey,
|
responder_vk: &ed25519::PublicKey,
|
||||||
expected_key_hash: &[u8],
|
expected_key_hash: &[u8],
|
||||||
response_bytes: &[u8],
|
encrypted_response_bytes: &[u8],
|
||||||
) -> Result<EncapsulationKey<'a>, KKTError> {
|
) -> Result<EncapsulationKey<'a>, KKTError> {
|
||||||
initiator_ingest_response(context, responder_vk, expected_key_hash, response_bytes)
|
let (responder_frame, responder_context) =
|
||||||
|
decrypt_kkt_response_frame(session_secret, encrypted_response_bytes)?;
|
||||||
|
|
||||||
|
initiator_ingest_response(
|
||||||
|
context,
|
||||||
|
&responder_frame,
|
||||||
|
&responder_context,
|
||||||
|
responder_vk,
|
||||||
|
expected_key_hash,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a KKT request and generate a signed response with the responder's KEM key.
|
/// Decrypts and validates an *Encrypted* KKT response
|
||||||
|
///
|
||||||
|
/// This is the client-side operation that processes the gateway's response.
|
||||||
|
pub fn decrypt_kkt_response_frame(
|
||||||
|
session_secret: &KKTSessionSecret,
|
||||||
|
frame_ciphertext: &[u8],
|
||||||
|
) -> Result<(KKTFrame, KKTContext), KKTError> {
|
||||||
|
decrypt_kkt_frame(session_secret, frame_ciphertext, KKT_RESPONSE_AAD)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle an *Encrypted* KKT request and generate a signed response with the responder's KEM key.
|
||||||
///
|
///
|
||||||
/// This is the gateway-side operation that processes a client's KKT request.
|
/// This is the gateway-side operation that processes a client's KKT request.
|
||||||
/// It validates the request signature (if authenticated) and responds with
|
/// It validates the request signature (if authenticated) and responds with
|
||||||
/// the gateway's KEM public key, signed for authenticity.
|
/// the gateway's KEM public key, signed for authenticity.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `request_frame` - Request frame received from initiator
|
/// * `encrypted_request_bytes` - encrypted KEM request
|
||||||
/// * `initiator_vk` - Initiator's Ed25519 verification key (None for anonymous)
|
/// * `initiator_vk` - Initiator's Ed25519 verification key (None for anonymous)
|
||||||
/// * `responder_signing_key` - Gateway's Ed25519 signing key
|
/// * `responder_signing_key` - Gateway's Ed25519 signing key
|
||||||
|
/// * `responder_dh_public_key` - Gateway's long-term x25519 Diffie-Hellman private key
|
||||||
/// * `responder_kem_key` - Gateway's KEM public key to send
|
/// * `responder_kem_key` - Gateway's KEM public key to send
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@@ -116,31 +156,40 @@ pub fn validate_kem_response<'a>(
|
|||||||
/// )?;
|
/// )?;
|
||||||
/// // Send response_frame back to client
|
/// // Send response_frame back to client
|
||||||
/// ```
|
/// ```
|
||||||
pub fn handle_kem_request<'a>(
|
pub fn handle_kem_request<'a, R>(
|
||||||
request_frame: &KKTFrame,
|
rng: &mut R,
|
||||||
|
encrypted_request_bytes: &[u8],
|
||||||
initiator_vk: Option<&ed25519::PublicKey>,
|
initiator_vk: Option<&ed25519::PublicKey>,
|
||||||
responder_signing_key: &ed25519::PrivateKey,
|
responder_signing_key: &ed25519::PrivateKey,
|
||||||
|
responder_dh_private_key: &x25519::PrivateKey,
|
||||||
responder_kem_key: &EncapsulationKey<'a>,
|
responder_kem_key: &EncapsulationKey<'a>,
|
||||||
) -> Result<KKTFrame, KKTError> {
|
) -> Result<Vec<u8>, KKTError>
|
||||||
// Parse context from the request frame
|
where
|
||||||
let request_bytes = request_frame.to_bytes();
|
R: RngCore + CryptoRng,
|
||||||
let (_, request_context) = KKTFrame::from_bytes(&request_bytes)?;
|
{
|
||||||
|
// Compute the session's shared secret, decrypt and parse context from the request frame
|
||||||
|
|
||||||
|
let (session_secret, request_frame, initiator_context) =
|
||||||
|
decrypt_initial_kkt_frame(responder_dh_private_key, encrypted_request_bytes)?;
|
||||||
|
|
||||||
// Validate the request (verifies signature if initiator_vk provided)
|
// Validate the request (verifies signature if initiator_vk provided)
|
||||||
let (mut response_context, _) = responder_ingest_message(
|
let (mut response_context, _) = responder_ingest_message(
|
||||||
&request_context,
|
&initiator_context,
|
||||||
initiator_vk,
|
initiator_vk,
|
||||||
None, // Not checking initiator's KEM key in OneWay mode
|
None, // Not checking initiator's KEM key in OneWay mode
|
||||||
request_frame,
|
&request_frame,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Generate signed response with our KEM public key
|
// Generate signed response with our KEM public key
|
||||||
responder_process(
|
let responder_frame = responder_process(
|
||||||
&mut response_context,
|
&mut response_context,
|
||||||
request_frame.session_id_ref(),
|
request_frame.session_id(),
|
||||||
responder_signing_key,
|
responder_signing_key,
|
||||||
responder_kem_key,
|
responder_kem_key,
|
||||||
)
|
)?;
|
||||||
|
|
||||||
|
// Encrypt the responder's response with the session's shared secret
|
||||||
|
encrypt_kkt_frame(rng, &session_secret, &responder_frame, KKT_RESPONSE_AAD)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -151,6 +200,13 @@ mod tests {
|
|||||||
key_utils::{generate_keypair_libcrux, hash_encapsulation_key},
|
key_utils::{generate_keypair_libcrux, hash_encapsulation_key},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn random_x25519_key() -> x25519::PrivateKey {
|
||||||
|
let mut bytes = [0u8; 32];
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
rng.fill_bytes(&mut bytes);
|
||||||
|
x25519::PrivateKey::from_secret(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_kkt_wrappers_oneway_authenticated() {
|
fn test_kkt_wrappers_oneway_authenticated() {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
@@ -158,11 +214,14 @@ mod tests {
|
|||||||
// Generate Ed25519 keypairs for both parties
|
// Generate Ed25519 keypairs for both parties
|
||||||
let mut initiator_secret = [0u8; 32];
|
let mut initiator_secret = [0u8; 32];
|
||||||
rng.fill_bytes(&mut initiator_secret);
|
rng.fill_bytes(&mut initiator_secret);
|
||||||
let initiator_keypair = ed25519::KeyPair::from_secret(initiator_secret, 0);
|
let ed25519_init = ed25519::KeyPair::from_secret(initiator_secret, 0);
|
||||||
|
|
||||||
let mut responder_secret = [0u8; 32];
|
let mut responder_secret = [0u8; 32];
|
||||||
rng.fill_bytes(&mut responder_secret);
|
rng.fill_bytes(&mut responder_secret);
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
let ed25519_resp = ed25519::KeyPair::from_secret(responder_secret, 1);
|
||||||
|
|
||||||
|
let x25519_resp_priv = random_x25519_key();
|
||||||
|
let x25519_resp_pub = x25519::PublicKey::from(&x25519_resp_priv);
|
||||||
|
|
||||||
// Generate responder's KEM keypair (X25519 for testing)
|
// Generate responder's KEM keypair (X25519 for testing)
|
||||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
@@ -185,14 +244,21 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Client: Request KEM key
|
// Client: Request KEM key
|
||||||
let (mut context, request_frame) =
|
let (session_key, mut context, request_frame_ciphertext) = request_kem_key(
|
||||||
request_kem_key(&mut rng, ciphersuite, initiator_keypair.private_key()).unwrap();
|
&mut rng,
|
||||||
|
ciphersuite,
|
||||||
|
ed25519_init.private_key(),
|
||||||
|
&x25519_resp_pub,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Gateway: Handle request
|
// Gateway: Handle request
|
||||||
let response_frame = handle_kem_request(
|
let response_frame_ciphertext = handle_kem_request(
|
||||||
&request_frame,
|
&mut rng,
|
||||||
Some(initiator_keypair.public_key()), // Authenticated
|
&request_frame_ciphertext,
|
||||||
responder_keypair.private_key(),
|
Some(ed25519_init.public_key()), // Authenticated
|
||||||
|
ed25519_resp.private_key(),
|
||||||
|
&x25519_resp_priv,
|
||||||
&responder_kem_key,
|
&responder_kem_key,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -200,9 +266,10 @@ mod tests {
|
|||||||
// Client: Validate response
|
// Client: Validate response
|
||||||
let obtained_key = validate_kem_response(
|
let obtained_key = validate_kem_response(
|
||||||
&mut context,
|
&mut context,
|
||||||
responder_keypair.public_key(),
|
&session_key,
|
||||||
|
ed25519_resp.public_key(),
|
||||||
&key_hash,
|
&key_hash,
|
||||||
&response_frame.to_bytes(),
|
&response_frame_ciphertext,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -222,6 +289,9 @@ mod tests {
|
|||||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||||
|
|
||||||
|
let x25519_resp_priv = random_x25519_key();
|
||||||
|
let x25519_resp_pub = x25519::PublicKey::from(&x25519_resp_priv);
|
||||||
|
|
||||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||||
KEM::X25519,
|
KEM::X25519,
|
||||||
HashFunction::Blake3,
|
HashFunction::Blake3,
|
||||||
@@ -240,11 +310,17 @@ mod tests {
|
|||||||
let (mut context, request_frame) =
|
let (mut context, request_frame) =
|
||||||
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
|
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
|
||||||
|
|
||||||
|
// Generate the session's shared secret and encrypt the Initiator's request
|
||||||
|
let (session_secret, encrypted_request_bytes) =
|
||||||
|
encrypt_initial_kkt_frame(&mut rng, &x25519_resp_pub, &request_frame).unwrap();
|
||||||
|
|
||||||
// Gateway: Handle anonymous request
|
// Gateway: Handle anonymous request
|
||||||
let response_frame = handle_kem_request(
|
let response_frame = handle_kem_request(
|
||||||
&request_frame,
|
&mut rng,
|
||||||
|
&encrypted_request_bytes,
|
||||||
None, // Anonymous - no verification key
|
None, // Anonymous - no verification key
|
||||||
responder_keypair.private_key(),
|
responder_keypair.private_key(),
|
||||||
|
&x25519_resp_priv,
|
||||||
&responder_kem_key,
|
&responder_kem_key,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -252,9 +328,10 @@ mod tests {
|
|||||||
// Initiator: Validate response
|
// Initiator: Validate response
|
||||||
let obtained_key = validate_kem_response(
|
let obtained_key = validate_kem_response(
|
||||||
&mut context,
|
&mut context,
|
||||||
|
&session_secret,
|
||||||
responder_keypair.public_key(),
|
responder_keypair.public_key(),
|
||||||
&key_hash,
|
&key_hash,
|
||||||
&response_frame.to_bytes(),
|
&response_frame,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -273,6 +350,9 @@ mod tests {
|
|||||||
rng.fill_bytes(&mut responder_secret);
|
rng.fill_bytes(&mut responder_secret);
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
||||||
|
|
||||||
|
let x25519_resp_priv = random_x25519_key();
|
||||||
|
let x25519_resp_pub = x25519::PublicKey::from(&x25519_resp_priv);
|
||||||
|
|
||||||
// Different keypair for wrong signature
|
// Different keypair for wrong signature
|
||||||
let mut wrong_secret = [0u8; 32];
|
let mut wrong_secret = [0u8; 32];
|
||||||
rng.fill_bytes(&mut wrong_secret);
|
rng.fill_bytes(&mut wrong_secret);
|
||||||
@@ -289,14 +369,21 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (_context, request_frame) =
|
let (_session_key, _context, request_frame_ciphertext) = request_kem_key(
|
||||||
request_kem_key(&mut rng, ciphersuite, initiator_keypair.private_key()).unwrap();
|
&mut rng,
|
||||||
|
ciphersuite,
|
||||||
|
initiator_keypair.private_key(),
|
||||||
|
&x25519_resp_pub,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Gateway handles request but we provide WRONG verification key
|
// Gateway handles request but we provide WRONG verification key
|
||||||
let result = handle_kem_request(
|
let result = handle_kem_request(
|
||||||
&request_frame,
|
&mut rng,
|
||||||
|
&request_frame_ciphertext,
|
||||||
Some(wrong_keypair.public_key()), // Wrong key!
|
Some(wrong_keypair.public_key()), // Wrong key!
|
||||||
responder_keypair.private_key(),
|
responder_keypair.private_key(),
|
||||||
|
&x25519_resp_priv,
|
||||||
&responder_kem_key,
|
&responder_kem_key,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -316,6 +403,9 @@ mod tests {
|
|||||||
rng.fill_bytes(&mut responder_secret);
|
rng.fill_bytes(&mut responder_secret);
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
||||||
|
|
||||||
|
let x25519_resp_priv = random_x25519_key();
|
||||||
|
let x25519_resp_pub = x25519::PublicKey::from(&x25519_resp_priv);
|
||||||
|
|
||||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||||
|
|
||||||
@@ -330,13 +420,20 @@ mod tests {
|
|||||||
// Use WRONG hash
|
// Use WRONG hash
|
||||||
let wrong_hash = [0u8; 32];
|
let wrong_hash = [0u8; 32];
|
||||||
|
|
||||||
let (mut context, request_frame) =
|
let (session_key, mut context, request_frame) = request_kem_key(
|
||||||
request_kem_key(&mut rng, ciphersuite, initiator_keypair.private_key()).unwrap();
|
&mut rng,
|
||||||
|
ciphersuite,
|
||||||
|
initiator_keypair.private_key(),
|
||||||
|
&x25519_resp_pub,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let response_frame = handle_kem_request(
|
let response_frame = handle_kem_request(
|
||||||
|
&mut rng,
|
||||||
&request_frame,
|
&request_frame,
|
||||||
Some(initiator_keypair.public_key()),
|
Some(initiator_keypair.public_key()),
|
||||||
responder_keypair.private_key(),
|
responder_keypair.private_key(),
|
||||||
|
&x25519_resp_priv,
|
||||||
&responder_kem_key,
|
&responder_kem_key,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -344,9 +441,10 @@ mod tests {
|
|||||||
// Client validates with WRONG hash
|
// Client validates with WRONG hash
|
||||||
let result = validate_kem_response(
|
let result = validate_kem_response(
|
||||||
&mut context,
|
&mut context,
|
||||||
|
&session_key,
|
||||||
responder_keypair.public_key(),
|
responder_keypair.public_key(),
|
||||||
&wrong_hash, // Wrong!
|
&wrong_hash, // Wrong!
|
||||||
&response_frame.to_bytes(),
|
&response_frame,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Should fail hash validation
|
// Should fail hash validation
|
||||||
|
|||||||
+288
-23
@@ -3,28 +3,31 @@
|
|||||||
|
|
||||||
pub mod ciphersuite;
|
pub mod ciphersuite;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
// pub mod encryption;
|
pub mod encryption;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
pub mod key_utils;
|
pub mod key_utils;
|
||||||
pub mod kkt;
|
pub mod kkt;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
|
||||||
// pub mod psq;
|
|
||||||
|
|
||||||
// This must be less than 4 bits
|
// This must be less than 4 bits
|
||||||
pub const KKT_VERSION: u8 = 1;
|
pub const KKT_VERSION: u8 = 1;
|
||||||
const _: () = assert!(KKT_VERSION < 1 << 4);
|
const _: () = assert!(KKT_VERSION < 1 << 4);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use crate::kkt::KKT_RESPONSE_AAD;
|
||||||
use rand::prelude::*;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, KEM},
|
ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, KEM},
|
||||||
|
encryption::{
|
||||||
|
decrypt_initial_kkt_frame, decrypt_kkt_frame, encrypt_initial_kkt_frame,
|
||||||
|
encrypt_kkt_frame,
|
||||||
|
},
|
||||||
frame::KKTFrame,
|
frame::KKTFrame,
|
||||||
key_utils::{generate_keypair_libcrux, generate_keypair_mceliece, hash_encapsulation_key},
|
key_utils::{
|
||||||
|
generate_keypair_ed25519, generate_keypair_libcrux, generate_keypair_mceliece,
|
||||||
|
generate_keypair_x25519, hash_encapsulation_key,
|
||||||
|
},
|
||||||
session::{
|
session::{
|
||||||
anonymous_initiator_process, initiator_ingest_response, initiator_process,
|
anonymous_initiator_process, initiator_ingest_response, initiator_process,
|
||||||
responder_ingest_message, responder_process,
|
responder_ingest_message, responder_process,
|
||||||
@@ -36,13 +39,9 @@ mod test {
|
|||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
// generate ed25519 keys
|
// generate ed25519 keys
|
||||||
let mut secret_initiator: [u8; 32] = [0u8; 32];
|
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||||
rng.fill_bytes(&mut secret_initiator);
|
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||||
let initiator_ed25519_keypair = ed25519::KeyPair::from_secret(secret_initiator, 0);
|
|
||||||
|
|
||||||
let mut secret_responder: [u8; 32] = [0u8; 32];
|
|
||||||
rng.fill_bytes(&mut secret_responder);
|
|
||||||
let responder_ed25519_keypair = ed25519::KeyPair::from_secret(secret_responder, 1);
|
|
||||||
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
|
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
|
||||||
for hash_function in [
|
for hash_function in [
|
||||||
HashFunction::Blake3,
|
HashFunction::Blake3,
|
||||||
@@ -117,7 +116,7 @@ mod test {
|
|||||||
|
|
||||||
let r_frame = responder_process(
|
let r_frame = responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
@@ -125,15 +124,18 @@ mod test {
|
|||||||
|
|
||||||
let r_bytes = r_frame.to_bytes();
|
let r_bytes = r_frame.to_bytes();
|
||||||
|
|
||||||
let obtained_key = initiator_ingest_response(
|
let (i_frame_r, i_context_r) = KKTFrame::from_bytes(&r_bytes).unwrap();
|
||||||
|
|
||||||
|
let i_obtained_key = initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&i_frame_r,
|
||||||
|
&i_context_r,
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(obtained_key.encode(), r_kem_key_bytes)
|
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
|
||||||
}
|
}
|
||||||
// Initiator, OneWay
|
// Initiator, OneWay
|
||||||
{
|
{
|
||||||
@@ -162,7 +164,7 @@ mod test {
|
|||||||
|
|
||||||
let r_frame = responder_process(
|
let r_frame = responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
@@ -170,11 +172,14 @@ mod test {
|
|||||||
|
|
||||||
let r_bytes = r_frame.to_bytes();
|
let r_bytes = r_frame.to_bytes();
|
||||||
|
|
||||||
|
let (i_frame_r, i_context_r) = KKTFrame::from_bytes(&r_bytes).unwrap();
|
||||||
|
|
||||||
let i_obtained_key = initiator_ingest_response(
|
let i_obtained_key = initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&i_frame_r,
|
||||||
|
&i_context_r,
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -208,7 +213,7 @@ mod test {
|
|||||||
|
|
||||||
let r_frame = responder_process(
|
let r_frame = responder_process(
|
||||||
&mut r_context,
|
&mut r_context,
|
||||||
i_frame_r.session_id_ref(),
|
i_frame_r.session_id(),
|
||||||
responder_ed25519_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
&responder_kem_public_key,
|
&responder_kem_public_key,
|
||||||
)
|
)
|
||||||
@@ -216,15 +221,275 @@ mod test {
|
|||||||
|
|
||||||
let r_bytes = r_frame.to_bytes();
|
let r_bytes = r_frame.to_bytes();
|
||||||
|
|
||||||
let obtained_key = initiator_ingest_response(
|
let (i_frame_r, i_context_r) = KKTFrame::from_bytes(&r_bytes).unwrap();
|
||||||
|
|
||||||
|
let i_obtained_key = initiator_ingest_response(
|
||||||
&mut i_context,
|
&mut i_context,
|
||||||
|
&i_frame_r,
|
||||||
|
&i_context_r,
|
||||||
responder_ed25519_keypair.public_key(),
|
responder_ed25519_keypair.public_key(),
|
||||||
&r_dir_hash,
|
&r_dir_hash,
|
||||||
&r_bytes,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(obtained_key.encode(), r_kem_key_bytes)
|
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_kkt_psq_e2e_encrypted() {
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
|
// generate ed25519 keys
|
||||||
|
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||||
|
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||||
|
|
||||||
|
// generate responder x25519 keys
|
||||||
|
let responder_x25519_keypair = generate_keypair_x25519(&mut rng);
|
||||||
|
|
||||||
|
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
|
||||||
|
for hash_function in [
|
||||||
|
HashFunction::Blake3,
|
||||||
|
HashFunction::SHA256,
|
||||||
|
HashFunction::SHAKE128,
|
||||||
|
HashFunction::SHAKE256,
|
||||||
|
] {
|
||||||
|
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||||
|
kem,
|
||||||
|
hash_function,
|
||||||
|
crate::ciphersuite::SignatureScheme::Ed25519,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// generate kem public keys
|
||||||
|
|
||||||
|
let (responder_kem_public_key, initiator_kem_public_key) = match kem {
|
||||||
|
KEM::MlKem768 => (
|
||||||
|
EncapsulationKey::MlKem768(
|
||||||
|
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
||||||
|
),
|
||||||
|
EncapsulationKey::MlKem768(
|
||||||
|
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
KEM::XWing => (
|
||||||
|
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||||
|
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
|
||||||
|
),
|
||||||
|
KEM::X25519 => (
|
||||||
|
EncapsulationKey::X25519(
|
||||||
|
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
||||||
|
),
|
||||||
|
EncapsulationKey::X25519(
|
||||||
|
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
KEM::McEliece => (
|
||||||
|
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
|
||||||
|
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let i_kem_key_bytes = initiator_kem_public_key.encode();
|
||||||
|
|
||||||
|
let r_kem_key_bytes = responder_kem_public_key.encode();
|
||||||
|
|
||||||
|
let i_dir_hash = hash_encapsulation_key(
|
||||||
|
&ciphersuite.hash_function(),
|
||||||
|
ciphersuite.hash_len(),
|
||||||
|
&i_kem_key_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
let r_dir_hash = hash_encapsulation_key(
|
||||||
|
&ciphersuite.hash_function(),
|
||||||
|
ciphersuite.hash_len(),
|
||||||
|
&r_kem_key_bytes,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Anonymous Initiator, OneWay
|
||||||
|
{
|
||||||
|
let (mut i_context, i_frame) =
|
||||||
|
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
|
||||||
|
|
||||||
|
// encryption - initiator frame
|
||||||
|
|
||||||
|
let (i_session_secret, i_bytes) = encrypt_initial_kkt_frame(
|
||||||
|
&mut rng,
|
||||||
|
responder_x25519_keypair.public_key(),
|
||||||
|
&i_frame,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// decryption - initiator frame
|
||||||
|
|
||||||
|
let (r_session_secret, i_frame_r, i_context_r) =
|
||||||
|
decrypt_initial_kkt_frame(responder_x25519_keypair.private_key(), &i_bytes)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (mut r_context, _) =
|
||||||
|
responder_ingest_message(&i_context_r, None, None, &i_frame_r).unwrap();
|
||||||
|
|
||||||
|
let r_frame = responder_process(
|
||||||
|
&mut r_context,
|
||||||
|
i_frame_r.session_id(),
|
||||||
|
responder_ed25519_keypair.private_key(),
|
||||||
|
&responder_kem_public_key,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// encryption - responder frame
|
||||||
|
let r_bytes =
|
||||||
|
encrypt_kkt_frame(&mut rng, &r_session_secret, &r_frame, KKT_RESPONSE_AAD)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// decryption - responder frame
|
||||||
|
|
||||||
|
let (i_frame_r, i_context_r) =
|
||||||
|
decrypt_kkt_frame(&i_session_secret, &r_bytes, KKT_RESPONSE_AAD).unwrap();
|
||||||
|
|
||||||
|
let i_obtained_key = initiator_ingest_response(
|
||||||
|
&mut i_context,
|
||||||
|
&i_frame_r,
|
||||||
|
&i_context_r,
|
||||||
|
responder_ed25519_keypair.public_key(),
|
||||||
|
&r_dir_hash,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
|
||||||
|
}
|
||||||
|
// Initiator, OneWay
|
||||||
|
{
|
||||||
|
let (mut i_context, i_frame) = initiator_process(
|
||||||
|
&mut rng,
|
||||||
|
crate::context::KKTMode::OneWay,
|
||||||
|
ciphersuite,
|
||||||
|
initiator_ed25519_keypair.private_key(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// encryption - initiator frame
|
||||||
|
|
||||||
|
let (i_session_secret, i_bytes) = encrypt_initial_kkt_frame(
|
||||||
|
&mut rng,
|
||||||
|
responder_x25519_keypair.public_key(),
|
||||||
|
&i_frame,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// decryption - initiator frame
|
||||||
|
|
||||||
|
let (r_session_secret, i_frame_r, r_context) =
|
||||||
|
decrypt_initial_kkt_frame(responder_x25519_keypair.private_key(), &i_bytes)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (mut r_context, r_obtained_key) = responder_ingest_message(
|
||||||
|
&r_context,
|
||||||
|
Some(initiator_ed25519_keypair.public_key()),
|
||||||
|
None,
|
||||||
|
&i_frame_r,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(r_obtained_key.is_none());
|
||||||
|
|
||||||
|
let r_frame = responder_process(
|
||||||
|
&mut r_context,
|
||||||
|
i_frame_r.session_id(),
|
||||||
|
responder_ed25519_keypair.private_key(),
|
||||||
|
&responder_kem_public_key,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// encryption - responder frame
|
||||||
|
let r_bytes =
|
||||||
|
encrypt_kkt_frame(&mut rng, &r_session_secret, &r_frame, KKT_RESPONSE_AAD)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// decryption - responder frame
|
||||||
|
|
||||||
|
let (i_frame_r, i_context_r) =
|
||||||
|
decrypt_kkt_frame(&i_session_secret, &r_bytes, KKT_RESPONSE_AAD).unwrap();
|
||||||
|
|
||||||
|
let i_obtained_key = initiator_ingest_response(
|
||||||
|
&mut i_context,
|
||||||
|
&i_frame_r,
|
||||||
|
&i_context_r,
|
||||||
|
responder_ed25519_keypair.public_key(),
|
||||||
|
&r_dir_hash,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiator, Mutual
|
||||||
|
{
|
||||||
|
let (mut i_context, i_frame) = initiator_process(
|
||||||
|
&mut rng,
|
||||||
|
crate::context::KKTMode::Mutual,
|
||||||
|
ciphersuite,
|
||||||
|
initiator_ed25519_keypair.private_key(),
|
||||||
|
Some(&initiator_kem_public_key),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// encryption - initiator frame
|
||||||
|
|
||||||
|
let (i_session_secret, i_bytes) = encrypt_initial_kkt_frame(
|
||||||
|
&mut rng,
|
||||||
|
responder_x25519_keypair.public_key(),
|
||||||
|
&i_frame,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// decryption - initiator frame
|
||||||
|
|
||||||
|
let (r_session_secret, i_frame_r, i_context_r) =
|
||||||
|
decrypt_initial_kkt_frame(responder_x25519_keypair.private_key(), &i_bytes)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (mut r_context, r_obtained_key) = responder_ingest_message(
|
||||||
|
&i_context_r,
|
||||||
|
Some(initiator_ed25519_keypair.public_key()),
|
||||||
|
Some(&i_dir_hash),
|
||||||
|
&i_frame_r,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(r_obtained_key.unwrap().encode(), i_kem_key_bytes);
|
||||||
|
|
||||||
|
let r_frame = responder_process(
|
||||||
|
&mut r_context,
|
||||||
|
i_frame_r.session_id(),
|
||||||
|
responder_ed25519_keypair.private_key(),
|
||||||
|
&responder_kem_public_key,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// encryption - responder frame
|
||||||
|
let r_bytes =
|
||||||
|
encrypt_kkt_frame(&mut rng, &r_session_secret, &r_frame, KKT_RESPONSE_AAD)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// decryption - responder frame
|
||||||
|
|
||||||
|
let (i_frame_r, i_context_r) =
|
||||||
|
decrypt_kkt_frame(&i_session_secret, &r_bytes, KKT_RESPONSE_AAD).unwrap();
|
||||||
|
|
||||||
|
let i_obtained_key = initiator_ingest_response(
|
||||||
|
&mut i_context,
|
||||||
|
&i_frame_r,
|
||||||
|
&i_context_r,
|
||||||
|
responder_ed25519_keypair.public_key(),
|
||||||
|
&r_dir_hash,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use nym_crypto::asymmetric::ed25519::{self, Signature};
|
use nym_crypto::asymmetric::ed25519::{self, Signature};
|
||||||
use rand::{CryptoRng, RngCore};
|
use rand::{CryptoRng, RngCore};
|
||||||
|
|
||||||
|
use crate::frame::KKTSessionId;
|
||||||
use crate::{
|
use crate::{
|
||||||
ciphersuite::{Ciphersuite, EncapsulationKey},
|
ciphersuite::{Ciphersuite, EncapsulationKey},
|
||||||
context::{KKTContext, KKTMode, KKTRole, KKTStatus},
|
context::{KKTContext, KKTMode, KKTRole, KKTStatus},
|
||||||
@@ -51,7 +52,7 @@ where
|
|||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
context,
|
context,
|
||||||
KKTFrame::new(&context_bytes, body, &session_id, &signature),
|
KKTFrame::new(context_bytes, body, session_id, &signature),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,43 +69,38 @@ where
|
|||||||
let mut session_id = [0u8; KKT_SESSION_ID_LEN];
|
let mut session_id = [0u8; KKT_SESSION_ID_LEN];
|
||||||
rng.fill_bytes(&mut session_id);
|
rng.fill_bytes(&mut session_id);
|
||||||
|
|
||||||
Ok((
|
Ok((context, KKTFrame::new(context_bytes, &[], session_id, &[])))
|
||||||
context,
|
|
||||||
KKTFrame::new(&context_bytes, &[], &session_id, &[]),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initiator_ingest_response<'a>(
|
pub fn initiator_ingest_response<'a>(
|
||||||
own_context: &mut KKTContext,
|
own_context: &mut KKTContext,
|
||||||
|
remote_frame: &KKTFrame,
|
||||||
|
remote_context: &KKTContext,
|
||||||
remote_verification_key: &ed25519::PublicKey,
|
remote_verification_key: &ed25519::PublicKey,
|
||||||
expected_hash: &[u8],
|
expected_hash: &[u8],
|
||||||
message_bytes: &[u8],
|
|
||||||
) -> Result<EncapsulationKey<'a>, KKTError> {
|
) -> Result<EncapsulationKey<'a>, KKTError> {
|
||||||
// sizes have to be correct
|
check_compatibility(own_context, remote_context)?;
|
||||||
let (frame, remote_context) = KKTFrame::from_bytes(message_bytes)?;
|
|
||||||
|
|
||||||
check_compatibility(own_context, &remote_context)?;
|
|
||||||
match remote_context.status() {
|
match remote_context.status() {
|
||||||
KKTStatus::Ok => {
|
KKTStatus::Ok => {
|
||||||
let mut bytes_to_verify: Vec<u8> = Vec::with_capacity(
|
let mut bytes_to_verify: Vec<u8> = Vec::with_capacity(
|
||||||
remote_context.full_message_len() - remote_context.signature_len(),
|
remote_context.full_message_len() - remote_context.signature_len(),
|
||||||
);
|
);
|
||||||
bytes_to_verify.extend_from_slice(&remote_context.encode()?);
|
bytes_to_verify.extend_from_slice(&remote_context.encode()?);
|
||||||
bytes_to_verify.extend_from_slice(frame.body_ref());
|
bytes_to_verify.extend_from_slice(remote_frame.body_ref());
|
||||||
bytes_to_verify.extend_from_slice(frame.session_id_ref());
|
bytes_to_verify.extend_from_slice(remote_frame.session_id_ref());
|
||||||
|
|
||||||
match Signature::from_bytes(frame.signature_ref()) {
|
match Signature::from_bytes(remote_frame.signature_ref()) {
|
||||||
Ok(sig) => match remote_verification_key.verify(bytes_to_verify, &sig) {
|
Ok(sig) => match remote_verification_key.verify(bytes_to_verify, &sig) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let received_encapsulation_key = EncapsulationKey::decode(
|
let received_encapsulation_key = EncapsulationKey::decode(
|
||||||
own_context.ciphersuite().kem(),
|
own_context.ciphersuite().kem(),
|
||||||
frame.body_ref(),
|
remote_frame.body_ref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
match validate_encapsulation_key(
|
match validate_encapsulation_key(
|
||||||
&own_context.ciphersuite().hash_function(),
|
&own_context.ciphersuite().hash_function(),
|
||||||
own_context.ciphersuite().hash_len(),
|
own_context.ciphersuite().hash_len(),
|
||||||
frame.body_ref(),
|
remote_frame.body_ref(),
|
||||||
expected_hash,
|
expected_hash,
|
||||||
) {
|
) {
|
||||||
true => Ok(received_encapsulation_key),
|
true => Ok(received_encapsulation_key),
|
||||||
@@ -206,7 +202,7 @@ pub fn responder_ingest_message<'a>(
|
|||||||
|
|
||||||
pub fn responder_process<'a>(
|
pub fn responder_process<'a>(
|
||||||
own_context: &mut KKTContext,
|
own_context: &mut KKTContext,
|
||||||
session_id: &[u8],
|
session_id: KKTSessionId,
|
||||||
signing_key: &ed25519::PrivateKey,
|
signing_key: &ed25519::PrivateKey,
|
||||||
encapsulation_key: &EncapsulationKey<'a>,
|
encapsulation_key: &EncapsulationKey<'a>,
|
||||||
) -> Result<KKTFrame, KKTError> {
|
) -> Result<KKTFrame, KKTError> {
|
||||||
@@ -218,11 +214,11 @@ pub fn responder_process<'a>(
|
|||||||
Vec::with_capacity(own_context.full_message_len() - own_context.signature_len());
|
Vec::with_capacity(own_context.full_message_len() - own_context.signature_len());
|
||||||
bytes_to_sign.extend_from_slice(&own_context.encode()?);
|
bytes_to_sign.extend_from_slice(&own_context.encode()?);
|
||||||
bytes_to_sign.extend_from_slice(&body);
|
bytes_to_sign.extend_from_slice(&body);
|
||||||
bytes_to_sign.extend_from_slice(session_id);
|
bytes_to_sign.extend_from_slice(&session_id);
|
||||||
|
|
||||||
let signature = signing_key.sign(bytes_to_sign).to_bytes();
|
let signature = signing_key.sign(bytes_to_sign).to_bytes();
|
||||||
|
|
||||||
Ok(KKTFrame::new(&context_bytes, &body, session_id, &signature))
|
Ok(KKTFrame::new(context_bytes, &body, session_id, &signature))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_compatibility(
|
fn check_compatibility(
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ edition = { workspace = true }
|
|||||||
license = { workspace = true }
|
license = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = { workspace = true }
|
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
snow = { workspace = true }
|
snow = { workspace = true }
|
||||||
@@ -14,9 +13,7 @@ serde = { workspace = true }
|
|||||||
bytes = { workspace = true }
|
bytes = { workspace = true }
|
||||||
dashmap = { workspace = true }
|
dashmap = { workspace = true }
|
||||||
sha2 = { workspace = true }
|
sha2 = { workspace = true }
|
||||||
ansi_term = { workspace = true }
|
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
utoipa = { workspace = true, features = ["macros", "non_strict_integers"] }
|
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
# rand 0.9 for KKT integration (nym-kkt uses rand 0.9)
|
# rand 0.9 for KKT integration (nym-kkt uses rand 0.9)
|
||||||
rand09 = { package = "rand", version = "0.9.2" }
|
rand09 = { package = "rand", version = "0.9.2" }
|
||||||
@@ -24,7 +21,6 @@ rand09 = { package = "rand", version = "0.9.2" }
|
|||||||
nym-crypto = { path = "../crypto", features = ["hashing", "asymmetric"] }
|
nym-crypto = { path = "../crypto", features = ["hashing", "asymmetric"] }
|
||||||
nym-kkt = { path = "../nym-kkt" }
|
nym-kkt = { path = "../nym-kkt" }
|
||||||
nym-lp-common = { path = "../nym-lp-common" }
|
nym-lp-common = { path = "../nym-lp-common" }
|
||||||
nym-sphinx = { path = "../nymsphinx" }
|
|
||||||
|
|
||||||
# libcrux dependencies for PSQ (Post-Quantum PSK derivation)
|
# libcrux dependencies for PSQ (Post-Quantum PSK derivation)
|
||||||
libcrux-psq = { git = "https://github.com/cryspen/libcrux", features = [
|
libcrux-psq = { git = "https://github.com/cryspen/libcrux", features = [
|
||||||
@@ -40,6 +36,7 @@ zeroize = { workspace = true, features = ["zeroize_derive"] }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.5", features = ["html_reports"] }
|
criterion = { version = "0.5", features = ["html_reports"] }
|
||||||
rand_chacha = "0.3"
|
rand_chacha = "0.3"
|
||||||
|
nym-crypto = { path = "../crypto", features = ["rand"] }
|
||||||
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
|
|||||||
+35
-150
@@ -2,13 +2,8 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::LpError;
|
use crate::LpError;
|
||||||
use crate::message::{
|
use crate::message::{LpMessage, MessageType};
|
||||||
ClientHelloData, EncryptedDataPayload, ForwardPacketData, HandshakeData, KKTRequestData,
|
|
||||||
KKTResponseData, LpMessage, MessageType, SubsessionKK1Data, SubsessionKK2Data,
|
|
||||||
SubsessionReadyData,
|
|
||||||
};
|
|
||||||
use crate::packet::{LpHeader, LpPacket, OuterHeader, TRAILER_LEN};
|
use crate::packet::{LpHeader, LpPacket, OuterHeader, TRAILER_LEN};
|
||||||
use crate::serialisation::{BincodeOptions, lp_bincode_serializer};
|
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use chacha20poly1305::{
|
use chacha20poly1305::{
|
||||||
ChaCha20Poly1305, Key, Nonce, Tag,
|
ChaCha20Poly1305, Key, Nonce, Tag,
|
||||||
@@ -96,87 +91,7 @@ fn parse_message_from_type_and_content(
|
|||||||
let message_type = MessageType::from_u32(msg_type_raw)
|
let message_type = MessageType::from_u32(msg_type_raw)
|
||||||
.ok_or_else(|| LpError::invalid_message_type(msg_type_raw))?;
|
.ok_or_else(|| LpError::invalid_message_type(msg_type_raw))?;
|
||||||
|
|
||||||
match message_type {
|
LpMessage::decode_content(content, message_type)
|
||||||
MessageType::Busy => {
|
|
||||||
if !content.is_empty() {
|
|
||||||
return Err(LpError::InvalidPayloadSize {
|
|
||||||
expected: 0,
|
|
||||||
actual: content.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(LpMessage::Busy)
|
|
||||||
}
|
|
||||||
MessageType::Handshake => Ok(LpMessage::Handshake(HandshakeData(content.to_vec()))),
|
|
||||||
MessageType::EncryptedData => Ok(LpMessage::EncryptedData(EncryptedDataPayload(
|
|
||||||
content.to_vec(),
|
|
||||||
))),
|
|
||||||
MessageType::ClientHello => {
|
|
||||||
let data = ClientHelloData::decode(content)?;
|
|
||||||
Ok(LpMessage::ClientHello(data))
|
|
||||||
}
|
|
||||||
MessageType::KKTRequest => Ok(LpMessage::KKTRequest(KKTRequestData(content.to_vec()))),
|
|
||||||
MessageType::KKTResponse => Ok(LpMessage::KKTResponse(KKTResponseData(content.to_vec()))),
|
|
||||||
MessageType::ForwardPacket => {
|
|
||||||
let data: ForwardPacketData = lp_bincode_serializer()
|
|
||||||
.deserialize(content)
|
|
||||||
.map_err(|e| LpError::DeserializationError(e.to_string()))?;
|
|
||||||
Ok(LpMessage::ForwardPacket(data))
|
|
||||||
}
|
|
||||||
MessageType::Collision => {
|
|
||||||
if !content.is_empty() {
|
|
||||||
return Err(LpError::InvalidPayloadSize {
|
|
||||||
expected: 0,
|
|
||||||
actual: content.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(LpMessage::Collision)
|
|
||||||
}
|
|
||||||
MessageType::Ack => {
|
|
||||||
if !content.is_empty() {
|
|
||||||
return Err(LpError::InvalidPayloadSize {
|
|
||||||
expected: 0,
|
|
||||||
actual: content.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(LpMessage::Ack)
|
|
||||||
}
|
|
||||||
MessageType::SubsessionRequest => {
|
|
||||||
if !content.is_empty() {
|
|
||||||
return Err(LpError::InvalidPayloadSize {
|
|
||||||
expected: 0,
|
|
||||||
actual: content.len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(LpMessage::SubsessionRequest)
|
|
||||||
}
|
|
||||||
MessageType::SubsessionKK1 => {
|
|
||||||
let data: SubsessionKK1Data = lp_bincode_serializer()
|
|
||||||
.deserialize(content)
|
|
||||||
.map_err(|e| LpError::DeserializationError(e.to_string()))?;
|
|
||||||
Ok(LpMessage::SubsessionKK1(data))
|
|
||||||
}
|
|
||||||
MessageType::SubsessionKK2 => {
|
|
||||||
let data: SubsessionKK2Data = lp_bincode_serializer()
|
|
||||||
.deserialize(content)
|
|
||||||
.map_err(|e| LpError::DeserializationError(e.to_string()))?;
|
|
||||||
Ok(LpMessage::SubsessionKK2(data))
|
|
||||||
}
|
|
||||||
MessageType::SubsessionReady => {
|
|
||||||
let data: SubsessionReadyData = lp_bincode_serializer()
|
|
||||||
.deserialize(content)
|
|
||||||
.map_err(|e| LpError::DeserializationError(e.to_string()))?;
|
|
||||||
Ok(LpMessage::SubsessionReady(data))
|
|
||||||
}
|
|
||||||
MessageType::SubsessionAbort => {
|
|
||||||
// Empty signal message - no content to deserialize
|
|
||||||
if !content.is_empty() {
|
|
||||||
return Err(LpError::DeserializationError(
|
|
||||||
"SubsessionAbort should have no payload".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(LpMessage::SubsessionAbort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse only the outer header from raw packet bytes.
|
/// Parse only the outer header from raw packet bytes.
|
||||||
@@ -249,7 +164,7 @@ pub fn parse_lp_packet(src: &[u8], outer_key: Option<&OuterAeadKey>) -> Result<L
|
|||||||
|
|
||||||
let header = LpHeader {
|
let header = LpHeader {
|
||||||
protocol_version,
|
protocol_version,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: outer_header.receiver_idx,
|
receiver_idx: outer_header.receiver_idx,
|
||||||
counter: outer_header.counter,
|
counter: outer_header.counter,
|
||||||
};
|
};
|
||||||
@@ -293,7 +208,7 @@ pub fn parse_lp_packet(src: &[u8], outer_key: Option<&OuterAeadKey>) -> Result<L
|
|||||||
|
|
||||||
let header = LpHeader {
|
let header = LpHeader {
|
||||||
protocol_version,
|
protocol_version,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: outer_header.receiver_idx,
|
receiver_idx: outer_header.receiver_idx,
|
||||||
counter: outer_header.counter,
|
counter: outer_header.counter,
|
||||||
};
|
};
|
||||||
@@ -406,6 +321,8 @@ mod tests {
|
|||||||
use crate::message::{EncryptedDataPayload, HandshakeData, LpMessage, MessageType};
|
use crate::message::{EncryptedDataPayload, HandshakeData, LpMessage, MessageType};
|
||||||
use crate::packet::{LpHeader, LpPacket, TRAILER_LEN};
|
use crate::packet::{LpHeader, LpPacket, TRAILER_LEN};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
|
use rand::thread_rng;
|
||||||
|
|
||||||
// With unified format, outer header (receiver_idx + counter) is always first
|
// With unified format, outer header (receiver_idx + counter) is always first
|
||||||
// and is the only cleartext portion for encrypted packets
|
// and is the only cleartext portion for encrypted packets
|
||||||
@@ -421,7 +338,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 123,
|
counter: 123,
|
||||||
},
|
},
|
||||||
@@ -452,7 +369,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 123,
|
counter: 123,
|
||||||
},
|
},
|
||||||
@@ -490,7 +407,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 123,
|
counter: 123,
|
||||||
},
|
},
|
||||||
@@ -688,11 +605,13 @@ mod tests {
|
|||||||
fn test_serialize_parse_client_hello() {
|
fn test_serialize_parse_client_hello() {
|
||||||
use crate::message::ClientHelloData;
|
use crate::message::ClientHelloData;
|
||||||
|
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
let valid_ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||||
let mut dst = BytesMut::new();
|
let mut dst = BytesMut::new();
|
||||||
|
|
||||||
// Create ClientHelloData
|
// Create ClientHelloData
|
||||||
let client_key = [42u8; 32];
|
let client_key = x25519::PublicKey::from_byte_array(&[42u8; 32]);
|
||||||
let client_ed25519_key = [43u8; 32];
|
let client_ed25519_key = *valid_ed25519.public_key();
|
||||||
let salt = [99u8; 32];
|
let salt = [99u8; 32];
|
||||||
let hello_data = ClientHelloData {
|
let hello_data = ClientHelloData {
|
||||||
receiver_index: 12345,
|
receiver_index: 12345,
|
||||||
@@ -705,7 +624,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 123,
|
counter: 123,
|
||||||
},
|
},
|
||||||
@@ -746,8 +665,11 @@ mod tests {
|
|||||||
.as_secs();
|
.as_secs();
|
||||||
|
|
||||||
// Create ClientHelloData with fresh salt
|
// Create ClientHelloData with fresh salt
|
||||||
let client_key = [7u8; 32];
|
let mut rng = thread_rng();
|
||||||
let client_ed25519_key = [8u8; 32];
|
let valid_ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||||
|
|
||||||
|
let client_key = x25519::PublicKey::from_byte_array(&[7u8; 32]);
|
||||||
|
let client_ed25519_key = *valid_ed25519.public_key();
|
||||||
let hello_data =
|
let hello_data =
|
||||||
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||||
|
|
||||||
@@ -755,7 +677,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 100,
|
receiver_idx: 100,
|
||||||
counter: 200,
|
counter: 200,
|
||||||
},
|
},
|
||||||
@@ -834,43 +756,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_client_hello_different_protocol_versions() {
|
|
||||||
use crate::message::ClientHelloData;
|
|
||||||
|
|
||||||
for version in [0u8, 1, 2, 255] {
|
|
||||||
let mut dst = BytesMut::new();
|
|
||||||
|
|
||||||
let hello_data = ClientHelloData {
|
|
||||||
receiver_index: version as u32,
|
|
||||||
client_lp_public_key: [version; 32],
|
|
||||||
client_ed25519_public_key: [version.wrapping_add(2); 32],
|
|
||||||
salt: [version.wrapping_add(1); 32],
|
|
||||||
};
|
|
||||||
|
|
||||||
let packet = LpPacket {
|
|
||||||
header: LpHeader {
|
|
||||||
protocol_version: 1,
|
|
||||||
reserved: 0,
|
|
||||||
receiver_idx: version as u32,
|
|
||||||
counter: version as u64,
|
|
||||||
},
|
|
||||||
message: LpMessage::ClientHello(hello_data.clone()),
|
|
||||||
trailer: [version; TRAILER_LEN],
|
|
||||||
};
|
|
||||||
|
|
||||||
serialize_lp_packet(&packet, &mut dst, None).unwrap();
|
|
||||||
let decoded = parse_lp_packet(&dst, None).unwrap();
|
|
||||||
|
|
||||||
match decoded.message {
|
|
||||||
LpMessage::ClientHello(decoded_data) => {
|
|
||||||
assert_eq!(decoded_data.client_lp_public_key, [version; 32]);
|
|
||||||
}
|
|
||||||
_ => panic!("Expected ClientHello message for version {}", version),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_forward_packet_encode_decode_roundtrip() {
|
fn test_forward_packet_encode_decode_roundtrip() {
|
||||||
let mut dst = BytesMut::new();
|
let mut dst = BytesMut::new();
|
||||||
@@ -884,7 +769,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 999,
|
receiver_idx: 999,
|
||||||
counter: 555,
|
counter: 555,
|
||||||
},
|
},
|
||||||
@@ -922,7 +807,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 12345,
|
receiver_idx: 12345,
|
||||||
counter: 999,
|
counter: 999,
|
||||||
},
|
},
|
||||||
@@ -951,7 +836,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 12345,
|
receiver_idx: 12345,
|
||||||
counter: 999,
|
counter: 999,
|
||||||
},
|
},
|
||||||
@@ -998,7 +883,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 12345,
|
receiver_idx: 12345,
|
||||||
counter: 999,
|
counter: 999,
|
||||||
},
|
},
|
||||||
@@ -1027,7 +912,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 12345,
|
receiver_idx: 12345,
|
||||||
counter: 999,
|
counter: 999,
|
||||||
},
|
},
|
||||||
@@ -1056,7 +941,7 @@ mod tests {
|
|||||||
let packet1 = LpPacket {
|
let packet1 = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 12345,
|
receiver_idx: 12345,
|
||||||
counter: 1,
|
counter: 1,
|
||||||
},
|
},
|
||||||
@@ -1067,7 +952,7 @@ mod tests {
|
|||||||
let packet2 = LpPacket {
|
let packet2 = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 12345,
|
receiver_idx: 12345,
|
||||||
counter: 2, // Different counter
|
counter: 2, // Different counter
|
||||||
},
|
},
|
||||||
@@ -1102,7 +987,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 12345,
|
receiver_idx: 12345,
|
||||||
counter: 999,
|
counter: 999,
|
||||||
},
|
},
|
||||||
@@ -1128,7 +1013,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 54321,
|
receiver_idx: 54321,
|
||||||
counter: 12345678,
|
counter: 12345678,
|
||||||
},
|
},
|
||||||
@@ -1161,7 +1046,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 99999,
|
receiver_idx: 99999,
|
||||||
counter: 2,
|
counter: 2,
|
||||||
},
|
},
|
||||||
@@ -1191,7 +1076,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 100,
|
counter: 100,
|
||||||
},
|
},
|
||||||
@@ -1220,7 +1105,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 123,
|
receiver_idx: 123,
|
||||||
counter: 456,
|
counter: 456,
|
||||||
},
|
},
|
||||||
@@ -1253,7 +1138,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 789,
|
receiver_idx: 789,
|
||||||
counter: 1000,
|
counter: 1000,
|
||||||
},
|
},
|
||||||
@@ -1286,7 +1171,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 200,
|
counter: 200,
|
||||||
},
|
},
|
||||||
@@ -1341,7 +1226,7 @@ mod tests {
|
|||||||
let packet = LpPacket {
|
let packet = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 54321,
|
receiver_idx: 54321,
|
||||||
counter: 999,
|
counter: 999,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,4 +82,12 @@ pub enum LpError {
|
|||||||
/// Outer AEAD authentication tag verification failed.
|
/// Outer AEAD authentication tag verification failed.
|
||||||
#[error("AEAD authentication tag verification failed")]
|
#[error("AEAD authentication tag verification failed")]
|
||||||
AeadTagMismatch,
|
AeadTagMismatch,
|
||||||
|
|
||||||
|
/// Received an LP packet with an incompatible, future, version
|
||||||
|
#[error("incompatible LP packet version. got: {got}, highest supported: {highest_supported}")]
|
||||||
|
IncompatibleFuturePacketVersion { got: u8, highest_supported: u8 },
|
||||||
|
|
||||||
|
/// Received an LP packet with an incompatible, legacy, version
|
||||||
|
#[error("incompatible LP packet version. got: {got}, lowest supported: {lowest_supported}")]
|
||||||
|
IncompatibleLegacyPacketVersion { got: u8, lowest_supported: u8 },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use nym_sphinx::{PrivateKey as SphinxPrivateKey, PublicKey as SphinxPublicKey};
|
|
||||||
use serde::Serialize;
|
|
||||||
use utoipa::ToSchema;
|
|
||||||
|
|
||||||
use crate::LpError;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PrivateKey(SphinxPrivateKey);
|
|
||||||
|
|
||||||
impl fmt::Debug for PrivateKey {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_tuple("PrivateKey").field(&"[REDACTED]").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for PrivateKey {
|
|
||||||
type Target = SphinxPrivateKey;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PrivateKey {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrivateKey {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let private_key = SphinxPrivateKey::random();
|
|
||||||
Self(private_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_base58_string(&self) -> String {
|
|
||||||
bs58::encode(self.0.to_bytes()).into_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_base58_string(s: &str) -> Result<Self, LpError> {
|
|
||||||
let bytes: [u8; 32] = bs58::decode(s).into_vec()?.try_into().unwrap();
|
|
||||||
Ok(PrivateKey(SphinxPrivateKey::from(bytes)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8; 32]) -> Self {
|
|
||||||
PrivateKey(SphinxPrivateKey::from(*bytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn public_key(&self) -> PublicKey {
|
|
||||||
let public_key = SphinxPublicKey::from(&self.0);
|
|
||||||
PublicKey(public_key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct PublicKey(SphinxPublicKey);
|
|
||||||
|
|
||||||
impl fmt::Debug for PublicKey {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_tuple("PublicKey")
|
|
||||||
.field(&self.to_base58_string())
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for PublicKey {
|
|
||||||
type Target = SphinxPublicKey;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PublicKey {
|
|
||||||
pub fn to_base58_string(&self) -> String {
|
|
||||||
bs58::encode(self.0.as_bytes()).into_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_base58_string(s: &str) -> Result<Self, LpError> {
|
|
||||||
let bytes: [u8; 32] = bs58::decode(s).into_vec()?.try_into().unwrap();
|
|
||||||
Ok(PublicKey(SphinxPublicKey::from(bytes)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, LpError> {
|
|
||||||
Ok(PublicKey(SphinxPublicKey::from(*bytes)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
|
||||||
self.0.as_bytes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PublicKey {
|
|
||||||
fn default() -> Self {
|
|
||||||
let private_key = PrivateKey::default();
|
|
||||||
private_key.public_key()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Keypair {
|
|
||||||
private_key: PrivateKey,
|
|
||||||
public_key: PublicKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Keypair {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Keypair {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let private_key = PrivateKey::default();
|
|
||||||
let public_key = private_key.public_key();
|
|
||||||
Self {
|
|
||||||
private_key,
|
|
||||||
public_key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_private_key(private_key: PrivateKey) -> Self {
|
|
||||||
let public_key = private_key.public_key();
|
|
||||||
Self {
|
|
||||||
private_key,
|
|
||||||
public_key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_keys(private_key: PrivateKey, public_key: PublicKey) -> Self {
|
|
||||||
Self {
|
|
||||||
private_key,
|
|
||||||
public_key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn private_key(&self) -> &PrivateKey {
|
|
||||||
&self.private_key
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn public_key(&self) -> &PublicKey {
|
|
||||||
&self.public_key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<KeypairReadable> for Keypair {
|
|
||||||
fn from(keypair: KeypairReadable) -> Self {
|
|
||||||
Self {
|
|
||||||
private_key: PrivateKey::from_base58_string(&keypair.private).unwrap(),
|
|
||||||
public_key: PublicKey::from_base58_string(&keypair.public).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Keypair> for KeypairReadable {
|
|
||||||
fn from(keypair: &Keypair) -> Self {
|
|
||||||
Self {
|
|
||||||
private: keypair.private_key.to_base58_string(),
|
|
||||||
public: keypair.public_key.to_base58_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FromStr for PrivateKey {
|
|
||||||
type Err = LpError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
PrivateKey::from_base58_string(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for PrivateKey {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.to_base58_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for PublicKey {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.to_base58_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, serde::Deserialize, Clone, ToSchema, Debug)]
|
|
||||||
pub struct KeypairReadable {
|
|
||||||
private: String,
|
|
||||||
public: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeypairReadable {
|
|
||||||
pub fn private_key(&self) -> Result<PrivateKey, LpError> {
|
|
||||||
PrivateKey::from_base58_string(&self.private)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn public_key(&self) -> Result<PublicKey, LpError> {
|
|
||||||
PublicKey::from_base58_string(&self.public)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -37,9 +37,10 @@
|
|||||||
//! ).unwrap();
|
//! ).unwrap();
|
||||||
//!
|
//!
|
||||||
//! // Client: Create request
|
//! // Client: Create request
|
||||||
//! let (client_context, request_data) = create_request(
|
//! let (session_secret, client_context, request_data) = create_request(
|
||||||
//! ciphersuite,
|
//! ciphersuite,
|
||||||
//! &client_signing_key,
|
//! &client_signing_key,
|
||||||
|
//! &responder_dh_public_key
|
||||||
//! ).unwrap();
|
//! ).unwrap();
|
||||||
//!
|
//!
|
||||||
//! // Gateway: Handle request
|
//! // Gateway: Handle request
|
||||||
@@ -47,12 +48,14 @@
|
|||||||
//! &request_data,
|
//! &request_data,
|
||||||
//! Some(&client_verification_key),
|
//! Some(&client_verification_key),
|
||||||
//! &gateway_signing_key,
|
//! &gateway_signing_key,
|
||||||
|
//! &gateway_dh_private_key,
|
||||||
//! &gateway_kem_public_key,
|
//! &gateway_kem_public_key,
|
||||||
//! ).unwrap();
|
//! ).unwrap();
|
||||||
//!
|
//!
|
||||||
//! // Client: Process response
|
//! // Client: Process response
|
||||||
//! let gateway_kem_key = process_response(
|
//! let gateway_kem_key = process_response(
|
||||||
//! client_context,
|
//! client_context,
|
||||||
|
//! &session_secret,
|
||||||
//! &gateway_verification_key,
|
//! &gateway_verification_key,
|
||||||
//! &expected_key_hash,
|
//! &expected_key_hash,
|
||||||
//! &response_data,
|
//! &response_data,
|
||||||
@@ -61,10 +64,10 @@
|
|||||||
|
|
||||||
use crate::LpError;
|
use crate::LpError;
|
||||||
use crate::message::{KKTRequestData, KKTResponseData};
|
use crate::message::{KKTRequestData, KKTResponseData};
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use nym_kkt::ciphersuite::{Ciphersuite, EncapsulationKey};
|
use nym_kkt::ciphersuite::{Ciphersuite, EncapsulationKey};
|
||||||
use nym_kkt::context::KKTContext;
|
use nym_kkt::context::KKTContext;
|
||||||
use nym_kkt::frame::KKTFrame;
|
use nym_kkt::encryption::KKTSessionSecret;
|
||||||
use nym_kkt::kkt::{handle_kem_request, request_kem_key, validate_kem_response};
|
use nym_kkt::kkt::{handle_kem_request, request_kem_key, validate_kem_response};
|
||||||
|
|
||||||
/// Creates a KKT request to obtain the responder's KEM public key.
|
/// Creates a KKT request to obtain the responder's KEM public key.
|
||||||
@@ -75,8 +78,10 @@ use nym_kkt::kkt::{handle_kem_request, request_kem_key, validate_kem_response};
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `ciphersuite` - Negotiated ciphersuite (KEM, hash, signature algorithms)
|
/// * `ciphersuite` - Negotiated ciphersuite (KEM, hash, signature algorithms)
|
||||||
/// * `signing_key` - Client's Ed25519 signing key for authentication
|
/// * `signing_key` - Client's Ed25519 signing key for authentication
|
||||||
|
/// * `responder_dh_public_key` - Gateway's x25519 public key (from directory)
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
|
/// * `KKTSessionSecret` - Session secret key to encrypt/decrypt KKT messages for this session
|
||||||
/// * `KKTContext` - Context to use when validating the response
|
/// * `KKTContext` - Context to use when validating the response
|
||||||
/// * `KKTRequestData` - Serialized KKT request frame to send to gateway
|
/// * `KKTRequestData` - Serialized KKT request frame to send to gateway
|
||||||
///
|
///
|
||||||
@@ -85,14 +90,15 @@ use nym_kkt::kkt::{handle_kem_request, request_kem_key, validate_kem_response};
|
|||||||
pub fn create_request(
|
pub fn create_request(
|
||||||
ciphersuite: Ciphersuite,
|
ciphersuite: Ciphersuite,
|
||||||
signing_key: &ed25519::PrivateKey,
|
signing_key: &ed25519::PrivateKey,
|
||||||
) -> Result<(KKTContext, KKTRequestData), LpError> {
|
responder_dh_public_key: &x25519::PublicKey,
|
||||||
|
) -> Result<(KKTSessionSecret, KKTContext, KKTRequestData), LpError> {
|
||||||
// Note: Uses rand 0.9's thread_rng() to match nym-kkt's rand version
|
// Note: Uses rand 0.9's thread_rng() to match nym-kkt's rand version
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
let (context, frame) = request_kem_key(&mut rng, ciphersuite, signing_key)
|
let (session_secret, context, request_bytes) =
|
||||||
.map_err(|e| LpError::KKTError(e.to_string()))?;
|
request_kem_key(&mut rng, ciphersuite, signing_key, responder_dh_public_key)
|
||||||
|
.map_err(|e| LpError::KKTError(e.to_string()))?;
|
||||||
|
|
||||||
let request_bytes = frame.to_bytes();
|
Ok((session_secret, context, KKTRequestData(request_bytes)))
|
||||||
Ok((context, KKTRequestData(request_bytes)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes a KKT response and extracts the responder's KEM public key.
|
/// Processes a KKT response and extracts the responder's KEM public key.
|
||||||
@@ -102,6 +108,7 @@ pub fn create_request(
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `context` - Context from the initial `create_request()` call
|
/// * `context` - Context from the initial `create_request()` call
|
||||||
|
/// * `session_secret` - The KKT session secret key from the initial `create_request()` call
|
||||||
/// * `responder_vk` - Responder's Ed25519 verification key (from directory)
|
/// * `responder_vk` - Responder's Ed25519 verification key (from directory)
|
||||||
/// * `expected_key_hash` - Expected hash of responder's KEM key (from directory)
|
/// * `expected_key_hash` - Expected hash of responder's KEM key (from directory)
|
||||||
/// * `response_data` - Serialized KKT response frame from responder
|
/// * `response_data` - Serialized KKT response frame from responder
|
||||||
@@ -116,12 +123,14 @@ pub fn create_request(
|
|||||||
/// - Key hash doesn't match expected value
|
/// - Key hash doesn't match expected value
|
||||||
pub fn process_response<'a>(
|
pub fn process_response<'a>(
|
||||||
mut context: KKTContext,
|
mut context: KKTContext,
|
||||||
|
session_secret: &KKTSessionSecret,
|
||||||
responder_vk: &ed25519::PublicKey,
|
responder_vk: &ed25519::PublicKey,
|
||||||
expected_key_hash: &[u8],
|
expected_key_hash: &[u8],
|
||||||
response_data: &KKTResponseData,
|
response_data: &KKTResponseData,
|
||||||
) -> Result<EncapsulationKey<'a>, LpError> {
|
) -> Result<EncapsulationKey<'a>, LpError> {
|
||||||
validate_kem_response(
|
validate_kem_response(
|
||||||
&mut context,
|
&mut context,
|
||||||
|
session_secret,
|
||||||
responder_vk,
|
responder_vk,
|
||||||
expected_key_hash,
|
expected_key_hash,
|
||||||
&response_data.0,
|
&response_data.0,
|
||||||
@@ -139,6 +148,7 @@ pub fn process_response<'a>(
|
|||||||
/// * `request_data` - Serialized KKT request frame from initiator
|
/// * `request_data` - Serialized KKT request frame from initiator
|
||||||
/// * `initiator_vk` - Initiator's Ed25519 verification key (None for anonymous)
|
/// * `initiator_vk` - Initiator's Ed25519 verification key (None for anonymous)
|
||||||
/// * `responder_signing_key` - Gateway's Ed25519 signing key
|
/// * `responder_signing_key` - Gateway's Ed25519 signing key
|
||||||
|
/// * `responder_dh_private_key` - Gateway's x25519 private key
|
||||||
/// * `responder_kem_key` - Gateway's KEM public key to send
|
/// * `responder_kem_key` - Gateway's KEM public key to send
|
||||||
///
|
///
|
||||||
/// # Returns
|
/// # Returns
|
||||||
@@ -153,22 +163,21 @@ pub fn handle_request<'a>(
|
|||||||
request_data: &KKTRequestData,
|
request_data: &KKTRequestData,
|
||||||
initiator_vk: Option<&ed25519::PublicKey>,
|
initiator_vk: Option<&ed25519::PublicKey>,
|
||||||
responder_signing_key: &ed25519::PrivateKey,
|
responder_signing_key: &ed25519::PrivateKey,
|
||||||
|
responder_dh_private_key: &x25519::PrivateKey,
|
||||||
responder_kem_key: &EncapsulationKey<'a>,
|
responder_kem_key: &EncapsulationKey<'a>,
|
||||||
) -> Result<KKTResponseData, LpError> {
|
) -> Result<KKTResponseData, LpError> {
|
||||||
// Deserialize request frame
|
let mut rng = rand09::rng();
|
||||||
let (request_frame, _) = KKTFrame::from_bytes(&request_data.0)
|
|
||||||
.map_err(|e| LpError::KKTError(format!("Failed to parse KKT request: {}", e)))?;
|
|
||||||
|
|
||||||
// Handle the request and generate response
|
// Handle the request and generate response
|
||||||
let response_frame = handle_kem_request(
|
let response_bytes = handle_kem_request(
|
||||||
&request_frame,
|
&mut rng,
|
||||||
|
&request_data.0,
|
||||||
initiator_vk,
|
initiator_vk,
|
||||||
responder_signing_key,
|
responder_signing_key,
|
||||||
|
responder_dh_private_key,
|
||||||
responder_kem_key,
|
responder_kem_key,
|
||||||
)
|
)
|
||||||
.map_err(|e| LpError::KKTError(e.to_string()))?;
|
.map_err(|e| LpError::KKTError(e.to_string()))?;
|
||||||
|
|
||||||
let response_bytes = response_frame.to_bytes();
|
|
||||||
Ok(KKTResponseData(response_bytes))
|
Ok(KKTResponseData(response_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +185,10 @@ pub fn handle_request<'a>(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use nym_kkt::ciphersuite::{HashFunction, KEM, SignatureScheme};
|
use nym_kkt::ciphersuite::{HashFunction, KEM, SignatureScheme};
|
||||||
use nym_kkt::key_utils::{generate_keypair_libcrux, hash_encapsulation_key};
|
use nym_kkt::key_utils::{
|
||||||
|
generate_keypair_ed25519, generate_keypair_libcrux, generate_keypair_x25519,
|
||||||
|
hash_encapsulation_key,
|
||||||
|
};
|
||||||
use rand09::RngCore;
|
use rand09::RngCore;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -184,13 +196,10 @@ mod tests {
|
|||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
// Generate Ed25519 keypairs for both parties
|
// Generate Ed25519 keypairs for both parties
|
||||||
let mut initiator_secret = [0u8; 32];
|
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||||
rng.fill_bytes(&mut initiator_secret);
|
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||||
let initiator_keypair = ed25519::KeyPair::from_secret(initiator_secret, 0);
|
|
||||||
|
|
||||||
let mut responder_secret = [0u8; 32];
|
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||||
rng.fill_bytes(&mut responder_secret);
|
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
|
||||||
|
|
||||||
// Generate responder's KEM keypair (X25519 for testing)
|
// Generate responder's KEM keypair (X25519 for testing)
|
||||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
@@ -213,14 +222,19 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Client: Create request
|
// Client: Create request
|
||||||
let (context, request_data) =
|
let (session_secret, context, request_data) = create_request(
|
||||||
create_request(ciphersuite, initiator_keypair.private_key()).unwrap();
|
ciphersuite,
|
||||||
|
initiator_ed25519_keypair.private_key(),
|
||||||
|
responder_x25519.public_key(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Gateway: Handle request
|
// Gateway: Handle request
|
||||||
let response_data = handle_request(
|
let response_data = handle_request(
|
||||||
&request_data,
|
&request_data,
|
||||||
Some(initiator_keypair.public_key()),
|
Some(initiator_ed25519_keypair.public_key()),
|
||||||
responder_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
|
responder_x25519.private_key(),
|
||||||
&responder_kem_key,
|
&responder_kem_key,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -228,7 +242,8 @@ mod tests {
|
|||||||
// Client: Process response
|
// Client: Process response
|
||||||
let obtained_key = process_response(
|
let obtained_key = process_response(
|
||||||
context,
|
context,
|
||||||
responder_keypair.public_key(),
|
&session_secret,
|
||||||
|
responder_ed25519_keypair.public_key(),
|
||||||
&key_hash,
|
&key_hash,
|
||||||
&response_data,
|
&response_data,
|
||||||
)
|
)
|
||||||
@@ -238,70 +253,71 @@ mod tests {
|
|||||||
assert_eq!(obtained_key.encode(), responder_kem_key.encode());
|
assert_eq!(obtained_key.encode(), responder_kem_key.encode());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_kkt_roundtrip_anonymous() {
|
// fn test_kkt_roundtrip_anonymous() {
|
||||||
let mut rng = rand09::rng();
|
// let mut rng = rand09::rng();
|
||||||
|
|
||||||
// Only responder has keys (anonymous initiator)
|
// // Only responder has keys (anonymous initiator)
|
||||||
let mut responder_secret = [0u8; 32];
|
// // Generate Ed25519 keypairs for both parties
|
||||||
rng.fill_bytes(&mut responder_secret);
|
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
|
||||||
|
|
||||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
// let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
|
||||||
|
|
||||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
// let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||||
KEM::X25519,
|
|
||||||
HashFunction::Blake3,
|
|
||||||
SignatureScheme::Ed25519,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let key_hash = hash_encapsulation_key(
|
// let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
&ciphersuite.hash_function(),
|
// let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||||
ciphersuite.hash_len(),
|
|
||||||
&responder_kem_key.encode(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Anonymous initiator - use anonymous_initiator_process directly
|
// let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||||
use nym_kkt::kkt::anonymous_initiator_process;
|
// KEM::X25519,
|
||||||
let (mut context, request_frame) =
|
// HashFunction::Blake3,
|
||||||
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
|
// SignatureScheme::Ed25519,
|
||||||
let request_data = KKTRequestData(request_frame.to_bytes());
|
// None,
|
||||||
|
// )
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
// Gateway: Handle anonymous request
|
// let key_hash = hash_encapsulation_key(
|
||||||
let response_data = handle_request(
|
// &ciphersuite.hash_function(),
|
||||||
&request_data,
|
// ciphersuite.hash_len(),
|
||||||
None, // Anonymous - no verification key
|
// &responder_kem_key.encode(),
|
||||||
responder_keypair.private_key(),
|
// );
|
||||||
&responder_kem_key,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Initiator: Validate response
|
// // Anonymous initiator - use anonymous_initiator_process directly
|
||||||
let obtained_key = validate_kem_response(
|
// use nym_kkt::kkt::anonymous_initiator_process;
|
||||||
&mut context,
|
// let (mut context, request_frame) =
|
||||||
responder_keypair.public_key(),
|
// anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
|
||||||
&key_hash,
|
// let request_data = KKTRequestData(request_frame.to_bytes());
|
||||||
&response_data.0,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(obtained_key.encode(), responder_kem_key.encode());
|
// // Gateway: Handle anonymous request
|
||||||
}
|
// let response_data = handle_request(
|
||||||
|
// &request_data,
|
||||||
|
// None,
|
||||||
|
// responder_ed25519_keypair.private_key(),
|
||||||
|
// &responder_x25519_sk,
|
||||||
|
// &responder_kem_key,
|
||||||
|
// )
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// // Initiator: Validate response
|
||||||
|
// let obtained_key = initiator_ingest_response(
|
||||||
|
// &mut context,
|
||||||
|
// responder_ed25519_keypair.public_key(),
|
||||||
|
// &key_hash,
|
||||||
|
// &response_data.0,
|
||||||
|
// )
|
||||||
|
// .unwrap();
|
||||||
|
|
||||||
|
// assert_eq!(obtained_key.encode(), responder_kem_key.encode());
|
||||||
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_signature_rejected() {
|
fn test_invalid_signature_rejected() {
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
let mut initiator_secret = [0u8; 32];
|
// Generate Ed25519 keypairs for both parties
|
||||||
rng.fill_bytes(&mut initiator_secret);
|
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||||
let initiator_keypair = ed25519::KeyPair::from_secret(initiator_secret, 0);
|
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||||
|
|
||||||
let mut responder_secret = [0u8; 32];
|
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||||
rng.fill_bytes(&mut responder_secret);
|
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
|
||||||
|
|
||||||
// Different keypair for wrong signature
|
// Different keypair for wrong signature
|
||||||
let mut wrong_secret = [0u8; 32];
|
let mut wrong_secret = [0u8; 32];
|
||||||
@@ -319,14 +335,19 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (_context, request_data) =
|
let (_session_secret, _context, request_data) = create_request(
|
||||||
create_request(ciphersuite, initiator_keypair.private_key()).unwrap();
|
ciphersuite,
|
||||||
|
initiator_ed25519_keypair.private_key(),
|
||||||
|
responder_x25519.public_key(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Gateway handles request but we provide WRONG verification key
|
// Gateway handles request but we provide WRONG verification key
|
||||||
let result = handle_request(
|
let result = handle_request(
|
||||||
&request_data,
|
&request_data,
|
||||||
Some(wrong_keypair.public_key()), // Wrong key!
|
Some(wrong_keypair.public_key()), // Wrong key!
|
||||||
responder_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
|
responder_x25519.private_key(),
|
||||||
&responder_kem_key,
|
&responder_kem_key,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -343,13 +364,11 @@ mod tests {
|
|||||||
fn test_hash_mismatch_rejected() {
|
fn test_hash_mismatch_rejected() {
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
let mut initiator_secret = [0u8; 32];
|
// Generate Ed25519 keypairs for both parties
|
||||||
rng.fill_bytes(&mut initiator_secret);
|
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||||
let initiator_keypair = ed25519::KeyPair::from_secret(initiator_secret, 0);
|
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||||
|
|
||||||
let mut responder_secret = [0u8; 32];
|
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||||
rng.fill_bytes(&mut responder_secret);
|
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
|
||||||
|
|
||||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||||
@@ -365,13 +384,18 @@ mod tests {
|
|||||||
// Use WRONG hash
|
// Use WRONG hash
|
||||||
let wrong_hash = [0u8; 32];
|
let wrong_hash = [0u8; 32];
|
||||||
|
|
||||||
let (context, request_data) =
|
let (session_secret, context, request_data) = create_request(
|
||||||
create_request(ciphersuite, initiator_keypair.private_key()).unwrap();
|
ciphersuite,
|
||||||
|
initiator_ed25519_keypair.private_key(),
|
||||||
|
responder_x25519.public_key(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let response_data = handle_request(
|
let response_data = handle_request(
|
||||||
&request_data,
|
&request_data,
|
||||||
Some(initiator_keypair.public_key()),
|
Some(initiator_ed25519_keypair.public_key()),
|
||||||
responder_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
|
responder_x25519.private_key(),
|
||||||
&responder_kem_key,
|
&responder_kem_key,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -379,7 +403,8 @@ mod tests {
|
|||||||
// Client validates with WRONG hash
|
// Client validates with WRONG hash
|
||||||
let result = process_response(
|
let result = process_response(
|
||||||
context,
|
context,
|
||||||
responder_keypair.public_key(),
|
&session_secret,
|
||||||
|
responder_ed25519_keypair.public_key(),
|
||||||
&wrong_hash, // Wrong!
|
&wrong_hash, // Wrong!
|
||||||
&response_data,
|
&response_data,
|
||||||
);
|
);
|
||||||
@@ -399,7 +424,9 @@ mod tests {
|
|||||||
|
|
||||||
let mut responder_secret = [0u8; 32];
|
let mut responder_secret = [0u8; 32];
|
||||||
rng.fill_bytes(&mut responder_secret);
|
rng.fill_bytes(&mut responder_secret);
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||||
|
|
||||||
|
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||||
|
|
||||||
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, responder_kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
let responder_kem_key = EncapsulationKey::X25519(responder_kem_pk);
|
||||||
@@ -410,7 +437,8 @@ mod tests {
|
|||||||
let result = handle_request(
|
let result = handle_request(
|
||||||
&malformed_request,
|
&malformed_request,
|
||||||
None,
|
None,
|
||||||
responder_keypair.private_key(),
|
responder_ed25519_keypair.private_key(),
|
||||||
|
responder_x25519.private_key(),
|
||||||
&responder_kem_key,
|
&responder_kem_key,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -427,13 +455,11 @@ mod tests {
|
|||||||
fn test_malformed_response_rejected() {
|
fn test_malformed_response_rejected() {
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
let mut initiator_secret = [0u8; 32];
|
// Generate Ed25519 keypairs for both parties
|
||||||
rng.fill_bytes(&mut initiator_secret);
|
let initiator_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(0));
|
||||||
let initiator_keypair = ed25519::KeyPair::from_secret(initiator_secret, 0);
|
let responder_ed25519_keypair = generate_keypair_ed25519(&mut rng, Some(1));
|
||||||
|
|
||||||
let mut responder_secret = [0u8; 32];
|
let responder_x25519 = generate_keypair_x25519(&mut rng);
|
||||||
rng.fill_bytes(&mut responder_secret);
|
|
||||||
let responder_keypair = ed25519::KeyPair::from_secret(responder_secret, 1);
|
|
||||||
|
|
||||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||||
KEM::X25519,
|
KEM::X25519,
|
||||||
@@ -443,8 +469,12 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (context, _request_data) =
|
let (session_secret, context, _request_data) = create_request(
|
||||||
create_request(ciphersuite, initiator_keypair.private_key()).unwrap();
|
ciphersuite,
|
||||||
|
initiator_ed25519_keypair.private_key(),
|
||||||
|
responder_x25519.public_key(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Create malformed response data
|
// Create malformed response data
|
||||||
let malformed_response = KKTResponseData(vec![0xFF; 100]);
|
let malformed_response = KKTResponseData(vec![0xFF; 100]);
|
||||||
@@ -452,7 +482,8 @@ mod tests {
|
|||||||
|
|
||||||
let result = process_response(
|
let result = process_response(
|
||||||
context,
|
context,
|
||||||
responder_keypair.public_key(),
|
&session_secret,
|
||||||
|
responder_ed25519_keypair.public_key(),
|
||||||
&key_hash,
|
&key_hash,
|
||||||
&malformed_response,
|
&malformed_response,
|
||||||
);
|
);
|
||||||
|
|||||||
+30
-36
@@ -4,7 +4,6 @@
|
|||||||
pub mod codec;
|
pub mod codec;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod keypair;
|
|
||||||
pub mod kkt_orchestrator;
|
pub mod kkt_orchestrator;
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod noise_protocol;
|
pub mod noise_protocol;
|
||||||
@@ -14,6 +13,7 @@ pub mod replay;
|
|||||||
pub mod session;
|
pub mod session;
|
||||||
mod session_integration;
|
mod session_integration;
|
||||||
pub mod session_manager;
|
pub mod session_manager;
|
||||||
|
pub mod state_machine;
|
||||||
|
|
||||||
pub use config::LpConfig;
|
pub use config::LpConfig;
|
||||||
pub use error::LpError;
|
pub use error::LpError;
|
||||||
@@ -22,11 +22,6 @@ pub use packet::{BOOTSTRAP_RECEIVER_IDX, LpPacket, OuterHeader};
|
|||||||
pub use replay::{ReceivingKeyCounterValidator, ReplayError};
|
pub use replay::{ReceivingKeyCounterValidator, ReplayError};
|
||||||
pub use session::{LpSession, generate_fresh_salt};
|
pub use session::{LpSession, generate_fresh_salt};
|
||||||
pub use session_manager::SessionManager;
|
pub use session_manager::SessionManager;
|
||||||
|
|
||||||
// Add the new state machine module
|
|
||||||
pub mod serialisation;
|
|
||||||
pub mod state_machine;
|
|
||||||
|
|
||||||
pub use state_machine::LpStateMachine;
|
pub use state_machine::LpStateMachine;
|
||||||
|
|
||||||
pub const NOISE_PATTERN: &str = "Noise_XKpsk3_25519_ChaChaPoly_SHA256";
|
pub const NOISE_PATTERN: &str = "Noise_XKpsk3_25519_ChaChaPoly_SHA256";
|
||||||
@@ -34,12 +29,14 @@ pub const NOISE_PSK_INDEX: u8 = 3;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
||||||
use crate::keypair::Keypair;
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
// X25519 keypairs for Noise protocol
|
// X25519 keypairs for Noise protocol
|
||||||
let keypair_1 = Keypair::default();
|
let keypair_1 = Arc::new(x25519::KeyPair::new(&mut rng));
|
||||||
let keypair_2 = Keypair::default();
|
let keypair_2 = Arc::new(x25519::KeyPair::new(&mut rng));
|
||||||
|
|
||||||
// Use a fixed receiver_index for deterministic tests
|
// Use a fixed receiver_index for deterministic tests
|
||||||
let receiver_index: u32 = 12345;
|
let receiver_index: u32 = 12345;
|
||||||
@@ -48,6 +45,8 @@ pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
|||||||
let ed25519_keypair_1 = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
let ed25519_keypair_1 = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
||||||
let ed25519_keypair_2 = ed25519::KeyPair::from_secret([2u8; 32], 1);
|
let ed25519_keypair_2 = ed25519::KeyPair::from_secret([2u8; 32], 1);
|
||||||
|
|
||||||
|
let ed25519_keypair1_pubkey = *ed25519_keypair_1.public_key();
|
||||||
|
|
||||||
// Use consistent salt for deterministic tests
|
// Use consistent salt for deterministic tests
|
||||||
let salt = [1u8; 32];
|
let salt = [1u8; 32];
|
||||||
|
|
||||||
@@ -56,11 +55,8 @@ pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
|||||||
let initiator_session = LpSession::new(
|
let initiator_session = LpSession::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(ed25519_keypair_1),
|
||||||
ed25519_keypair_1.private_key(),
|
keypair_1.clone(),
|
||||||
ed25519_keypair_1.public_key(),
|
|
||||||
),
|
|
||||||
keypair_1.private_key(),
|
|
||||||
ed25519_keypair_2.public_key(),
|
ed25519_keypair_2.public_key(),
|
||||||
keypair_2.public_key(),
|
keypair_2.public_key(),
|
||||||
&salt,
|
&salt,
|
||||||
@@ -70,12 +66,9 @@ pub fn sessions_for_tests() -> (LpSession, LpSession) {
|
|||||||
let responder_session = LpSession::new(
|
let responder_session = LpSession::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false,
|
false,
|
||||||
(
|
Arc::new(ed25519_keypair_2),
|
||||||
ed25519_keypair_2.private_key(),
|
keypair_2.clone(),
|
||||||
ed25519_keypair_2.public_key(),
|
&ed25519_keypair1_pubkey,
|
||||||
),
|
|
||||||
keypair_2.private_key(),
|
|
||||||
ed25519_keypair_1.public_key(),
|
|
||||||
keypair_1.public_key(),
|
keypair_1.public_key(),
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -91,6 +84,7 @@ mod tests {
|
|||||||
use crate::session_manager::SessionManager;
|
use crate::session_manager::SessionManager;
|
||||||
use crate::{LpError, sessions_for_tests};
|
use crate::{LpError, sessions_for_tests};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
// Import the new standalone functions
|
// Import the new standalone functions
|
||||||
use crate::codec::{parse_lp_packet, serialize_lp_packet};
|
use crate::codec::{parse_lp_packet, serialize_lp_packet};
|
||||||
@@ -104,7 +98,7 @@ mod tests {
|
|||||||
let packet1 = LpPacket {
|
let packet1 = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42, // Matches session's sending_index assumption for this test
|
receiver_idx: 42, // Matches session's sending_index assumption for this test
|
||||||
counter: 0,
|
counter: 0,
|
||||||
},
|
},
|
||||||
@@ -133,7 +127,7 @@ mod tests {
|
|||||||
let packet2 = LpPacket {
|
let packet2 = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 0, // Same counter as before (replay)
|
counter: 0, // Same counter as before (replay)
|
||||||
},
|
},
|
||||||
@@ -163,7 +157,7 @@ mod tests {
|
|||||||
let packet3 = LpPacket {
|
let packet3 = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 1, // Incremented counter
|
counter: 1, // Incremented counter
|
||||||
},
|
},
|
||||||
@@ -206,6 +200,10 @@ mod tests {
|
|||||||
let ed25519_keypair_local = ed25519::KeyPair::from_secret([8u8; 32], 0);
|
let ed25519_keypair_local = ed25519::KeyPair::from_secret([8u8; 32], 0);
|
||||||
let ed25519_keypair_remote = ed25519::KeyPair::from_secret([9u8; 32], 1);
|
let ed25519_keypair_remote = ed25519::KeyPair::from_secret([9u8; 32], 1);
|
||||||
|
|
||||||
|
let ed25519_keypair_local_pubkey = *ed25519_keypair_local.public_key();
|
||||||
|
let x25519_keypair_local_pubkey = ed25519_keypair_local_pubkey.to_x25519().unwrap();
|
||||||
|
let x25519_keypair_remote_pubkey = ed25519_keypair_remote.public_key().to_x25519().unwrap();
|
||||||
|
|
||||||
// Use fixed receiver_index for deterministic test
|
// Use fixed receiver_index for deterministic test
|
||||||
let receiver_index: u32 = 54321;
|
let receiver_index: u32 = 54321;
|
||||||
|
|
||||||
@@ -216,11 +214,9 @@ mod tests {
|
|||||||
let _ = local_manager
|
let _ = local_manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(
|
Arc::new(ed25519_keypair_local),
|
||||||
ed25519_keypair_local.private_key(),
|
|
||||||
ed25519_keypair_local.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_remote.public_key(),
|
ed25519_keypair_remote.public_key(),
|
||||||
|
&x25519_keypair_remote_pubkey,
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -229,11 +225,9 @@ mod tests {
|
|||||||
let _ = remote_manager
|
let _ = remote_manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(
|
Arc::new(ed25519_keypair_remote),
|
||||||
ed25519_keypair_remote.private_key(),
|
&ed25519_keypair_local_pubkey,
|
||||||
ed25519_keypair_remote.public_key(),
|
&x25519_keypair_local_pubkey,
|
||||||
),
|
|
||||||
ed25519_keypair_local.public_key(),
|
|
||||||
false,
|
false,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -242,7 +236,7 @@ mod tests {
|
|||||||
let packet1 = LpPacket {
|
let packet1 = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: receiver_index,
|
receiver_idx: receiver_index,
|
||||||
counter: 0,
|
counter: 0,
|
||||||
},
|
},
|
||||||
@@ -275,7 +269,7 @@ mod tests {
|
|||||||
let packet2 = LpPacket {
|
let packet2 = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: receiver_index,
|
receiver_idx: receiver_index,
|
||||||
counter: 1,
|
counter: 1,
|
||||||
},
|
},
|
||||||
@@ -303,7 +297,7 @@ mod tests {
|
|||||||
let packet3 = LpPacket {
|
let packet3 = LpPacket {
|
||||||
header: LpHeader {
|
header: LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: receiver_index,
|
receiver_idx: receiver_index,
|
||||||
counter: 0, // Replay of first packet
|
counter: 0, // Replay of first packet
|
||||||
},
|
},
|
||||||
|
|||||||
+321
-78
@@ -1,10 +1,10 @@
|
|||||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use crate::serialisation::{BincodeOptions, lp_bincode_serializer};
|
|
||||||
use crate::{BOOTSTRAP_RECEIVER_IDX, LpError};
|
use crate::{BOOTSTRAP_RECEIVER_IDX, LpError};
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
@@ -15,16 +15,21 @@ pub struct ClientHelloData {
|
|||||||
/// Auto-generated randomly by the client
|
/// Auto-generated randomly by the client
|
||||||
pub receiver_index: u32,
|
pub receiver_index: u32,
|
||||||
/// Client's LP x25519 public key (32 bytes) - derived from Ed25519 key
|
/// Client's LP x25519 public key (32 bytes) - derived from Ed25519 key
|
||||||
pub client_lp_public_key: [u8; 32],
|
pub client_lp_public_key: x25519::PublicKey,
|
||||||
/// Client's Ed25519 public key (32 bytes) - for PSQ authentication
|
/// Client's Ed25519 public key (32 bytes) - for PSQ authentication
|
||||||
pub client_ed25519_public_key: [u8; 32],
|
pub client_ed25519_public_key: ed25519::PublicKey,
|
||||||
/// Salt for PSK derivation (32 bytes: 8-byte timestamp + 24-byte nonce)
|
/// Salt for PSK derivation (32 bytes: 8-byte timestamp + 24-byte nonce)
|
||||||
pub salt: [u8; 32],
|
pub salt: [u8; 32],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientHelloData {
|
impl ClientHelloData {
|
||||||
|
// 4 bytes for receiver index + 32 bytes for client lp key, 32 bytes for client ed25519 key + 32 bytes for salt
|
||||||
pub const LEN: usize = 100;
|
pub const LEN: usize = 100;
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
Self::LEN
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_receiver_index() -> u32 {
|
fn generate_receiver_index() -> u32 {
|
||||||
loop {
|
loop {
|
||||||
let candidate = rand::random();
|
let candidate = rand::random();
|
||||||
@@ -42,8 +47,8 @@ impl ClientHelloData {
|
|||||||
/// * `client_lp_public_key` - Client's x25519 public key (derived from Ed25519)
|
/// * `client_lp_public_key` - Client's x25519 public key (derived from Ed25519)
|
||||||
/// * `client_ed25519_public_key` - Client's Ed25519 public key (for PSQ authentication)
|
/// * `client_ed25519_public_key` - Client's Ed25519 public key (for PSQ authentication)
|
||||||
pub fn new_with_fresh_salt(
|
pub fn new_with_fresh_salt(
|
||||||
client_lp_public_key: [u8; 32],
|
client_lp_public_key: x25519::PublicKey,
|
||||||
client_ed25519_public_key: [u8; 32],
|
client_ed25519_public_key: ed25519::PublicKey,
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Generate salt: timestamp + nonce
|
// Generate salt: timestamp + nonce
|
||||||
@@ -74,14 +79,13 @@ impl ClientHelloData {
|
|||||||
u64::from_le_bytes(timestamp_bytes)
|
u64::from_le_bytes(timestamp_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode(&self) -> Vec<u8> {
|
pub fn encode(&self, dst: &mut BytesMut) {
|
||||||
let mut out = Vec::with_capacity(Self::LEN);
|
dst.put_u32_le(self.receiver_index);
|
||||||
out.put_u32_le(self.receiver_index);
|
dst.put_slice(self.client_lp_public_key.as_bytes());
|
||||||
out.put_slice(&self.client_lp_public_key);
|
dst.put_slice(self.client_ed25519_public_key.as_bytes());
|
||||||
out.put_slice(&self.client_ed25519_public_key);
|
dst.put_slice(&self.salt);
|
||||||
out.put_slice(&self.salt);
|
|
||||||
out
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decode(b: &[u8]) -> Result<Self, LpError> {
|
pub fn decode(b: &[u8]) -> Result<Self, LpError> {
|
||||||
if b.len() != Self::LEN {
|
if b.len() != Self::LEN {
|
||||||
return Err(LpError::DeserializationError(format!(
|
return Err(LpError::DeserializationError(format!(
|
||||||
@@ -93,10 +97,15 @@ impl ClientHelloData {
|
|||||||
|
|
||||||
// SAFETY: we checked for valid byte lengths
|
// SAFETY: we checked for valid byte lengths
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let client_lp_public_key_bytes = b[4..36].try_into().unwrap();
|
||||||
|
let client_ed25519_public_key_bytes = b[36..68].try_into().unwrap();
|
||||||
|
|
||||||
Ok(ClientHelloData {
|
Ok(ClientHelloData {
|
||||||
receiver_index: u32::from_le_bytes([b[0], b[1], b[2], b[3]]),
|
receiver_index: u32::from_le_bytes([b[0], b[1], b[2], b[3]]),
|
||||||
client_lp_public_key: b[4..36].try_into().unwrap(),
|
client_lp_public_key: x25519::PublicKey::from_byte_array(client_lp_public_key_bytes),
|
||||||
client_ed25519_public_key: b[36..68].try_into().unwrap(),
|
client_ed25519_public_key: ed25519::PublicKey::from_byte_array(
|
||||||
|
client_ed25519_public_key_bytes,
|
||||||
|
)?,
|
||||||
salt: b[68..].try_into().unwrap(),
|
salt: b[68..].try_into().unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -141,23 +150,80 @@ impl MessageType {
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct HandshakeData(pub Vec<u8>);
|
pub struct HandshakeData(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl HandshakeData {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
dst.put_slice(&self.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(bytes: &[u8]) -> Result<Self, LpError> {
|
||||||
|
Ok(HandshakeData(bytes.to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct EncryptedDataPayload(pub Vec<u8>);
|
pub struct EncryptedDataPayload(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl EncryptedDataPayload {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
dst.put_slice(&self.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(bytes: &[u8]) -> Result<Self, LpError> {
|
||||||
|
Ok(EncryptedDataPayload(bytes.to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// KKT request frame data (serialized KKTFrame bytes)
|
/// KKT request frame data (serialized KKTFrame bytes)
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct KKTRequestData(pub Vec<u8>);
|
pub struct KKTRequestData(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl KKTRequestData {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
dst.put_slice(&self.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(bytes: &[u8]) -> Result<Self, LpError> {
|
||||||
|
Ok(KKTRequestData(bytes.to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// KKT response frame data (serialized KKTFrame bytes)
|
/// KKT response frame data (serialized KKTFrame bytes)
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct KKTResponseData(pub Vec<u8>);
|
pub struct KKTResponseData(pub Vec<u8>);
|
||||||
|
|
||||||
|
impl KKTResponseData {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
dst.put_slice(&self.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(bytes: &[u8]) -> Result<Self, LpError> {
|
||||||
|
Ok(KKTResponseData(bytes.to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Packet forwarding request with embedded inner LP packet
|
/// Packet forwarding request with embedded inner LP packet
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ForwardPacketData {
|
pub struct ForwardPacketData {
|
||||||
/// Target gateway's Ed25519 identity (32 bytes)
|
/// Target gateway's Ed25519 identity (32 bytes)
|
||||||
pub target_gateway_identity: [u8; 32],
|
pub target_gateway_identity: [u8; 32],
|
||||||
|
|
||||||
|
// TODO: replace it with `SocketAddr`
|
||||||
/// Target gateway's LP address (IP:port string)
|
/// Target gateway's LP address (IP:port string)
|
||||||
pub target_lp_address: String,
|
pub target_lp_address: String,
|
||||||
|
|
||||||
@@ -166,27 +232,157 @@ pub struct ForwardPacketData {
|
|||||||
pub inner_packet_bytes: Vec<u8>,
|
pub inner_packet_bytes: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ForwardPacketData {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
// 32 bytes target gateway identity
|
||||||
|
// +
|
||||||
|
// 4 bytes length of target lp address
|
||||||
|
// +
|
||||||
|
// target_lp_address.len()
|
||||||
|
// +
|
||||||
|
// 4 bytes of length of inner packet bytes
|
||||||
|
// +
|
||||||
|
// inner_packet_bytes.len()
|
||||||
|
32 + 4 + self.target_lp_address.len() + 4 + self.inner_packet_bytes.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
dst.put_slice(&self.target_gateway_identity);
|
||||||
|
dst.put_u16_le(self.target_lp_address.len() as u16);
|
||||||
|
dst.put_slice(self.target_lp_address.as_bytes());
|
||||||
|
dst.put_u32_le(self.inner_packet_bytes.len() as u32);
|
||||||
|
dst.put_slice(&self.inner_packet_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
self.encode(&mut buf);
|
||||||
|
buf.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(bytes: &[u8]) -> Result<Self, LpError> {
|
||||||
|
// smallest possible packet with empty address and empty data
|
||||||
|
if bytes.len() < 38 {
|
||||||
|
return Err(LpError::DeserializationError(format!(
|
||||||
|
"Too few bytes to deserialise ForwardPacketData[1]. got {}",
|
||||||
|
bytes.len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
// SAFETY: we ensured we have sufficient data
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
let target_gateway_identity = bytes[0..32].try_into().unwrap();
|
||||||
|
let target_lp_address_len = u16::from_le_bytes([bytes[32], bytes[33]]);
|
||||||
|
|
||||||
|
// smallest possible packet with empty data
|
||||||
|
if bytes[34..].len() < 4 + target_lp_address_len as usize {
|
||||||
|
return Err(LpError::DeserializationError(format!(
|
||||||
|
"Too few bytes to deserialise ForwardPacketData[2]. got {}",
|
||||||
|
bytes.len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_lp_address =
|
||||||
|
String::from_utf8_lossy(&bytes[34..34 + target_lp_address_len as usize]).to_string();
|
||||||
|
let inner_packet_bytes_len = u32::from_le_bytes([
|
||||||
|
bytes[34 + target_lp_address_len as usize],
|
||||||
|
bytes[34 + target_lp_address_len as usize + 1],
|
||||||
|
bytes[34 + target_lp_address_len as usize + 2],
|
||||||
|
bytes[34 + target_lp_address_len as usize + 3],
|
||||||
|
]);
|
||||||
|
if bytes[34 + target_lp_address_len as usize + 4..].len() != inner_packet_bytes_len as usize
|
||||||
|
{
|
||||||
|
return Err(LpError::DeserializationError(format!(
|
||||||
|
"Expected {inner_packet_bytes_len} bytes to deserialise inner packet bytes of ForwardPacketData. got {}",
|
||||||
|
bytes[34 + target_lp_address_len as usize + 4..].len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let inner_packet_bytes = bytes[34 + target_lp_address_len as usize + 4..].to_vec();
|
||||||
|
|
||||||
|
Ok(ForwardPacketData {
|
||||||
|
target_gateway_identity,
|
||||||
|
target_lp_address,
|
||||||
|
inner_packet_bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Subsession KK1 message - first message of Noise KK handshake
|
/// Subsession KK1 message - first message of Noise KK handshake
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SubsessionKK1Data {
|
pub struct SubsessionKK1Data {
|
||||||
/// Noise KK first message payload (ephemeral key + encrypted static)
|
/// Noise KK first message payload (ephemeral key + encrypted static)
|
||||||
pub payload: Vec<u8>,
|
pub payload: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SubsessionKK1Data {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.payload.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
dst.put_slice(&self.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(bytes: &[u8]) -> Result<Self, LpError> {
|
||||||
|
Ok(SubsessionKK1Data {
|
||||||
|
payload: bytes.to_vec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Subsession KK2 message - second message of Noise KK handshake
|
/// Subsession KK2 message - second message of Noise KK handshake
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SubsessionKK2Data {
|
pub struct SubsessionKK2Data {
|
||||||
/// Noise KK second message payload (ephemeral key + encrypted response)
|
/// Noise KK second message payload (ephemeral key + encrypted response)
|
||||||
pub payload: Vec<u8>,
|
pub payload: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SubsessionKK2Data {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.payload.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
dst.put_slice(&self.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(bytes: &[u8]) -> Result<Self, LpError> {
|
||||||
|
Ok(SubsessionKK2Data {
|
||||||
|
payload: bytes.to_vec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Subsession ready confirmation with new session index
|
/// Subsession ready confirmation with new session index
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SubsessionReadyData {
|
pub struct SubsessionReadyData {
|
||||||
/// New subsession's receiver index for routing
|
/// New subsession's receiver index for routing
|
||||||
pub receiver_index: u32,
|
pub receiver_index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SubsessionReadyData {
|
||||||
|
pub const LEN: usize = 4;
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
Self::LEN
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode(&self, dst: &mut BytesMut) {
|
||||||
|
dst.put_u32_le(self.receiver_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(bytes: &[u8]) -> Result<Self, LpError> {
|
||||||
|
if bytes.len() != 4 {
|
||||||
|
return Err(LpError::DeserializationError(format!(
|
||||||
|
"Expected 4 bytes to deserialise SubsessionReadyData. got {}",
|
||||||
|
bytes.len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(SubsessionReadyData {
|
||||||
|
receiver_index: u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LpMessage {
|
pub enum LpMessage {
|
||||||
Busy,
|
Busy,
|
||||||
@@ -275,23 +471,18 @@ impl LpMessage {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
LpMessage::Busy => 0,
|
LpMessage::Busy => 0,
|
||||||
LpMessage::Handshake(payload) => payload.0.len(),
|
LpMessage::Handshake(payload) => payload.len(),
|
||||||
LpMessage::EncryptedData(payload) => payload.0.len(),
|
LpMessage::EncryptedData(payload) => payload.len(),
|
||||||
// 4 bytes receiver_index + 32 bytes x25519 key + 32 bytes ed25519 key + 32 bytes salt
|
LpMessage::ClientHello(payload) => payload.len(),
|
||||||
LpMessage::ClientHello(_) => ClientHelloData::LEN,
|
LpMessage::KKTRequest(payload) => payload.len(),
|
||||||
LpMessage::KKTRequest(payload) => payload.0.len(),
|
LpMessage::KKTResponse(payload) => payload.len(),
|
||||||
LpMessage::KKTResponse(payload) => payload.0.len(),
|
LpMessage::ForwardPacket(payload) => payload.len(),
|
||||||
LpMessage::ForwardPacket(data) => {
|
|
||||||
32 + data.target_lp_address.len() + data.inner_packet_bytes.len() + 10
|
|
||||||
}
|
|
||||||
LpMessage::Collision => 0,
|
LpMessage::Collision => 0,
|
||||||
LpMessage::Ack => 0,
|
LpMessage::Ack => 0,
|
||||||
LpMessage::SubsessionRequest => 0,
|
LpMessage::SubsessionRequest => 0,
|
||||||
// Variable length: bincode overhead (~8 bytes for Vec length) + payload
|
LpMessage::SubsessionKK1(payload) => payload.len(),
|
||||||
LpMessage::SubsessionKK1(data) => 8 + data.payload.len(),
|
LpMessage::SubsessionKK2(payload) => payload.len(),
|
||||||
LpMessage::SubsessionKK2(data) => 8 + data.payload.len(),
|
LpMessage::SubsessionReady(payload) => payload.len(),
|
||||||
// 4 bytes u32 + bincode overhead (~4 bytes)
|
|
||||||
LpMessage::SubsessionReady(_) => 8,
|
|
||||||
LpMessage::SubsessionAbort => 0,
|
LpMessage::SubsessionAbort => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -318,51 +509,90 @@ impl LpMessage {
|
|||||||
pub fn encode_content(&self, dst: &mut BytesMut) {
|
pub fn encode_content(&self, dst: &mut BytesMut) {
|
||||||
match self {
|
match self {
|
||||||
LpMessage::Busy => { /* No content */ }
|
LpMessage::Busy => { /* No content */ }
|
||||||
LpMessage::Handshake(payload) => {
|
LpMessage::Handshake(payload) => payload.encode(dst),
|
||||||
dst.put_slice(&payload.0);
|
LpMessage::EncryptedData(payload) => payload.encode(dst),
|
||||||
}
|
LpMessage::ClientHello(data) => data.encode(dst),
|
||||||
LpMessage::EncryptedData(payload) => {
|
LpMessage::KKTRequest(payload) => payload.encode(dst),
|
||||||
dst.put_slice(&payload.0);
|
LpMessage::KKTResponse(payload) => payload.encode(dst),
|
||||||
}
|
LpMessage::ForwardPacket(data) => data.encode(dst),
|
||||||
LpMessage::ClientHello(data) => {
|
|
||||||
dst.put_slice(&data.encode());
|
|
||||||
}
|
|
||||||
LpMessage::KKTRequest(payload) => {
|
|
||||||
dst.put_slice(&payload.0);
|
|
||||||
}
|
|
||||||
LpMessage::KKTResponse(payload) => {
|
|
||||||
dst.put_slice(&payload.0);
|
|
||||||
}
|
|
||||||
LpMessage::ForwardPacket(data) => {
|
|
||||||
let serialized = lp_bincode_serializer()
|
|
||||||
.serialize(data)
|
|
||||||
.expect("Failed to serialize ForwardPacketData");
|
|
||||||
dst.put_slice(&serialized);
|
|
||||||
}
|
|
||||||
LpMessage::Collision => { /* No content */ }
|
LpMessage::Collision => { /* No content */ }
|
||||||
LpMessage::Ack => { /* No content */ }
|
LpMessage::Ack => { /* No content */ }
|
||||||
LpMessage::SubsessionRequest => { /* No content - signal only */ }
|
LpMessage::SubsessionRequest => { /* No content - signal only */ }
|
||||||
LpMessage::SubsessionKK1(data) => {
|
LpMessage::SubsessionKK1(data) => data.encode(dst),
|
||||||
let serialized = lp_bincode_serializer()
|
LpMessage::SubsessionKK2(data) => data.encode(dst),
|
||||||
.serialize(data)
|
LpMessage::SubsessionReady(data) => data.encode(dst),
|
||||||
.expect("Failed to serialize SubsessionKK1Data");
|
|
||||||
dst.put_slice(&serialized);
|
|
||||||
}
|
|
||||||
LpMessage::SubsessionKK2(data) => {
|
|
||||||
let serialized = lp_bincode_serializer()
|
|
||||||
.serialize(data)
|
|
||||||
.expect("Failed to serialize SubsessionKK2Data");
|
|
||||||
dst.put_slice(&serialized);
|
|
||||||
}
|
|
||||||
LpMessage::SubsessionReady(data) => {
|
|
||||||
let serialized = lp_bincode_serializer()
|
|
||||||
.serialize(data)
|
|
||||||
.expect("Failed to serialize SubsessionReadyData");
|
|
||||||
dst.put_slice(&serialized);
|
|
||||||
}
|
|
||||||
LpMessage::SubsessionAbort => { /* No content - signal only */ }
|
LpMessage::SubsessionAbort => { /* No content - signal only */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse message from its type and content bytes.
|
||||||
|
///
|
||||||
|
/// Used when decrypting outer-encrypted packets where the message type
|
||||||
|
/// was encrypted along with the content.
|
||||||
|
pub fn decode_content(content: &[u8], message_type: MessageType) -> Result<Self, LpError> {
|
||||||
|
match message_type {
|
||||||
|
MessageType::Busy => {
|
||||||
|
content.ensure_empty()?;
|
||||||
|
Ok(LpMessage::Busy)
|
||||||
|
}
|
||||||
|
MessageType::Handshake => Ok(LpMessage::Handshake(HandshakeData::decode(content)?)),
|
||||||
|
MessageType::EncryptedData => Ok(LpMessage::EncryptedData(
|
||||||
|
EncryptedDataPayload::decode(content)?,
|
||||||
|
)),
|
||||||
|
MessageType::ClientHello => {
|
||||||
|
Ok(LpMessage::ClientHello(ClientHelloData::decode(content)?))
|
||||||
|
}
|
||||||
|
MessageType::KKTRequest => Ok(LpMessage::KKTRequest(KKTRequestData::decode(content)?)),
|
||||||
|
MessageType::KKTResponse => {
|
||||||
|
Ok(LpMessage::KKTResponse(KKTResponseData::decode(content)?))
|
||||||
|
}
|
||||||
|
MessageType::ForwardPacket => Ok(LpMessage::ForwardPacket(ForwardPacketData::decode(
|
||||||
|
content,
|
||||||
|
)?)),
|
||||||
|
MessageType::Collision => {
|
||||||
|
content.ensure_empty()?;
|
||||||
|
Ok(LpMessage::Collision)
|
||||||
|
}
|
||||||
|
MessageType::Ack => {
|
||||||
|
content.ensure_empty()?;
|
||||||
|
Ok(LpMessage::Ack)
|
||||||
|
}
|
||||||
|
MessageType::SubsessionRequest => {
|
||||||
|
content.ensure_empty()?;
|
||||||
|
Ok(LpMessage::SubsessionRequest)
|
||||||
|
}
|
||||||
|
MessageType::SubsessionKK1 => Ok(LpMessage::SubsessionKK1(SubsessionKK1Data::decode(
|
||||||
|
content,
|
||||||
|
)?)),
|
||||||
|
MessageType::SubsessionKK2 => Ok(LpMessage::SubsessionKK2(SubsessionKK2Data::decode(
|
||||||
|
content,
|
||||||
|
)?)),
|
||||||
|
MessageType::SubsessionReady => Ok(LpMessage::SubsessionReady(
|
||||||
|
SubsessionReadyData::decode(content)?,
|
||||||
|
)),
|
||||||
|
MessageType::SubsessionAbort => {
|
||||||
|
content.ensure_empty()?;
|
||||||
|
Ok(LpMessage::SubsessionAbort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper trait for improving readability to return error if bytes content is not empty
|
||||||
|
trait EnsureEmptyContent {
|
||||||
|
fn ensure_empty(&self) -> Result<(), LpError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnsureEmptyContent for &[u8] {
|
||||||
|
fn ensure_empty(&self) -> Result<(), LpError> {
|
||||||
|
if !self.is_empty() {
|
||||||
|
return Err(LpError::InvalidPayloadSize {
|
||||||
|
expected: 0,
|
||||||
|
actual: self.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -379,7 +609,7 @@ mod tests {
|
|||||||
|
|
||||||
let resp_header = LpHeader {
|
let resp_header = LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 0,
|
receiver_idx: 0,
|
||||||
counter: 0,
|
counter: 0,
|
||||||
};
|
};
|
||||||
@@ -411,8 +641,13 @@ mod tests {
|
|||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.expect("System time before UNIX epoch")
|
.expect("System time before UNIX epoch")
|
||||||
.as_secs();
|
.as_secs();
|
||||||
let client_key = [1u8; 32];
|
|
||||||
let client_ed25519_key = [2u8; 32];
|
let mut rng = rand::thread_rng();
|
||||||
|
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||||
|
let x25519 = ed25519.to_x25519();
|
||||||
|
|
||||||
|
let client_key = *x25519.public_key();
|
||||||
|
let client_ed25519_key = *ed25519.public_key();
|
||||||
let hello1 =
|
let hello1 =
|
||||||
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||||
let hello2 =
|
let hello2 =
|
||||||
@@ -433,8 +668,12 @@ mod tests {
|
|||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.expect("System time before UNIX epoch")
|
.expect("System time before UNIX epoch")
|
||||||
.as_secs();
|
.as_secs();
|
||||||
let client_key = [2u8; 32];
|
let mut rng = rand::thread_rng();
|
||||||
let client_ed25519_key = [3u8; 32];
|
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||||
|
let x25519 = ed25519.to_x25519();
|
||||||
|
|
||||||
|
let client_key = *x25519.public_key();
|
||||||
|
let client_ed25519_key = *ed25519.public_key();
|
||||||
let hello = ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
let hello = ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||||
|
|
||||||
let timestamp = hello.extract_timestamp();
|
let timestamp = hello.extract_timestamp();
|
||||||
@@ -453,8 +692,12 @@ mod tests {
|
|||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.expect("System time before UNIX epoch")
|
.expect("System time before UNIX epoch")
|
||||||
.as_secs();
|
.as_secs();
|
||||||
let client_key = [3u8; 32];
|
let mut rng = rand::thread_rng();
|
||||||
let client_ed25519_key = [4u8; 32];
|
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||||
|
let x25519 = ed25519.to_x25519();
|
||||||
|
|
||||||
|
let client_key = *x25519.public_key();
|
||||||
|
let client_ed25519_key = *ed25519.public_key();
|
||||||
let hello = ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
let hello = ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||||
|
|
||||||
// First 8 bytes should be non-zero timestamp
|
// First 8 bytes should be non-zero timestamp
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use parking_lot::Mutex;
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) const UDP_HEADER_LEN: usize = 8;
|
pub(crate) const UDP_HEADER_LEN: usize = 8;
|
||||||
@@ -25,6 +26,11 @@ pub const TRAILER_LEN: usize = 16;
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) const UDP_PAYLOAD_SIZE: usize = MTU - UDP_OVERHEAD - TRAILER_LEN;
|
pub(crate) const UDP_PAYLOAD_SIZE: usize = MTU - UDP_OVERHEAD - TRAILER_LEN;
|
||||||
|
|
||||||
|
pub mod version {
|
||||||
|
/// The current version of the Lewes Protocol that is put into each new constructed header.
|
||||||
|
pub const CURRENT: u8 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LpPacket {
|
pub struct LpPacket {
|
||||||
pub(crate) header: LpHeader,
|
pub(crate) header: LpHeader,
|
||||||
@@ -187,7 +193,7 @@ impl OuterHeader {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LpHeader {
|
pub struct LpHeader {
|
||||||
pub protocol_version: u8,
|
pub protocol_version: u8,
|
||||||
pub reserved: u16,
|
pub reserved: [u8; 3],
|
||||||
pub receiver_idx: u32,
|
pub receiver_idx: u32,
|
||||||
pub counter: u64,
|
pub counter: u64,
|
||||||
}
|
}
|
||||||
@@ -199,8 +205,8 @@ impl LpHeader {
|
|||||||
impl LpHeader {
|
impl LpHeader {
|
||||||
pub fn new(receiver_idx: u32, counter: u64) -> Self {
|
pub fn new(receiver_idx: u32, counter: u64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
protocol_version: 1,
|
protocol_version: version::CURRENT,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx,
|
receiver_idx,
|
||||||
counter,
|
counter,
|
||||||
}
|
}
|
||||||
@@ -211,7 +217,7 @@ impl LpHeader {
|
|||||||
dst.put_u8(self.protocol_version);
|
dst.put_u8(self.protocol_version);
|
||||||
|
|
||||||
// reserved
|
// reserved
|
||||||
dst.put_slice(&[0, 0, 0]);
|
dst.put_slice(&self.reserved);
|
||||||
|
|
||||||
// sender index
|
// sender index
|
||||||
dst.put_slice(&self.receiver_idx.to_le_bytes());
|
dst.put_slice(&self.receiver_idx.to_le_bytes());
|
||||||
@@ -226,7 +232,28 @@ impl LpHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let protocol_version = src[0];
|
let protocol_version = src[0];
|
||||||
// Skip reserved bytes [1..4]
|
|
||||||
|
// Ensure we are using compatible protocol
|
||||||
|
// right now only support a single version
|
||||||
|
if protocol_version > version::CURRENT {
|
||||||
|
return Err(LpError::IncompatibleFuturePacketVersion {
|
||||||
|
got: protocol_version,
|
||||||
|
highest_supported: version::CURRENT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if protocol_version < version::CURRENT {
|
||||||
|
return Err(LpError::IncompatibleLegacyPacketVersion {
|
||||||
|
got: protocol_version,
|
||||||
|
lowest_supported: version::CURRENT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip reserved bytes, but log if they're different from the expected zeroes
|
||||||
|
let reserved = [src[1], src[2], src[3]];
|
||||||
|
if reserved != [0u8; 3] {
|
||||||
|
warn!("received non-zero reserved bytes. got: {reserved:?}");
|
||||||
|
}
|
||||||
|
|
||||||
let mut receiver_idx_bytes = [0u8; 4];
|
let mut receiver_idx_bytes = [0u8; 4];
|
||||||
receiver_idx_bytes.copy_from_slice(&src[4..8]);
|
receiver_idx_bytes.copy_from_slice(&src[4..8]);
|
||||||
@@ -238,7 +265,7 @@ impl LpHeader {
|
|||||||
|
|
||||||
Ok(LpHeader {
|
Ok(LpHeader {
|
||||||
protocol_version,
|
protocol_version,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx,
|
receiver_idx,
|
||||||
counter,
|
counter,
|
||||||
})
|
})
|
||||||
|
|||||||
+35
-32
@@ -47,12 +47,11 @@
|
|||||||
//! - **No cleanup needed**: No state was mutated
|
//! - **No cleanup needed**: No state was mutated
|
||||||
|
|
||||||
use crate::LpError;
|
use crate::LpError;
|
||||||
use crate::keypair::{PrivateKey, PublicKey};
|
|
||||||
use libcrux_psq::v1::cred::{Authenticator, Ed25519};
|
use libcrux_psq::v1::cred::{Authenticator, Ed25519};
|
||||||
use libcrux_psq::v1::impls::X25519 as PsqX25519;
|
use libcrux_psq::v1::impls::X25519 as PsqX25519;
|
||||||
use libcrux_psq::v1::psk_registration::{Initiator, InitiatorMsg, Responder};
|
use libcrux_psq::v1::psk_registration::{Initiator, InitiatorMsg, Responder};
|
||||||
use libcrux_psq::v1::traits::{Ciphertext as PsqCiphertext, PSQ};
|
use libcrux_psq::v1::traits::{Ciphertext as PsqCiphertext, PSQ};
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
|
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tls_codec::{Deserialize as TlsDeserializeTrait, Serialize as TlsSerializeTrait};
|
use tls_codec::{Deserialize as TlsDeserializeTrait, Serialize as TlsSerializeTrait};
|
||||||
@@ -137,8 +136,8 @@ pub struct PsqResponderResult {
|
|||||||
/// // Send ciphertext to gateway
|
/// // Send ciphertext to gateway
|
||||||
/// ```
|
/// ```
|
||||||
pub fn derive_psk_with_psq_initiator(
|
pub fn derive_psk_with_psq_initiator(
|
||||||
local_x25519_private: &PrivateKey,
|
local_x25519_private: &x25519::PrivateKey,
|
||||||
remote_x25519_public: &PublicKey,
|
remote_x25519_public: &x25519::PublicKey,
|
||||||
remote_kem_public: &EncapsulationKey,
|
remote_kem_public: &EncapsulationKey,
|
||||||
salt: &[u8; 32],
|
salt: &[u8; 32],
|
||||||
) -> Result<([u8; 32], Vec<u8>), LpError> {
|
) -> Result<([u8; 32], Vec<u8>), LpError> {
|
||||||
@@ -168,7 +167,7 @@ pub fn derive_psk_with_psq_initiator(
|
|||||||
|
|
||||||
// Step 3: Combine ECDH + PSQ via Blake3 KDF
|
// Step 3: Combine ECDH + PSQ via Blake3 KDF
|
||||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
||||||
combined.extend_from_slice(ecdh_secret.as_bytes());
|
combined.extend_from_slice(&ecdh_secret);
|
||||||
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
||||||
combined.extend_from_slice(salt);
|
combined.extend_from_slice(salt);
|
||||||
|
|
||||||
@@ -220,8 +219,8 @@ pub fn derive_psk_with_psq_initiator(
|
|||||||
/// )?;
|
/// )?;
|
||||||
/// ```
|
/// ```
|
||||||
pub fn derive_psk_with_psq_responder(
|
pub fn derive_psk_with_psq_responder(
|
||||||
local_x25519_private: &PrivateKey,
|
local_x25519_private: &x25519::PrivateKey,
|
||||||
remote_x25519_public: &PublicKey,
|
remote_x25519_public: &x25519::PublicKey,
|
||||||
local_kem_keypair: (&DecapsulationKey, &EncapsulationKey),
|
local_kem_keypair: (&DecapsulationKey, &EncapsulationKey),
|
||||||
ciphertext: &[u8],
|
ciphertext: &[u8],
|
||||||
salt: &[u8; 32],
|
salt: &[u8; 32],
|
||||||
@@ -249,7 +248,7 @@ pub fn derive_psk_with_psq_responder(
|
|||||||
|
|
||||||
// Step 5: Combine ECDH + PSQ via Blake3 KDF (same formula as initiator)
|
// Step 5: Combine ECDH + PSQ via Blake3 KDF (same formula as initiator)
|
||||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
||||||
combined.extend_from_slice(ecdh_secret.as_bytes());
|
combined.extend_from_slice(&ecdh_secret);
|
||||||
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
||||||
combined.extend_from_slice(salt);
|
combined.extend_from_slice(salt);
|
||||||
|
|
||||||
@@ -280,8 +279,8 @@ pub fn derive_psk_with_psq_responder(
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// `PsqInitiatorResult` containing PSK, payload, and raw PQ shared secret
|
/// `PsqInitiatorResult` containing PSK, payload, and raw PQ shared secret
|
||||||
pub fn psq_initiator_create_message(
|
pub fn psq_initiator_create_message(
|
||||||
local_x25519_private: &PrivateKey,
|
local_x25519_private: &x25519::PrivateKey,
|
||||||
remote_x25519_public: &PublicKey,
|
remote_x25519_public: &x25519::PublicKey,
|
||||||
remote_kem_public: &EncapsulationKey,
|
remote_kem_public: &EncapsulationKey,
|
||||||
client_ed25519_sk: &ed25519::PrivateKey,
|
client_ed25519_sk: &ed25519::PrivateKey,
|
||||||
client_ed25519_pk: &ed25519::PublicKey,
|
client_ed25519_pk: &ed25519::PublicKey,
|
||||||
@@ -335,7 +334,7 @@ pub fn psq_initiator_create_message(
|
|||||||
|
|
||||||
// Step 3: Combine ECDH + PSQ via Blake3 KDF
|
// Step 3: Combine ECDH + PSQ via Blake3 KDF
|
||||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
||||||
combined.extend_from_slice(ecdh_secret.as_bytes());
|
combined.extend_from_slice(&ecdh_secret);
|
||||||
combined.extend_from_slice(psq_psk); // psq_psk is already a &[u8; 32]
|
combined.extend_from_slice(psq_psk); // psq_psk is already a &[u8; 32]
|
||||||
combined.extend_from_slice(salt);
|
combined.extend_from_slice(salt);
|
||||||
|
|
||||||
@@ -375,8 +374,8 @@ pub fn psq_initiator_create_message(
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
/// `PsqResponderResult` containing PSK, PSK handle, and raw PQ shared secret
|
/// `PsqResponderResult` containing PSK, PSK handle, and raw PQ shared secret
|
||||||
pub fn psq_responder_process_message(
|
pub fn psq_responder_process_message(
|
||||||
local_x25519_private: &PrivateKey,
|
local_x25519_private: &x25519::PrivateKey,
|
||||||
remote_x25519_public: &PublicKey,
|
remote_x25519_public: &x25519::PublicKey,
|
||||||
local_kem_keypair: (&DecapsulationKey, &EncapsulationKey),
|
local_kem_keypair: (&DecapsulationKey, &EncapsulationKey),
|
||||||
initiator_ed25519_pk: &ed25519::PublicKey,
|
initiator_ed25519_pk: &ed25519::PublicKey,
|
||||||
psq_payload: &[u8],
|
psq_payload: &[u8],
|
||||||
@@ -444,7 +443,7 @@ pub fn psq_responder_process_message(
|
|||||||
|
|
||||||
// Step 6: Combine ECDH + PSQ via Blake3 KDF (same formula as initiator)
|
// Step 6: Combine ECDH + PSQ via Blake3 KDF (same formula as initiator)
|
||||||
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
let mut combined = Vec::with_capacity(64 + psq_psk.len());
|
||||||
combined.extend_from_slice(ecdh_secret.as_bytes());
|
combined.extend_from_slice(&ecdh_secret);
|
||||||
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
combined.extend_from_slice(&psq_psk); // psq_psk is [u8; 32], need &
|
||||||
combined.extend_from_slice(salt);
|
combined.extend_from_slice(salt);
|
||||||
|
|
||||||
@@ -493,12 +492,16 @@ pub fn derive_subsession_psk(pq_shared_secret: &[u8; 32], subsession_index: u64)
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::keypair::Keypair;
|
use rand::thread_rng;
|
||||||
|
|
||||||
|
fn generate_x25519_keypair() -> x25519::KeyPair {
|
||||||
|
x25519::KeyPair::new(&mut thread_rng())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_psk_derivation_is_symmetric() {
|
fn test_psk_derivation_is_symmetric() {
|
||||||
let keypair_1 = Keypair::default();
|
let keypair_1 = generate_x25519_keypair();
|
||||||
let keypair_2 = Keypair::default();
|
let keypair_2 = generate_x25519_keypair();
|
||||||
let salt = [2u8; 32];
|
let salt = [2u8; 32];
|
||||||
|
|
||||||
let mut rng = &mut rand09::rng();
|
let mut rng = &mut rand09::rng();
|
||||||
@@ -533,8 +536,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_different_salts_produce_different_psks() {
|
fn test_different_salts_produce_different_psks() {
|
||||||
let keypair_1 = Keypair::default();
|
let keypair_1 = generate_x25519_keypair();
|
||||||
let keypair_2 = Keypair::default();
|
let keypair_2 = generate_x25519_keypair();
|
||||||
|
|
||||||
let salt1 = [1u8; 32];
|
let salt1 = [1u8; 32];
|
||||||
let salt2 = [2u8; 32];
|
let salt2 = [2u8; 32];
|
||||||
@@ -562,9 +565,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_different_keys_produce_different_psks() {
|
fn test_different_keys_produce_different_psks() {
|
||||||
let keypair_1 = Keypair::default();
|
let keypair_1 = generate_x25519_keypair();
|
||||||
let keypair_2 = Keypair::default();
|
let keypair_2 = generate_x25519_keypair();
|
||||||
let keypair_3 = Keypair::default();
|
let keypair_3 = generate_x25519_keypair();
|
||||||
let salt = [3u8; 32];
|
let salt = [3u8; 32];
|
||||||
|
|
||||||
let mut rng = &mut rand09::rng();
|
let mut rng = &mut rand09::rng();
|
||||||
@@ -601,8 +604,8 @@ mod tests {
|
|||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
// Generate X25519 keypairs for Noise
|
// Generate X25519 keypairs for Noise
|
||||||
let client_keypair = Keypair::default();
|
let client_keypair = generate_x25519_keypair();
|
||||||
let gateway_keypair = Keypair::default();
|
let gateway_keypair = generate_x25519_keypair();
|
||||||
|
|
||||||
// Generate KEM keypair for PSQ
|
// Generate KEM keypair for PSQ
|
||||||
let (kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
@@ -659,8 +662,8 @@ mod tests {
|
|||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
// Generate X25519 keypairs for Noise
|
// Generate X25519 keypairs for Noise
|
||||||
let client_keypair = Keypair::default();
|
let client_keypair = generate_x25519_keypair();
|
||||||
let gateway_keypair = Keypair::default();
|
let gateway_keypair = generate_x25519_keypair();
|
||||||
|
|
||||||
// Generate KEM keypair for PSQ
|
// Generate KEM keypair for PSQ
|
||||||
let (kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (kem_sk, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
@@ -698,8 +701,8 @@ mod tests {
|
|||||||
fn test_different_kem_keys_different_psk() {
|
fn test_different_kem_keys_different_psk() {
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
let client_keypair = Keypair::default();
|
let client_keypair = generate_x25519_keypair();
|
||||||
let gateway_keypair = Keypair::default();
|
let gateway_keypair = generate_x25519_keypair();
|
||||||
|
|
||||||
// Two different KEM keypairs
|
// Two different KEM keypairs
|
||||||
let (_, kem_pk1) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, kem_pk1) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
@@ -736,8 +739,8 @@ mod tests {
|
|||||||
fn test_psq_psk_output_length() {
|
fn test_psq_psk_output_length() {
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
let client_keypair = Keypair::default();
|
let client_keypair = generate_x25519_keypair();
|
||||||
let gateway_keypair = Keypair::default();
|
let gateway_keypair = generate_x25519_keypair();
|
||||||
|
|
||||||
let (_, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
let enc_key = EncapsulationKey::X25519(kem_pk);
|
let enc_key = EncapsulationKey::X25519(kem_pk);
|
||||||
@@ -759,8 +762,8 @@ mod tests {
|
|||||||
fn test_psq_different_salts_different_psks() {
|
fn test_psq_different_salts_different_psks() {
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
let client_keypair = Keypair::default();
|
let client_keypair = generate_x25519_keypair();
|
||||||
let gateway_keypair = Keypair::default();
|
let gateway_keypair = generate_x25519_keypair();
|
||||||
|
|
||||||
let (_, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
let (_, kem_pk) = generate_keypair_libcrux(&mut rng, KEM::X25519).unwrap();
|
||||||
let enc_key = EncapsulationKey::X25519(kem_pk);
|
let enc_key = EncapsulationKey::X25519(kem_pk);
|
||||||
|
|||||||
+128
-151
@@ -7,7 +7,6 @@
|
|||||||
//! and Noise protocol state handling.
|
//! and Noise protocol state handling.
|
||||||
|
|
||||||
use crate::codec::OuterAeadKey;
|
use crate::codec::OuterAeadKey;
|
||||||
use crate::keypair::{PrivateKey, PublicKey};
|
|
||||||
use crate::message::{EncryptedDataPayload, HandshakeData};
|
use crate::message::{EncryptedDataPayload, HandshakeData};
|
||||||
use crate::noise_protocol::{NoiseError, NoiseProtocol, ReadResult};
|
use crate::noise_protocol::{NoiseError, NoiseProtocol, ReadResult};
|
||||||
use crate::packet::LpHeader;
|
use crate::packet::LpHeader;
|
||||||
@@ -16,10 +15,13 @@ use crate::psk::{
|
|||||||
};
|
};
|
||||||
use crate::replay::ReceivingKeyCounterValidator;
|
use crate::replay::ReceivingKeyCounterValidator;
|
||||||
use crate::{LpError, LpMessage, LpPacket};
|
use crate::{LpError, LpMessage, LpPacket};
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
|
use nym_kkt::ciphersuite::{DecapsulationKey, EncapsulationKey};
|
||||||
|
use nym_kkt::encryption::KKTSessionSecret;
|
||||||
|
use nym_kkt::kkt::decrypt_kkt_response_frame;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use snow::Builder;
|
use snow::Builder;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
@@ -71,6 +73,7 @@ pub enum KKTState {
|
|||||||
InitiatorWaiting {
|
InitiatorWaiting {
|
||||||
/// KKT context for verifying the response
|
/// KKT context for verifying the response
|
||||||
context: nym_kkt::context::KKTContext,
|
context: nym_kkt::context::KKTContext,
|
||||||
|
session_secret: KKTSessionSecret,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// KKT exchange completed (initiator received and validated KEM key).
|
/// KKT exchange completed (initiator received and validated KEM key).
|
||||||
@@ -88,7 +91,7 @@ impl std::fmt::Debug for KKTState {
|
|||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::NotStarted => write!(f, "KKTState::NotStarted"),
|
Self::NotStarted => write!(f, "KKTState::NotStarted"),
|
||||||
Self::InitiatorWaiting { context } => f
|
Self::InitiatorWaiting { context, .. } => f
|
||||||
.debug_struct("KKTState::InitiatorWaiting")
|
.debug_struct("KKTState::InitiatorWaiting")
|
||||||
.field("context", context)
|
.field("context", context)
|
||||||
.finish(),
|
.finish(),
|
||||||
@@ -179,20 +182,17 @@ pub struct LpSession {
|
|||||||
psk_injected: AtomicBool,
|
psk_injected: AtomicBool,
|
||||||
|
|
||||||
// PSQ-related keys stored for handshake
|
// PSQ-related keys stored for handshake
|
||||||
/// Local Ed25519 private key for PSQ authentication
|
/// Local Ed25519 keys for PSQ authentication
|
||||||
local_ed25519_private: ed25519::PrivateKey,
|
local_ed25519: Arc<ed25519::KeyPair>,
|
||||||
|
|
||||||
/// Local Ed25519 public key for PSQ authentication
|
|
||||||
local_ed25519_public: ed25519::PublicKey,
|
|
||||||
|
|
||||||
/// Remote Ed25519 public key for PSQ authentication
|
/// Remote Ed25519 public key for PSQ authentication
|
||||||
remote_ed25519_public: ed25519::PublicKey,
|
remote_ed25519_public: ed25519::PublicKey,
|
||||||
|
|
||||||
/// Local X25519 private key (Noise static key)
|
/// Local x25519 keys (Noise static key)
|
||||||
local_x25519_private: PrivateKey,
|
local_x25519: Arc<x25519::KeyPair>,
|
||||||
|
|
||||||
/// Remote X25519 public key (Noise static key)
|
/// Remote X25519 public key (Noise static key)
|
||||||
remote_x25519_public: PublicKey,
|
remote_x25519_public: x25519::PublicKey,
|
||||||
|
|
||||||
/// Salt for PSK derivation
|
/// Salt for PSK derivation
|
||||||
salt: [u8; 32],
|
salt: [u8; 32],
|
||||||
@@ -273,8 +273,7 @@ impl LpSession {
|
|||||||
/// Defaults to 1 (current LP version). Set during handshake via
|
/// Defaults to 1 (current LP version). Set during handshake via
|
||||||
/// `set_negotiated_version()` when ClientHello/ServerHello is processed.
|
/// `set_negotiated_version()` when ClientHello/ServerHello is processed.
|
||||||
pub fn negotiated_version(&self) -> u8 {
|
pub fn negotiated_version(&self) -> u8 {
|
||||||
self.negotiated_version
|
self.negotiated_version.load(Ordering::Acquire)
|
||||||
.load(std::sync::atomic::Ordering::Acquire)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the negotiated protocol version from handshake packet header.
|
/// Sets the negotiated protocol version from handshake packet header.
|
||||||
@@ -282,23 +281,22 @@ impl LpSession {
|
|||||||
/// Should be called during handshake when processing ClientHello (responder)
|
/// Should be called during handshake when processing ClientHello (responder)
|
||||||
/// or ServerHello (initiator) to record the agreed protocol version.
|
/// or ServerHello (initiator) to record the agreed protocol version.
|
||||||
pub fn set_negotiated_version(&self, version: u8) {
|
pub fn set_negotiated_version(&self, version: u8) {
|
||||||
self.negotiated_version
|
self.negotiated_version.store(version, Ordering::Release);
|
||||||
.store(version, std::sync::atomic::Ordering::Release);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the local X25519 public key derived from the private key.
|
/// Returns the local X25519 public key.
|
||||||
///
|
///
|
||||||
/// This is used for KKT protocol when the responder needs to send their
|
/// This is used for KKT protocol when the responder needs to send their
|
||||||
/// KEM public key in the KKT response.
|
/// KEM public key in the KKT response.
|
||||||
pub fn local_x25519_public(&self) -> PublicKey {
|
pub fn local_x25519_public(&self) -> x25519::PublicKey {
|
||||||
self.local_x25519_private.public_key()
|
*self.local_x25519.public_key()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the remote X25519 public key.
|
/// Returns the remote X25519 public key.
|
||||||
///
|
///
|
||||||
/// Used for tie-breaking in simultaneous subsession initiation.
|
/// Used for tie-breaking in simultaneous subsession initiation.
|
||||||
/// Lower key loses and becomes responder.
|
/// Lower key loses and becomes responder.
|
||||||
pub fn remote_x25519_public(&self) -> &PublicKey {
|
pub fn remote_x25519_public(&self) -> &x25519::PublicKey {
|
||||||
&self.remote_x25519_public
|
&self.remote_x25519_public
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,17 +351,17 @@ impl LpSession {
|
|||||||
/// * `id` - Session identifier
|
/// * `id` - Session identifier
|
||||||
/// * `is_initiator` - True if this side initiates the Noise handshake.
|
/// * `is_initiator` - True if this side initiates the Noise handshake.
|
||||||
/// * `local_ed25519_keypair` - This side's Ed25519 keypair for PSQ authentication
|
/// * `local_ed25519_keypair` - This side's Ed25519 keypair for PSQ authentication
|
||||||
/// * `local_x25519_key` - This side's X25519 private key for Noise protocol and DHKEM
|
/// * `local_x25519_keypair` - This side's X25519 keypair for Noise protocol and DHKEM
|
||||||
/// * `remote_ed25519_key` - Peer's Ed25519 public key for PSQ authentication
|
/// * `remote_ed25519_key` - Peer's Ed25519 public key for PSQ authentication
|
||||||
/// * `remote_x25519_key` - Peer's X25519 public key for Noise protocol and DHKEM
|
/// * `remote_x25519_key` - Peer's X25519 public key for Noise protocol and DHKEM
|
||||||
/// * `salt` - Salt for PSK derivation
|
/// * `salt` - Salt for PSK derivation
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: u32,
|
id: u32,
|
||||||
is_initiator: bool,
|
is_initiator: bool,
|
||||||
local_ed25519_keypair: (&ed25519::PrivateKey, &ed25519::PublicKey),
|
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||||
local_x25519_key: &PrivateKey,
|
local_x25519_keypair: Arc<x25519::KeyPair>,
|
||||||
remote_ed25519_key: &ed25519::PublicKey,
|
remote_ed25519_key: &ed25519::PublicKey,
|
||||||
remote_x25519_key: &PublicKey,
|
remote_x25519_key: &x25519::PublicKey,
|
||||||
salt: &[u8; 32],
|
salt: &[u8; 32],
|
||||||
) -> Result<Self, LpError> {
|
) -> Result<Self, LpError> {
|
||||||
// XKpsk3 pattern requires remote static key known upfront (XK)
|
// XKpsk3 pattern requires remote static key known upfront (XK)
|
||||||
@@ -374,8 +372,8 @@ impl LpSession {
|
|||||||
let params = pattern_name.parse()?;
|
let params = pattern_name.parse()?;
|
||||||
let builder = Builder::new(params);
|
let builder = Builder::new(params);
|
||||||
|
|
||||||
let local_key_bytes = local_x25519_key.to_bytes();
|
let local_key_bytes = local_x25519_keypair.private_key().as_bytes();
|
||||||
let builder = builder.local_private_key(&local_key_bytes);
|
let builder = builder.local_private_key(local_key_bytes);
|
||||||
|
|
||||||
let remote_key_bytes = remote_x25519_key.to_bytes();
|
let remote_key_bytes = remote_x25519_key.to_bytes();
|
||||||
let builder = builder.remote_public_key(&remote_key_bytes);
|
let builder = builder.remote_public_key(&remote_key_bytes);
|
||||||
@@ -413,19 +411,10 @@ impl LpSession {
|
|||||||
sending_counter: AtomicU64::new(0),
|
sending_counter: AtomicU64::new(0),
|
||||||
receiving_counter: Mutex::new(ReceivingKeyCounterValidator::default()),
|
receiving_counter: Mutex::new(ReceivingKeyCounterValidator::default()),
|
||||||
psk_injected: AtomicBool::new(false),
|
psk_injected: AtomicBool::new(false),
|
||||||
// Ed25519 keys don't impl Clone, so convert to bytes and reconstruct
|
local_ed25519: local_ed25519_keypair.clone(),
|
||||||
local_ed25519_private: ed25519::PrivateKey::from_bytes(
|
remote_ed25519_public: *remote_ed25519_key,
|
||||||
&local_ed25519_keypair.0.to_bytes(),
|
local_x25519: local_x25519_keypair,
|
||||||
)
|
remote_x25519_public: *remote_x25519_key,
|
||||||
.expect("Valid ed25519 private key"),
|
|
||||||
local_ed25519_public: ed25519::PublicKey::from_bytes(
|
|
||||||
&local_ed25519_keypair.1.to_bytes(),
|
|
||||||
)
|
|
||||||
.expect("Valid ed25519 public key"),
|
|
||||||
remote_ed25519_public: ed25519::PublicKey::from_bytes(&remote_ed25519_key.to_bytes())
|
|
||||||
.expect("Valid ed25519 public key"),
|
|
||||||
local_x25519_private: local_x25519_key.clone(),
|
|
||||||
remote_x25519_public: remote_x25519_key.clone(),
|
|
||||||
salt: *salt,
|
salt: *salt,
|
||||||
outer_aead_key: Mutex::new(None),
|
outer_aead_key: Mutex::new(None),
|
||||||
pq_shared_secret: Mutex::new(None),
|
pq_shared_secret: Mutex::new(None),
|
||||||
@@ -561,13 +550,20 @@ impl LpSession {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut rng = rand09::rng();
|
let mut rng = rand09::rng();
|
||||||
match request_kem_key(&mut rng, ciphersuite, &self.local_ed25519_private) {
|
match request_kem_key(
|
||||||
Ok((context, request_frame)) => {
|
&mut rng,
|
||||||
|
ciphersuite,
|
||||||
|
self.local_ed25519.private_key(),
|
||||||
|
&self.remote_x25519_public,
|
||||||
|
) {
|
||||||
|
Ok((session_secret, context, request_bytes)) => {
|
||||||
// Store context for response validation
|
// Store context for response validation
|
||||||
*kkt_state = KKTState::InitiatorWaiting { context };
|
*kkt_state = KKTState::InitiatorWaiting {
|
||||||
|
context,
|
||||||
|
session_secret,
|
||||||
|
};
|
||||||
|
|
||||||
// Serialize KKT frame to bytes
|
// Serialize KKT frame to bytes
|
||||||
let request_bytes = request_frame.to_bytes();
|
|
||||||
Some(Ok(LpMessage::KKTRequest(crate::message::KKTRequestData(
|
Some(Ok(LpMessage::KKTRequest(crate::message::KKTRequestData(
|
||||||
request_bytes,
|
request_bytes,
|
||||||
))))
|
))))
|
||||||
@@ -613,8 +609,11 @@ impl LpSession {
|
|||||||
let mut kkt_state = self.kkt_state.lock();
|
let mut kkt_state = self.kkt_state.lock();
|
||||||
|
|
||||||
// Extract context from waiting state
|
// Extract context from waiting state
|
||||||
let mut context = match &*kkt_state {
|
let (mut context, session_secret) = match &*kkt_state {
|
||||||
KKTState::InitiatorWaiting { context } => *context,
|
KKTState::InitiatorWaiting {
|
||||||
|
context,
|
||||||
|
session_secret,
|
||||||
|
} => (*context, *session_secret),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(LpError::Internal(
|
return Err(LpError::Internal(
|
||||||
"KKT response received in invalid state".to_string(),
|
"KKT response received in invalid state".to_string(),
|
||||||
@@ -629,11 +628,10 @@ impl LpSession {
|
|||||||
None => {
|
None => {
|
||||||
// Signature-only mode: extract key from response and compute its hash
|
// Signature-only mode: extract key from response and compute its hash
|
||||||
// This effectively bypasses hash validation while keeping signature validation
|
// This effectively bypasses hash validation while keeping signature validation
|
||||||
use nym_kkt::frame::KKTFrame;
|
let (frame, _) = decrypt_kkt_response_frame(&session_secret, response_bytes)
|
||||||
|
.map_err(|e| {
|
||||||
let (frame, _) = KKTFrame::from_bytes(response_bytes).map_err(|e| {
|
LpError::Internal(format!("Failed to decrypt KKT response: {:?}", e))
|
||||||
LpError::Internal(format!("Failed to parse KKT response: {:?}", e))
|
})?;
|
||||||
})?;
|
|
||||||
|
|
||||||
hash_for_validation = hash_encapsulation_key(
|
hash_for_validation = hash_encapsulation_key(
|
||||||
&context.ciphersuite().hash_function(),
|
&context.ciphersuite().hash_function(),
|
||||||
@@ -647,6 +645,7 @@ impl LpSession {
|
|||||||
// Validate response and extract KEM key
|
// Validate response and extract KEM key
|
||||||
let kem_pk = validate_kem_response(
|
let kem_pk = validate_kem_response(
|
||||||
&mut context,
|
&mut context,
|
||||||
|
&session_secret,
|
||||||
&self.remote_ed25519_public,
|
&self.remote_ed25519_public,
|
||||||
hash_ref,
|
hash_ref,
|
||||||
response_bytes,
|
response_bytes,
|
||||||
@@ -680,20 +679,19 @@ impl LpSession {
|
|||||||
request_bytes: &[u8],
|
request_bytes: &[u8],
|
||||||
responder_kem_pk: &EncapsulationKey,
|
responder_kem_pk: &EncapsulationKey,
|
||||||
) -> Result<LpMessage, LpError> {
|
) -> Result<LpMessage, LpError> {
|
||||||
use nym_kkt::{frame::KKTFrame, kkt::handle_kem_request};
|
use nym_kkt::kkt::handle_kem_request;
|
||||||
|
|
||||||
|
let mut rng = rand09::rng();
|
||||||
|
|
||||||
let mut kkt_state = self.kkt_state.lock();
|
let mut kkt_state = self.kkt_state.lock();
|
||||||
|
|
||||||
// Deserialize request frame
|
|
||||||
let (request_frame, _) = KKTFrame::from_bytes(request_bytes).map_err(|e| {
|
|
||||||
LpError::Internal(format!("KKT request deserialization failed: {:?}", e))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Handle request and create signed response
|
// Handle request and create signed response
|
||||||
let response_frame = handle_kem_request(
|
let response_bytes = handle_kem_request(
|
||||||
&request_frame,
|
&mut rng,
|
||||||
|
request_bytes,
|
||||||
Some(&self.remote_ed25519_public), // Verify initiator signature
|
Some(&self.remote_ed25519_public), // Verify initiator signature
|
||||||
&self.local_ed25519_private, // Sign response
|
self.local_ed25519.private_key(), // Sign response
|
||||||
|
self.local_x25519.private_key(),
|
||||||
responder_kem_pk,
|
responder_kem_pk,
|
||||||
)
|
)
|
||||||
.map_err(|e| LpError::Internal(format!("KKT request handling failed: {:?}", e)))?;
|
.map_err(|e| LpError::Internal(format!("KKT request handling failed: {:?}", e)))?;
|
||||||
@@ -702,9 +700,6 @@ impl LpSession {
|
|||||||
// Responder doesn't store the kem_pk since they already have their own KEM keypair
|
// Responder doesn't store the kem_pk since they already have their own KEM keypair
|
||||||
*kkt_state = KKTState::ResponderProcessed;
|
*kkt_state = KKTState::ResponderProcessed;
|
||||||
|
|
||||||
// Serialize response frame
|
|
||||||
let response_bytes = response_frame.to_bytes();
|
|
||||||
|
|
||||||
Ok(LpMessage::KKTResponse(crate::message::KKTResponseData(
|
Ok(LpMessage::KKTResponse(crate::message::KKTResponseData(
|
||||||
response_bytes,
|
response_bytes,
|
||||||
)))
|
)))
|
||||||
@@ -748,11 +743,11 @@ impl LpSession {
|
|||||||
let session_context = self.id.to_le_bytes();
|
let session_context = self.id.to_le_bytes();
|
||||||
|
|
||||||
let psq_result = match psq_initiator_create_message(
|
let psq_result = match psq_initiator_create_message(
|
||||||
&self.local_x25519_private,
|
self.local_x25519.private_key(),
|
||||||
&self.remote_x25519_public,
|
&self.remote_x25519_public,
|
||||||
remote_kem,
|
remote_kem,
|
||||||
&self.local_ed25519_private,
|
self.local_ed25519.private_key(),
|
||||||
&self.local_ed25519_public,
|
self.local_ed25519.public_key(),
|
||||||
&self.salt,
|
&self.salt,
|
||||||
&session_context,
|
&session_context,
|
||||||
) {
|
) {
|
||||||
@@ -886,7 +881,7 @@ impl LpSession {
|
|||||||
let noise_payload = &payload[2 + psq_len..];
|
let noise_payload = &payload[2 + psq_len..];
|
||||||
|
|
||||||
// Convert X25519 local keys to DecapsulationKey/EncapsulationKey (DHKEM)
|
// Convert X25519 local keys to DecapsulationKey/EncapsulationKey (DHKEM)
|
||||||
let local_private_bytes = &self.local_x25519_private.to_bytes();
|
let local_private_bytes = &self.local_x25519.private_key().to_bytes();
|
||||||
let libcrux_private_key = libcrux_kem::PrivateKey::decode(
|
let libcrux_private_key = libcrux_kem::PrivateKey::decode(
|
||||||
libcrux_kem::Algorithm::X25519,
|
libcrux_kem::Algorithm::X25519,
|
||||||
local_private_bytes,
|
local_private_bytes,
|
||||||
@@ -899,7 +894,7 @@ impl LpSession {
|
|||||||
})?;
|
})?;
|
||||||
let dec_key = DecapsulationKey::X25519(libcrux_private_key);
|
let dec_key = DecapsulationKey::X25519(libcrux_private_key);
|
||||||
|
|
||||||
let local_public_key = self.local_x25519_private.public_key();
|
let local_public_key = self.local_x25519_public();
|
||||||
let local_public_bytes = local_public_key.as_bytes();
|
let local_public_bytes = local_public_key.as_bytes();
|
||||||
let libcrux_public_key = libcrux_kem::PublicKey::decode(
|
let libcrux_public_key = libcrux_kem::PublicKey::decode(
|
||||||
libcrux_kem::Algorithm::X25519,
|
libcrux_kem::Algorithm::X25519,
|
||||||
@@ -917,7 +912,7 @@ impl LpSession {
|
|||||||
let session_context = self.id.to_le_bytes();
|
let session_context = self.id.to_le_bytes();
|
||||||
|
|
||||||
let psq_result = match psq_responder_process_message(
|
let psq_result = match psq_responder_process_message(
|
||||||
&self.local_x25519_private,
|
self.local_x25519.private_key(),
|
||||||
&self.remote_x25519_public,
|
&self.remote_x25519_public,
|
||||||
(&dec_key, &enc_key),
|
(&dec_key, &enc_key),
|
||||||
&self.remote_ed25519_public,
|
&self.remote_ed25519_public,
|
||||||
@@ -1123,7 +1118,7 @@ impl LpSession {
|
|||||||
/// Test-only method to set KKT state to Completed with a mock KEM key.
|
/// Test-only method to set KKT state to Completed with a mock KEM key.
|
||||||
/// This allows tests to bypass KKT exchange and directly test PSQ handshake.
|
/// This allows tests to bypass KKT exchange and directly test PSQ handshake.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn set_kkt_completed_for_test(&self, remote_x25519_pub: &PublicKey) {
|
pub(crate) fn set_kkt_completed_for_test(&self, remote_x25519_pub: &x25519::PublicKey) {
|
||||||
// Convert remote X25519 public key to EncapsulationKey for testing
|
// Convert remote X25519 public key to EncapsulationKey for testing
|
||||||
let remote_kem_bytes = remote_x25519_pub.as_bytes();
|
let remote_kem_bytes = remote_x25519_pub.as_bytes();
|
||||||
let libcrux_public_key =
|
let libcrux_public_key =
|
||||||
@@ -1176,7 +1171,7 @@ impl LpSession {
|
|||||||
let pattern_name = "Noise_KKpsk0_25519_ChaChaPoly_SHA256";
|
let pattern_name = "Noise_KKpsk0_25519_ChaChaPoly_SHA256";
|
||||||
let params = pattern_name.parse()?;
|
let params = pattern_name.parse()?;
|
||||||
|
|
||||||
let local_key_bytes = self.local_x25519_private.to_bytes();
|
let local_key_bytes = self.local_x25519.private_key().to_bytes();
|
||||||
let remote_key_bytes = self.remote_x25519_public.to_bytes();
|
let remote_key_bytes = self.remote_x25519_public.to_bytes();
|
||||||
|
|
||||||
let builder = Builder::new(params)
|
let builder = Builder::new(params)
|
||||||
@@ -1195,22 +1190,12 @@ impl LpSession {
|
|||||||
noise_state: Mutex::new(NoiseProtocol::new(handshake_state)),
|
noise_state: Mutex::new(NoiseProtocol::new(handshake_state)),
|
||||||
is_initiator,
|
is_initiator,
|
||||||
// Copy key material from parent for into_session() conversion
|
// Copy key material from parent for into_session() conversion
|
||||||
local_ed25519_private: ed25519::PrivateKey::from_bytes(
|
local_ed25519: self.local_ed25519.clone(),
|
||||||
&self.local_ed25519_private.to_bytes(),
|
remote_ed25519_public: self.remote_ed25519_public,
|
||||||
)
|
remote_x25519_public: self.remote_x25519_public,
|
||||||
.expect("Valid Ed25519 private key from parent"),
|
|
||||||
local_ed25519_public: ed25519::PublicKey::from_bytes(
|
|
||||||
&self.local_ed25519_public.to_bytes(),
|
|
||||||
)
|
|
||||||
.expect("Valid Ed25519 public key from parent"),
|
|
||||||
remote_ed25519_public: ed25519::PublicKey::from_bytes(
|
|
||||||
&self.remote_ed25519_public.to_bytes(),
|
|
||||||
)
|
|
||||||
.expect("Valid Ed25519 public key from parent"),
|
|
||||||
local_x25519_private: self.local_x25519_private.clone(),
|
|
||||||
remote_x25519_public: self.remote_x25519_public.clone(),
|
|
||||||
pq_shared_secret: PqSharedSecret::new(pq_secret),
|
pq_shared_secret: PqSharedSecret::new(pq_secret),
|
||||||
subsession_psk,
|
subsession_psk,
|
||||||
|
local_x25519: self.local_x25519.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1240,16 +1225,17 @@ pub struct SubsessionHandshake {
|
|||||||
is_initiator: bool,
|
is_initiator: bool,
|
||||||
|
|
||||||
// Key material inherited from parent session for into_session() conversion
|
// Key material inherited from parent session for into_session() conversion
|
||||||
/// Local Ed25519 private key (for PSQ auth if needed)
|
/// Local Ed25519 keys (for PSQ auth if needed)
|
||||||
local_ed25519_private: ed25519::PrivateKey,
|
local_ed25519: Arc<ed25519::KeyPair>,
|
||||||
/// Local Ed25519 public key
|
|
||||||
local_ed25519_public: ed25519::PublicKey,
|
/// Local x25519 keys (Noise static key)
|
||||||
|
local_x25519: Arc<x25519::KeyPair>,
|
||||||
|
|
||||||
/// Remote Ed25519 public key
|
/// Remote Ed25519 public key
|
||||||
remote_ed25519_public: ed25519::PublicKey,
|
remote_ed25519_public: ed25519::PublicKey,
|
||||||
/// Local X25519 private key (Noise static key)
|
|
||||||
local_x25519_private: PrivateKey,
|
|
||||||
/// Remote X25519 public key (Noise static key)
|
/// Remote X25519 public key (Noise static key)
|
||||||
remote_x25519_public: PublicKey,
|
remote_x25519_public: x25519::PublicKey,
|
||||||
/// PQ shared secret inherited from parent (for creating further subsessions)
|
/// PQ shared secret inherited from parent (for creating further subsessions)
|
||||||
pq_shared_secret: PqSharedSecret,
|
pq_shared_secret: PqSharedSecret,
|
||||||
/// Subsession PSK (for deriving outer AEAD key)
|
/// Subsession PSK (for deriving outer AEAD key)
|
||||||
@@ -1342,10 +1328,9 @@ impl SubsessionHandshake {
|
|||||||
sending_counter: AtomicU64::new(0),
|
sending_counter: AtomicU64::new(0),
|
||||||
receiving_counter: Mutex::new(ReceivingKeyCounterValidator::new(0)),
|
receiving_counter: Mutex::new(ReceivingKeyCounterValidator::new(0)),
|
||||||
psk_injected: AtomicBool::new(true), // PSK was in KKpsk0
|
psk_injected: AtomicBool::new(true), // PSK was in KKpsk0
|
||||||
local_ed25519_private: self.local_ed25519_private,
|
local_ed25519: self.local_ed25519,
|
||||||
local_ed25519_public: self.local_ed25519_public,
|
|
||||||
remote_ed25519_public: self.remote_ed25519_public,
|
remote_ed25519_public: self.remote_ed25519_public,
|
||||||
local_x25519_private: self.local_x25519_private,
|
local_x25519: self.local_x25519,
|
||||||
remote_x25519_public: self.remote_x25519_public,
|
remote_x25519_public: self.remote_x25519_public,
|
||||||
salt,
|
salt,
|
||||||
outer_aead_key: Mutex::new(Some(outer_key)),
|
outer_aead_key: Mutex::new(Some(outer_key)),
|
||||||
@@ -1363,18 +1348,19 @@ impl SubsessionHandshake {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{replay::ReplayError, sessions_for_tests};
|
use crate::{replay::ReplayError, sessions_for_tests};
|
||||||
|
use rand::thread_rng;
|
||||||
|
|
||||||
// Helper function to generate keypairs for tests
|
// Helper function to generate keypairs for tests
|
||||||
fn generate_keypair() -> crate::keypair::Keypair {
|
fn generate_keypair() -> x25519::KeyPair {
|
||||||
crate::keypair::Keypair::default()
|
x25519::KeyPair::new(&mut thread_rng())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to create a session with real keys for handshake tests
|
// Helper function to create a session with real keys for handshake tests
|
||||||
fn create_handshake_test_session(
|
fn create_handshake_test_session(
|
||||||
receiver_index: u32,
|
receiver_index: u32,
|
||||||
is_initiator: bool,
|
is_initiator: bool,
|
||||||
local_keys: &crate::keypair::Keypair,
|
local_keys: &x25519::KeyPair,
|
||||||
remote_pub_key: &crate::keypair::PublicKey,
|
remote_pub_key: &x25519::PublicKey,
|
||||||
) -> LpSession {
|
) -> LpSession {
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use nym_crypto::asymmetric::ed25519;
|
||||||
|
|
||||||
@@ -1387,6 +1373,9 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let local_ed25519 = ed25519::KeyPair::from_secret(local_ed25519_seed, 0);
|
let local_ed25519 = ed25519::KeyPair::from_secret(local_ed25519_seed, 0);
|
||||||
|
let local_x25519 = x25519::PrivateKey::from_bytes(local_keys.private_key().as_bytes())
|
||||||
|
.unwrap()
|
||||||
|
.into();
|
||||||
let remote_ed25519 = ed25519::KeyPair::from_secret(remote_ed25519_seed, 1);
|
let remote_ed25519 = ed25519::KeyPair::from_secret(remote_ed25519_seed, 1);
|
||||||
|
|
||||||
let salt = [0u8; 32]; // Test salt
|
let salt = [0u8; 32]; // Test salt
|
||||||
@@ -1395,8 +1384,8 @@ mod tests {
|
|||||||
let session = LpSession::new(
|
let session = LpSession::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
is_initiator,
|
is_initiator,
|
||||||
(local_ed25519.private_key(), local_ed25519.public_key()),
|
Arc::new(local_ed25519),
|
||||||
local_keys.private_key(),
|
Arc::new(local_x25519),
|
||||||
remote_ed25519.public_key(),
|
remote_ed25519.public_key(),
|
||||||
remote_pub_key,
|
remote_pub_key,
|
||||||
&salt,
|
&salt,
|
||||||
@@ -1501,8 +1490,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_prepare_handshake_message_initial_state() {
|
fn test_prepare_handshake_message_initial_state() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
let receiver_index = 12345u32;
|
let receiver_index = 12345u32;
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
@@ -1533,8 +1522,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_handshake_message_first_step() {
|
fn test_process_handshake_message_first_step() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
let receiver_index = 12345u32;
|
let receiver_index = 12345u32;
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
@@ -1579,8 +1568,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_handshake_driver_simulation() {
|
fn test_handshake_driver_simulation() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
12345u32,
|
12345u32,
|
||||||
@@ -1674,8 +1663,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_encrypt_decrypt_after_handshake() {
|
fn test_encrypt_decrypt_after_handshake() {
|
||||||
// --- Setup Handshake ---
|
// --- Setup Handshake ---
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
12345u32,
|
12345u32,
|
||||||
@@ -1743,8 +1732,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encrypt_decrypt_before_handshake() {
|
fn test_encrypt_decrypt_before_handshake() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
12345u32,
|
12345u32,
|
||||||
@@ -1819,8 +1808,8 @@ mod tests {
|
|||||||
/// Test that PSQ runs during handshake and derives a PSK
|
/// Test that PSQ runs during handshake and derives a PSK
|
||||||
#[test]
|
#[test]
|
||||||
fn test_psq_handshake_runs_with_psk_injection() {
|
fn test_psq_handshake_runs_with_psk_injection() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
12345u32,
|
12345u32,
|
||||||
@@ -1896,8 +1885,8 @@ mod tests {
|
|||||||
fn test_x25519_to_kem_conversion() {
|
fn test_x25519_to_kem_conversion() {
|
||||||
use nym_kkt::ciphersuite::EncapsulationKey;
|
use nym_kkt::ciphersuite::EncapsulationKey;
|
||||||
|
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
// Verify we can convert X25519 public key to KEM format (as done in session.rs)
|
// Verify we can convert X25519 public key to KEM format (as done in session.rs)
|
||||||
let x25519_public_bytes = responder_keys.public_key().as_bytes();
|
let x25519_public_bytes = responder_keys.public_key().as_bytes();
|
||||||
@@ -1920,8 +1909,8 @@ mod tests {
|
|||||||
/// Test that PSQ actually derives a different PSK (not using dummy)
|
/// Test that PSQ actually derives a different PSK (not using dummy)
|
||||||
#[test]
|
#[test]
|
||||||
fn test_psq_derived_psk_differs_from_dummy() {
|
fn test_psq_derived_psk_differs_from_dummy() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
// Create sessions - they start with dummy PSK [0u8; 32]
|
// Create sessions - they start with dummy PSK [0u8; 32]
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
@@ -1994,8 +1983,8 @@ mod tests {
|
|||||||
/// Test full end-to-end handshake with PSQ integration
|
/// Test full end-to-end handshake with PSQ integration
|
||||||
#[test]
|
#[test]
|
||||||
fn test_handshake_with_psq_end_to_end() {
|
fn test_handshake_with_psq_end_to_end() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
12345u32,
|
12345u32,
|
||||||
@@ -2080,8 +2069,8 @@ mod tests {
|
|||||||
/// Test that Ed25519 keys are used in PSQ authentication
|
/// Test that Ed25519 keys are used in PSQ authentication
|
||||||
#[test]
|
#[test]
|
||||||
fn test_psq_handshake_uses_ed25519_authentication() {
|
fn test_psq_handshake_uses_ed25519_authentication() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
// Create sessions with explicit Ed25519 keys
|
// Create sessions with explicit Ed25519 keys
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
@@ -2163,8 +2152,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_handshake_abort_on_psq_failure() {
|
fn test_handshake_abort_on_psq_failure() {
|
||||||
// Test that Ed25519 auth failure causes handshake abort
|
// Test that Ed25519 auth failure causes handshake abort
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
// Create sessions with MISMATCHED Ed25519 keys
|
// Create sessions with MISMATCHED Ed25519 keys
|
||||||
// This simulates authentication failure
|
// This simulates authentication failure
|
||||||
@@ -2177,11 +2166,8 @@ mod tests {
|
|||||||
let initiator_session = LpSession::new(
|
let initiator_session = LpSession::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(initiator_ed25519),
|
||||||
initiator_ed25519.private_key(),
|
initiator_keys.clone(),
|
||||||
initiator_ed25519.public_key(),
|
|
||||||
),
|
|
||||||
initiator_keys.private_key(),
|
|
||||||
wrong_ed25519.public_key(), // Responder expects THIS key
|
wrong_ed25519.public_key(), // Responder expects THIS key
|
||||||
responder_keys.public_key(),
|
responder_keys.public_key(),
|
||||||
&salt,
|
&salt,
|
||||||
@@ -2195,11 +2181,8 @@ mod tests {
|
|||||||
let responder_session = LpSession::new(
|
let responder_session = LpSession::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false,
|
false,
|
||||||
(
|
Arc::new(responder_ed25519),
|
||||||
responder_ed25519.private_key(),
|
responder_keys.clone(),
|
||||||
responder_ed25519.public_key(),
|
|
||||||
),
|
|
||||||
responder_keys.private_key(),
|
|
||||||
wrong_ed25519.public_key(), // Expects WRONG key (not initiator's)
|
wrong_ed25519.public_key(), // Expects WRONG key (not initiator's)
|
||||||
initiator_keys.public_key(),
|
initiator_keys.public_key(),
|
||||||
&salt,
|
&salt,
|
||||||
@@ -2232,8 +2215,8 @@ mod tests {
|
|||||||
fn test_psq_invalid_signature() {
|
fn test_psq_invalid_signature() {
|
||||||
// Test Ed25519 signature validation specifically
|
// Test Ed25519 signature validation specifically
|
||||||
// Setup with matching X25519 keys but mismatched Ed25519 keys
|
// Setup with matching X25519 keys but mismatched Ed25519 keys
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
// Initiator uses Ed25519 key [1u8]
|
// Initiator uses Ed25519 key [1u8]
|
||||||
let initiator_ed25519 = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
let initiator_ed25519 = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
||||||
@@ -2248,11 +2231,8 @@ mod tests {
|
|||||||
let initiator_session = LpSession::new(
|
let initiator_session = LpSession::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(initiator_ed25519),
|
||||||
initiator_ed25519.private_key(),
|
initiator_keys.clone(),
|
||||||
initiator_ed25519.public_key(),
|
|
||||||
),
|
|
||||||
initiator_keys.private_key(),
|
|
||||||
wrong_ed25519_public, // This doesn't matter for initiator
|
wrong_ed25519_public, // This doesn't matter for initiator
|
||||||
responder_keys.public_key(),
|
responder_keys.public_key(),
|
||||||
&salt,
|
&salt,
|
||||||
@@ -2266,11 +2246,8 @@ mod tests {
|
|||||||
let responder_session = LpSession::new(
|
let responder_session = LpSession::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false,
|
false,
|
||||||
(
|
Arc::new(responder_ed25519),
|
||||||
responder_ed25519.private_key(),
|
responder_keys.clone(),
|
||||||
responder_ed25519.public_key(),
|
|
||||||
),
|
|
||||||
responder_keys.private_key(),
|
|
||||||
wrong_ed25519_public, // Responder expects WRONG key
|
wrong_ed25519_public, // Responder expects WRONG key
|
||||||
initiator_keys.public_key(),
|
initiator_keys.public_key(),
|
||||||
&salt,
|
&salt,
|
||||||
@@ -2355,8 +2332,8 @@ mod tests {
|
|||||||
// This test verifies the safety mechanism that prevents transport mode operations
|
// This test verifies the safety mechanism that prevents transport mode operations
|
||||||
// from running with the dummy PSK if PSQ injection fails or is skipped.
|
// from running with the dummy PSK if PSQ injection fails or is skipped.
|
||||||
|
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
// Create session but don't complete handshake (no PSK injection will occur)
|
// Create session but don't complete handshake (no PSK injection will occur)
|
||||||
let session = create_handshake_test_session(
|
let session = create_handshake_test_session(
|
||||||
@@ -2404,8 +2381,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_demote_sets_read_only() {
|
fn test_demote_sets_read_only() {
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
let session = create_handshake_test_session(
|
let session = create_handshake_test_session(
|
||||||
12345u32,
|
12345u32,
|
||||||
@@ -2429,8 +2406,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_encrypt_fails_after_demotion() {
|
fn test_encrypt_fails_after_demotion() {
|
||||||
// --- Setup Handshake ---
|
// --- Setup Handshake ---
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
12345u32,
|
12345u32,
|
||||||
@@ -2485,8 +2462,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_decrypt_works_after_demotion() {
|
fn test_decrypt_works_after_demotion() {
|
||||||
// --- Setup Handshake ---
|
// --- Setup Handshake ---
|
||||||
let initiator_keys = generate_keypair();
|
let initiator_keys = Arc::new(generate_keypair());
|
||||||
let responder_keys = generate_keypair();
|
let responder_keys = Arc::new(generate_keypair());
|
||||||
|
|
||||||
let initiator_session = create_handshake_test_session(
|
let initiator_session = create_handshake_test_session(
|
||||||
12345u32,
|
12345u32,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::codec::{parse_lp_packet, serialize_lp_packet};
|
use crate::codec::{parse_lp_packet, serialize_lp_packet};
|
||||||
use crate::keypair::PublicKey;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
LpError,
|
LpError,
|
||||||
message::LpMessage,
|
message::LpMessage,
|
||||||
@@ -9,7 +8,8 @@ mod tests {
|
|||||||
session_manager::SessionManager,
|
session_manager::SessionManager,
|
||||||
};
|
};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
// Function to create a test packet - similar to how it's done in codec.rs tests
|
// Function to create a test packet - similar to how it's done in codec.rs tests
|
||||||
fn create_test_packet(
|
fn create_test_packet(
|
||||||
@@ -21,7 +21,7 @@ mod tests {
|
|||||||
// Create the header
|
// Create the header
|
||||||
let header = LpHeader {
|
let header = LpHeader {
|
||||||
protocol_version,
|
protocol_version,
|
||||||
reserved: 0u16, // reserved
|
reserved: [0u8; 3], // reserved
|
||||||
receiver_idx,
|
receiver_idx,
|
||||||
counter,
|
counter,
|
||||||
};
|
};
|
||||||
@@ -53,6 +53,11 @@ mod tests {
|
|||||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
let ed25519_keypair_a = ed25519::KeyPair::from_secret([1u8; 32], 0);
|
||||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([2u8; 32], 1);
|
let ed25519_keypair_b = ed25519::KeyPair::from_secret([2u8; 32], 1);
|
||||||
|
|
||||||
|
let x25519_keypair_a = ed25519_keypair_a.to_x25519();
|
||||||
|
let x25519_keypair_b = ed25519_keypair_b.to_x25519();
|
||||||
|
|
||||||
|
let ed25519_pubkey_a = *ed25519_keypair_a.public_key();
|
||||||
|
|
||||||
// Derive X25519 keys from Ed25519 (needed for KKT init test)
|
// Derive X25519 keys from Ed25519 (needed for KKT init test)
|
||||||
let x25519_pub_a = ed25519_keypair_a
|
let x25519_pub_a = ed25519_keypair_a
|
||||||
.public_key()
|
.public_key()
|
||||||
@@ -64,9 +69,9 @@ mod tests {
|
|||||||
.expect("Failed to derive X25519 from Ed25519");
|
.expect("Failed to derive X25519 from Ed25519");
|
||||||
|
|
||||||
// Convert to LP keypair types
|
// Convert to LP keypair types
|
||||||
let lp_pub_a = PublicKey::from_bytes(x25519_pub_a.as_bytes())
|
let lp_pub_a = x25519::PublicKey::from_bytes(x25519_pub_a.as_bytes())
|
||||||
.expect("Failed to create PublicKey from bytes");
|
.expect("Failed to create PublicKey from bytes");
|
||||||
let lp_pub_b = PublicKey::from_bytes(x25519_pub_b.as_bytes())
|
let lp_pub_b = x25519::PublicKey::from_bytes(x25519_pub_b.as_bytes())
|
||||||
.expect("Failed to create PublicKey from bytes");
|
.expect("Failed to create PublicKey from bytes");
|
||||||
|
|
||||||
// Use fixed receiver_index for deterministic test
|
// Use fixed receiver_index for deterministic test
|
||||||
@@ -79,11 +84,9 @@ mod tests {
|
|||||||
let peer_a_sm = session_manager_1
|
let peer_a_sm = session_manager_1
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(
|
Arc::new(ed25519_keypair_a),
|
||||||
ed25519_keypair_a.private_key(),
|
|
||||||
ed25519_keypair_a.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_b.public_key(),
|
ed25519_keypair_b.public_key(),
|
||||||
|
x25519_keypair_b.public_key(),
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -92,11 +95,9 @@ mod tests {
|
|||||||
let peer_b_sm = session_manager_2
|
let peer_b_sm = session_manager_2
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(
|
Arc::new(ed25519_keypair_b),
|
||||||
ed25519_keypair_b.private_key(),
|
&ed25519_pubkey_a,
|
||||||
ed25519_keypair_b.public_key(),
|
x25519_keypair_a.public_key(),
|
||||||
),
|
|
||||||
ed25519_keypair_a.public_key(),
|
|
||||||
false,
|
false,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -512,6 +513,11 @@ mod tests {
|
|||||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([3u8; 32], 0);
|
let ed25519_keypair_a = ed25519::KeyPair::from_secret([3u8; 32], 0);
|
||||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([4u8; 32], 1);
|
let ed25519_keypair_b = ed25519::KeyPair::from_secret([4u8; 32], 1);
|
||||||
|
|
||||||
|
let x25519_keypair_a = ed25519_keypair_a.to_x25519();
|
||||||
|
let x25519_keypair_b = ed25519_keypair_b.to_x25519();
|
||||||
|
|
||||||
|
let ed25519_pubkey_a = *ed25519_keypair_a.public_key();
|
||||||
|
|
||||||
// Derive X25519 keys from Ed25519 (same as state machine does internally)
|
// Derive X25519 keys from Ed25519 (same as state machine does internally)
|
||||||
let x25519_pub_a = ed25519_keypair_a
|
let x25519_pub_a = ed25519_keypair_a
|
||||||
.public_key()
|
.public_key()
|
||||||
@@ -523,9 +529,9 @@ mod tests {
|
|||||||
.expect("Failed to derive X25519 from Ed25519");
|
.expect("Failed to derive X25519 from Ed25519");
|
||||||
|
|
||||||
// Convert to LP keypair types
|
// Convert to LP keypair types
|
||||||
let lp_pub_a = PublicKey::from_bytes(x25519_pub_a.as_bytes())
|
let lp_pub_a = x25519::PublicKey::from_bytes(x25519_pub_a.as_bytes())
|
||||||
.expect("Failed to create PublicKey from bytes");
|
.expect("Failed to create PublicKey from bytes");
|
||||||
let lp_pub_b = PublicKey::from_bytes(x25519_pub_b.as_bytes())
|
let lp_pub_b = x25519::PublicKey::from_bytes(x25519_pub_b.as_bytes())
|
||||||
.expect("Failed to create PublicKey from bytes");
|
.expect("Failed to create PublicKey from bytes");
|
||||||
|
|
||||||
// Use fixed receiver_index for test
|
// Use fixed receiver_index for test
|
||||||
@@ -537,11 +543,9 @@ mod tests {
|
|||||||
let peer_a_sm = session_manager_1
|
let peer_a_sm = session_manager_1
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(
|
Arc::new(ed25519_keypair_a),
|
||||||
ed25519_keypair_a.private_key(),
|
|
||||||
ed25519_keypair_a.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_b.public_key(),
|
ed25519_keypair_b.public_key(),
|
||||||
|
x25519_keypair_b.public_key(),
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -549,11 +553,9 @@ mod tests {
|
|||||||
let peer_b_sm = session_manager_2
|
let peer_b_sm = session_manager_2
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(
|
Arc::new(ed25519_keypair_b),
|
||||||
ed25519_keypair_b.private_key(),
|
&ed25519_pubkey_a,
|
||||||
ed25519_keypair_b.public_key(),
|
x25519_keypair_a.public_key(),
|
||||||
),
|
|
||||||
ed25519_keypair_a.public_key(),
|
|
||||||
false,
|
false,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -720,16 +722,22 @@ mod tests {
|
|||||||
let session_manager = SessionManager::new();
|
let session_manager = SessionManager::new();
|
||||||
|
|
||||||
// Generate Ed25519 keypair for PSQ authentication
|
// Generate Ed25519 keypair for PSQ authentication
|
||||||
let ed25519_keypair = ed25519::KeyPair::from_secret([5u8; 32], 0);
|
let ed25519_keypair_a = ed25519::KeyPair::from_secret([5u8; 32], 0);
|
||||||
|
let ed25519_keypair_b = ed25519::KeyPair::from_secret([6u8; 32], 0);
|
||||||
|
|
||||||
|
let x25519_keypair_a = ed25519_keypair_a.to_x25519();
|
||||||
|
let x25519_keypair_b = ed25519_keypair_b.to_x25519();
|
||||||
|
|
||||||
// Derive X25519 key from Ed25519 (same as state machine does internally)
|
// Derive X25519 key from Ed25519 (same as state machine does internally)
|
||||||
let x25519_pub = ed25519_keypair
|
let x25519_pub = ed25519_keypair_a
|
||||||
.public_key()
|
.public_key()
|
||||||
.to_x25519()
|
.to_x25519()
|
||||||
.expect("Failed to derive X25519 from Ed25519");
|
.expect("Failed to derive X25519 from Ed25519");
|
||||||
|
|
||||||
|
let keypair_a = Arc::new(ed25519_keypair_a);
|
||||||
|
|
||||||
// Convert to LP keypair type (still needed for init_kkt_for_test below if used)
|
// Convert to LP keypair type (still needed for init_kkt_for_test below if used)
|
||||||
let _lp_pub = PublicKey::from_bytes(x25519_pub.as_bytes())
|
let _lp_pub = x25519::PublicKey::from_bytes(x25519_pub.as_bytes())
|
||||||
.expect("Failed to create PublicKey from bytes");
|
.expect("Failed to create PublicKey from bytes");
|
||||||
|
|
||||||
// Use fixed receiver_index for test
|
// Use fixed receiver_index for test
|
||||||
@@ -742,8 +750,9 @@ mod tests {
|
|||||||
let _session = session_manager
|
let _session = session_manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
keypair_a.clone(),
|
||||||
ed25519_keypair.public_key(),
|
ed25519_keypair_b.public_key(),
|
||||||
|
x25519_keypair_b.public_key(),
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -765,8 +774,9 @@ mod tests {
|
|||||||
let _temp_session = session_manager
|
let _temp_session = session_manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index_temp,
|
receiver_index_temp,
|
||||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
keypair_a.clone(),
|
||||||
ed25519_keypair.public_key(),
|
ed25519_keypair_b.public_key(),
|
||||||
|
x25519_keypair_a.public_key(),
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -849,6 +859,12 @@ mod tests {
|
|||||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([6u8; 32], 0);
|
let ed25519_keypair_a = ed25519::KeyPair::from_secret([6u8; 32], 0);
|
||||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([7u8; 32], 1);
|
let ed25519_keypair_b = ed25519::KeyPair::from_secret([7u8; 32], 1);
|
||||||
|
|
||||||
|
let x25519_keypair_a = ed25519_keypair_a.to_x25519();
|
||||||
|
let x25519_keypair_b = ed25519_keypair_b.to_x25519();
|
||||||
|
|
||||||
|
let pubkey_a = *ed25519_keypair_a.public_key();
|
||||||
|
let pubkey_b = *ed25519_keypair_b.public_key();
|
||||||
|
|
||||||
// Use fixed receiver_index for test
|
// Use fixed receiver_index for test
|
||||||
let receiver_index: u32 = 100005;
|
let receiver_index: u32 = 100005;
|
||||||
|
|
||||||
@@ -860,11 +876,9 @@ mod tests {
|
|||||||
session_manager_1
|
session_manager_1
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(
|
Arc::new(ed25519_keypair_a),
|
||||||
ed25519_keypair_a.private_key(),
|
&pubkey_b,
|
||||||
ed25519_keypair_a.public_key()
|
x25519_keypair_b.public_key(),
|
||||||
),
|
|
||||||
ed25519_keypair_b.public_key(),
|
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
) // Initiator
|
) // Initiator
|
||||||
@@ -874,11 +888,9 @@ mod tests {
|
|||||||
session_manager_2
|
session_manager_2
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(
|
Arc::new(ed25519_keypair_b),
|
||||||
ed25519_keypair_b.private_key(),
|
&pubkey_a,
|
||||||
ed25519_keypair_b.public_key()
|
x25519_keypair_a.public_key(),
|
||||||
),
|
|
||||||
ed25519_keypair_a.public_key(),
|
|
||||||
false,
|
false,
|
||||||
&salt,
|
&salt,
|
||||||
) // Responder
|
) // Responder
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
//! creation, retrieval, and storage of sessions.
|
//! creation, retrieval, and storage of sessions.
|
||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::noise_protocol::ReadResult;
|
use crate::noise_protocol::ReadResult;
|
||||||
use crate::state_machine::{LpAction, LpInput, LpState, LpStateBare};
|
use crate::state_machine::{LpAction, LpInput, LpState, LpStateBare};
|
||||||
@@ -169,8 +170,9 @@ impl SessionManager {
|
|||||||
pub fn create_session_state_machine(
|
pub fn create_session_state_machine(
|
||||||
&self,
|
&self,
|
||||||
receiver_index: u32,
|
receiver_index: u32,
|
||||||
local_ed25519_keypair: (&ed25519::PrivateKey, &ed25519::PublicKey),
|
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||||
remote_ed25519_key: &ed25519::PublicKey,
|
remote_ed25519_key: &ed25519::PublicKey,
|
||||||
|
remote_x25519_key: &x25519::PublicKey,
|
||||||
is_initiator: bool,
|
is_initiator: bool,
|
||||||
salt: &[u8; 32],
|
salt: &[u8; 32],
|
||||||
) -> Result<u32, LpError> {
|
) -> Result<u32, LpError> {
|
||||||
@@ -179,6 +181,7 @@ impl SessionManager {
|
|||||||
is_initiator,
|
is_initiator,
|
||||||
local_ed25519_keypair,
|
local_ed25519_keypair,
|
||||||
remote_ed25519_key,
|
remote_ed25519_key,
|
||||||
|
remote_x25519_key,
|
||||||
salt,
|
salt,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -199,7 +202,7 @@ impl SessionManager {
|
|||||||
pub fn init_kkt_for_test(
|
pub fn init_kkt_for_test(
|
||||||
&self,
|
&self,
|
||||||
lp_id: u32,
|
lp_id: u32,
|
||||||
remote_x25519_pub: &crate::keypair::PublicKey,
|
remote_x25519_pub: &x25519::PublicKey,
|
||||||
) -> Result<(), LpError> {
|
) -> Result<(), LpError> {
|
||||||
self.with_state_machine(lp_id, |sm| {
|
self.with_state_machine(lp_id, |sm| {
|
||||||
sm.session()?.set_kkt_completed_for_test(remote_x25519_pub);
|
sm.session()?.set_kkt_completed_for_test(remote_x25519_pub);
|
||||||
@@ -217,14 +220,19 @@ mod tests {
|
|||||||
fn test_session_manager_get() {
|
fn test_session_manager_get() {
|
||||||
let manager = SessionManager::new();
|
let manager = SessionManager::new();
|
||||||
let ed25519_keypair = ed25519::KeyPair::from_secret([10u8; 32], 0);
|
let ed25519_keypair = ed25519::KeyPair::from_secret([10u8; 32], 0);
|
||||||
|
let ed25519_keypair2 = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
||||||
|
|
||||||
|
let x25519_keypair2 = ed25519_keypair2.to_x25519();
|
||||||
|
|
||||||
let salt = [47u8; 32];
|
let salt = [47u8; 32];
|
||||||
let receiver_index: u32 = 1001;
|
let receiver_index: u32 = 1001;
|
||||||
|
|
||||||
let sm_1_id = manager
|
let sm_1_id = manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
Arc::new(ed25519_keypair),
|
||||||
ed25519_keypair.public_key(),
|
ed25519_keypair2.public_key(),
|
||||||
|
x25519_keypair2.public_key(),
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -241,14 +249,19 @@ mod tests {
|
|||||||
fn test_session_manager_remove() {
|
fn test_session_manager_remove() {
|
||||||
let manager = SessionManager::new();
|
let manager = SessionManager::new();
|
||||||
let ed25519_keypair = ed25519::KeyPair::from_secret([11u8; 32], 0);
|
let ed25519_keypair = ed25519::KeyPair::from_secret([11u8; 32], 0);
|
||||||
|
let ed25519_keypair2 = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
||||||
|
|
||||||
|
let x25519_keypair2 = ed25519_keypair2.to_x25519();
|
||||||
|
|
||||||
let salt = [48u8; 32];
|
let salt = [48u8; 32];
|
||||||
let receiver_index: u32 = 2002;
|
let receiver_index: u32 = 2002;
|
||||||
|
|
||||||
let sm_1_id = manager
|
let sm_1_id = manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
Arc::new(ed25519_keypair),
|
||||||
ed25519_keypair.public_key(),
|
ed25519_keypair2.public_key(),
|
||||||
|
x25519_keypair2.public_key(),
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -270,14 +283,20 @@ mod tests {
|
|||||||
let ed25519_keypair_3 = ed25519::KeyPair::from_secret([14u8; 32], 2);
|
let ed25519_keypair_3 = ed25519::KeyPair::from_secret([14u8; 32], 2);
|
||||||
let salt = [49u8; 32];
|
let salt = [49u8; 32];
|
||||||
|
|
||||||
|
let pubkey1 = *ed25519_keypair_1.public_key();
|
||||||
|
let pubkey2 = *ed25519_keypair_2.public_key();
|
||||||
|
let pubkey3 = *ed25519_keypair_3.public_key();
|
||||||
|
|
||||||
|
let xpubkey1 = *ed25519_keypair_1.to_x25519().public_key();
|
||||||
|
let xpubkey2 = *ed25519_keypair_2.to_x25519().public_key();
|
||||||
|
let xpubkey3 = *ed25519_keypair_3.to_x25519().public_key();
|
||||||
|
|
||||||
let sm_1 = manager
|
let sm_1 = manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
3001,
|
3001,
|
||||||
(
|
Arc::new(ed25519_keypair_1),
|
||||||
ed25519_keypair_1.private_key(),
|
&pubkey2,
|
||||||
ed25519_keypair_1.public_key(),
|
&xpubkey2,
|
||||||
),
|
|
||||||
ed25519_keypair_1.public_key(),
|
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -286,11 +305,9 @@ mod tests {
|
|||||||
let sm_2 = manager
|
let sm_2 = manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
3002,
|
3002,
|
||||||
(
|
Arc::new(ed25519_keypair_2),
|
||||||
ed25519_keypair_2.private_key(),
|
&pubkey3,
|
||||||
ed25519_keypair_2.public_key(),
|
&xpubkey3,
|
||||||
),
|
|
||||||
ed25519_keypair_2.public_key(),
|
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -299,11 +316,9 @@ mod tests {
|
|||||||
let sm_3 = manager
|
let sm_3 = manager
|
||||||
.create_session_state_machine(
|
.create_session_state_machine(
|
||||||
3003,
|
3003,
|
||||||
(
|
Arc::new(ed25519_keypair_3),
|
||||||
ed25519_keypair_3.private_key(),
|
&pubkey1,
|
||||||
ed25519_keypair_3.public_key(),
|
&xpubkey1,
|
||||||
),
|
|
||||||
ed25519_keypair_3.public_key(),
|
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
@@ -324,13 +339,17 @@ mod tests {
|
|||||||
fn test_session_manager_create_session() {
|
fn test_session_manager_create_session() {
|
||||||
let manager = SessionManager::new();
|
let manager = SessionManager::new();
|
||||||
let ed25519_keypair = ed25519::KeyPair::from_secret([15u8; 32], 0);
|
let ed25519_keypair = ed25519::KeyPair::from_secret([15u8; 32], 0);
|
||||||
|
let ed25519_keypair2 = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
||||||
let salt = [50u8; 32];
|
let salt = [50u8; 32];
|
||||||
let receiver_index: u32 = 4004;
|
let receiver_index: u32 = 4004;
|
||||||
|
|
||||||
|
let x25519_keypair2 = ed25519_keypair2.to_x25519();
|
||||||
|
|
||||||
let sm = manager.create_session_state_machine(
|
let sm = manager.create_session_state_machine(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
(ed25519_keypair.private_key(), ed25519_keypair.public_key()),
|
Arc::new(ed25519_keypair),
|
||||||
ed25519_keypair.public_key(),
|
ed25519_keypair2.public_key(),
|
||||||
|
x25519_keypair2.public_key(),
|
||||||
true,
|
true,
|
||||||
&salt,
|
&salt,
|
||||||
);
|
);
|
||||||
|
|||||||
+270
-281
@@ -15,15 +15,15 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
LpError,
|
LpError,
|
||||||
keypair::{Keypair, PrivateKey as LpPrivateKey, PublicKey as LpPublicKey},
|
|
||||||
message::{LpMessage, SubsessionKK1Data, SubsessionKK2Data, SubsessionReadyData},
|
message::{LpMessage, SubsessionKK1Data, SubsessionKK2Data, SubsessionReadyData},
|
||||||
noise_protocol::NoiseError,
|
noise_protocol::NoiseError,
|
||||||
packet::LpPacket,
|
packet::LpPacket,
|
||||||
session::{LpSession, SubsessionHandshake},
|
session::{LpSession, SubsessionHandshake},
|
||||||
};
|
};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use nym_crypto::asymmetric::ed25519;
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::sync::Arc;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
/// Represents the possible states of the Lewes Protocol connection.
|
/// Represents the possible states of the Lewes Protocol connection.
|
||||||
@@ -90,6 +90,7 @@ impl From<&LpState> for LpStateBare {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents inputs that drive the state machine transitions.
|
/// Represents inputs that drive the state machine transitions.
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum LpInput {
|
pub enum LpInput {
|
||||||
/// Explicitly trigger the start of the handshake (optional, could be implicit on creation)
|
/// Explicitly trigger the start of the handshake (optional, could be implicit on creation)
|
||||||
@@ -189,7 +190,8 @@ impl LpStateMachine {
|
|||||||
/// * `is_initiator` - Whether this side initiates the handshake
|
/// * `is_initiator` - Whether this side initiates the handshake
|
||||||
/// * `local_ed25519_keypair` - Ed25519 keypair for PSQ authentication and X25519 derivation
|
/// * `local_ed25519_keypair` - Ed25519 keypair for PSQ authentication and X25519 derivation
|
||||||
/// (from client identity key or gateway signing key)
|
/// (from client identity key or gateway signing key)
|
||||||
/// * `remote_ed25519_key` - Peer's Ed25519 public key for PSQ authentication and X25519 derivation
|
/// * `remote_ed25519_key` - Peer's Ed25519 public key for PSQ authentication
|
||||||
|
/// * `remote_x25519_key` - Peer's x25519 public key for Noise protocol and DHKEM
|
||||||
/// * `salt` - Fresh salt for PSK derivation (must be unique per session)
|
/// * `salt` - Fresh salt for PSK derivation (must be unique per session)
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@@ -199,8 +201,9 @@ impl LpStateMachine {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
receiver_index: u32,
|
receiver_index: u32,
|
||||||
is_initiator: bool,
|
is_initiator: bool,
|
||||||
local_ed25519_keypair: (&ed25519::PrivateKey, &ed25519::PublicKey),
|
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||||
remote_ed25519_key: &ed25519::PublicKey,
|
remote_ed25519_key: &ed25519::PublicKey,
|
||||||
|
remote_x25519_key: &x25519::PublicKey,
|
||||||
salt: &[u8; 32],
|
salt: &[u8; 32],
|
||||||
) -> Result<Self, LpError> {
|
) -> Result<Self, LpError> {
|
||||||
// We use standard RFC 7748 conversion to derive X25519 keys from Ed25519 identity keys.
|
// We use standard RFC 7748 conversion to derive X25519 keys from Ed25519 identity keys.
|
||||||
@@ -213,23 +216,7 @@ impl LpStateMachine {
|
|||||||
// - PSQ ECDH baseline security (pre-quantum)
|
// - PSQ ECDH baseline security (pre-quantum)
|
||||||
|
|
||||||
// Convert Ed25519 keys to X25519 for Noise protocol
|
// Convert Ed25519 keys to X25519 for Noise protocol
|
||||||
let local_x25519_private = local_ed25519_keypair.0.to_x25519();
|
let local_x25519 = Arc::new(local_ed25519_keypair.to_x25519());
|
||||||
let local_x25519_public = local_ed25519_keypair
|
|
||||||
.1
|
|
||||||
.to_x25519()
|
|
||||||
.map_err(LpError::Ed25519RecoveryError)?;
|
|
||||||
|
|
||||||
let remote_x25519_public = remote_ed25519_key
|
|
||||||
.to_x25519()
|
|
||||||
.map_err(LpError::Ed25519RecoveryError)?;
|
|
||||||
|
|
||||||
// Convert nym_crypto X25519 types to nym_lp keypair types
|
|
||||||
let lp_private = LpPrivateKey::from_bytes(local_x25519_private.as_bytes());
|
|
||||||
let lp_public = LpPublicKey::from_bytes(local_x25519_public.as_bytes())?;
|
|
||||||
let lp_remote_public = LpPublicKey::from_bytes(remote_x25519_public.as_bytes())?;
|
|
||||||
|
|
||||||
// Create X25519 keypair for Noise
|
|
||||||
let local_x25519_keypair = Keypair::from_keys(lp_private, lp_public);
|
|
||||||
|
|
||||||
// Create the session with both Ed25519 (for PSQ auth) and derived X25519 keys (for Noise)
|
// Create the session with both Ed25519 (for PSQ auth) and derived X25519 keys (for Noise)
|
||||||
// receiver_index is client-proposed, passed through directly
|
// receiver_index is client-proposed, passed through directly
|
||||||
@@ -237,9 +224,9 @@ impl LpStateMachine {
|
|||||||
receiver_index,
|
receiver_index,
|
||||||
is_initiator,
|
is_initiator,
|
||||||
local_ed25519_keypair,
|
local_ed25519_keypair,
|
||||||
local_x25519_keypair.private_key(),
|
local_x25519,
|
||||||
remote_ed25519_key,
|
remote_ed25519_key,
|
||||||
&lp_remote_public,
|
remote_x25519_key,
|
||||||
salt,
|
salt,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -439,75 +426,75 @@ impl LpStateMachine {
|
|||||||
// --- Inline handle_handshake_packet logic ---
|
// --- Inline handle_handshake_packet logic ---
|
||||||
// 1. Check replay protection *before* processing
|
// 1. Check replay protection *before* processing
|
||||||
if let Err(e) = session.receiving_counter_quick_check(packet.header.counter) {
|
if let Err(e) = session.receiving_counter_quick_check(packet.header.counter) {
|
||||||
let _reason = e.to_string();
|
let _reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Handshaking { session }
|
LpState::Handshaking { session }
|
||||||
// LpState::Closed { reason }
|
// LpState::Closed { reason }
|
||||||
} else {
|
} else {
|
||||||
// 2. Process the handshake message
|
// 2. Process the handshake message
|
||||||
match session.process_handshake_message(&packet.message) {
|
match session.process_handshake_message(&packet.message) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// 3. Mark counter as received *after* successful processing
|
// 3. Mark counter as received *after* successful processing
|
||||||
if let Err(e) = session.receiving_counter_mark(packet.header.counter) {
|
if let Err(e) = session.receiving_counter_mark(packet.header.counter) {
|
||||||
let _reason = e.to_string();
|
let _reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
// LpState::Closed { reason }
|
// LpState::Closed { reason }
|
||||||
LpState::Handshaking { session }
|
LpState::Handshaking { session }
|
||||||
} else {
|
} else {
|
||||||
// 4. First check if we need to send a handshake message (before checking completion)
|
// 4. First check if we need to send a handshake message (before checking completion)
|
||||||
match session.prepare_handshake_message() {
|
match session.prepare_handshake_message() {
|
||||||
Some(Ok(message)) => {
|
Some(Ok(message)) => {
|
||||||
match session.next_packet(message) {
|
match session.next_packet(message) {
|
||||||
Ok(response_packet) => {
|
Ok(response_packet) => {
|
||||||
result_action = Some(Ok(LpAction::SendPacket(response_packet)));
|
result_action = Some(Ok(LpAction::SendPacket(response_packet)));
|
||||||
// Check if handshake became complete after preparing message
|
// Check if handshake became complete after preparing message
|
||||||
if session.is_handshake_complete() {
|
if session.is_handshake_complete() {
|
||||||
LpState::Transport { session } // Transition to Transport
|
LpState::Transport { session } // Transition to Transport
|
||||||
} else {
|
} else {
|
||||||
LpState::Handshaking { session } // Remain Handshaking
|
LpState::Handshaking { session } // Remain Handshaking
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let reason = e.to_string();
|
let reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
let reason = e.to_string();
|
let reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// 5. No message to send - check if handshake is complete
|
// 5. No message to send - check if handshake is complete
|
||||||
if session.is_handshake_complete() {
|
if session.is_handshake_complete() {
|
||||||
result_action = Some(Ok(LpAction::HandshakeComplete));
|
result_action = Some(Ok(LpAction::HandshakeComplete));
|
||||||
LpState::Transport { session } // Transition to Transport
|
LpState::Transport { session } // Transition to Transport
|
||||||
} else {
|
} else {
|
||||||
// Handshake stalled unexpectedly
|
// Handshake stalled unexpectedly
|
||||||
let err = LpError::NoiseError(NoiseError::Other(
|
let err = LpError::NoiseError(NoiseError::Other(
|
||||||
"Handshake stalled unexpectedly".to_string(),
|
"Handshake stalled unexpectedly".to_string(),
|
||||||
));
|
));
|
||||||
let reason = err.to_string();
|
let reason = err.to_string();
|
||||||
result_action = Some(Err(err));
|
result_action = Some(Err(err));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => { // Error from process_handshake_message
|
Err(e) => { // Error from process_handshake_message
|
||||||
let reason = e.to_string();
|
let reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// --- End inline handle_handshake_packet logic ---
|
// --- End inline handle_handshake_packet logic ---
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Reject SendData during handshake
|
// Reject SendData during handshake
|
||||||
(LpState::Handshaking { session }, LpInput::SendData(_)) => { // Keep session if returning to this state
|
(LpState::Handshaking { session }, LpInput::SendData(_)) => { // Keep session if returning to this state
|
||||||
result_action = Some(Err(LpError::InvalidStateTransition {
|
result_action = Some(Err(LpError::InvalidStateTransition {
|
||||||
state: "Handshaking".to_string(),
|
state: "Handshaking".to_string(),
|
||||||
@@ -522,114 +509,114 @@ impl LpStateMachine {
|
|||||||
state: "Handshaking".to_string(),
|
state: "Handshaking".to_string(),
|
||||||
input: "StartHandshake".to_string(),
|
input: "StartHandshake".to_string(),
|
||||||
}));
|
}));
|
||||||
// Invalid input, remain in Handshaking state
|
// Invalid input, remain in Handshaking state
|
||||||
LpState::Handshaking { session }
|
LpState::Handshaking { session }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Transport State ---
|
// --- Transport State ---
|
||||||
(LpState::Transport { session }, LpInput::ReceivePacket(packet)) => {
|
(LpState::Transport { session }, LpInput::ReceivePacket(packet)) => {
|
||||||
// Check if packet lp_id matches our session
|
// Check if packet lp_id matches our session
|
||||||
if packet.header.receiver_idx() != session.id() {
|
if packet.header.receiver_idx() != session.id() {
|
||||||
result_action = Some(Err(LpError::UnknownSessionId(packet.header.receiver_idx())));
|
result_action = Some(Err(LpError::UnknownSessionId(packet.header.receiver_idx())));
|
||||||
LpState::Transport { session }
|
LpState::Transport { session }
|
||||||
} else {
|
} else {
|
||||||
// Check message type - handle subsession initiation from peer
|
// Check message type - handle subsession initiation from peer
|
||||||
match &packet.message {
|
match &packet.message {
|
||||||
// Peer initiated subsession - we become responder
|
// Peer initiated subsession - we become responder
|
||||||
LpMessage::SubsessionKK1(kk1_data) => {
|
LpMessage::SubsessionKK1(kk1_data) => {
|
||||||
// Create subsession as responder
|
// Create subsession as responder
|
||||||
let subsession_index = session.next_subsession_index();
|
let subsession_index = session.next_subsession_index();
|
||||||
match session.create_subsession(subsession_index, false) {
|
match session.create_subsession(subsession_index, false) {
|
||||||
Ok(subsession) => {
|
Ok(subsession) => {
|
||||||
// Process KK1
|
// Process KK1
|
||||||
match subsession.process_message(&kk1_data.payload) {
|
match subsession.process_message(&kk1_data.payload) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Prepare KK2 response
|
// Prepare KK2 response
|
||||||
match subsession.prepare_message() {
|
match subsession.prepare_message() {
|
||||||
Ok(kk2_payload) => {
|
Ok(kk2_payload) => {
|
||||||
let kk2_msg = LpMessage::SubsessionKK2(SubsessionKK2Data { payload: kk2_payload });
|
let kk2_msg = LpMessage::SubsessionKK2(SubsessionKK2Data { payload: kk2_payload });
|
||||||
match session.next_packet(kk2_msg) {
|
match session.next_packet(kk2_msg) {
|
||||||
Ok(response_packet) => {
|
Ok(response_packet) => {
|
||||||
result_action = Some(Ok(LpAction::SendPacket(response_packet)));
|
result_action = Some(Ok(LpAction::SendPacket(response_packet)));
|
||||||
// Stay in SubsessionHandshaking, wait for SubsessionReady
|
// Stay in SubsessionHandshaking, wait for SubsessionReady
|
||||||
LpState::SubsessionHandshaking { session, subsession: Box::new(subsession) }
|
LpState::SubsessionHandshaking { session, subsession: Box::new(subsession) }
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let reason = e.to_string();
|
let reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let reason = e.to_string();
|
let reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let reason = e.to_string();
|
let reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let reason = e.to_string();
|
let reason = e.to_string();
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Normal encrypted data
|
// Normal encrypted data
|
||||||
LpMessage::EncryptedData(_) => {
|
LpMessage::EncryptedData(_) => {
|
||||||
// 1. Check replay protection
|
// 1. Check replay protection
|
||||||
if let Err(e) = session.receiving_counter_quick_check(packet.header.counter) {
|
if let Err(e) = session.receiving_counter_quick_check(packet.header.counter) {
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Transport { session }
|
LpState::Transport { session }
|
||||||
} else {
|
} else {
|
||||||
// 2. Decrypt data
|
// 2. Decrypt data
|
||||||
match session.decrypt_data(&packet.message) {
|
match session.decrypt_data(&packet.message) {
|
||||||
Ok(plaintext) => {
|
Ok(plaintext) => {
|
||||||
// 3. Mark counter as received
|
// 3. Mark counter as received
|
||||||
if let Err(e) = session.receiving_counter_mark(packet.header.counter) {
|
if let Err(e) = session.receiving_counter_mark(packet.header.counter) {
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::Transport { session }
|
LpState::Transport { session }
|
||||||
} else {
|
} else {
|
||||||
// 4. Deliver data
|
// 4. Deliver data
|
||||||
result_action = Some(Ok(LpAction::DeliverData(BytesMut::from(plaintext.as_slice()))));
|
result_action = Some(Ok(LpAction::DeliverData(BytesMut::from(plaintext.as_slice()))));
|
||||||
LpState::Transport { session }
|
LpState::Transport { session }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let reason = e.to_string();
|
let reason = e.to_string();
|
||||||
result_action = Some(Err(e.into()));
|
result_action = Some(Err(e.into()));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Stale abort in Transport state - race already resolved.
|
// Stale abort in Transport state - race already resolved.
|
||||||
// This can happen if abort arrives after loser already returned to Transport
|
// This can happen if abort arrives after loser already returned to Transport
|
||||||
// via KK1 processing (loser detected local < remote and became responder).
|
// via KK1 processing (loser detected local < remote and became responder).
|
||||||
// The winner's abort message arrived late. Silently ignore.
|
// The winner's abort message arrived late. Silently ignore.
|
||||||
LpMessage::SubsessionAbort => {
|
LpMessage::SubsessionAbort => {
|
||||||
debug!("Ignoring stale SubsessionAbort in Transport state");
|
debug!("Ignoring stale SubsessionAbort in Transport state");
|
||||||
result_action = None;
|
result_action = None;
|
||||||
LpState::Transport { session }
|
LpState::Transport { session }
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Unexpected message type in Transport state
|
// Unexpected message type in Transport state
|
||||||
let err = LpError::InvalidStateTransition {
|
let err = LpError::InvalidStateTransition {
|
||||||
state: "Transport".to_string(),
|
state: "Transport".to_string(),
|
||||||
input: format!("Unexpected message type: {}", packet.message),
|
input: format!("Unexpected message type: {}", packet.message),
|
||||||
};
|
};
|
||||||
result_action = Some(Err(err));
|
result_action = Some(Err(err));
|
||||||
LpState::Transport { session }
|
LpState::Transport { session }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(LpState::Transport { session }, LpInput::SendData(data)) => {
|
(LpState::Transport { session }, LpInput::SendData(data)) => {
|
||||||
// Encrypt and send application data
|
// Encrypt and send application data
|
||||||
@@ -641,17 +628,17 @@ impl LpStateMachine {
|
|||||||
result_action = Some(Err(e.into()));
|
result_action = Some(Err(e.into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remain in transport state
|
// Remain in transport state
|
||||||
LpState::Transport { session }
|
LpState::Transport { session }
|
||||||
}
|
}
|
||||||
// Reject StartHandshake if already in transport
|
// Reject StartHandshake if already in transport
|
||||||
(LpState::Transport { session }, LpInput::StartHandshake) => { // Keep session
|
(LpState::Transport { session }, LpInput::StartHandshake) => { // Keep session
|
||||||
result_action = Some(Err(LpError::InvalidStateTransition {
|
result_action = Some(Err(LpError::InvalidStateTransition {
|
||||||
state: "Transport".to_string(),
|
state: "Transport".to_string(),
|
||||||
input: "StartHandshake".to_string(),
|
input: "StartHandshake".to_string(),
|
||||||
}));
|
}));
|
||||||
// Invalid input, remain in Transport state
|
// Invalid input, remain in Transport state
|
||||||
LpState::Transport { session }
|
LpState::Transport { session }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Transport + InitiateSubsession → SubsessionHandshaking ---
|
// --- Transport + InitiateSubsession → SubsessionHandshaking ---
|
||||||
@@ -970,25 +957,25 @@ impl LpStateMachine {
|
|||||||
result_action = Some(Err(LpError::UnknownSessionId(packet.header.receiver_idx())));
|
result_action = Some(Err(LpError::UnknownSessionId(packet.header.receiver_idx())));
|
||||||
LpState::ReadOnlyTransport { session }
|
LpState::ReadOnlyTransport { session }
|
||||||
} else if let Err(e) = session.receiving_counter_quick_check(packet.header.counter) {
|
} else if let Err(e) = session.receiving_counter_quick_check(packet.header.counter) {
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::ReadOnlyTransport { session }
|
LpState::ReadOnlyTransport { session }
|
||||||
} else {
|
} else {
|
||||||
match session.decrypt_data(&packet.message) {
|
match session.decrypt_data(&packet.message) {
|
||||||
Ok(plaintext) => {
|
Ok(plaintext) => {
|
||||||
if let Err(e) = session.receiving_counter_mark(packet.header.counter) {
|
if let Err(e) = session.receiving_counter_mark(packet.header.counter) {
|
||||||
result_action = Some(Err(e));
|
result_action = Some(Err(e));
|
||||||
LpState::ReadOnlyTransport { session }
|
LpState::ReadOnlyTransport { session }
|
||||||
} else {
|
} else {
|
||||||
result_action = Some(Ok(LpAction::DeliverData(BytesMut::from(plaintext.as_slice()))));
|
result_action = Some(Ok(LpAction::DeliverData(BytesMut::from(plaintext.as_slice()))));
|
||||||
LpState::ReadOnlyTransport { session }
|
LpState::ReadOnlyTransport { session }
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let reason = e.to_string();
|
|
||||||
result_action = Some(Err(e.into()));
|
|
||||||
LpState::Closed { reason }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let reason = e.to_string();
|
||||||
|
result_action = Some(Err(e.into()));
|
||||||
|
LpState::Closed { reason }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1026,8 +1013,8 @@ impl LpStateMachine {
|
|||||||
LpInput::Close,
|
LpInput::Close,
|
||||||
) => {
|
) => {
|
||||||
result_action = Some(Ok(LpAction::ConnectionClosed));
|
result_action = Some(Ok(LpAction::ConnectionClosed));
|
||||||
// Transition to Closed state
|
// Transition to Closed state
|
||||||
LpState::Closed { reason: "Closed by user".to_string() }
|
LpState::Closed { reason: "Closed by user".to_string() }
|
||||||
}
|
}
|
||||||
// Ignore Close if already Closed
|
// Ignore Close if already Closed
|
||||||
(closed_state @ LpState::Closed { .. }, LpInput::Close) => {
|
(closed_state @ LpState::Closed { .. }, LpInput::Close) => {
|
||||||
@@ -1040,36 +1027,36 @@ impl LpStateMachine {
|
|||||||
// result_action = Some(Err(LpError::LpSessionClosed));
|
// result_action = Some(Err(LpError::LpSessionClosed));
|
||||||
// closed_state
|
// closed_state
|
||||||
// }
|
// }
|
||||||
// Ignore ReceivePacket if Closed
|
// Ignore ReceivePacket if Closed
|
||||||
(closed_state @ LpState::Closed { .. }, LpInput::ReceivePacket(_)) => {
|
(closed_state @ LpState::Closed { .. }, LpInput::ReceivePacket(_)) => {
|
||||||
result_action = Some(Err(LpError::LpSessionClosed));
|
result_action = Some(Err(LpError::LpSessionClosed));
|
||||||
closed_state
|
closed_state
|
||||||
}
|
}
|
||||||
// Ignore SendData if Closed
|
// Ignore SendData if Closed
|
||||||
(closed_state @ LpState::Closed { .. }, LpInput::SendData(_)) => {
|
(closed_state @ LpState::Closed { .. }, LpInput::SendData(_)) => {
|
||||||
result_action = Some(Err(LpError::LpSessionClosed));
|
result_action = Some(Err(LpError::LpSessionClosed));
|
||||||
closed_state
|
closed_state
|
||||||
}
|
}
|
||||||
// Processing state should not be matched directly if using replace
|
// Processing state should not be matched directly if using replace
|
||||||
(LpState::Processing, _) => {
|
(LpState::Processing, _) => {
|
||||||
// This case should ideally be unreachable if placeholder logic is correct
|
// This case should ideally be unreachable if placeholder logic is correct
|
||||||
let err = LpError::Internal("Reached Processing state unexpectedly".to_string());
|
let err = LpError::Internal("Reached Processing state unexpectedly".to_string());
|
||||||
let reason = err.to_string();
|
let reason = err.to_string();
|
||||||
result_action = Some(Err(err));
|
result_action = Some(Err(err));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Default: Invalid input for current state (if any combinations missed) ---
|
// --- Default: Invalid input for current state (if any combinations missed) ---
|
||||||
// Consider if this should transition to Closed state. For now, just report error
|
// Consider if this should transition to Closed state. For now, just report error
|
||||||
// and transition to Closed as a safety measure.
|
// and transition to Closed as a safety measure.
|
||||||
(invalid_state, input) => {
|
(invalid_state, input) => {
|
||||||
let err = LpError::InvalidStateTransition {
|
let err = LpError::InvalidStateTransition {
|
||||||
state: format!("{:?}", invalid_state), // Use owned state for debug info
|
state: format!("{:?}", invalid_state), // Use owned state for debug info
|
||||||
input: format!("{:?}", input),
|
input: format!("{:?}", input),
|
||||||
};
|
};
|
||||||
let reason = err.to_string();
|
let reason = err.to_string();
|
||||||
result_action = Some(Err(err));
|
result_action = Some(Err(err));
|
||||||
LpState::Closed { reason }
|
LpState::Closed { reason }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1105,6 +1092,10 @@ mod tests {
|
|||||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
let ed25519_keypair_init = ed25519::KeyPair::from_secret([16u8; 32], 0);
|
||||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([17u8; 32], 1);
|
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([17u8; 32], 1);
|
||||||
|
|
||||||
|
let ed25519_pubkey_init = *ed25519_keypair_init.public_key();
|
||||||
|
let x25519_pubkey_init = ed25519_pubkey_init.to_x25519().unwrap();
|
||||||
|
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||||
|
|
||||||
// Test salt
|
// Test salt
|
||||||
let salt = [51u8; 32];
|
let salt = [51u8; 32];
|
||||||
|
|
||||||
@@ -1113,11 +1104,9 @@ mod tests {
|
|||||||
let initiator_sm = LpStateMachine::new(
|
let initiator_sm = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(ed25519_keypair_init),
|
||||||
ed25519_keypair_init.private_key(),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_resp.public_key(),
|
ed25519_keypair_resp.public_key(),
|
||||||
|
&x25519_pubkey_resp,
|
||||||
&salt,
|
&salt,
|
||||||
);
|
);
|
||||||
assert!(initiator_sm.is_ok());
|
assert!(initiator_sm.is_ok());
|
||||||
@@ -1132,11 +1121,9 @@ mod tests {
|
|||||||
let responder_sm = LpStateMachine::new(
|
let responder_sm = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false,
|
false,
|
||||||
(
|
Arc::new(ed25519_keypair_resp),
|
||||||
ed25519_keypair_resp.private_key(),
|
&ed25519_pubkey_init,
|
||||||
ed25519_keypair_resp.public_key(),
|
&x25519_pubkey_init,
|
||||||
),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
&salt,
|
&salt,
|
||||||
);
|
);
|
||||||
assert!(responder_sm.is_ok());
|
assert!(responder_sm.is_ok());
|
||||||
@@ -1158,6 +1145,10 @@ mod tests {
|
|||||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([18u8; 32], 0);
|
let ed25519_keypair_init = ed25519::KeyPair::from_secret([18u8; 32], 0);
|
||||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([19u8; 32], 1);
|
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([19u8; 32], 1);
|
||||||
|
|
||||||
|
let ed25519_pubkey_init = *ed25519_keypair_init.public_key();
|
||||||
|
let x25519_pubkey_init = ed25519_pubkey_init.to_x25519().unwrap();
|
||||||
|
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||||
|
|
||||||
// Test salt
|
// Test salt
|
||||||
let salt = [52u8; 32];
|
let salt = [52u8; 32];
|
||||||
let receiver_index: u32 = 88888;
|
let receiver_index: u32 = 88888;
|
||||||
@@ -1166,11 +1157,9 @@ mod tests {
|
|||||||
let mut initiator = LpStateMachine::new(
|
let mut initiator = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true, // is_initiator
|
true, // is_initiator
|
||||||
(
|
Arc::new(ed25519_keypair_init),
|
||||||
ed25519_keypair_init.private_key(),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_resp.public_key(),
|
ed25519_keypair_resp.public_key(),
|
||||||
|
&x25519_pubkey_resp,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1178,11 +1167,9 @@ mod tests {
|
|||||||
let mut responder = LpStateMachine::new(
|
let mut responder = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false, // is_initiator
|
false, // is_initiator
|
||||||
(
|
Arc::new(ed25519_keypair_resp),
|
||||||
ed25519_keypair_resp.private_key(),
|
&ed25519_pubkey_init,
|
||||||
ed25519_keypair_resp.public_key(),
|
&x25519_pubkey_init,
|
||||||
),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1366,6 +1353,8 @@ mod tests {
|
|||||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([20u8; 32], 0);
|
let ed25519_keypair_init = ed25519::KeyPair::from_secret([20u8; 32], 0);
|
||||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([21u8; 32], 1);
|
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([21u8; 32], 1);
|
||||||
|
|
||||||
|
let x25519_pubkey_init = *ed25519_keypair_init.to_x25519().public_key();
|
||||||
|
|
||||||
let salt = [53u8; 32];
|
let salt = [53u8; 32];
|
||||||
let receiver_index: u32 = 99901;
|
let receiver_index: u32 = 99901;
|
||||||
|
|
||||||
@@ -1373,11 +1362,9 @@ mod tests {
|
|||||||
let mut initiator = LpStateMachine::new(
|
let mut initiator = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(ed25519_keypair_init),
|
||||||
ed25519_keypair_init.private_key(),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_resp.public_key(),
|
ed25519_keypair_resp.public_key(),
|
||||||
|
&x25519_pubkey_init,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1397,6 +1384,9 @@ mod tests {
|
|||||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([22u8; 32], 0);
|
let ed25519_keypair_init = ed25519::KeyPair::from_secret([22u8; 32], 0);
|
||||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([23u8; 32], 1);
|
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([23u8; 32], 1);
|
||||||
|
|
||||||
|
let ed25519_pubkey_init = *ed25519_keypair_init.public_key();
|
||||||
|
let x25519_pubkey_init = ed25519_pubkey_init.to_x25519().unwrap();
|
||||||
|
|
||||||
let salt = [54u8; 32];
|
let salt = [54u8; 32];
|
||||||
let receiver_index: u32 = 99902;
|
let receiver_index: u32 = 99902;
|
||||||
|
|
||||||
@@ -1404,11 +1394,9 @@ mod tests {
|
|||||||
let mut responder = LpStateMachine::new(
|
let mut responder = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false,
|
false,
|
||||||
(
|
Arc::new(ed25519_keypair_resp),
|
||||||
ed25519_keypair_resp.private_key(),
|
&ed25519_pubkey_init,
|
||||||
ed25519_keypair_resp.public_key(),
|
&x25519_pubkey_init,
|
||||||
),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1428,6 +1416,10 @@ mod tests {
|
|||||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([24u8; 32], 0);
|
let ed25519_keypair_init = ed25519::KeyPair::from_secret([24u8; 32], 0);
|
||||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([25u8; 32], 1);
|
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([25u8; 32], 1);
|
||||||
|
|
||||||
|
let ed25519_pubkey_init = *ed25519_keypair_init.public_key();
|
||||||
|
let x25519_pubkey_init = ed25519_pubkey_init.to_x25519().unwrap();
|
||||||
|
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||||
|
|
||||||
let salt = [55u8; 32];
|
let salt = [55u8; 32];
|
||||||
let receiver_index: u32 = 99903;
|
let receiver_index: u32 = 99903;
|
||||||
|
|
||||||
@@ -1435,11 +1427,9 @@ mod tests {
|
|||||||
let mut initiator = LpStateMachine::new(
|
let mut initiator = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(ed25519_keypair_init),
|
||||||
ed25519_keypair_init.private_key(),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_resp.public_key(),
|
ed25519_keypair_resp.public_key(),
|
||||||
|
&x25519_pubkey_resp,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1447,11 +1437,9 @@ mod tests {
|
|||||||
let mut responder = LpStateMachine::new(
|
let mut responder = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false,
|
false,
|
||||||
(
|
Arc::new(ed25519_keypair_resp),
|
||||||
ed25519_keypair_resp.private_key(),
|
&ed25519_pubkey_init,
|
||||||
ed25519_keypair_resp.public_key(),
|
&x25519_pubkey_init,
|
||||||
),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1493,6 +1481,8 @@ mod tests {
|
|||||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([26u8; 32], 0);
|
let ed25519_keypair_init = ed25519::KeyPair::from_secret([26u8; 32], 0);
|
||||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([27u8; 32], 1);
|
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([27u8; 32], 1);
|
||||||
|
|
||||||
|
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||||
|
|
||||||
let salt = [56u8; 32];
|
let salt = [56u8; 32];
|
||||||
let receiver_index: u32 = 99904;
|
let receiver_index: u32 = 99904;
|
||||||
|
|
||||||
@@ -1500,11 +1490,9 @@ mod tests {
|
|||||||
let mut initiator = LpStateMachine::new(
|
let mut initiator = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(ed25519_keypair_init),
|
||||||
ed25519_keypair_init.private_key(),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_resp.public_key(),
|
ed25519_keypair_resp.public_key(),
|
||||||
|
&x25519_pubkey_resp,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1525,6 +1513,8 @@ mod tests {
|
|||||||
let ed25519_keypair_init = ed25519::KeyPair::from_secret([28u8; 32], 0);
|
let ed25519_keypair_init = ed25519::KeyPair::from_secret([28u8; 32], 0);
|
||||||
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([29u8; 32], 1);
|
let ed25519_keypair_resp = ed25519::KeyPair::from_secret([29u8; 32], 1);
|
||||||
|
|
||||||
|
let x25519_pubkey_resp = *ed25519_keypair_resp.to_x25519().public_key();
|
||||||
|
|
||||||
let salt = [57u8; 32];
|
let salt = [57u8; 32];
|
||||||
let receiver_index: u32 = 99905;
|
let receiver_index: u32 = 99905;
|
||||||
|
|
||||||
@@ -1532,11 +1522,9 @@ mod tests {
|
|||||||
let mut initiator = LpStateMachine::new(
|
let mut initiator = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(ed25519_keypair_init),
|
||||||
ed25519_keypair_init.private_key(),
|
|
||||||
ed25519_keypair_init.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_resp.public_key(),
|
ed25519_keypair_resp.public_key(),
|
||||||
|
&x25519_pubkey_resp,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1570,6 +1558,11 @@ mod tests {
|
|||||||
let ed25519_keypair_a = ed25519::KeyPair::from_secret([30u8; 32], 0);
|
let ed25519_keypair_a = ed25519::KeyPair::from_secret([30u8; 32], 0);
|
||||||
let ed25519_keypair_b = ed25519::KeyPair::from_secret([31u8; 32], 1);
|
let ed25519_keypair_b = ed25519::KeyPair::from_secret([31u8; 32], 1);
|
||||||
|
|
||||||
|
let ed25519_pubkey_a = *ed25519_keypair_a.public_key();
|
||||||
|
|
||||||
|
let x25519_pubkey_a = *ed25519_keypair_a.to_x25519().public_key();
|
||||||
|
let x25519_pubkey_b = *ed25519_keypair_b.to_x25519().public_key();
|
||||||
|
|
||||||
let salt = [60u8; 32];
|
let salt = [60u8; 32];
|
||||||
let receiver_index: u32 = 111111;
|
let receiver_index: u32 = 111111;
|
||||||
|
|
||||||
@@ -1577,11 +1570,9 @@ mod tests {
|
|||||||
let mut alice = LpStateMachine::new(
|
let mut alice = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true,
|
true,
|
||||||
(
|
Arc::new(ed25519_keypair_a),
|
||||||
ed25519_keypair_a.private_key(),
|
|
||||||
ed25519_keypair_a.public_key(),
|
|
||||||
),
|
|
||||||
ed25519_keypair_b.public_key(),
|
ed25519_keypair_b.public_key(),
|
||||||
|
&x25519_pubkey_b,
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1589,11 +1580,9 @@ mod tests {
|
|||||||
let mut bob = LpStateMachine::new(
|
let mut bob = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false,
|
false,
|
||||||
(
|
Arc::new(ed25519_keypair_b),
|
||||||
ed25519_keypair_b.private_key(),
|
&ed25519_pubkey_a,
|
||||||
ed25519_keypair_b.public_key(),
|
&x25519_pubkey_a,
|
||||||
),
|
|
||||||
ed25519_keypair_a.public_key(),
|
|
||||||
&salt,
|
&salt,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use nym_sphinx_types::{
|
|||||||
};
|
};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{debug, info, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum MixProcessingResultData {
|
pub enum MixProcessingResultData {
|
||||||
@@ -335,36 +335,9 @@ fn process_final_hop(
|
|||||||
packet_type: PacketType,
|
packet_type: PacketType,
|
||||||
key_rotation: SphinxKeyRotation,
|
key_rotation: SphinxKeyRotation,
|
||||||
) -> Result<MixProcessingResultData, PacketProcessingError> {
|
) -> Result<MixProcessingResultData, PacketProcessingError> {
|
||||||
let payload_size = payload.len();
|
|
||||||
debug!(
|
|
||||||
"process_final_hop: payload_size={}, packet_size={:?}, packet_type={:?}, destination={}",
|
|
||||||
payload_size, packet_size, packet_type, destination
|
|
||||||
);
|
|
||||||
|
|
||||||
let (forward_ack, message) =
|
let (forward_ack, message) =
|
||||||
split_into_ack_and_message(payload, packet_size, packet_type, key_rotation)?;
|
split_into_ack_and_message(payload, packet_size, packet_type, key_rotation)?;
|
||||||
|
|
||||||
let message_size = message.len();
|
|
||||||
let ack_present = forward_ack.is_some();
|
|
||||||
debug!(
|
|
||||||
"process_final_hop: after split - message_size={}, ack_present={}, payload_size={}",
|
|
||||||
message_size, ack_present, payload_size
|
|
||||||
);
|
|
||||||
|
|
||||||
// Verify expected message size
|
|
||||||
if let PacketSize::RegularPacket = packet_size {
|
|
||||||
use nym_sphinx_addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
|
|
||||||
let sphinx_ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
|
|
||||||
let expected_message_size = packet_size.plaintext_size() - sphinx_ack_overhead;
|
|
||||||
if message_size != expected_message_size {
|
|
||||||
use tracing::warn;
|
|
||||||
warn!(
|
|
||||||
"process_final_hop: MESSAGE SIZE MISMATCH! message_size={}, expected={}, payload_size={}, ack_present={}",
|
|
||||||
message_size, expected_message_size, payload_size, ack_present
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(MixProcessingResultData::FinalHop {
|
Ok(MixProcessingResultData::FinalHop {
|
||||||
final_hop_data: ProcessedFinalHop {
|
final_hop_data: ProcessedFinalHop {
|
||||||
destination,
|
destination,
|
||||||
@@ -391,17 +364,28 @@ fn split_into_ack_and_message(
|
|||||||
| PacketSize::ExtendedPacket32
|
| PacketSize::ExtendedPacket32
|
||||||
| PacketSize::OutfoxRegularPacket => {
|
| PacketSize::OutfoxRegularPacket => {
|
||||||
trace!("received a normal packet!");
|
trace!("received a normal packet!");
|
||||||
let (ack_data, message) = split_hop_data_into_ack_and_message(data, packet_type)?;
|
cfg_if::cfg_if! {
|
||||||
let (ack_first_hop, ack_packet) =
|
if #[cfg(feature = "no-mix-acks")] {
|
||||||
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
|
let _ = packet_type;
|
||||||
Ok((first_hop, packet)) => (first_hop, packet),
|
let _ = key_rotation;
|
||||||
Err(err) => {
|
|
||||||
info!("Failed to recover first hop from ack data: {err}");
|
// AIDEV-NOTE: When no-mix-acks is enabled, skip ack extraction entirely.
|
||||||
return Err(err.into());
|
// The full payload (including ack portion) is returned as the message.
|
||||||
}
|
Ok((None, data))
|
||||||
};
|
} else {
|
||||||
let forward_ack = MixPacket::new(ack_first_hop, ack_packet, packet_type, key_rotation);
|
let (ack_data, message) = split_hop_data_into_ack_and_message(data, packet_type)?;
|
||||||
Ok((Some(forward_ack), message))
|
let (ack_first_hop, ack_packet) =
|
||||||
|
match SurbAck::try_recover_first_hop_packet(&ack_data, packet_type) {
|
||||||
|
Ok((first_hop, packet)) => (first_hop, packet),
|
||||||
|
Err(err) => {
|
||||||
|
tracing::info!("Failed to recover first hop from ack data: {err}");
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let forward_ack = MixPacket::new(ack_first_hop, ack_packet, packet_type, key_rotation);
|
||||||
|
Ok((Some(forward_ack), message))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ license.workspace = true
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bincode = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
tokio-util.workspace = true
|
tokio-util.workspace = true
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
mod lp_messages;
|
|
||||||
|
|
||||||
pub use lp_messages::{
|
|
||||||
LpGatewayData, LpRegistrationRequest, LpRegistrationResponse, RegistrationMode,
|
|
||||||
};
|
|
||||||
|
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||||
|
|
||||||
use nym_authenticator_requests::AuthenticatorVersion;
|
use nym_authenticator_requests::AuthenticatorVersion;
|
||||||
@@ -15,6 +9,14 @@ use nym_ip_packet_requests::IpPair;
|
|||||||
use nym_sphinx::addressing::{NodeIdentity, Recipient};
|
use nym_sphinx::addressing::{NodeIdentity, Recipient};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
mod lp_messages;
|
||||||
|
mod serialisation;
|
||||||
|
|
||||||
|
pub use lp_messages::{
|
||||||
|
LpGatewayData, LpRegistrationRequest, LpRegistrationResponse, RegistrationMode,
|
||||||
|
};
|
||||||
|
pub use serialisation::BincodeError;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct NymNode {
|
pub struct NymNode {
|
||||||
pub identity: NodeIdentity,
|
pub identity: NodeIdentity,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use crate::GatewayData;
|
use crate::GatewayData;
|
||||||
|
use crate::serialisation::{BincodeError, BincodeOptions, lp_bincode_serializer};
|
||||||
|
|
||||||
/// Registration request sent by client after LP handshake
|
/// Registration request sent by client after LP handshake
|
||||||
/// Aligned with existing authenticator registration flow
|
/// Aligned with existing authenticator registration flow
|
||||||
@@ -128,6 +129,16 @@ impl LpRegistrationRequest {
|
|||||||
|
|
||||||
(now as i64 - self.timestamp as i64).abs() <= max_skew_secs as i64
|
(now as i64 - self.timestamp as i64).abs() <= max_skew_secs as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to serialise this `LpRegistrationRequest` into bytes.
|
||||||
|
pub fn serialise(&self) -> Result<Vec<u8>, BincodeError> {
|
||||||
|
lp_bincode_serializer().serialize(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to deserialise a `LpRegistrationRequest` from bytes.
|
||||||
|
pub fn try_deserialise(b: &[u8]) -> Result<Self, BincodeError> {
|
||||||
|
lp_bincode_serializer().deserialize(b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LpRegistrationResponse {
|
impl LpRegistrationResponse {
|
||||||
@@ -163,6 +174,16 @@ impl LpRegistrationResponse {
|
|||||||
allocated_bandwidth: 0,
|
allocated_bandwidth: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to serialise this `LpRegistrationResponse` into bytes.
|
||||||
|
pub fn serialise(&self) -> Result<Vec<u8>, BincodeError> {
|
||||||
|
lp_bincode_serializer().serialize(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to deserialise a `LpRegistrationResponse` from bytes.
|
||||||
|
pub fn try_deserialise(b: &[u8]) -> Result<Self, BincodeError> {
|
||||||
|
lp_bincode_serializer().deserialize(b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
+127
-42
@@ -6,16 +6,18 @@
|
|||||||
// #![warn(clippy::expect_used)]
|
// #![warn(clippy::expect_used)]
|
||||||
// #![warn(clippy::unwrap_used)]
|
// #![warn(clippy::unwrap_used)]
|
||||||
|
|
||||||
use defguard_wireguard_rs::{WGApi, WireguardInterfaceApi, host::Peer, key::Key, net::IpAddrMask};
|
use defguard_wireguard_rs::{
|
||||||
|
WGApi, WireguardInterfaceApi, error::WireguardInterfaceError, host::Peer, key::Key,
|
||||||
|
net::IpAddrMask,
|
||||||
|
};
|
||||||
use nym_crypto::asymmetric::x25519::KeyPair;
|
use nym_crypto::asymmetric::x25519::KeyPair;
|
||||||
|
use std::net::IpAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc::{self, Receiver, Sender};
|
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use nym_ip_packet_requests::IpPair;
|
use nym_ip_packet_requests::IpPair;
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
use std::net::IpAddr;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use nym_network_defaults::constants::WG_TUN_BASE_NAME;
|
use nym_network_defaults::constants::WG_TUN_BASE_NAME;
|
||||||
@@ -33,13 +35,104 @@ pub use peer_controller::{PeerControlRequest, PeerRegistrationData};
|
|||||||
|
|
||||||
pub const CONTROL_CHANNEL_SIZE: usize = 256;
|
pub const CONTROL_CHANNEL_SIZE: usize = 256;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum WgApiWrapperError {
|
||||||
|
#[error("WireGuard kernel implementation is not available on this platform")]
|
||||||
|
KernelUnavailable,
|
||||||
|
|
||||||
|
#[error("WireGuard userspace implementation is not available on this platform")]
|
||||||
|
UserspaceUnavailable,
|
||||||
|
|
||||||
|
#[error("WireGuard interface error: {0}")]
|
||||||
|
Interface(#[from] WireguardInterfaceError),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct WgApiWrapper {
|
pub struct WgApiWrapper {
|
||||||
inner: WGApi,
|
inner: Box<dyn WireguardInterfaceApi + Sync + Send>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WgApiWrapper {
|
||||||
|
/// Create new instance of `WgApiWrapper` choosing internal implementation based on `use_userspace` flag and platform availability.
|
||||||
|
///
|
||||||
|
/// Falls back to userspace implementation when kernel implementation is requested but not available.
|
||||||
|
pub fn new(ifname: &str, use_userspace: bool) -> Result<Self, WgApiWrapperError> {
|
||||||
|
if use_userspace {
|
||||||
|
Self::userspace(ifname)
|
||||||
|
} else {
|
||||||
|
Self::kernel(ifname).or_else(|err| {
|
||||||
|
if matches!(err, WgApiWrapperError::KernelUnavailable) {
|
||||||
|
Self::userspace(ifname)
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create userspace implementation
|
||||||
|
fn userspace(_ifname: &str) -> Result<Self, WgApiWrapperError> {
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
let api = WGApi::<defguard_wireguard_rs::Userspace>::new(_ifname)?;
|
||||||
|
Ok(Self {
|
||||||
|
inner: Box::new(api),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "macos",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd"
|
||||||
|
)))]
|
||||||
|
{
|
||||||
|
Err(WgApiWrapperError::UserspaceUnavailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create kernel implementation if available.
|
||||||
|
fn kernel(_ifname: &str) -> Result<Self, WgApiWrapperError> {
|
||||||
|
#[cfg(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "windows",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd"
|
||||||
|
))]
|
||||||
|
{
|
||||||
|
let api = WGApi::<defguard_wireguard_rs::Kernel>::new(_ifname)?;
|
||||||
|
Ok(Self {
|
||||||
|
inner: Box::new(api),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(
|
||||||
|
target_os = "linux",
|
||||||
|
target_os = "windows",
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "netbsd"
|
||||||
|
)))]
|
||||||
|
{
|
||||||
|
Err(WgApiWrapperError::KernelUnavailable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for WgApiWrapper {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Err(e) = self.inner.remove_interface() {
|
||||||
|
error!("Could not remove the wireguard interface: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WireguardInterfaceApi for WgApiWrapper {
|
impl WireguardInterfaceApi for WgApiWrapper {
|
||||||
fn create_interface(
|
fn create_interface(
|
||||||
&self,
|
&mut self,
|
||||||
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
||||||
self.inner.create_interface()
|
self.inner.create_interface()
|
||||||
}
|
}
|
||||||
@@ -58,7 +151,6 @@ impl WireguardInterfaceApi for WgApiWrapper {
|
|||||||
self.inner.configure_peer_routing(peers)
|
self.inner.configure_peer_routing(peers)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
fn configure_interface(
|
fn configure_interface(
|
||||||
&self,
|
&self,
|
||||||
config: &defguard_wireguard_rs::InterfaceConfiguration,
|
config: &defguard_wireguard_rs::InterfaceConfiguration,
|
||||||
@@ -66,21 +158,20 @@ impl WireguardInterfaceApi for WgApiWrapper {
|
|||||||
self.inner.configure_interface(config)
|
self.inner.configure_interface(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(not(windows))]
|
||||||
fn configure_interface(
|
|
||||||
&self,
|
|
||||||
config: &defguard_wireguard_rs::InterfaceConfiguration,
|
|
||||||
dns: &[std::net::IpAddr],
|
|
||||||
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
|
||||||
self.inner.configure_interface(config, dns)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_interface(
|
fn remove_interface(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
||||||
self.inner.remove_interface()
|
self.inner.remove_interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn remove_interface(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
||||||
|
self.inner.remove_interface()
|
||||||
|
}
|
||||||
|
|
||||||
fn configure_peer(
|
fn configure_peer(
|
||||||
&self,
|
&self,
|
||||||
peer: &Peer,
|
peer: &Peer,
|
||||||
@@ -106,24 +197,10 @@ impl WireguardInterfaceApi for WgApiWrapper {
|
|||||||
|
|
||||||
fn configure_dns(
|
fn configure_dns(
|
||||||
&self,
|
&self,
|
||||||
dns: &[std::net::IpAddr],
|
dns: &[IpAddr],
|
||||||
|
search_domains: &[&str],
|
||||||
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
) -> Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
||||||
self.inner.configure_dns(dns)
|
self.inner.configure_dns(dns, search_domains)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WgApiWrapper {
|
|
||||||
pub fn new(wg_api: WGApi) -> Self {
|
|
||||||
WgApiWrapper { inner: wg_api }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for WgApiWrapper {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Err(e) = defguard_wireguard_rs::WireguardInterfaceApi::remove_interface(&self.inner)
|
|
||||||
{
|
|
||||||
error!("Could not remove the wireguard interface: {e:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +258,7 @@ pub async fn start_wireguard(
|
|||||||
use_userspace: bool,
|
use_userspace: bool,
|
||||||
) -> Result<std::sync::Arc<WgApiWrapper>, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
) -> Result<std::sync::Arc<WgApiWrapper>, Box<dyn std::error::Error + Send + Sync + 'static>> {
|
||||||
use base64::{Engine, prelude::BASE64_STANDARD};
|
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||||
use defguard_wireguard_rs::{InterfaceConfiguration, WireguardInterfaceApi};
|
use defguard_wireguard_rs::InterfaceConfiguration;
|
||||||
use ip_network::IpNetwork;
|
use ip_network::IpNetwork;
|
||||||
use peer_controller::PeerController;
|
use peer_controller::PeerController;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -193,7 +270,7 @@ pub async fn start_wireguard(
|
|||||||
"Initializing WireGuard interface '{}' with use_userspace={}",
|
"Initializing WireGuard interface '{}' with use_userspace={}",
|
||||||
ifname, use_userspace
|
ifname, use_userspace
|
||||||
);
|
);
|
||||||
let wg_api = defguard_wireguard_rs::WGApi::new(ifname.clone(), use_userspace)?;
|
let mut wg_api = WgApiWrapper::new(&ifname, use_userspace)?;
|
||||||
let mut peer_bandwidth_managers = HashMap::with_capacity(peers.len());
|
let mut peer_bandwidth_managers = HashMap::with_capacity(peers.len());
|
||||||
|
|
||||||
for peer in peers.iter() {
|
for peer in peers.iter() {
|
||||||
@@ -214,14 +291,22 @@ pub async fn start_wireguard(
|
|||||||
let interface_config = InterfaceConfiguration {
|
let interface_config = InterfaceConfiguration {
|
||||||
name: ifname.clone(),
|
name: ifname.clone(),
|
||||||
prvkey: BASE64_STANDARD.encode(wireguard_data.inner.keypair().private_key().to_bytes()),
|
prvkey: BASE64_STANDARD.encode(wireguard_data.inner.keypair().private_key().to_bytes()),
|
||||||
address: wireguard_data.inner.config().private_ipv4.to_string(),
|
addresses: vec![IpAddrMask::host(IpAddr::from(
|
||||||
port: wireguard_data.inner.config().announced_tunnel_port as u32,
|
wireguard_data.inner.config().private_ipv4,
|
||||||
|
))],
|
||||||
|
port: wireguard_data.inner.config().announced_tunnel_port,
|
||||||
peers: peers.clone(), // Clone since we need to use peers later to mark IPs as used
|
peers: peers.clone(), // Clone since we need to use peers later to mark IPs as used
|
||||||
mtu: None,
|
mtu: None,
|
||||||
};
|
};
|
||||||
info!(
|
info!(
|
||||||
"attempting to configure wireguard interface '{ifname}': address={}, port={}",
|
"attempting to configure wireguard interface '{ifname}': addresses=[{}], port={}",
|
||||||
interface_config.address, interface_config.port
|
interface_config
|
||||||
|
.addresses
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
interface_config.port
|
||||||
);
|
);
|
||||||
|
|
||||||
info!("Configuring WireGuard interface...");
|
info!("Configuring WireGuard interface...");
|
||||||
@@ -263,7 +348,6 @@ pub async fn start_wireguard(
|
|||||||
wg_api.configure_peer_routing(&[catch_all_peer])?;
|
wg_api.configure_peer_routing(&[catch_all_peer])?;
|
||||||
|
|
||||||
let host = wg_api.read_interface_data()?;
|
let host = wg_api.read_interface_data()?;
|
||||||
let wg_api = std::sync::Arc::new(WgApiWrapper::new(wg_api));
|
|
||||||
|
|
||||||
// Initialize IP pool from configuration
|
// Initialize IP pool from configuration
|
||||||
info!("Initializing IP pool for WireGuard peer allocation");
|
info!("Initializing IP pool for WireGuard peer allocation");
|
||||||
@@ -278,13 +362,13 @@ pub async fn start_wireguard(
|
|||||||
for peer in &peers {
|
for peer in &peers {
|
||||||
for allowed_ip in &peer.allowed_ips {
|
for allowed_ip in &peer.allowed_ips {
|
||||||
// Extract IPv4 and IPv6 from peer's allowed_ips
|
// Extract IPv4 and IPv6 from peer's allowed_ips
|
||||||
if let IpAddr::V4(ipv4) = allowed_ip.ip {
|
if let IpAddr::V4(ipv4) = allowed_ip.address {
|
||||||
// Find corresponding IPv6
|
// Find corresponding IPv6
|
||||||
if let Some(ipv6_mask) = peer
|
if let Some(ipv6_mask) = peer
|
||||||
.allowed_ips
|
.allowed_ips
|
||||||
.iter()
|
.iter()
|
||||||
.find(|ip| matches!(ip.ip, IpAddr::V6(_)))
|
.find(|ip| matches!(ip.address, IpAddr::V6(_)))
|
||||||
&& let IpAddr::V6(ipv6) = ipv6_mask.ip
|
&& let IpAddr::V6(ipv6) = ipv6_mask.address
|
||||||
{
|
{
|
||||||
ip_pool.mark_used(IpPair::new(ipv4, ipv6)).await;
|
ip_pool.mark_used(IpPair::new(ipv4, ipv6)).await;
|
||||||
}
|
}
|
||||||
@@ -292,6 +376,7 @@ pub async fn start_wireguard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let wg_api = std::sync::Arc::new(wg_api);
|
||||||
let mut controller = PeerController::new(
|
let mut controller = PeerController::new(
|
||||||
ecash_manager,
|
ecash_manager,
|
||||||
metrics,
|
metrics,
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ impl PeerController {
|
|||||||
bw_manager
|
bw_manager
|
||||||
.allowed_ips()
|
.allowed_ips()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|ip_mask| ip_mask.ip == ip)
|
.find(|ip_mask| ip_mask.address == ip)
|
||||||
.and(Some(key.clone()))
|
.and(Some(key.clone()))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -551,7 +551,7 @@ struct MockWgApi {
|
|||||||
#[allow(clippy::todo)]
|
#[allow(clippy::todo)]
|
||||||
impl WireguardInterfaceApi for MockWgApi {
|
impl WireguardInterfaceApi for MockWgApi {
|
||||||
fn create_interface(
|
fn create_interface(
|
||||||
&self,
|
&mut self,
|
||||||
) -> std::result::Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
) -> std::result::Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
@@ -570,7 +570,6 @@ impl WireguardInterfaceApi for MockWgApi {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
fn configure_interface(
|
fn configure_interface(
|
||||||
&self,
|
&self,
|
||||||
_config: &defguard_wireguard_rs::InterfaceConfiguration,
|
_config: &defguard_wireguard_rs::InterfaceConfiguration,
|
||||||
@@ -578,21 +577,20 @@ impl WireguardInterfaceApi for MockWgApi {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(not(windows))]
|
||||||
fn configure_interface(
|
|
||||||
&self,
|
|
||||||
_config: &defguard_wireguard_rs::InterfaceConfiguration,
|
|
||||||
_dns: &[std::net::IpAddr],
|
|
||||||
) -> std::result::Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_interface(
|
fn remove_interface(
|
||||||
&self,
|
&self,
|
||||||
) -> std::result::Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
) -> std::result::Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn remove_interface(
|
||||||
|
&mut self,
|
||||||
|
) -> std::result::Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
fn configure_peer(
|
fn configure_peer(
|
||||||
&self,
|
&self,
|
||||||
peer: &Peer,
|
peer: &Peer,
|
||||||
@@ -623,6 +621,7 @@ impl WireguardInterfaceApi for MockWgApi {
|
|||||||
fn configure_dns(
|
fn configure_dns(
|
||||||
&self,
|
&self,
|
||||||
_dns: &[std::net::IpAddr],
|
_dns: &[std::net::IpAddr],
|
||||||
|
_search_domains: &[&str],
|
||||||
) -> std::result::Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
) -> std::result::Result<(), defguard_wireguard_rs::error::WireguardInterfaceError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,31 +278,6 @@ impl<R, S> FreshHandler<R, S> {
|
|||||||
where
|
where
|
||||||
S: AsyncRead + AsyncWrite + Unpin,
|
S: AsyncRead + AsyncWrite + Unpin,
|
||||||
{
|
{
|
||||||
// Log message sizes before sending to client
|
|
||||||
for (idx, packet) in packets.iter().enumerate() {
|
|
||||||
use nym_sphinx::addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
|
|
||||||
use nym_sphinx::params::PacketSize;
|
|
||||||
let sphinx_ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
|
|
||||||
let expected_size = PacketSize::RegularPacket.plaintext_size() - sphinx_ack_overhead;
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"push_packets_to_client: packet[{}] size={}, expected_regular_size={}, ack_overhead={}",
|
|
||||||
idx,
|
|
||||||
packet.len(),
|
|
||||||
expected_size,
|
|
||||||
sphinx_ack_overhead
|
|
||||||
);
|
|
||||||
|
|
||||||
if packet.len() == PacketSize::RegularPacket.plaintext_size() {
|
|
||||||
warn!(
|
|
||||||
"push_packets_to_client: WARNING - packet[{}] has full plaintext size ({}), expected {} (after ACK removal)",
|
|
||||||
idx,
|
|
||||||
packet.len(),
|
|
||||||
expected_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: into_ws_message encrypts the requests and adds a MAC on it. Perhaps it should
|
// note: into_ws_message encrypts the requests and adds a MAC on it. Perhaps it should
|
||||||
// be more explicit in the naming?
|
// be more explicit in the naming?
|
||||||
let messages: Vec<Result<Message, WsError>> = packets
|
let messages: Vec<Result<Message, WsError>> = packets
|
||||||
@@ -521,9 +496,8 @@ impl<R, S> FreshHandler<R, S> {
|
|||||||
error!("{incompatible_err}");
|
error!("{incompatible_err}");
|
||||||
Err(incompatible_err)
|
Err(incompatible_err)
|
||||||
} else {
|
} else {
|
||||||
// Return the client's version for backwards compatibility.. temporary solution.
|
|
||||||
debug!("the client is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
debug!("the client is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||||
Ok(client_protocol_version)
|
Ok(CURRENT_PROTOCOL_VERSION)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,9 +717,9 @@ impl<R, S> FreshHandler<R, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_supported_protocol_request(&self) -> ServerResponse {
|
pub(crate) fn handle_supported_protocol_request(&self) -> ServerResponse {
|
||||||
use nym_gateway_requests::EMBEDDED_KEY_ROTATION_INFO_VERSION;
|
debug!("returning gateway protocol version");
|
||||||
ServerResponse::SupportedProtocol {
|
ServerResponse::SupportedProtocol {
|
||||||
version: EMBEDDED_KEY_ROTATION_INFO_VERSION,
|
version: CURRENT_PROTOCOL_VERSION,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ impl MixnetListener {
|
|||||||
let allowed_ipv4 = peer
|
let allowed_ipv4 = peer
|
||||||
.allowed_ips
|
.allowed_ips
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|ip_mask| match ip_mask.ip {
|
.find_map(|ip_mask| match ip_mask.address {
|
||||||
IpAddr::V4(ipv4_addr) => Some(ipv4_addr),
|
IpAddr::V4(ipv4_addr) => Some(ipv4_addr),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
@@ -307,7 +307,7 @@ impl MixnetListener {
|
|||||||
let allowed_ipv6 = peer
|
let allowed_ipv6 = peer
|
||||||
.allowed_ips
|
.allowed_ips
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|ip_mask| match ip_mask.ip {
|
.find_map(|ip_mask| match ip_mask.address {
|
||||||
IpAddr::V6(ipv6_addr) => Some(ipv6_addr),
|
IpAddr::V6(ipv6_addr) => Some(ipv6_addr),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
@@ -846,23 +846,11 @@ impl MixnetListener {
|
|||||||
| AuthenticatorVersion::V1
|
| AuthenticatorVersion::V1
|
||||||
| AuthenticatorVersion::V2
|
| AuthenticatorVersion::V2
|
||||||
| AuthenticatorVersion::V3
|
| AuthenticatorVersion::V3
|
||||||
| AuthenticatorVersion::V4 => {
|
| AuthenticatorVersion::V4
|
||||||
// pre v5 this message hasn't existed
|
| AuthenticatorVersion::V5 => {
|
||||||
|
// pre v6 this message hasn't existed
|
||||||
return Err(AuthenticatorError::UnknownVersion);
|
return Err(AuthenticatorError::UnknownVersion);
|
||||||
}
|
}
|
||||||
AuthenticatorVersion::V5 => {
|
|
||||||
// V5 clients shouldn't send this message, but for backward compatibility
|
|
||||||
// with V5 clients connecting to V6 gateways, we allow it and return
|
|
||||||
// a V6-style response (V5 clients won't understand it, but at least we don't error)
|
|
||||||
// This enables V5 clients from mainnet (release/2026.1-kiwi) to work with
|
|
||||||
// V6 gateways (release/2026.2-oscypek) during the transition period
|
|
||||||
v6::response::AuthenticatorResponse::new_upgrade_mode_check(
|
|
||||||
request_id,
|
|
||||||
self.upgrade_mode_enabled(),
|
|
||||||
)
|
|
||||||
.to_bytes()
|
|
||||||
.map_err(AuthenticatorError::response_serialisation)?
|
|
||||||
}
|
|
||||||
AuthenticatorVersion::V6 => {
|
AuthenticatorVersion::V6 => {
|
||||||
v6::response::AuthenticatorResponse::new_upgrade_mode_check(
|
v6::response::AuthenticatorResponse::new_upgrade_mode_check(
|
||||||
request_id,
|
request_id,
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ use super::messages::LpRegistrationRequest;
|
|||||||
use super::registration::process_registration;
|
use super::registration::process_registration;
|
||||||
use super::LpHandlerState;
|
use super::LpHandlerState;
|
||||||
use crate::error::GatewayError;
|
use crate::error::GatewayError;
|
||||||
use nym_lp::serialisation::{lp_bincode_serializer, BincodeOptions};
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use nym_lp::{
|
use nym_lp::{
|
||||||
codec::OuterAeadKey, keypair::PublicKey, message::ForwardPacketData, packet::LpHeader,
|
codec::OuterAeadKey, message::ForwardPacketData, packet::LpHeader, LpMessage, LpPacket,
|
||||||
LpMessage, LpPacket, OuterHeader,
|
OuterHeader,
|
||||||
};
|
};
|
||||||
use nym_lp_transport::traits::LpTransport;
|
use nym_lp_transport::traits::LpTransport;
|
||||||
use nym_metrics::{add_histogram_obs, inc};
|
use nym_metrics::{add_histogram_obs, inc};
|
||||||
@@ -290,31 +290,11 @@ where
|
|||||||
async fn handle_client_hello(&mut self, packet: LpPacket) -> Result<(), GatewayError> {
|
async fn handle_client_hello(&mut self, packet: LpPacket) -> Result<(), GatewayError> {
|
||||||
use nym_lp::packet::LpHeader;
|
use nym_lp::packet::LpHeader;
|
||||||
use nym_lp::state_machine::{LpInput, LpStateMachine};
|
use nym_lp::state_machine::{LpInput, LpStateMachine};
|
||||||
|
let remote = self.remote_addr;
|
||||||
|
|
||||||
// Extract ClientHello data
|
// Extract ClientHello data
|
||||||
let (receiver_index, client_ed25519_pubkey, salt) = match packet.message() {
|
let hello_data = match packet.message() {
|
||||||
LpMessage::ClientHello(hello_data) => {
|
LpMessage::ClientHello(hello_data) => hello_data,
|
||||||
// Validate timestamp
|
|
||||||
let timestamp = hello_data.extract_timestamp();
|
|
||||||
Self::validate_timestamp(
|
|
||||||
timestamp,
|
|
||||||
self.state.lp_config.debug.timestamp_tolerance,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Extract client-proposed receiver_index
|
|
||||||
let receiver_index = hello_data.receiver_index;
|
|
||||||
|
|
||||||
let client_ed25519_pubkey = nym_crypto::asymmetric::ed25519::PublicKey::from_bytes(
|
|
||||||
&hello_data.client_ed25519_public_key,
|
|
||||||
)
|
|
||||||
.map_err(|e| {
|
|
||||||
GatewayError::LpProtocolError(
|
|
||||||
format!("Invalid client Ed25519 public key: {e}",),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
(receiver_index, client_ed25519_pubkey, hello_data.salt)
|
|
||||||
}
|
|
||||||
other => {
|
other => {
|
||||||
inc!("lp_client_hello_failed");
|
inc!("lp_client_hello_failed");
|
||||||
return Err(GatewayError::LpProtocolError(format!(
|
return Err(GatewayError::LpProtocolError(format!(
|
||||||
@@ -323,20 +303,23 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(
|
// Validate timestamp
|
||||||
"Processing ClientHello from {} (proposed receiver_index={})",
|
let timestamp = hello_data.extract_timestamp();
|
||||||
self.remote_addr, receiver_index
|
Self::validate_timestamp(timestamp, self.state.lp_config.debug.timestamp_tolerance)?;
|
||||||
);
|
|
||||||
|
// Extract client-proposed receiver_index
|
||||||
|
let receiver_index = hello_data.receiver_index;
|
||||||
|
|
||||||
|
// let client_ed25519_pubkey = hello_data.client_ed25519_public_key;
|
||||||
|
|
||||||
|
debug!("Processing ClientHello from {remote} (proposed receiver_index={receiver_index})",);
|
||||||
|
|
||||||
// Collision check for client-proposed receiver_index
|
// Collision check for client-proposed receiver_index
|
||||||
// Check both handshake_states (in-progress) and session_states (established)
|
// Check both handshake_states (in-progress) and session_states (established)
|
||||||
if self.state.handshake_states.contains_key(&receiver_index)
|
if self.state.handshake_states.contains_key(&receiver_index)
|
||||||
|| self.state.session_states.contains_key(&receiver_index)
|
|| self.state.session_states.contains_key(&receiver_index)
|
||||||
{
|
{
|
||||||
warn!(
|
warn!("Receiver index collision: {receiver_index} from {remote}",);
|
||||||
"Receiver index collision: {} from {}",
|
|
||||||
receiver_index, self.remote_addr
|
|
||||||
);
|
|
||||||
inc!("lp_receiver_index_collision");
|
inc!("lp_receiver_index_collision");
|
||||||
|
|
||||||
// Send Collision response to tell client to retry with new receiver_index
|
// Send Collision response to tell client to retry with new receiver_index
|
||||||
@@ -344,7 +327,7 @@ where
|
|||||||
// Note: Do NOT set binding on collision - client may retry with new receiver_index
|
// Note: Do NOT set binding on collision - client may retry with new receiver_index
|
||||||
let collision_packet =
|
let collision_packet =
|
||||||
LpPacket::new(LpHeader::new(receiver_index, 0), LpMessage::Collision);
|
LpPacket::new(LpHeader::new(receiver_index, 0), LpMessage::Collision);
|
||||||
self.send_lp_packet(&collision_packet, None).await?;
|
self.send_lp_packet(collision_packet, None).await?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -352,32 +335,23 @@ where
|
|||||||
// Collision check passed - bind this connection to the receiver_index
|
// Collision check passed - bind this connection to the receiver_index
|
||||||
// All subsequent packets on this connection must use this receiver_index
|
// All subsequent packets on this connection must use this receiver_index
|
||||||
self.bound_receiver_idx = Some(receiver_index);
|
self.bound_receiver_idx = Some(receiver_index);
|
||||||
trace!(
|
trace!("Bound connection from {remote} to receiver_idx={receiver_index} (via ClientHello)",);
|
||||||
"Bound connection from {} to receiver_idx={} (via ClientHello)",
|
|
||||||
self.remote_addr,
|
|
||||||
receiver_index
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create state machine for this handshake using client-proposed receiver_index
|
// Create state machine for this handshake using client-proposed receiver_index
|
||||||
let mut state_machine = LpStateMachine::new(
|
let mut state_machine = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
false, // responder
|
false, // responder
|
||||||
(
|
self.state.local_identity.clone(),
|
||||||
self.state.local_identity.private_key(),
|
&hello_data.client_ed25519_public_key,
|
||||||
self.state.local_identity.public_key(),
|
&hello_data.client_lp_public_key,
|
||||||
),
|
&hello_data.salt,
|
||||||
&client_ed25519_pubkey,
|
|
||||||
&salt,
|
|
||||||
)
|
)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
inc!("lp_client_hello_failed");
|
inc!("lp_client_hello_failed");
|
||||||
GatewayError::LpHandshakeError(format!("Failed to create state machine: {}", e))
|
GatewayError::LpHandshakeError(format!("Failed to create state machine: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
debug!(
|
debug!("Created handshake state for {remote} (receiver_index={receiver_index})",);
|
||||||
"Created handshake state for {} (receiver_index={})",
|
|
||||||
self.remote_addr, receiver_index
|
|
||||||
);
|
|
||||||
|
|
||||||
// Transition state machine to KKTExchange (responder waits for client's KKT request)
|
// Transition state machine to KKTExchange (responder waits for client's KKT request)
|
||||||
// For responder, StartHandshake returns None (just transitions state)
|
// For responder, StartHandshake returns None (just transitions state)
|
||||||
@@ -385,8 +359,7 @@ where
|
|||||||
if let Some(Err(e)) = state_machine.process_input(LpInput::StartHandshake) {
|
if let Some(Err(e)) = state_machine.process_input(LpInput::StartHandshake) {
|
||||||
inc!("lp_client_hello_failed");
|
inc!("lp_client_hello_failed");
|
||||||
return Err(GatewayError::LpHandshakeError(format!(
|
return Err(GatewayError::LpHandshakeError(format!(
|
||||||
"StartHandshake failed: {}",
|
"StartHandshake failed: {e}",
|
||||||
e
|
|
||||||
)));
|
)));
|
||||||
// Responder (gateway) gets Ok but no packet to send - we just wait for client's next packet
|
// Responder (gateway) gets Ok but no packet to send - we just wait for client's next packet
|
||||||
}
|
}
|
||||||
@@ -397,14 +370,13 @@ where
|
|||||||
.insert(receiver_index, super::TimestampedState::new(state_machine));
|
.insert(receiver_index, super::TimestampedState::new(state_machine));
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Stored handshake state for {} (receiver_index={}) - waiting for KKT request",
|
"Stored handshake state for {remote} (receiver_index={receiver_index}) - waiting for KKT request",
|
||||||
self.remote_addr, receiver_index
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send Ack to confirm ClientHello received
|
// Send Ack to confirm ClientHello received
|
||||||
// No outer key - this is before PSK derivation
|
// No outer key - this is before PSK derivation
|
||||||
let ack_packet = LpPacket::new(LpHeader::new(receiver_index, 0), LpMessage::Ack);
|
let ack_packet = LpPacket::new(LpHeader::new(receiver_index, 0), LpMessage::Ack);
|
||||||
self.send_lp_packet(&ack_packet, None).await?;
|
self.send_lp_packet(ack_packet, None).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -508,7 +480,7 @@ where
|
|||||||
|
|
||||||
// Send response packet if needed
|
// Send response packet if needed
|
||||||
if let Some((packet, outer_key)) = should_send {
|
if let Some((packet, outer_key)) = should_send {
|
||||||
self.send_lp_packet(&packet, outer_key.as_ref()).await?;
|
self.send_lp_packet(packet, outer_key.as_ref()).await?;
|
||||||
trace!(
|
trace!(
|
||||||
"Sent handshake response to {} (encrypted={})",
|
"Sent handshake response to {} (encrypted={})",
|
||||||
self.remote_addr,
|
self.remote_addr,
|
||||||
@@ -586,7 +558,7 @@ where
|
|||||||
self.remote_addr, receiver_idx
|
self.remote_addr, receiver_idx
|
||||||
);
|
);
|
||||||
inc!("lp_subsession_kk2_sent");
|
inc!("lp_subsession_kk2_sent");
|
||||||
self.send_lp_packet(&response_packet, outer_key.as_ref())
|
self.send_lp_packet(response_packet, outer_key.as_ref())
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -632,9 +604,7 @@ where
|
|||||||
let remote = self.remote_addr;
|
let remote = self.remote_addr;
|
||||||
|
|
||||||
// Try to deserialize as LpRegistrationRequest first (most common case after handshake)
|
// Try to deserialize as LpRegistrationRequest first (most common case after handshake)
|
||||||
if let Ok(request) =
|
if let Ok(request) = LpRegistrationRequest::try_deserialise(&decrypted_bytes) {
|
||||||
lp_bincode_serializer().deserialize::<LpRegistrationRequest>(&decrypted_bytes)
|
|
||||||
{
|
|
||||||
debug!(
|
debug!(
|
||||||
"LP registration request from {remote} (receiver_idx={receiver_idx}): mode={:?}",
|
"LP registration request from {remote} (receiver_idx={receiver_idx}): mode={:?}",
|
||||||
request.mode
|
request.mode
|
||||||
@@ -645,9 +615,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to deserialize as ForwardPacketData (entry gateway forwarding to exit)
|
// Try to deserialize as ForwardPacketData (entry gateway forwarding to exit)
|
||||||
if let Ok(forward_data) =
|
if let Ok(forward_data) = ForwardPacketData::decode(&decrypted_bytes) {
|
||||||
lp_bincode_serializer().deserialize::<ForwardPacketData>(&decrypted_bytes)
|
|
||||||
{
|
|
||||||
debug!(
|
debug!(
|
||||||
"LP forward request from {remote} (receiver_idx={receiver_idx}) to {}",
|
"LP forward request from {remote} (receiver_idx={receiver_idx}) to {}",
|
||||||
forward_data.target_lp_address
|
forward_data.target_lp_address
|
||||||
@@ -689,7 +657,7 @@ where
|
|||||||
|
|
||||||
// Send SubsessionReady packet if present (for initiator - gateway is responder, so typically None)
|
// Send SubsessionReady packet if present (for initiator - gateway is responder, so typically None)
|
||||||
if let Some(packet) = ready_packet {
|
if let Some(packet) = ready_packet {
|
||||||
self.send_lp_packet(&packet, outer_key.as_ref()).await?;
|
self.send_lp_packet(packet, outer_key.as_ref()).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new state machine from completed subsession
|
// Create new state machine from completed subsession
|
||||||
@@ -759,7 +727,7 @@ where
|
|||||||
.map_err(|e| GatewayError::LpProtocolError(format!("Session error: {}", e)))?;
|
.map_err(|e| GatewayError::LpProtocolError(format!("Session error: {}", e)))?;
|
||||||
|
|
||||||
// Serialize and encrypt response
|
// Serialize and encrypt response
|
||||||
let response_bytes = lp_bincode_serializer().serialize(&response).map_err(|e| {
|
let response_bytes = response.serialise().map_err(|e| {
|
||||||
GatewayError::LpProtocolError(format!("Failed to serialize response: {}", e))
|
GatewayError::LpProtocolError(format!("Failed to serialize response: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -777,7 +745,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Send response (encrypted with outer AEAD)
|
// Send response (encrypted with outer AEAD)
|
||||||
self.send_lp_packet(&response_packet, outer_key.as_ref())
|
self.send_lp_packet(response_packet, outer_key.as_ref())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if response.success {
|
if response.success {
|
||||||
@@ -834,7 +802,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Send encrypted response to client (encrypted with outer AEAD)
|
// Send encrypted response to client (encrypted with outer AEAD)
|
||||||
self.send_lp_packet(&response_packet, outer_key.as_ref())
|
self.send_lp_packet(response_packet, outer_key.as_ref())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
@@ -902,14 +870,7 @@ where
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
async fn receive_client_hello(
|
async fn receive_client_hello(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<
|
) -> Result<(x25519::PublicKey, ed25519::PublicKey, [u8; 32]), GatewayError> {
|
||||||
(
|
|
||||||
PublicKey,
|
|
||||||
nym_crypto::asymmetric::ed25519::PublicKey,
|
|
||||||
[u8; 32],
|
|
||||||
),
|
|
||||||
GatewayError,
|
|
||||||
> {
|
|
||||||
// Receive first packet which should be ClientHello (no outer encryption)
|
// Receive first packet which should be ClientHello (no outer encryption)
|
||||||
let (raw_bytes, _header) = self.receive_raw_packet().await?;
|
let (raw_bytes, _header) = self.receive_raw_packet().await?;
|
||||||
let packet = nym_lp::codec::parse_lp_packet(&raw_bytes, None)
|
let packet = nym_lp::codec::parse_lp_packet(&raw_bytes, None)
|
||||||
@@ -936,22 +897,11 @@ where
|
|||||||
self.state.lp_config.debug.timestamp_tolerance.as_secs()
|
self.state.lp_config.debug.timestamp_tolerance.as_secs()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Convert bytes to X25519 PublicKey (for Noise protocol)
|
// Retrieve X25519 PublicKey (for Noise protocol)
|
||||||
let client_pubkey = PublicKey::from_bytes(&hello_data.client_lp_public_key)
|
let client_pubkey = hello_data.client_lp_public_key;
|
||||||
.map_err(|e| {
|
|
||||||
GatewayError::LpProtocolError(format!("Invalid client public key: {}", e))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Convert bytes to Ed25519 PublicKey (for PSQ authentication)
|
// Retrieve Ed25519 PublicKey (for PSQ authentication)
|
||||||
let client_ed25519_pubkey = nym_crypto::asymmetric::ed25519::PublicKey::from_bytes(
|
let client_ed25519_pubkey = hello_data.client_ed25519_public_key;
|
||||||
&hello_data.client_ed25519_public_key,
|
|
||||||
)
|
|
||||||
.map_err(|e| {
|
|
||||||
GatewayError::LpProtocolError(format!(
|
|
||||||
"Invalid client Ed25519 public key: {}",
|
|
||||||
e
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Extract salt for PSK derivation
|
// Extract salt for PSK derivation
|
||||||
let salt = hello_data.salt;
|
let salt = hello_data.salt;
|
||||||
@@ -1223,7 +1173,7 @@ where
|
|||||||
/// * `outer_key` - Optional outer AEAD key for encryption (None for cleartext, Some for encrypted)
|
/// * `outer_key` - Optional outer AEAD key for encryption (None for cleartext, Some for encrypted)
|
||||||
async fn send_lp_packet(
|
async fn send_lp_packet(
|
||||||
&mut self,
|
&mut self,
|
||||||
packet: &LpPacket,
|
packet: LpPacket,
|
||||||
outer_key: Option<&OuterAeadKey>,
|
outer_key: Option<&OuterAeadKey>,
|
||||||
) -> Result<(), GatewayError> {
|
) -> Result<(), GatewayError> {
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
@@ -1231,7 +1181,7 @@ where
|
|||||||
|
|
||||||
// Serialize the packet (encrypted if outer_key provided)
|
// Serialize the packet (encrypted if outer_key provided)
|
||||||
let mut packet_buf = BytesMut::new();
|
let mut packet_buf = BytesMut::new();
|
||||||
serialize_lp_packet(packet, &mut packet_buf, outer_key).map_err(|e| {
|
serialize_lp_packet(&packet, &mut packet_buf, outer_key).map_err(|e| {
|
||||||
GatewayError::LpProtocolError(format!("Failed to serialize packet: {}", e))
|
GatewayError::LpProtocolError(format!("Failed to serialize packet: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -1525,7 +1475,7 @@ mod tests {
|
|||||||
let packet = LpPacket::new(
|
let packet = LpPacket::new(
|
||||||
LpHeader {
|
LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 42,
|
receiver_idx: 42,
|
||||||
counter: 0,
|
counter: 0,
|
||||||
},
|
},
|
||||||
@@ -1591,13 +1541,13 @@ mod tests {
|
|||||||
let packet = LpPacket::new(
|
let packet = LpPacket::new(
|
||||||
LpHeader {
|
LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 99,
|
receiver_idx: 99,
|
||||||
counter: 5,
|
counter: 5,
|
||||||
},
|
},
|
||||||
LpMessage::Busy,
|
LpMessage::Busy,
|
||||||
);
|
);
|
||||||
handler.send_lp_packet(&packet, None).await
|
handler.send_lp_packet(packet, None).await
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut client_stream = TcpStream::connect(addr).await.unwrap();
|
let mut client_stream = TcpStream::connect(addr).await.unwrap();
|
||||||
@@ -1631,13 +1581,13 @@ mod tests {
|
|||||||
let packet = LpPacket::new(
|
let packet = LpPacket::new(
|
||||||
LpHeader {
|
LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 100,
|
receiver_idx: 100,
|
||||||
counter: 10,
|
counter: 10,
|
||||||
},
|
},
|
||||||
LpMessage::Handshake(HandshakeData(handshake_data)),
|
LpMessage::Handshake(HandshakeData(handshake_data)),
|
||||||
);
|
);
|
||||||
handler.send_lp_packet(&packet, None).await
|
handler.send_lp_packet(packet, None).await
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut client_stream = TcpStream::connect(addr).await.unwrap();
|
let mut client_stream = TcpStream::connect(addr).await.unwrap();
|
||||||
@@ -1672,13 +1622,13 @@ mod tests {
|
|||||||
let packet = LpPacket::new(
|
let packet = LpPacket::new(
|
||||||
LpHeader {
|
LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 200,
|
receiver_idx: 200,
|
||||||
counter: 20,
|
counter: 20,
|
||||||
},
|
},
|
||||||
LpMessage::EncryptedData(EncryptedDataPayload(encrypted_payload)),
|
LpMessage::EncryptedData(EncryptedDataPayload(encrypted_payload)),
|
||||||
);
|
);
|
||||||
handler.send_lp_packet(&packet, None).await
|
handler.send_lp_packet(packet, None).await
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut client_stream = TcpStream::connect(addr).await.unwrap();
|
let mut client_stream = TcpStream::connect(addr).await.unwrap();
|
||||||
@@ -1710,8 +1660,13 @@ mod tests {
|
|||||||
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
||||||
let addr = listener.local_addr().unwrap();
|
let addr = listener.local_addr().unwrap();
|
||||||
|
|
||||||
let client_key = [7u8; 32];
|
let mut rng = rand::thread_rng();
|
||||||
let client_ed25519_key = [8u8; 32];
|
let ed25519 = ed25519::KeyPair::new(&mut rng);
|
||||||
|
let x25519 = ed25519.to_x25519();
|
||||||
|
|
||||||
|
let client_key = *x25519.public_key();
|
||||||
|
let client_ed25519_key = *ed25519.public_key();
|
||||||
|
|
||||||
let hello_data =
|
let hello_data =
|
||||||
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
ClientHelloData::new_with_fresh_salt(client_key, client_ed25519_key, timestamp);
|
||||||
let expected_salt = hello_data.salt; // Clone salt before moving hello_data
|
let expected_salt = hello_data.salt; // Clone salt before moving hello_data
|
||||||
@@ -1724,13 +1679,13 @@ mod tests {
|
|||||||
let packet = LpPacket::new(
|
let packet = LpPacket::new(
|
||||||
LpHeader {
|
LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 300,
|
receiver_idx: 300,
|
||||||
counter: 30,
|
counter: 30,
|
||||||
},
|
},
|
||||||
LpMessage::ClientHello(hello_data),
|
LpMessage::ClientHello(hello_data),
|
||||||
);
|
);
|
||||||
handler.send_lp_packet(&packet, None).await
|
handler.send_lp_packet(packet, None).await
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut client_stream = TcpStream::connect(addr).await.unwrap();
|
let mut client_stream = TcpStream::connect(addr).await.unwrap();
|
||||||
@@ -1782,14 +1737,14 @@ mod tests {
|
|||||||
let client_x25519_public = client_ed25519_keypair.public_key().to_x25519().unwrap();
|
let client_x25519_public = client_ed25519_keypair.public_key().to_x25519().unwrap();
|
||||||
|
|
||||||
let hello_data = ClientHelloData::new_with_fresh_salt(
|
let hello_data = ClientHelloData::new_with_fresh_salt(
|
||||||
client_x25519_public.to_bytes(),
|
client_x25519_public,
|
||||||
client_ed25519_keypair.public_key().to_bytes(),
|
*client_ed25519_keypair.public_key(),
|
||||||
timestamp,
|
timestamp,
|
||||||
);
|
);
|
||||||
let packet = LpPacket::new(
|
let packet = LpPacket::new(
|
||||||
LpHeader {
|
LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 0,
|
receiver_idx: 0,
|
||||||
counter: 0,
|
counter: 0,
|
||||||
},
|
},
|
||||||
@@ -1843,8 +1798,8 @@ mod tests {
|
|||||||
let client_x25519_public = client_ed25519_keypair.public_key().to_x25519().unwrap();
|
let client_x25519_public = client_ed25519_keypair.public_key().to_x25519().unwrap();
|
||||||
|
|
||||||
let mut hello_data = ClientHelloData::new_with_fresh_salt(
|
let mut hello_data = ClientHelloData::new_with_fresh_salt(
|
||||||
client_x25519_public.to_bytes(),
|
client_x25519_public,
|
||||||
client_ed25519_keypair.public_key().to_bytes(),
|
*client_ed25519_keypair.public_key(),
|
||||||
timestamp,
|
timestamp,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1859,7 +1814,7 @@ mod tests {
|
|||||||
let packet = LpPacket::new(
|
let packet = LpPacket::new(
|
||||||
LpHeader {
|
LpHeader {
|
||||||
protocol_version: 1,
|
protocol_version: 1,
|
||||||
reserved: 0,
|
reserved: [0u8; 3],
|
||||||
receiver_idx: 0,
|
receiver_idx: 0,
|
||||||
counter: 0,
|
counter: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ async fn check_existing_registration(
|
|||||||
let mut ipv4 = None;
|
let mut ipv4 = None;
|
||||||
let mut ipv6 = None;
|
let mut ipv6 = None;
|
||||||
for ip_mask in &defguard_peer.allowed_ips {
|
for ip_mask in &defguard_peer.allowed_ips {
|
||||||
match ip_mask.ip {
|
match ip_mask.address {
|
||||||
std::net::IpAddr::V4(v4) => ipv4 = Some(v4),
|
std::net::IpAddr::V4(v4) => ipv4 = Some(v4),
|
||||||
std::net::IpAddr::V6(v6) => ipv6 = Some(v6),
|
std::net::IpAddr::V6(v6) => ipv6 = Some(v6),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ impl GatewayTasksBuilder {
|
|||||||
.await?;
|
.await?;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
used_private_network_ips.push(allowed_ip.ip);
|
used_private_network_ips.push(allowed_ip.address);
|
||||||
all_peers.push(peer);
|
all_peers.push(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-api"
|
name = "nym-api"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
version = "1.1.72"
|
version = "1.1.71"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nym-node"
|
name = "nym-node"
|
||||||
version = "1.24.0"
|
version = "1.23.0"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
homepage.workspace = true
|
homepage.workspace = true
|
||||||
|
|||||||
@@ -151,29 +151,6 @@ impl ConnectionHandler {
|
|||||||
|
|
||||||
let client = final_hop_data.destination;
|
let client = final_hop_data.destination;
|
||||||
let message = final_hop_data.message;
|
let message = final_hop_data.message;
|
||||||
let message_size = message.len();
|
|
||||||
|
|
||||||
// Log message size for debugging
|
|
||||||
use nym_sphinx_addressing::nodes::MAX_NODE_ADDRESS_UNPADDED_LEN;
|
|
||||||
use nym_sphinx_params::PacketSize;
|
|
||||||
let sphinx_ack_overhead = PacketSize::AckPacket.size() + MAX_NODE_ADDRESS_UNPADDED_LEN;
|
|
||||||
let expected_size = PacketSize::RegularPacket.plaintext_size() - sphinx_ack_overhead;
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"handle_final_hop: client={}, message_size={}, expected_regular_size={}, ack_overhead={}, forward_ack_present={}",
|
|
||||||
client,
|
|
||||||
message_size,
|
|
||||||
expected_size,
|
|
||||||
sphinx_ack_overhead,
|
|
||||||
final_hop_data.forward_ack.is_some()
|
|
||||||
);
|
|
||||||
|
|
||||||
if message_size == PacketSize::RegularPacket.plaintext_size() {
|
|
||||||
warn!(
|
|
||||||
"handle_final_hop: WARNING - message has full plaintext size ({}), expected {} (after ACK removal)",
|
|
||||||
message_size, expected_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if possible attempt to push message directly to the client
|
// if possible attempt to push message directly to the client
|
||||||
match self.shared.try_push_message_to_client(client, message) {
|
match self.shared.try_push_message_to_client(client, message) {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ use nym_crypto::asymmetric::{ed25519, x25519};
|
|||||||
use nym_lp::LpPacket;
|
use nym_lp::LpPacket;
|
||||||
use nym_lp::codec::{OuterAeadKey, parse_lp_packet, serialize_lp_packet};
|
use nym_lp::codec::{OuterAeadKey, parse_lp_packet, serialize_lp_packet};
|
||||||
use nym_lp::message::ForwardPacketData;
|
use nym_lp::message::ForwardPacketData;
|
||||||
use nym_lp::serialisation::{BincodeOptions, lp_bincode_serializer};
|
|
||||||
use nym_lp::state_machine::{LpAction, LpInput, LpStateMachine};
|
use nym_lp::state_machine::{LpAction, LpInput, LpStateMachine};
|
||||||
use nym_lp_transport::traits::LpTransport;
|
use nym_lp_transport::traits::LpTransport;
|
||||||
use nym_registration_common::{GatewayData, LpRegistrationRequest, LpRegistrationResponse};
|
use nym_registration_common::{GatewayData, LpRegistrationRequest, LpRegistrationResponse};
|
||||||
@@ -329,6 +328,10 @@ where
|
|||||||
LpClientError::Crypto(format!("Failed to derive X25519 public key: {e}"))
|
LpClientError::Crypto(format!("Failed to derive X25519 public key: {e}"))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let gateway_x25519_public = self.gateway_ed25519_public_key.to_x25519().map_err(|e| {
|
||||||
|
LpClientError::Crypto(format!("Failed to derive X25519 public key: {e}"))
|
||||||
|
})?;
|
||||||
|
|
||||||
let timestamp = SystemTime::now()
|
let timestamp = SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.map_err(|_| LpClientError::Other("System time before UNIX epoch".into()))?
|
.map_err(|_| LpClientError::Other("System time before UNIX epoch".into()))?
|
||||||
@@ -336,8 +339,8 @@ where
|
|||||||
|
|
||||||
// Step 2: Generate ClientHelloData with fresh salt and both public keys
|
// Step 2: Generate ClientHelloData with fresh salt and both public keys
|
||||||
let client_hello_data = nym_lp::ClientHelloData::new_with_fresh_salt(
|
let client_hello_data = nym_lp::ClientHelloData::new_with_fresh_salt(
|
||||||
client_x25519_public.to_bytes(),
|
client_x25519_public,
|
||||||
self.local_ed25519_keypair.public_key().to_bytes(),
|
*self.local_ed25519_keypair.public_key(),
|
||||||
timestamp,
|
timestamp,
|
||||||
);
|
);
|
||||||
let salt = client_hello_data.salt;
|
let salt = client_hello_data.salt;
|
||||||
@@ -382,11 +385,9 @@ where
|
|||||||
let mut state_machine = LpStateMachine::new(
|
let mut state_machine = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true, // is_initiator
|
true, // is_initiator
|
||||||
(
|
self.local_ed25519_keypair.clone(),
|
||||||
self.local_ed25519_keypair.private_key(),
|
|
||||||
self.local_ed25519_keypair.public_key(),
|
|
||||||
),
|
|
||||||
&self.gateway_ed25519_public_key,
|
&self.gateway_ed25519_public_key,
|
||||||
|
&gateway_x25519_public,
|
||||||
&salt,
|
&salt,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -757,7 +758,7 @@ where
|
|||||||
tracing::trace!("Built registration request: {:?}", request);
|
tracing::trace!("Built registration request: {:?}", request);
|
||||||
|
|
||||||
// 2. Serialize the request
|
// 2. Serialize the request
|
||||||
let request_bytes = lp_bincode_serializer().serialize(&request).map_err(|e| {
|
let request_bytes = request.serialise().map_err(|e| {
|
||||||
LpClientError::SendRegistrationRequest(format!("Failed to serialize request: {e}"))
|
LpClientError::SendRegistrationRequest(format!("Failed to serialize request: {e}"))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -843,13 +844,11 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 8. Deserialize the response
|
// 8. Deserialize the response
|
||||||
let response: LpRegistrationResponse = lp_bincode_serializer()
|
let response = LpRegistrationResponse::try_deserialise(&response_data).map_err(|e| {
|
||||||
.deserialize(&response_data)
|
LpClientError::ReceiveRegistrationResponse(format!(
|
||||||
.map_err(|e| {
|
"Failed to deserialize registration response: {e}",
|
||||||
LpClientError::ReceiveRegistrationResponse(format!(
|
))
|
||||||
"Failed to deserialize registration response: {e}",
|
})?;
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Received registration response: success={}",
|
"Received registration response: success={}",
|
||||||
@@ -1037,11 +1036,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 2. Serialize the ForwardPacketData
|
// 2. Serialize the ForwardPacketData
|
||||||
let forward_data_bytes = lp_bincode_serializer()
|
let forward_data_bytes = forward_data.to_bytes();
|
||||||
.serialize(&forward_data)
|
|
||||||
.map_err(|e| {
|
|
||||||
LpClientError::Transport(format!("Failed to serialize ForwardPacketData: {e}"))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
tracing::trace!(
|
tracing::trace!(
|
||||||
"Serialized ForwardPacketData ({} bytes)",
|
"Serialized ForwardPacketData ({} bytes)",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
//! Error types for LP (Lewes Protocol) client operations.
|
//! Error types for LP (Lewes Protocol) client operations.
|
||||||
|
|
||||||
use nym_lp::LpError;
|
use nym_lp::LpError;
|
||||||
use nym_lp::serialisation::BincodeError;
|
use nym_registration_common::BincodeError;
|
||||||
use std::io;
|
use std::io;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ use nym_bandwidth_controller::BandwidthTicketProvider;
|
|||||||
use nym_credentials_interface::TicketType;
|
use nym_credentials_interface::TicketType;
|
||||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
use nym_lp::codec::{OuterAeadKey, parse_lp_packet, serialize_lp_packet};
|
use nym_lp::codec::{OuterAeadKey, parse_lp_packet, serialize_lp_packet};
|
||||||
use nym_lp::serialisation::{BincodeOptions, lp_bincode_serializer};
|
|
||||||
use nym_lp::state_machine::{LpAction, LpInput, LpStateMachine};
|
use nym_lp::state_machine::{LpAction, LpInput, LpStateMachine};
|
||||||
use nym_lp::{LpMessage, LpPacket};
|
use nym_lp::{LpMessage, LpPacket};
|
||||||
use nym_lp_transport::traits::LpTransport;
|
use nym_lp_transport::traits::LpTransport;
|
||||||
@@ -130,6 +129,10 @@ impl NestedLpSession {
|
|||||||
LpClientError::Crypto(format!("Failed to derive X25519 public key: {}", e))
|
LpClientError::Crypto(format!("Failed to derive X25519 public key: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let gateway_x25519_public = self.exit_public_key.to_x25519().map_err(|e| {
|
||||||
|
LpClientError::Crypto(format!("Failed to derive X25519 public key: {e}"))
|
||||||
|
})?;
|
||||||
|
|
||||||
let timestamp = SystemTime::now()
|
let timestamp = SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.map_err(|_| LpClientError::Other("System time before UNIX epoch".into()))?
|
.map_err(|_| LpClientError::Other("System time before UNIX epoch".into()))?
|
||||||
@@ -137,8 +140,8 @@ impl NestedLpSession {
|
|||||||
|
|
||||||
// Step 2: Generate ClientHello for exit gateway
|
// Step 2: Generate ClientHello for exit gateway
|
||||||
let client_hello_data = nym_lp::ClientHelloData::new_with_fresh_salt(
|
let client_hello_data = nym_lp::ClientHelloData::new_with_fresh_salt(
|
||||||
client_x25519_public.to_bytes(),
|
client_x25519_public,
|
||||||
self.client_keypair.public_key().to_bytes(),
|
*self.client_keypair.public_key(),
|
||||||
timestamp,
|
timestamp,
|
||||||
);
|
);
|
||||||
let salt = client_hello_data.salt;
|
let salt = client_hello_data.salt;
|
||||||
@@ -192,11 +195,9 @@ impl NestedLpSession {
|
|||||||
let mut state_machine = LpStateMachine::new(
|
let mut state_machine = LpStateMachine::new(
|
||||||
receiver_index,
|
receiver_index,
|
||||||
true, // is_initiator
|
true, // is_initiator
|
||||||
(
|
self.client_keypair.clone(),
|
||||||
self.client_keypair.private_key(),
|
|
||||||
self.client_keypair.public_key(),
|
|
||||||
),
|
|
||||||
&self.exit_public_key,
|
&self.exit_public_key,
|
||||||
|
&gateway_x25519_public,
|
||||||
&salt,
|
&salt,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -329,7 +330,7 @@ impl NestedLpSession {
|
|||||||
tracing::trace!("Built registration request: {:?}", request);
|
tracing::trace!("Built registration request: {:?}", request);
|
||||||
|
|
||||||
// Step 4: Serialize the request
|
// Step 4: Serialize the request
|
||||||
let request_bytes = lp_bincode_serializer().serialize(&request).map_err(|e| {
|
let request_bytes = request.serialise().map_err(|e| {
|
||||||
LpClientError::Transport(format!("Failed to serialize registration request: {}", e))
|
LpClientError::Transport(format!("Failed to serialize registration request: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -397,14 +398,12 @@ impl NestedLpSession {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Step 10: Deserialize the response
|
// Step 10: Deserialize the response
|
||||||
let response: LpRegistrationResponse = lp_bincode_serializer()
|
let response = LpRegistrationResponse::try_deserialise(&response_data).map_err(|e| {
|
||||||
.deserialize(&response_data)
|
LpClientError::Transport(format!(
|
||||||
.map_err(|e| {
|
"Failed to deserialize registration response: {}",
|
||||||
LpClientError::Transport(format!(
|
e
|
||||||
"Failed to deserialize registration response: {}",
|
))
|
||||||
e
|
})?;
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Received registration response from exit: success={}",
|
"Received registration response from exit: success={}",
|
||||||
@@ -505,7 +504,7 @@ impl NestedLpSession {
|
|||||||
tracing::trace!("Built registration request: {:?}", request);
|
tracing::trace!("Built registration request: {:?}", request);
|
||||||
|
|
||||||
// Step 5: Serialize the request
|
// Step 5: Serialize the request
|
||||||
let request_bytes = lp_bincode_serializer().serialize(&request).map_err(|e| {
|
let request_bytes = request.serialise().map_err(|e| {
|
||||||
LpClientError::Transport(format!("Failed to serialize registration request: {}", e))
|
LpClientError::Transport(format!("Failed to serialize registration request: {}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -573,14 +572,12 @@ impl NestedLpSession {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Step 11: Deserialize the response
|
// Step 11: Deserialize the response
|
||||||
let response: LpRegistrationResponse = lp_bincode_serializer()
|
let response = LpRegistrationResponse::try_deserialise(&response_data).map_err(|e| {
|
||||||
.deserialize(&response_data)
|
LpClientError::Transport(format!(
|
||||||
.map_err(|e| {
|
"Failed to deserialize registration response: {}",
|
||||||
LpClientError::Transport(format!(
|
e
|
||||||
"Failed to deserialize registration response: {}",
|
))
|
||||||
e
|
})?;
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"Received registration response from exit: success={}",
|
"Received registration response from exit: success={}",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-network-requester"
|
name = "nym-network-requester"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
version = "1.1.70"
|
version = "1.1.69"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version = "1.85"
|
rust-version = "1.85"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-cli"
|
name = "nym-cli"
|
||||||
version = "1.1.69"
|
version = "1.1.68"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nymvisor"
|
name = "nymvisor"
|
||||||
version = "0.1.34"
|
version = "0.1.33"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
homepage.workspace = true
|
homepage.workspace = true
|
||||||
|
|||||||
Reference in New Issue
Block a user