challenge_response 0.5.46

Perform HMAC-SHA1 and OTP challenges with YubiKey, OnlyKey and NitroKey, in pure Rust.
Documentation
use crate::error::ChallengeResponseError;
use crate::usb::{Backend, Device, HID_GET_REPORT, HID_SET_REPORT, PRODUCT_ID, REPORT_TYPE_FEATURE, VENDOR_ID};

use rusb::{request_type, Context, DeviceHandle, Direction, Recipient, RequestType, UsbContext};
use std::time::Duration;

pub struct RUSBBackend {
    context: Context,
}

impl Backend<DeviceHandle<Context>, u8> for RUSBBackend {
    fn new() -> Result<Self, ChallengeResponseError> {
        let context = match Context::new() {
            Ok(c) => c,
            Err(e) => return Err(ChallengeResponseError::UsbError(e)),
        };
        Ok(Self { context })
    }

    fn open_device(
        &mut self,
        bus_id: u8,
        address_id: u8,
    ) -> Result<(DeviceHandle<Context>, Vec<u8>), ChallengeResponseError> {
        let devices = match self.context.devices() {
            Ok(device) => device,
            Err(_) => {
                return Err(ChallengeResponseError::DeviceNotFound);
            }
        };

        for device in devices.iter() {
            match device.device_descriptor() {
                Ok(_) => {}
                Err(_) => {
                    return Err(ChallengeResponseError::DeviceNotFound);
                }
            };

            if device.bus_number() == bus_id && device.address() == address_id {
                match device.open() {
                    Ok(handle) => {
                        let config = match device.config_descriptor(0) {
                            Ok(c) => c,
                            Err(_) => continue,
                        };

                        let mut _interfaces = Vec::new();
                        for interface in config.interfaces() {
                            for usb_int in interface.descriptors() {
                                match handle.kernel_driver_active(usb_int.interface_number()) {
                                    Ok(true) => {
                                        #[cfg(not(any(target_os = "macos", target_os = "windows")))]
                                        handle.detach_kernel_driver(usb_int.interface_number())?;
                                    }
                                    _ => continue,
                                };

                                if handle.active_configuration()? != config.number() {
                                    handle.set_active_configuration(config.number())?;
                                }
                                #[cfg(not(any(target_os = "macos", target_os = "windows")))]
                                handle.claim_interface(usb_int.interface_number())?;
                                #[cfg(not(any(target_os = "macos", target_os = "windows")))]
                                _interfaces.push(usb_int.interface_number());
                            }
                        }

                        return Ok((handle, _interfaces));
                    }
                    Err(_) => {
                        return Err(ChallengeResponseError::OpenDeviceError);
                    }
                }
            }
        }

        Err(ChallengeResponseError::DeviceNotFound)
    }

    #[cfg(any(target_os = "macos", target_os = "windows"))]
    fn close_device(
        &self,
        _handle: DeviceHandle<Context>,
        _interfaces: Vec<u8>,
    ) -> Result<(), ChallengeResponseError> {
        Ok(())
    }

    #[cfg(not(any(target_os = "macos", target_os = "windows")))]
    fn close_device(
        &self,
        handle: DeviceHandle<Context>,
        interfaces: Vec<u8>,
    ) -> Result<(), ChallengeResponseError> {
        for interface in interfaces {
            handle.release_interface(interface)?;
            handle.attach_kernel_driver(interface)?;
        }
        Ok(())
    }

    fn read(
        &self,
        handle: &mut DeviceHandle<Context>,
        buf: &mut [u8],
    ) -> Result<usize, ChallengeResponseError> {
        assert_eq!(buf.len(), 8);
        let reqtype = request_type(Direction::In, RequestType::Class, Recipient::Interface);
        let value = REPORT_TYPE_FEATURE << 8;
        Ok(handle.read_control(reqtype, HID_GET_REPORT, value, 0, buf, Duration::new(2, 0))?)
    }

    fn raw_write(
        &self,
        handle: &mut DeviceHandle<Context>,
        packet: &[u8],
    ) -> Result<(), ChallengeResponseError> {
        let reqtype = request_type(Direction::Out, RequestType::Class, Recipient::Interface);
        let value = REPORT_TYPE_FEATURE << 8;
        if handle.write_control(reqtype, HID_SET_REPORT, value, 0, &packet, Duration::new(2, 0))? != 8 {
            Err(ChallengeResponseError::CanNotWriteToDevice)
        } else {
            Ok(())
        }
    }

    fn find_device(&mut self) -> Result<Device, ChallengeResponseError> {
        let devices = match self.context.devices() {
            Ok(d) => d,
            Err(e) => return Err(ChallengeResponseError::UsbError(e)),
        };
        for device in devices.iter() {
            let descr = device
                .device_descriptor()
                .map_err(|e| ChallengeResponseError::UsbError(e))?;
            if !VENDOR_ID.contains(&descr.vendor_id()) || !PRODUCT_ID.contains(&descr.product_id()) {
                continue;
            }

            let name = device.open()?.read_product_string_ascii(&descr).ok();
            let serial = self
                .read_serial_from_device(device.bus_number(), device.address())
                .ok();
            let device = Device {
                name,
                serial,
                product_id: descr.product_id(),
                vendor_id: descr.vendor_id(),
                bus_id: device.bus_number(),
                address_id: device.address(),
            };

            return Ok(device);
        }

        Err(ChallengeResponseError::DeviceNotFound)
    }

    fn find_device_from_serial(&mut self, serial: u32) -> Result<Device, ChallengeResponseError> {
        let devices = match self.context.devices() {
            Ok(d) => d,
            Err(e) => return Err(ChallengeResponseError::UsbError(e)),
        };
        for device in devices.iter() {
            let descr = device
                .device_descriptor()
                .map_err(|e| ChallengeResponseError::UsbError(e))?;
            if !VENDOR_ID.contains(&descr.vendor_id()) || !PRODUCT_ID.contains(&descr.product_id()) {
                continue;
            }

            let name = device.open()?.read_product_string_ascii(&descr).ok();
            let fetched_serial = match self
                .read_serial_from_device(device.bus_number(), device.address())
                .ok()
            {
                Some(s) => s,
                None => 0,
            };
            if serial == fetched_serial {
                let device = Device {
                    name,
                    serial: Some(serial),
                    product_id: descr.product_id(),
                    vendor_id: descr.vendor_id(),
                    bus_id: device.bus_number(),
                    address_id: device.address(),
                };

                return Ok(device);
            }
        }

        Err(ChallengeResponseError::DeviceNotFound)
    }

    fn find_all_devices(&mut self) -> Result<Vec<Device>, ChallengeResponseError> {
        let mut result: Vec<Device> = Vec::new();
        let devices = match self.context.devices() {
            Ok(d) => d,
            Err(e) => return Err(ChallengeResponseError::UsbError(e)),
        };
        for device in devices.iter() {
            let descr = device
                .device_descriptor()
                .map_err(|e| ChallengeResponseError::UsbError(e))?;
            if !VENDOR_ID.contains(&descr.vendor_id()) || !PRODUCT_ID.contains(&descr.product_id()) {
                continue;
            }

            let name = device.open()?.read_product_string_ascii(&descr).ok();
            let serial = self
                .read_serial_from_device(device.bus_number(), device.address())
                .ok();
            let device = Device {
                name,
                serial,
                product_id: descr.product_id(),
                vendor_id: descr.vendor_id(),
                bus_id: device.bus_number(),
                address_id: device.address(),
            };
            result.push(device);
        }

        if !result.is_empty() {
            return Ok(result);
        }

        Err(ChallengeResponseError::DeviceNotFound)
    }
}