From 5a4eb9908d6fc587818f2e49cdb7edd64d458e80 Mon Sep 17 00:00:00 2001 From: "M. Sandoval" Date: Fri, 5 Feb 2021 10:09:59 +0000 Subject: [PATCH] otp: Use binascii to decode base32 --- Cargo.lock | 8 +++++++- Cargo.toml | 2 +- src/models/otp.rs | 45 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6506a14..477ae79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -259,7 +259,7 @@ dependencies = [ "anyhow", "ashpd", "async-std", - "data-encoding", + "binascii", "diesel", "diesel_migrations", "futures", @@ -314,6 +314,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + [[package]] name = "bitflags" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index 9f0ccd3..d0f7113 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ adw = {package = "libadwaita", git = "https://gitlab.gnome.org/bilelmoussaoui/li anyhow = "1.0" ashpd = {git = "https://github.com/bilelmoussaoui/ashpd", features = ["feature_gtk4"]} async-std = "1.9" -data-encoding = "2.3" +binascii = "0.1" diesel = {version = "1.4", features = ["sqlite", "r2d2"]} diesel_migrations = {version = "1.4", features = ["sqlite"]} futures = "0.3" diff --git a/src/models/otp.rs b/src/models/otp.rs index 10382f2..381ae5f 100644 --- a/src/models/otp.rs +++ b/src/models/otp.rs @@ -1,5 +1,5 @@ use super::Algorithm; -use anyhow::Result; +use anyhow::{anyhow, Result}; use ring::hmac; use std::convert::TryInto; use std::time::{SystemTime, UNIX_EPOCH}; @@ -18,10 +18,14 @@ pub static TOTP_DEFAULT_PERIOD: u32 = 30; /// into a byte string. It fails if secret is not a valid Base32 string. fn decode_secret(secret: &str) -> Result> { let secret = secret.trim().replace(' ', "").to_uppercase(); + // The buffer should have a length of secret.len() * 5 / 8. + let size = secret.len(); + let mut output_buffer = std::iter::repeat(0).take(size).collect::>(); + let vec = binascii::b32decode(secret.as_bytes(), &mut output_buffer) + .map_err(|_| anyhow!("Invalid Input"))? + .to_vec(); - data_encoding::BASE32_NOPAD - .decode(secret.as_bytes()) - .map_err(From::from) + Ok(vec) } /// Validates if `secret` is a valid Base32 String. @@ -96,13 +100,29 @@ pub(crate) fn time_based_counter(period: u32) -> u64 { #[cfg(test)] mod tests { use super::{format, hotp, steam, Algorithm, DEFAULT_DIGITS, TOTP_DEFAULT_PERIOD}; - use data_encoding::BASE32_NOPAD; #[test] fn test_totp() { - let secret_sha1 = BASE32_NOPAD.encode(b"12345678901234567890"); - let secret_sha256 = BASE32_NOPAD.encode(b"12345678901234567890123456789012"); - let secret_sha512 = BASE32_NOPAD - .encode(b"1234567890123456789012345678901234567890123456789012345678901234"); + let secret_sha1 = String::from_utf8( + binascii::b32encode(b"12345678901234567890", &mut [0; 64]) + .unwrap() + .to_vec(), + ) + .unwrap(); + let secret_sha256 = String::from_utf8( + binascii::b32encode(b"12345678901234567890123456789012", &mut [0; 64]) + .unwrap() + .to_vec(), + ) + .unwrap(); + let secret_sha512 = String::from_utf8( + binascii::b32encode( + b"1234567890123456789012345678901234567890123456789012345678901234", + &mut [0; 128], + ) + .unwrap() + .to_vec(), + ) + .unwrap(); let counter1 = 59 / TOTP_DEFAULT_PERIOD as u64; assert_eq!( @@ -204,7 +224,12 @@ mod tests { hotp("BASE32SECRET3232", 1401, Algorithm::SHA1, DEFAULT_DIGITS).ok(), Some(316439) ); - let secret = BASE32_NOPAD.encode(b"12345678901234567890"); + let secret = String::from_utf8( + binascii::b32encode(b"12345678901234567890", &mut [0; 64]) + .unwrap() + .to_vec(), + ) + .unwrap(); assert_eq!( Some(755224), hotp(&secret, 0, Algorithm::SHA1, DEFAULT_DIGITS).ok()