diff options
-rw-r--r-- | nitrocli/src/main.rs | 7 | ||||
-rw-r--r-- | nitrocli/src/pinentry.rs | 55 |
2 files changed, 48 insertions, 14 deletions
diff --git a/nitrocli/src/main.rs b/nitrocli/src/main.rs index 0ba232f..ebc0589 100644 --- a/nitrocli/src/main.rs +++ b/nitrocli/src/main.rs @@ -42,6 +42,7 @@ type Result<T> = result::Result<T, Error>; type NitroFunc = Fn(&mut libhid::Handle) -> Result<()>; +const PIN_TYPE: pinentry::PinType = pinentry::PinType::User; const SEND_TRY_COUNT: i8 = 3; const RECV_TRY_COUNT: i8 = 40; const SEND_RECV_DELAY_MS: u64 = 200; @@ -255,7 +256,7 @@ fn open() -> Result<()> { let mut retry = 3; let mut error_msg: Option<&str> = None; loop { - let passphrase = pinentry::inquire_passphrase(error_msg)?; + let passphrase = pinentry::inquire_passphrase(PIN_TYPE, error_msg)?; let payload = nitrokey::EnableEncryptedVolumeCommand::new(&passphrase); let report = nitrokey::Report::from(payload); @@ -264,7 +265,7 @@ fn open() -> Result<()> { let mut status = response.data.storage_status; if status == nitrokey::StorageStatus::WrongPassword { - pinentry::clear_passphrase()?; + pinentry::clear_passphrase(PIN_TYPE)?; retry -= 1; if retry > 0 { @@ -328,7 +329,7 @@ fn close() -> Result<()> { /// Clear the PIN stored when opening the nitrokey's encrypted volume. fn clear() -> Result<()> { - pinentry::clear_passphrase() + pinentry::clear_passphrase(PIN_TYPE) } diff --git a/nitrocli/src/pinentry.rs b/nitrocli/src/pinentry.rs index 6cf3093..0553b8e 100644 --- a/nitrocli/src/pinentry.rs +++ b/nitrocli/src/pinentry.rs @@ -21,7 +21,41 @@ use error::Error; use std::process; -const CACHE_ID: &str = "nitrocli:user"; +/// PIN type requested from pinentry. +/// +/// The available PIN types correspond to the PIN types used by the Nitrokey devices: user and +/// admin. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum PinType { + /// The admin PIN. + Admin, + /// The user PIN. + User, +} + + +impl PinType { + fn cache_id(self) -> &'static str { + match self { + PinType::Admin => "nitrocli:admin", + PinType::User => "nitrocli:user", + } + } + + fn prompt(self) -> &'static str { + match self { + PinType::Admin => "Admin PIN", + PinType::User => "User PIN", + } + } + + fn description(self) -> &'static str { + match self { + PinType::Admin => "Please enter admin PIN", + PinType::User => "Please enter user PIN", + } + } +} fn parse_pinentry_passphrase(response: Vec<u8>) -> Result<Vec<u8>, Error> { @@ -49,14 +83,13 @@ fn parse_pinentry_passphrase(response: Vec<u8>) -> Result<Vec<u8>, Error> { } -pub fn inquire_passphrase(error_msg: Option<&str>) -> Result<Vec<u8>, Error> { - const PINENTRY_ERROR_MSG_EMPTY: &str = "+"; - const PINENTRY_PROMPT: &str = "PIN"; - const PINENTRY_DESCR: &str = "Please+enter+user+PIN"; +pub fn inquire_passphrase(pin_type: PinType, error_msg: Option<&str>) -> Result<Vec<u8>, Error> { + let cache_id = pin_type.cache_id(); + let error_msg = error_msg.map(|msg| msg.replace(" ", "+")).unwrap_or(String::from("+")); + let prompt = pin_type.prompt(); + let description = pin_type.description().replace(" ", "+"); - let error_msg = error_msg.map(|msg| msg.replace(" ", "+")) - .unwrap_or(PINENTRY_ERROR_MSG_EMPTY.to_string()); - let args = vec![CACHE_ID, &error_msg, PINENTRY_PROMPT, PINENTRY_DESCR].join(" "); + let args = vec![cache_id, &error_msg, prompt, &description].join(" "); let command = "GET_PASSPHRASE --data ".to_string() + &args; // We could also use the --data parameter here to have a more direct // representation of the passphrase but the resulting response was @@ -83,9 +116,9 @@ fn parse_pinentry_response(response: Vec<u8>) -> Result<(), Error> { } -/// Clear the cached passphrase. -pub fn clear_passphrase() -> Result<(), Error> { - let command = "CLEAR_PASSPHRASE ".to_string() + CACHE_ID; +/// Clear the cached passphrase of the given type. +pub fn clear_passphrase(pin_type: PinType) -> Result<(), Error> { + let command = "CLEAR_PASSPHRASE ".to_string() + pin_type.cache_id(); let output = process::Command::new("gpg-connect-agent").arg(command) .arg("/bye") .output()?; |