Add LpConfig struct and AIDEV-NOTE documentation for KKT+PSQ

- Create config.rs with LpConfig struct (kem_algorithm, psk_ttl, enable_kkt)
- Export LpConfig from lib.rs
- Add AIDEV-NOTE to psk.rs explaining:
  - Why PSQ is embedded in Noise (single round-trip, PSK binding)
  - KEM migration path (X25519 → MlKem768 → XWing)
- Add AIDEV-NOTE to state_machine.rs explaining protocol flow:
  - KKTExchange → Handshaking → Transport state transitions
  - PSK derivation formula (ECDH || PSQ || salt)
This commit is contained in:
durch
2025-12-16 20:15:17 +01:00
committed by Jędrzej Stuczyński
parent b5f1bab87b
commit bb76ff82bd
4 changed files with 103 additions and 1 deletions
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
//! Configuration for LP protocol.
//!
//! AIDEV-NOTE: LP security stack = KKT (key fetch) → PSQ (PQ PSK) → Noise (transport).
//! KEM algorithm selection affects only PSQ layer. Noise always uses X25519 DH.
//! Migration to PQ KEMs (MlKem768, XWing) requires only config change.
use nym_kkt::ciphersuite::KEM;
use serde::{Deserialize, Serialize};
use std::time::Duration;
/// Default PSK time-to-live (1 hour, matches psk.rs implementation).
pub const DEFAULT_PSK_TTL_SECS: u64 = 3600;
/// Configuration for LP protocol.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LpConfig {
/// KEM algorithm for PSQ key encapsulation.
/// X25519 = classical (testing), MlKem768 = PQ, XWing = hybrid.
#[serde(with = "kem_serde")]
pub kem_algorithm: KEM,
/// PSK time-to-live in seconds.
pub psk_ttl_secs: u64,
/// Enable KKT for authenticated key distribution.
pub enable_kkt: bool,
}
impl Default for LpConfig {
fn default() -> Self {
Self {
kem_algorithm: KEM::X25519,
psk_ttl_secs: DEFAULT_PSK_TTL_SECS,
enable_kkt: true,
}
}
}
impl LpConfig {
/// Returns PSK TTL as Duration.
pub fn psk_ttl(&self) -> Duration {
Duration::from_secs(self.psk_ttl_secs)
}
}
mod kem_serde {
use nym_kkt::ciphersuite::KEM;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(kem: &KEM, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match kem {
KEM::X25519 => "X25519",
KEM::MlKem768 => "MlKem768",
KEM::XWing => "XWing",
KEM::McEliece => "McEliece",
}
.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<KEM, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"X25519" => Ok(KEM::X25519),
"MlKem768" => Ok(KEM::MlKem768),
"XWing" => Ok(KEM::XWing),
"McEliece" => Ok(KEM::McEliece),
_ => Err(serde::de::Error::custom(format!("Unknown KEM: {}", s))),
}
}
}
+2
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
pub mod codec;
pub mod config;
pub mod error;
pub mod keypair;
pub mod kkt_orchestrator;
@@ -14,6 +15,7 @@ pub mod session;
mod session_integration;
pub mod session_manager;
pub use config::LpConfig;
pub use error::LpError;
pub use message::{ClientHelloData, LpMessage};
pub use packet::{BOOTSTRAP_RECEIVER_IDX, LpPacket, OuterHeader};
+12 -1
View File
@@ -6,6 +6,12 @@
//! This module implements identity-bound PSK derivation where both client and gateway
//! derive the same PSK from their LP keypairs.
//!
//! AIDEV-NOTE: PSQ is embedded in Noise (not separate protocol) because:
//! 1. Single round-trip: PSQ ciphertext piggybacks on Noise handshake messages
//! 2. PSK binding: Noise XKpsk3 pattern authenticates both ECDH and PSQ-derived PSK
//! 3. Simpler state machine: No separate PSQ negotiation phase needed
//! 4. Atomic security: Session establishment either succeeds fully or fails completely
//!
//! Two approaches are supported:
//! - **Legacy ECDH-only** (`derive_psk`) - Simple but no post-quantum security
//! - **PSQ-enhanced** (`derive_psk_with_psq_*`) - Combines ECDH with post-quantum KEM
@@ -140,7 +146,12 @@ pub fn derive_psk_with_psq_initiator(
let ecdh_secret = local_x25519_private.diffie_hellman(remote_x25519_public);
// Step 2: PSQ encapsulation for post-quantum security
// Extract X25519 public key from EncapsulationKey
// AIDEV-NOTE: KEM algorithm migration path:
// - X25519: Current default for testing/compatibility (no HNDL resistance)
// - MlKem768: Future production default (NIST PQ Level 3, HNDL resistant)
// - XWing: Maximum security option (hybrid X25519 + ML-KEM)
// Migration: Update LpConfig.kem_algorithm, no protocol changes needed.
// KKT protocol adapts automatically to different KEM key sizes.
let kem_pk = match remote_kem_public {
EncapsulationKey::X25519(pk) => pk,
_ => {
+10
View File
@@ -2,6 +2,16 @@
// SPDX-License-Identifier: Apache-2.0
//! Lewes Protocol State Machine for managing connection lifecycle.
//!
//! AIDEV-NOTE: LP protocol flow (KKT → PSQ → Noise):
//! 1. KKTExchange: Client requests gateway's KEM public key (signed for MITM protection)
//! 2. Handshaking: Noise XKpsk3 with PSQ-derived PSK embedded in handshake messages
//! - PSQ ciphertext piggybacked on ClientHello (no extra round-trip)
//! - PSK = Blake3(ECDH || PSQ_secret || salt) provides hybrid classical+PQ security
//! 3. Transport: ChaCha20-Poly1305 authenticated encryption with derived keys
//!
//! State machine ensures protocol steps execute in correct order. Invalid transitions
//! return LpError, preventing protocol violations.
use crate::{
LpError,