1use std::convert::TryFrom;
7
8use crate::ocard::data::{CardCapabilities, CardServiceData, HistoricalBytes};
9use crate::Error;
10
11impl CardCapabilities {
12 pub fn command_chaining(&self) -> bool {
13 self.command_chaining
14 }
15
16 pub fn extended_lc_le(&self) -> bool {
17 self.extended_lc_le
18 }
19
20 pub fn extended_length_information(&self) -> bool {
21 self.extended_length_information
22 }
23}
24
25impl From<[u8; 3]> for CardCapabilities {
26 fn from(data: [u8; 3]) -> Self {
27 let byte3 = data[2];
28
29 let command_chaining = byte3 & 0x80 != 0;
30 let extended_lc_le = byte3 & 0x40 != 0;
31 let extended_length_information = byte3 & 0x20 != 0;
32
33 Self {
34 command_chaining,
35 extended_lc_le,
36 extended_length_information,
37 }
38 }
39}
40
41impl From<u8> for CardServiceData {
42 fn from(data: u8) -> Self {
43 let select_by_full_df_name = data & 0x80 != 0;
44 let select_by_partial_df_name = data & 0x40 != 0;
45 let dos_available_in_ef_dir = data & 0x20 != 0;
46 let dos_available_in_ef_atr_info = data & 0x10 != 0;
47 let access_services = [data & 0x8 != 0, data & 0x4 != 0, data & 0x2 != 0];
48 let mf = data & 0x1 != 0;
49
50 Self {
51 select_by_full_df_name,
52 select_by_partial_df_name,
53 dos_available_in_ef_dir,
54 dos_available_in_ef_atr_info,
55 access_services,
56 mf,
57 }
58 }
59}
60
61fn split_tl(tl: u8) -> (u8, u8) {
67 let tag = (tl & 0xf0) >> 4;
68 let len = tl & 0x0f;
69
70 (tag, len)
71}
72
73impl HistoricalBytes {
74 pub fn card_capabilities(&self) -> Option<&CardCapabilities> {
75 self.cc.as_ref()
76 }
77
78 pub fn card_service_data(&self) -> Option<&CardServiceData> {
79 self.csd.as_ref()
80 }
81}
82
83impl TryFrom<&[u8]> for HistoricalBytes {
84 type Error = crate::Error;
85
86 fn try_from(mut data: &[u8]) -> Result<Self, Self::Error> {
87 #[allow(clippy::expect_used)] if data.ends_with(&[0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) {
90 data = data
91 .strip_suffix(&[0x0, 0x0, 0x0, 0x0, 0x0])
92 .expect("checked");
93 }
94
95 if data == [0x0, 0x31, 0xc5, 0x73, 0xc0, 0x1, 0x80, 0x7, 0x90, 0x0] {
97 data = &[0x0, 0x31, 0xc5, 0x73, 0xc0, 0x1, 0x80, 0x5, 0x90, 0x0];
98 }
99
100 let len = data.len();
101
102 if len < 4 {
103 return Err(Error::ParseError(format!(
106 "Historical bytes too short ({len} bytes), must be >= 4"
107 )));
108 }
109
110 if data[0] != 0 {
111 return Err(Error::ParseError(
115 "Unexpected category indicator in historical bytes".into(),
116 ));
117 }
118
119 let cib = data[0];
121
122 let mut csd = None;
124
125 let mut cc = None;
127
128 let mut ctlv = data[1..len - 3].to_vec();
130 while !ctlv.is_empty() {
131 let (t, l) = split_tl(ctlv[0]);
132
133 if ctlv.len() < (1 + l as usize) {
137 return Err(Error::ParseError(format!(
138 "Illegal length value in Historical Bytes TL {} len {} l {}",
139 ctlv[0],
140 ctlv.len(),
141 l
142 )));
143 }
144
145 match (t, l) {
146 (0x3, 0x1) => {
147 csd = Some(ctlv[1]);
148 ctlv.drain(0..2);
149 }
150 (0x7, 0x3) => {
151 cc = Some([ctlv[1], ctlv[2], ctlv[3]]);
152 ctlv.drain(0..4);
153 }
154 (_, _) => {
155 log::trace!("historical bytes: ignored (tag {}, len {})", t, l);
160 ctlv.drain(0..(l as usize + 1));
161 }
162 }
163 }
164
165 let sib = match data[len - 3] {
167 0 => {
168 0
171 }
172 3 => {
173 3
177 }
178 5 => {
179 5
183 }
184 _ => {
185 return Err(Error::ParseError(
186 "unexpected status indicator in historical bytes".into(),
187 ));
188 }
189 };
190
191 let cc = cc.map(CardCapabilities::from);
197 let csd = csd.map(CardServiceData::from);
198
199 Ok(Self { cib, csd, cc, sib })
200 }
201}
202
203#[cfg(test)]
204mod test {
205 use std::convert::TryInto;
206
207 use super::*;
208
209 #[test]
210 fn test_split_tl() {
211 assert_eq!(split_tl(0x31), (3, 1));
212 assert_eq!(split_tl(0x73), (7, 3));
213 assert_eq!(split_tl(0x00), (0, 0));
214 assert_eq!(split_tl(0xff), (0xf, 0xf));
215 }
216
217 #[test]
218 fn test_gnuk() -> Result<(), Error> {
219 let data: &[u8] = &[0x0, 0x31, 0x84, 0x73, 0x80, 0x1, 0x80, 0x5, 0x90, 0x0];
221 let hist: HistoricalBytes = data.try_into()?;
222
223 assert_eq!(
224 hist,
225 HistoricalBytes {
226 cib: 0,
227 csd: Some(CardServiceData {
228 select_by_full_df_name: true,
229 select_by_partial_df_name: false,
230 dos_available_in_ef_dir: false,
231 dos_available_in_ef_atr_info: false,
232 access_services: [false, true, false,],
233 mf: false,
234 }),
235 cc: Some(CardCapabilities {
236 command_chaining: true,
237 extended_lc_le: false,
238 extended_length_information: false,
239 }),
240 sib: 5
241 }
242 );
243
244 Ok(())
245 }
246
247 #[test]
248 fn test_floss34() -> Result<(), Error> {
249 let data: &[u8] = &[0x0, 0x31, 0xf5, 0x73, 0xc0, 0x1, 0x60, 0x5, 0x90, 0x0];
251 let hist: HistoricalBytes = data.try_into()?;
252
253 assert_eq!(
254 hist,
255 HistoricalBytes {
256 cib: 0,
257 csd: Some(CardServiceData {
258 select_by_full_df_name: true,
259 select_by_partial_df_name: true,
260 dos_available_in_ef_dir: true,
261 dos_available_in_ef_atr_info: true,
262 access_services: [false, true, false,],
263 mf: true,
264 },),
265 cc: Some(CardCapabilities {
266 command_chaining: false,
267 extended_lc_le: true,
268 extended_length_information: true,
269 },),
270 sib: 5,
271 }
272 );
273
274 Ok(())
275 }
276
277 #[test]
278 fn test_yk5() -> Result<(), Error> {
279 let data: &[u8] = &[0x0, 0x73, 0x0, 0x0, 0xe0, 0x5, 0x90, 0x0];
281 let hist: HistoricalBytes = data.try_into()?;
282
283 assert_eq!(
284 hist,
285 HistoricalBytes {
286 cib: 0,
287 csd: None,
288 cc: Some(CardCapabilities {
289 command_chaining: true,
290 extended_lc_le: true,
291 extended_length_information: true,
292 },),
293 sib: 5,
294 }
295 );
296
297 Ok(())
298 }
299
300 #[test]
301 fn test_yk4() -> Result<(), Error> {
302 let data: &[u8] = &[0x0, 0x73, 0x0, 0x0, 0x80, 0x5, 0x90, 0x0];
304 let hist: HistoricalBytes = data.try_into()?;
305
306 assert_eq!(
307 hist,
308 HistoricalBytes {
309 cib: 0,
310 csd: None,
311 cc: Some(CardCapabilities {
312 command_chaining: true,
313 extended_lc_le: false,
314 extended_length_information: false,
315 },),
316 sib: 5,
317 }
318 );
319
320 Ok(())
321 }
322
323 #[test]
324 fn test_yk_neo() -> Result<(), Error> {
325 let data: &[u8] = &[
327 0x0, 0x73, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
328 ];
329 let hist: HistoricalBytes = data.try_into()?;
330
331 assert_eq!(
332 hist,
333 HistoricalBytes {
334 cib: 0,
335 csd: None,
336 cc: Some(CardCapabilities {
337 command_chaining: true,
338 extended_lc_le: false,
339 extended_length_information: false
340 }),
341 sib: 0
342 }
343 );
344
345 Ok(())
346 }
347
348 #[test]
349 fn test_ledger_nano_s() -> Result<(), Error> {
350 let data: &[u8] = &[
351 0x0, 0x31, 0xc5, 0x73, 0xc0, 0x1, 0x80, 0x7, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
352 ];
353 let hist: HistoricalBytes = data.try_into()?;
354
355 assert_eq!(
356 hist,
357 HistoricalBytes {
358 cib: 0,
359 csd: Some(CardServiceData {
360 select_by_full_df_name: true,
361 select_by_partial_df_name: true,
362 dos_available_in_ef_dir: false,
363 dos_available_in_ef_atr_info: false,
364 access_services: [false, true, false],
365 mf: true
366 }),
367 cc: Some(CardCapabilities {
368 command_chaining: true,
369 extended_lc_le: false,
370 extended_length_information: false
371 }),
372 sib: 5
373 }
374 );
375
376 Ok(())
377 }
378}