devices/usb/backend/fido_backend/
fido_device.rs1use std::fs::File;
6use std::io::Error as IOError;
7use std::io::Write;
8use std::sync::Arc;
9
10use base::debug;
11use base::error;
12use base::warn;
13use base::AsRawDescriptor;
14use base::EventType;
15use base::RawDescriptor;
16use sync::Mutex;
17use zerocopy::FromBytes;
18
19use crate::usb::backend::fido_backend::constants;
20use crate::usb::backend::fido_backend::error::Error;
21use crate::usb::backend::fido_backend::error::Result;
22use crate::usb::backend::fido_backend::fido_guest::FidoGuestKey;
23use crate::usb::backend::fido_backend::fido_transaction::TransactionManager;
24use crate::usb::backend::fido_backend::hid_utils::verify_is_fido_device;
25use crate::usb::backend::fido_backend::poll_thread::PollTimer;
26use crate::utils::EventLoop;
27
28#[derive(FromBytes, Debug)]
29#[repr(C)]
30pub struct InitPacket {
31 cid: [u8; constants::CID_SIZE],
32 cmd: u8,
33 bcnth: u8,
34 bcntl: u8,
35 data: [u8; constants::PACKET_INIT_DATA_SIZE],
36}
37
38impl InitPacket {
39 pub fn extract_cid(bytes: &[u8; constants::U2FHID_PACKET_SIZE]) -> [u8; constants::CID_SIZE] {
40 bytes[0..constants::CID_SIZE].try_into().unwrap()
42 }
43
44 fn is_valid(bytes: &[u8; constants::U2FHID_PACKET_SIZE]) -> bool {
45 (bytes[4] & constants::PACKET_INIT_VALID_CMD) != 0
46 }
47
48 pub fn from_bytes(bytes: &[u8; constants::U2FHID_PACKET_SIZE]) -> Result<InitPacket> {
49 if !InitPacket::is_valid(bytes) {
50 return Err(Error::InvalidInitPacket);
51 }
52
53 InitPacket::read_from_bytes(bytes).map_err(|_| Error::CannotConvertInitPacketFromBytes)
54 }
55
56 pub fn bcnt(&self) -> u16 {
57 (self.bcnth as u16) << 8 | (self.bcntl as u16)
58 }
59}
60
61pub struct FidoDevice {
63 pub guest_key: Arc<Mutex<FidoGuestKey>>,
65 pub transaction_manager: Arc<Mutex<TransactionManager>>,
67 pub is_active: bool,
70 pub is_device_lost: bool,
73 event_loop: Arc<EventLoop>,
75 pub transfer_timer: PollTimer,
77 pub fd: Arc<Mutex<File>>,
79}
80
81impl AsRawDescriptor for FidoDevice {
82 fn as_raw_descriptor(&self) -> RawDescriptor {
83 self.fd.lock().as_raw_descriptor()
84 }
85}
86
87impl FidoDevice {
88 pub fn new(hidraw: File, event_loop: Arc<EventLoop>) -> Result<FidoDevice> {
89 verify_is_fido_device(&hidraw)?;
90 let timer = PollTimer::new(
91 "USB transfer timer".to_string(),
92 std::time::Duration::from_millis(constants::USB_POLL_RATE_MILLIS),
93 )?;
94 Ok(FidoDevice {
95 guest_key: Arc::new(Mutex::new(FidoGuestKey::new()?)),
96 transaction_manager: Arc::new(Mutex::new(TransactionManager::new()?)),
97 is_active: false,
98 is_device_lost: false,
99 event_loop,
100 transfer_timer: timer,
101 fd: Arc::new(Mutex::new(hidraw)),
102 })
103 }
104
105 pub fn set_active(&mut self, active: bool) {
109 if self.is_active && !active {
110 if let Err(e) = self.event_loop.pause_event_for_descriptor(self) {
111 error!("Could not deactivate polling of host device: {}", e);
112 }
113 } else if !self.is_active && active {
114 if let Err(e) = self
115 .event_loop
116 .resume_event_for_descriptor(self, EventType::Read)
117 {
118 error!(
119 "Could not resume polling of host device, transactions will be lost: {}",
120 e
121 );
122 }
123 }
124
125 self.is_active = active;
126 }
127
128 pub fn start_transaction(&mut self, packet: &InitPacket) -> Result<()> {
130 let nonce = if packet.cid == constants::BROADCAST_CID {
131 packet.data[..constants::NONCE_SIZE]
132 .try_into()
133 .map_err(|_| Error::InvalidNonceSize)?
134 } else {
135 constants::EMPTY_NONCE
136 };
137
138 if self
140 .transaction_manager
141 .lock()
142 .start_transaction(packet.cid, nonce)
143 {
144 self.transaction_manager.lock().transaction_timer.arm()?;
146 }
147
148 self.set_active(true);
150 Ok(())
151 }
152
153 pub fn recv_from_host(&mut self, packet: &[u8; constants::U2FHID_PACKET_SIZE]) -> Result<()> {
156 let cid = InitPacket::extract_cid(packet);
157 let transaction_opt = if cid == constants::BROADCAST_CID {
158 match InitPacket::from_bytes(packet) {
159 Ok(packet) => {
160 if packet.cmd == constants::U2FHID_ERROR_CMD {
163 self.transaction_manager.lock().get_transaction(cid)
164 } else {
166 let nonce = packet.data[..constants::NONCE_SIZE]
167 .try_into()
168 .map_err(|_| Error::InvalidNonceSize)?;
169 self.transaction_manager
170 .lock()
171 .get_transaction_from_nonce(nonce)
172 }
173 }
174 _ => {
175 return Ok(());
177 }
178 }
179 } else {
180 self.transaction_manager.lock().get_transaction(cid)
181 };
182
183 let transaction = match transaction_opt {
184 Some(t) => t,
185 None => {
186 debug!("Ignoring non-started transaction");
187 return Ok(());
188 }
189 };
190
191 match InitPacket::from_bytes(packet) {
192 Ok(packet) => {
193 if packet.cid == constants::BROADCAST_CID {
194 let nonce = &packet.data[..constants::NONCE_SIZE];
195 if transaction.nonce != nonce {
196 if packet.cmd != constants::U2FHID_ERROR_CMD {
199 warn!(
200 "u2f: received a broadcast transaction with mismatched nonce.\
201 Ignoring transaction."
202 );
203 return Ok(());
204 }
205 }
206 }
207 self.transaction_manager.lock().update_transaction(
208 cid,
209 packet.bcnt(),
210 constants::PACKET_INIT_DATA_SIZE as u16,
211 );
212 }
213 Err(Error::InvalidInitPacket) => {
215 self.transaction_manager.lock().update_transaction(
216 cid,
217 transaction.resp_bcnt,
218 transaction.resp_size + constants::PACKET_CONT_DATA_SIZE as u16,
219 );
220 }
221 Err(e) => {
222 error!(
223 "u2f: received an invalid transaction state: {:?}. Ignoring transaction.",
224 e
225 );
226 return Ok(());
227 }
228 }
229
230 let transaction = match self.transaction_manager.lock().get_transaction(cid) {
233 Some(t) => t,
234 None => {
235 error!(
236 "We lost a transaction on the way. This is a bug. (cid: {:?})",
237 cid
238 );
239 return Ok(());
240 }
241 };
242 if transaction.resp_size >= transaction.resp_bcnt {
244 if self
245 .transaction_manager
246 .lock()
247 .close_transaction(transaction.cid)
248 {
249 self.set_active(false);
252 }
253 }
254
255 let mut guest_key = self.guest_key.lock();
256 if guest_key.pending_in_packets.is_empty() {
257 if let Err(e) = guest_key.timer.arm() {
259 error!(
260 "Unable to start U2F guest key timer. U2F packets may be lost. {}",
261 e
262 );
263 }
264 }
265 guest_key.pending_in_packets.push_back(*packet);
266
267 Ok(())
268 }
269
270 pub fn recv_from_guest(
272 &mut self,
273 packet: &[u8; constants::U2FHID_PACKET_SIZE],
274 ) -> Result<usize> {
275 let mut host_packet = vec![0; constants::U2FHID_PACKET_SIZE + 1];
280
281 match InitPacket::from_bytes(packet) {
282 Ok(init_packet) => {
283 self.start_transaction(&init_packet)?;
284 }
285 Err(Error::InvalidInitPacket) => {
286 }
288 Err(e) => {
289 warn!("Received malformed or invalid u2f-hid init packet, request will be dropped");
290 return Err(e);
291 }
292 }
293
294 host_packet[1..].copy_from_slice(packet.as_slice());
295
296 let written = self
297 .fd
298 .lock()
299 .write(&host_packet)
300 .map_err(Error::WriteHidrawDevice)?;
301
302 if written != host_packet.len() {
303 return Err(Error::WriteHidrawDevice(IOError::other(
304 "Wrote too few bytes to hidraw device.",
305 )));
306 }
307
308 Ok(host_packet.len() - 1)
310 }
311}