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
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use std::ffi::CStr;
use std::io::Cursor;
use std::sync::Mutex;

use async_trait::async_trait;
use byteorder::{BigEndian, ReadBytesExt};
use hidapi::HidApi;

use ledger_hw::device::{LEDGER_CHANNEL, LEDGER_PACKET_SIZE, LEDGER_USAGE_PAGE, LEDGER_VENDOR_ID};
use ledger_hw_transport::Transport;

#[macro_use]
extern crate nix;
pub mod linux;

const LEDGER_TIMEOUT: i32 = 10_000_000;

#[allow(dead_code)]
pub struct HidTransport {
    device: Mutex<hidapi::HidDevice>,
}

impl HidTransport {
    pub fn new() -> Result<HidTransport, HidTransportError> {
        let api = HidApi::new().map_err(|e| HidTransportError::HidError(e))?;
        let path = get_device_path(&api)?;
        let device = api
            .open_path(&path)
            .map_err(|e| HidTransportError::HidError(e))?;
        Ok(HidTransport {
            device: Mutex::new(device),
        })
    }
}

#[async_trait]
impl Transport for HidTransport {
    type Error = HidTransportError;
    async fn exchange(&self, command: &[u8]) -> Result<Vec<u8>, Self::Error> {
        let mut payload = Vec::with_capacity(command.len() as usize + 2);
        let length = (command.len() as u16).to_be_bytes();
        payload.extend_from_slice(&length);
        payload.extend_from_slice(&command);

        let mut buffer = vec![0u8; LEDGER_PACKET_SIZE];
        let channel_bytes = LEDGER_CHANNEL.to_be_bytes();
        buffer[..2].copy_from_slice(&channel_bytes);
        buffer[2] = 0x05u8;

        let guard = self.device.lock().unwrap();

        for (i, chunk) in payload.chunks(LEDGER_PACKET_SIZE - 5).enumerate() {
            let idx_bytes = (i as u16).to_be_bytes();
            buffer[3..5].copy_from_slice(&idx_bytes);
            buffer[5..5 + chunk.len()].copy_from_slice(chunk);

            let size = guard
                .write(&buffer)
                .map_err(|e| HidTransportError::HidError(e))?;
            if size < buffer.len() {
                return Err(HidTransportError::RequestError);
            }
        }

        let mut answer: Vec<u8> = Vec::with_capacity(256);
        let mut buffer = vec![0u8; LEDGER_PACKET_SIZE];

        let first = guard
            .read_timeout(&mut buffer, LEDGER_TIMEOUT)
            .map_err(|e| HidTransportError::HidError(e))?;
        if first < 7 {
            return Err(HidTransportError::RequestError);
        }
        let mut reader = Cursor::new(&buffer);
        let (_channel, _tag, _idx) = read_channel_tag_idx(&mut reader)?;
        let length = reader
            .read_u16::<BigEndian>()
            .map_err(|_| HidTransportError::Deserialization)? as usize;
        let chunk = &buffer[reader.position() as usize..buffer.len()];
        answer.extend_from_slice(chunk);

        while answer.len() < length {
            let res = guard
                .read_timeout(&mut buffer, LEDGER_TIMEOUT)
                .map_err(|e| HidTransportError::HidError(e))?;
            if res < 5 {
                return Err(HidTransportError::RequestError);
            }

            let mut rdr = Cursor::new(&buffer);
            let (_channel, _tag, _idx) = read_channel_tag_idx(&mut rdr)?;

            let available: usize = buffer.len() - rdr.position() as usize;
            let missing: usize = length - answer.len();
            let end_p = rdr.position() as usize + std::cmp::min(available, missing);

            let new_chunk = &buffer[rdr.position() as usize..end_p];
            answer.extend_from_slice(new_chunk);
        }
        answer.truncate(length);
        Ok(answer)
    }
}

fn read_channel_tag_idx(
    reader: &mut Cursor<&Vec<u8>>,
) -> Result<(u16, u8, u16), HidTransportError> {
    let channel = reader
        .read_u16::<BigEndian>()
        .map_err(|_| HidTransportError::Deserialization)?;
    let tag = reader
        .read_u8()
        .map_err(|_| HidTransportError::Deserialization)?;
    let idx = reader
        .read_u16::<BigEndian>()
        .map_err(|_| HidTransportError::Deserialization)?;
    Ok((channel, tag, idx))
}

#[cfg(target_os = "linux")]
pub fn get_device_path(api: &HidApi) -> Result<&CStr, HidTransportError> {
    for device in api.device_list() {
        if device.vendor_id() == LEDGER_VENDOR_ID {
            let usage = if device.usage_page() != 0 {
                device.usage_page()
            } else {
                linux::get_usage_page(device.path())
                    .map_err(|e| HidTransportError::DeviceUsagePageUnreachable(e))?
            };
            if usage == LEDGER_USAGE_PAGE {
                return Ok(device.path());
            }
        }
    }
    return Err(HidTransportError::DeviceNotFound);
}

#[cfg(not(target_os = "linux"))]
pub fn get_device_path(api: &HidApi) -> Result<&CStr, HidTransportError> {
    for device in api.device_list() {
        if device.vendor_id() == LEDGER_VENDOR_ID && device.usage_page() == LEDGER_USAGE_PAGE {
            return Ok(device.path());
        }
    }
    return Err(HidTransportError::DeviceNotFound);
}

#[derive(Debug)]
pub enum HidTransportError {
    Deserialization,
    RequestError,
    HidError(hidapi::HidError),
    DeviceUsagePageUnreachable(linux::Error),
    DeviceNotFound,
}