use std::path::PathBuf;
use anyhow::anyhow;
use anyhow::Result;
use clap::Parser;
use openpgp_card::state::Transaction;
use openpgp_card::Card;
use crate::util;
use crate::util::{load_pin, print_gnuk_note};
use crate::{ENTER_ADMIN_PIN, ENTER_USER_PIN};
#[derive(Parser, Debug)]
pub struct PinCommand {
#[arg(
name = "card ident",
short = 'c',
long = "card",
help = "Identifier of the card to use"
)]
pub ident: String,
#[command(subcommand)]
pub cmd: PinSubCommand,
}
#[derive(Parser, Debug)]
pub enum PinSubCommand {
SetUser {
#[arg(
name = "User PIN file old",
short = 'p',
long = "user-pin-old",
help = "Optionally, get old User PIN from a file"
)]
user_pin_old: Option<PathBuf>,
#[arg(
name = "User PIN file new",
short = 'q',
long = "user-pin-new",
help = "Optionally, get new User PIN from a file"
)]
user_pin_new: Option<PathBuf>,
},
SetAdmin {
#[arg(
name = "Admin PIN file old",
short = 'P',
long = "admin-pin-old",
help = "Optionally, get old Admin PIN from a file"
)]
admin_pin_old: Option<PathBuf>,
#[arg(
name = "Admin PIN file new",
short = 'Q',
long = "admin-pin-new",
help = "Optionally, get new Admin PIN from a file"
)]
admin_pin_new: Option<PathBuf>,
},
ResetUser {
#[arg(
name = "Admin PIN file",
short = 'P',
long = "admin-pin",
help = "Optionally, get Admin PIN from a file"
)]
admin_pin: Option<PathBuf>,
#[arg(
name = "User PIN file new",
short = 'p',
long = "user-pin-new",
help = "Optionally, get new User PIN from a file"
)]
user_pin_new: Option<PathBuf>,
},
ResetUserRc {
#[arg(
name = "Resetting Code file",
short = 'r',
long = "reset-code",
help = "Optionally, get the Resetting Code from a file"
)]
reset_code: Option<PathBuf>,
#[arg(
name = "User PIN file new",
short = 'p',
long = "user-pin-new",
help = "Optionally, get new User PIN from a file"
)]
user_pin_new: Option<PathBuf>,
},
SetReset {
#[arg(
name = "Admin PIN file",
short = 'P',
long = "admin-pin",
help = "Optionally, get Admin PIN from a file"
)]
admin_pin: Option<PathBuf>,
#[arg(
name = "Resetting Code file",
short = 'r',
long = "reset-code",
help = "Optionally, get the Resetting Code from a file"
)]
reset_code: Option<PathBuf>,
},
}
pub fn pin(ident: &str, cmd: PinSubCommand) -> Result<()> {
let mut open = util::open_card(ident)?;
let card = open.transaction()?;
match cmd {
PinSubCommand::SetUser {
user_pin_old,
user_pin_new,
} => set_user(user_pin_old, user_pin_new, card),
PinSubCommand::SetAdmin {
admin_pin_old,
admin_pin_new,
} => set_admin(admin_pin_old, admin_pin_new, card),
PinSubCommand::ResetUser {
admin_pin,
user_pin_new,
} => reset_user(admin_pin, user_pin_new, card),
PinSubCommand::SetReset {
admin_pin,
reset_code,
} => set_reset(admin_pin, reset_code, card),
PinSubCommand::ResetUserRc {
reset_code,
user_pin_new,
} => reset_user_rc(reset_code, user_pin_new, card),
}
}
fn set_user(
user_pin_old: Option<PathBuf>,
user_pin_new: Option<PathBuf>,
mut card: Card<Transaction>,
) -> Result<()> {
let pinpad_modify = card.feature_pinpad_modify();
let res = if !pinpad_modify {
let user_pin1 = util::get_pin(&mut card, user_pin_old, ENTER_USER_PIN)?
.ok_or_else(|| anyhow!("We have a PIN pad"))?;
card.verify_user_pin(user_pin1.clone())?;
println!("PIN was accepted by the card.\n");
let pin_new = match user_pin_new {
None => {
util::input_pin_twice("Enter new User PIN: ", "Repeat the new User PIN: ")?
}
Some(path) => load_pin(&path)?,
};
card.change_user_pin(user_pin1, pin_new)
} else {
card.change_user_pin_pinpad(&|| {
println!("Enter old User PIN on card reader pinpad, then new User PIN (twice).")
})
};
match res {
Err(err) => {
println!("\nFailed to change the User PIN!");
println!("{err:?}");
print_gnuk_note(err, &mut card)?;
}
Ok(_) => println!("\nUser PIN has been set."),
}
Ok(())
}
fn set_admin(
admin_pin_old: Option<PathBuf>,
admin_pin_new: Option<PathBuf>,
mut card: Card<Transaction>,
) -> Result<()> {
let pinpad_modify = card.feature_pinpad_modify();
if !pinpad_modify {
let admin_pin1 = util::get_pin(&mut card, admin_pin_old, ENTER_ADMIN_PIN)?
.ok_or_else(|| anyhow!("We have a PIN pad"))?;
card.verify_admin_pin(admin_pin1.clone())?;
println!("PIN was accepted by the card.\n");
let pin_new = match admin_pin_new {
None => {
util::input_pin_twice("Enter new Admin PIN: ", "Repeat the new Admin PIN: ")?
}
Some(path) => load_pin(&path)?,
};
card.change_admin_pin(admin_pin1, pin_new)?;
} else {
card.change_admin_pin_pinpad(&|| {
println!("Enter old Admin PIN on card reader pinpad, then new Admin PIN (twice).")
})?;
};
println!("\nAdmin PIN has been set.");
Ok(())
}
fn reset_user(
admin_pin: Option<PathBuf>,
user_pin_new: Option<PathBuf>,
mut card: Card<Transaction>,
) -> Result<()> {
match util::get_pin(&mut card, admin_pin, ENTER_ADMIN_PIN)? {
Some(admin_pin) => {
card.verify_admin_pin(admin_pin)?;
}
None => {
card.verify_admin_pinpad(&|| println!("Enter Admin PIN on pinpad."))?;
}
}
println!("PIN was accepted by the card.\n");
let pin = match user_pin_new {
None => util::input_pin_twice("Enter new User PIN: ", "Repeat the new User PIN: ")?,
Some(path) => load_pin(&path)?,
};
let res = if let Ok(mut admin) = card.to_admin_card(None) {
admin.reset_user_pin(pin)
} else {
return Err(anyhow::anyhow!("Failed to use card in admin-mode."));
};
match res {
Err(err) => {
println!("\nFailed to change the User PIN!");
print_gnuk_note(err, &mut card)?;
}
Ok(_) => println!("\nUser PIN has been set."),
}
Ok(())
}
fn set_reset(
admin_pin: Option<PathBuf>,
reset_code: Option<PathBuf>,
mut card: Card<Transaction>,
) -> Result<()> {
match util::get_pin(&mut card, admin_pin, ENTER_ADMIN_PIN)? {
Some(admin_pin) => {
card.verify_admin_pin(admin_pin)?;
}
None => {
card.verify_admin_pinpad(&|| println!("Enter Admin PIN on pinpad."))?;
}
}
println!("PIN was accepted by the card.\n");
let code = match reset_code {
None => util::input_pin_twice(
"Enter new resetting code: ",
"Repeat the new resetting code: ",
)?,
Some(path) => load_pin(&path)?,
};
if let Ok(mut admin) = card.to_admin_card(None) {
admin.set_resetting_code(code)?;
println!("\nResetting code has been set.");
Ok(())
} else {
Err(anyhow::anyhow!("Failed to use card in admin-mode."))
}
}
fn reset_user_rc(
reset_code: Option<PathBuf>,
user_pin_new: Option<PathBuf>,
mut card: Card<Transaction>,
) -> Result<()> {
let rst = if let Some(path) = reset_code {
load_pin(&path)?
} else {
rpassword::prompt_password("Enter resetting code: ")?.into()
};
let pin = match user_pin_new {
None => util::input_pin_twice("Enter new User PIN: ", "Repeat the new User PIN: ")?,
Some(path) => load_pin(&path)?,
};
match card.reset_user_pin(rst, pin) {
Err(err) => {
println!("\nFailed to change the User PIN!");
print_gnuk_note(err, &mut card)
}
Ok(_) => {
println!("\nUser PIN has been set.");
Ok(())
}
}
}