netget/server/usb/smartcard/
ccid.rs1use anyhow::{bail, Result};
22
23pub mod command {
25 pub const ICC_POWER_ON: u8 = 0x62;
26 pub const ICC_POWER_OFF: u8 = 0x63;
27 pub const GET_SLOT_STATUS: u8 = 0x65;
28 pub const XFR_BLOCK: u8 = 0x6F;
29 pub const GET_PARAMETERS: u8 = 0x6C;
30 pub const RESET_PARAMETERS: u8 = 0x6D;
31 pub const SET_PARAMETERS: u8 = 0x61;
32 pub const ESCAPE: u8 = 0x6B;
33 pub const ICC_CLOCK: u8 = 0x6E;
34 pub const T0_APDU: u8 = 0x6A;
35 pub const ABORT: u8 = 0x72;
36 pub const SET_DATA_RATE_AND_CLOCK_FREQUENCY: u8 = 0x73;
37}
38
39pub mod response {
41 pub const DATA_BLOCK: u8 = 0x80;
42 pub const SLOT_STATUS: u8 = 0x81;
43 pub const PARAMETERS: u8 = 0x82;
44 pub const ESCAPE: u8 = 0x83;
45 pub const DATA_RATE_AND_CLOCK_FREQUENCY: u8 = 0x84;
46 pub const NOTIFY_SLOT_CHANGE: u8 = 0x50;
48}
49
50pub mod icc_status {
52 pub const PRESENT_ACTIVE: u8 = 0;
53 pub const PRESENT_INACTIVE: u8 = 1;
54 pub const NOT_PRESENT: u8 = 2;
55}
56
57pub mod command_status {
59 pub const PROCESSED: u8 = 0;
60 pub const FAILED: u8 = 1;
61}
62
63pub mod error {
65 pub const CMD_NOT_SUPPORTED: u8 = 0x00;
67 pub const ICC_MUTE: u8 = 0xFE;
69 pub const NONE: u8 = 0x00;
71}
72
73pub const HEADER_LEN: usize = 10;
75
76pub const MAX_MESSAGE_LEN: usize = 271;
80
81pub const MAX_PAYLOAD_LEN: usize = MAX_MESSAGE_LEN - HEADER_LEN;
83
84pub fn status_byte(icc: u8, command: u8) -> u8 {
86 (icc & 0x03) | ((command & 0x03) << 6)
87}
88
89#[derive(Debug, Clone)]
91pub struct CcidCommand {
92 pub message_type: u8,
93 pub slot: u8,
94 pub seq: u8,
96 pub params: [u8; 3],
99 pub data: Vec<u8>,
100}
101
102impl CcidCommand {
103 pub fn parse(bytes: &[u8]) -> Result<Self> {
105 if bytes.len() < HEADER_LEN {
106 bail!(
107 "CCID message too short: {} byte(s), need at least {}",
108 bytes.len(),
109 HEADER_LEN
110 );
111 }
112
113 let declared = u32::from_le_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]) as usize;
114 if declared > MAX_PAYLOAD_LEN {
115 bail!(
116 "CCID dwLength={} exceeds the advertised maximum payload of {} byte(s)",
117 declared,
118 MAX_PAYLOAD_LEN
119 );
120 }
121 if bytes.len() < HEADER_LEN + declared {
122 bail!(
123 "CCID message truncated: dwLength={} but only {} payload byte(s) present",
124 declared,
125 bytes.len() - HEADER_LEN
126 );
127 }
128
129 Ok(Self {
130 message_type: bytes[0],
131 slot: bytes[5],
132 seq: bytes[6],
133 params: [bytes[7], bytes[8], bytes[9]],
134 data: bytes[HEADER_LEN..HEADER_LEN + declared].to_vec(),
135 })
136 }
137
138 pub fn name(&self) -> &'static str {
140 match self.message_type {
141 command::ICC_POWER_ON => "PC_to_RDR_IccPowerOn",
142 command::ICC_POWER_OFF => "PC_to_RDR_IccPowerOff",
143 command::GET_SLOT_STATUS => "PC_to_RDR_GetSlotStatus",
144 command::XFR_BLOCK => "PC_to_RDR_XfrBlock",
145 command::GET_PARAMETERS => "PC_to_RDR_GetParameters",
146 command::RESET_PARAMETERS => "PC_to_RDR_ResetParameters",
147 command::SET_PARAMETERS => "PC_to_RDR_SetParameters",
148 command::ESCAPE => "PC_to_RDR_Escape",
149 command::ICC_CLOCK => "PC_to_RDR_IccClock",
150 command::T0_APDU => "PC_to_RDR_T0APDU",
151 command::ABORT => "PC_to_RDR_Abort",
152 command::SET_DATA_RATE_AND_CLOCK_FREQUENCY => "PC_to_RDR_SetDataRateAndClockFrequency",
153 _ => "PC_to_RDR_Unknown",
154 }
155 }
156}
157
158fn build(message_type: u8, slot: u8, seq: u8, params: [u8; 3], payload: &[u8]) -> Result<Vec<u8>> {
163 if payload.len() > MAX_PAYLOAD_LEN {
164 bail!(
165 "CCID payload of {} byte(s) exceeds dwMaxCCIDMessageLength ({} byte(s) of payload)",
166 payload.len(),
167 MAX_PAYLOAD_LEN
168 );
169 }
170 let mut message = Vec::with_capacity(HEADER_LEN + payload.len());
171 message.push(message_type);
172 message.extend_from_slice(&(payload.len() as u32).to_le_bytes());
173 message.push(slot);
174 message.push(seq);
175 message.extend_from_slice(¶ms);
176 message.extend_from_slice(payload);
177 Ok(message)
178}
179
180pub fn data_block(slot: u8, seq: u8, icc: u8, cmd: u8, err: u8, payload: &[u8]) -> Result<Vec<u8>> {
183 build(
184 response::DATA_BLOCK,
185 slot,
186 seq,
187 [status_byte(icc, cmd), err, 0x00],
188 payload,
189 )
190}
191
192pub fn slot_status(slot: u8, seq: u8, icc: u8, cmd: u8, err: u8) -> Vec<u8> {
195 build(
197 response::SLOT_STATUS,
198 slot,
199 seq,
200 [status_byte(icc, cmd), err, 0x00],
201 &[],
202 )
203 .expect("slot status has an empty payload")
204}
205
206pub fn parameters_t0(slot: u8, seq: u8, icc: u8) -> Vec<u8> {
208 const T0_PARAMETERS: [u8; 5] = [0x11, 0x00, 0x00, 0x0A, 0x00];
211 build(
212 response::PARAMETERS,
213 slot,
214 seq,
215 [
216 status_byte(icc, command_status::PROCESSED),
217 error::NONE,
218 0x00,
219 ],
220 &T0_PARAMETERS,
221 )
222 .expect("T=0 parameter block is 5 bytes")
223}
224
225pub fn escape_unsupported(slot: u8, seq: u8, icc: u8) -> Vec<u8> {
227 build(
228 response::ESCAPE,
229 slot,
230 seq,
231 [
232 status_byte(icc, command_status::FAILED),
233 error::CMD_NOT_SUPPORTED,
234 0x00,
235 ],
236 &[],
237 )
238 .expect("escape response has an empty payload")
239}
240
241pub fn notify_slot_change(card_present: bool) -> Vec<u8> {
246 let mut bitmap = 0b10; if card_present {
248 bitmap |= 0b01;
249 }
250 vec![response::NOTIFY_SLOT_CHANGE, bitmap]
251}
252
253pub fn class_descriptor() -> Vec<u8> {
257 let mut descriptor = Vec::with_capacity(54);
258 descriptor.push(54); descriptor.push(0x21); descriptor.extend_from_slice(&0x0110u16.to_le_bytes()); descriptor.push(0x00); descriptor.push(0x07); descriptor.extend_from_slice(&0x0000_0003u32.to_le_bytes()); descriptor.extend_from_slice(&3_580u32.to_le_bytes()); descriptor.extend_from_slice(&3_580u32.to_le_bytes()); descriptor.push(0x00); descriptor.extend_from_slice(&9_600u32.to_le_bytes()); descriptor.extend_from_slice(&9_600u32.to_le_bytes()); descriptor.push(0x00); descriptor.extend_from_slice(&254u32.to_le_bytes()); descriptor.extend_from_slice(&0u32.to_le_bytes()); descriptor.extend_from_slice(&0u32.to_le_bytes()); descriptor.extend_from_slice(&0x0002_04BAu32.to_le_bytes());
277 descriptor.extend_from_slice(&(MAX_MESSAGE_LEN as u32).to_le_bytes()); descriptor.push(0x00); descriptor.push(0x00); descriptor.extend_from_slice(&0u16.to_le_bytes()); descriptor.push(0x00); descriptor.push(0x01); descriptor
284}