opgpcard: refactor admin generate
This commit is contained in:
parent
72f8a1994b
commit
00d40e940b
1 changed files with 80 additions and 126 deletions
|
@ -69,28 +69,7 @@ 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),
|
||||||
#[clap(name = "User PIN file", short = 'p', long = "user-pin")]
|
|
||||||
user_pin: Option<PathBuf>,
|
|
||||||
|
|
||||||
/// Output file (stdout if unset)
|
|
||||||
#[clap(name = "output", long = "output", short = 'o')]
|
|
||||||
output: Option<PathBuf>,
|
|
||||||
|
|
||||||
#[clap(long = "no-decrypt", action = clap::ArgAction::SetFalse)]
|
|
||||||
decrypt: bool,
|
|
||||||
|
|
||||||
#[clap(long = "no-auth", action = clap::ArgAction::SetFalse)]
|
|
||||||
auth: bool,
|
|
||||||
|
|
||||||
/// Algorithm
|
|
||||||
#[clap(value_enum)]
|
|
||||||
algo: Option<Algo>,
|
|
||||||
|
|
||||||
/// User ID to add to the exported certificate representation
|
|
||||||
#[clap(name = "User ID", short = 'u', long = "userid")]
|
|
||||||
user_id: Vec<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Set touch policy
|
/// Set touch policy
|
||||||
Touch {
|
Touch {
|
||||||
|
@ -102,6 +81,30 @@ pub enum AdminSubCommand {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub struct AdminGenerateCommand {
|
||||||
|
#[clap(name = "User PIN file", short = 'p', long = "user-pin")]
|
||||||
|
user_pin: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// Output file (stdout if unset)
|
||||||
|
#[clap(name = "output", long = "output", short = 'o')]
|
||||||
|
output_file: Option<PathBuf>,
|
||||||
|
|
||||||
|
#[clap(long = "no-decrypt", action = clap::ArgAction::SetFalse)]
|
||||||
|
decrypt: bool,
|
||||||
|
|
||||||
|
#[clap(long = "no-auth", action = clap::ArgAction::SetFalse)]
|
||||||
|
auth: bool,
|
||||||
|
|
||||||
|
/// Algorithm
|
||||||
|
#[clap(value_enum)]
|
||||||
|
algo: Option<Algo>,
|
||||||
|
|
||||||
|
/// User ID to add to the exported certificate representation
|
||||||
|
#[clap(name = "User ID", short = 'u', long = "userid")]
|
||||||
|
user_ids: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(ValueEnum, Debug, Clone)]
|
#[derive(ValueEnum, Debug, Clone)]
|
||||||
#[clap(rename_all = "UPPER")]
|
#[clap(rename_all = "UPPER")]
|
||||||
pub enum BasePlusAttKeySlot {
|
pub enum BasePlusAttKeySlot {
|
||||||
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue