From f038b53dfaf68be0d52d1d8aa3d2df922aabd787 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 16 Jan 2019 01:03:30 +0000 Subject: Add the reset command to perform a factory reset After performing the factory reset, we also build the AES key so that the device is fully usable. Due to timing issue, we have to add a delay between the factory reset and building the AES key. --- nitrocli/src/args.rs | 10 ++++++++++ nitrocli/src/commands.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) (limited to 'nitrocli/src') diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs index 3b89bf1..43f866d 100644 --- a/nitrocli/src/args.rs +++ b/nitrocli/src/args.rs @@ -81,6 +81,7 @@ Enum! {Command, [ Otp => ("otp", otp), Pin => ("pin", pin), Pws => ("pws", pws), + Reset => ("reset", reset), Status => ("status", status), Storage => ("storage", storage) ]} @@ -192,6 +193,15 @@ fn status(ctx: &mut ExecCtx<'_>, args: Vec) -> Result<()> { commands::status(ctx) } +/// Perform a factory reset. +fn reset(ctx: &mut ExecCtx<'_>, args: Vec) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Performs a factory reset"); + parse(ctx, &parser, args)?; + + commands::reset(ctx) +} + Enum! {StorageCommand, [ Close => ("close", storage_close), Hidden => ("hidden", storage_hidden), diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index 989abbf..aed0319 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -19,6 +19,7 @@ use std::fmt; use std::result; +use std::thread; use std::time; use std::u8; @@ -33,6 +34,8 @@ use crate::error::Error; use crate::pinentry; use crate::Result; +const NITROKEY_DEFAULT_ADMIN_PIN: &str = "12345678"; + /// Create an `error::Error` with an error message of the format `msg: err`. fn get_error(msg: &'static str, err: nitrokey::CommandError) -> Error { Error::CommandError(Some(msg), err) @@ -291,6 +294,29 @@ pub fn status(ctx: &mut args::ExecCtx<'_>) -> Result<()> { print_status(ctx, model, &device) } +/// Perform a factory reset. +pub fn reset(ctx: &mut args::ExecCtx<'_>) -> Result<()> { + let device = get_device(ctx)?; + let pin_entry = pinentry::PinEntry::from(pinentry::PinType::Admin, &device)?; + + // To force the user to enter the admin PIN before performing a + // factory reset, we clear the pinentry cache for the admin PIN. + pinentry::clear(&pin_entry)?; + + try_with_pin(ctx, &pin_entry, "Factory reset failed", |pin| { + device.factory_reset(&pin)?; + // Work around for a timing issue between factory_reset and + // build_aes_key, see + // https://github.com/Nitrokey/nitrokey-storage-firmware/issues/80 + thread::sleep(time::Duration::from_secs(3)); + // Another work around for spurious WrongPassword returns of + // build_aes_key after a factory reset on Pro devices. + // https://github.com/Nitrokey/nitrokey-pro-firmware/issues/57 + let _ = device.get_user_retry_count(); + device.build_aes_key(NITROKEY_DEFAULT_ADMIN_PIN) + }) +} + /// Open the encrypted volume on the nitrokey. pub fn storage_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { let device = get_storage_device(ctx)?; -- cgit v1.2.1