aboutsummaryrefslogtreecommitdiff
path: root/nitrocli/src/pinentry.rs
diff options
context:
space:
mode:
authorDaniel Mueller <deso@posteo.net>2019-01-11 19:45:59 -0800
committerDaniel Mueller <deso@posteo.net>2019-01-11 19:45:59 -0800
commit44b8c57a6f8701c50b179e482deca79a9e4e7acb (patch)
tree2631ea33ef6e147f1013ac80e22e26363ecd6dbf /nitrocli/src/pinentry.rs
parent772123f4197aded2a99efc5170978dd0bfbc091f (diff)
downloadnitrocli-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.rs86
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")