diff options
author | Daniel Mueller <deso@posteo.net> | 2019-01-11 19:45:59 -0800 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2019-01-11 19:45:59 -0800 |
commit | 44b8c57a6f8701c50b179e482deca79a9e4e7acb (patch) | |
tree | 2631ea33ef6e147f1013ac80e22e26363ecd6dbf /nitrocli/src/pinentry.rs | |
parent | 772123f4197aded2a99efc5170978dd0bfbc091f (diff) | |
download | nitrocli-44b8c57a6f8701c50b179e482deca79a9e4e7acb.tar.gz nitrocli-44b8c57a6f8701c50b179e482deca79a9e4e7acb.tar.bz2 |
Isolate cached PINs for multiple devices from each other
The application supports multiple devices both plugged in at the same
time as well as when used after the other. However, the GPG cache ID we
use for storing and retrieving the respective PIN is effectively a
constant. This constraint can cause problems when devices have different
PINs, as the PIN of a previously plugged in device may be reused for an
operation on a different one.
To resolve this problem this change adds the respective device's model
and serial number to the cache ID. As each serial number is supposed to
be different, this will ensure that the correct PIN is used for each
device. With this change we also show the model and serial number of the
currently used device in the pinentry dialog.
Note that because we do not store the serial numbers of all previously
plugged in devices, the pin clear command will only clear the PIN for
the currently plugged in device. If a user wants to make sure that a
cached PIN is cleared, the pin clear command should be invoked before
unplugging the device.
Diffstat (limited to 'nitrocli/src/pinentry.rs')
-rw-r--r-- | nitrocli/src/pinentry.rs | 86 |
1 files changed, 59 insertions, 27 deletions
diff --git a/nitrocli/src/pinentry.rs b/nitrocli/src/pinentry.rs index d54a182..1eecdd0 100644 --- a/nitrocli/src/pinentry.rs +++ b/nitrocli/src/pinentry.rs @@ -46,34 +46,66 @@ impl str::FromStr for PinType { } } -impl PinType { - fn cache_id(self) -> &'static str { - match self { - PinType::Admin => "nitrocli:admin", - PinType::User => "nitrocli:user", +#[derive(Debug)] +pub struct PinEntry { + pin_type: PinType, + model: nitrokey::Model, + serial: String, +} + +impl PinEntry { + pub fn from<D>(pin_type: PinType, device: &D) -> crate::Result<Self> + where + D: nitrokey::Device, + { + let model = device.get_model(); + let serial = device.get_serial_number()?; + Ok(Self { + pin_type, + model, + serial, + }) + } + + fn cache_id(&self) -> String { + let model = self.model.to_string().to_lowercase(); + let suffix = format!("{}:{}", model, self.serial); + + match self.pin_type { + PinType::Admin => format!("nitrocli:admin:{}", suffix), + PinType::User => format!("nitrocli:user:{}", suffix), } } - fn prompt(self) -> &'static str { - match self { + fn prompt(&self) -> &'static str { + match self.pin_type { PinType::Admin => "Admin PIN", PinType::User => "User PIN", } } - fn description(self, mode: Mode) -> &'static str { - match self { - PinType::Admin => match mode { - Mode::Choose => "Please enter a new admin PIN", - Mode::Confirm => "Please confirm the new admin PIN", - Mode::Query => "Please enter the admin PIN", + fn description(&self, mode: Mode) -> String { + format!( + "{} for\rNitrokey {} {}", + match self.pin_type { + PinType::Admin => match mode { + Mode::Choose => "Please enter a new admin PIN", + Mode::Confirm => "Please confirm the new admin PIN", + Mode::Query => "Please enter the admin PIN", + }, + PinType::User => match mode { + Mode::Choose => "Please enter a new user PIN", + Mode::Confirm => "Please confirm the new user PIN", + Mode::Query => "Please enter the user PIN", + }, }, - PinType::User => match mode { - Mode::Choose => "Please enter a new user PIN", - Mode::Confirm => "Please confirm the new user PIN", - Mode::Query => "Please enter the user PIN", - }, - } + self.model, + self.serial, + ) + } + + pub fn pin_type(&self) -> PinType { + self.pin_type } } @@ -134,18 +166,18 @@ where /// description and to decide whether a quality bar is shown in the /// dialog. pub fn inquire_pin( - pin_type: PinType, + pin_entry: &PinEntry, mode: Mode, error_msg: Option<&str>, ) -> Result<String, Error> { - let cache_id = pin_type.cache_id(); + let cache_id = pin_entry.cache_id(); let error_msg = error_msg .map(|msg| msg.replace(" ", "+")) .unwrap_or_else(|| String::from("+")); - let prompt = pin_type.prompt().replace(" ", "+"); - let description = pin_type.description(mode).replace(" ", "+"); + let prompt = pin_entry.prompt().replace(" ", "+"); + let description = pin_entry.description(mode).replace(" ", "+"); - let args = vec![cache_id, &error_msg, &prompt, &description].join(" "); + let args = vec![cache_id, error_msg, prompt, description].join(" "); let mut command = "GET_PASSPHRASE --data ".to_string(); if mode.show_quality_bar() { command += "--qualitybar "; @@ -178,9 +210,9 @@ where Err(Error::Error(format!("Unexpected response: {}", string))) } -/// Clear the cached pin of the given type. -pub fn clear_pin(pin_type: PinType) -> Result<(), Error> { - let command = "CLEAR_PASSPHRASE ".to_string() + pin_type.cache_id(); +/// Clear the cached pin represented by the given entry. +pub fn clear_pin(pin_entry: &PinEntry) -> Result<(), Error> { + let command = format!("CLEAR_PASSPHRASE {}", pin_entry.cache_id()); let output = process::Command::new("gpg-connect-agent") .arg(command) .arg("/bye") |