Merge pull request #72 from iqlusioninc/status-command
cli: add `status` command
This commit is contained in:
Generated
+10
@@ -710,6 +710,14 @@ name = "subtle"
|
|||||||
version = "2.2.2"
|
version = "2.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle-encoding"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@@ -856,6 +864,7 @@ dependencies = [
|
|||||||
"secrecy 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"secrecy 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"subtle-encoding 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"x509-parser 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"x509-parser 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@@ -972,6 +981,7 @@ dependencies = [
|
|||||||
"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||||
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
||||||
"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941"
|
"checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941"
|
||||||
|
"checksum subtle-encoding 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbc5188a16f729680b6d495b0deaa776944b8e509d24b7f989489b0d8bbcb63b"
|
||||||
"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
|
"checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238"
|
||||||
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||||
"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
|
"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ rsa = "0.1.4"
|
|||||||
secrecy = "0.5"
|
secrecy = "0.5"
|
||||||
sha-1 = "0.8"
|
sha-1 = "0.8"
|
||||||
subtle = "2"
|
subtle = "2"
|
||||||
|
subtle-encoding = "0.5"
|
||||||
x509-parser = "0.6"
|
x509-parser = "0.6"
|
||||||
zeroize = "1"
|
zeroize = "1"
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
use yubikey_cli::commands::YubikeyCli;
|
use yubikey_cli::commands::YubiKeyCli;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
YubikeyCli::parse_args_default_or_exit().run();
|
YubiKeyCli::parse_args_default_or_exit().run();
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-21
@@ -1,9 +1,10 @@
|
|||||||
//! Commands of the CLI application
|
//! Commands of the CLI application
|
||||||
|
|
||||||
pub mod readers;
|
pub mod readers;
|
||||||
|
pub mod status;
|
||||||
|
|
||||||
use self::readers::ReadersCmd;
|
use self::{readers::ReadersCmd, status::StatusCmd};
|
||||||
use crate::status::{self, STDOUT};
|
use crate::terminal::{self, STDOUT};
|
||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
@@ -11,36 +12,29 @@ use std::{
|
|||||||
process::exit,
|
process::exit,
|
||||||
};
|
};
|
||||||
use termcolor::{ColorChoice, ColorSpec, WriteColor};
|
use termcolor::{ColorChoice, ColorSpec, WriteColor};
|
||||||
|
use yubikey_piv::{Serial, YubiKey};
|
||||||
|
|
||||||
/// The `yubikey` CLI utility
|
/// The `yubikey` CLI utility
|
||||||
#[derive(Debug, Options)]
|
#[derive(Debug, Options)]
|
||||||
pub struct YubikeyCli {
|
pub struct YubiKeyCli {
|
||||||
/// Obtain help about the current command
|
/// Obtain help about the current command
|
||||||
#[options(short = "h", help = "print help message")]
|
#[options(short = "h", help = "print help message")]
|
||||||
pub help: bool,
|
pub help: bool,
|
||||||
|
|
||||||
|
/// Specify the serial number of the YubiKey to connect to
|
||||||
|
#[options(
|
||||||
|
short = "s",
|
||||||
|
long = "serial",
|
||||||
|
help = "serial number of the YubiKey to connect to"
|
||||||
|
)]
|
||||||
|
pub serial: Option<Serial>,
|
||||||
|
|
||||||
/// Subcommand to execute.
|
/// Subcommand to execute.
|
||||||
#[options(command)]
|
#[options(command)]
|
||||||
pub command: Option<Commands>,
|
pub command: Option<Commands>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl YubikeyCli {
|
impl YubiKeyCli {
|
||||||
/// Run the underlying command type or print usage info and exit
|
|
||||||
pub fn run(&self) {
|
|
||||||
// TODO(tarcieri): make this more configurable
|
|
||||||
status::set_color_choice(ColorChoice::Auto);
|
|
||||||
|
|
||||||
// Only show logs if `RUST_LOG` is set
|
|
||||||
if env::var("RUST_LOG").is_ok() {
|
|
||||||
env_logger::builder().format_timestamp(None).init();
|
|
||||||
}
|
|
||||||
|
|
||||||
match &self.command {
|
|
||||||
Some(cmd) => cmd.run(),
|
|
||||||
None => Self::print_usage().unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Print usage information
|
/// Print usage information
|
||||||
pub fn print_usage() -> Result<(), io::Error> {
|
pub fn print_usage() -> Result<(), io::Error> {
|
||||||
let mut stdout = STDOUT.lock();
|
let mut stdout = STDOUT.lock();
|
||||||
@@ -65,6 +59,36 @@ impl YubikeyCli {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run the underlying command type or print usage info and exit
|
||||||
|
pub fn run(&self) {
|
||||||
|
// TODO(tarcieri): make this more configurable
|
||||||
|
terminal::set_color_choice(ColorChoice::Auto);
|
||||||
|
|
||||||
|
// Only show logs if `RUST_LOG` is set
|
||||||
|
if env::var("RUST_LOG").is_ok() {
|
||||||
|
env_logger::builder().format_timestamp(None).init();
|
||||||
|
}
|
||||||
|
|
||||||
|
match &self.command {
|
||||||
|
Some(cmd) => cmd.run(self.yubikey_init()),
|
||||||
|
None => Self::print_usage().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the YubiKey client driver
|
||||||
|
fn yubikey_init(&self) -> YubiKey {
|
||||||
|
match self.serial {
|
||||||
|
Some(serial) => YubiKey::open_by_serial(serial).unwrap_or_else(|e| {
|
||||||
|
status_err!("couldn't open YubiKey (serial #{}): {}", serial, e);
|
||||||
|
exit(1);
|
||||||
|
}),
|
||||||
|
None => YubiKey::open().unwrap_or_else(|e| {
|
||||||
|
status_err!("couldn't open default YubiKey: {}", e);
|
||||||
|
exit(1);
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subcommands of this application
|
/// Subcommands of this application
|
||||||
@@ -81,15 +105,20 @@ pub enum Commands {
|
|||||||
/// `readers` subcommand
|
/// `readers` subcommand
|
||||||
#[options(help = "list detected readers")]
|
#[options(help = "list detected readers")]
|
||||||
Readers(ReadersCmd),
|
Readers(ReadersCmd),
|
||||||
|
|
||||||
|
/// `status` subcommand
|
||||||
|
#[options(help = "show yubikey status")]
|
||||||
|
Status(StatusCmd),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Commands {
|
impl Commands {
|
||||||
/// Run the given command
|
/// Run the given command
|
||||||
pub fn run(&self) {
|
pub fn run(&self, yubikey: YubiKey) {
|
||||||
match self {
|
match self {
|
||||||
Commands::Help(help) => help.run(),
|
Commands::Help(help) => help.run(),
|
||||||
Commands::Version(version) => version.run(),
|
Commands::Version(version) => version.run(),
|
||||||
Commands::Readers(list) => list.run(),
|
Commands::Readers(list) => list.run(),
|
||||||
|
Commands::Status(status) => status.run(yubikey),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
//! List detected readers
|
//! List detected readers
|
||||||
|
|
||||||
|
use crate::terminal::STDOUT;
|
||||||
use gumdrop::Options;
|
use gumdrop::Options;
|
||||||
use std::process::exit;
|
use std::{
|
||||||
use yubikey_piv::readers::Readers;
|
io::{self, Write},
|
||||||
|
process::exit,
|
||||||
|
};
|
||||||
|
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
||||||
|
use yubikey_piv::{Readers, Serial};
|
||||||
|
|
||||||
/// The `readers` subcommand
|
/// The `readers` subcommand
|
||||||
#[derive(Debug, Options)]
|
#[derive(Debug, Options)]
|
||||||
@@ -26,6 +31,9 @@ impl ReadersCmd {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut s = STDOUT.lock();
|
||||||
|
s.reset().unwrap();
|
||||||
|
|
||||||
for (i, reader) in readers_iter.enumerate() {
|
for (i, reader) in readers_iter.enumerate() {
|
||||||
let name = reader.name();
|
let name = reader.name();
|
||||||
let mut yubikey = match reader.open() {
|
let mut yubikey = match reader.open() {
|
||||||
@@ -34,7 +42,23 @@ impl ReadersCmd {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let serial = yubikey.serial();
|
let serial = yubikey.serial();
|
||||||
println!("{}: {} (serial: {})", i + 1, name, serial);
|
self.print_reader(&mut s, i + 1, &name, serial).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print a reader
|
||||||
|
fn print_reader(
|
||||||
|
&self,
|
||||||
|
stream: &mut StandardStreamLock<'_>,
|
||||||
|
index: usize,
|
||||||
|
name: &str,
|
||||||
|
serial: Serial,
|
||||||
|
) -> Result<(), io::Error> {
|
||||||
|
stream.set_color(ColorSpec::new().set_bold(true))?;
|
||||||
|
write!(stream, "{:>3}:", index)?;
|
||||||
|
stream.reset()?;
|
||||||
|
writeln!(stream, " {} (serial: {})", name, serial)?;
|
||||||
|
stream.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
//! Print device status
|
||||||
|
|
||||||
|
use crate::terminal::STDOUT;
|
||||||
|
use gumdrop::Options;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use termcolor::{ColorSpec, StandardStreamLock, WriteColor};
|
||||||
|
use yubikey_piv::YubiKey;
|
||||||
|
|
||||||
|
// String to use for `None`
|
||||||
|
const NONE_STR: &str = "<none>";
|
||||||
|
|
||||||
|
/// The `status` subcommand
|
||||||
|
#[derive(Debug, Options)]
|
||||||
|
pub struct StatusCmd {}
|
||||||
|
|
||||||
|
impl StatusCmd {
|
||||||
|
/// Run the `status` subcommand
|
||||||
|
pub fn run(&self, mut yk: YubiKey) {
|
||||||
|
let mut s = STDOUT.lock();
|
||||||
|
s.reset().unwrap();
|
||||||
|
|
||||||
|
self.attr(&mut s, "version", yk.version()).unwrap();
|
||||||
|
self.attr(&mut s, "serial", yk.serial()).unwrap();
|
||||||
|
|
||||||
|
if let Ok(chuid) = yk.chuid() {
|
||||||
|
self.attr(&mut s, "CHUID", chuid).unwrap();
|
||||||
|
} else {
|
||||||
|
self.attr(&mut s, "CHUID", NONE_STR).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(chuid) = yk.cccid() {
|
||||||
|
self.attr(&mut s, "CCC", chuid).unwrap();
|
||||||
|
} else {
|
||||||
|
self.attr(&mut s, "CCC", NONE_STR).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attr(&mut s, "PIN retries", yk.get_pin_retries().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print a status attribute
|
||||||
|
fn attr(
|
||||||
|
&self,
|
||||||
|
stream: &mut StandardStreamLock<'_>,
|
||||||
|
name: &str,
|
||||||
|
value: impl ToString,
|
||||||
|
) -> Result<(), io::Error> {
|
||||||
|
stream.set_color(ColorSpec::new().set_bold(true))?;
|
||||||
|
write!(stream, "{:>12}:", name)?;
|
||||||
|
stream.reset()?;
|
||||||
|
writeln!(stream, " {}", value.to_string())?;
|
||||||
|
stream.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -9,6 +9,6 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod status;
|
pub mod terminal;
|
||||||
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! status_ok {
|
macro_rules! status_ok {
|
||||||
($status:expr, $msg:expr) => {
|
($status:expr, $msg:expr) => {
|
||||||
$crate::status::Status::new()
|
$crate::terminal::Status::new()
|
||||||
.justified()
|
.justified()
|
||||||
.bold()
|
.bold()
|
||||||
.color(termcolor::Color::Green)
|
.color(termcolor::Color::Green)
|
||||||
@@ -25,7 +25,7 @@ macro_rules! status_ok {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! status_warn {
|
macro_rules! status_warn {
|
||||||
($msg:expr) => {
|
($msg:expr) => {
|
||||||
$crate::status::Status::new()
|
$crate::terminal::Status::new()
|
||||||
.bold()
|
.bold()
|
||||||
.color(termcolor::Color::Yellow)
|
.color(termcolor::Color::Yellow)
|
||||||
.status("warning:")
|
.status("warning:")
|
||||||
@@ -40,7 +40,7 @@ macro_rules! status_warn {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! status_err {
|
macro_rules! status_err {
|
||||||
($msg:expr) => {
|
($msg:expr) => {
|
||||||
$crate::status::Status::new()
|
$crate::terminal::Status::new()
|
||||||
.bold()
|
.bold()
|
||||||
.color(termcolor::Color::Red)
|
.color(termcolor::Color::Red)
|
||||||
.status("error:")
|
.status("error:")
|
||||||
+12
-1
@@ -32,7 +32,8 @@
|
|||||||
|
|
||||||
use crate::{error::Error, yubikey::YubiKey};
|
use crate::{error::Error, yubikey::YubiKey};
|
||||||
use getrandom::getrandom;
|
use getrandom::getrandom;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug, Display};
|
||||||
|
use subtle_encoding::hex;
|
||||||
|
|
||||||
/// CCCID size
|
/// CCCID size
|
||||||
pub const CCCID_SIZE: usize = 14;
|
pub const CCCID_SIZE: usize = 14;
|
||||||
@@ -116,3 +117,13 @@ impl Debug for CCC {
|
|||||||
write!(f, "CCC({:?})", &self.0[..])
|
write!(f, "CCC({:?})", &self.0[..])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for CCC {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
String::from_utf8(hex::encode(&self.0[..])).unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+12
-1
@@ -32,7 +32,8 @@
|
|||||||
|
|
||||||
use crate::{error::Error, yubikey::YubiKey};
|
use crate::{error::Error, yubikey::YubiKey};
|
||||||
use getrandom::getrandom;
|
use getrandom::getrandom;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug, Display};
|
||||||
|
use subtle_encoding::hex;
|
||||||
|
|
||||||
/// CHUID size
|
/// CHUID size
|
||||||
pub const CHUID_SIZE: usize = 59;
|
pub const CHUID_SIZE: usize = 59;
|
||||||
@@ -149,6 +150,16 @@ impl CHUID {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for CHUID {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
String::from_utf8(hex::encode(&self.0[..])).unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Debug for CHUID {
|
impl Debug for CHUID {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "CHUID({:?})", &self.0[..])
|
write!(f, "CHUID({:?})", &self.0[..])
|
||||||
|
|||||||
+7
-1
@@ -156,7 +156,13 @@ pub mod settings;
|
|||||||
mod transaction;
|
mod transaction;
|
||||||
pub mod yubikey;
|
pub mod yubikey;
|
||||||
|
|
||||||
pub use self::{error::Error, key::Key, mgm::MgmKey, readers::Readers, yubikey::YubiKey};
|
pub use self::{
|
||||||
|
error::Error,
|
||||||
|
key::Key,
|
||||||
|
mgm::MgmKey,
|
||||||
|
readers::Readers,
|
||||||
|
yubikey::{Serial, YubiKey},
|
||||||
|
};
|
||||||
|
|
||||||
/// Object identifiers
|
/// Object identifiers
|
||||||
pub type ObjectId = u32;
|
pub type ObjectId = u32;
|
||||||
|
|||||||
+9
-1
@@ -43,6 +43,7 @@ use pcsc::Card;
|
|||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
fmt::{self, Display},
|
fmt::{self, Display},
|
||||||
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
@@ -103,6 +104,14 @@ impl From<Serial> for u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for Serial {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Error> {
|
||||||
|
u32::from_str(s).map(Serial).map_err(|_| Error::ParseError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Serial {
|
impl Display for Serial {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.0)
|
write!(f, "{}", self.0)
|
||||||
@@ -350,7 +359,6 @@ impl YubiKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of PIN retries
|
/// Get the number of PIN retries
|
||||||
#[cfg(feature = "untested")]
|
|
||||||
pub fn get_pin_retries(&mut self) -> Result<u8, Error> {
|
pub fn get_pin_retries(&mut self) -> Result<u8, Error> {
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user