diff --git a/Cargo.toml b/Cargo.toml index 41ce813..fbb75df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ cortex-m-semihosting = { version = "0.3.5", optional = true} heapless = "0.5.5" heapless-bytes = { git = "https://github.com/ycrypto/heapless-bytes", branch = "main" } # heapless-bytes = { path = "../../../heapless-bytes" } +interchange = { path = "../interchange" } usb-device = { version = "0.2.3", features = ["control-buffer-256"] } [features] diff --git a/src/class.rs b/src/class.rs index 0015f02..9e9c389 100644 --- a/src/class.rs +++ b/src/class.rs @@ -1,11 +1,15 @@ use core::convert::TryFrom; use cortex_m_semihosting::hprintln; +use interchange::RequestPipe; use crate::{ constants::*, types::{ - apdu, + apdu::{ + self, + ApduInterchange, + }, ClassRequest, packet::{ self, @@ -33,7 +37,10 @@ impl Ccid where Bus: 'static + UsbBus, { - pub fn new(allocator: &'static UsbBusAllocator) -> Self { + pub fn new( + allocator: &'static UsbBusAllocator, + request_pipe: RequestPipe, + ) -> Self { let read = allocator.bulk(PACKET_SIZE as _); let write = allocator.bulk(PACKET_SIZE as _); // TODO: Add interrupt endpoint, so PC/SC does not @@ -42,10 +49,15 @@ where // PROBLEM: We don't have enough endpoints on the peripheral :/ // (USBHS should have one more) // let interrupt = allocator.interrupt(8 as _, 32); - let pipe = Pipe::new(write); + let pipe = Pipe::new(write, request_pipe); let interface_number = allocator.interface(); Self { interface_number, read, /* interrupt, */ pipe } } + + // needs better name, maybe call directly + pub fn sneaky_poll(&mut self) { + self.poll(); + } } impl UsbClass for Ccid @@ -72,6 +84,7 @@ where } fn poll(&mut self) { + // hprintln!("poll of ccid").ok(); self.pipe.poll_app(); self.pipe.maybe_send_packet(); } diff --git a/src/pipe.rs b/src/pipe.rs index f64c5f3..51b394a 100644 --- a/src/pipe.rs +++ b/src/pipe.rs @@ -1,11 +1,15 @@ use core::convert::TryFrom; use cortex_m_semihosting::hprintln; +use interchange::RequestPipe; use crate::{ constants::*, types::{ - apdu, + apdu::{ + self, + ApduInterchange, + }, MessageBuffer, packet::{ self, @@ -41,7 +45,9 @@ where // pub(crate) rpc: TransportEndpoint<'rpc>, seq: u8, state: State, + // TODO: remove, use interchange message: MessageBuffer, + interchange: RequestPipe, sent: usize, outbox: Option, } @@ -50,7 +56,10 @@ impl Pipe where Bus: 'static + UsbBus, { - pub(crate) fn new(write: EndpointIn<'static, Bus>) -> Self { + pub(crate) fn new( + write: EndpointIn<'static, Bus>, + request_pipe: RequestPipe, + ) -> Self { assert!(MAX_MSG_LENGTH >= PACKET_SIZE); @@ -61,6 +70,7 @@ where sent: 0, outbox: None, message: MessageBuffer::new(), + interchange: request_pipe, } } @@ -148,7 +158,6 @@ where assert!(command.data().len() + self.message.len() <= MAX_MSG_LENGTH); self.message.extend_from_slice(command.data()).unwrap(); self.call_app(); - self.state = State::Processing; } _ => panic!("{:?} unexpected in receiving state"), } @@ -174,18 +183,38 @@ where } fn call_app(&mut self) { + hprintln!("called piv app").ok(); + self.interchange.try_request( + apdu::Command::try_from(&self.message).unwrap() + ).expect("could not deposit command"); + hprintln!("set ccid state to processing").ok(); + self.state = State::Processing; // todo!("have message of length {} to dispatch", self.message.len()); } pub fn poll_app(&mut self) { + // static mut i: usize = 0; + // unsafe { + // if i < 100 { + // i += 1; + // } else { + // hprintln!(".").ok(); + // } + // } if let State::Processing = self.state { + hprintln!("processing, checking for response, interchange state {:?}", + self.interchange.state_byte).ok(); - crate::piv::fake_piv(&mut self.message); + if let Some(response) = self.interchange.take_response() { + self.message = response.into_message(); - // we should have an open XfrBlock allowance - self.state = State::ReadyToSend; - self.sent = 0; - self.prime_outbox(); + // crate::piv::fake_piv(&mut self.message); + + // we should have an open XfrBlock allowance + self.state = State::ReadyToSend; + self.sent = 0; + self.prime_outbox(); + } } } diff --git a/src/piv.rs b/src/piv.rs index fd3a1f6..e5a763f 100644 --- a/src/piv.rs +++ b/src/piv.rs @@ -44,7 +44,7 @@ pub const PIV_AID: [u8; 11] pub const YUBICO_OTP_PIX: &[u8; 3] = &[0x20, 0x01, 0x01]; pub const YUBICO_OTP_AID: &[u8; 8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01]; // they use it to "deauthenticate user PIN and mgmt key": https://git.io/JfWgN -pub const YUBICO_MGMT_PIX: [u8; 3] = &[0x47, 0x11, 0x17]; +pub const YUBICO_MGMT_PIX: &[u8; 3] = &[0x47, 0x11, 0x17]; pub const YUBICO_MGMT_AID: &[u8; 8] = &[0xa0, 0x00, 0x00, 0x05, 0x27, 0x20, 0x01, 0x01]; // https://git.io/JfW28 @@ -146,7 +146,7 @@ pub const GET_DATA: (u8, u8, u8, u8) = ( // == == == == == == == == == == == pub fn fake_piv(command: &mut MessageBuffer) { - let apdu = match apdu::Apdu::try_from(command.as_mut()) { + let apdu = match apdu::Apdu::try_from(command.as_ref()) { Ok(apdu) => apdu, Err(_) => { invalid_apdu(command); @@ -170,11 +170,11 @@ pub fn fake_piv(command: &mut MessageBuffer) { // // 05808693 APDU: 00 A4 04 00 05 A0 00 00 03 08 hprintln!("got SELECT").ok(); - let is_nist_rid = apdu.data() == &NIST_RID; + let is_nist_rid = apdu.data() == &NIST_RID[..]; let is_piv = apdu.data() == &PIV_AID; let is_trunc_piv = apdu.data() == &PIV_TRUNCATED_AID; let is_pivish = is_piv || is_trunc_piv || is_nist_rid; - let is_yubico = apdu.data() == YUBICO_AID; + let is_yubico = apdu.data() == YUBICO_OTP_AID; if is_pivish { hprintln!("for PIV").ok(); @@ -381,7 +381,7 @@ fn select(command: &mut MessageBuffer) { // Coexistent tag allocation authority der.nested(0x79, |der| { // Application identifier - der.raw_tlv(0x4f, &NIST_RID) + der.raw_tlv(0x4f, &NIST_RID[..]) // })?; }) diff --git a/src/types/apdu.rs b/src/types/apdu.rs index 1f0d9f6..4c50dfc 100644 --- a/src/types/apdu.rs +++ b/src/types/apdu.rs @@ -1,10 +1,58 @@ use super::*; +#[derive(Clone, Debug, PartialEq)] +pub struct Command { + pub cla: u8, + pub ins: u8, + pub p1: u8, + pub p2: u8, + pub lc: usize, + pub le: usize, + pub data: MessageBuffer, +} + +impl core::convert::TryFrom<&MessageBuffer> for Command { + type Error = (); + fn try_from(message: &MessageBuffer) -> core::result::Result { + let apdu = Apdu::try_from(message.as_ref())?; + Ok(Self { + cla: apdu.cla(), + ins: apdu.ins(), + p1: apdu.p1(), + p2: apdu.p2(), + lc: apdu.lc(), + le: apdu.le(), + data: MessageBuffer::try_from_slice(apdu.data()).unwrap(), + }) + } +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct Response { + pub sw1: u8, + pub sw2: u8, + pub data: MessageBuffer, +} + +impl Response { + pub fn into_message(self) -> MessageBuffer { + let mut message = MessageBuffer::new(); + message.extend_from_slice(&self.data).unwrap(); + message.push(self.sw1).unwrap(); + message.push(self.sw2).unwrap(); + message + } +} + +interchange::interchange! { + ApduInterchange: (Command, Response) +} + pub struct Apdu<'a> { lc: usize, le: usize, offset: usize, - apdu: &'a mut [u8] + apdu: &'a [u8] } impl<'a> core::ops::Deref for Apdu<'a> { @@ -16,9 +64,9 @@ impl<'a> core::ops::Deref for Apdu<'a> { } } -impl<'a> core::convert::TryFrom<&'a mut [u8]> for Apdu<'a> { +impl<'a> core::convert::TryFrom<&'a [u8]> for Apdu<'a> { type Error = (); - fn try_from(apdu: &'a mut [u8]) -> core::result::Result { + fn try_from(apdu: &'a [u8]) -> core::result::Result { let (lc, le, offset) = calculate_lengths(apdu)?; Ok(Self { lc, le, offset, apdu }) }