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
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)]
pub enum OathType {
Totp = 0x10,
@ -96,7 +96,7 @@ pub enum OathDigits {
Eight = 8,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Hash, Eq)]
pub struct OathCodeDisplay {
code: u32,
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::*;
mod transaction;
use transaction::*;
mod legacy;
pub use legacy::*;
/// Utilities for interacting with YubiKey OATH/TOTP functionality
extern crate pcsc;
use base32::Alphabet;
use openssl::hash::MessageDigest;
use sha1::Sha1;
use std::iter::zip;
use std::str::{self};
use base64::{engine::general_purpose, Engine as _};
@ -21,7 +18,7 @@ use openssl::pkcs5::pbkdf2_hmac;
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
use byteorder::{BigEndian, WriteBytesExt};
use std::time::SystemTime;
pub struct YubiKey<'a> {
@ -71,31 +68,25 @@ pub struct OathCode {
pub struct OathCredential<'a> {
device_id: &'a str,
id: Vec<u8>,
issuer: Option<&'a str>,
name: &'a str,
issuer: Option<String>,
name: String,
oath_type: OathType,
period: u64,
touch_required: Option<bool>,
// TODO: Support this stuff
// pub oath_type: OathType,
// pub touch: bool,
// pub algo: OathAlgo,
// pub hidden: bool,
// pub steam: bool,
touch_required: bool,
pub code: Option<OathCodeDisplay>,
}
impl<'a> OathCredential<'a> {
/* pub fn new(name: &str, code: OathCode) -> OathCredential {
OathCredential {
name,
code,
// oath_type: oath_type,
// touch: touch,
// algo: algo,
// hidden: name.starts_with("_hidden:"),
// steam: name.starts_with("Steam:"),
}
} */
pub fn display(&self) -> String {
format!(
"{}: {}",
self.name,
self.code
.as_ref()
.map(OathCodeDisplay::display)
.unwrap_or("".to_string())
)
}
}
impl<'a> PartialOrd for OathCredential<'a> {
@ -103,7 +94,7 @@ impl<'a> PartialOrd for OathCredential<'a> {
let a = (
self.issuer
.clone()
.unwrap_or_else(|| self.name)
.unwrap_or_else(|| self.name.clone())
.to_lowercase(),
self.name.to_lowercase(),
);
@ -111,7 +102,7 @@ impl<'a> PartialOrd for OathCredential<'a> {
other
.issuer
.clone()
.unwrap_or_else(|| other.name)
.unwrap_or_else(|| other.name.clone())
.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
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) {
Ok(d) => d.to_string(),
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
};
return (Some(caps[4].to_string()), caps[5].to_string(), period);
return (
Some(caps[4].to_string()),
caps[5].to_string(),
period.into(),
);
} else {
return (None, data, DEFAULT_PERIOD);
return (None, data, DEFAULT_PERIOD.into());
}
} else {
let (issuer, rest) = if let Some(pos) = data.find(':') {
@ -302,7 +297,7 @@ impl<'a> OathSession<'a> {
}
/// 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
let response = self.transaction_context.apdu_read_all(
0,
@ -317,23 +312,34 @@ impl<'a> OathSession<'a> {
let mut key_buffer = Vec::new();
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 display = OathCodeDisplay::new(metadata[..].try_into().unwrap());
let name = str::from_utf8(&name_bytes).unwrap();
key_buffer.push(LegacyOathCredential::new(
for (cred_id, meta) in TlvZipIter::from_vec(response?) {
// let name = str::from_utf8(&cred_id.value()).unwrap();
let oath_type = if Into::<u8>::into(meta.tag()) == (Tag::Hotp as u8) {
OathType::Hotp
} else {
OathType::Totp
};
let touch = Into::<u8>::into(meta.tag()) == (Tag::Touch as u8); // touch only works with totp, this is intended
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,
OathCode {
display,
valid_from: 0,
valid_to: 0x7FFFFFFFFFFFFFFF,
period,
touch_required: touch,
oath_type,
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);
}
}

View file

@ -3,30 +3,16 @@ 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;
pub struct ApduResponse {
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>> {
let mut buf: &[u8] = data.leak();
let mut parsed_manual = HashMap::new();
while !buf.is_empty() {
let (r, remaining) = Tlv::parse(buf);
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
}
for res in TlvIter::from_vec(data) {
parsed_manual.insert(res.tag().into(), res.value().to_vec());
}
return parsed_manual;
}
pub fn tlv_to_lists(data: Vec<u8>) -> HashMap<u8, Vec<Vec<u8>>> {
let mut buf: &[u8] = data.leak();
let mut parsed_manual: HashMap<u8, Vec<Vec<u8>>> = 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
pub struct TlvZipIter<'a> {
iter: TlvIter<'a>,
}
impl<'a> TlvZipIter<'a> {
pub fn new(value: &'a [u8]) -> Self {
TlvZipIter {
iter: TlvIter::new(value).into_iter(),
}
}
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;
}

View file

@ -52,20 +52,7 @@ fn main() {
// Enumerate the OATH codes
for oath in codes {
let code = oath.code.display.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);
println!("Found OATH label: {}", oath.display());
}
}
}