1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 extern crate log; 6 7 use crate::consts::{CID_BROADCAST, MAX_HID_RPT_SIZE}; 8 use crate::platform::iokit::*; 9 use crate::u2ftypes::{U2FDevice, U2FDeviceInfo}; 10 use core_foundation::base::*; 11 use core_foundation::string::*; 12 use std::convert::TryInto; 13 use std::io; 14 use std::io::{Read, Write}; 15 use std::sync::mpsc::{Receiver, RecvTimeoutError}; 16 use std::time::Duration; 17 18 const READ_TIMEOUT: u64 = 15; 19 20 pub struct Device { 21 device_ref: IOHIDDeviceRef, 22 cid: [u8; 4], 23 report_rx: Receiver<Vec<u8>>, 24 dev_info: Option<U2FDeviceInfo>, 25 } 26 27 impl Device { new(dev_ids: (IOHIDDeviceRef, Receiver<Vec<u8>>)) -> io::Result<Self>28 pub fn new(dev_ids: (IOHIDDeviceRef, Receiver<Vec<u8>>)) -> io::Result<Self> { 29 let (device_ref, report_rx) = dev_ids; 30 Ok(Self { 31 device_ref, 32 cid: CID_BROADCAST, 33 report_rx, 34 dev_info: None, 35 }) 36 } 37 is_u2f(&self) -> bool38 pub fn is_u2f(&self) -> bool { 39 true 40 } 41 get_property_macos(&self, prop_name: &str) -> io::Result<String>42 unsafe fn get_property_macos(&self, prop_name: &str) -> io::Result<String> { 43 let prop_ref = IOHIDDeviceGetProperty( 44 self.device_ref, 45 CFString::new(prop_name).as_concrete_TypeRef(), 46 ); 47 if prop_ref.is_null() { 48 return Err(io::Error::new( 49 io::ErrorKind::InvalidData, 50 format!( 51 "IOHIDDeviceGetProperty received nullptr for property {}", 52 prop_name 53 ), 54 )); 55 } 56 57 if CFGetTypeID(prop_ref) != CFStringGetTypeID() { 58 return Err(io::Error::new( 59 io::ErrorKind::InvalidInput, 60 format!( 61 "IOHIDDeviceGetProperty returned non-string type for property {}", 62 prop_name 63 ), 64 )); 65 } 66 67 Ok(CFString::from_void(prop_ref).to_string()) 68 } 69 } 70 71 impl PartialEq for Device { eq(&self, other_device: &Device) -> bool72 fn eq(&self, other_device: &Device) -> bool { 73 self.device_ref == other_device.device_ref 74 } 75 } 76 77 impl Read for Device { read(&mut self, mut bytes: &mut [u8]) -> io::Result<usize>78 fn read(&mut self, mut bytes: &mut [u8]) -> io::Result<usize> { 79 let timeout = Duration::from_secs(READ_TIMEOUT); 80 let data = match self.report_rx.recv_timeout(timeout) { 81 Ok(v) => v, 82 Err(e) if e == RecvTimeoutError::Timeout => { 83 return Err(io::Error::new(io::ErrorKind::TimedOut, e)); 84 } 85 Err(e) => { 86 return Err(io::Error::new(io::ErrorKind::UnexpectedEof, e)); 87 } 88 }; 89 bytes.write(&data) 90 } 91 } 92 93 impl Write for Device { write(&mut self, bytes: &[u8]) -> io::Result<usize>94 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { 95 assert_eq!(bytes.len(), self.out_rpt_size() + 1); 96 97 let report_id = i64::from(bytes[0]); 98 // Skip report number when not using numbered reports. 99 let start = if report_id == 0x0 { 1 } else { 0 }; 100 let data = &bytes[start..]; 101 102 let result = unsafe { 103 IOHIDDeviceSetReport( 104 self.device_ref, 105 kIOHIDReportTypeOutput, 106 report_id.try_into().unwrap(), 107 data.as_ptr(), 108 data.len() as CFIndex, 109 ) 110 }; 111 if result != 0 { 112 warn!("set_report sending failure = {0:X}", result); 113 return Err(io::Error::from_raw_os_error(result)); 114 } 115 trace!("set_report sending success = {0:X}", result); 116 117 Ok(bytes.len()) 118 } 119 120 // USB HID writes don't buffer, so this will be a nop. flush(&mut self) -> io::Result<()>121 fn flush(&mut self) -> io::Result<()> { 122 Ok(()) 123 } 124 } 125 126 impl U2FDevice for Device { get_cid(&self) -> &[u8; 4]127 fn get_cid(&self) -> &[u8; 4] { 128 &self.cid 129 } 130 set_cid(&mut self, cid: [u8; 4])131 fn set_cid(&mut self, cid: [u8; 4]) { 132 self.cid = cid; 133 } 134 in_rpt_size(&self) -> usize135 fn in_rpt_size(&self) -> usize { 136 MAX_HID_RPT_SIZE 137 } 138 out_rpt_size(&self) -> usize139 fn out_rpt_size(&self) -> usize { 140 MAX_HID_RPT_SIZE 141 } 142 get_property(&self, prop_name: &str) -> io::Result<String>143 fn get_property(&self, prop_name: &str) -> io::Result<String> { 144 unsafe { self.get_property_macos(prop_name) } 145 } 146 get_device_info(&self) -> U2FDeviceInfo147 fn get_device_info(&self) -> U2FDeviceInfo { 148 // unwrap is okay, as dev_info must have already been set, else 149 // a programmer error 150 self.dev_info.clone().unwrap() 151 } 152 set_device_info(&mut self, dev_info: U2FDeviceInfo)153 fn set_device_info(&mut self, dev_info: U2FDeviceInfo) { 154 self.dev_info = Some(dev_info); 155 } 156 } 157