1use core::borrow::Borrow;
19use core::future::Future;
20use core::mem::MaybeUninit;
21
22use num_derive::FromPrimitive;
23
24use crate::crypto::Crypto;
25use crate::dm::AttrChangeNotifier;
26use crate::error::{Error, ErrorCode};
27use crate::respond::ExchangeHandler;
28use crate::tlv::{FromTLV, ToTLV};
29use crate::transport::exchange::{Exchange, MessageMeta};
30use crate::utils::init::InitMaybeUninit;
31use crate::utils::storage::{ReadBuf, WriteBuf};
32
33use case::CaseResponder;
34use pase::PaseResponder;
35
36pub mod busy;
37pub mod case;
38pub mod checkin;
39#[cfg(feature = "groups")]
40pub mod mcsp;
41pub mod pase;
42
43pub const PROTO_ID_SECURE_CHANNEL: u16 = 0x00;
45
46#[derive(FromPrimitive, Debug, Copy, Clone, Eq, PartialEq)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub enum OpCode {
49 MsgCounterSyncReq = 0x00,
50 MsgCounterSyncResp = 0x01,
51 MRPStandAloneAck = 0x10,
52 PBKDFParamRequest = 0x20,
53 PBKDFParamResponse = 0x21,
54 PASEPake1 = 0x22,
55 PASEPake2 = 0x23,
56 PASEPake3 = 0x24,
57 CASESigma1 = 0x30,
58 CASESigma2 = 0x31,
59 CASESigma3 = 0x32,
60 CASESigma2Resume = 0x33,
61 StatusReport = 0x40,
62 CheckIn = 0x50,
63}
64
65impl OpCode {
66 pub fn meta(&self) -> MessageMeta {
67 MessageMeta {
68 proto_id: PROTO_ID_SECURE_CHANNEL,
69 proto_opcode: *self as u8,
70 reliable: !matches!(self, Self::MRPStandAloneAck | Self::CheckIn),
73 }
74 }
75
76 pub fn is_tlv(&self) -> bool {
77 !matches!(
78 self,
79 Self::MRPStandAloneAck
80 | Self::StatusReport
81 | Self::MsgCounterSyncReq
82 | Self::MsgCounterSyncResp
83 | Self::CheckIn
84 )
85 }
86}
87
88impl From<OpCode> for MessageMeta {
89 fn from(op: OpCode) -> Self {
90 op.meta()
91 }
92}
93
94#[derive(Debug, Copy, Clone, PartialEq, Eq)]
95#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96pub enum SCStatusCodes {
97 SessionEstablishmentSuccess = 0,
98 NoSharedTrustRoots = 1,
99 InvalidParameter = 2,
100 CloseSession = 3,
101 Busy = 4,
102 SessionNotFound = 5,
103}
104
105impl SCStatusCodes {
106 pub fn reliable(&self) -> bool {
107 !matches!(
109 self,
110 SCStatusCodes::CloseSession | SCStatusCodes::Busy | SCStatusCodes::SessionNotFound
111 )
112 }
113
114 pub fn as_report<'a>(&self, payload: &'a [u8]) -> StatusReport<'a> {
115 let general_code = match self {
116 SCStatusCodes::SessionEstablishmentSuccess => GeneralCode::Success,
117 SCStatusCodes::CloseSession => GeneralCode::Success,
118 SCStatusCodes::Busy => GeneralCode::Busy,
119 SCStatusCodes::InvalidParameter
120 | SCStatusCodes::NoSharedTrustRoots
121 | SCStatusCodes::SessionNotFound => GeneralCode::Failure,
122 };
123
124 StatusReport {
125 general_code,
126 proto_id: PROTO_ID_SECURE_CHANNEL as u32,
127 proto_code: *self as u16,
128 proto_data: payload,
129 }
130 }
131}
132
133pub async fn complete_with_status(
134 exchange: &mut Exchange<'_>,
135 status_code: SCStatusCodes,
136 payload: &[u8],
137) -> Result<(), Error> {
138 exchange
139 .send_with(|_, wb| sc_write(wb, status_code, payload))
140 .await
141}
142
143pub fn sc_write(
144 wb: &mut WriteBuf,
145 status_code: SCStatusCodes,
146 payload: &[u8],
147) -> Result<Option<MessageMeta>, Error> {
148 status_code.as_report(payload).write(wb)?;
149
150 Ok(Some(
151 OpCode::StatusReport.meta().reliable(status_code.reliable()),
152 ))
153}
154
155#[allow(dead_code)]
156#[derive(FromPrimitive, PartialEq, Eq, Debug, Copy, Clone)]
157#[cfg_attr(feature = "defmt", derive(defmt::Format))]
158pub enum GeneralCode {
159 Success = 0,
160 Failure = 1,
161 BadPrecondition = 2,
162 OutOfRange = 3,
163 BadRequest = 4,
164 Unsupported = 5,
165 Unexpected = 6,
166 ResourceExhausted = 7,
167 Busy = 8,
168 Timeout = 9,
169 Continue = 10,
170 Aborted = 11,
171 InvalidArgument = 12,
172 NotFound = 13,
173 AlreadyExists = 14,
174 PermissionDenied = 15,
175 DataLoss = 16,
176}
177
178#[derive(Default, Clone, FromTLV, ToTLV, Debug)]
181#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182#[tlvargs(start = 1)]
183pub(crate) struct SessionParameters {
184 pub(crate) sii: Option<u32>,
186 pub(crate) sai: Option<u32>,
188 pub(crate) sat: Option<u16>,
190 pub(crate) dm_revision: Option<u16>,
192 pub(crate) im_revision: Option<u16>,
194 pub(crate) spec_version: Option<u32>,
196 pub(crate) max_paths_per_invoke: Option<u16>,
198}
199
200#[derive(Debug, Clone)]
202#[cfg_attr(feature = "defmt", derive(defmt::Format))]
203pub struct StatusReport<'a> {
204 pub general_code: GeneralCode,
205 pub proto_id: u32,
206 pub proto_code: u16,
207 pub proto_data: &'a [u8],
208}
209
210impl<'a> StatusReport<'a> {
211 pub fn read<T>(pb: &'a mut ReadBuf<T>) -> Result<Self, Error>
212 where
213 T: Borrow<[u8]>,
214 {
215 Ok(Self {
216 general_code: num::FromPrimitive::from_u16(pb.le_u16()?)
217 .ok_or(ErrorCode::InvalidOpcode)?,
218 proto_id: pb.le_u32()?,
219 proto_code: pb.le_u16()?,
220 proto_data: pb.as_slice(),
221 })
222 }
223
224 pub fn write(&self, wb: &mut WriteBuf) -> Result<(), Error> {
225 wb.le_u16(self.general_code as u16)?;
226 wb.le_u32(self.proto_id)?;
227 wb.le_u16(self.proto_code)?;
228 wb.copy_from_slice(self.proto_data)?;
229
230 Ok(())
231 }
232}
233
234pub trait AsyncScHandler {
243 async fn check_in(&self, _exchange: Exchange<'_>) -> Result<(), Error> {
245 warn!("Check-In: Unexpected Check-In message received; dropping");
246 Ok(())
247 }
248
249 async fn mcsp_resp(&self, _exchange: Exchange<'_>) -> Result<(), Error> {
252 warn!("MCSP: Unsolicited MsgCounterSyncResp received; dropping");
253 Ok(())
254 }
255}
256
257impl AsyncScHandler for () {}
258
259impl<T> AsyncScHandler for &T
260where
261 T: AsyncScHandler,
262{
263 async fn check_in(&self, exchange: Exchange<'_>) -> Result<(), Error> {
264 T::check_in(self, exchange).await
265 }
266
267 async fn mcsp_resp(&self, exchange: Exchange<'_>) -> Result<(), Error> {
268 T::mcsp_resp(self, exchange).await
269 }
270}
271
272pub struct SecureChannel<'a, C, H = ()> {
274 crypto: C,
275 notify: &'a dyn AttrChangeNotifier,
276 handler: H,
277}
278
279impl<'a, C: Crypto> SecureChannel<'a, C, ()> {
280 #[inline(always)]
281 pub const fn new(crypto: C, notify: &'a dyn AttrChangeNotifier) -> Self {
282 Self {
283 crypto,
284 notify,
285 handler: (),
286 }
287 }
288}
289
290impl<'a, C: Crypto, H: AsyncScHandler> SecureChannel<'a, C, H> {
291 #[inline(always)]
294 pub const fn new_with_handler(
295 crypto: C,
296 notify: &'a dyn AttrChangeNotifier,
297 handler: H,
298 ) -> Self {
299 Self {
300 crypto,
301 notify,
302 handler,
303 }
304 }
305
306 pub async fn handle(&self, mut exchange: Exchange<'_>) -> Result<(), Error> {
307 if exchange.rx().is_err() {
308 exchange.recv_fetch().await?;
309 }
310
311 let meta = exchange.rx()?.meta();
312 if meta.proto_id != PROTO_ID_SECURE_CHANNEL {
313 Err(ErrorCode::InvalidProto)?;
314 }
315
316 match meta.opcode()? {
317 OpCode::PBKDFParamRequest => {
318 let mut pase = MaybeUninit::uninit(); pase.init_with(PaseResponder::init(&self.crypto, self.notify))
320 .handle(exchange)
321 .await
322 }
323 OpCode::CASESigma1 => {
324 let mut case = MaybeUninit::uninit(); case.init_with(CaseResponder::init(&self.crypto))
326 .handle(exchange)
327 .await
328 }
329 #[cfg(feature = "groups")]
330 OpCode::MsgCounterSyncReq => {
331 mcsp::respond(&self.crypto, exchange).await
335 }
336 OpCode::MsgCounterSyncResp => {
337 self.handler.mcsp_resp(exchange).await
339 }
340 OpCode::CheckIn => {
341 self.handler.check_in(exchange).await
344 }
345 opcode => {
346 error!("Invalid opcode: {:?}", opcode);
347 Err(ErrorCode::InvalidOpcode.into())
348 }
349 }
350 }
351}
352
353impl<C: Crypto, H: AsyncScHandler> ExchangeHandler for SecureChannel<'_, C, H> {
354 fn handle(&self, exchange: Exchange<'_>) -> impl Future<Output = Result<(), Error>> {
355 SecureChannel::handle(self, exchange)
356 }
357}
358
359async fn expect_opcode(exchange: &mut Exchange<'_>, opcode: OpCode) -> Result<(), Error> {
363 let result = check_opcode(exchange, opcode);
364
365 if let Err(err) = result {
366 if !exchange.rx()?.meta().is_sc_status() {
367 let _ = complete_with_status(exchange, SCStatusCodes::InvalidParameter, &[]).await;
371 }
372
373 Err(err)
374 } else {
375 Ok(())
376 }
377}
378
379fn check_opcode(exchange: &Exchange<'_>, opcode: OpCode) -> Result<(), Error> {
383 let meta = exchange.rx()?.meta();
384 let their_opcode = meta.opcode::<OpCode>()?;
385
386 if their_opcode == opcode {
387 Ok(())
388 } else {
389 error!("Invalid opcode: {:?}, expected: {:?}", their_opcode, opcode);
390
391 if matches!(their_opcode, OpCode::StatusReport) {
392 let mut rb = ReadBuf::new(exchange.rx()?.payload());
393
394 match StatusReport::read(&mut rb) {
396 Ok(status_report) => error!("Status Report: {:?}", status_report),
397 Err(e) => error!("Failed to parse Status Report: {:?}", e),
398 }
399 }
400
401 Err(ErrorCode::Invalid.into())
402 }
403}