use crate::seph::{self, PacketTypes};
mod event;
pub use event::{DecodedEvent, DecodedEventType};
mod bolos;
mod callbacks;
use bolos::handle_bolos_apdu;
use callbacks::{fetch_apdu_header_impl, next_event_ahead_impl, reply_status_impl, set_comm};
pub use crate::io_legacy::{ApduHeader, Event, Reply, StatusWords};
use crate::io_callbacks::nbgl_register_callbacks;
#[cfg(any(
target_os = "nanox",
target_os = "stax",
target_os = "flex",
target_os = "apex_p"
))]
use crate::seph::ItcUxEvent;
use ledger_secure_sdk_sys::seph as sys_seph;
use ledger_secure_sdk_sys::*;
#[cfg(any(target_os = "nanosplus", target_os = "nanox"))]
use crate::buttons::ButtonEvent;
#[cfg(any(target_os = "nanosplus", target_os = "nanox"))]
use ledger_secure_sdk_sys::buttons::{get_button_event, ButtonsState};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CommError {
Overflow,
IoError,
}
pub const DEFAULT_BUF_SIZE: usize = 273;
pub struct Comm<const N: usize = DEFAULT_BUF_SIZE> {
buf: [u8; N],
expected_cla: Option<u8>,
apdu_type: u8,
#[cfg(any(target_os = "nanosplus", target_os = "nanox"))]
buttons: ButtonsState,
pending_apdu: bool,
pending_header: ApduHeader,
pending_offset: usize,
pending_length: usize,
}
impl<const N: usize> Comm<N> {
pub fn new() -> Self {
Self {
buf: [0; N],
expected_cla: None,
apdu_type: PacketTypes::PacketTypeNone as u8,
#[cfg(any(target_os = "nanosplus", target_os = "nanox"))]
buttons: ButtonsState::default(),
pending_apdu: false,
pending_header: ApduHeader {
cla: 0,
ins: 0,
p1: 0,
p2: 0,
},
pending_offset: 0,
pending_length: 0,
}
}
pub(crate) fn nbgl_register_comm(&mut self) {
set_comm::<N>(self);
nbgl_register_callbacks(
next_event_ahead_impl::<N>,
fetch_apdu_header_impl::<N>,
reply_status_impl::<N>,
);
}
fn recv(&mut self, check_se_event: bool) -> Result<Rx<'_, N>, CommError> {
let result = sys_seph::io_rx(&mut self.buf, check_se_event);
if result < 0 {
return Err(CommError::IoError);
}
Ok(Rx {
comm: self,
len: result as usize,
})
}
pub fn begin_response(&mut self) -> CommandResponse<'_, N> {
CommandResponse { comm: self, len: 0 }
}
pub fn send<T: Into<Reply>>(&mut self, data: &[u8], reply: T) -> Result<(), CommError> {
self.begin_response().extend(data)?.send(reply).unwrap();
Ok(())
}
pub fn try_next_event(&mut self) -> DecodedEvent<N> {
if self.pending_apdu {
self.pending_apdu = false;
return DecodedEvent::from_type(DecodedEventType::Apdu {
header: self.pending_header,
offset: self.pending_offset,
length: self.pending_length,
});
}
self.recv(true).unwrap().decode_event()
}
pub fn next_event(&mut self) -> DecodedEvent<N> {
loop {
let ety = self.try_next_event().into_type();
if !matches!(ety, DecodedEventType::Ignored) {
return DecodedEvent::from_type(ety);
}
}
}
pub fn next_command(&mut self) -> Command<'_, N> {
loop {
let ety = self.next_event().into_type();
match ety {
DecodedEventType::Apdu {
header,
offset,
length,
} => {
if header.cla == 0xB0 && header.p1 == 0x00 && header.p2 == 0x00 {
handle_bolos_apdu::<N>(self, header.ins);
continue;
}
if let Some(cla) = self.expected_cla {
if header.cla != cla {
let _ = self.begin_response().send(StatusWords::BadCla);
continue;
}
}
return Command::new(self, header, offset, length);
}
DecodedEventType::ApduError(e) => self.send(&[], StatusWords::from(e)).unwrap(),
_ => {}
}
}
}
pub fn set_expected_cla(mut self, cla: u8) -> Self {
self.expected_cla = Some(cla);
self
}
}
pub enum ApduError {
BadLen,
}
impl From<ApduError> for StatusWords {
fn from(e: ApduError) -> Self {
match e {
ApduError::BadLen => StatusWords::BadLen,
}
}
}
pub struct Command<'a, const N: usize = DEFAULT_BUF_SIZE> {
comm: &'a mut Comm<N>,
header: ApduHeader,
offset: usize,
length: usize,
}
impl<'a, const N: usize> Command<'a, N> {
pub fn new(comm: &'a mut Comm<N>, header: ApduHeader, offset: usize, length: usize) -> Self {
Self {
comm,
header,
offset,
length,
}
}
pub fn decode<T>(&self) -> Result<T, Reply>
where
T: TryFrom<ApduHeader>,
Reply: From<<T as TryFrom<ApduHeader>>::Error>,
{
T::try_from(self.header).map_err(Reply::from)
}
pub fn get_data(&self) -> &[u8] {
&self.comm.buf[self.offset..self.offset + self.length]
}
pub fn into_response(self) -> CommandResponse<'a, N> {
CommandResponse {
comm: self.comm,
len: 0,
}
}
pub fn into_comm(self) -> &'a mut Comm<N> {
self.comm
}
pub fn reply<T: Into<Reply>>(self, data: &[u8], reply: T) -> Result<(), CommError> {
self.into_response().extend(data)?.send(reply)?;
Ok(())
}
}
pub(crate) struct Rx<'a, const N: usize = DEFAULT_BUF_SIZE> {
comm: &'a mut Comm<N>,
len: usize,
}
impl<'a, const N: usize> core::ops::Deref for Rx<'a, N> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<'a, const N: usize> Rx<'a, N> {
pub fn as_slice(&self) -> &[u8] {
&self.comm.buf[..self.len]
}
pub fn len(&self) -> usize {
self.len
}
pub fn consume(self) -> () {}
pub fn decode_event(self) -> DecodedEvent<N> {
DecodedEvent::new(self.comm, self.len)
}
}
pub struct CommandResponse<'a, const N: usize = DEFAULT_BUF_SIZE> {
comm: &'a mut Comm<N>,
len: usize,
}
impl<'a, const N: usize> CommandResponse<'a, N> {
pub fn new(comm: &'a mut Comm<N>) -> Self {
Self { comm, len: 0 }
}
pub fn len(&self) -> usize {
self.len
}
#[inline]
fn try_append(&mut self, src: &[u8]) -> Result<(), CommError> {
let start = self.len;
let end = start.checked_add(src.len()).ok_or(CommError::Overflow)?;
if end > N - 2 {
return Err(CommError::Overflow);
}
self.comm.buf[start..end].copy_from_slice(src);
self.len = end;
Ok(())
}
pub fn extend(self, src: &[u8]) -> Result<Self, CommError> {
let mut this = self;
this.try_append(src)?;
Ok(this)
}
pub fn append(&mut self, src: &[u8]) -> Result<&mut Self, CommError> {
self.try_append(src)?;
Ok(self)
}
pub fn send<T: Into<Reply>>(mut self, reply: T) -> Result<&'a mut Comm<N>, CommError> {
let sw: u16 = reply.into().0;
self.append(sw.to_be_bytes().as_ref())?;
let n = self.len;
if 0 > sys_seph::io_tx(self.comm.apdu_type, self.comm.buf[..n].as_ref(), n) {
return Err(CommError::IoError);
}
self.comm.pending_apdu = false;
Ok(self.comm)
}
pub fn clear(&mut self) {
self.len = 0;
}
}