1use std::convert::{TryFrom, TryInto};
2
3use crate::errors::LedgerError;
4
5const MAX_DATA_SIZE: usize = 255;
6
7#[derive(Debug, Clone, Eq, PartialEq)]
10pub struct APDUData(Vec<u8>);
11
12impl std::ops::Deref for APDUData {
13 type Target = Vec<u8>;
14
15 fn deref(&self) -> &Self::Target {
16 &self.0
17 }
18}
19
20impl APDUData {
21 pub fn new(buf: &[u8]) -> Self {
24 let length = std::cmp::min(buf.len(), MAX_DATA_SIZE);
25 APDUData(buf[..length].to_vec())
26 }
27
28 pub fn resize(&mut self, new_size: usize, fill_with: u8) {
30 self.0
31 .resize(std::cmp::min(new_size, MAX_DATA_SIZE), fill_with)
32 }
33
34 #[allow(clippy::missing_const_for_fn)] pub fn data(self) -> Vec<u8> {
37 self.0
38 }
39}
40
41impl From<&[u8]> for APDUData {
42 fn from(buf: &[u8]) -> Self {
43 Self::new(buf)
44 }
45}
46
47impl From<Vec<u8>> for APDUData {
48 fn from(mut v: Vec<u8>) -> Self {
49 v.resize(std::cmp::min(v.len(), MAX_DATA_SIZE), 0);
50 Self(v)
51 }
52}
53
54impl AsRef<[u8]> for APDUData {
55 #[inline]
56 fn as_ref(&self) -> &[u8] {
57 &self.0[..]
58 }
59}
60
61#[derive(Debug, Clone, Eq, PartialEq)]
65pub struct APDUCommand {
66 pub cla: u8,
68 pub ins: u8,
70 pub p1: u8,
72 pub p2: u8,
74 pub data: APDUData,
76 pub response_len: Option<u8>,
78}
79
80impl std::fmt::Display for APDUCommand {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 f.debug_struct("APDUCommand")
83 .field("cla", &self.cla)
84 .field("ins", &self.ins)
85 .field("p1", &self.p1)
86 .field("p2", &self.p2)
87 .field("data", &hex::encode(&*self.data))
88 .field("response_len", &self.response_len)
89 .finish()
90 }
91}
92
93impl APDUCommand {
94 pub fn serialized_length(&self) -> usize {
96 let mut length = 4;
97 if !self.data.is_empty() {
98 length += 1;
99 length += self.data.len();
100 }
101 length += self.response_len.is_some() as usize;
102 length
103 }
104
105 pub fn write_to<W: std::io::Write>(&self, w: &mut W) -> Result<usize, std::io::Error> {
107 w.write_all(&[self.cla, self.ins, self.p1, self.p2])?;
108 if !self.data.is_empty() {
109 w.write_all(&[self.data.len() as u8])?;
110 w.write_all(self.data.as_ref())?;
111 }
112 if let Some(response_len) = self.response_len {
113 w.write_all(&[response_len])?;
114 }
115 Ok(self.serialized_length())
116 }
117
118 pub fn serialize(&self) -> Vec<u8> {
120 let mut v = Vec::with_capacity(self.serialized_length());
121 self.write_to(&mut v).unwrap();
122 v
123 }
124}
125
126#[derive(Debug, Clone, Eq, PartialEq)]
129pub struct APDUAnswer {
130 response: Vec<u8>,
131}
132
133impl std::ops::Deref for APDUAnswer {
134 type Target = Vec<u8>;
135
136 fn deref(&self) -> &Self::Target {
137 &self.response
138 }
139}
140
141impl std::fmt::Display for APDUAnswer {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 write!(
144 f,
145 "APDUAnswer: {{\n\tResponse: {:?} \n\tData: {:?}\n}}",
146 self.response_status(),
147 self.data()
148 )
149 }
150}
151
152impl APDUAnswer {
153 pub fn from_answer(response: Vec<u8>) -> Result<APDUAnswer, LedgerError> {
155 if response.len() < 2 {
156 Err(LedgerError::ResponseTooShort(response.to_vec()))
157 } else {
158 Ok(Self { response })
159 }
160 }
161
162 pub fn is_success(&self) -> bool {
164 match self.response_status() {
165 Some(opcode) => opcode.is_success(),
166 None => false,
167 }
168 }
169
170 pub fn retcode(&self) -> u16 {
174 let mut buf = [0u8; 2];
175 buf.copy_from_slice(&self.response[self.len() - 2..]);
176 u16::from_be_bytes(buf)
177 }
178
179 pub fn response_status(&self) -> Option<APDUResponseCodes> {
183 self.retcode().try_into().ok()
184 }
185
186 pub fn data(&self) -> Option<&[u8]> {
188 if self.is_success() {
189 Some(&self.response[..self.len() - 2])
190 } else {
191 None
192 }
193 }
194}
195
196#[repr(u16)]
197#[derive(Copy, Clone, Debug, Eq, PartialEq)]
198pub enum APDUResponseCodes {
201 NoError = 0x9000,
203 ExecutionError = 0x6400,
205 WrongLength = 0x6700,
207 UnlockDeviceError = 0x6804,
209 EmptyBuffer = 0x6982,
211 OutputBufferTooSmall = 0x6983,
213 DataInvalid = 0x6984,
215 ConditionsNotSatisfied = 0x6985,
217 CommandNotAllowed = 0x6986,
219 InvalidData = 0x6A80,
221 InvalidP1P2 = 0x6B00,
223 InsNotSupported = 0x6D00,
225 ClaNotSupported = 0x6E00,
227 Unknown = 0x6F00,
229 SignVerifyError = 0x6F01,
231}
232
233impl std::fmt::Display for APDUResponseCodes {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 write!(f, "Code {:x} ({})", *self as u16, self.description())
236 }
237}
238
239impl APDUResponseCodes {
240 pub const fn is_success(self) -> bool {
242 matches!(self, APDUResponseCodes::NoError)
243 }
244
245 pub const fn description(self) -> &'static str {
247 match self {
248 APDUResponseCodes::NoError => "[APDU_CODE_NOERROR]",
249 APDUResponseCodes::ExecutionError => {
250 "[APDU_CODE_EXECUTION_ERROR] No information given (NV-Ram not changed)"
251 }
252 APDUResponseCodes::WrongLength => "[APDU_CODE_WRONG_LENGTH] Wrong length",
253 APDUResponseCodes::UnlockDeviceError => {
254 "[APDU_CODE_UNLOCK_DEVICE_ERROR] Device is locked"
255 }
256 APDUResponseCodes::EmptyBuffer => "[APDU_CODE_EMPTY_BUFFER]",
257 APDUResponseCodes::OutputBufferTooSmall => "[APDU_CODE_OUTPUT_BUFFER_TOO_SMALL]",
258 APDUResponseCodes::DataInvalid => {
259 "[APDU_CODE_DATA_INVALID] data reversibly blocked (invalidated)"
260 }
261 APDUResponseCodes::ConditionsNotSatisfied => {
262 "[APDU_CODE_CONDITIONS_NOT_SATISFIED] Conditions of use not satisfied"
263 }
264 APDUResponseCodes::CommandNotAllowed => {
265 "[APDU_CODE_COMMAND_NOT_ALLOWED] Command not allowed (no current EF)"
266 }
267 APDUResponseCodes::InvalidData => {
268 "[APDU_CODE_INVALID_DATA] The parameters in the data field are incorrect"
269 }
270 APDUResponseCodes::InvalidP1P2 => "[APDU_CODE_INVALIDP1P2] Wrong parameter(s) P1-P2",
271 APDUResponseCodes::InsNotSupported => {
272 "[APDU_CODE_INS_NOT_SUPPORTED] Instruction code not supported or invalid. Hint: Is the correct application open on the device?"
273 }
274 APDUResponseCodes::ClaNotSupported => {
275 "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported"
276 }
277 APDUResponseCodes::Unknown => "[APDU_CODE_UNKNOWN]",
278 APDUResponseCodes::SignVerifyError => "[APDU_CODE_SIGN_VERIFY_ERROR]",
279 }
280 }
281}
282
283impl TryFrom<u16> for APDUResponseCodes {
284 type Error = LedgerError;
285
286 fn try_from(code: u16) -> Result<Self, Self::Error> {
287 match code {
288 0x9000 => Ok(APDUResponseCodes::NoError),
289 0x6400 => Ok(APDUResponseCodes::ExecutionError),
290 0x6700 => Ok(APDUResponseCodes::WrongLength),
291 0x6804 => Ok(APDUResponseCodes::UnlockDeviceError),
292 0x6982 => Ok(APDUResponseCodes::EmptyBuffer),
293 0x6983 => Ok(APDUResponseCodes::OutputBufferTooSmall),
294 0x6984 => Ok(APDUResponseCodes::DataInvalid),
295 0x6985 => Ok(APDUResponseCodes::ConditionsNotSatisfied),
296 0x6986 => Ok(APDUResponseCodes::CommandNotAllowed),
297 0x6A80 => Ok(APDUResponseCodes::InvalidData),
298 0x6B00 => Ok(APDUResponseCodes::InvalidP1P2),
299 0x6D00 => Ok(APDUResponseCodes::InsNotSupported),
300 0x6E00 => Ok(APDUResponseCodes::ClaNotSupported),
301 0x6F00 => Ok(APDUResponseCodes::Unknown),
302 0x6F01 => Ok(APDUResponseCodes::SignVerifyError),
303 _ => Err(LedgerError::UnknownAPDUCode(code)),
304 }
305 }
306}
307
308#[cfg(test)]
309mod test {
310 use super::*;
311
312 #[test]
313 fn serialize() {
314 let data: &[u8] = &[0, 0, 0, 1, 0, 0, 0, 1];
315
316 let command = APDUCommand {
317 cla: 0xe0,
318 ins: 0x01,
319 p1: 0x00,
320 p2: 0x00,
321 data: data.into(),
322 response_len: None,
323 };
324
325 let serialized_command = command.serialize();
326 let expected = vec![224, 1, 0, 0, 8, 0, 0, 0, 1, 0, 0, 0, 1];
327 assert_eq!(serialized_command, expected);
328
329 let command = APDUCommand {
330 cla: 0xe0,
331 ins: 0x01,
332 p1: 0x00,
333 p2: 0x00,
334 data: data.into(),
335 response_len: Some(13),
336 };
337
338 let serialized_command = command.serialize();
339 let expected = vec![224, 1, 0, 0, 8, 0, 0, 0, 1, 0, 0, 0, 1, 13];
340 assert_eq!(serialized_command, expected)
341 }
342}
343
344