keyfork: improve prompt UX of `wizard generate-shard-secret`
This commit is contained in:
parent
240a10a063
commit
4c6c071539
|
@ -1,6 +1,6 @@
|
||||||
use super::Keyfork;
|
use super::Keyfork;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use std::{collections::HashSet, io::IsTerminal, path::PathBuf, fs::OpenOptions};
|
use std::{collections::HashSet, fs::OpenOptions, io::IsTerminal, path::PathBuf};
|
||||||
|
|
||||||
use card_backend_pcsc::PcscBackend;
|
use card_backend_pcsc::PcscBackend;
|
||||||
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
|
use openpgp_card_sequoia::{state::Open, types::KeyType, Card};
|
||||||
|
@ -56,6 +56,7 @@ fn factory_reset_current_card(
|
||||||
user_pin: &str,
|
user_pin: &str,
|
||||||
admin_pin: &str,
|
admin_pin: &str,
|
||||||
cert: &Cert,
|
cert: &Cert,
|
||||||
|
card_backend: PcscBackend,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let policy = openpgp::policy::NullPolicy::new();
|
let policy = openpgp::policy::NullPolicy::new();
|
||||||
let valid_cert = cert.with_policy(&policy, None)?;
|
let valid_cert = cert.with_policy(&policy, None)?;
|
||||||
|
@ -77,30 +78,31 @@ fn factory_reset_current_card(
|
||||||
.secret()
|
.secret()
|
||||||
.next()
|
.next()
|
||||||
.expect("no authentication key found");
|
.expect("no authentication key found");
|
||||||
if let Some(current_backend) = PcscBackend::cards(None)?.next().transpose()? {
|
let mut card = Card::<Open>::new(card_backend)?;
|
||||||
let mut card = Card::<Open>::new(current_backend)?;
|
let mut transaction = card.transaction()?;
|
||||||
let mut transaction = card.transaction()?;
|
let application_identifier = transaction.application_identifier()?.ident();
|
||||||
let application_identifier = transaction.application_identifier()?.ident();
|
if seen_cards.contains(&application_identifier) {
|
||||||
if seen_cards.contains(&application_identifier) {
|
// we were given the same card, error
|
||||||
// we were given the same card, error
|
panic!("Previously used card {application_identifier} was reused");
|
||||||
panic!("Previously used card {application_identifier} was reused");
|
|
||||||
} else {
|
|
||||||
seen_cards.insert(application_identifier);
|
|
||||||
}
|
|
||||||
transaction.factory_reset()?;
|
|
||||||
let mut admin = transaction.to_admin_card("12345678")?;
|
|
||||||
admin.upload_key(signing_key, KeyType::Signing, None)?;
|
|
||||||
admin.upload_key(decryption_key, KeyType::Decryption, None)?;
|
|
||||||
admin.upload_key(authentication_key, KeyType::Authentication, None)?;
|
|
||||||
transaction.change_user_pin("123456", user_pin)?;
|
|
||||||
transaction.change_admin_pin("12345678", admin_pin)?;
|
|
||||||
} else {
|
} else {
|
||||||
panic!("No smart card found");
|
seen_cards.insert(application_identifier);
|
||||||
}
|
}
|
||||||
|
transaction.factory_reset()?;
|
||||||
|
let mut admin = transaction.to_admin_card("12345678")?;
|
||||||
|
admin.upload_key(signing_key, KeyType::Signing, None)?;
|
||||||
|
admin.upload_key(decryption_key, KeyType::Decryption, None)?;
|
||||||
|
admin.upload_key(authentication_key, KeyType::Authentication, None)?;
|
||||||
|
transaction.change_user_pin("123456", user_pin)?;
|
||||||
|
transaction.change_admin_pin("12345678", admin_pin)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_shard_secret(threshold: u8, max: u8, keys_per_shard: u8, output_file: &Option<PathBuf>) -> Result<()> {
|
fn generate_shard_secret(
|
||||||
|
threshold: u8,
|
||||||
|
max: u8,
|
||||||
|
keys_per_shard: u8,
|
||||||
|
output_file: &Option<PathBuf>,
|
||||||
|
) -> Result<()> {
|
||||||
let seed = keyfork_entropy::generate_entropy_of_size(256 / 8)?;
|
let seed = keyfork_entropy::generate_entropy_of_size(256 / 8)?;
|
||||||
let mut pm = Terminal::new(std::io::stdin(), std::io::stderr())?;
|
let mut pm = Terminal::new(std::io::stdin(), std::io::stderr())?;
|
||||||
let mut certs = vec![];
|
let mut certs = vec![];
|
||||||
|
@ -128,10 +130,19 @@ fn generate_shard_secret(threshold: u8, max: u8, keys_per_shard: u8, output_file
|
||||||
let cert = derive_key(&seed, index)?;
|
let cert = derive_key(&seed, index)?;
|
||||||
for i in 0..keys_per_shard {
|
for i in 0..keys_per_shard {
|
||||||
pm.prompt_message(Message::Text(format!(
|
pm.prompt_message(Message::Text(format!(
|
||||||
"Please insert key #{} for user #{}",
|
"Please remove all keys and insert key #{} for user #{}",
|
||||||
i + 1,
|
i + 1,
|
||||||
index + 1,
|
index + 1,
|
||||||
)))?;
|
)))?;
|
||||||
|
let card_backend = loop {
|
||||||
|
if let Some(c) = PcscBackend::cards(None)?.next().transpose()? {
|
||||||
|
break c;
|
||||||
|
}
|
||||||
|
pm.prompt_message(Message::Text(
|
||||||
|
"No smart card was found. Please plug in a smart card and press enter"
|
||||||
|
.to_string(),
|
||||||
|
))?;
|
||||||
|
};
|
||||||
let user_pin = pm.prompt_validated_passphrase(
|
let user_pin = pm.prompt_validated_passphrase(
|
||||||
"Please enter the new smartcard User PIN: ",
|
"Please enter the new smartcard User PIN: ",
|
||||||
3,
|
3,
|
||||||
|
@ -142,7 +153,13 @@ fn generate_shard_secret(threshold: u8, max: u8, keys_per_shard: u8, output_file
|
||||||
3,
|
3,
|
||||||
&admin_pin_validator,
|
&admin_pin_validator,
|
||||||
)?;
|
)?;
|
||||||
factory_reset_current_card(&mut seen_cards, user_pin.trim(), admin_pin.trim(), &cert)?;
|
factory_reset_current_card(
|
||||||
|
&mut seen_cards,
|
||||||
|
user_pin.trim(),
|
||||||
|
admin_pin.trim(),
|
||||||
|
&cert,
|
||||||
|
card_backend,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
certs.push(cert);
|
certs.push(cert);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,10 @@ pub enum Error {
|
||||||
#[error("Validation of the input failed after {0} retries (last error: {1})")]
|
#[error("Validation of the input failed after {0} retries (last error: {1})")]
|
||||||
Validation(u8, String),
|
Validation(u8, String),
|
||||||
|
|
||||||
|
/// A ctrl-c interrupt was caught by the handler.
|
||||||
|
#[error("User pressed ctrl-c, terminating the session")]
|
||||||
|
CtrlC,
|
||||||
|
|
||||||
/// An error occurred while interacting with a terminal.
|
/// An error occurred while interacting with a terminal.
|
||||||
#[error("IO Error: {0}")]
|
#[error("IO Error: {0}")]
|
||||||
IO(#[from] std::io::Error),
|
IO(#[from] std::io::Error),
|
||||||
|
|
|
@ -269,6 +269,9 @@ impl<R, W> PromptHandler for Terminal<R, W> where R: Read + Sized, W: Write + As
|
||||||
input.push(' ');
|
input.push(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
KeyCode::Char('c') if k.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
|
return Err(Error::CtrlC);
|
||||||
|
}
|
||||||
KeyCode::Char(' ') => {
|
KeyCode::Char(' ') => {
|
||||||
if !input.chars().next_back().is_some_and(char::is_whitespace) {
|
if !input.chars().next_back().is_some_and(char::is_whitespace) {
|
||||||
input.push(' ');
|
input.push(' ');
|
||||||
|
@ -410,6 +413,9 @@ impl<R, W> PromptHandler for Terminal<R, W> where R: Read + Sized, W: Write + As
|
||||||
.flush()?;
|
.flush()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
KeyCode::Char('c') if k.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
|
return Err(Error::CtrlC);
|
||||||
|
}
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
if prefix_length + passphrase.len() < (cols - 1) as usize {
|
if prefix_length + passphrase.len() < (cols - 1) as usize {
|
||||||
terminal.queue(Print("*"))?.flush()?;
|
terminal.queue(Print("*"))?.flush()?;
|
||||||
|
@ -485,6 +491,9 @@ impl<R, W> PromptHandler for Terminal<R, W> where R: Read + Sized, W: Write + As
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match read()? {
|
match read()? {
|
||||||
Event::Key(k) => match k.code {
|
Event::Key(k) => match k.code {
|
||||||
|
KeyCode::Char('c') if k.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
|
return Err(Error::CtrlC);
|
||||||
|
}
|
||||||
KeyCode::Enter | KeyCode::Char(' ' | 'q') => break,
|
KeyCode::Enter | KeyCode::Char(' ' | 'q') => break,
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue