scll_core/workflow/
open_scp.rs1use heapless::Vec;
11
12use crate::aid::Aid;
13use crate::backend::{KeyBackend, KeyHandle, Scp02Backend, Scp03Backend, ScpMode};
14use crate::error::ScllError;
15use crate::limits::SCP03_S16_MAX;
16use crate::model::ScpVariant;
17use crate::report::{OpenScpParams, OpenScpReport, ScpTargetKind};
18use crate::scp::{self, scp02, scp03, ScpSession};
19use crate::transport::Transport;
20use crate::workflow::session::{self, SW_OK};
21
22const SCP02_KEY_ID: u8 = 0x00;
25
26#[derive(Debug, Clone, Copy)]
29pub struct SdKeys {
30 pub enc: KeyHandle,
31 pub mac: KeyHandle,
32 pub dek: KeyHandle,
33}
34
35pub struct OpenScpArgs<'a> {
40 pub target_aid: &'a [u8],
41 pub target_kind: ScpTargetKind,
42 pub sd_keys: SdKeys,
43 pub advertised: &'a [ScpVariant],
44 pub force_scp: Option<ScpVariant>,
45 pub kvn: u8,
46 pub requested_level: u8,
47}
48
49#[allow(clippy::similar_names)] pub fn open_scp<B>(
59 t: &mut dyn Transport,
60 backend: &B,
61 args: &OpenScpArgs<'_>,
62) -> Result<OpenScpReport, ScllError>
63where
64 B: KeyBackend + Scp02Backend + Scp03Backend,
65{
66 let variant =
67 scp::select(args.advertised, args.force_scp).ok_or(ScllError::ScpProtocolUnsupported)?;
68
69 let fci = session::select(t, args.target_aid)?;
71
72 let target_aid = Aid::new(args.target_aid)?;
81 let invoker_aid = session::fci_df_name(&fci)
82 .ok()
83 .flatten()
84 .and_then(|name| Aid::new(name).ok())
85 .unwrap_or_else(|| target_aid.clone());
86
87 let (session, kvn_effective, i_param_effective, security_level_effective) = match variant {
88 ScpVariant::Scp03 { i_param } => {
89 let mode = ScpMode::from_i(i_param);
91 let mut host_buf = [0u8; SCP03_S16_MAX];
92 let host_len = mode.field_len();
93 backend.random_bytes(&mut host_buf[..host_len])?;
94 let host_challenge = &host_buf[..host_len];
95
96 let iu_capdu = scp03::iu_command(args.kvn, host_challenge)?;
97 let (iu_data, sw) = session::transmit_plain(t, &iu_capdu)?;
98 if sw != SW_OK {
99 return Err(ScllError::from_general_sw(sw));
100 }
101 let iu = scp03::parse_iu_response(&iu_data)?;
102 let (state, ea) = scp03::begin(
103 backend,
104 &args.sd_keys.enc,
105 &args.sd_keys.mac,
106 args.kvn,
107 args.requested_level,
108 host_challenge,
109 invoker_aid.as_bytes(), &iu_data,
111 )?;
112 let level = state.security_level();
113 let (_d, ea_sw) = session::transmit_plain(t, &ea)?;
114 if ea_sw != SW_OK {
115 return Err(ScllError::ExternalAuthFail { sw: ea_sw });
116 }
117 (ScpSession::Scp03(state), iu.kvn, iu.i_param, level)
118 }
119 ScpVariant::Scp02 { i_param } => {
120 let mut host_challenge = [0u8; 8];
122 backend.random_bytes(&mut host_challenge)?;
123 let iu_capdu = scp02::iu_command(args.kvn, SCP02_KEY_ID, &host_challenge)?;
124 let (iu_data, sw) = session::transmit_plain(t, &iu_capdu)?;
125 if sw != SW_OK {
126 return Err(ScllError::from_general_sw(sw));
127 }
128 let iu = scp02::parse_iu_response(&iu_data)?;
129 let (state, ea) = scp02::begin(
130 backend,
131 &args.sd_keys.enc,
132 &args.sd_keys.mac,
133 &args.sd_keys.dek,
134 i_param,
135 args.kvn,
136 args.requested_level,
137 &host_challenge,
138 &iu_data,
139 )?;
140 let level = state.security_level();
141 let (_d, ea_sw) = session::transmit_plain(t, &ea)?;
142 if ea_sw != SW_OK {
143 return Err(ScllError::ExternalAuthFail { sw: ea_sw });
144 }
145 (ScpSession::Scp02(state), iu.kvn, i_param, level)
146 }
147 };
148
149 let session_id = session.session_id();
150 let scp_protocol_effective = session.protocol();
151
152 Ok(OpenScpReport {
153 session,
154 effective: OpenScpParams {
155 target_aid: target_aid.clone(),
156 target_kind: args.target_kind,
157 sd_aid_used_for_keys: target_aid,
158 scp_protocol_effective,
159 kvn_requested: args.kvn,
160 kvn_effective,
161 i_param_effective,
162 security_level_requested: args.requested_level,
163 security_level_effective,
164 session_id,
165 invoker_aid_used: invoker_aid,
166 },
167 warnings: Vec::new(),
168 })
169}