Skip to main content

emerald_hwkey/ledger/connect/
mod.rs

1use std::sync::{Arc, Mutex};
2use crate::errors::HWKeyError;
3use crate::ledger::apdu::APDU;
4use crate::ledger::comm::{sendrecv_timeout, LedgerTransport};
5use direct::AppDetails;
6use crate::ledger::app::LedgerApp;
7
8pub mod direct;
9pub mod shared;
10#[cfg(feature = "speculos")]
11mod speculos;
12#[cfg(feature = "speculos")]
13pub mod speculos_api;
14#[cfg(test)]
15pub mod mock;
16
17pub use {
18    direct::LedgerHidKey,
19    shared::LedgerKeyShared,
20};
21
22#[cfg(feature = "speculos")]
23pub use {
24    speculos::LedgerSpeculosKey,
25};
26use crate::ledger::connect::direct::LedgerDetails;
27
28pub trait LedgerKey {
29
30    type Transport: LedgerTransport;
31
32    fn create() -> Result<Self, HWKeyError> where Self: Sized;
33
34    /// Establishes connection to the Ledger device.
35    /// 
36    /// This method MUST be called before using any other Ledger operations.
37    /// All subsequent operations (read, write, get_app_details, etc.) will fail
38    /// with `HWKeyError::Unavailable` if the device is not connected.
39    fn connect(&mut self) -> Result<(), HWKeyError>;
40
41    ///
42    /// Get information about the currently running app on Ledger
43    /// If no app is running it produces the same info from the OS.
44    fn get_app_details(&self) -> Result<AppDetails, HWKeyError> {
45        let apdu = APDU {
46            cla: 0xb0,
47            ins: 0x01,
48            ..APDU::default()
49        };
50        let device = self.open_exclusive()?;
51        let conn = device.lock()
52            .map_err(|_| HWKeyError::Unavailable)?;
53        match sendrecv_timeout(&*conn, &apdu, 100) {
54            Err(e) => match e {
55                HWKeyError::EmptyResponse => Ok(AppDetails::default()),
56                _ => Err(e),
57            }
58            Ok(resp) => AppDetails::try_from(resp)
59        }
60    }
61
62    fn open_exclusive(&self) -> Result<Arc<Mutex<Self::Transport>>, HWKeyError>;
63
64    ///
65    /// Access a particular type of app. Please ensure that the app is actually launched with [get_app_details] before accessing it.
66    fn access<A>(&self) -> Result<A, HWKeyError> where A: LedgerApp, Self::Transport: 'static {
67        let conn = self.open_exclusive()?;
68        Ok(A::new(conn))
69    }
70
71    ///
72    /// Get information about the Ledger itself.
73    /// It's available _only if no app_ is launched.
74    fn get_ledger_version(&self) -> Result<LedgerDetails, HWKeyError> {
75        let apdu = APDU {
76            cla: 0xe0,
77            ins: 0x01,
78            ..APDU::default()
79        };
80        let device = self.open_exclusive()?;
81        let conn = device.lock()
82            .map_err(|_| HWKeyError::Unavailable)?;
83        let resp = sendrecv_timeout(&*conn, &apdu, 100)?;
84        LedgerDetails::try_from(resp)
85    }
86}