Skip to content

Commit

Permalink
Support reading OTP secrets from stdin
Browse files Browse the repository at this point in the history
In an earlier commit we added support for reading PWS passwords from
stdin. With this patch, we use the same mechanism for OTP secrets.
  • Loading branch information
robinkrahl authored and d-e-s-o committed Apr 24, 2021
1 parent b8b8579 commit 91fa845
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 6 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Unreleased
- Removed the `pws set` subcommand
- Added the `--only-aes-key` option to the `reset` command to build a new AES
key without performing a factory reset
- Added support for reading PWS passwords from stdin
- Added support for reading PWS passwords and OTP secrets from stdin
- Added `NITROCLI_RESOLVED_USB_PATH` environment variable to be used by
extensions
- Allowed entering of `base32` encoded strings containing spaces
Expand Down
6 changes: 4 additions & 2 deletions doc/nitrocli.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH NITROCLI 1 2021-04-20
.TH NITROCLI 1 2021-04-24
.SH NAME
nitrocli \- access Nitrokey devices
.SH SYNOPSIS
Expand Down Expand Up @@ -182,7 +182,7 @@ If \fB\-\-time\fR is set, it is set to \fItime\fR instead, which must be a Unix
timestamp (i.e., the number of seconds since 1970-01-01 00:00:00 UTC).
This command might require the user PIN (see the Configuration section).
.TP
\fBnitrocli otp set \fIslot name secret \
\fBnitrocli otp set \fIslot name secret\fR|\fB-\fR \
\fR[\fB\-a\fR|\fB\-\-algorithm \fIalgorithm\fR] \
[\fB\-d\fR|\fB\-\-digits \fIdigits\fR] [\fB\-c\fR|\fB\-\-counter \fIcounter\fR] \
[\fB\-t\fR|\fB\-\-time-window \fItime-window\fR] \
Expand All @@ -191,6 +191,8 @@ Configure a one-time password slot.
\fIslot\fR is the number of the slot to configure.
\fIname\fR is the name of the slot (may not be empty).
\fIsecret\fR is the secret value to store in that slot.
If \fIsecret\fR is set to \fB-\fR, the secret is read from the standard
input.

The \fB\-\-format\fR option specifies the format of the secret.
If it is set to \fBascii\fR, each character of the given secret is interpreted
Expand Down
Binary file modified doc/nitrocli.1.pdf
Binary file not shown.
10 changes: 7 additions & 3 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,15 +824,18 @@ fn prepare_base32_secret(secret: &str) -> anyhow::Result<String> {
}

/// Prepare a secret string in the given format for libnitrokey.
fn prepare_secret(secret: String, format: args::OtpSecretFormat) -> anyhow::Result<String> {
fn prepare_secret(
secret: borrow::Cow<'_, str>,
format: args::OtpSecretFormat,
) -> anyhow::Result<String> {
match format {
args::OtpSecretFormat::Ascii => prepare_ascii_secret(&secret),
args::OtpSecretFormat::Base32 => prepare_base32_secret(&secret),
args::OtpSecretFormat::Hex => {
// We need to ensure to provide a string with an even number of
// characters in it, just because that's what libnitrokey
// expects. So prepend a '0' if that is not the case.
let mut secret = secret;
let mut secret = secret.into_owned();
if secret.len() % 2 != 0 {
secret.insert(0, '0')
}
Expand All @@ -843,7 +846,8 @@ fn prepare_secret(secret: String, format: args::OtpSecretFormat) -> anyhow::Resu

/// Configure a one-time password slot on the Nitrokey device.
pub fn otp_set(ctx: &mut Context<'_>, args: args::OtpSetArgs) -> anyhow::Result<()> {
let secret = prepare_secret(args.secret, args.format)?;
let secret = value_or_stdin(ctx, &args.secret)?;
let secret = prepare_secret(secret, args.format)?;
let data = nitrokey::OtpSlotData::new(args.slot, args.name, secret, args.digits.into());
let (algorithm, counter, time_window) = (args.algorithm, args.counter, args.time_window);
with_device(ctx, |ctx, device| {
Expand Down
18 changes: 18 additions & 0 deletions src/tests/otp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,24 @@ fn set_totp_uneven_chars(model: nitrokey::Model) -> anyhow::Result<()> {
Ok(())
}

#[test_device]
fn set_stdin(model: nitrokey::Model) -> anyhow::Result<()> {
const SECRET: &str = "12345678901234567890";
const TIME: &str = stringify!(1111111111);
const OTP: &str = concat!(14050471, "\n");

let _ = Nitrocli::new()
.model(model)
.stdin(SECRET)
.handle(&["otp", "set", "-d", "8", "-f", "ascii", "2", "name", "-"])?;

let out = Nitrocli::new()
.model(model)
.handle(&["otp", "get", "-t", TIME, "2"])?;
assert_eq!(out, OTP);
Ok(())
}

#[test_device]
fn clear(model: nitrokey::Model) -> anyhow::Result<()> {
let mut ncli = Nitrocli::new().model(model);
Expand Down

0 comments on commit 91fa845

Please sign in to comment.