alpine/handshake/
server.rs

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