diff options
| -rw-r--r-- | nitrocli/src/commands.rs | 149 | ||||
| -rw-r--r-- | nitrocli/src/main.rs | 130 | 
2 files changed, 151 insertions, 128 deletions
| diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs new file mode 100644 index 0000000..4a33364 --- /dev/null +++ b/nitrocli/src/commands.rs @@ -0,0 +1,149 @@ +// commands.rs + +// ************************************************************************* +// * Copyright (C) 2018 Daniel Mueller (deso@posteo.net)                   * +// *                                                                       * +// * This program is free software: you can redistribute it and/or modify  * +// * it under the terms of the GNU General Public License as published by  * +// * the Free Software Foundation, either version 3 of the License, or     * +// * (at your option) any later version.                                   * +// *                                                                       * +// * This program is distributed in the hope that it will be useful,       * +// * but WITHOUT ANY WARRANTY; without even the implied warranty of        * +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         * +// * GNU General Public License for more details.                          * +// *                                                                       * +// * You should have received a copy of the GNU General Public License     * +// * along with this program.  If not, see <http://www.gnu.org/licenses/>. * +// ************************************************************************* + +use crate::error::Error; +use crate::pinentry; +use crate::Result; + +const PIN_TYPE: pinentry::PinType = pinentry::PinType::User; + +/// Create an `error::Error` with an error message of the format `msg: err`. +fn get_error(msg: &str, err: &nitrokey::CommandError) -> Error { +  Error::Error(format!("{}: {:?}", msg, err)) +} + +/// Connect to a Nitrokey Storage device and return it. +fn get_storage_device() -> Result<nitrokey::Storage> { +  nitrokey::Storage::connect() +    .or_else(|_| Err(Error::Error("Nitrokey device not found".to_string()))) +} + +/// Return a string representation of the given volume status. +fn get_volume_status(status: &nitrokey::VolumeStatus) -> &'static str { +  if status.active { +    if status.read_only { +      "read-only" +    } else { +      "active" +    } +  } else { +    "inactive" +  } +} + +/// Pretty print the response of a status command. +fn print_status(status: &nitrokey::StorageStatus) { +  // We omit displaying information about the smartcard here as this +  // program really is only about the SD card portion of the device. +  println!( +    r#"Status: +  SD card ID:        {id:#x} +  firmware version:  {fwv0}.{fwv1} +  firmware:          {fw} +  storage keys:      {sk} +  user retry count:  {urc} +  admin retry count: {arc} +  volumes: +    unencrypted:     {vu} +    encrypted:       {ve} +    hidden:          {vh}"#, +    id = status.serial_number_sd_card, +    fwv0 = status.firmware_version_major, +    fwv1 = status.firmware_version_minor, +    fw = if status.firmware_locked { +      "locked" +    } else { +      "unlocked" +    }, +    sk = if status.stick_initialized { +      "created" +    } else { +      "not created" +    }, +    urc = status.user_retry_count, +    arc = status.admin_retry_count, +    vu = get_volume_status(&status.unencrypted_volume), +    ve = get_volume_status(&status.encrypted_volume), +    vh = get_volume_status(&status.hidden_volume), +  ); +} + +/// Inquire the status of the nitrokey. +pub fn status() -> Result<()> { +  let status = get_storage_device()? +    .get_status() +    .map_err(|err| get_error("Getting Storage status failed", &err))?; + +  print_status(&status); +  Ok(()) +} + +/// Open the encrypted volume on the nitrokey. +pub fn open() -> Result<()> { +  let device = get_storage_device()?; + +  let mut retry = 3; +  let mut error_msg: Option<&str> = None; +  loop { +    // TODO: Rethink the usage of String::from_utf8_lossy here. We may +    //       not want to silently modify the password! +    let passphrase = pinentry::inquire_passphrase(PIN_TYPE, error_msg)?; +    let passphrase = String::from_utf8_lossy(&passphrase); +    match device.enable_encrypted_volume(&passphrase) { +      Ok(()) => return Ok(()), +      Err(err) => match err { +        nitrokey::CommandError::WrongPassword => { +          pinentry::clear_passphrase(PIN_TYPE)?; +          retry -= 1; + +          if retry > 0 { +            error_msg = Some("Wrong password, please reenter"); +            continue; +          } +          let error = "Opening encrypted volume failed: Wrong password"; +          return Err(Error::Error(error.to_string())); +        } +        err => return Err(get_error("Opening encrypted volume failed", &err)), +      }, +    }; +  } +} + +#[link(name = "c")] +extern "C" { +  fn sync(); +} + +/// Close the previously opened encrypted volume. +pub fn close() -> Result<()> { +  // Flush all filesystem caches to disk. We are mostly interested in +  // making sure that the encrypted volume on the nitrokey we are +  // about to close is not closed while not all data was written to +  // it. +  unsafe { sync() }; + +  get_storage_device()? +    .disable_encrypted_volume() +    .map_err(|err| get_error("Closing encrypted volume failed", &err)) +} + +/// Clear the PIN stored when opening the nitrokey's encrypted volume. +pub fn clear() -> Result<()> { +  pinentry::clear_passphrase(PIN_TYPE) +} diff --git a/nitrocli/src/main.rs b/nitrocli/src/main.rs index c190d1b..86e9188 100644 --- a/nitrocli/src/main.rs +++ b/nitrocli/src/main.rs @@ -68,6 +68,7 @@  //! Nitrocli is a program providing a command line interface to certain  //! commands of the Nitrokey Storage device. +mod commands;  mod error;  mod pinentry; @@ -80,133 +81,6 @@ use crate::error::Error;  type Result<T> = result::Result<T, Error>; -const PIN_TYPE: pinentry::PinType = pinentry::PinType::User; - -/// Create an `error::Error` with an error message of the format `msg: err`. -fn get_error(msg: &str, err: &nitrokey::CommandError) -> Error { -  Error::Error(format!("{}: {:?}", msg, err)) -} - -/// Connect to a Nitrokey Storage device and return it. -fn get_storage_device() -> Result<nitrokey::Storage> { -  nitrokey::Storage::connect() -    .or_else(|_| Err(Error::Error("Nitrokey device not found".to_string()))) -} - -/// Return a string representation of the given volume status. -fn get_volume_status(status: &nitrokey::VolumeStatus) -> &'static str { -  if status.active { -    if status.read_only { -      "read-only" -    } else { -      "active" -    } -  } else { -    "inactive" -  } -} - -/// Pretty print the response of a status command. -fn print_status(status: &nitrokey::StorageStatus) { -  // We omit displaying information about the smartcard here as this -  // program really is only about the SD card portion of the device. -  println!( -    r#"Status: -  SD card ID:        {id:#x} -  firmware version:  {fwv0}.{fwv1} -  firmware:          {fw} -  storage keys:      {sk} -  user retry count:  {urc} -  admin retry count: {arc} -  volumes: -    unencrypted:     {vu} -    encrypted:       {ve} -    hidden:          {vh}"#, -    id = status.serial_number_sd_card, -    fwv0 = status.firmware_version_major, -    fwv1 = status.firmware_version_minor, -    fw = if status.firmware_locked { -      "locked" -    } else { -      "unlocked" -    }, -    sk = if status.stick_initialized { -      "created" -    } else { -      "not created" -    }, -    urc = status.user_retry_count, -    arc = status.admin_retry_count, -    vu = get_volume_status(&status.unencrypted_volume), -    ve = get_volume_status(&status.encrypted_volume), -    vh = get_volume_status(&status.hidden_volume), -  ); -} - -/// Inquire the status of the nitrokey. -fn status() -> Result<()> { -  let status = get_storage_device()? -    .get_status() -    .map_err(|err| get_error("Getting Storage status failed", &err))?; - -  print_status(&status); -  Ok(()) -} - -/// Open the encrypted volume on the nitrokey. -fn open() -> Result<()> { -  let device = get_storage_device()?; - -  let mut retry = 3; -  let mut error_msg: Option<&str> = None; -  loop { -    // TODO: Rethink the usage of String::from_utf8_lossy here. We may -    //       not want to silently modify the password! -    let passphrase = pinentry::inquire_passphrase(PIN_TYPE, error_msg)?; -    let passphrase = String::from_utf8_lossy(&passphrase); -    match device.enable_encrypted_volume(&passphrase) { -      Ok(()) => return Ok(()), -      Err(err) => match err { -        nitrokey::CommandError::WrongPassword => { -          pinentry::clear_passphrase(PIN_TYPE)?; -          retry -= 1; - -          if retry > 0 { -            error_msg = Some("Wrong password, please reenter"); -            continue; -          } -          let error = "Opening encrypted volume failed: Wrong password"; -          return Err(Error::Error(error.to_string())); -        } -        err => return Err(get_error("Opening encrypted volume failed", &err)), -      }, -    }; -  } -} - -#[link(name = "c")] -extern "C" { -  fn sync(); -} - -/// Close the previously opened encrypted volume. -fn close() -> Result<()> { -  // Flush all filesystem caches to disk. We are mostly interested in -  // making sure that the encrypted volume on the nitrokey we are -  // about to close is not closed while not all data was written to -  // it. -  unsafe { sync() }; - -  get_storage_device()? -    .disable_encrypted_volume() -    .map_err(|err| get_error("Closing encrypted volume failed", &err)) -} - -/// Clear the PIN stored when opening the nitrokey's encrypted volume. -fn clear() -> Result<()> { -  pinentry::clear_passphrase(PIN_TYPE) -} -  // A macro for generating a match of the different supported commands.  // Each supplied command is converted into a string and matched against.  macro_rules! commands { @@ -237,7 +111,7 @@ fn run() -> i32 {      return 1;    } -  commands!(&argv[1], [status, open, close, clear]); +  commands!(&argv[1], [commands::status, commands::open, commands::close, commands::clear]);  }  fn main() { | 
