From a83454bcc9cb3f7d10b4ee5926490c80b222430b Mon Sep 17 00:00:00 2001 From: Daniel Mueller Date: Sat, 8 Jun 2019 11:02:12 -0700 Subject: Add support for changing read-write mode of unencrypted volume This change adds support for changing the read-write mode of the unencrypted volume. To do so, we introduce a new top-level command, unencrypted, with a new subcommand, set, that accepts the new mode of the volume. --- nitrocli/CHANGELOG.md | 2 ++ nitrocli/README.md | 2 ++ nitrocli/doc/nitrocli.1 | 11 ++++++++- nitrocli/doc/nitrocli.1.pdf | Bin 18095 -> 18442 bytes nitrocli/src/args.rs | 50 ++++++++++++++++++++++++++++++++++++++ nitrocli/src/commands.rs | 24 ++++++++++++++++++ nitrocli/src/tests/mod.rs | 1 + nitrocli/src/tests/unencrypted.rs | 41 +++++++++++++++++++++++++++++++ 8 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 nitrocli/src/tests/unencrypted.rs diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md index 601284f..d43b412 100644 --- a/nitrocli/CHANGELOG.md +++ b/nitrocli/CHANGELOG.md @@ -1,5 +1,7 @@ Unreleased ---------- +- Added `unencrypted` command with `set` subcommand for changing the + unencrypted volume's read-write mode - Changed `storage hidden` subcommand to `hidden` top-level command - Renamed `storage` command to `encrypted` - Removed `storage status` subcommand diff --git a/nitrocli/README.md b/nitrocli/README.md index 0a30696..7e15425 100644 --- a/nitrocli/README.md +++ b/nitrocli/README.md @@ -39,6 +39,8 @@ The following commands are currently supported: - set: Set the data on a PWS slot. - status: List all PWS slots. - clear: Delete a PWS slot. +- unencrypted: Work with the Nitrokey Storage's unencrypted volume. + - set: Change the read-write mode of the unencrypted volume. Usage diff --git a/nitrocli/doc/nitrocli.1 b/nitrocli/doc/nitrocli.1 index 3c1e1e4..75f5960 100644 --- a/nitrocli/doc/nitrocli.1 +++ b/nitrocli/doc/nitrocli.1 @@ -1,4 +1,4 @@ -.TH NITROCLI 1 2019-05-26 +.TH NITROCLI 1 2019-06-08 .SH NAME nitrocli \- access Nitrokey devices .SH SYNOPSIS @@ -63,6 +63,15 @@ this overlay (which is required to achieve plausible deniability of the existence of hidden volumes), the burden of ensuring that data on the encrypted volume does not overlap with data on one of the hidden volumes is on the user. .TP +\fBnitrocli unencrypted set \fImode\fR +Change the read-write mode of the volume. +\fImode\fR is the type of the mode to change to: \fBread-write\fR to make the +volume readable and writable or \fBread-only\fR to make it only readable. +This command requires the admin PIN. + +Note that this command requires firmware version 0.51 or higher. Earlier +versions are not supported. +.TP \fBnitrocli encrypted open Open the encrypted volume on the Nitrokey Storage. The user PIN that is required to open the volume is queried using diff --git a/nitrocli/doc/nitrocli.1.pdf b/nitrocli/doc/nitrocli.1.pdf index 0384072..cd15a66 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 16ff314..ae09c07 100644 --- a/nitrocli/src/args.rs +++ b/nitrocli/src/args.rs @@ -129,6 +129,7 @@ Enum! {Command, [ Pws => ("pws", pws), Reset => ("reset", reset), Status => ("status", status), + Unencrypted => ("unencrypted", unencrypted), ]} Enum! {ConfigCommand, [ @@ -247,6 +248,55 @@ fn reset(ctx: &mut ExecCtx<'_>, args: Vec) -> Result<()> { commands::reset(ctx) } +Enum! {UnencryptedCommand, [ + Set => ("set", unencrypted_set), +]} + +Enum! {UnencryptedVolumeMode, [ + ReadWrite => "read-write", + ReadOnly => "read-only", +]} + +/// Execute an unencrypted subcommand. +fn unencrypted(ctx: &mut ExecCtx<'_>, args: Vec) -> Result<()> { + let mut subcommand = UnencryptedCommand::Set; + let help = cmd_help!(subcommand); + let mut subargs = vec![]; + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Interacts with the device's unencrypted volume"); + let _ = + parser + .refer(&mut subcommand) + .required() + .add_argument("subcommand", argparse::Store, &help); + let _ = parser.refer(&mut subargs).add_argument( + "arguments", + argparse::List, + "The arguments for the subcommand", + ); + parser.stop_on_first_argument(true); + parse(ctx, parser, args)?; + + subargs.insert(0, format!("nitrocli {}", subcommand)); + subcommand.execute(ctx, subargs) +} + +/// Change the configuration of the unencrypted volume. +fn unencrypted_set(ctx: &mut ExecCtx<'_>, args: Vec) -> Result<()> { + let mut mode = UnencryptedVolumeMode::ReadWrite; + let help = format!("The mode to change to ({})", fmt_enum!(mode)); + let mut parser = argparse::ArgumentParser::new(); + parser + .set_description("Changes the configuration of the unencrypted volume on a Nitrokey Storage"); + let _ = parser + .refer(&mut mode) + .required() + .add_argument("type", argparse::Store, &help); + parse(ctx, parser, args)?; + + commands::unencrypted_set(ctx, mode) +} + Enum! {EncryptedCommand, [ Close => ("close", encrypted_close), Open => ("open", encrypted_open), diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index 3cad8f1..8db5cd8 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -361,6 +361,30 @@ pub fn reset(ctx: &mut args::ExecCtx<'_>) -> Result<()> { }) } +/// Change the configuration of the unencrypted volume. +pub fn unencrypted_set( + ctx: &mut args::ExecCtx<'_>, + mode: args::UnencryptedVolumeMode, +) -> Result<()> { + let device = get_storage_device(ctx)?; + let pin_entry = pinentry::PinEntry::from(pinentry::PinType::Admin, &device)?; + let mode = match mode { + args::UnencryptedVolumeMode::ReadWrite => nitrokey::VolumeMode::ReadWrite, + args::UnencryptedVolumeMode::ReadOnly => nitrokey::VolumeMode::ReadOnly, + }; + + // The unencrypted volume may reconnect, so be sure to flush caches to + // disk. + unsafe { sync() }; + + try_with_pin( + ctx, + &pin_entry, + "Changing unencrypted volume mode failed", + |pin| device.set_unencrypted_volume_mode(&pin, mode), + ) +} + /// Open the encrypted volume on the Nitrokey. pub fn encrypted_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { let device = get_storage_device(ctx)?; diff --git a/nitrocli/src/tests/mod.rs b/nitrocli/src/tests/mod.rs index 70a3d20..2f2926f 100644 --- a/nitrocli/src/tests/mod.rs +++ b/nitrocli/src/tests/mod.rs @@ -46,6 +46,7 @@ mod pws; mod reset; mod run; mod status; +mod unencrypted; /// A trait simplifying checking for expected errors. pub trait UnwrapError { diff --git a/nitrocli/src/tests/unencrypted.rs b/nitrocli/src/tests/unencrypted.rs new file mode 100644 index 0000000..c976f50 --- /dev/null +++ b/nitrocli/src/tests/unencrypted.rs @@ -0,0 +1,41 @@ +// unencrypted.rs + +// ************************************************************************* +// * Copyright (C) 2019 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 . * +// ************************************************************************* + +use super::*; + +#[test_device] +fn unencrypted_set_read_write(device: nitrokey::Storage) -> crate::Result<()> { + let mut ncli = Nitrocli::with_dev(device); + let out = ncli.handle(&["unencrypted", "set", "read-write"])?; + assert!(out.is_empty()); + + let device = nitrokey::Storage::connect()?; + assert!(device.get_status()?.unencrypted_volume.active); + assert!(!device.get_status()?.unencrypted_volume.read_only); + drop(device); + + let out = ncli.handle(&["unencrypted", "set", "read-only"])?; + assert!(out.is_empty()); + + let device = nitrokey::Storage::connect()?; + assert!(device.get_status()?.unencrypted_volume.active); + assert!(device.get_status()?.unencrypted_volume.read_only); + + Ok(()) +} -- cgit v1.2.3