ledger_ethereum/command/
sign_transaction.rs

1use ledger_transport::{APDUCommand, APDUErrorCode, Exchange};
2use ledger_zondax_generic::{App, LedgerAppError};
3
4use crate::command::InstructionCode;
5use crate::types::{BIP44Path, ChunkPayloadType, EthError, LedgerEthTransactionResolution};
6use crate::EthApp;
7
8#[derive(Debug)]
9pub struct Signature {
10    pub v: u8,
11    pub r: [u8; 32],
12    pub s: [u8; 32],
13}
14
15impl<E> EthApp<E>
16where
17    E: Exchange + Send + Sync,
18    E::Error: std::error::Error,
19{
20    /// Sign a transaction
21    pub async fn sign(
22        &self,
23        path: &BIP44Path,
24        raw_tx: &[u8],
25        // TODO: come back to this later and see if we can resolve txns instead of blind signing
26        _resolution: Option<LedgerEthTransactionResolution>,
27    ) -> Result<Signature, EthError<E::Error>> {
28        let mut data = vec![];
29        let path = path.serialize_bip44();
30        data.extend_from_slice(&path);
31        data.extend_from_slice(raw_tx);
32
33        let command = APDUCommand {
34            cla: Self::CLA,
35            ins: InstructionCode::SignTransaction as _,
36            p1: ChunkPayloadType::First as u8,
37            p2: 0x00,
38            data,
39        };
40
41        let response = self.send_chunks(command).await?;
42
43        let response_data = response.data();
44        match response.error_code() {
45            Ok(APDUErrorCode::NoError) if response_data.is_empty() => {
46                return Err(EthError::Ledger(LedgerAppError::NoSignature))
47            }
48            // Last response should contain the answer
49            Ok(APDUErrorCode::NoError) if response_data.len() < 3 => {
50                return Err(EthError::Ledger(LedgerAppError::InvalidSignature))
51            }
52            Ok(APDUErrorCode::NoError) => {}
53            Ok(err) => {
54                return Err(EthError::Ledger(LedgerAppError::AppSpecific(
55                    err as _,
56                    err.description(),
57                )))
58            }
59            Err(err) => {
60                return Err(EthError::Ledger(LedgerAppError::AppSpecific(
61                    err,
62                    "[APDU_ERROR] Unknown".to_string(),
63                )))
64            }
65        }
66
67        let v = response_data
68            .first()
69            .ok_or(EthError::MissingResponseData(
70                "signature v component".into(),
71            ))?
72            .to_owned();
73        let r = response_data
74            .get(1..33)
75            .ok_or(EthError::MissingResponseData(
76                "signature r component".into(),
77            ))?
78            .try_into() // safe due to get() range
79            .unwrap();
80        let s = response_data
81            .get(33..65)
82            .ok_or(EthError::MissingResponseData(
83                "signature s component".into(),
84            ))?
85            .try_into() // safe due to get() range
86            .unwrap();
87        Ok(Signature { v, r, s })
88    }
89}