Skip to main content

scll_core/workflow/
transmit.rs

1//! Step 10 — applet APDU exchange (PDD §5.10).
2//!
3//! Public wrapper over the session wrap/transmit/unwrap of step 9. Returns the
4//! plaintext R-APDU data and the SW. The session's fixed security level governs
5//! C-MAC / C-ENC / R-MAC (§4.1); a wrap/unwrap failure tears the session down
6//! backend-side (Fork F3) and surfaces as [`ScllError::Backend`].
7
8use heapless::Vec;
9
10use crate::backend::{Scp02Backend, Scp03Backend};
11use crate::error::ScllError;
12use crate::report::{AppletTransmitParams, AppletTransmitReport};
13use crate::scp::ScpSession;
14use crate::transport::Transport;
15use crate::workflow::session;
16
17/// Exchange one application-level APDU over an open [`ScpSession`].
18///
19/// # Errors
20/// Returns [`ScllError::Backend`] on a wrap/unwrap failure, a mapped transport
21/// error, or [`ScllError::Card`] for a malformed response. The returned SW is
22/// **not** itself treated as an error — the caller inspects `report.sw`.
23pub fn transmit<B>(
24    t: &mut dyn Transport,
25    backend: &B,
26    session: &mut ScpSession,
27    plaintext_capdu: &[u8],
28) -> Result<AppletTransmitReport, ScllError>
29where
30    B: Scp02Backend + Scp03Backend,
31{
32    let session_id = session.session_id();
33    let sec_level = session.security_level();
34    let scp_protocol = session.protocol();
35
36    let (data, sw) = session::transmit_in_session(t, backend, session, plaintext_capdu)?;
37
38    let capdu_plaintext_len = u16::try_from(plaintext_capdu.len()).unwrap_or(u16::MAX);
39    let rapdu_plaintext_len = u16::try_from(data.len()).unwrap_or(u16::MAX);
40
41    Ok(AppletTransmitReport {
42        rapdu: data,
43        sw,
44        effective: AppletTransmitParams {
45            session_id,
46            capdu_plaintext_len,
47            rapdu_plaintext_len,
48            sw: sw.to_be_bytes(),
49            sec_level,
50            scp_protocol,
51        },
52        warnings: Vec::new(),
53    })
54}