1use std::time::{SystemTime, UNIX_EPOCH};
2
3use crate::crypto::{compute_mac, verify_mac, SessionKeys};
4use crate::handshake::HandshakeError;
5use crate::messages::{Acknowledge, ControlEnvelope, ControlOp, MessageType};
6use crate::{handshake::transport::ReliableControlChannel, handshake::HandshakeTransport};
7use serde::{de::DeserializeOwned, Serialize};
8use uuid::Uuid;
9
10#[derive(Debug)]
12pub struct ControlCrypto {
13 keys: SessionKeys,
14}
15
16impl ControlCrypto {
17 pub fn new(keys: SessionKeys) -> Self {
18 Self { keys }
19 }
20
21 pub fn mac_for_payload(
22 &self,
23 seq: u64,
24 session_id: &Uuid,
25 payload: &serde_json::Value,
26 ) -> Result<Vec<u8>, HandshakeError> {
27 let bytes = serde_cbor::to_vec(payload)
28 .map_err(|e| HandshakeError::Protocol(format!("payload encode: {}", e)))?;
29 compute_mac(&self.keys, seq, &bytes, session_id.as_bytes())
30 .map_err(|e| HandshakeError::Authentication(e.to_string()))
31 }
32
33 pub fn verify_mac(
34 &self,
35 seq: u64,
36 session_id: &Uuid,
37 payload: &serde_json::Value,
38 mac: &[u8],
39 ) -> Result<(), HandshakeError> {
40 let bytes = serde_cbor::to_vec(payload)
41 .map_err(|e| HandshakeError::Protocol(format!("payload encode: {}", e)))?;
42 if verify_mac(&self.keys, seq, &bytes, session_id.as_bytes(), mac) {
43 Ok(())
44 } else {
45 Err(HandshakeError::Authentication(
46 "control MAC validation failed".into(),
47 ))
48 }
49 }
50
51 pub fn mac_for_ack(
52 &self,
53 seq: u64,
54 session_id: &Uuid,
55 ok: bool,
56 detail: Option<&str>,
57 payload: Option<&[u8]>,
58 ) -> Result<Vec<u8>, HandshakeError> {
59 let record = AckMacRecord {
60 ok,
61 detail,
62 payload,
63 };
64 let bytes = serde_cbor::to_vec(&record)
65 .map_err(|e| HandshakeError::Protocol(format!("ack encode: {}", e)))?;
66 compute_mac(&self.keys, seq, &bytes, session_id.as_bytes())
67 .map_err(|e| HandshakeError::Authentication(e.to_string()))
68 }
69
70 pub fn decode_ack_payload<T>(payload: Option<&[u8]>) -> Result<Option<T>, HandshakeError>
71 where
72 T: DeserializeOwned,
73 {
74 if let Some(bytes) = payload {
75 serde_cbor::from_slice(bytes)
76 .map(Some)
77 .map_err(|e| HandshakeError::Protocol(format!("ack payload decode: {}", e)))
78 } else {
79 Ok(None)
80 }
81 }
82}
83
84#[derive(Serialize)]
85struct AckMacRecord<'a> {
86 ok: bool,
87 detail: Option<&'a str>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 payload: Option<&'a [u8]>,
90}
91
92#[derive(Debug)]
94pub struct ControlClient {
95 pub device_id: Uuid,
96 pub crypto: ControlCrypto,
97 pub session_id: Uuid,
98}
99
100impl ControlClient {
101 pub fn new(device_id: Uuid, session_id: Uuid, crypto: ControlCrypto) -> Self {
102 Self {
103 device_id,
104 crypto,
105 session_id,
106 }
107 }
108
109 pub fn envelope(
110 &self,
111 seq: u64,
112 op: ControlOp,
113 payload: serde_json::Value,
114 ) -> Result<ControlEnvelope, HandshakeError> {
115 let mac = self
116 .crypto
117 .mac_for_payload(seq, &self.session_id, &payload)?;
118 Ok(ControlEnvelope {
119 message_type: MessageType::AlpineControl,
120 session_id: self.session_id,
121 seq,
122 op,
123 payload,
124 mac,
125 })
126 }
127
128 pub async fn send<T: HandshakeTransport + Send>(
129 &self,
130 channel: &mut ReliableControlChannel<T>,
131 op: ControlOp,
132 payload: serde_json::Value,
133 ) -> Result<Acknowledge, HandshakeError> {
134 let seq = channel.next_seq();
135 let env = self.envelope(seq, op, payload)?;
136 channel.send_reliable(env).await
137 }
138
139 pub fn now_ms() -> u64 {
140 SystemTime::now()
141 .duration_since(UNIX_EPOCH)
142 .unwrap_or_default()
143 .as_millis() as u64
144 }
145}
146
147pub struct ControlResponder {
149 pub crypto: ControlCrypto,
150 pub session_id: Uuid,
151}
152
153impl ControlResponder {
154 pub fn new(session_id: Uuid, crypto: ControlCrypto) -> Self {
155 Self { crypto, session_id }
156 }
157
158 pub fn verify(&self, env: &ControlEnvelope) -> Result<(), HandshakeError> {
159 self.crypto
160 .verify_mac(env.seq, &env.session_id, &env.payload, &env.mac)
161 }
162
163 pub fn ack(
164 &self,
165 seq: u64,
166 ok: bool,
167 detail: Option<String>,
168 payload: Option<Vec<u8>>,
169 ) -> Result<Acknowledge, HandshakeError> {
170 let mac = self.crypto.mac_for_ack(
171 seq,
172 &self.session_id,
173 ok,
174 detail.as_deref(),
175 payload.as_deref(),
176 )?;
177 Ok(Acknowledge {
178 message_type: MessageType::AlpineControlAck,
179 session_id: self.session_id,
180 seq,
181 ok,
182 detail,
183 payload,
184 mac,
185 })
186 }
187}