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