1use 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; #[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
67pub 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 ledger.set_account(coin_type, bip32_account)?;
78
79 Ok(Box::new(ledger))
80}
81
82pub 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
109pub 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
116pub 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
123pub 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 fn new(transport_type: &TransportTypes, callback: Option<Callback>) -> Result<Self, APIError> {
142 let transport = crate::transport::create_transport(transport_type, callback)?;
143
144 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 if version < MINIMUM_APP_VERSION {
155 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 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 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 fn read_data_bufer(&self) -> Result<Vec<u8>, APIError> {
222 let dbs = api::get_data_buffer_state::exec(self.transport())?;
224
225 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 let mut buffer: Vec<u8> = Vec::new();
235
236 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 if blocks_needed > dbs.data_block_count {
244 return Err(APIError::CommandInvalidData);
245 }
246
247 for block in 0..blocks_needed {
249 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 fn write_data_buffer(&self, data: Vec<u8>) -> Result<(), APIError> {
258 api::clear_data_buffer::exec(self.transport())?;
260
261 let dbs = api::get_data_buffer_state::exec(self.transport())?;
263
264 if dbs.data_type as u8 != DataTypeEnum::Empty as u8 {
266 return Err(APIError::CommandNotAllowed);
267 }
268
269 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 if blocks_needed > dbs.data_block_count {
277 return Err(APIError::CommandInvalidData);
278 }
279
280 let mut iter = data.chunks(dbs.data_block_size as usize);
282 for block in 0..blocks_needed {
283 let mut block_data = iter.next().unwrap().to_vec();
285
286 while block_data.len() < dbs.data_block_size as usize {
290 block_data.push(0u8);
291 }
292
293 api::write_data_block::exec(self.transport(), block, block_data)?;
295 }
296 Ok(())
297 }
298
299 pub fn reset(&self) -> Result<(), APIError> {
301 api::reset::exec(self.transport())?;
302 Ok(())
303 }
304
305 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 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 api::generate_address::exec(self.transport(), show, bip32, count as u32)?;
335
336 let buffer = self.read_data_bufer()?;
338
339 let mut addresses: Vec<[u8; 32]> = Vec::new();
340 for i in 0_usize..count {
341 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(); 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 if self.version < MINIMUM_APP_VERSION_GENERATE_PUBLIC_KEYS {
360 return Err(APIError::AppTooOld);
361 }
362
363 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 api::generate_public_key::exec(self.transport(), show, bip32, count as u32)?;
374
375 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(); 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 api::clear_data_buffer::exec(self.transport())?;
395
396 api::generate_address::exec(
398 self.transport(),
399 false, LedgerBIP32Index {
401 bip32_index: constants::HARDENED,
402 bip32_change: constants::HARDENED,
403 },
404 1, )?;
406
407 let buffer = self.read_data_bufer()?;
409
410 let addr = buffer[1..constants::ADDRESS_WITH_TYPE_SIZE_BYTES]
412 .try_into()
413 .unwrap();
414 Ok(addr)
415 }
416
417 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 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 if buffer_len > self.data_buffer_size {
439 return Err(api::errors::APIError::EssenceTooLarge);
440 }
441
442 self.write_data_buffer(buffer)?;
444
445 api::prepare_signing::exec(self.transport(), has_remainder, remainder_index, remainder)?;
447
448 let dbs = api::get_data_buffer_state::exec(self.transport())?;
450
451 if dbs.data_length != buffer_len as u16 {
454 return Err(APIError::Unknown);
455 }
456
457 Ok(())
458 }
459
460 pub fn prepare_blind_signing(
464 &self,
465 key_indices: Vec<LedgerBIP32Index>,
466 essence_hash: Vec<u8>,
467 ) -> Result<(), api::errors::APIError> {
468 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 self.write_data_buffer(buffer)?;
482
483 api::prepare_blind_signing::exec(self.transport())?;
485
486 let dbs = api::get_data_buffer_state::exec(self.transport())?;
488
489 if dbs.data_length != buffer_len as u16 {
492 return Err(APIError::Unknown);
493 }
494
495 Ok(())
496 }
497
498 pub fn user_confirm(&self) -> Result<(), APIError> {
503 api::user_confirm::exec(self.transport())?;
504 Ok(())
505 }
506
507 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 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}