#[cfg(feature = "bitcoin")] mod bitcoin; #[cfg(feature = "bitcoin")] pub use self::bitcoin::*; #[cfg(feature = "ethereum")] mod ethereum; #[cfg(feature = "ethereum")] pub use ethereum::*; #[cfg(feature = "solana")] mod solana; #[cfg(feature = "solana")] pub use solana::*; pub mod common; pub use common::*; use crate::{ error::{Error, Result}, messages::TrezorMessage, protos, protos::MessageType::*, transport::{ProtoMessage, Transport}, Model, }; use protobuf::MessageField; use tracing::{debug, trace}; /// A Trezor client. pub struct Trezor { model: Model, // Cached features for later inspection. features: Option, transport: Box, } /// Create a new Trezor instance with the given transport. pub fn trezor_with_transport(model: Model, transport: Box) -> Trezor { Trezor { model, transport, features: None } } impl Trezor { /// Get the model of the Trezor device. pub fn model(&self) -> Model { self.model } /// Get the features of the Trezor device. pub fn features(&self) -> Option<&protos::Features> { self.features.as_ref() } /// Sends a message and returns the raw ProtoMessage struct that was responded by the device. /// This method is only exported for users that want to expand the features of this library /// f.e. for supporting additional coins etc. pub fn call_raw(&mut self, message: S) -> Result { let proto_msg = ProtoMessage(S::MESSAGE_TYPE, message.write_to_bytes()?); self.transport.write_message(proto_msg).map_err(Error::TransportSendMessage)?; self.transport.read_message().map_err(Error::TransportReceiveMessage) } /// Sends a message and returns a TrezorResponse with either the expected response message, /// a failure or an interaction request. /// This method is only exported for users that want to expand the features of this library /// f.e. for supporting additional coins etc. pub fn call<'a, T, S: TrezorMessage, R: TrezorMessage>( &'a mut self, message: S, result_handler: Box>, ) -> Result> { trace!("Sending {:?} msg: {:?}", S::MESSAGE_TYPE, message); let resp = self.call_raw(message)?; if resp.message_type() == R::MESSAGE_TYPE { let resp_msg = resp.into_message()?; trace!("Received {:?} msg: {:?}", R::MESSAGE_TYPE, resp_msg); Ok(TrezorResponse::Ok(result_handler(self, resp_msg)?)) } else { match resp.message_type() { MessageType_Failure => { let fail_msg = resp.into_message()?; debug!("Received failure: {:?}", fail_msg); Ok(TrezorResponse::Failure(fail_msg)) } MessageType_ButtonRequest => { let req_msg = resp.into_message()?; trace!("Received ButtonRequest: {:?}", req_msg); Ok(TrezorResponse::ButtonRequest(ButtonRequest { message: req_msg, client: self, result_handler, })) } MessageType_PinMatrixRequest => { let req_msg = resp.into_message()?; trace!("Received PinMatrixRequest: {:?}", req_msg); Ok(TrezorResponse::PinMatrixRequest(PinMatrixRequest { message: req_msg, client: self, result_handler, })) } MessageType_PassphraseRequest => { let req_msg = resp.into_message()?; trace!("Received PassphraseRequest: {:?}", req_msg); Ok(TrezorResponse::PassphraseRequest(PassphraseRequest { message: req_msg, client: self, result_handler, })) } mtype => { debug!( "Received unexpected msg type: {:?}; raw msg: {}", mtype, hex::encode(resp.into_payload()) ); Err(Error::UnexpectedMessageType(mtype)) } } } } pub fn init_device(&mut self, session_id: Option>) -> Result<()> { let features = self.initialize(session_id)?.ok()?; self.features = Some(features); Ok(()) } pub fn initialize( &mut self, session_id: Option>, ) -> Result> { let mut req = protos::Initialize::new(); if let Some(session_id) = session_id { req.set_session_id(session_id); } self.call(req, Box::new(|_, m| Ok(m))) } pub fn ping(&mut self, message: &str) -> Result> { let mut req = protos::Ping::new(); req.set_message(message.to_owned()); self.call(req, Box::new(|_, _| Ok(()))) } pub fn change_pin(&mut self, remove: bool) -> Result> { let mut req = protos::ChangePin::new(); req.set_remove(remove); self.call(req, Box::new(|_, _| Ok(()))) } pub fn wipe_device(&mut self) -> Result> { let req = protos::WipeDevice::new(); self.call(req, Box::new(|_, _| Ok(()))) } pub fn recover_device( &mut self, word_count: WordCount, passphrase_protection: bool, pin_protection: bool, label: String, dry_run: bool, ) -> Result> { let mut req = protos::RecoveryDevice::new(); req.set_word_count(word_count as u32); req.set_passphrase_protection(passphrase_protection); req.set_pin_protection(pin_protection); req.set_label(label); req.set_enforce_wordlist(true); if dry_run { req.set_type(protos::RecoveryType::DryRun); } else { req.set_type(protos::RecoveryType::NormalRecovery); } req.set_input_method(protos::recovery_device::RecoveryDeviceInputMethod::ScrambledWords); //TODO(stevenroose) support languages req.set_language("english".to_owned()); self.call(req, Box::new(|_, _| Ok(()))) } #[allow(clippy::too_many_arguments)] pub fn reset_device( &mut self, strength: usize, passphrase_protection: bool, pin_protection: bool, label: String, skip_backup: bool, no_backup: bool, ) -> Result, protos::EntropyRequest>> { let mut req = protos::ResetDevice::new(); req.set_strength(strength as u32); req.set_passphrase_protection(passphrase_protection); req.set_pin_protection(pin_protection); req.set_label(label); req.set_skip_backup(skip_backup); req.set_no_backup(no_backup); self.call(req, Box::new(|c, _| Ok(EntropyRequest { client: c }))) } pub fn backup(&mut self) -> Result> { let req = protos::BackupDevice::new(); self.call(req, Box::new(|_, _| Ok(()))) } //TODO(stevenroose) support U2F stuff? currently ignored all pub fn apply_settings( &mut self, label: Option, use_passphrase: Option, homescreen: Option>, auto_lock_delay_ms: Option, ) -> Result> { let mut req = protos::ApplySettings::new(); if let Some(label) = label { req.set_label(label); } if let Some(use_passphrase) = use_passphrase { req.set_use_passphrase(use_passphrase); } if let Some(homescreen) = homescreen { req.set_homescreen(homescreen); } if let Some(auto_lock_delay_ms) = auto_lock_delay_ms { req.set_auto_lock_delay_ms(auto_lock_delay_ms as u32); } self.call(req, Box::new(|_, _| Ok(()))) } pub fn sign_identity( &mut self, identity: protos::IdentityType, digest: Vec, curve: String, ) -> Result, protos::SignedIdentity>> { let mut req = protos::SignIdentity::new(); req.identity = MessageField::some(identity); req.set_challenge_hidden(digest); req.set_challenge_visual("".to_owned()); req.set_ecdsa_curve_name(curve); self.call(req, Box::new(|_, m| Ok(m.signature().to_owned()))) } pub fn get_ecdh_session_key( &mut self, identity: protos::IdentityType, peer_public_key: Vec, curve: String, ) -> Result> { let mut req = protos::GetECDHSessionKey::new(); req.identity = MessageField::some(identity); req.set_peer_public_key(peer_public_key); req.set_ecdsa_curve_name(curve); self.call(req, Box::new(|_, m| Ok(m))) } }