1use heapless::Vec;
14
15use crate::aid::Aid;
16use crate::backend::{Scp02Backend, Scp03Backend};
17use crate::command::delete::delete_object;
18use crate::command::install::{install_for_install_make_selectable, PrivLen};
19use crate::error::ScllError;
20use crate::limits::{INSTALL_PARAMS_MAX, MAX_REMOVED_OBJECTS};
21use crate::report::{
22 DeleteCascade, DeleteObjectParams, DeleteObjectReport, DeleteTargetKind, InstallAppletParams,
23 InstallAppletReport,
24};
25use crate::scp::ScpSession;
26use crate::transport::Transport;
27use crate::workflow::session::{self, SW_CONDITIONS, SW_OK, SW_REF_NOT_FOUND, SW_WRONG_DATA};
28
29const TAG_C9_APP_PARAMS: u8 = 0xC9;
31const TAG_EF_SYS_PARAMS: u8 = 0xEF;
33const PRIV_REFUSED_MASK: u8 = 0x80 | 0x40 | 0x20 | 0x01;
37
38pub struct InstallAppletArgs<'a> {
40 pub parent_sd_aid: &'a [u8],
41 pub package_aid: &'a [u8],
42 pub module_aid: &'a [u8],
43 pub instance_aid: &'a [u8],
44 pub privileges: [u8; 3],
45 pub system_install_params: &'a [u8],
46 pub applet_install_params: &'a [u8],
47}
48
49pub fn install_applet<B>(
56 t: &mut dyn Transport,
57 backend: &B,
58 parent_session: &mut ScpSession,
59 args: &InstallAppletArgs<'_>,
60 priv_len: PrivLen,
61) -> Result<InstallAppletReport, ScllError>
62where
63 B: Scp02Backend + Scp03Backend,
64{
65 if args.privileges[0] & PRIV_REFUSED_MASK != 0 {
66 return Err(ScllError::UnsupportedPrivilege);
67 }
68 let params = build_install_params(args.applet_install_params, args.system_install_params)?;
69 let capdu = install_for_install_make_selectable(
70 args.package_aid,
71 args.module_aid,
72 args.instance_aid,
73 args.privileges,
74 priv_len,
75 ¶ms,
76 )?;
77 let (_d, sw) = session::transmit_in_session(t, backend, parent_session, &capdu)?;
78 match sw {
79 SW_OK => {}
80 SW_REF_NOT_FOUND => return Err(ScllError::PackageNotFound),
81 SW_WRONG_DATA => return Err(ScllError::AidAlreadyExists),
82 other => return Err(ScllError::from_general_sw(other)),
83 }
84 Ok(InstallAppletReport {
85 effective: InstallAppletParams {
86 instance_aid: Aid::new(args.instance_aid)?,
87 package_aid_used: Aid::new(args.package_aid)?,
88 module_aid_used: Aid::new(args.module_aid)?,
89 privileges_used: args.privileges,
90 system_install_params: copy_bounded::<INSTALL_PARAMS_MAX>(args.system_install_params)?,
91 applet_install_params: copy_bounded::<INSTALL_PARAMS_MAX>(args.applet_install_params)?,
92 parent_sd_aid: Aid::new(args.parent_sd_aid)?,
93 },
94 warnings: Vec::new(),
95 })
96}
97
98pub struct DeleteAppletArgs<'a> {
101 pub instance_aid: &'a [u8],
102 pub elf_aid: Option<&'a [u8]>,
103 pub cascade_elf: DeleteCascade,
104}
105
106pub fn delete_applet<B>(
113 t: &mut dyn Transport,
114 backend: &B,
115 parent_session: &mut ScpSession,
116 args: &DeleteAppletArgs<'_>,
117) -> Result<DeleteObjectReport, ScllError>
118where
119 B: Scp02Backend + Scp03Backend,
120{
121 let capdu = delete_object(args.instance_aid, false)?;
123 let (_d, sw) = session::transmit_in_session(t, backend, parent_session, &capdu)?;
124 match sw {
125 SW_OK => {}
126 SW_REF_NOT_FOUND => return Err(ScllError::TargetNoLongerExists),
127 other => return Err(ScllError::from_general_sw(other)),
128 }
129 let mut instances_removed: Vec<Aid, MAX_REMOVED_OBJECTS> = Vec::new();
130 let _ = instances_removed.push(Aid::new(args.instance_aid)?);
131
132 let mut elfs_removed: Vec<Aid, MAX_REMOVED_OBJECTS> = Vec::new();
134 let want_cascade = matches!(
135 args.cascade_elf,
136 DeleteCascade::Always | DeleteCascade::IfLastInstance
137 );
138 let mut cascade_used = false;
139 if want_cascade {
140 if let Some(elf) = args.elf_aid {
141 let ecapdu = delete_object(elf, false)?;
142 let (_ed, esw) = session::transmit_in_session(t, backend, parent_session, &ecapdu)?;
143 match esw {
144 SW_OK => {
145 let _ = elfs_removed.push(Aid::new(elf)?);
146 cascade_used = true;
147 }
148 SW_CONDITIONS => return Err(ScllError::ElfHasOtherInstances),
150 SW_REF_NOT_FOUND => return Err(ScllError::TargetNoLongerExists),
151 other => return Err(ScllError::from_general_sw(other)),
152 }
153 }
154 }
155
156 Ok(DeleteObjectReport {
157 effective: DeleteObjectParams {
158 target_aid: Aid::new(args.instance_aid)?,
159 target_kind: DeleteTargetKind::AppletInstance,
160 cascade_requested: args.cascade_elf,
161 cascade_used,
162 instances_removed,
163 elfs_removed,
164 },
165 warnings: Vec::new(),
166 })
167}
168
169fn build_install_params(
171 app_params: &[u8],
172 sys_params: &[u8],
173) -> Result<Vec<u8, INSTALL_PARAMS_MAX>, ScllError> {
174 let mut out: Vec<u8, INSTALL_PARAMS_MAX> = Vec::new();
175 push_tlv(&mut out, TAG_C9_APP_PARAMS, app_params)?;
176 if !sys_params.is_empty() {
177 push_tlv(&mut out, TAG_EF_SYS_PARAMS, sys_params)?;
178 }
179 Ok(out)
180}
181
182fn overflow() -> ScllError {
184 ScllError::Build(crate::command::BuildError::Overflow)
185}
186
187fn push_tlv<const N: usize>(out: &mut Vec<u8, N>, tag: u8, value: &[u8]) -> Result<(), ScllError> {
189 let len = u8::try_from(value.len()).map_err(|_| overflow())?;
190 out.push(tag).map_err(|_| overflow())?;
191 out.push(len).map_err(|_| overflow())?;
192 out.extend_from_slice(value).map_err(|()| overflow())
193}
194
195fn copy_bounded<const N: usize>(src: &[u8]) -> Result<Vec<u8, N>, ScllError> {
197 let mut v: Vec<u8, N> = Vec::new();
198 v.extend_from_slice(src).map_err(|()| overflow())?;
199 Ok(v)
200}