From 1d1cc940f47c41637adea5c5a1e5d3c80807f9d7 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Sun, 6 Jan 2019 00:23:27 +0100 Subject: Add the base32 format for OTP secrets Many applications display OTP secrets in the base32 format (according to RFC 4648). This patch adds base32 as a possible value for the --format option to the otp set subcommand. --- nitrocli/doc/nitrocli.1 | 9 ++++++++- nitrocli/doc/nitrocli.1.pdf | Bin 14203 -> 14513 bytes nitrocli/src/args.rs | 5 ++++- nitrocli/src/commands.rs | 8 ++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/nitrocli/doc/nitrocli.1 b/nitrocli/doc/nitrocli.1 index 16e06a0..036c25f 100644 --- a/nitrocli/doc/nitrocli.1 +++ b/nitrocli/doc/nitrocli.1 @@ -83,7 +83,7 @@ This command might require the user PIN (see the Configuration section). \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] \ -[\fB-f\fR|\fB\-\-format ascii\fR|\fBhex\fR] +[\fB-f\fR|\fB\-\-format ascii\fR|\fBbase32\fR|\fBhex\fR] 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). @@ -92,6 +92,8 @@ Configure a one-time password slot. 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 as the ASCII code of one byte. +If it is set to \fBbase32\fR, the secret is interpreted as a base32 string +according to RFC 4648. If it is set to \fBhex\fR, every two characters are interpreted as the hexadecimal value of one byte. The default value is \fBhex\fR. @@ -230,6 +232,11 @@ Configure a one-time password slot with an ASCII secret representation: $ \fBnitrocli otp set 1 test\-foobar foobar \-\-format ascii \-\-algorithm hotp\fR $ \fBnitrocli otp set 0 test\-rfc6238 12345678901234567890 \-\-format ascii \-\-algorithm totp \-\-digits 8\fR .P +Configure a one-time password slot with a base32 secret representation: + $ \fBnitrocli otp set 0 test\-rfc4226 gezdgnbvgy3tqojqgezdgnbvgy3tqojq \-\-format base32 \-\-algorithm hotp\fR + $ \fBnitrocli otp set 1 test\-foobar mzxw6ytboi====== \-\-format base32 \-\-algorithm hotp\fR + $ \fBnitrocli otp set 0 test\-rfc6238 gezdgnbvgy3tqojqgezdgnbvgy3tqojq \-\-format base32 \-\-algorithm totp \-\-digits 8\fR +.P Generate a one-time password: $ \fBnitrocli otp get 0 \-\-algorithm hotp\fR 755224 diff --git a/nitrocli/doc/nitrocli.1.pdf b/nitrocli/doc/nitrocli.1.pdf index 4a3528f..5fa9049 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 b109944..b5d4e81 100644 --- a/nitrocli/src/args.rs +++ b/nitrocli/src/args.rs @@ -327,6 +327,7 @@ impl From for nitrokey::OtpMode { #[derive(Clone, Copy, Debug, PartialEq)] pub enum OtpSecretFormat { Ascii, + Base32, Hex, } @@ -337,6 +338,7 @@ impl fmt::Display for OtpSecretFormat { "{}", match *self { OtpSecretFormat::Ascii => "ascii", + OtpSecretFormat::Base32 => "base32", OtpSecretFormat::Hex => "hex", } ) @@ -349,6 +351,7 @@ impl str::FromStr for OtpSecretFormat { fn from_str(s: &str) -> result::Result { match s { "ascii" => Ok(OtpSecretFormat::Ascii), + "base32" => Ok(OtpSecretFormat::Base32), "hex" => Ok(OtpSecretFormat::Hex), _ => Err(()), } @@ -777,7 +780,7 @@ pub fn otp_set(ctx: &ExecCtx, args: Vec) -> Result<()> { let _ = parser.refer(&mut secret_format).add_option( &["-f", "--format"], argparse::StoreOption, - "The format of the secret (ascii|hex)", + "The format of the secret (ascii|base32|hex)", ); parse(&parser, args)?; drop(parser); diff --git a/nitrocli/src/commands.rs b/nitrocli/src/commands.rs index 71b2bdd..289c257 100644 --- a/nitrocli/src/commands.rs +++ b/nitrocli/src/commands.rs @@ -457,6 +457,13 @@ fn prepare_ascii_secret(secret: &str) -> Result { } } +/// Prepare a base32 secret string for libnitrokey. +fn prepare_base32_secret(secret: &str) -> Result { + base32::decode(base32::Alphabet::RFC4648 { padding: false }, secret) + .map(|vec| format_bytes(&vec)) + .ok_or_else(|| Error::Error("Could not parse base32 secret".to_string())) +} + /// Configure a one-time password slot on the Nitrokey device. pub fn otp_set( ctx: &args::ExecCtx, @@ -468,6 +475,7 @@ pub fn otp_set( ) -> Result<()> { let secret = match secret_format { args::OtpSecretFormat::Ascii => prepare_ascii_secret(&data.secret)?, + args::OtpSecretFormat::Base32 => prepare_base32_secret(&data.secret)?, args::OtpSecretFormat::Hex => data.secret, }; let data = nitrokey::OtpSlotData { secret, ..data }; -- cgit v1.2.1