use heapless::Vec;
use crate::aid::Aid;
use crate::backend::{Scp02Backend, Scp03Backend};
use crate::command::delete::delete_object;
use crate::command::install::{install_for_install_make_selectable, PrivLen};
use crate::error::ScllError;
use crate::limits::{INSTALL_PARAMS_MAX, MAX_REMOVED_OBJECTS};
use crate::report::{
DeleteCascade, DeleteObjectParams, DeleteObjectReport, DeleteTargetKind, InstallAppletParams,
InstallAppletReport,
};
use crate::scp::ScpSession;
use crate::transport::Transport;
use crate::workflow::session::{self, SW_CONDITIONS, SW_OK, SW_REF_NOT_FOUND, SW_WRONG_DATA};
const TAG_C9_APP_PARAMS: u8 = 0xC9;
const TAG_EF_SYS_PARAMS: u8 = 0xEF;
const PRIV_REFUSED_MASK: u8 = 0x80 | 0x40 | 0x20 | 0x01;
pub struct InstallAppletArgs<'a> {
pub parent_sd_aid: &'a [u8],
pub package_aid: &'a [u8],
pub module_aid: &'a [u8],
pub instance_aid: &'a [u8],
pub privileges: [u8; 3],
pub system_install_params: &'a [u8],
pub applet_install_params: &'a [u8],
}
pub fn install_applet<B>(
t: &mut dyn Transport,
backend: &B,
parent_session: &mut ScpSession,
args: &InstallAppletArgs<'_>,
priv_len: PrivLen,
) -> Result<InstallAppletReport, ScllError>
where
B: Scp02Backend + Scp03Backend,
{
if args.privileges[0] & PRIV_REFUSED_MASK != 0 {
return Err(ScllError::UnsupportedPrivilege);
}
let params = build_install_params(args.applet_install_params, args.system_install_params)?;
let capdu = install_for_install_make_selectable(
args.package_aid,
args.module_aid,
args.instance_aid,
args.privileges,
priv_len,
¶ms,
)?;
let (_d, sw) = session::transmit_in_session(t, backend, parent_session, &capdu)?;
match sw {
SW_OK => {}
SW_REF_NOT_FOUND => return Err(ScllError::PackageNotFound),
SW_WRONG_DATA => return Err(ScllError::AidAlreadyExists),
other => return Err(ScllError::from_general_sw(other)),
}
Ok(InstallAppletReport {
effective: InstallAppletParams {
instance_aid: Aid::new(args.instance_aid)?,
package_aid_used: Aid::new(args.package_aid)?,
module_aid_used: Aid::new(args.module_aid)?,
privileges_used: args.privileges,
system_install_params: copy_bounded::<INSTALL_PARAMS_MAX>(args.system_install_params)?,
applet_install_params: copy_bounded::<INSTALL_PARAMS_MAX>(args.applet_install_params)?,
parent_sd_aid: Aid::new(args.parent_sd_aid)?,
},
warnings: Vec::new(),
})
}
pub struct DeleteAppletArgs<'a> {
pub instance_aid: &'a [u8],
pub elf_aid: Option<&'a [u8]>,
pub cascade_elf: DeleteCascade,
}
pub fn delete_applet<B>(
t: &mut dyn Transport,
backend: &B,
parent_session: &mut ScpSession,
args: &DeleteAppletArgs<'_>,
) -> Result<DeleteObjectReport, ScllError>
where
B: Scp02Backend + Scp03Backend,
{
let capdu = delete_object(args.instance_aid, false)?;
let (_d, sw) = session::transmit_in_session(t, backend, parent_session, &capdu)?;
match sw {
SW_OK => {}
SW_REF_NOT_FOUND => return Err(ScllError::TargetNoLongerExists),
other => return Err(ScllError::from_general_sw(other)),
}
let mut instances_removed: Vec<Aid, MAX_REMOVED_OBJECTS> = Vec::new();
let _ = instances_removed.push(Aid::new(args.instance_aid)?);
let mut elfs_removed: Vec<Aid, MAX_REMOVED_OBJECTS> = Vec::new();
let want_cascade = matches!(
args.cascade_elf,
DeleteCascade::Always | DeleteCascade::IfLastInstance
);
let mut cascade_used = false;
if want_cascade {
if let Some(elf) = args.elf_aid {
let ecapdu = delete_object(elf, false)?;
let (_ed, esw) = session::transmit_in_session(t, backend, parent_session, &ecapdu)?;
match esw {
SW_OK => {
let _ = elfs_removed.push(Aid::new(elf)?);
cascade_used = true;
}
SW_CONDITIONS => return Err(ScllError::ElfHasOtherInstances),
SW_REF_NOT_FOUND => return Err(ScllError::TargetNoLongerExists),
other => return Err(ScllError::from_general_sw(other)),
}
}
}
Ok(DeleteObjectReport {
effective: DeleteObjectParams {
target_aid: Aid::new(args.instance_aid)?,
target_kind: DeleteTargetKind::AppletInstance,
cascade_requested: args.cascade_elf,
cascade_used,
instances_removed,
elfs_removed,
},
warnings: Vec::new(),
})
}
fn build_install_params(
app_params: &[u8],
sys_params: &[u8],
) -> Result<Vec<u8, INSTALL_PARAMS_MAX>, ScllError> {
let mut out: Vec<u8, INSTALL_PARAMS_MAX> = Vec::new();
push_tlv(&mut out, TAG_C9_APP_PARAMS, app_params)?;
if !sys_params.is_empty() {
push_tlv(&mut out, TAG_EF_SYS_PARAMS, sys_params)?;
}
Ok(out)
}
fn overflow() -> ScllError {
ScllError::Build(crate::command::BuildError::Overflow)
}
fn push_tlv<const N: usize>(out: &mut Vec<u8, N>, tag: u8, value: &[u8]) -> Result<(), ScllError> {
let len = u8::try_from(value.len()).map_err(|_| overflow())?;
out.push(tag).map_err(|_| overflow())?;
out.push(len).map_err(|_| overflow())?;
out.extend_from_slice(value).map_err(|()| overflow())
}
fn copy_bounded<const N: usize>(src: &[u8]) -> Result<Vec<u8, N>, ScllError> {
let mut v: Vec<u8, N> = Vec::new();
v.extend_from_slice(src).map_err(|()| overflow())?;
Ok(v)
}