mirror of
https://github.com/LordGrimmauld/yubi-oath-rs.git
synced 2025-03-03 21:34:40 +01:00
clean up legacy
This commit is contained in:
parent
eeafc231be
commit
34f1c21bf0
6 changed files with 116 additions and 175 deletions
|
@ -55,7 +55,6 @@
|
|||
openssl.dev
|
||||
rust
|
||||
];
|
||||
shellHook = "ln -s ${rust} ./.direnv/rust";
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue