1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
//! Simple usage of OTP primitives
use super::{
	Array,
	HOTP,
	TOTP,
};
use std::{
	error::Error,
	fmt,
	primitive::str,
	time::*,
};
#[derive(Debug)]
/// Errors regarding the errors that may come up.
///
/// 2.x.x: Added `VerificationFailed`, giving a more notable error.
pub enum OTPErr {
	/// System time less than zero
	Time(SystemTimeError),
	/// Character was not in the base-10 radix
	Radix(char),
	/// Overflow occured
	Overflow,
	/// Verification failed
	VerificationFailed,
}
impl fmt::Display for OTPErr {
	fn fmt(
		&self,
		f: &mut fmt::Formatter,
	) -> fmt::Result {
		use OTPErr::*;
		match self {
			Time(e) => write!(
				f,
				"system time was less than unix epoch by {:?}",
				e.duration(),
			),
			Radix(c) => write!(f, "char {:?} is not a base-10 digit", c),
			Overflow => write!(f, "otp would overflow an i32"),
			VerificationFailed => write!(f, "verification failed"),
		}
	}
}
/// Turns some input into a u32
fn input_to_i32(input: &str) -> Result<i32, OTPErr> {
	input
		.split_ascii_whitespace()
		.flat_map(str::chars)
		.try_fold(0i32, |acc, val| {
			let add = val.to_digit(10).ok_or_else(|| OTPErr::Radix(val))?;
			acc
				.checked_mul(10)
				.ok_or_else(|| OTPErr::Overflow)?
				.checked_add(add as i32)
				.ok_or_else(|| OTPErr::Overflow)
		})
}
impl Error for OTPErr {}
/// Reads a google authenticator-compatible TOTP with a secret
///
/// v2.x.x: Made return value a `Result<()>` instead of `Result<bool>`.
pub fn read_totp_gauth<Val, Secret>(
	input: Val,
	secret: Secret,
) -> Result<(), OTPErr>
where
	Val: AsRef<str>,
	Secret: Array,
{
	let time = SystemTime::now()
		.duration_since(SystemTime::UNIX_EPOCH)
		.map_err(|e| OTPErr::Time(e))?
		.as_secs();
	let input = input_to_i32(input.as_ref())?;
	if TOTP::new(secret).verify_range_default(time, input as u32) {
		Ok(())
	} else {
		Err(OTPErr::VerificationFailed)
	}
}
/// Reads a google authenticator-compatible HOTP with a secret
///
/// v2.x.x: Made return value a `Result<()>` instead of `Result<bool>`.
pub fn read_hotp_gauth<Val, Secret>(
	input: Val,
	secret: Secret,
	counter: u64,
) -> Result<(), OTPErr>
where
	Val: AsRef<str>,
	Secret: Array,
{
	let input = input_to_i32(input.as_ref())?;
	if HOTP::new(secret).verify(counter, input as u32) {
		Ok(())
	} else {
		Err(OTPErr::VerificationFailed)
	}
}