Basics for CCID from Zissou (ATR)

This commit is contained in:
Nicolas Stalder
2020-05-02 04:14:46 +02:00
commit 9ed8a6fd9f
5 changed files with 579 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "usbd-ccid"
version = "0.0.0-unreleased"
authors = ["Nicolas Stalder <n@stalder.io>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cortex-m-semihosting = { version = "0.3.5", optional = true}
usb-device = { version = "0.2.3", features = ["control-buffer-256"] }
[features]
default = [
"cortex-m-semihosting",
]
highspeed-usb = []
+130
View File
@@ -0,0 +1,130 @@
use core::convert::TryFrom;
use cortex_m_semihosting::hprintln;
use crate::{
constants::*,
pipe::Pipe,
};
use usb_device::class_prelude::*;
type Result<T> = core::result::Result<T, UsbError>;
pub struct Ccid<Bus>
where
Bus: 'static + UsbBus,
{
interface_number: InterfaceNumber,
pipe: Pipe<Bus>,
must_send_zlp: bool,
}
impl<Bus> Ccid<Bus>
where
Bus: 'static + UsbBus,
{
pub fn new(allocator: &'static UsbBusAllocator<Bus>) -> Self {
let read_endpoint = allocator.bulk(PACKET_SIZE as _);
let write_endpoint = allocator.bulk(PACKET_SIZE as _);
let pipe = Pipe::new(read_endpoint, write_endpoint);
let interface_number = allocator.interface();
Self { interface_number, pipe, must_send_zlp: false }
}
}
pub enum ClassRequests {
}
impl<Bus> UsbClass<Bus> for Ccid<Bus>
where
Bus: 'static + UsbBus,
{
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter)
-> Result<()>
{
writer.interface(
self.interface_number,
CLASS_CCID,
SUBCLASS_NONE,
TransferMode::Bulk as u8,
)?;
writer.write(
FUNCTIONAL_INTERFACE,
&FUNCTIONAL_INTERFACE_DESCRIPTOR,
)?;
writer.endpoint(&self.pipe.write);
writer.endpoint(&self.pipe.read);
Ok(())
}
fn poll(&mut self) {
self.pipe.maybe_write_packet();
}
fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
if addr != self.pipe.write.address() { return; }
if self.must_send_zlp {
self.pipe.write.write(&[]).ok();
self.must_send_zlp = false;
} else {
self.pipe.maybe_write_packet();
}
}
fn endpoint_out(&mut self, addr: EndpointAddress) {
if addr != self.pipe.read.address() { return; }
self.pipe.read_and_handle_packet();
}
fn control_in(&mut self, transfer: ControlIn<Bus>) {
use usb_device::control::*;
let Request { request_type, recipient, index, request, .. } = *transfer.request();
if index as u8 != u8::from(self.interface_number) {
return;
}
if (request_type, recipient) == (RequestType::Class, Recipient::Interface) {
match ClassRequest::try_from(request) {
Ok(request) => {
match request {
ClassRequest::Abort => {
// oh yeah?
transfer.reject().ok();
todo!();
}
// not strictly needed, as our bNumClockSupported = 0
ClassRequest::GetClockFrequencies => {
transfer.accept(|data| {
data.copy_from_slice(&CLOCK_FREQUENCY_KHZ);
Ok(4)
}).ok();
},
// not strictly needed, as our bNumDataRatesSupported = 0
ClassRequest::GetDataRates => {
transfer.accept(|data| {
data.copy_from_slice(&DATA_RATE_BPS);
Ok(4)
}).ok();
},
}
}
Err(()) => {
hprintln!("unexpected request: {}", request).ok();
}
}
}
}
fn control_out(&mut self, transfer: ControlOut<Bus>) {
// todo!();
}
}
+137
View File
@@ -0,0 +1,137 @@
// can be 8, 16, 32, 64 or 512
#[cfg(feature = "highspeed-usb")]
pub const PACKET_SIZE: usize = 512;
#[cfg(not(feature = "highspeed-usb"))]
pub const PACKET_SIZE: usize = 64;
pub const CLASS_CCID: u8 = 0x0B;
pub const SUBCLASS_NONE: u8 = 0x0;
#[repr(u8)]
pub enum TransferMode {
// bulk transfers, optional interrupt IN
Bulk = 0,
// control transfers, no interrupt IN
ControlA = 1,
// control transfers, optional interrupt IN
ControlB = 2,
}
pub const FUNCTIONAL_INTERFACE: u8 = 0x21;
pub enum ClassRequest {
Abort = 1,
GetClockFrequencies = 2,
GetDataRates = 3,
}
impl core::convert::TryFrom<u8> for ClassRequest {
type Error = ();
fn try_from(request: u8) -> core::result::Result<Self, ()> {
Ok(match request {
1 => Self::Abort,
2 => Self::GetClockFrequencies,
3 => Self::GetDataRates,
_ => return Err(()),
})
}
}
// NB: all numbers are little-endian
// 4000 KHz = 4MHz
// pub const CLOCK_FREQUENCY: [u8; 4] = 4000u32.to_le_bytes();
// instead, use Python: `import struct; struct.pack("<I", 4000)`
pub const CLOCK_FREQUENCY_KHZ: [u8; 4] = [0xa0, 0x0f, 0x00, 0x00];
// 307200 bps (gnuk: 9600)
pub const DATA_RATE_BPS: [u8; 4] = [0x00, 0xb0, 0x04, 0x00];
// 2038 (gnuk: 254)
pub const MAX_IFSD: [u8; 4] = [0xf6, 0x07, 0x00, 0x00];
//
// dwMaxCCIDMsgLen 3072 (gnuk: 271)
pub const MAX_MSG_LENGTH: usize = 3072;
pub const MAX_MSG_LENGTH_LE: [u8; 4] = [0x00, 0x0C, 0x00, 0x00];
pub const NUM_SLOTS: u8 = 1;
pub const MAX_BUSY_SLOTS: u8 = 1;
// bPinSupport (0x0 = none, 0x01 = verification, 0x02 = modification)
pub const PIN_SUPPORT: u8 = 0;
// cf. Sec. 5.1 in: https://www.usb.org/sites/default/files/DWG_Smart-Card_CCID_Rev110.pdf
pub const FUNCTIONAL_INTERFACE_DESCRIPTOR: [u8; 52] = [
// bcdCCID rev1.10 <-- Linux doesn't know about this
// 0x10, 0x01,
// bcdCCID rev1.00
0x00, 0x01,
// bMaxSlotIndex
NUM_SLOTS - 1,
// bVoltageSupport (5.0V + 3.0V + 1.8V)
0x07,
// dwProtocols: T=1 only (0 = T=0, 3 = T0+T1)
0x02, 0x00, 0x00, 0x00,
// dwDefaultClock (4 MHz)
CLOCK_FREQUENCY_KHZ[0],
CLOCK_FREQUENCY_KHZ[1],
CLOCK_FREQUENCY_KHZ[2],
CLOCK_FREQUENCY_KHZ[3],
// dwMaximumClock (same)
CLOCK_FREQUENCY_KHZ[0],
CLOCK_FREQUENCY_KHZ[1],
CLOCK_FREQUENCY_KHZ[2],
CLOCK_FREQUENCY_KHZ[3],
// bNumClockSupported
0x00,
// dwDataRate (307200 bps)
DATA_RATE_BPS[0],
DATA_RATE_BPS[1],
DATA_RATE_BPS[2],
DATA_RATE_BPS[3],
// dwMaxDataRate (same)
DATA_RATE_BPS[0],
DATA_RATE_BPS[1],
DATA_RATE_BPS[2],
DATA_RATE_BPS[3],
// bNumDataRatesSupported
0x00,
// dwMaxIFSD (2038)
MAX_IFSD[0],
MAX_IFSD[1],
MAX_IFSD[2],
MAX_IFSD[3],
// dwSyncProtocols: none
0x00, 0x00, 0x00, 0x00,
// dwMechanical: no special characteristics
0x00, 0x00, 0x00, 0x00,
// dwFeatures, see following comments
// Auto configuration based on ATR
// Auto activation on insert
// Auto voltage selection
// Auto clock change
// Auto baud rate change
// Auto parameter negotiation made by CCID
// Short and extended APDU level exchange
0xFE, 0x00, 0x04, 0x00,
// dwMaxCCIDMsgLen (3072)
// gnuk: 271
MAX_MSG_LENGTH_LE[0],
MAX_MSG_LENGTH_LE[1],
MAX_MSG_LENGTH_LE[2],
MAX_MSG_LENGTH_LE[3],
// bClassGetResponse ("echo")
0xFF,
// bClassEnvelope ("echo"), gnuk: 0
0xFF,
// wlcdLayout (none)
0x00, 0x00,
// bPinSupport
PIN_SUPPORT,
// bMaxCCIDBusySlots
MAX_BUSY_SLOTS,
];
+10
View File
@@ -0,0 +1,10 @@
#![no_std]
//! https://www.usb.org/sites/default/files/DWG_Smart-Card_CCID_Rev110.pdf
//! https://www.usb.org/sites/default/files/DWG_Smart-Card_USB-ICC_ICCD_rev10.pdf
pub mod constants;
pub mod class;
pub mod pipe;
pub use class::Ccid;
+284
View File
@@ -0,0 +1,284 @@
use core::{
borrow::{Borrow, BorrowMut},
convert::{TryFrom, TryInto},
};
use cortex_m_semihosting::hprintln;
use crate::constants::*;
use usb_device::class_prelude::*;
pub struct Pipe<Bus>
where
Bus: UsbBus + 'static,
{
pub(crate) read: EndpointOut<'static, Bus>,
pub(crate) write: EndpointIn<'static, Bus>,
// pub(crate) rpc: TransportEndpoint<'rpc>,
packet: Packet,
buffer: [u8; MAX_MSG_LENGTH],
}
impl<Bus> Pipe<Bus>
where
Bus: 'static + UsbBus,
{
pub(crate) fn new(
read: EndpointOut<'static, Bus>,
write: EndpointIn<'static, Bus>,
)
-> Self { Self {
read,
write,
packet: Packet { buffer: [0u8; PACKET_SIZE] },
buffer: [0u8; MAX_MSG_LENGTH],
} }
}
#[derive(Copy, Clone)]
pub struct Packet {
buffer: [u8; PACKET_SIZE as _],
}
impl core::fmt::Debug for Packet {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut debug_struct = f.debug_struct("Packet");
let mut debug_struct = match self.msg_type() {
Ok(message_type) => debug_struct.field("type", &message_type),
Err(()) => debug_struct.field("type", &self[0]),
};
debug_struct
.field("slot", &self.slot())
.field("seq", &self.slot())
// other fields?
// .field("data[..16]", &self.data()[..16])
// .field("data", self.data())
.finish()
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum MessageType {
PowerOn = 0x62,
PowerOff = 0x63,
GetSlotStatus = 0x65,
TransferBlock = 0x6f,
}
pub struct SlotStatus {
seq: u8
}
impl SlotStatus {
pub fn new(seq: u8) -> Self {
Self { seq }
}
}
impl From<SlotStatus> for Packet {
fn from(slot_status: SlotStatus) -> Self {
let mut buffer = [0u8; PACKET_SIZE];
buffer[0] = 0x81;
// buffer[1..5] = 0, no extra data
// buffer[5] = 0, only one slot
buffer[6] = slot_status.seq;
// buffer[7] = 0, status
// buffer[8] = 0, error
// buffer[9] = 0, chain parameter, this is complete
Packet { buffer }
}
}
pub struct DataBlock<'a> {
seq: u8,
data: &'a [u8],
}
impl<'a> DataBlock<'a> {
pub fn new(seq: u8, data: &'a [u8]) -> Self {
assert!(data.len() + 10 <= PACKET_SIZE);
Self { seq, data }
}
}
impl<'a> From<DataBlock<'a>> for Packet {
fn from(data_block: DataBlock) -> Self {
let mut buffer = [0u8; PACKET_SIZE];
let len = data_block.data.len();
buffer[0] = 0x80;
buffer[1..5].copy_from_slice(&len.to_le_bytes());
// buffer[5] = 0, only one slot
buffer[6] = data_block.seq;
// buffer[7] = 0, status
// buffer[8] = 0, error
// buffer[9] = 0, chain parameter, this is complete
buffer[10..][..len].copy_from_slice(data_block.data);
Packet { buffer }
}
}
impl core::convert::TryFrom<u8> for MessageType {
type Error = ();
fn try_from(message_type_byte: u8) -> core::result::Result<Self, ()> {
Ok(match message_type_byte {
0x62 => Self::PowerOn,
0x63 => Self::PowerOff,
0x65 => Self::GetSlotStatus,
0x6f => Self::TransferBlock,
_ => return Err(()),
})
}
}
impl Packet {
#[inline(always)]
pub fn msg_type(&self) -> core::result::Result<MessageType, ()> {
self[0].try_into()
}
#[inline(always)]
pub fn len(&self) -> usize {
u32::from_le_bytes(self[1..5].try_into().unwrap()) as usize
}
#[inline(always)]
pub fn slot(&self) -> u8 {
// we have only one slot
assert!(self[5] == 0);
*&self[5]
}
#[inline(always)]
pub fn seq(&self) -> u8 { *&self[6] }
// either three message specific bytes, or
// a status field (1 byte), an error field and one message specific byte
#[inline(always)]
pub fn data(&self) -> &[u8] { &self[10..] }
}
// impl core::borrow::Borrow<[u8; PACKET_SIZE]> for Packet {
// fn borrow(&self) -> &[u8; PACKET_SIZE] {
// &self.buffer
// }
// }
// impl core::borrow::BorrowMut<[u8; PACKET_SIZE]> for Packet {
// fn borrow_mut(&mut self) -> &mut [u8; PACKET_SIZE] {
// &mut self.buffer
// }
// }
impl core::ops::Deref for Packet {
type Target = [u8];
fn deref(&self) -> &Self::Target {
// let len = self.len();
// &self.buffer[..10 + len]
&self.buffer
}
}
impl core::ops::DerefMut for Packet {
fn deref_mut(&mut self) -> &mut Self::Target {
// let len = self.len();
// &mut self.buffer[..10 + len]
&mut self.buffer
}
}
impl<Bus> Pipe<Bus>
where
Bus: 'static + UsbBus
{
pub fn read_and_handle_packet(&mut self) {
// let data: &mut [u8; PACKET_SIZE] = self.packet.borrow_mut();
// let read = match self.read.read(&mut data[..]) {
let read = match self.read.read(&mut self.packet) {
// let read = match self.read.read(&mut self.packet.borrow_mut()[..]) {
Ok(read) => {
// all packets have 10 byte header before eventual data
if read < 10 { panic!("unexpected small packet"); }
read
}
Err(_) => {
// usb-device lists WouldBlock + BufferOverflow as possible errors.
// both should not occur here, and we can't do anything anyway.
panic!("unexpected read error");
}
};
hprintln!("got a packet of len {}: {:?}", read, &self.packet).ok();
if let Ok(msg_type) = self.packet.msg_type() {
match msg_type {
MessageType::PowerOff |
MessageType::GetSlotStatus => {
self.send_slot_status();
}
MessageType::PowerOn => {
self.send_atr();
}
MessageType::TransferBlock => {
//
}
}
}
}
fn send_slot_status(&self) {
let packet = Packet::from(SlotStatus::new(self.packet.seq()));
hprintln!("answering with: {:?}", &packet).ok();
match self.write.write(&packet[..10 + packet.len()]) {
Ok(10) => {}
Ok(n) => panic!("expected to send exactly 10 bytes, sent {}", n),
Err(UsbError::WouldBlock) => panic!("would block not handled yet"),
Err(e) => panic!("unexpected error {:?}", e),
}
}
fn send_atr(&self) {
let packet = Packet::from(DataBlock::new(
self.packet.seq(),
&[0x3b, 0x8c,0x80,0x01],
));
hprintln!("answering with: {:?}", &packet).ok();
match self.write.write(&packet[..10 + packet.len()]) {
Ok(14) => {}
Ok(n) => panic!("expected to send exactly 14 bytes, sent {}", n),
Err(UsbError::WouldBlock) => panic!("would block not handled yet"),
Err(e) => panic!("unexpected error {:?}", e),
}
}
pub fn maybe_write_packet(&mut self) {
// let result = self.write.write(&packet);
// match result {
// Err(UsbError::WouldBlock) => {
// // fine, can't write try later
// // this shouldn't happen probably
// },
}
// pub fn read_address(&self) -> EndpointAddress {
// self.read.address()
// }
// pub fn write_address(&self) -> EndpointAddress {
// self.write.address()
// }
}