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/CHANGELOG.md | 1 + nitrocli/doc/nitrocli.1 | 12 +++++++++++- nitrocli/doc/nitrocli.1.pdf | Bin 17334 -> 17751 bytes nitrocli/src/args.rs | 10 ++++++++++ nitrocli/src/commands.rs | 26 ++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md index 9eb8f17..8e70530 100644 --- a/nitrocli/CHANGELOG.md +++ b/nitrocli/CHANGELOG.md @@ -1,5 +1,6 @@ Unreleased ---------- +- Added the `reset` command to perform a factory reset - Added the `-V`/`--version` option to print the program's version diff --git a/nitrocli/doc/nitrocli.1 b/nitrocli/doc/nitrocli.1 index 74fd635..66d73f9 100644 --- a/nitrocli/doc/nitrocli.1 +++ b/nitrocli/doc/nitrocli.1 @@ -41,6 +41,16 @@ Lock the Nitrokey. This command locks the password safe (see the Password safe section). On the Nitrokey Storage, it will also close any active encrypted or hidden volumes (see the Storage section). +.TP +.B nitrocli reset +Perform a factory reset on the Nitrokey. +This command performs a factory reset on the OpenPGP smart card, clears the +flash storage and builds a new AES key. +The user PIN is reset to 123456, the admin PIN to 12345678. + +This command requires the admin PIN. +To avoid accidental calls of this command, the user has to enter the PIN even +if it has been cached. .SS Storage The Nitrokey Storage comes with a storage area. This area is comprised of an @@ -226,7 +236,7 @@ The initial retry counter is three. If the retry counter for the user PIN is zero, you can use the \fBpin unblock\fR command to unblock and reset the user PIN. If the retry counter for the admin PIN is zero, you have to perform a factory -reset using \fBgpg\fR(1). +reset using the \fBreset\fR command or \fBgpg\fR(1). Use the \fBstatus\fR command to check the retry counters. .TP .B nitrocli pin clear diff --git a/nitrocli/doc/nitrocli.1.pdf b/nitrocli/doc/nitrocli.1.pdf index 32cf085..ff72f94 100644 Binary files a/nitrocli/doc/nitrocli.1.pdf and b/nitrocli/doc/nitrocli.1.pdf differ 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.3