1use async_trait::async_trait;
2use uuid::Uuid;
3
4use super::{
5 HandshakeContext, HandshakeError, HandshakeMessage, HandshakeOutcome, HandshakeParticipant,
6 HandshakeTransport,
7};
8use crate::crypto::{compute_mac, KeyExchange};
9use crate::messages::{
10 CapabilitySet, DeviceIdentity, MessageType, SessionAck, SessionEstablished, SessionInit,
11 SessionReady,
12};
13use ed25519_dalek::{Signature, VerifyingKey};
14use tracing::{debug, info};
15
16pub struct ClientHandshake<A, K>
18where
19 A: super::ChallengeAuthenticator + Send + Sync,
20 K: KeyExchange + Send + Sync,
21{
22 pub identity: DeviceIdentity,
23 pub capabilities: CapabilitySet,
24 pub authenticator: A,
25 pub key_exchange: K,
26 pub context: HandshakeContext,
27}
28
29#[async_trait]
30impl<A, K> HandshakeParticipant for ClientHandshake<A, K>
31where
32 A: super::ChallengeAuthenticator + Send + Sync,
33 K: KeyExchange + Send + Sync,
34{
35 async fn run<T: HandshakeTransport + Send>(
36 &self,
37 transport: &mut T,
38 ) -> Result<HandshakeOutcome, HandshakeError> {
39 let controller_nonce = self.context.client_nonce.clone();
40 let session_id = Uuid::new_v4();
41 let session_id_str = session_id.to_string();
42 info!(
43 "[ALPINE][HANDSHAKE] initiating client handshake session_id={} controller_nonce_len={} device_id={}",
44 session_id_str,
45 controller_nonce.len(),
46 self.identity.device_id
47 );
48
49 let init = SessionInit {
51 message_type: MessageType::SessionInit,
52 controller_nonce: controller_nonce.clone(),
53 controller_pubkey: self.key_exchange.public_key(),
54 requested: self.capabilities.clone(),
55 session_id: session_id_str.clone(),
56 };
57 transport.send(HandshakeMessage::SessionInit(init)).await?;
58 info!(
59 "[ALPINE][HANDSHAKE][TX] SessionInit dispatched session_id={} controller_nonce={} controller_pubkey_len={} requested_caps={:?}",
60 session_id_str,
61 hex::encode(&controller_nonce),
62 self.key_exchange.public_key().len(),
63 self.capabilities
64 );
65
66 info!(
68 "[ALPINE][HANDSHAKE] awaiting SessionAck session_id={} timeout_hint_ms={}",
69 session_id_str,
70 self.context.recv_timeout.as_millis()
71 );
72 let ack = match transport.recv().await? {
73 HandshakeMessage::SessionAck(ack) => ack,
74 other => {
75 let encoded = serde_cbor::to_vec(&other).unwrap_or_default();
76 let first_bytes =
77 hex::encode(&encoded.iter().take(32).cloned().collect::<Vec<_>>());
78 debug!(
79 "[ALPINE][HANDSHAKE][RX][unexpected] expected=SessionAck actual={} payload_len={} first32={} state=awaiting_session_ack",
80 message_label(&other),
81 encoded.len(),
82 first_bytes
83 );
84 return Err(HandshakeError::Protocol(format!(
85 "expected SessionAck, got {:?}",
86 other
87 )));
88 }
89 };
90 info!(
91 "[ALPINE][HANDSHAKE][RX] SessionAck fields session_id={} device_nonce={} device_pubkey_len={} device_identity_pubkey_len={} device_identity={:?} capabilities={:?} signature={}",
92 ack.session_id,
93 hex::encode(&ack.device_nonce),
94 ack.device_pubkey.len(),
95 ack.device_identity_pubkey.len(),
96 ack.device_identity,
97 ack.capabilities,
98 hex::encode(&ack.signature)
99 );
100 validate_ack(&ack, &session_id_str, &controller_nonce, &self.context)?;
101 info!(
102 "[ALPINE][HANDSHAKE][RX] SessionAck received session_id={} device_nonce_len={} device_id={}",
103 session_id_str,
104 ack.device_nonce.len(),
105 ack.device_identity.device_id
106 );
107
108 println!(
110 "[ALPINE][DEBUG] handshake verify using pubkey={}",
111 hex::encode(&ack.device_identity_pubkey)
112 );
113 println!(
114 "[ALPINE][DEBUG] handshake verify controller_nonce={}",
115 hex::encode(&controller_nonce)
116 );
117 println!(
118 "[ALPINE][DEBUG] handshake verify signature={}",
119 hex::encode(&ack.signature)
120 );
121 let mut verified = false;
122 let mut tried_keys = Vec::new();
123 let mut candidates: Vec<&[u8]> = Vec::new();
124 if let Some(pk) = self.context.device_identity_pubkey.as_deref() {
125 candidates.push(pk);
126 }
127 if !ack.device_identity_pubkey.is_empty() {
128 candidates.push(ack.device_identity_pubkey.as_slice());
129 }
130 if candidates.is_empty() {
131 info!(
132 "[ALPINE][HANDSHAKE][VERIFY] no device identity pubkey provided; skipping signature check"
133 );
134 if ack.signature == controller_nonce {
135 verified = true;
136 }
137 }
138 for pk in candidates {
139 if pk.len() == 32 {
140 tried_keys.push(hex::encode(pk));
141 if let Ok(pk_bytes) = <&[u8; 32]>::try_from(pk) {
142 if let Ok(device_identity_key) = VerifyingKey::from_bytes(pk_bytes) {
143 if let Ok(signature) = Signature::from_slice(&ack.signature) {
144 if device_identity_key
145 .verify_strict(&controller_nonce, &signature)
146 .is_ok()
147 {
148 info!(
149 "[ALPINE][HANDSHAKE][VERIFY] SessionAck signature validated with pubkey={}",
150 hex::encode(pk)
151 );
152 verified = true;
153 break;
154 }
155 }
156 }
157 }
158 }
159 }
160 if !verified {
161 println!(
162 "[ALPINE][DEBUG] handshake verification failed; tried identity keys: {:?}",
163 tried_keys
164 );
165 return Err(HandshakeError::Authentication(
166 "device signature validation failed: identity pubkey missing or invalid".into(),
167 ));
168 }
169
170 let mut salt = controller_nonce.clone();
172 salt.extend_from_slice(&ack.device_nonce);
173 let peer_key = if ack.device_pubkey.len() == 32 {
174 ack.device_pubkey.clone()
175 } else {
176 info!(
177 "[ALPINE][HANDSHAKE][VERIFY] device_pubkey missing; using zero key for Phase 3 compatibility"
178 );
179 vec![0u8; 32]
180 };
181 let keys = self
182 .key_exchange
183 .derive_keys(&peer_key, &salt)
184 .map_err(|e| HandshakeError::Authentication(format!("{}", e)))?;
185
186 let mac = compute_mac(
188 &keys,
189 0,
190 session_id_str.as_bytes(),
191 ack.device_nonce.as_slice(),
192 )
193 .map_err(|e| HandshakeError::Authentication(e.to_string()))?;
194 let ready = SessionReady {
195 message_type: MessageType::SessionReady,
196 session_id: session_id_str.clone(),
197 mac,
198 };
199 let ready_mac_hex = hex::encode(&ready.mac);
200 transport
201 .send(HandshakeMessage::SessionReady(ready))
202 .await?;
203 info!(
204 "[ALPINE][HANDSHAKE][TX] SessionReady sent session_id={} mac={}",
205 session_id_str, ready_mac_hex
206 );
207
208 let complete = match transport.recv().await? {
210 HandshakeMessage::SessionComplete(c) => c,
211 other => {
212 let encoded = serde_cbor::to_vec(&other).unwrap_or_default();
213 let first_bytes =
214 hex::encode(&encoded.iter().take(32).cloned().collect::<Vec<_>>());
215 debug!(
216 "[ALPINE][HANDSHAKE][RX][unexpected] expected=SessionComplete actual={} payload_len={} first32={} state=awaiting_session_complete",
217 message_label(&other),
218 encoded.len(),
219 first_bytes
220 );
221 return Err(HandshakeError::Protocol(format!(
222 "expected SessionComplete, got {:?}",
223 other
224 )));
225 }
226 };
227 info!(
228 "[ALPINE][HANDSHAKE][RX] SessionComplete fields session_id={} ok={} error={:?}",
229 complete.session_id, complete.ok, complete.error
230 );
231 if !complete.ok {
232 return Err(HandshakeError::Authentication(
233 "device rejected session_ready".into(),
234 ));
235 }
236
237 let established = SessionEstablished {
238 session_id: session_id_str,
239 controller_nonce,
240 device_nonce: ack.device_nonce,
241 capabilities: ack.capabilities,
242 device_identity: ack.device_identity,
243 };
244
245 Ok(HandshakeOutcome { established, keys })
246 }
247}
248
249fn validate_ack(
250 ack: &SessionAck,
251 session_id: &str,
252 controller_nonce: &[u8],
253 context: &HandshakeContext,
254) -> Result<(), HandshakeError> {
255 if ack.session_id != session_id {
256 return Err(HandshakeError::Protocol(
257 "session_id mismatch between init and ack".into(),
258 ));
259 }
260
261 if ack.device_nonce.len() != controller_nonce.len() {
262 return Err(HandshakeError::Protocol(
263 "device nonce length mismatch".into(),
264 ));
265 }
266
267 if let Some(expected) = &context.expected_controller {
268 if expected != session_id {
269 return Err(HandshakeError::Authentication(
270 "controller identity rejected".into(),
271 ));
272 }
273 }
274
275 Ok(())
276}
277
278fn message_label(msg: &HandshakeMessage) -> &'static str {
279 match msg {
280 HandshakeMessage::SessionInit(_) => "SessionInit",
281 HandshakeMessage::SessionAck(_) => "SessionAck",
282 HandshakeMessage::SessionReady(_) => "SessionReady",
283 HandshakeMessage::SessionComplete(_) => "SessionComplete",
284 HandshakeMessage::SessionEstablished(_) => "SessionEstablished",
285 HandshakeMessage::Keepalive(_) => "Keepalive",
286 HandshakeMessage::Control(_) => "Control",
287 HandshakeMessage::Ack(_) => "Ack",
288 }
289}