better formatting, listing codes without calculate

This commit is contained in:
Grimmauld 2025-02-12 18:18:33 +01:00
parent 0c877a859a
commit 85b8ccea35
No known key found for this signature in database
4 changed files with 87 additions and 48 deletions

View file

@ -1,3 +1,5 @@
use std::fmt::Display;
use iso7816_tlv::simple::Tlv;
use sha1::{Digest, Sha1};
use sha2::{Sha256, Sha512};
@ -153,12 +155,18 @@ pub enum OathDigits {
Eight = 8,
}
#[derive(Debug, PartialEq, Hash, Eq)]
#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
pub struct OathCodeDisplay {
code: u32,
digits: u8,
}
impl Display for OathCodeDisplay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{:01$}", self.code, usize::from(self.digits)))
}
}
impl OathCodeDisplay {
pub fn from_tlv(tlv: Tlv) -> Option<Self> {
if Into::<u8>::into(tlv.tag()) == (Tag::TruncatedResponse as u8) && tlv.value().len() == 5 {
@ -183,8 +191,4 @@ impl OathCodeDisplay {
code: u32::from_be_bytes((&bytes[1..5]).try_into().unwrap()),
}
}
pub fn display(&self) -> String {
format!("{:01$}", self.code, usize::from(self.digits))
}
}

View file

@ -12,6 +12,7 @@ use pbkdf2::pbkdf2_hmac_array;
use sha1::Sha1;
use std::{
fmt::Display,
str::{self},
time::Duration,
};
@ -79,6 +80,16 @@ pub struct RefreshableOathCredential<'a> {
refresh_provider: &'a OathSession<'a>,
}
impl<'a> Display for RefreshableOathCredential<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(c) = self.code {
f.write_fmt(format_args!("{}: {}", self.cred.id_data, c))
} else {
f.write_fmt(format_args!("{}", self.cred.id_data))
}
}
}
impl<'a> RefreshableOathCredential<'a> {
pub fn new(cred: OathCredential, refresh_provider: &'a OathSession<'a>) -> Self {
RefreshableOathCredential {
@ -96,17 +107,6 @@ impl<'a> RefreshableOathCredential<'a> {
RefreshableOathCredential::format_validity_time_frame(&self, timestamp);
}
pub fn display(&self) -> String {
format!(
"{}: {}",
self.cred.id_data.name,
self.code
.as_ref()
.map(OathCodeDisplay::display)
.unwrap_or("".to_string())
)
}
pub fn refresh(&mut self) {
let timestamp = SystemTime::now();
let refresh_result = self
@ -177,6 +177,19 @@ impl<'a> OathSession<'a> {
self.version
}
pub fn delete_code(
&self,
cred: OathCredential,
) -> Result<ApduResponse, FormattableErrorResponse> {
self.transaction_context.apdu(
0,
Instruction::Delete as u8,
0,
0,
Some(&to_tlv(Tag::Name, &cred.id_data.format_cred_id())),
)
}
pub fn calculate_code(
&self,
cred: OathCredential,
@ -216,8 +229,8 @@ impl<'a> OathSession<'a> {
))
}
/// Read the OATH codes from the device
pub fn get_oath_codes(
/// Read the OATH codes from the device, calculate TOTP codes that don't need touch
pub fn calculate_oath_codes(
&self,
) -> Result<Vec<RefreshableOathCredential>, FormattableErrorResponse> {
let timestamp = SystemTime::now();
@ -252,6 +265,24 @@ impl<'a> OathSession<'a> {
key_buffer.push(refreshable_cred);
}
return Ok(key_buffer);
}
pub fn list_oath_codes(&self) -> Result<Vec<CredentialIDData>, FormattableErrorResponse> {
// Request OATH codes from device
let response =
self.transaction_context
.apdu_read_all(0, Instruction::List as u8, 0, 0, None);
let mut key_buffer = Vec::new();
for cred_id in TlvIter::from_vec(response?) {
let id_data = CredentialIDData::from_bytes(
&cred_id.value()[1..],
*cred_id.value().get(0).unwrap_or(&0u8) & 0xf0,
);
key_buffer.push(id_data);
}
return Ok(key_buffer);
}
}

View file

@ -4,7 +4,10 @@ use crate::lib_ykoath2::*;
extern crate pcsc;
use regex::Regex;
use std::str::{self};
use std::{
fmt::Write,
str::{self},
};
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct CredentialIDData {
@ -14,20 +17,18 @@ pub struct CredentialIDData {
pub period: u32,
}
impl Display for CredentialIDData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(i) = self.issuer.clone() {
f.write_fmt(format_args!("{}: ", i))?;
}
f.write_str(&self.name)
}
}
impl CredentialIDData {
pub fn from_tlv(id_bytes: &[u8], oath_type_tag: iso7816_tlv::simple::Tag) -> Self {
let oath_type = if Into::<u8>::into(oath_type_tag) == (Tag::Hotp as u8) {
OathType::Hotp
} else {
OathType::Totp
};
let (issuer, name, period) = CredentialIDData::parse_cred_id(id_bytes, oath_type);
return CredentialIDData {
issuer,
name,
period,
oath_type,
};
return CredentialIDData::from_bytes(id_bytes, Into::<u8>::into(oath_type_tag));
}
pub fn format_cred_id(&self) -> Vec<u8> {
@ -70,20 +71,19 @@ impl CredentialIDData {
});
}
}
}
pub struct CredentialData {
id_data: CredentialIDData,
hash_algorithm: HashAlgo,
// secret: bytes,
digits: OathDigits, // = DEFAULT_DIGITS,
counter: u32, // = DEFAULT_IMF,
}
impl CredentialData {
// TODO: parse_uri
pub fn get_id(&self) -> Vec<u8> {
return self.id_data.format_cred_id();
pub(crate) fn from_bytes(id_bytes: &[u8], tag: u8) -> CredentialIDData {
let oath_type = if tag == (Tag::Hotp as u8) {
OathType::Hotp
} else {
OathType::Totp
};
let (issuer, name, period) = CredentialIDData::parse_cred_id(id_bytes, oath_type);
return CredentialIDData {
issuer,
name,
period,
oath_type,
};
}
}

View file

@ -38,7 +38,11 @@ fn main() {
println!("Found device with label {}", device_label);
let session = OathSession::new(yubikey);
println!("YubiKey version is {:?}", session.get_version());
let codes = match session.get_oath_codes() {
for c in session.list_oath_codes().unwrap() {
println!("{}", c);
}
let codes = match session.calculate_oath_codes() {
Ok(codes) => codes,
Err(e) => {
println!("ERROR {}", e);
@ -51,12 +55,12 @@ fn main() {
println!("No credentials on device {}", device_label);
}
thread::sleep(time::Duration::from_secs(45)); // show refresh is working
thread::sleep(time::Duration::from_secs(5)); // show refresh is working
// Enumerate the OATH codes
for oath in codes {
// let recalculated = session.calculate_code(oath.cred, None).unwrap();
println!("Found OATH label: {}", oath.get_or_refresh().display());
println!("Found OATH label: {}", oath.get_or_refresh());
}
}
}