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
|
openssl.dev
|
||||||
rust
|
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)]
|
#[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,
|
||||||
|
|
|
@ -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::*;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue