alpine/handshake/
server.rs

1use async_trait::async_trait;
2
3use super::{
4    new_nonce, ChallengeAuthenticator, HandshakeContext, HandshakeError, HandshakeMessage,
5    HandshakeOutcome, HandshakeParticipant, HandshakeTransport,
6};
7use crate::crypto::{compute_mac, KeyExchange};
8use crate::messages::{
9    CapabilitySet, DeviceIdentity, MessageType, SessionAck, SessionComplete, SessionEstablished,
10};
11
12/// Node-side handshake driver that validates the controller and proves identity.
13pub struct ServerHandshake<A, K>
14where
15    A: ChallengeAuthenticator + Send + Sync,
16    K: KeyExchange + Send + Sync,
17{
18    pub identity: DeviceIdentity,
19    pub capabilities: CapabilitySet,
20    pub authenticator: A,
21    pub key_exchange: K,
22    pub context: HandshakeContext,
23}
24
25#[async_trait]
26impl<A, K> HandshakeParticipant for ServerHandshake<A, K>
27where
28    A: ChallengeAuthenticator + Send + Sync,
29    K: KeyExchange + Send + Sync,
30{
31    async fn run<T: HandshakeTransport + Send>(
32        &self,
33        transport: &mut T,
34    ) -> Result<HandshakeOutcome, HandshakeError> {
35        // 1) Controller -> device: session_init
36        let init = match transport.recv().await? {
37            HandshakeMessage::SessionInit(msg) => msg,
38            other => {
39                return Err(HandshakeError::Protocol(format!(
40                    "expected SessionInit, got {:?}",
41                    other
42                )))
43            }
44        };
45
46        if let Some(expected) = &self.context.expected_controller {
47            if expected != &init.session_id.to_string() {
48                return Err(HandshakeError::Authentication(
49                    "controller identity not authorized".into(),
50                ));
51            }
52        }
53
54        // 2) Device -> controller: session_ack
55        let device_nonce = new_nonce().to_vec();
56        let signature = self.authenticator.sign_challenge(&init.controller_nonce);
57        let ack = SessionAck {
58            message_type: MessageType::SessionAck,
59            device_nonce: device_nonce.clone(),
60            device_pubkey: self.key_exchange.public_key(),
61            device_identity: self.identity.clone(),
62            capabilities: self.capabilities.clone(),
63            signature,
64            session_id: init.session_id,
65        };
66        transport
67            .send(HandshakeMessage::SessionAck(ack.clone()))
68            .await?;
69
70        // 3) Controller -> device: session_ready (validate MAC)
71        let ready = match transport.recv().await? {
72            HandshakeMessage::SessionReady(r) => r,
73            other => {
74                return Err(HandshakeError::Protocol(format!(
75                    "expected SessionReady, got {:?}",
76                    other
77                )))
78            }
79        };
80
81        if ready.session_id != init.session_id {
82            return Err(HandshakeError::Protocol(
83                "session_id mismatch between init and ready".into(),
84            ));
85        }
86
87        let mut salt = init.controller_nonce.clone();
88        salt.extend_from_slice(&device_nonce);
89        let keys = self
90            .key_exchange
91            .derive_keys(&init.controller_pubkey, &salt)
92            .map_err(|e| HandshakeError::Authentication(format!("{}", e)))?;
93        let mac_valid = compute_mac(
94            &keys,
95            0,
96            init.session_id.as_bytes(),
97            device_nonce.as_slice(),
98        )
99        .map(|expected| expected == ready.mac)
100        .unwrap_or(false);
101        if !mac_valid {
102            return Err(HandshakeError::Authentication(
103                "session_ready MAC invalid".into(),
104            ));
105        }
106
107        // 4) Device -> controller: session_complete
108        let complete = SessionComplete {
109            message_type: MessageType::SessionComplete,
110            session_id: init.session_id,
111            ok: true,
112            error: None,
113        };
114        transport
115            .send(HandshakeMessage::SessionComplete(complete))
116            .await?;
117
118        let established = SessionEstablished {
119            session_id: init.session_id,
120            controller_nonce: init.controller_nonce,
121            device_nonce,
122            capabilities: init.requested,
123            device_identity: self.identity.clone(),
124        };
125
126        Ok(HandshakeOutcome { established, keys })
127    }
128}