From 01485a31683d7e6a12d3d75a05f029f26b9d2573 Mon Sep 17 00:00:00 2001 From: Grimmauld Date: Sat, 8 Feb 2025 21:18:02 +0100 Subject: [PATCH] clean up tlv list parsing --- src/lib_ykoath2.rs | 120 +++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/src/lib_ykoath2.rs b/src/lib_ykoath2.rs index b13c642..0c77d39 100644 --- a/src/lib_ykoath2.rs +++ b/src/lib_ykoath2.rs @@ -8,6 +8,8 @@ use openssl::hash::MessageDigest; use ouroboros::self_referencing; use regex::Regex; use std::collections::HashMap; +use std::default; +use std::iter::zip; use std::str::{self}; use apdu_core::{Command, Response}; @@ -431,6 +433,9 @@ fn to_error_response(sw1: u8, sw2: u8) -> Option { match code { code if code == ErrorResponse::GenericError as usize => Some(String::from("Generic error")), code if code == ErrorResponse::NoSpace as usize => Some(String::from("No space on device")), + code if code == ErrorResponse::NoSuchObject as usize => { + Some(String::from("No such object")) + } code if code == ErrorResponse::CommandAborted as usize => { Some(String::from("Command was aborted")) } @@ -517,8 +522,10 @@ impl TransactionContext { pub struct OathSession<'a> { version: &'a [u8], + salt: &'a [u8], + challenge: &'a [u8], transaction_context: TransactionContext, - pub name: &'a str, + pub name: String, } fn clone_with_lifetime<'a>(data: &'a [u8]) -> Vec { @@ -527,7 +534,7 @@ fn clone_with_lifetime<'a>(data: &'a [u8]) -> Vec { } impl<'a> OathSession<'a> { - pub fn new(name: &'a str) -> Self { + pub fn new(name: &str) -> Self { let transaction_context = TransactionContext::from_name(name); let info_buffer = transaction_context .apdu_read_all(0, INS_SELECT, 0x04, 0, Some(&OATH_AID)) @@ -544,7 +551,15 @@ impl<'a> OathSession<'a> { info_map.get(&(Tag::Version as u8)).unwrap_or(&vec![0u8; 0]), ) .leak(), - name, + salt: clone_with_lifetime(info_map.get(&(Tag::Name as u8)).unwrap_or(&vec![0u8; 0])) + .leak(), + challenge: clone_with_lifetime( + info_map + .get(&(Tag::Challenge as u8)) + .unwrap_or(&vec![0u8; 0]), + ) + .leak(), + name: name.to_string(), transaction_context, } } @@ -567,76 +582,35 @@ impl<'a> OathSession<'a> { )), ); - self.parse_list(&response?) - } - /// Accepts a raw byte buffer payload and parses it - pub fn parse_list(&self, b: &[u8]) -> Result, String> { - let mut rdr = Cursor::new(b); - let mut results = Vec::new(); + let mut key_buffer = Vec::new(); - loop { - if let Err(_) = rdr.read_u8() { - break; + let info_map = tlv_to_lists(response?); + for (name_bytes, metadata) in zip( + info_map.get(&(Tag::Name as u8)).unwrap(), // todo: error handling + info_map.get(&(Tag::TruncatedResponse as u8)).unwrap(), + ) { + assert!(metadata.len() == 5); + let name = str::from_utf8(&name_bytes).unwrap(); + let k_len = metadata.get(0).unwrap(); + let k_len_enum = match *k_len { + 6u8 => OathDigits::Six, + 8u8 => OathDigits::Eight, + _ => continue, }; - let mut len: u16 = match rdr.read_u8() { - Ok(len) => len as u16, - Err(_) => break, - }; + let code = BigEndian::read_u32(&metadata[1..5]); - if len > 0x80 { - let n_bytes = len - 0x80; - - if n_bytes == 1 { - len = match rdr.read_u8() { - Ok(len) => len as u16, - Err(_) => break, - }; - } else if n_bytes == 2 { - len = match rdr.read_u16::() { - Ok(len) => len, - Err(_) => break, - }; - } - } - - let mut name = Vec::with_capacity(len as usize); - - unsafe { - name.set_len(len as usize); - } - - if let Err(_) = rdr.read_exact(&mut name) { - break; - }; - - rdr.read_u8().unwrap(); // TODO: Don't discard the response tag - rdr.read_u8().unwrap(); // TODO: Don't discard the response lenght + 1 - - let digits = match rdr.read_u8() { - Ok(6) => OathDigits::Six, - Ok(8) => OathDigits::Eight, - Ok(_) => break, - Err(_) => break, - }; - - let value = match rdr.read_u32::() { - Ok(val) => val, - Err(_) => break, - }; - - results.push(LegacyOathCredential::new( - &String::from_utf8(name).unwrap(), + key_buffer.push(LegacyOathCredential::new( + name, OathCode { - digits, - value, + digits: k_len_enum, + value: code, valid_from: 0, valid_to: 0x7FFFFFFFFFFFFFFF, }, )); } - - Ok(results) + return Ok(key_buffer); } } @@ -681,6 +655,26 @@ fn tlv_to_map(data: Vec) -> HashMap> { if let Ok(res) = r { parsed_manual.insert(res.tag().into(), res.value().to_vec()); } else { + println!("tlv parsing error"); + break; // Exit if parsing fails + } + } + return parsed_manual; +} + +fn tlv_to_lists(data: Vec) -> HashMap>> { + let mut buf: &[u8] = data.leak(); + let mut parsed_manual: HashMap>> = HashMap::new(); + while !buf.is_empty() { + let (r, remaining) = Tlv::parse(buf); + buf = remaining; + if let Ok(res) = r { + parsed_manual + .entry(res.tag().into()) + .or_insert_with(Vec::new) + .push(res.value().to_vec()); + } else { + println!("tlv parsing error"); break; // Exit if parsing fails } }