clean up legacy

This commit is contained in:
Grimmauld 2025-02-10 10:58:54 +01:00
parent eeafc231be
commit 34f1c21bf0
No known key found for this signature in database
6 changed files with 116 additions and 175 deletions

View file

@ -55,7 +55,6 @@
openssl.dev openssl.dev
rust rust
]; ];
shellHook = "ln -s ${rust} ./.direnv/rust";
}; };
}); });

View file

@ -83,7 +83,7 @@ impl HashAlgo {
} }
} }
#[derive(Debug, PartialEq, Copy, Clone, Eq)] #[derive(Debug, PartialEq, Copy, Clone, Eq, Hash)]
#[repr(u8)] #[repr(u8)]
pub enum OathType { pub enum OathType {
Totp = 0x10, Totp = 0x10,
@ -96,7 +96,7 @@ pub enum OathDigits {
Eight = 8, Eight = 8,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Hash, Eq)]
pub struct OathCodeDisplay { pub struct OathCodeDisplay {
code: u32, code: u32,
digits: u8, digits: u8,

View file

@ -1,76 +0,0 @@
#[crate_type = "lib"]
extern crate byteorder;
use crate::lib_ykoath2::*;
/// Utilities for interacting with YubiKey OATH/TOTP functionality
extern crate pcsc;
use base32::Alphabet;
use iso7816_tlv::simple::{Tag as TlvTag, Tlv};
use openssl::hash::MessageDigest;
use sha1::Sha1;
use ouroboros::self_referencing;
use regex::Regex;
use std::collections::HashMap;
use std::iter::zip;
use std::str::{self};
use apdu_core::{Command, Response};
use base64::{engine::general_purpose, Engine as _};
use hmac::{Hmac, Mac};
use openssl::pkcs5::pbkdf2_hmac;
use pcsc::{Card, Transaction};
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use std::ffi::CString;
use std::time::SystemTime;
#[derive(Debug, PartialEq)]
pub struct LegacyOathCredential {
pub name: String,
pub code: OathCode,
// TODO: Support this stuff
// pub oath_type: OathType,
// pub touch: bool,
// pub algo: OathAlgo,
// pub hidden: bool,
// pub steam: bool,
}
impl LegacyOathCredential {
pub fn new(name: &str, code: OathCode) -> LegacyOathCredential {
LegacyOathCredential {
name: name.to_string(),
code: code,
// oath_type: oath_type,
// touch: touch,
// algo: algo,
// hidden: name.starts_with("_hidden:"),
// steam: name.starts_with("Steam:"),
}
}
}
pub fn legacy_format_code(code: u32, digits: OathDigits) -> String {
let mut code_string = code.to_string();
match digits {
OathDigits::Six => {
if code_string.len() <= 6 {
format!("{:0>6}", code_string)
} else {
code_string.split_off(code_string.len() - 6)
}
}
OathDigits::Eight => {
if code_string.len() <= 8 {
format!("{:0>8}", code_string)
} else {
code_string.split_off(code_string.len() - 8)
}
}
}
}

View file

@ -3,15 +3,12 @@ mod constants;
use constants::*; use constants::*;
mod transaction; mod transaction;
use transaction::*; use transaction::*;
mod legacy;
pub use legacy::*;
/// Utilities for interacting with YubiKey OATH/TOTP functionality /// Utilities for interacting with YubiKey OATH/TOTP functionality
extern crate pcsc; extern crate pcsc;
use base32::Alphabet; use base32::Alphabet;
use openssl::hash::MessageDigest; use openssl::hash::MessageDigest;
use sha1::Sha1; use sha1::Sha1;
use std::iter::zip;
use std::str::{self}; use std::str::{self};
use base64::{engine::general_purpose, Engine as _}; use base64::{engine::general_purpose, Engine as _};
@ -21,7 +18,7 @@ use openssl::pkcs5::pbkdf2_hmac;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use byteorder::{BigEndian, WriteBytesExt};
use std::time::SystemTime; use std::time::SystemTime;
pub struct YubiKey<'a> { pub struct YubiKey<'a> {
@ -71,31 +68,25 @@ pub struct OathCode {
pub struct OathCredential<'a> { pub struct OathCredential<'a> {
device_id: &'a str, device_id: &'a str,
id: Vec<u8>, id: Vec<u8>,
issuer: Option<&'a str>, issuer: Option<String>,
name: &'a str, name: String,
oath_type: OathType, oath_type: OathType,
period: u64, period: u64,
touch_required: Option<bool>, touch_required: bool,
// TODO: Support this stuff pub code: Option<OathCodeDisplay>,
// pub oath_type: OathType,
// pub touch: bool,
// pub algo: OathAlgo,
// pub hidden: bool,
// pub steam: bool,
} }
impl<'a> OathCredential<'a> { impl<'a> OathCredential<'a> {
/* pub fn new(name: &str, code: OathCode) -> OathCredential { pub fn display(&self) -> String {
OathCredential { format!(
name, "{}: {}",
code, self.name,
// oath_type: oath_type, self.code
// touch: touch, .as_ref()
// algo: algo, .map(OathCodeDisplay::display)
// hidden: name.starts_with("_hidden:"), .unwrap_or("".to_string())
// steam: name.starts_with("Steam:"), )
} }
} */
} }
impl<'a> PartialOrd for OathCredential<'a> { impl<'a> PartialOrd for OathCredential<'a> {
@ -103,7 +94,7 @@ impl<'a> PartialOrd for OathCredential<'a> {
let a = ( let a = (
self.issuer self.issuer
.clone() .clone()
.unwrap_or_else(|| self.name) .unwrap_or_else(|| self.name.clone())
.to_lowercase(), .to_lowercase(),
self.name.to_lowercase(), self.name.to_lowercase(),
); );
@ -111,7 +102,7 @@ impl<'a> PartialOrd for OathCredential<'a> {
other other
.issuer .issuer
.clone() .clone()
.unwrap_or_else(|| other.name) .unwrap_or_else(|| other.name.clone())
.to_lowercase(), .to_lowercase(),
other.name.to_lowercase(), other.name.to_lowercase(),
); );
@ -148,7 +139,7 @@ fn _format_cred_id(issuer: Option<&str>, name: &str, oath_type: OathType, period
} }
// Function to parse the credential ID // Function to parse the credential ID
fn _parse_cred_id(cred_id: &[u8], oath_type: OathType) -> (Option<String>, String, u32) { fn _parse_cred_id(cred_id: &[u8], oath_type: OathType) -> (Option<String>, String, u64) {
let data = match str::from_utf8(cred_id) { let data = match str::from_utf8(cred_id) {
Ok(d) => d.to_string(), Ok(d) => d.to_string(),
Err(_) => return (None, String::new(), 0), // Handle invalid UTF-8 Err(_) => return (None, String::new(), 0), // Handle invalid UTF-8
@ -163,9 +154,13 @@ fn _parse_cred_id(cred_id: &[u8], oath_type: OathType) -> (Option<String>, Strin
DEFAULT_PERIOD DEFAULT_PERIOD
}; };
return (Some(caps[4].to_string()), caps[5].to_string(), period); return (
Some(caps[4].to_string()),
caps[5].to_string(),
period.into(),
);
} else { } else {
return (None, data, DEFAULT_PERIOD); return (None, data, DEFAULT_PERIOD.into());
} }
} else { } else {
let (issuer, rest) = if let Some(pos) = data.find(':') { let (issuer, rest) = if let Some(pos) = data.find(':') {
@ -302,7 +297,7 @@ impl<'a> OathSession<'a> {
} }
/// Read the OATH codes from the device /// Read the OATH codes from the device
pub fn get_oath_codes(&self) -> Result<Vec<LegacyOathCredential>, String> { pub fn get_oath_codes(&self) -> Result<Vec<OathCredential>, String> {
// Request OATH codes from device // Request OATH codes from device
let response = self.transaction_context.apdu_read_all( let response = self.transaction_context.apdu_read_all(
0, 0,
@ -317,23 +312,34 @@ impl<'a> OathSession<'a> {
let mut key_buffer = Vec::new(); let mut key_buffer = Vec::new();
let info_map = tlv_to_lists(response?); for (cred_id, meta) in TlvZipIter::from_vec(response?) {
for (name_bytes, metadata) in zip( // let name = str::from_utf8(&cred_id.value()).unwrap();
info_map.get(&(Tag::Name as u8)).unwrap(), // todo: error handling let oath_type = if Into::<u8>::into(meta.tag()) == (Tag::Hotp as u8) {
info_map.get(&(Tag::TruncatedResponse as u8)).unwrap(), OathType::Hotp
) { } else {
assert!(metadata.len() == 5); OathType::Totp
let display = OathCodeDisplay::new(metadata[..].try_into().unwrap()); };
let name = str::from_utf8(&name_bytes).unwrap(); let touch = Into::<u8>::into(meta.tag()) == (Tag::Touch as u8); // touch only works with totp, this is intended
key_buffer.push(LegacyOathCredential::new( let (issuer, name, period) = _parse_cred_id(cred_id.value(), oath_type);
let cred = OathCredential {
device_id: &self.name,
id: meta.value().to_vec(),
issuer,
name, name,
OathCode { period,
display, touch_required: touch,
valid_from: 0, oath_type,
valid_to: 0x7FFFFFFFFFFFFFFF, code: if Into::<u8>::into(meta.tag()) == (Tag::TruncatedResponse as u8) {
assert!(meta.value().len() == 5);
let display = OathCodeDisplay::new(meta.value()[..].try_into().unwrap());
Some(display)
} else {
None
}, },
)); };
key_buffer.push(cred);
} }
return Ok(key_buffer); return Ok(key_buffer);
} }
} }

View file

@ -3,30 +3,16 @@ extern crate byteorder;
use crate::lib_ykoath2::*; use crate::lib_ykoath2::*;
/// Utilities for interacting with YubiKey OATH/TOTP functionality /// Utilities for interacting with YubiKey OATH/TOTP functionality
extern crate pcsc; extern crate pcsc;
use base32::Alphabet;
use iso7816_tlv::simple::{Tag as TlvTag, Tlv}; use iso7816_tlv::simple::{Tag as TlvTag, Tlv};
use openssl::hash::MessageDigest;
use sha1::Sha1;
use ouroboros::self_referencing; use ouroboros::self_referencing;
use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::zip;
use std::str::{self}; use std::str::{self};
use apdu_core::{Command, Response}; use apdu_core::{Command, Response};
use base64::{engine::general_purpose, Engine as _};
use hmac::{Hmac, Mac};
use openssl::pkcs5::pbkdf2_hmac;
use pcsc::{Card, Transaction}; use pcsc::{Card, Transaction};
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use std::ffi::CString; use std::ffi::CString;
use std::time::SystemTime;
pub struct ApduResponse { pub struct ApduResponse {
pub buf: Vec<u8>, pub buf: Vec<u8>,
@ -192,36 +178,75 @@ pub fn to_tlv(tag: Tag, value: &[u8]) -> Vec<u8> {
} }
pub fn tlv_to_map(data: Vec<u8>) -> HashMap<u8, Vec<u8>> { pub fn tlv_to_map(data: Vec<u8>) -> HashMap<u8, Vec<u8>> {
let mut buf: &[u8] = data.leak();
let mut parsed_manual = HashMap::new(); let mut parsed_manual = HashMap::new();
while !buf.is_empty() { for res in TlvIter::from_vec(data) {
let (r, remaining) = Tlv::parse(buf); parsed_manual.insert(res.tag().into(), res.value().to_vec());
buf = remaining;
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; return parsed_manual;
} }
pub fn tlv_to_lists(data: Vec<u8>) -> HashMap<u8, Vec<Vec<u8>>> { pub struct TlvZipIter<'a> {
let mut buf: &[u8] = data.leak(); iter: TlvIter<'a>,
let mut parsed_manual: HashMap<u8, Vec<Vec<u8>>> = HashMap::new(); }
while !buf.is_empty() {
let (r, remaining) = Tlv::parse(buf); impl<'a> TlvZipIter<'a> {
buf = remaining; pub fn new(value: &'a [u8]) -> Self {
if let Ok(res) = r { TlvZipIter {
parsed_manual iter: TlvIter::new(value).into_iter(),
.entry(res.tag().into())
.or_insert_with(Vec::new)
.push(res.value().to_vec());
} else {
println!("tlv parsing error");
break; // Exit if parsing fails
} }
} }
pub fn from_vec(value: Vec<u8>) -> Self {
TlvZipIter {
iter: TlvIter::from_vec(value).into_iter(),
}
}
pub fn from_tlv_iter(value: TlvIter<'a>) -> Self {
TlvZipIter { iter: value }
}
}
impl<'a> Iterator for TlvZipIter<'a> {
type Item = (Tlv, Tlv);
fn next(&mut self) -> Option<Self::Item> {
return Some((self.iter.next()?, self.iter.next()?));
}
}
#[derive(Copy, Clone)]
pub struct TlvIter<'a> {
buf: &'a [u8],
}
impl<'a> TlvIter<'a> {
pub fn new(value: &'a [u8]) -> Self {
TlvIter { buf: value }
}
pub fn from_vec(value: Vec<u8>) -> Self {
TlvIter { buf: value.leak() }
}
}
impl<'a> Iterator for TlvIter<'a> {
type Item = Tlv;
fn next(&mut self) -> Option<Self::Item> {
if self.buf.is_empty() {
return None;
}
let (r, remaining) = Tlv::parse(self.buf);
self.buf = remaining;
return r.ok();
}
}
pub fn tlv_to_lists(data: Vec<u8>) -> HashMap<u8, Vec<Vec<u8>>> {
let mut parsed_manual: HashMap<u8, Vec<Vec<u8>>> = HashMap::new();
for res in TlvIter::from_vec(data) {
parsed_manual
.entry(res.tag().into())
.or_insert_with(Vec::new)
.push(res.value().to_vec());
}
return parsed_manual; return parsed_manual;
} }

View file

@ -52,20 +52,7 @@ fn main() {
// Enumerate the OATH codes // Enumerate the OATH codes
for oath in codes { for oath in codes {
let code = oath.code.display.display(); println!("Found OATH label: {}", oath.display());
let name_clone = oath.name.clone();
let mut label_vec: Vec<&str> = name_clone.split(":").collect();
let mut code_entry_label: String = String::from(label_vec.remove(0));
if label_vec.len() > 0 {
code_entry_label.push_str(" (");
code_entry_label.push_str(&label_vec.join(""));
code_entry_label.push_str(") ");
}
code_entry_label.push_str(&code.clone().to_owned());
println!("Found OATH label: {}", code_entry_label);
} }
} }
} }