mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-11 17:17:54 +00:00
Session 46: Noise IK peer authentication implementation
Implement Noise Protocol Framework for peer authentication using the IK pattern, which allows responders to learn initiator identity from the encrypted handshake message. Protocol: Noise_IK_secp256k1_ChaChaPoly_SHA256 - Message 1: 82 bytes (ephemeral + encrypted static) - Message 2: 33 bytes (ephemeral only) New noise.rs module (~600 lines): - HandshakeState: Manages handshake for both initiator/responder roles - NoiseSession: Post-handshake symmetric encryption - CipherState: ChaCha20-Poly1305 with nonce counter - SymmetricState: HKDF-SHA256 key derivation PeerConnection integration: - Simplified HandshakeState enum (Initial/SentMsg1/ReceivedMsg1/Complete/Failed) - start_handshake(): Initiator generates msg1 - receive_handshake_init(): Responder processes msg1, discovers identity - complete_handshake(): Initiator completes with msg2 - take_session(): Extract NoiseSession for ActivePeer Identity helpers: - Identity::keypair() for Noise operations - PeerIdentity::from_pubkey_full() preserves parity for ECDH - PeerIdentity::pubkey_full() returns full key or derives with even parity Node changes: - initiate_peer_connection() now starts handshake and sends msg1 - Method is async to use transport's async send All 219 tests pass.
This commit is contained in:
+51
-8
@@ -376,9 +376,8 @@ impl Node {
|
||||
/// Initiate connections to configured static peers.
|
||||
///
|
||||
/// For each peer configured with AutoConnect policy, creates a link and
|
||||
/// peer entry. The peer starts in Connecting state; authentication
|
||||
/// handshake will be handled by the event loop.
|
||||
fn initiate_peer_connections(&mut self) {
|
||||
/// peer entry, then starts the Noise handshake by sending the first message.
|
||||
async fn initiate_peer_connections(&mut self) {
|
||||
// Collect peer configs to avoid borrow conflicts
|
||||
let peer_configs: Vec<_> = self.config.auto_connect_peers().cloned().collect();
|
||||
|
||||
@@ -390,7 +389,7 @@ impl Node {
|
||||
info!(count = peer_configs.len(), "Initiating static peer connections");
|
||||
|
||||
for peer_config in peer_configs {
|
||||
if let Err(e) = self.initiate_peer_connection(&peer_config) {
|
||||
if let Err(e) = self.initiate_peer_connection(&peer_config).await {
|
||||
warn!(
|
||||
npub = %peer_config.npub,
|
||||
alias = ?peer_config.alias,
|
||||
@@ -402,7 +401,9 @@ impl Node {
|
||||
}
|
||||
|
||||
/// Initiate a connection to a single peer.
|
||||
fn initiate_peer_connection(&mut self, peer_config: &PeerConfig) -> Result<(), NodeError> {
|
||||
///
|
||||
/// Creates a link, starts the Noise handshake, and sends the first message.
|
||||
async fn initiate_peer_connection(&mut self, peer_config: &PeerConfig) -> Result<(), NodeError> {
|
||||
// Parse the peer's npub to get their identity
|
||||
let peer_identity = PeerIdentity::from_npub(&peer_config.npub).map_err(|e| {
|
||||
NodeError::InvalidPeerNpub {
|
||||
@@ -469,14 +470,31 @@ impl Node {
|
||||
|
||||
// Add reverse lookup for packet dispatch
|
||||
self.addr_to_link
|
||||
.insert((transport_id, remote_addr), link_id);
|
||||
.insert((transport_id, remote_addr.clone()), link_id);
|
||||
|
||||
// Create connection in handshake phase (outbound knows expected identity)
|
||||
let current_time_ms = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_millis() as u64)
|
||||
.unwrap_or(0);
|
||||
let connection = PeerConnection::outbound(link_id, peer_identity.clone(), current_time_ms);
|
||||
let mut connection = PeerConnection::outbound(link_id, peer_identity.clone(), current_time_ms);
|
||||
|
||||
// Start the Noise handshake and get message 1
|
||||
let our_keypair = self.identity.keypair();
|
||||
let handshake_msg = match connection.start_handshake(our_keypair, current_time_ms) {
|
||||
Ok(msg) => msg,
|
||||
Err(e) => {
|
||||
warn!(
|
||||
npub = %peer_config.npub,
|
||||
error = %e,
|
||||
"Failed to start handshake"
|
||||
);
|
||||
// Clean up the link we just created
|
||||
self.links.remove(&link_id);
|
||||
self.addr_to_link.remove(&(transport_id, remote_addr));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let alias_display = peer_config
|
||||
.alias
|
||||
@@ -493,6 +511,31 @@ impl Node {
|
||||
|
||||
self.connections.insert(link_id, connection);
|
||||
|
||||
// Send the handshake message
|
||||
if let Some(transport) = self.transports.get(&transport_id) {
|
||||
match transport.send(&remote_addr, &handshake_msg).await {
|
||||
Ok(bytes) => {
|
||||
debug!(
|
||||
link_id = %link_id,
|
||||
bytes,
|
||||
"Sent Noise handshake message 1"
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
link_id = %link_id,
|
||||
error = %e,
|
||||
"Failed to send handshake message"
|
||||
);
|
||||
// Mark connection as failed but don't remove it yet
|
||||
// The event loop can handle retry logic
|
||||
if let Some(conn) = self.connections.get_mut(&link_id) {
|
||||
conn.mark_failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Successfully initiated connection via this address
|
||||
return Ok(());
|
||||
}
|
||||
@@ -989,7 +1032,7 @@ impl Node {
|
||||
}
|
||||
|
||||
// Connect to static peers (step 5 per architecture doc)
|
||||
self.initiate_peer_connections();
|
||||
self.initiate_peer_connections().await;
|
||||
|
||||
self.state = NodeState::Running;
|
||||
info!(
|
||||
|
||||
Reference in New Issue
Block a user