1use std::time::Duration;
4
5use encdec::{EncDec, Encode};
6use tracing::{debug, error};
7
8use ledger_proto::{
9 apdus::{
10 decode_app_data, AppData, AppInfoReq, AppInfoResp, AppListNextReq, AppListStartReq,
11 DeviceInfoReq, DeviceInfoResp,
12 },
13 ApduError, ApduReq, GenericApdu, StatusCode,
14};
15
16use crate::{
17 info::{AppInfo, DeviceInfo},
18 Error, Exchange,
19};
20
21const APDU_BUFF_LEN: usize = 256;
22
23#[cfg_attr(not(feature = "unstable_async_trait"), async_trait::async_trait)]
25pub trait Device {
26 async fn request<'a, 'b, RESP: EncDec<'b, ApduError>>(
28 &mut self,
29 request: impl ApduReq<'a> + Send,
30 buff: &'b mut [u8],
31 timeout: Duration,
32 ) -> Result<RESP, Error>;
33
34 async fn app_info(&mut self, timeout: Duration) -> Result<AppInfo, Error> {
36 let mut buff = [0u8; APDU_BUFF_LEN];
37
38 let r = self
39 .request::<AppInfoResp>(AppInfoReq {}, &mut buff[..], timeout)
40 .await?;
41
42 Ok(AppInfo {
43 name: r.name.to_string(),
44 version: r.version.to_string(),
45 flags: r.flags,
46 })
47 }
48
49 async fn device_info(&mut self, timeout: Duration) -> Result<DeviceInfo, Error> {
51 let mut buff = [0u8; APDU_BUFF_LEN];
52
53 let r = self
54 .request::<DeviceInfoResp>(DeviceInfoReq {}, &mut buff[..], timeout)
55 .await?;
56
57 Ok(DeviceInfo {
58 target_id: r.target_id,
59 se_version: r.se_version.to_string(),
60 mcu_version: r.mcu_version.to_string(),
61 flags: r.flags.to_vec(),
62 })
63 }
64
65 async fn app_list(&mut self, timeout: Duration) -> Result<Vec<AppData>, Error> {
67 let mut buff = [0u8; APDU_BUFF_LEN];
68
69 let mut app_data_list: Vec<AppData> = Default::default();
70
71 let mut start: bool = true;
72
73 loop {
74 let r = match start {
75 true => {
76 self.request::<GenericApdu>(AppListStartReq {}, &mut buff[..], timeout)
77 .await
78 }
79 false => {
80 self.request::<GenericApdu>(AppListNextReq {}, &mut buff[..], timeout)
81 .await
82 }
83 };
84
85 start = false;
86
87 match r {
88 Ok(apdu_output) => {
89 let mut offset: usize = 1;
90 while offset < apdu_output.data.len() - 2 {
91 let data = decode_app_data(apdu_output.data.as_slice(), &mut offset)
92 .map_err(Error::from)?;
93 app_data_list.push(data);
94 }
95 }
96 Err(Error::Status(StatusCode::Ok)) => {
97 break;
98 }
99 Err(e) => {
100 error!("Command failed: {e:?}");
101 return Err(e);
102 }
103 }
104 }
105 Ok(app_data_list)
106 }
107}
108
109#[cfg_attr(not(feature = "unstable_async_trait"), async_trait::async_trait)]
111impl<T: Exchange + Send> Device for T {
112 async fn request<'a, 'b, RESP: EncDec<'b, ApduError>>(
114 &mut self,
115 req: impl ApduReq<'a> + Send,
116 buff: &'b mut [u8],
117 timeout: Duration,
118 ) -> Result<RESP, Error> {
119 debug!("TX: {req:?}");
120
121 let n = encode_request(req, buff)?;
123
124 let resp_bytes = self.exchange(&buff[..n], timeout).await?;
126
127 let n = resp_bytes.len();
130 if n > buff.len() {
131 error!(
132 "Response length exceeds buffer length ({} > {})",
133 n,
134 buff.len()
135 );
136 return Err(ApduError::InvalidLength.into());
137 }
138 buff[..n].copy_from_slice(&resp_bytes[..]);
139
140 if n == 2 {
142 let v = u16::from_be_bytes([resp_bytes[0], resp_bytes[1]]);
144 match StatusCode::try_from(v) {
145 Ok(c) => return Err(Error::Status(c)),
146 Err(_) => return Err(Error::UnknownStatus(resp_bytes[0], resp_bytes[1])),
147 }
148 }
149
150 let (resp, _) = RESP::decode(&buff[..n - 2])?;
152
153 debug!("RX: {resp:?}");
154
155 Ok(resp)
157 }
158}
159
160fn encode_request<'a, REQ: ApduReq<'a>>(req: REQ, buff: &mut [u8]) -> Result<usize, Error> {
162 let mut index = 0;
163
164 let data_len = req.encode_len()?;
165
166 if buff.len() < 5 + data_len {
168 return Err(ApduError::InvalidLength.into());
169 }
170
171 let h = req.header();
175 index += h.encode(&mut buff[index..])?;
176
177 if data_len > u8::MAX as usize {
179 return Err(ApduError::InvalidLength.into());
180 }
181 buff[index] = data_len as u8;
182 index += 1;
183
184 index += req.encode(&mut buff[index..])?;
186
187 Ok(index)
188}
189
190#[cfg(test)]
191mod tests {
192 use ledger_proto::{apdus::AppInfoReq, ApduStatic};
193
194 use super::encode_request;
195
196 #[test]
197 fn test_encode_requests() {
198 let mut buff = [0u8; 256];
199
200 let req = AppInfoReq {};
201 let n = encode_request(req, &mut buff).unwrap();
202 assert_eq!(n, 5);
203 assert_eq!(
204 &buff[..n],
205 &[AppInfoReq::CLA, AppInfoReq::INS, 0x00, 0x00, 0x00]
206 );
207 }
208}