mirror of
https://github.com/LordGrimmauld/yubi-oath-rs.git
synced 2025-03-04 05:44:40 +01:00
Add basic cli infrastructure
This commit is contained in:
parent
1423a48620
commit
ac58bd5cf2
12 changed files with 337 additions and 21 deletions
146
Cargo.lock
generated
146
Cargo.lock
generated
|
@ -17,6 +17,56 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"once_cell",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "apdu-core"
|
name = "apdu-core"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -44,6 +94,61 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.5.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cli"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"pcsc",
|
||||||
|
"ykoath2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
@ -102,6 +207,12 @@ version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -111,6 +222,12 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iso7816-tlv"
|
name = "iso7816-tlv"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
@ -132,6 +249,12 @@ version = "2.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ouroboros"
|
name = "ouroboros"
|
||||||
version = "0.18.5"
|
version = "0.18.5"
|
||||||
|
@ -149,7 +272,7 @@ version = "0.18.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
|
checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"proc-macro2-diagnostics",
|
"proc-macro2-diagnostics",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -280,6 +403,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
|
@ -315,6 +444,12 @@ version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
|
@ -330,6 +465,15 @@ dependencies = [
|
||||||
"wit-bindgen-rt",
|
"wit-bindgen-rt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
|
27
Cargo.toml
27
Cargo.toml
|
@ -1,23 +1,10 @@
|
||||||
[dependencies]
|
[workspace]
|
||||||
apdu-core = "0.4.0"
|
resolver = "2"
|
||||||
getrandom = "0.3.1"
|
|
||||||
hmac = "0.12.1"
|
|
||||||
iso7816-tlv = "0.4.4"
|
|
||||||
ouroboros = "0.18.5"
|
|
||||||
pbkdf2 = {version = "0.12.2", features = ["sha1"]}
|
|
||||||
pcsc = "2.9.0"
|
|
||||||
regex = "1.11.1"
|
|
||||||
sha1 = "0.10.6"
|
|
||||||
sha2 = "0.10.8"
|
|
||||||
|
|
||||||
[[example]]
|
members = [
|
||||||
name = "example"
|
"lib",
|
||||||
path = "./src/example.rs"
|
"cli",
|
||||||
|
]
|
||||||
|
|
||||||
[package]
|
[workspace.package]
|
||||||
name = "ykoath2"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Grimmauld <grimmauld@grimmauld.de>"]
|
|
||||||
description = "experiments with smartcards"
|
|
||||||
license-file = "LICENSE"
|
|
||||||
|
|
8
cli/Cargo.toml
Normal file
8
cli/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "cli"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5", features = [ "cargo", "derive" ] }
|
||||||
|
pcsc = "2.9.0"
|
||||||
|
ykoath2 = {path = "../lib"}
|
140
cli/src/main.rs
Normal file
140
cli/src/main.rs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
use ykoath2::{
|
||||||
|
constants::OathType, oath_credential::OathCredential, oath_credential_id::CredentialIDData,
|
||||||
|
OathSession,
|
||||||
|
};
|
||||||
|
// use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Commands {
|
||||||
|
#[command(name = "store", about = "Store a credential")]
|
||||||
|
Store {
|
||||||
|
#[arg(help = "Credential name")]
|
||||||
|
name: String,
|
||||||
|
#[arg(help = "Credential type: Time-based or counter-based")]
|
||||||
|
oath_type: String,
|
||||||
|
#[arg(help = "Credential issuer")]
|
||||||
|
issuer: Option<String>,
|
||||||
|
#[arg(help = "Credential refresh period if it is time-based")]
|
||||||
|
period: Option<u8>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[command(name = "tokens", about = "List all credentials for a device")]
|
||||||
|
Tokens,
|
||||||
|
|
||||||
|
#[command(name = "list", about = "List all connected devices")]
|
||||||
|
List,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[clap(version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Commands,
|
||||||
|
#[command(flatten)]
|
||||||
|
args: Arguments,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug)]
|
||||||
|
struct Arguments {
|
||||||
|
#[arg(name = "device", short, long, global = true, help = "Yubikey device")]
|
||||||
|
device: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
// Create a HID API context for detecting devices
|
||||||
|
let context = pcsc::Context::establish(pcsc::Scope::User).unwrap();
|
||||||
|
let mut readers_buf = [0; 2048];
|
||||||
|
let devices = context
|
||||||
|
.list_readers(&mut readers_buf)
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| r.to_str().unwrap())
|
||||||
|
.collect::<Vec<&str>>();
|
||||||
|
|
||||||
|
// Show message if no YubiKey(s)
|
||||||
|
if devices.is_empty() {
|
||||||
|
println!("No yubikeys detected");
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
match cli.command {
|
||||||
|
Commands::Tokens => {
|
||||||
|
let Some(selected_device) = cli.args.device else {
|
||||||
|
println!("A device is required to store a credential.");
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
if devices.iter().find(|d| **d == selected_device).is_none() {
|
||||||
|
println!("{selected_device} was not found.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
let session = OathSession::new(&selected_device).unwrap();
|
||||||
|
for code in session.list_oath_codes().unwrap() {
|
||||||
|
let oath_type = if code.oath_type() == OathType::Hotp {
|
||||||
|
"hotp"
|
||||||
|
} else {
|
||||||
|
"totp"
|
||||||
|
};
|
||||||
|
println!(
|
||||||
|
"Name: {}, Issuer: {}, Type: {}",
|
||||||
|
code.name(),
|
||||||
|
code.issuer().unwrap_or_default(),
|
||||||
|
oath_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::Store {
|
||||||
|
name,
|
||||||
|
oath_type,
|
||||||
|
issuer,
|
||||||
|
period,
|
||||||
|
} => {
|
||||||
|
let Some(selected_device) = cli.args.device else {
|
||||||
|
println!("A device is required to store a credential.");
|
||||||
|
std::process::exit(1);
|
||||||
|
};
|
||||||
|
if devices.iter().find(|d| **d == selected_device).is_none() {
|
||||||
|
println!("{selected_device} was not found.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
let session = OathSession::new(&selected_device).unwrap();
|
||||||
|
session
|
||||||
|
.put_credential(
|
||||||
|
OathCredential::new(
|
||||||
|
&selected_device,
|
||||||
|
CredentialIDData::new(
|
||||||
|
&name,
|
||||||
|
ykoath2::constants::OathType::Totp,
|
||||||
|
issuer.as_deref(),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
b"some secret",
|
||||||
|
ykoath2::constants::HashAlgo::Sha256,
|
||||||
|
6,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Commands::List => {
|
||||||
|
// Print device info for all the YubiKeys we detected
|
||||||
|
for device in devices {
|
||||||
|
let session = OathSession::new(device).unwrap();
|
||||||
|
println!("Device: {device}.");
|
||||||
|
println!(
|
||||||
|
"Version: {:#?}.",
|
||||||
|
session
|
||||||
|
.version()
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(".")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
lib/Cargo.toml
Normal file
23
lib/Cargo.toml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[dependencies]
|
||||||
|
apdu-core = "0.4.0"
|
||||||
|
getrandom = "0.3.1"
|
||||||
|
hmac = "0.12.1"
|
||||||
|
iso7816-tlv = "0.4.4"
|
||||||
|
ouroboros = "0.18.5"
|
||||||
|
pbkdf2 = {version = "0.12.2", features = ["sha1"]}
|
||||||
|
pcsc = "2.9.0"
|
||||||
|
regex = "1.11.1"
|
||||||
|
sha1 = "0.10.6"
|
||||||
|
sha2 = "0.10.8"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "example"
|
||||||
|
path = "./src/example.rs"
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "ykoath2"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Grimmauld <grimmauld@grimmauld.de>"]
|
||||||
|
description = "experiments with smartcards"
|
||||||
|
license-file = "LICENSE"
|
|
@ -38,6 +38,20 @@ impl Display for CredentialIDData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CredentialIDData {
|
impl CredentialIDData {
|
||||||
|
pub fn new(
|
||||||
|
name: &str,
|
||||||
|
oath_type: OathType,
|
||||||
|
issuer: Option<&str>,
|
||||||
|
period: Option<Duration>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_owned(),
|
||||||
|
oath_type,
|
||||||
|
issuer: issuer.map(ToOwned::to_owned),
|
||||||
|
period,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// reads id data from tlv data
|
/// reads id data from tlv data
|
||||||
/// `id_bytes` refers to the byte buffer containing issuer, name and period
|
/// `id_bytes` refers to the byte buffer containing issuer, name and period
|
||||||
/// `oath_type_tag` refers to the tlv tag containing the oath type information
|
/// `oath_type_tag` refers to the tlv tag containing the oath type information
|
Loading…
Add table
Reference in a new issue