mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-01 02:10:56 +00:00
248 lines
9.3 KiB
Rust
248 lines
9.3 KiB
Rust
use crate::{
|
|
error::{Error, Result},
|
|
messages::TrezorMessage,
|
|
protos, Trezor,
|
|
};
|
|
use std::fmt;
|
|
|
|
// Some types with raw protos that we use in the public interface so they have to be exported.
|
|
pub use protos::{
|
|
button_request::ButtonRequestType, pin_matrix_request::PinMatrixRequestType, Features,
|
|
};
|
|
|
|
#[cfg(feature = "bitcoin")]
|
|
pub use protos::InputScriptType;
|
|
|
|
/// The different options for the number of words in a seed phrase.
|
|
pub enum WordCount {
|
|
W12 = 12,
|
|
W18 = 18,
|
|
W24 = 24,
|
|
}
|
|
|
|
/// The different types of user interactions the Trezor device can request.
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
pub enum InteractionType {
|
|
Button,
|
|
PinMatrix,
|
|
Passphrase,
|
|
PassphraseState,
|
|
}
|
|
|
|
//TODO(stevenroose) should this be FnOnce and put in an FnBox?
|
|
/// Function to be passed to the `Trezor.call` method to process the Trezor response message into a
|
|
/// general-purpose type.
|
|
pub type ResultHandler<'a, T, R> = dyn Fn(&'a mut Trezor, R) -> Result<T>;
|
|
|
|
/// A button request message sent by the device.
|
|
pub struct ButtonRequest<'a, T, R: TrezorMessage> {
|
|
pub message: protos::ButtonRequest,
|
|
pub client: &'a mut Trezor,
|
|
pub result_handler: Box<ResultHandler<'a, T, R>>,
|
|
}
|
|
|
|
impl<'a, T, R: TrezorMessage> fmt::Debug for ButtonRequest<'a, T, R> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(&self.message, f)
|
|
}
|
|
}
|
|
|
|
impl<'a, T, R: TrezorMessage> ButtonRequest<'a, T, R> {
|
|
/// The type of button request.
|
|
pub fn request_type(&self) -> ButtonRequestType {
|
|
self.message.code()
|
|
}
|
|
|
|
/// Ack the request and get the next message from the device.
|
|
pub fn ack(self) -> Result<TrezorResponse<'a, T, R>> {
|
|
let req = protos::ButtonAck::new();
|
|
self.client.call(req, self.result_handler)
|
|
}
|
|
}
|
|
|
|
/// A PIN matrix request message sent by the device.
|
|
pub struct PinMatrixRequest<'a, T, R: TrezorMessage> {
|
|
pub message: protos::PinMatrixRequest,
|
|
pub client: &'a mut Trezor,
|
|
pub result_handler: Box<ResultHandler<'a, T, R>>,
|
|
}
|
|
|
|
impl<'a, T, R: TrezorMessage> fmt::Debug for PinMatrixRequest<'a, T, R> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(&self.message, f)
|
|
}
|
|
}
|
|
|
|
impl<'a, T, R: TrezorMessage> PinMatrixRequest<'a, T, R> {
|
|
/// The type of PIN matrix request.
|
|
pub fn request_type(&self) -> PinMatrixRequestType {
|
|
self.message.type_()
|
|
}
|
|
|
|
/// Ack the request with a PIN and get the next message from the device.
|
|
pub fn ack_pin(self, pin: String) -> Result<TrezorResponse<'a, T, R>> {
|
|
let mut req = protos::PinMatrixAck::new();
|
|
req.set_pin(pin);
|
|
self.client.call(req, self.result_handler)
|
|
}
|
|
}
|
|
|
|
/// A passphrase request message sent by the device.
|
|
pub struct PassphraseRequest<'a, T, R: TrezorMessage> {
|
|
pub message: protos::PassphraseRequest,
|
|
pub client: &'a mut Trezor,
|
|
pub result_handler: Box<ResultHandler<'a, T, R>>,
|
|
}
|
|
|
|
impl<'a, T, R: TrezorMessage> fmt::Debug for PassphraseRequest<'a, T, R> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Debug::fmt(&self.message, f)
|
|
}
|
|
}
|
|
|
|
impl<'a, T, R: TrezorMessage> PassphraseRequest<'a, T, R> {
|
|
/// Check whether the use is supposed to enter the passphrase on the device or not.
|
|
pub fn on_device(&self) -> bool {
|
|
self.message._on_device()
|
|
}
|
|
|
|
/// Ack the request with a passphrase and get the next message from the device.
|
|
pub fn ack_passphrase(self, passphrase: String) -> Result<TrezorResponse<'a, T, R>> {
|
|
let mut req = protos::PassphraseAck::new();
|
|
req.set_passphrase(passphrase);
|
|
self.client.call(req, self.result_handler)
|
|
}
|
|
|
|
/// Ack the request without a passphrase to let the user enter it on the device
|
|
/// and get the next message from the device.
|
|
pub fn ack(self, on_device: bool) -> Result<TrezorResponse<'a, T, R>> {
|
|
let mut req = protos::PassphraseAck::new();
|
|
if on_device {
|
|
req.set_on_device(on_device);
|
|
}
|
|
self.client.call(req, self.result_handler)
|
|
}
|
|
}
|
|
|
|
/// A response from a Trezor device. On every message exchange, instead of the expected/desired
|
|
/// response, the Trezor can ask for some user interaction, or can send a failure.
|
|
#[derive(Debug)]
|
|
pub enum TrezorResponse<'a, T, R: TrezorMessage> {
|
|
Ok(T),
|
|
Failure(protos::Failure),
|
|
ButtonRequest(ButtonRequest<'a, T, R>),
|
|
PinMatrixRequest(PinMatrixRequest<'a, T, R>),
|
|
PassphraseRequest(PassphraseRequest<'a, T, R>),
|
|
//TODO(stevenroose) This should be taken out of this enum and intrinsically attached to the
|
|
// PassphraseRequest variant. However, it's currently impossible to do this. It might be
|
|
// possible to do with FnBox (currently nightly) or when Box<FnOnce> becomes possible.
|
|
}
|
|
|
|
impl<'a, T, R: TrezorMessage> fmt::Display for TrezorResponse<'a, T, R> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
// TODO(stevenroose) should we make T: Debug?
|
|
TrezorResponse::Ok(ref _m) => f.write_str("Ok"),
|
|
TrezorResponse::Failure(ref m) => write!(f, "Failure: {:?}", m),
|
|
TrezorResponse::ButtonRequest(ref r) => write!(f, "ButtonRequest: {:?}", r),
|
|
TrezorResponse::PinMatrixRequest(ref r) => write!(f, "PinMatrixRequest: {:?}", r),
|
|
TrezorResponse::PassphraseRequest(ref r) => write!(f, "PassphraseRequest: {:?}", r),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a, T, R: TrezorMessage> TrezorResponse<'a, T, R> {
|
|
/// Get the actual `Ok` response value or an error if not `Ok`.
|
|
pub fn ok(self) -> Result<T> {
|
|
match self {
|
|
TrezorResponse::Ok(m) => Ok(m),
|
|
TrezorResponse::Failure(m) => Err(Error::FailureResponse(m)),
|
|
TrezorResponse::ButtonRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::Button))
|
|
}
|
|
TrezorResponse::PinMatrixRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::PinMatrix))
|
|
}
|
|
TrezorResponse::PassphraseRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::Passphrase))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get the button request object or an error if not `ButtonRequest`.
|
|
pub fn button_request(self) -> Result<ButtonRequest<'a, T, R>> {
|
|
match self {
|
|
TrezorResponse::ButtonRequest(r) => Ok(r),
|
|
TrezorResponse::Ok(_) => Err(Error::UnexpectedMessageType(R::MESSAGE_TYPE)),
|
|
TrezorResponse::Failure(m) => Err(Error::FailureResponse(m)),
|
|
TrezorResponse::PinMatrixRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::PinMatrix))
|
|
}
|
|
TrezorResponse::PassphraseRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::Passphrase))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get the PIN matrix request object or an error if not `PinMatrixRequest`.
|
|
pub fn pin_matrix_request(self) -> Result<PinMatrixRequest<'a, T, R>> {
|
|
match self {
|
|
TrezorResponse::PinMatrixRequest(r) => Ok(r),
|
|
TrezorResponse::Ok(_) => Err(Error::UnexpectedMessageType(R::MESSAGE_TYPE)),
|
|
TrezorResponse::Failure(m) => Err(Error::FailureResponse(m)),
|
|
TrezorResponse::ButtonRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::Button))
|
|
}
|
|
TrezorResponse::PassphraseRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::Passphrase))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get the passphrase request object or an error if not `PassphraseRequest`.
|
|
pub fn passphrase_request(self) -> Result<PassphraseRequest<'a, T, R>> {
|
|
match self {
|
|
TrezorResponse::PassphraseRequest(r) => Ok(r),
|
|
TrezorResponse::Ok(_) => Err(Error::UnexpectedMessageType(R::MESSAGE_TYPE)),
|
|
TrezorResponse::Failure(m) => Err(Error::FailureResponse(m)),
|
|
TrezorResponse::ButtonRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::Button))
|
|
}
|
|
TrezorResponse::PinMatrixRequest(_) => {
|
|
Err(Error::UnexpectedInteractionRequest(InteractionType::PinMatrix))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn handle_interaction<T, R: TrezorMessage>(resp: TrezorResponse<'_, T, R>) -> Result<T> {
|
|
match resp {
|
|
TrezorResponse::Ok(res) => Ok(res),
|
|
TrezorResponse::Failure(_) => resp.ok(), // assering ok() returns the failure error
|
|
TrezorResponse::ButtonRequest(req) => handle_interaction(req.ack()?),
|
|
TrezorResponse::PinMatrixRequest(_) => Err(Error::UnsupportedNetwork),
|
|
TrezorResponse::PassphraseRequest(req) => handle_interaction({
|
|
let on_device = req.on_device();
|
|
req.ack(!on_device)?
|
|
}),
|
|
}
|
|
}
|
|
|
|
/// When resetting the device, it will ask for entropy to aid key generation.
|
|
pub struct EntropyRequest<'a> {
|
|
pub client: &'a mut Trezor,
|
|
}
|
|
|
|
impl<'a> EntropyRequest<'a> {
|
|
/// Provide exactly 32 bytes or entropy.
|
|
pub fn ack_entropy(self, entropy: Vec<u8>) -> Result<TrezorResponse<'a, (), protos::Success>> {
|
|
if entropy.len() != 32 {
|
|
return Err(Error::InvalidEntropy)
|
|
}
|
|
|
|
let mut req = protos::EntropyAck::new();
|
|
req.set_entropy(entropy);
|
|
self.client.call(req, Box::new(|_, _| Ok(())))
|
|
}
|
|
}
|