mirror of
https://github.com/trussed-dev/apdu-dispatch.git
synced 2026-10-28 01:03:29 -07:00
205 lines
5.6 KiB
Rust
205 lines
5.6 KiB
Rust
use apdu_app::{App, Result};
|
|
use apdu_dispatch::{
|
|
dispatch::ApduDispatch,
|
|
interchanges::{Channel, Data, Requester},
|
|
};
|
|
use heapless::VecView;
|
|
use hex_literal::hex;
|
|
use iso7816::{command::CommandView, Aid, Interface};
|
|
|
|
const AID_PIV: &[u8] = &hex!("A000000308000010000100");
|
|
const AID_OPENPGP: &[u8] = &hex!("D276000124010304000FFFFFFFFF0000");
|
|
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
struct Calls {
|
|
select: usize,
|
|
deselect: usize,
|
|
call: usize,
|
|
}
|
|
|
|
struct TestApp {
|
|
aid: Aid,
|
|
calls: Calls,
|
|
}
|
|
|
|
impl TestApp {
|
|
fn new(aid: Aid) -> Self {
|
|
Self {
|
|
aid,
|
|
calls: Calls::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl iso7816::App for TestApp {
|
|
fn aid(&self) -> Aid {
|
|
self.aid
|
|
}
|
|
}
|
|
|
|
impl App for TestApp {
|
|
fn select(
|
|
&mut self,
|
|
_interface: Interface,
|
|
_apdu: CommandView<'_>,
|
|
_reply: &mut VecView<u8>,
|
|
) -> Result {
|
|
self.calls.select += 1;
|
|
Ok(())
|
|
}
|
|
|
|
fn deselect(&mut self) {
|
|
self.calls.deselect += 1;
|
|
}
|
|
|
|
fn call(
|
|
&mut self,
|
|
_interface: Interface,
|
|
_apdu: CommandView<'_>,
|
|
_reply: &mut VecView<u8>,
|
|
) -> Result {
|
|
self.calls.call += 1;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
struct Channels {
|
|
contact: Channel,
|
|
contactless: Channel,
|
|
}
|
|
|
|
impl Channels {
|
|
fn new() -> Self {
|
|
Self {
|
|
contact: Channel::new(),
|
|
contactless: Channel::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Dispatch<'a, const N: usize> {
|
|
contact_rq: Requester<'a>,
|
|
contactless_rq: Requester<'a>,
|
|
dispatch: ApduDispatch<'a>,
|
|
apps: [TestApp; N],
|
|
}
|
|
|
|
impl<'a, const N: usize> Dispatch<'a, N> {
|
|
fn new(channels: &'a Channels, aids: [Aid; N]) -> Self {
|
|
let (contact_rq, contact_rp) = channels.contact.split().unwrap();
|
|
let (contactless_rq, contactless_rp) = channels.contactless.split().unwrap();
|
|
let dispatch = ApduDispatch::new(contact_rp, contactless_rp);
|
|
let apps = aids.map(TestApp::new);
|
|
Self {
|
|
contact_rq,
|
|
contactless_rq,
|
|
dispatch,
|
|
apps,
|
|
}
|
|
}
|
|
|
|
fn send_command(&mut self, interface: Interface, command: &[u8]) -> Option<Data> {
|
|
let rq = match interface {
|
|
Interface::Contact => &mut self.contact_rq,
|
|
Interface::Contactless => &mut self.contactless_rq,
|
|
};
|
|
rq.request(command.try_into().unwrap()).unwrap();
|
|
let mut apps: Vec<&mut dyn App> = self.apps.iter_mut().map(|app| app as _).collect();
|
|
self.dispatch.poll(&mut apps);
|
|
rq.take_response()
|
|
}
|
|
|
|
fn calls(&self) -> [Calls; N] {
|
|
self.apps.each_ref().map(|app| app.calls)
|
|
}
|
|
}
|
|
|
|
fn select(aid: &[u8]) -> Vec<u8> {
|
|
let mut cmd = Vec::from(hex!("00A40400"));
|
|
cmd.push(aid.len().try_into().unwrap());
|
|
cmd.extend(aid);
|
|
cmd
|
|
}
|
|
|
|
#[test]
|
|
fn test_simple() {
|
|
let aids = [Aid::try_new(AID_PIV).unwrap()];
|
|
let mut calls = [Calls::default()];
|
|
let channels = Channels::new();
|
|
let mut dispatch = Dispatch::new(&channels, aids);
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let dummy_cmd = hex!("00000000");
|
|
let select_cmd = select(AID_PIV);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &dummy_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("6A82"));
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &select_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[0].select += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &select_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[0].select += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &dummy_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[0].call += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
}
|
|
|
|
#[test]
|
|
fn test_multi() {
|
|
let aids = [
|
|
Aid::try_new(AID_PIV).unwrap(),
|
|
Aid::try_new(AID_OPENPGP).unwrap(),
|
|
];
|
|
let mut calls = [Calls::default(), Calls::default()];
|
|
let channels = Channels::new();
|
|
let mut dispatch = Dispatch::new(&channels, aids);
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let dummy_cmd = hex!("00000000");
|
|
let select_piv_cmd = select(AID_PIV);
|
|
let select_openpgp_cmd = select(AID_OPENPGP);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &dummy_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("6A82"));
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &select_piv_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[0].select += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &select_piv_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[0].select += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &dummy_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[0].call += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &select_openpgp_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[0].deselect += 1;
|
|
calls[1].select += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &select_openpgp_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[1].select += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
|
|
let response = dispatch.send_command(Interface::Contact, &dummy_cmd);
|
|
assert_eq!(response.unwrap(), &hex!("9000"));
|
|
calls[1].call += 1;
|
|
assert_eq!(dispatch.calls(), calls);
|
|
}
|