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 use std::fs::{File, OpenOptions};
6 use std::io;
7 use std::io::{Read, Write};
8 use std::os::windows::io::AsRawHandle;
9 
10 use super::winapi::DeviceCapabilities;
11 use crate::consts::{CID_BROADCAST, FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID, MAX_HID_RPT_SIZE};
12 use crate::u2ftypes::{U2FDevice, U2FDeviceInfo};
13 
14 #[derive(Debug)]
15 pub struct Device {
16     path: String,
17     file: File,
18     cid: [u8; 4],
19     dev_info: Option<U2FDeviceInfo>,
20 }
21 
22 impl Device {
new(path: String) -> io::Result<Self>23     pub fn new(path: String) -> io::Result<Self> {
24         let file = OpenOptions::new().read(true).write(true).open(&path)?;
25         Ok(Self {
26             path,
27             file,
28             cid: CID_BROADCAST,
29             dev_info: None,
30         })
31     }
32 
is_u2f(&self) -> bool33     pub fn is_u2f(&self) -> bool {
34         match DeviceCapabilities::new(self.file.as_raw_handle()) {
35             Ok(caps) => caps.usage() == FIDO_USAGE_U2FHID && caps.usage_page() == FIDO_USAGE_PAGE,
36             _ => false,
37         }
38     }
39 }
40 
41 impl PartialEq for Device {
eq(&self, other: &Device) -> bool42     fn eq(&self, other: &Device) -> bool {
43         self.path == other.path
44     }
45 }
46 
47 impl Read for Device {
read(&mut self, bytes: &mut [u8]) -> io::Result<usize>48     fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
49         // Windows always includes the report ID.
50         let mut input = [0u8; MAX_HID_RPT_SIZE + 1];
51         let _ = self.file.read(&mut input)?;
52         bytes.clone_from_slice(&input[1..]);
53         Ok(bytes.len() as usize)
54     }
55 }
56 
57 impl Write for Device {
write(&mut self, bytes: &[u8]) -> io::Result<usize>58     fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
59         self.file.write(bytes)
60     }
61 
flush(&mut self) -> io::Result<()>62     fn flush(&mut self) -> io::Result<()> {
63         self.file.flush()
64     }
65 }
66 
67 impl U2FDevice for Device {
get_cid<'a>(&'a self) -> &'a [u8; 4]68     fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
69         &self.cid
70     }
71 
set_cid(&mut self, cid: [u8; 4])72     fn set_cid(&mut self, cid: [u8; 4]) {
73         self.cid = cid;
74     }
75 
in_rpt_size(&self) -> usize76     fn in_rpt_size(&self) -> usize {
77         MAX_HID_RPT_SIZE
78     }
79 
out_rpt_size(&self) -> usize80     fn out_rpt_size(&self) -> usize {
81         MAX_HID_RPT_SIZE
82     }
83 
get_property(&self, _prop_name: &str) -> io::Result<String>84     fn get_property(&self, _prop_name: &str) -> io::Result<String> {
85         Err(io::Error::new(io::ErrorKind::Other, "Not implemented"))
86     }
87 
get_device_info(&self) -> U2FDeviceInfo88     fn get_device_info(&self) -> U2FDeviceInfo {
89         // unwrap is okay, as dev_info must have already been set, else
90         // a programmer error
91         self.dev_info.clone().unwrap()
92     }
93 
set_device_info(&mut self, dev_info: U2FDeviceInfo)94     fn set_device_info(&mut self, dev_info: U2FDeviceInfo) {
95         self.dev_info = Some(dev_info);
96     }
97 }
98