Skip to main content

yubikey_hmac_otp/
lib.rs

1extern crate rusb;
2
3#[macro_use]
4extern crate structure;
5
6extern crate aes;
7extern crate block_modes;
8extern crate hmac;
9extern crate rand;
10extern crate sha1;
11#[macro_use]
12extern crate bitflags;
13
14pub mod config;
15pub mod configure;
16pub mod hmacmode;
17mod manager;
18pub mod otpmode;
19pub mod sec;
20pub mod yubicoerror;
21
22use aes::cipher::generic_array::GenericArray;
23
24use config::Command;
25use config::{Config, Slot};
26use configure::DeviceModeConfig;
27use hmacmode::Hmac;
28use manager::{Flags, Frame};
29use otpmode::Aes128Block;
30use rusb::{Context, UsbContext};
31use sec::{crc16, CRC_RESIDUAL_OK};
32use yubicoerror::YubicoError;
33
34const VENDOR_ID: u16 = 0x1050;
35
36/// The `Result` type used in this crate.
37type Result<T> = ::std::result::Result<T, YubicoError>;
38
39#[derive(Clone, Debug, PartialEq)]
40pub struct Yubikey {
41    pub name: Option<String>,
42    pub serial: Option<u32>,
43    pub product_id: u16,
44    pub vendor_id: u16,
45    pub bus_id: u8,
46    pub address_id: u8,
47}
48
49pub struct Yubico {
50    context: Context,
51}
52
53impl Yubico {
54    /// Creates a new Yubico instance.
55    pub fn new() -> Self {
56        Yubico {
57            context: Context::new().unwrap(),
58        }
59    }
60
61    pub fn read_serial_from_device(&mut self, device: rusb::Device<Context>) -> Result<u32> {
62        // let mut context = Context::new()?;
63        let mut handle =
64            manager::open_device(&mut self.context, device.bus_number(), device.address())?;
65        let challenge = [0; 64];
66        let command = Command::DeviceSerial;
67
68        let d = Frame::new(challenge, command); // FixMe: do not need a challange
69        let mut buf = [0; 8];
70        manager::wait(
71            &mut handle.0,
72            |f| !f.contains(Flags::SLOT_WRITE_FLAG),
73            &mut buf,
74        )?;
75
76        manager::write_frame(&mut handle.0, &d)?;
77
78        // Read the response.
79        let mut response = [0; 36];
80        manager::read_response(&mut handle.0, &mut response)?;
81
82        drop(handle); // Close Device
83
84        // Check response.
85        if crc16(&response[..6]) != crate::sec::CRC_RESIDUAL_OK {
86            return Err(YubicoError::WrongCRC);
87        }
88
89        let serial = structure!("2I").unpack(response[..8].to_vec())?;
90
91        Ok(serial.0)
92    }
93
94    pub fn find_yubikey(&mut self) -> Result<Yubikey> {
95        for device in self.context.devices().unwrap().iter() {
96            let descr = device.device_descriptor().unwrap();
97            if descr.vendor_id() == VENDOR_ID {
98                let name = device.open()?.read_product_string_ascii(&descr).ok();
99                let serial = self.read_serial_from_device(device.clone()).ok();
100                let yubikey = Yubikey {
101                    name: name,
102                    serial: serial,
103                    product_id: descr.product_id(),
104                    vendor_id: descr.vendor_id(),
105                    bus_id: device.bus_number(),
106                    address_id: device.address(),
107                };
108
109                return Ok(yubikey);
110            }
111        }
112
113        Err(YubicoError::DeviceNotFound)
114    }
115
116    pub fn find_yubikey_from_serial(&mut self, serial: u32) -> Result<Yubikey> {
117        for device in self.context.devices().unwrap().iter() {
118            let descr = device.device_descriptor().unwrap();
119            if descr.vendor_id() == VENDOR_ID {
120                let name = device.open()?.read_product_string_ascii(&descr).ok();
121                let fetched_serial = match self.read_serial_from_device(device.clone()).ok() {
122                    Some(s) => s,
123                    None => 0,
124                };
125                if serial == fetched_serial {
126                    let yubikey = Yubikey {
127                        name: name,
128                        serial: Some(serial),
129                        product_id: descr.product_id(),
130                        vendor_id: descr.vendor_id(),
131                        bus_id: device.bus_number(),
132                        address_id: device.address(),
133                    };
134
135                    return Ok(yubikey);
136                }
137            }
138        }
139
140        Err(YubicoError::DeviceNotFound)
141    }
142
143    pub fn find_all_yubikeys(&mut self) -> Result<Vec<Yubikey>> {
144        let mut result: Vec<Yubikey> = Vec::new();
145        for device in self.context.devices().unwrap().iter() {
146            let descr = device.device_descriptor().unwrap();
147            if descr.vendor_id() == VENDOR_ID {
148                let name = device.open()?.read_product_string_ascii(&descr).ok();
149                let serial = self.read_serial_from_device(device.clone()).ok();
150                let yubikey = Yubikey {
151                    name: name,
152                    serial: serial,
153                    product_id: descr.product_id(),
154                    vendor_id: descr.vendor_id(),
155                    bus_id: device.bus_number(),
156                    address_id: device.address(),
157                };
158                result.push(yubikey);
159            }
160        }
161
162        if !result.is_empty() {
163            return Ok(result);
164        }
165
166        Err(YubicoError::DeviceNotFound)
167    }
168
169    pub fn write_config(
170        &mut self,
171        conf: Config,
172        device_config: &mut DeviceModeConfig,
173    ) -> Result<()> {
174        let d = device_config.to_frame(conf.command);
175        let mut buf = [0; 8];
176
177        match manager::open_device(
178            &mut self.context,
179            conf.yubikey.bus_id,
180            conf.yubikey.address_id,
181        ) {
182            Ok((mut handle, interfaces)) => {
183                manager::wait(
184                    &mut handle,
185                    |f| !f.contains(Flags::SLOT_WRITE_FLAG),
186                    &mut buf,
187                )?;
188
189                // TODO: Should check version number.
190
191                manager::write_frame(&mut handle, &d)?;
192                manager::wait(
193                    &mut handle,
194                    |f| !f.contains(Flags::SLOT_WRITE_FLAG),
195                    &mut buf,
196                )?;
197                manager::close_device(handle, interfaces)?;
198
199                Ok(())
200            }
201            Err(error) => Err(error),
202        }
203    }
204
205    pub fn read_serial_number(&mut self, conf: Config) -> Result<u32> {
206        match manager::open_device(
207            &mut self.context,
208            conf.yubikey.bus_id,
209            conf.yubikey.address_id,
210        ) {
211            Ok((mut handle, interfaces)) => {
212                let challenge = [0; 64];
213                let command = Command::DeviceSerial;
214
215                let d = Frame::new(challenge, command); // FixMe: do not need a challange
216                let mut buf = [0; 8];
217                manager::wait(
218                    &mut handle,
219                    |f| !f.contains(manager::Flags::SLOT_WRITE_FLAG),
220                    &mut buf,
221                )?;
222
223                manager::write_frame(&mut handle, &d)?;
224
225                // Read the response.
226                let mut response = [0; 36];
227                manager::read_response(&mut handle, &mut response)?;
228                manager::close_device(handle, interfaces)?;
229
230                // Check response.
231                if crc16(&response[..6]) != CRC_RESIDUAL_OK {
232                    return Err(YubicoError::WrongCRC);
233                }
234
235                let serial = structure!("2I").unpack(response[..8].to_vec())?;
236
237                Ok(serial.0)
238            }
239            Err(error) => Err(error),
240        }
241    }
242
243    pub fn challenge_response_hmac(&mut self, chall: &[u8], conf: Config) -> Result<Hmac> {
244        let mut hmac = Hmac([0; 20]);
245
246        match manager::open_device(
247            &mut self.context,
248            conf.yubikey.bus_id,
249            conf.yubikey.address_id,
250        ) {
251            Ok((mut handle, interfaces)) => {
252                let mut challenge = [0; 64];
253
254                if conf.variable && chall.last() == Some(&0) {
255                    challenge = [0xff; 64];
256                }
257
258                let mut command = Command::ChallengeHmac1;
259                if let Slot::Slot2 = conf.slot {
260                    command = Command::ChallengeHmac2;
261                }
262
263                (&mut challenge[..chall.len()]).copy_from_slice(chall);
264                let d = Frame::new(challenge, command);
265                let mut buf = [0; 8];
266                manager::wait(
267                    &mut handle,
268                    |f| !f.contains(manager::Flags::SLOT_WRITE_FLAG),
269                    &mut buf,
270                )?;
271
272                manager::write_frame(&mut handle, &d)?;
273
274                // Read the response.
275                let mut response = [0; 36];
276                manager::read_response(&mut handle, &mut response)?;
277                manager::close_device(handle, interfaces)?;
278
279                // Check response.
280                if crc16(&response[..22]) != CRC_RESIDUAL_OK {
281                    return Err(YubicoError::WrongCRC);
282                }
283
284                hmac.0.clone_from_slice(&response[..20]);
285
286                Ok(hmac)
287            }
288            Err(error) => Err(error),
289        }
290    }
291
292    pub fn challenge_response_otp(&mut self, chall: &[u8], conf: Config) -> Result<Aes128Block> {
293        let mut block = Aes128Block {
294            block: GenericArray::clone_from_slice(&[0; 16]),
295        };
296
297        match manager::open_device(
298            &mut self.context,
299            conf.yubikey.bus_id,
300            conf.yubikey.address_id,
301        ) {
302            Ok((mut handle, interfaces)) => {
303                let mut challenge = [0; 64];
304                //(&mut challenge[..6]).copy_from_slice(chall);
305
306                let mut command = Command::ChallengeOtp1;
307                if let Slot::Slot2 = conf.slot {
308                    command = Command::ChallengeOtp2;
309                }
310
311                (&mut challenge[..chall.len()]).copy_from_slice(chall);
312                let d = Frame::new(challenge, command);
313                let mut buf = [0; 8];
314                eprintln!("Step 1 done");
315                let mut response = [0; 36];
316                manager::wait(
317                    &mut handle,
318                    |f| !f.contains(manager::Flags::SLOT_WRITE_FLAG),
319                    &mut buf,
320                )?;
321                eprintln!("Step 2 done");
322                manager::write_frame(&mut handle, &d)?;
323                eprintln!("Step 3 done");
324                manager::read_response(&mut handle, &mut response)?;
325                eprintln!("Step 4 done");
326                manager::close_device(handle, interfaces)?;
327                eprintln!("Step 5 done");
328
329                // Check response.
330                if crc16(&response[..18]) != CRC_RESIDUAL_OK {
331                    return Err(YubicoError::WrongCRC);
332                }
333
334                block.block.copy_from_slice(&response[..16]);
335
336                Ok(block)
337            }
338            Err(error) => Err(error),
339        }
340    }
341}