opgpcard: refactor admin generate

This commit is contained in:
Nora Widdecke 2022-10-26 16:29:27 +02:00
parent 72f8a1994b
commit 00d40e940b
No known key found for this signature in database
GPG key ID: 2D4111B31DBB99B6

View file

@ -69,13 +69,26 @@ pub enum AdminSubCommand {
/// ///
/// A signing key is always created, decryption and authentication keys /// A signing key is always created, decryption and authentication keys
/// are optional. /// are optional.
Generate { Generate(AdminGenerateCommand),
/// Set touch policy
Touch {
#[clap(name = "Key slot", short = 'k', long = "key", value_enum)]
key: BasePlusAttKeySlot,
#[clap(name = "Policy", short = 'p', long = "policy", value_enum)]
policy: TouchPolicy,
},
}
#[derive(Parser, Debug)]
pub struct AdminGenerateCommand {
#[clap(name = "User PIN file", short = 'p', long = "user-pin")] #[clap(name = "User PIN file", short = 'p', long = "user-pin")]
user_pin: Option<PathBuf>, user_pin: Option<PathBuf>,
/// Output file (stdout if unset) /// Output file (stdout if unset)
#[clap(name = "output", long = "output", short = 'o')] #[clap(name = "output", long = "output", short = 'o')]
output: Option<PathBuf>, output_file: Option<PathBuf>,
#[clap(long = "no-decrypt", action = clap::ArgAction::SetFalse)] #[clap(long = "no-decrypt", action = clap::ArgAction::SetFalse)]
decrypt: bool, decrypt: bool,
@ -89,17 +102,7 @@ pub enum AdminSubCommand {
/// User ID to add to the exported certificate representation /// User ID to add to the exported certificate representation
#[clap(name = "User ID", short = 'u', long = "userid")] #[clap(name = "User ID", short = 'u', long = "userid")]
user_id: Vec<String>, user_ids: Vec<String>,
},
/// Set touch policy
Touch {
#[clap(name = "Key slot", short = 'k', long = "key", value_enum)]
key: BasePlusAttKeySlot,
#[clap(name = "Policy", short = 'p', long = "policy", value_enum)]
policy: TouchPolicy,
},
} }
#[derive(ValueEnum, Debug, Clone)] #[derive(ValueEnum, Debug, Clone)]
@ -201,26 +204,8 @@ pub fn admin(
} => { } => {
import_command(keyfile, sig_fp, dec_fp, auth_fp, open, admin_pin.as_deref())?; import_command(keyfile, sig_fp, dec_fp, auth_fp, open, admin_pin.as_deref())?;
} }
AdminSubCommand::Generate { AdminSubCommand::Generate(cmd) => {
user_pin, generate_command(output_format, output_version, open, admin_pin.as_deref(), cmd)?;
output,
decrypt,
auth,
algo,
user_id,
} => {
generate_command(
output_format,
output_version,
open,
admin_pin.as_deref(),
user_pin,
output,
decrypt,
auth,
algo,
user_id,
)?;
} }
AdminSubCommand::Touch { key, policy } => { AdminSubCommand::Touch { key, policy } => {
touch_command(open, admin_pin.as_deref(), key, policy)?; touch_command(open, admin_pin.as_deref(), key, policy)?;
@ -257,72 +242,6 @@ fn keys_pick_explicit<'a>(
Ok([key_by_fp(sig_fp)?, key_by_fp(dec_fp)?, key_by_fp(auth_fp)?]) Ok([key_by_fp(sig_fp)?, key_by_fp(dec_fp)?, key_by_fp(auth_fp)?])
} }
#[allow(clippy::too_many_arguments)]
fn generate_keys(
format: OutputFormat,
version: OutputVersion,
mut open: Open,
admin_pin: Option<&[u8]>,
user_pin: Option<&[u8]>,
output_file: Option<PathBuf>,
decrypt: bool,
auth: bool,
algo: Option<AlgoSimple>,
user_ids: Vec<String>,
) -> Result<()> {
let mut output = output::AdminGenerate::default();
output.ident(open.application_identifier()?.ident());
// 1) Interpret the user's choice of algorithm.
//
// Unset (None) means that the algorithm that is specified on the card
// should remain unchanged.
//
// For RSA, different cards use different exact algorithm
// specifications. In particular, the length of the value `e` differs
// between cards. Some devices use 32 bit length for e, others use 17 bit.
// In some cases, it's possible to get this information from the card,
// but I believe this information is not obtainable in all cases.
// Because of this, for generation of RSA keys, here we take the approach
// of first trying one variant, and then if that fails, try the other.
log::info!(" Key generation will be attempted with algo: {:?}", algo);
output.algorithm(format!("{:?}", algo));
// 2) Then, generate keys on the card.
// We need "admin" access to the card for this).
let (key_sig, key_dec, key_aut) = {
if let Ok(mut admin) = util::verify_to_admin(&mut open, admin_pin) {
gen_subkeys(&mut admin, decrypt, auth, algo)?
} else {
return Err(anyhow!("Failed to open card in admin mode."));
}
};
// 3) Generate a Cert from the generated keys. For this, we
// need "signing" access to the card (to make binding signatures within
// the Cert).
let cert = crate::get_cert(
&mut open,
key_sig,
key_dec,
key_aut,
user_pin,
&user_ids,
&|| println!("Enter User PIN on card reader pinpad."),
)?;
let armored = String::from_utf8(cert.armored().to_vec()?)?;
output.public_key(armored);
// Write armored certificate to the output file (or stdout)
let mut handle = util::open_or_stdout(output_file.as_deref())?;
handle.write_all(output.print(format, version)?.as_bytes())?;
let _ = handle.write(b"\n")?;
Ok(())
}
fn gen_subkeys( fn gen_subkeys(
admin: &mut Admin, admin: &mut Admin,
decrypt: bool, decrypt: bool,
@ -509,27 +428,62 @@ fn generate_command(
admin_pin: Option<&[u8]>, admin_pin: Option<&[u8]>,
user_pin: Option<PathBuf>, cmd: AdminGenerateCommand,
output: Option<PathBuf>,
decrypt: bool,
auth: bool,
algo: Option<Algo>,
user_id: Vec<String>,
) -> Result<()> { ) -> Result<()> {
let user_pin = util::get_pin(&mut open, user_pin, ENTER_USER_PIN); let user_pin = util::get_pin(&mut open, cmd.user_pin, ENTER_USER_PIN);
generate_keys( let mut output = output::AdminGenerate::default();
output_format, output.ident(open.application_identifier()?.ident());
output_version,
open, // 1) Interpret the user's choice of algorithm.
admin_pin.as_deref(), //
// Unset (None) means that the algorithm that is specified on the card
// should remain unchanged.
//
// For RSA, different cards use different exact algorithm
// specifications. In particular, the length of the value `e` differs
// between cards. Some devices use 32 bit length for e, others use 17 bit.
// In some cases, it's possible to get this information from the card,
// but I believe this information is not obtainable in all cases.
// Because of this, for generation of RSA keys, here we take the approach
// of first trying one variant, and then if that fails, try the other.
let algo = cmd.algo.map(AlgoSimple::from);
log::info!(" Key generation will be attempted with algo: {:?}", algo);
output.algorithm(format!("{:?}", algo));
// 2) Then, generate keys on the card.
// We need "admin" access to the card for this).
let (key_sig, key_dec, key_aut) = {
if let Ok(mut admin) = util::verify_to_admin(&mut open, admin_pin) {
gen_subkeys(&mut admin, cmd.decrypt, cmd.auth, algo)?
} else {
return Err(anyhow!("Failed to open card in admin mode."));
}
};
// 3) Generate a Cert from the generated keys. For this, we
// need "signing" access to the card (to make binding signatures within
// the Cert).
let cert = crate::get_cert(
&mut open,
key_sig,
key_dec,
key_aut,
user_pin.as_deref(), user_pin.as_deref(),
output, &cmd.user_ids,
decrypt, &|| println!("Enter User PIN on card reader pinpad."),
auth, )?;
algo.map(AlgoSimple::from),
user_id, let armored = String::from_utf8(cert.armored().to_vec()?)?;
) output.public_key(armored);
// Write armored certificate to the output file (or stdout)
let mut handle = util::open_or_stdout(cmd.output_file.as_deref())?;
handle.write_all(output.print(output_format, output_version)?.as_bytes())?;
let _ = handle.write(b"\n")?;
Ok(())
} }
fn touch_command( fn touch_command(