1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::fs::File;
use std::os::raw::c_int;

use base::handle_eintr_errno;
use base::ioctl_ior_nr;

use crate::usb::backend::fido_backend::constants;
use crate::usb::backend::fido_backend::error::Error;
use crate::usb::backend::fido_backend::error::Result;

#[repr(C)]
#[derive(Clone)]
pub struct HidrawReportDescriptor {
    pub size: u32,
    pub value: [u8; constants::HID_MAX_DESCRIPTOR_SIZE],
}

pub const HID_IO_TYPE: u32 = 'H' as u32;

ioctl_ior_nr!(HIDIOCGRDESCSIZE, HID_IO_TYPE, 0x01, c_int);
ioctl_ior_nr!(HIDIOCGRDESC, HID_IO_TYPE, 0x02, HidrawReportDescriptor);

/// Verifies that the given `hidraw` file handle is a valid FIDO device.
/// In case it is not, it returns an `InvalidHidrawDevice` erro.
pub fn verify_is_fido_device(hidraw: &File) -> Result<()> {
    let mut desc_size: c_int = 0;
    // SAFETY:
    // Safe because:
    // - We check the return value after the call.
    // - ioctl(HIDIOCGRDDESCSIZE) does not hold the descriptor after the call.
    unsafe {
        let ret = handle_eintr_errno!(base::ioctl_with_mut_ref(
            hidraw,
            HIDIOCGRDESCSIZE,
            &mut desc_size
        ));
        if ret < 0 || (desc_size as usize) < constants::HID_REPORT_DESC_HEADER.len() {
            return Err(Error::InvalidHidrawDevice);
        }
    }

    let mut descriptor = HidrawReportDescriptor {
        size: desc_size as u32,
        value: [0; constants::HID_MAX_DESCRIPTOR_SIZE],
    };

    // SAFETY:
    // Safe because:
    // - We check the return value after the call.
    // - ioctl(HIDIOCGRDESC) does not hold the descriptor after the call.
    unsafe {
        let ret = handle_eintr_errno!(base::ioctl_with_mut_ref(
            hidraw,
            HIDIOCGRDESC,
            &mut descriptor
        ));
        if ret < 0 {
            return Err(Error::InvalidHidrawDevice);
        }
    }

    if descriptor.value[..constants::HID_REPORT_DESC_HEADER.len()]
        != *constants::HID_REPORT_DESC_HEADER
    {
        return Err(Error::InvalidHidrawDevice);
    }
    Ok(())
}