1use heapless::Vec;
19
20use crate::aid::Aid;
21use crate::backend::{Scp02Backend, Scp03Backend};
22use crate::cap::{parse as cap_parse, InflateCtx};
23use crate::command::delete::delete_object;
24use crate::command::install::{install_for_install_make_selectable, install_for_load, PrivLen};
25use crate::command::load::load_block;
26use crate::error::ScllError;
27use crate::limits::{HASH_MAX, INSTALL_PARAMS_MAX, LOAD_BLOCK_DATA};
28use crate::report::{
29 CreateSsdParams, CreateSsdReport, DeleteCascade, DeleteObjectParams, DeleteObjectReport,
30 DeleteTargetKind, LoadPackageParams, LoadPackageReport,
31};
32use crate::scp::ScpSession;
33use crate::transport::Transport;
34use crate::workflow::session::{self, SW_CONDITIONS, SW_OK, SW_REF_NOT_FOUND, SW_WRONG_DATA};
35
36const PRIV_REFUSED_MASK: u8 = 0x40 | 0x20 | 0x01;
40const MAX_LOAD_BYTES: usize = 256 * LOAD_BLOCK_DATA;
42
43pub struct CreateSsdArgs<'a> {
45 pub parent_sd_aid: &'a [u8],
46 pub ssd_aid: &'a [u8],
47 pub elf_aid: &'a [u8],
48 pub module_aid: &'a [u8],
49 pub privileges: [u8; 3],
50 pub install_params: &'a [u8],
53}
54
55pub fn create_ssd<B>(
62 t: &mut dyn Transport,
63 backend: &B,
64 parent_session: &mut ScpSession,
65 args: &CreateSsdArgs<'_>,
66 priv_len: PrivLen,
67) -> Result<CreateSsdReport, ScllError>
68where
69 B: Scp02Backend + Scp03Backend,
70{
71 if args.privileges[0] & PRIV_REFUSED_MASK != 0 {
72 return Err(ScllError::UnsupportedPrivilege);
73 }
74 let capdu = install_for_install_make_selectable(
75 args.elf_aid,
76 args.module_aid,
77 args.ssd_aid,
78 args.privileges,
79 priv_len,
80 args.install_params,
81 )?;
82 let (_d, sw) = session::transmit_in_session(t, backend, parent_session, &capdu)?;
83 match sw {
84 SW_OK => {}
85 SW_REF_NOT_FOUND => return Err(ScllError::ResidentSdNotFound),
86 SW_WRONG_DATA => return Err(ScllError::AidAlreadyExists),
87 other => return Err(ScllError::from_general_sw(other)),
88 }
89 Ok(CreateSsdReport {
90 effective: CreateSsdParams {
91 ssd_aid_effective: Aid::new(args.ssd_aid)?,
92 aid_was_generated: false,
93 parent_sd_aid: Aid::new(args.parent_sd_aid)?,
94 privileges_used: args.privileges,
95 elf_aid_used: Aid::new(args.elf_aid)?,
96 module_aid_used: Aid::new(args.module_aid)?,
97 install_params_used: copy_bounded::<INSTALL_PARAMS_MAX>(args.install_params)?,
98 },
99 warnings: Vec::new(),
100 })
101}
102
103pub struct LoadPackageArgs<'a> {
106 pub target_sd_aid: &'a [u8],
107 pub cap_zip: &'a [u8],
108 pub lfdb_hash: &'a [u8],
109}
110
111pub fn load_package<B>(
120 t: &mut dyn Transport,
121 backend: &B,
122 parent_session: &mut ScpSession,
123 args: &LoadPackageArgs<'_>,
124 infl: &mut InflateCtx,
125) -> Result<LoadPackageReport, ScllError>
126where
127 B: Scp02Backend + Scp03Backend,
128{
129 let cap = cap_parse(args.cap_zip, infl)?;
130 let package_aid = cap.package_aid.clone();
131 let total = cap.lfdb().len();
132 if total > MAX_LOAD_BYTES {
133 return Err(ScllError::LoadTooLarge);
134 }
135
136 let install = install_for_load(package_aid.as_bytes(), args.target_sd_aid, args.lfdb_hash)?;
138 let (_d, sw) = session::transmit_in_session(t, backend, parent_session, &install)?;
139 match sw {
140 SW_OK => {}
141 SW_REF_NOT_FOUND => return Err(ScllError::ResidentSdNotFound),
142 SW_WRONG_DATA => return Err(ScllError::PackageAidExists),
143 other => return Err(ScllError::from_general_sw(other)),
144 }
145
146 infl.reset();
148 let mut lfdb = cap.lfdb();
149 let mut out = [0u8; LOAD_BLOCK_DATA];
150 let mut sent = 0usize;
151 let mut block_no = 0u8;
152 let mut blocks_sent = 0u16;
153 loop {
154 let n = lfdb.next_block(infl, &mut out)?;
155 if n == 0 {
156 break;
157 }
158 sent += n;
159 let last = sent >= total;
160 let capdu = load_block(block_no, last, &out[..n])?;
161 let (_ld, lsw) = session::transmit_in_session(t, backend, parent_session, &capdu)?;
162 if lsw != SW_OK {
163 return Err(ScllError::from_general_sw(lsw));
164 }
165 block_no = block_no.wrapping_add(1);
166 blocks_sent += 1;
167 if last {
168 break;
169 }
170 }
171
172 Ok(LoadPackageReport {
173 effective: LoadPackageParams {
174 package_aid,
175 load_file_size: u32::try_from(total).unwrap_or(u32::MAX),
176 hash_value: copy_bounded::<HASH_MAX>(args.lfdb_hash)?,
177 block_count: blocks_sent,
178 target_sd_aid: Aid::new(args.target_sd_aid)?,
179 },
180 warnings: Vec::new(),
181 })
182}
183
184pub fn delete_ssd<B>(
191 t: &mut dyn Transport,
192 backend: &B,
193 parent_session: &mut ScpSession,
194 ssd_aid: &[u8],
195 cascade: DeleteCascade,
196) -> Result<DeleteObjectReport, ScllError>
197where
198 B: Scp02Backend + Scp03Backend,
199{
200 let cascade_flag = matches!(cascade, DeleteCascade::Cascade | DeleteCascade::Always);
201 let capdu = delete_object(ssd_aid, cascade_flag)?;
202 let (_d, sw) = session::transmit_in_session(t, backend, parent_session, &capdu)?;
203 match sw {
204 SW_OK => {}
205 SW_REF_NOT_FOUND => return Err(ScllError::TargetNoLongerExists),
206 SW_CONDITIONS if !cascade_flag => return Err(ScllError::SsdHasApplets),
207 other => return Err(ScllError::from_general_sw(other)),
208 }
209 Ok(DeleteObjectReport {
210 effective: DeleteObjectParams {
211 target_aid: Aid::new(ssd_aid)?,
212 target_kind: DeleteTargetKind::Ssd,
213 cascade_requested: cascade,
214 cascade_used: cascade_flag,
215 instances_removed: Vec::new(),
216 elfs_removed: Vec::new(),
217 },
218 warnings: Vec::new(),
219 })
220}
221
222fn copy_bounded<const N: usize>(src: &[u8]) -> Result<Vec<u8, N>, ScllError> {
224 let mut v = Vec::new();
225 v.extend_from_slice(src)
226 .map_err(|()| ScllError::Build(crate::command::BuildError::Overflow))?;
227 Ok(v)
228}