ledger_ethereum/command/
get_address.rs

1use ledger_transport::{APDUCommand, APDUErrorCode, Exchange};
2use ledger_zondax_generic::App;
3
4use crate::command::InstructionCode;
5use crate::types::{BIP44Path, EthError};
6use crate::{EthApp, LedgerAppError};
7
8#[derive(Debug)]
9pub struct Address {
10    /// Secp256k1 pubkey bytes
11    pub public_key: Vec<u8>,
12    /// Address bytes in raw UTF-8, without "0x" prefix
13    pub address: Vec<u8>,
14    /// Optional chain code bytes
15    pub chain_code: Option<Vec<u8>>,
16}
17
18impl<E> EthApp<E>
19where
20    E: Exchange + Send + Sync,
21    E::Error: std::error::Error,
22{
23    /// Retrieves the public key and address
24    pub async fn address(
25        &self,
26        path: &BIP44Path,
27        enable_display: Option<bool>,
28        enabled_chain_code: Option<bool>,
29    ) -> Result<Address, EthError<E::Error>> {
30        let data = path.serialize_bip44();
31        let p1 = enable_display.map_or(0, |v| v as u8);
32        let p2 = enabled_chain_code.map_or(0, |v| v as u8);
33
34        let command = APDUCommand {
35            cla: Self::CLA,
36            ins: InstructionCode::GetAddress as _,
37            p1,
38            p2,
39            data,
40        };
41
42        let response = self
43            .transport
44            .exchange(&command)
45            .await
46            .map_err(LedgerAppError::TransportError)?;
47
48        let response_data = response.data();
49        match response.error_code() {
50            Ok(APDUErrorCode::NoError) => {}
51            Ok(err) => {
52                return Err(EthError::Ledger(LedgerAppError::AppSpecific(
53                    err as _,
54                    err.description(),
55                )))
56            }
57            Err(err) => {
58                return Err(EthError::Ledger(LedgerAppError::AppSpecific(
59                    err,
60                    "[APDU_ERROR] Unknown".to_string(),
61                )))
62            }
63        }
64
65        let public_key_len: usize = (*response_data
66            .first()
67            .ok_or(EthError::MissingResponseData("pubkey length".into()))?)
68        .into();
69
70        let pubkey_start = 1;
71        let pubkey_end = pubkey_start + public_key_len;
72        let public_key = response_data
73            .get(pubkey_start..pubkey_end)
74            .ok_or(EthError::MissingResponseData("public key".into()))?
75            .to_vec();
76
77        let address_len: usize = (*response_data
78            .get(pubkey_end)
79            .ok_or(EthError::MissingResponseData("address length".into()))?)
80        .into();
81        let address_start = pubkey_end + 1;
82        let address_end = address_start + address_len;
83        let address = response_data
84            .get(address_start..address_end)
85            .ok_or(EthError::MissingResponseData("address".into()))?
86            .to_vec();
87
88        let chain_code = if let Some(true) = enabled_chain_code {
89            let cc_start = address_end + 1;
90            let cc_end = address_start + address_len;
91            Some(
92                response_data
93                    .get(cc_start..cc_end)
94                    .ok_or(EthError::MissingResponseData("chain code".into()))?
95                    .to_vec(),
96            )
97        } else {
98            None
99        };
100        Ok(Address {
101            public_key,
102            address,
103            chain_code,
104        })
105    }
106}