1use core::mem::MaybeUninit;
24use core::num::NonZeroU8;
25
26use crate::alloc;
27use crate::cert::CertRef;
28use crate::crypto::{
29 CanonPkcPublicKeyRef, CanonPkcSignature, CanonPkcSignatureRef, Crypto, Hash, AEAD_CANON_KEY_LEN,
30};
31use crate::error::{Error, ErrorCode};
32use crate::sc::{complete_with_status, GeneralCode, OpCode, SCStatusCodes, StatusReport};
33use crate::tlv::{get_root_node_struct, FromTLV, OctetStr, TLVElement, TLVTag, TLVWrite};
34use crate::transport::exchange::Exchange;
35use crate::transport::session::{NocCatIds, ReservedSession, SessionMode};
36use crate::utils::init::InitMaybeUninit;
37use crate::utils::storage::ReadBuf;
38
39use super::casep::{CaseP, CaseRandom, CaseRandomRef, CaseSessionKeys, CASE_RESUMPTION_ID_ZEROED};
40use super::CASE_LARGE_BUF_SIZE;
41
42#[derive(FromTLV, Debug)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45#[tlvargs(start = 1, lifetime = "'a")]
46struct Sigma2Resp<'a> {
47 responder_random: OctetStr<'a>,
49 responder_sessid: u16,
51 responder_eph_pub_key: OctetStr<'a>,
53 encrypted2: OctetStr<'a>,
55}
56
57#[derive(FromTLV)]
59#[tlvargs(start = 1, lifetime = "'a")]
60struct TBEData2Decrypt<'a> {
61 responder_noc: OctetStr<'a>,
62 responder_icac: Option<OctetStr<'a>>,
63 signature: OctetStr<'a>,
64 resumption_id: OctetStr<'a>,
65}
66
67pub struct CaseInitiator<'a, C: Crypto + 'a> {
77 casep: CaseP<'a, C>,
78 peer_node_id: u64,
80 secret_key: Option<C::SecretKey<'a>>,
82}
83
84impl<'a, C: Crypto + 'a> CaseInitiator<'a, C> {
85 const fn new(peer_node_id: u64) -> Self {
87 Self {
88 casep: CaseP::new(),
89 peer_node_id,
90 secret_key: None,
91 }
92 }
93
94 pub async fn initiate(
110 exchange: &mut Exchange<'_>,
111 crypto: &'a C,
112 fab_idx: NonZeroU8,
113 peer_node_id: u64,
114 ) -> Result<(), Error> {
115 let mut session = ReservedSession::reserve(exchange.matter(), crypto).await?;
117
118 let mut initiator = Self::new(peer_node_id);
119
120 let mut random = MaybeUninit::<CaseRandom>::uninit();
121 let random = random.init_with(CaseRandom::init());
122
123 let mut dest_id = MaybeUninit::<Hash>::uninit();
124 let dest_id = dest_id.init_with(Hash::init());
125
126 let local_sessid = exchange.with_state(|state| {
128 let local_sessid = state.sessions.get_next_sess_id();
129
130 let fabric = state.fabrics.fabric(fab_idx)?;
131
132 let secret_key = initiator.casep.start_initiator(
133 crypto,
134 fabric,
135 peer_node_id,
136 local_sessid,
137 random,
138 dest_id,
139 )?;
140
141 initiator.secret_key = Some(secret_key);
142
143 Ok(local_sessid)
144 })?;
145
146 let mut tt_updated = false;
148 exchange
149 .send_with(|_, tw| {
150 tw.start_struct(&TLVTag::Anonymous)?;
151 tw.str(&TLVTag::Context(1), random.access())?;
152 tw.u16(&TLVTag::Context(2), local_sessid)?;
153 tw.str(&TLVTag::Context(3), dest_id.access())?;
154 tw.str(&TLVTag::Context(4), initiator.casep.our_pub_key().access())?;
155 tw.end_container()?;
156
157 if !tt_updated {
158 initiator.casep.update_tt(tw.as_slice())?;
159 tt_updated = true;
160 }
161
162 Ok(Some(OpCode::CASESigma1.into()))
163 })
164 .await?;
165
166 exchange.recv_fetch().await?;
168
169 {
170 let rx = exchange.rx()?;
171 let meta = rx.meta();
172
173 if meta.proto_opcode == OpCode::StatusReport as u8 {
175 let mut rb = ReadBuf::new(rx.payload());
176 let status = StatusReport::read(&mut rb)?;
177 error!(
178 "CASE Sigma1 failed: general={:?}, proto_code={}",
179 status.general_code, status.proto_code
180 );
181 return Err(ErrorCode::Invalid.into());
182 }
183
184 if meta.proto_opcode != OpCode::CASESigma2 as u8 {
186 error!(
187 "Unexpected opcode: expected CASESigma2, got {}",
188 meta.proto_opcode
189 );
190 return Err(ErrorCode::InvalidOpcode.into());
191 }
192 }
193
194 let (peer_catids, _resumption_id) = {
196 let rx = exchange.rx()?;
197 let raw_sigma2_payload = rx.payload();
198
199 let sigma2 = Sigma2Resp::from_tlv(&get_root_node_struct(raw_sigma2_payload)?)?;
200
201 let result = exchange.with_state(|state| {
202 let mut encrypted2_buf = alloc!([0u8; CASE_LARGE_BUF_SIZE]); if sigma2.encrypted2.0.len() > encrypted2_buf.len() {
206 error!("Sigma2 encrypted data too large");
207 return Err(ErrorCode::BufferTooSmall.into());
208 }
209
210 let encrypted2 = &mut encrypted2_buf[..sigma2.encrypted2.0.len()];
211 encrypted2.copy_from_slice(sigma2.encrypted2.0);
212
213 let peer_random = CaseRandomRef::try_new(sigma2.responder_random.0)?;
214 let peer_sessid = sigma2.responder_sessid;
215 let peer_eph_pub_key =
216 CanonPkcPublicKeyRef::try_new(sigma2.responder_eph_pub_key.0)?;
217
218 let fabric = state.fabrics.fabric(fab_idx)?;
219
220 let secret_key = initiator
221 .secret_key
222 .as_ref()
223 .ok_or(ErrorCode::InvalidState)?;
224
225 let len = initiator
227 .casep
228 .sigma2_decrypt(
229 crypto,
230 fabric,
231 secret_key,
232 raw_sigma2_payload,
233 peer_random,
234 peer_sessid,
235 peer_eph_pub_key,
236 encrypted2,
237 )
238 .inspect_err(|e| {
239 error!("Failed to decrypt Sigma2 TBE: {}", e);
240 })?;
241
242 initiator.secret_key = None;
244
245 let decrypted = &encrypted2[..len];
246 let decrypted_data = TBEData2Decrypt::from_tlv(&get_root_node_struct(decrypted)?)?;
247
248 let responder_noc = CertRef::new(TLVElement::new(decrypted_data.responder_noc.0));
250 let icac_cert = decrypted_data
251 .responder_icac
252 .as_ref()
253 .map(|icac| CertRef::new(TLVElement::new(icac.0)));
254
255 let mut tmp_buf = alloc!([0u8; CASE_LARGE_BUF_SIZE]); initiator
257 .casep
258 .validate_certs(
259 crypto,
260 state.rtc.utc_time(),
261 fabric,
262 &responder_noc,
263 icac_cert.as_ref(),
264 &mut tmp_buf[..],
265 )
266 .inspect_err(|e| {
267 error!("Certificate chain doesn't match: {}", e);
268 })?;
269
270 if responder_noc.get_node_id()? != initiator.peer_node_id {
272 error!(
273 "Responder node ID doesn't match expected peer: expected {}, got {}",
274 initiator.peer_node_id,
275 responder_noc.get_node_id()?
276 );
277
278 Err(ErrorCode::Invalid)?;
279 }
280
281 initiator
283 .casep
284 .validate_peer_tbs_signature(
285 crypto,
286 decrypted_data.responder_noc.0,
287 decrypted_data.responder_icac.map(|a| a.0),
288 &responder_noc,
289 CanonPkcSignatureRef::try_new(decrypted_data.signature.0)?,
290 &mut tmp_buf[..],
291 )
292 .inspect_err(|e| {
293 error!("Sigma2 signature doesn't match: {}", e);
294 })?;
295
296 let mut peer_catids: NocCatIds = Default::default();
298 responder_noc.get_cat_ids(&mut peer_catids)?;
299
300 let mut resumption_id = CASE_RESUMPTION_ID_ZEROED;
302 resumption_id
303 .access_mut()
304 .copy_from_slice(decrypted_data.resumption_id.0);
305
306 Ok((peer_catids, resumption_id))
307 });
308
309 if result.is_err() {
310 complete_with_status(exchange, SCStatusCodes::InvalidParameter, &[]).await?;
311 }
312
313 result
314 }?;
315
316 let mut signature = MaybeUninit::<CanonPkcSignature>::uninit();
318 let signature = signature.init_with(CanonPkcSignature::init());
319
320 exchange.with_state(|state| {
321 let fabric = state.fabrics.fabric(fab_idx)?;
322
323 let mut tmp_buf = alloc!([0u8; CASE_LARGE_BUF_SIZE]);
325 initiator
326 .casep
327 .compute_sigma3_signature(crypto, fabric, &mut tmp_buf[..], signature)
328 })?;
329
330 let mut tt_updated = false;
332 exchange
333 .send_with(|exchange_ref, tw| {
334 exchange_ref.with_state(|state| {
335 let fabric = state.fabrics.fabric(fab_idx)?;
336
337 tw.start_struct(&TLVTag::Anonymous)?;
338 tw.str_cb(&TLVTag::Context(1), |buf| {
339 initiator
340 .casep
341 .sigma3_encrypt(crypto, fabric, signature.reference(), buf)
342 })?;
343 tw.end_container()?;
344
345 if !tt_updated {
346 initiator.casep.update_tt(tw.as_slice())?;
347 tt_updated = true;
348 }
349
350 Ok(Some(OpCode::CASESigma3.into()))
351 })
352 })
353 .await?;
354
355 exchange.recv_fetch().await?;
357
358 {
359 let rx = exchange.rx()?;
360 let meta = rx.meta();
361
362 if meta.proto_opcode != OpCode::StatusReport as u8 {
363 error!(
364 "Unexpected opcode: expected StatusReport, got {}",
365 meta.proto_opcode
366 );
367 return Err(ErrorCode::InvalidOpcode.into());
368 }
369
370 let mut rb = ReadBuf::new(rx.payload());
371 let status = StatusReport::read(&mut rb)?;
372
373 if status.general_code != GeneralCode::Success
374 || status.proto_code != SCStatusCodes::SessionEstablishmentSuccess as u16
375 {
376 error!(
377 "CASE failed: general={:?}, proto_code={}",
378 status.general_code, status.proto_code
379 );
380 return Err(ErrorCode::Invalid.into());
381 }
382 }
383
384 {
386 let mut session_keys = MaybeUninit::<CaseSessionKeys>::uninit();
387 let session_keys = session_keys.init_with(CaseSessionKeys::init());
388
389 let (peer_addr, local_node_id) = exchange.with_state(|state| {
390 let sess = exchange.id().session(&mut state.sessions);
391
392 let fabric = state.fabrics.fabric(fab_idx)?;
393
394 initiator.casep.compute_session_keys(
395 crypto,
396 fabric.ipk().op_key(),
397 session_keys,
398 )?;
399
400 Ok((sess.get_peer_addr(), fabric.node_id()))
401 })?;
402
403 let (enc_key, remaining) = session_keys
405 .reference()
406 .split::<AEAD_CANON_KEY_LEN, { AEAD_CANON_KEY_LEN * 2 }>();
407 let (dec_key, att_challenge) =
408 remaining.split::<AEAD_CANON_KEY_LEN, AEAD_CANON_KEY_LEN>();
409
410 session.update(
411 local_node_id,
412 peer_node_id,
413 initiator.casep.peer_sessid(),
414 initiator.casep.local_sessid(),
415 peer_addr,
416 SessionMode::Case {
417 fab_idx,
418 cat_ids: peer_catids,
419 },
420 Some(dec_key),
421 Some(enc_key),
422 Some(att_challenge),
423 )?;
424 }
425
426 session.complete();
427
428 exchange.acknowledge().await?;
429
430 info!(
431 "CASE session established: local_sessid={}, peer_sessid={}",
432 initiator.casep.local_sessid(),
433 initiator.casep.peer_sessid()
434 );
435
436 Ok(())
437 }
438}