iota_ledger_nano/
lib.rs

1//! Library
2
3use std::convert::TryInto;
4use std::{thread, time};
5
6pub mod ledger;
7pub use ledger::ledger_apdu::{APDUAnswer, APDUCommand};
8
9use crate::api::constants;
10use crate::api::constants::DataTypeEnum;
11use crate::api::errors::APIError;
12
13pub use crate::ledger::ledger_transport_tcp::Callback;
14pub use crate::transport::{LedgerTransport, Transport, TransportTypes};
15
16pub use crate::api::packable::{Error as PackableError, Packable, Read, Write};
17
18pub mod api;
19pub mod transport;
20
21const MINIMUM_APP_VERSION: u32 = 6002;
22const MINIMUM_APP_VERSION_GENERATE_PUBLIC_KEYS: u32 = 8007; // generate public keys supported starting with 0.8.7
23
24#[derive(Default, Debug, Clone, Copy, Hash, Eq, PartialEq)]
25pub struct LedgerBIP32Index {
26    pub bip32_index: u32,
27    pub bip32_change: u32,
28}
29
30impl Packable for LedgerBIP32Index {
31    fn packed_len(&self) -> usize {
32        0u32.packed_len() + 0u32.packed_len()
33    }
34
35    fn pack<W: Write>(&self, buf: &mut W) -> Result<(), PackableError> {
36        self.bip32_index.pack(buf)?;
37        self.bip32_change.pack(buf)?;
38        Ok(())
39    }
40
41    fn unpack<R: Read>(buf: &mut R) -> Result<Self, PackableError>
42    where
43        Self: Sized,
44    {
45        Ok(Self {
46            bip32_index: u32::unpack(buf)?,
47            bip32_change: u32::unpack(buf)?,
48        })
49    }
50}
51
52pub enum LedgerDeviceTypes {
53    LedgerNanoS,
54    LedgerNanoSPlus,
55    LedgerNanoX,
56}
57
58pub struct LedgerHardwareWallet {
59    version: u32,
60    transport: Transport,
61    transport_type: TransportTypes,
62    device_type: LedgerDeviceTypes,
63    data_buffer_size: usize,
64    is_debug_app: bool,
65}
66
67/// Get Ledger by transport_type
68pub fn get_ledger_by_type(
69    coin_type: u32,
70    bip32_account: u32,
71    transport_type: &TransportTypes,
72    callback: Option<crate::ledger::ledger_transport_tcp::Callback>,
73) -> Result<Box<LedgerHardwareWallet>, APIError> {
74    let ledger = crate::LedgerHardwareWallet::new(transport_type, callback)?;
75
76    // set account
77    ledger.set_account(coin_type, bip32_account)?;
78
79    Ok(Box::new(ledger))
80}
81
82/// Get currently opened app
83/// If "BOLOS" is returned, the dashboard is open
84pub fn get_opened_app(transport_type: &TransportTypes) -> Result<(String, String), APIError> {
85    let transport = crate::transport::create_transport(transport_type, None)?;
86
87    let app = crate::api::app_get_name::exec(&transport)?;
88    Ok((app.app, app.version))
89}
90
91pub fn get_app_config(
92    transport_type: &TransportTypes,
93) -> Result<api::get_app_config::Response, APIError> {
94    let transport = crate::transport::create_transport(transport_type, None)?;
95
96    let app_config = crate::api::get_app_config::exec(&transport)?;
97
98    Ok(app_config)
99}
100
101pub fn get_buffer_size(transport_type: &TransportTypes) -> Result<usize, APIError> {
102    let transport = crate::transport::create_transport(transport_type, None)?;
103
104    let data_buffer_state = crate::api::get_data_buffer_state::exec(&transport)?;
105
106    Ok(data_buffer_state.data_block_size as usize * data_buffer_state.data_block_count as usize)
107}
108
109/// Open app on the nano s/x
110/// Only works if dashboard is open
111pub fn open_app(transport_type: &TransportTypes, app: String) -> Result<(), APIError> {
112    let transport = crate::transport::create_transport(transport_type, None)?;
113    crate::api::app_open::exec(&transport, app)
114}
115
116/// Close current opened app on the nano s/x
117/// Only works if an app is open
118pub fn exit_app(transport_type: &TransportTypes) -> Result<(), APIError> {
119    let transport = crate::transport::create_transport(transport_type, None)?;
120    crate::api::app_exit::exec(&transport)
121}
122
123/// Get Ledger
124/// If is_simulator is true, you will get a TCP transfer for use with Speculos
125/// If it's false, you will get a native USB HID transfer for real devices
126pub fn get_ledger(
127    coin_type: u32,
128    bip32_account: u32,
129    is_simulator: bool,
130) -> Result<Box<LedgerHardwareWallet>, APIError> {
131    let transport_type = match is_simulator {
132        true => TransportTypes::TCP,
133        false => TransportTypes::NativeHID,
134    };
135    get_ledger_by_type(coin_type, bip32_account, &transport_type, None)
136}
137
138impl LedgerHardwareWallet {
139    // creates object but doesn't connect it
140    // initialize with dummy-device
141    fn new(transport_type: &TransportTypes, callback: Option<Callback>) -> Result<Self, APIError> {
142        let transport = crate::transport::create_transport(transport_type, callback)?;
143
144        // reset api
145        crate::api::reset::exec(&transport)?;
146
147        let res = crate::api::get_app_config::exec(&transport)?;
148
149        let version = res.app_version_major as u32 * 1000000
150            + res.app_version_minor as u32 * 1000
151            + res.app_version_patch as u32;
152
153        // signature changed from signing the essence to signing the hash of the essence (0.6.1 to 0.6.2)
154        if version < MINIMUM_APP_VERSION {
155            // temporary for not needing to change wallet.rs
156            return Err(APIError::AppTooOld);
157        }
158
159        let device_type = match res.device {
160            0 => LedgerDeviceTypes::LedgerNanoS,
161            1 => LedgerDeviceTypes::LedgerNanoX,
162            2 => LedgerDeviceTypes::LedgerNanoSPlus,
163            _ => {
164                return Err(APIError::Unknown);
165            }
166        };
167
168        let data_buffer_state = crate::api::get_data_buffer_state::exec(&transport)?;
169
170        Ok(LedgerHardwareWallet {
171            version,
172            transport,
173            transport_type: *transport_type,
174            device_type,
175            data_buffer_size: data_buffer_state.data_block_size as usize
176                * data_buffer_state.data_block_count as usize,
177            is_debug_app: res.is_debug_app == 1,
178        })
179    }
180
181    fn transport(&self) -> &Transport {
182        &self.transport
183    }
184
185    pub fn get_transport_type(&self) -> TransportTypes {
186        self.transport_type
187    }
188
189    pub fn is_simulator(&self) -> bool {
190        match self.transport.transport {
191            LedgerTransport::TCP(_) => true,
192            LedgerTransport::NativeHID(_) => false,
193        }
194    }
195
196    // something like tri-state ... true / false / error
197    pub fn is_debug_app(&self) -> bool {
198        self.is_debug_app
199    }
200
201    pub fn device_type(&self) -> &LedgerDeviceTypes {
202        &self.device_type
203    }
204
205    pub fn get_buffer_size(&self) -> usize {
206        self.data_buffer_size
207    }
208
209    // uses the get_data_buffer_state-Api call to figure out if the ledger is locked
210    pub fn is_locked(&self) -> Result<bool, APIError> {
211        match api::get_data_buffer_state::exec(self.transport()) {
212            Err(APIError::SecurityStatusNotSatisfied) => Ok(true),
213            Ok(_) => Ok(false),
214            Err(e) => Err(e),
215        }
216    }
217
218    // convenience function for first getting the data buffer state and then
219    // downloading as many blocks as needed from the device
220    // it returns a vector with the size reported by the device
221    fn read_data_bufer(&self) -> Result<Vec<u8>, APIError> {
222        // get buffer state
223        let dbs = api::get_data_buffer_state::exec(self.transport())?;
224
225        // is buffer state okay? (read allowed, contains addresses, valid flag set)
226        if dbs.data_type as u8 != constants::DataTypeEnum::GeneratedAddress as u8
227            && dbs.data_type as u8 != constants::DataTypeEnum::GeneratedPublicKeys as u8
228            && dbs.data_type as u8 != constants::DataTypeEnum::Signatures as u8
229        {
230            return Err(APIError::CommandNotAllowed);
231        }
232
233        // buffer to read data from device
234        let mut buffer: Vec<u8> = Vec::new();
235
236        // how many block do we need to read?
237        let mut blocks_needed: u8 = (dbs.data_length / dbs.data_block_size as u16) as u8;
238        if (dbs.data_length % dbs.data_block_size as u16) as u8 != 0 {
239            blocks_needed += 1;
240        }
241
242        // too many blocks?
243        if blocks_needed > dbs.data_block_count {
244            return Err(APIError::CommandInvalidData);
245        }
246
247        // read blocks to buffer
248        for block in 0..blocks_needed {
249            // read data buffer to get address
250            let mut res = api::read_data_block::exec(self.transport(), block)?;
251            buffer.append(&mut res.data);
252        }
253        Ok(buffer[0..dbs.data_length as usize].to_vec())
254    }
255
256    // convenience function - write as many pages as needed to transfer data to the device
257    fn write_data_buffer(&self, data: Vec<u8>) -> Result<(), APIError> {
258        // clear data buffer before data can be uploaded and validated
259        api::clear_data_buffer::exec(self.transport())?;
260
261        // get buffer state
262        let dbs = api::get_data_buffer_state::exec(self.transport())?;
263
264        // is buffer state okay? (write allowed, is empty)
265        if dbs.data_type as u8 != DataTypeEnum::Empty as u8 {
266            return Err(APIError::CommandNotAllowed);
267        }
268
269        // how many blocks to upload?
270        let mut blocks_needed = (data.len() / dbs.data_block_size as usize) as u8;
271        if (data.len() % dbs.data_block_size as usize) as u8 != 0 {
272            blocks_needed += 1;
273        }
274
275        // too many blocks?
276        if blocks_needed > dbs.data_block_count {
277            return Err(APIError::CommandInvalidData);
278        }
279
280        // transfer blocks
281        let mut iter = data.chunks(dbs.data_block_size as usize);
282        for block in 0..blocks_needed {
283            // get next chunk of data
284            let mut block_data = iter.next().unwrap().to_vec();
285
286            // block has to be exactly data_block_size but last chunk can have fewer bytes
287            // fill it up to the correct size
288            // TODO: is there some nicer way?
289            while block_data.len() < dbs.data_block_size as usize {
290                block_data.push(0u8);
291            }
292
293            // now write block
294            api::write_data_block::exec(self.transport(), block, block_data)?;
295        }
296        Ok(())
297    }
298
299    /// resets api (also resets account index)
300    pub fn reset(&self) -> Result<(), APIError> {
301        api::reset::exec(self.transport())?;
302        Ok(())
303    }
304
305    /// Set BIP32 account index
306    ///
307    /// For all crypto operations following BIP32 path is used: `2c'/107a'/account'/index'`. This command sets the
308    /// third component of the BIP32 path. The account index remains valid until the API is reset.
309    ///
310    /// The MSB (=hardened) always must be set.
311    pub fn set_account(&self, coin_type: u32, bip32_account: u32) -> Result<(), APIError> {
312        let app_config = crate::api::get_app_config::exec(self.transport())?;
313
314        api::set_account::exec(coin_type, app_config, self.transport(), bip32_account)?;
315        Ok(())
316    }
317
318    pub fn get_addresses(
319        &self,
320        show: bool,
321        bip32: LedgerBIP32Index,
322        count: usize,
323    ) -> Result<Vec<[u8; constants::ADDRESS_SIZE_BYTES]>, api::errors::APIError> {
324        // clear data buffer before addresses can be generated
325        api::clear_data_buffer::exec(self.transport())?;
326
327        let max_count = self.data_buffer_size / constants::ADDRESS_WITH_TYPE_SIZE_BYTES;
328
329        if count > max_count {
330            return Err(api::errors::APIError::CommandInvalidData);
331        }
332
333        // generate one or more address(es)
334        api::generate_address::exec(self.transport(), show, bip32, count as u32)?;
335
336        // read addresses from device
337        let buffer = self.read_data_bufer()?;
338
339        let mut addresses: Vec<[u8; 32]> = Vec::new();
340        for i in 0_usize..count {
341            // no need to copy address type byte!
342            let addr: [u8; 32] = buffer[i * constants::ADDRESS_WITH_TYPE_SIZE_BYTES + 1
343                ..(i + 1) * constants::ADDRESS_WITH_TYPE_SIZE_BYTES]
344                .try_into()
345                .unwrap(); // each 33 bytes one address
346            addresses.push(addr);
347        }
348
349        Ok(addresses)
350    }
351
352    pub fn get_public_keys(
353        &self,
354        show: bool,
355        bip32: LedgerBIP32Index,
356        count: usize,
357    ) -> Result<Vec<[u8; constants::PUBLIC_KEY_SIZE_BYTES]>, api::errors::APIError> {
358        // generate public key api call exists >= 0.8.7
359        if self.version < MINIMUM_APP_VERSION_GENERATE_PUBLIC_KEYS {
360            return Err(APIError::AppTooOld);
361        }
362
363        // clear data buffer before public keys can be generated
364        api::clear_data_buffer::exec(self.transport())?;
365
366        let max_count = self.data_buffer_size / constants::PUBLIC_KEY_SIZE_BYTES;
367
368        if count > max_count {
369            return Err(api::errors::APIError::CommandInvalidData);
370        }
371
372        // generate one or more public key(s)
373        api::generate_public_key::exec(self.transport(), show, bip32, count as u32)?;
374
375        // read addresses from device
376        let buffer = self.read_data_bufer()?;
377
378        let mut public_keys: Vec<[u8; 32]> = Vec::new();
379        for i in 0_usize..count {
380            let addr = buffer
381                [i * constants::PUBLIC_KEY_SIZE_BYTES..(i + 1) * constants::PUBLIC_KEY_SIZE_BYTES]
382                .try_into()
383                .unwrap(); // each 33 bytes one address
384            public_keys.push(addr);
385        }
386
387        Ok(public_keys)
388    }
389
390    pub fn get_first_address(
391        &self,
392    ) -> Result<[u8; constants::ADDRESS_SIZE_BYTES], api::errors::APIError> {
393        // clear data buffer before addresses can be generated
394        api::clear_data_buffer::exec(self.transport())?;
395
396        // generate one single address
397        api::generate_address::exec(
398            self.transport(),
399            false, // non interactive
400            LedgerBIP32Index {
401                bip32_index: constants::HARDENED,
402                bip32_change: constants::HARDENED,
403            },
404            1, // single address
405        )?;
406
407        // read addresses from device
408        let buffer = self.read_data_bufer()?;
409
410        // no need to copy address type byte!
411        let addr = buffer[1..constants::ADDRESS_WITH_TYPE_SIZE_BYTES]
412            .try_into()
413            .unwrap();
414        Ok(addr)
415    }
416
417    /// Prepare Signing
418    ///
419    /// Uploads the essence, parses and validates it.
420    pub fn prepare_signing(
421        &self,
422        key_indices: Vec<LedgerBIP32Index>,
423        essence: Vec<u8>,
424        has_remainder: bool,
425        remainder_index: u16,
426        remainder: LedgerBIP32Index,
427    ) -> Result<(), api::errors::APIError> {
428        // clone buffer because we have to add the key indices after the essence
429        let mut buffer: Vec<u8> = essence.to_vec();
430        for key in key_indices.iter() {
431            key.pack(&mut buffer).map_err(|_| APIError::Unknown)?;
432        }
433        let buffer_len = buffer.len();
434
435        // we can catch the error here before it happens on the hardware wallet
436        // the wallet would respond with `InvalidData` but an error code indicating
437        // why the data is invalid certainly is helpful.
438        if buffer_len > self.data_buffer_size {
439            return Err(api::errors::APIError::EssenceTooLarge);
440        }
441
442        // write data to the device
443        self.write_data_buffer(buffer)?;
444
445        // now validate essence
446        api::prepare_signing::exec(self.transport(), has_remainder, remainder_index, remainder)?;
447
448        // get buffer state
449        let dbs = api::get_data_buffer_state::exec(self.transport())?;
450
451        // if recognized length is not the buffer_len, something went wrong
452        // during parsing
453        if dbs.data_length != buffer_len as u16 {
454            return Err(APIError::Unknown);
455        }
456
457        Ok(())
458    }
459
460    /// Prepare Blind Signing
461    ///
462    /// Uploads the essence hash and validates it
463    pub fn prepare_blind_signing(
464        &self,
465        key_indices: Vec<LedgerBIP32Index>,
466        essence_hash: Vec<u8>,
467    ) -> Result<(), api::errors::APIError> {
468        // clone buffer because we have to add the key indices after the essence
469        let mut buffer: Vec<u8> = essence_hash.to_vec();
470        let key_number: u16 = key_indices.len() as u16;
471        key_number
472            .pack(&mut buffer)
473            .map_err(|_| APIError::Unknown)?;
474
475        for key in key_indices.iter() {
476            key.pack(&mut buffer).map_err(|_| APIError::Unknown)?;
477        }
478        let buffer_len = buffer.len();
479
480        // write data to the device
481        self.write_data_buffer(buffer)?;
482
483        // now validate essence
484        api::prepare_blind_signing::exec(self.transport())?;
485
486        // get buffer state
487        let dbs = api::get_data_buffer_state::exec(self.transport())?;
488
489        // if recognized length is not the buffer_len, something went wrong
490        // during parsing
491        if dbs.data_length != buffer_len as u16 {
492            return Err(APIError::Unknown);
493        }
494
495        Ok(())
496    }
497
498    /// User Confirm
499    ///
500    /// Displays the (parsed and validated) essence in human readable form on the screen of the
501    /// hardware wallet and waits for accepting or rejecting it.
502    pub fn user_confirm(&self) -> Result<(), APIError> {
503        api::user_confirm::exec(self.transport())?;
504        Ok(())
505    }
506
507    /// Sign
508    ///
509    /// The publicly usable function for signing an essence.
510    pub fn sign(&self, num_inputs: u16) -> Result<Vec<u8>, api::errors::APIError> {
511        let mut signatures: Vec<u8> = Vec::new();
512
513        for signature_idx in 0..num_inputs as u8 {
514            let mut signature = api::sign::exec(self.transport(), signature_idx)?;
515            signatures.append(&mut signature.data);
516        }
517
518        Ok(signatures)
519    }
520
521    // methods only available if compiled with APP_DEBUG flag
522    pub fn memory_dump(&self, filename: String) -> Result<(), api::errors::APIError> {
523        if !self.is_debug_app() {
524            return Err(APIError::CommandNotAllowed);
525        }
526        api::dump_memory::memory_dump(self.transport(), filename)
527    }
528
529    pub fn set_non_interactive_mode(
530        &self,
531        non_interactive_mode: bool,
532    ) -> Result<(), api::errors::APIError> {
533        if !self.is_debug_app() {
534            return Err(APIError::CommandNotAllowed);
535        }
536        api::set_non_interactive_mode::exec(self.transport(), non_interactive_mode)
537    }
538}