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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#![deny(missing_docs, missing_debug_implementations)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "unsize", feature(fixed_size_array))]
//! # `miniotp`
//!
//! An otp crate that aims for correctness of implementation, while giving the same speed that one
//! would expect.
//!
//! Features:
//!
//! - `std` - on by default; enables use of system time and/or allocations.
//! - `alloc` - off by default; enables use of the [`alloc`](https://doc.rust-lang.org/alloc/)
//!   crate where allocations are required.
//! - `only-gauth` - you only use google authenticator-valid codes. This disables some features,
//!   like setting the length, algorithm, period, or epoch. This is enabled by default.
//! - `cstr` - enables support for creating a code from a `AsRef<CStr>`; for easier compatibility
//!   with [`noria::DataType::Text(_)`](https://docs.rs/noria/0.1.2), or other data bases.
//! - `base32` - on by default; enables a set of functions related to taking an OTP secret as or
//!   into a string.
//! - `serde` - off by default; adds (de)serialisation ability to TOTP and HOTP structs.

#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(all(feature = "base32", any(feature = "std", feature = "alloc")))]
pub(crate) const B32A: base32::Alphabet =
	base32::Alphabet::RFC4648 { padding: false };

#[cfg(not(feature = "only-gauth"))]
pub(crate) mod alg;
#[cfg(not(feature = "only-gauth"))]
pub use alg::*;
mod array;
pub use array::Array;
mod hotp;
pub use hotp::HOTP;
mod totp;
pub use totp::TOTP;

#[cfg(all(feature = "base32", any(feature = "alloc", feature = "std")))]
mod err {
	#[cfg(feature = "cstr")]
	use std::str::Utf8Error;
	/// Mapping error type.
	#[derive(Debug)]
	pub enum Error {
		#[cfg(feature = "cstr")]
		/// UTF-8 encoding error
		///
		/// Only returned when using a cstring.
		Utf8(Utf8Error),
		/// Bad base32 encoding
		BadEnc,
	}
	impl core::fmt::Display for Error {
		fn fmt(
			&self,
			f: &mut core::fmt::Formatter,
		) -> core::fmt::Result {
			match self {
				#[cfg(feature = "cstr")]
				Self::Utf8(ref e) => core::fmt::Display::fmt(e, f),
				Self::BadEnc => f.write_str("Bad base32 encoding"),
			}
		}
	}
	#[cfg(feature = "cstr")]
	impl From<Utf8Error> for Error {
		fn from(e: Utf8Error) -> Self {
			Self::Utf8(e)
		}
	}
	#[cfg(feature = "std")]
	impl std::error::Error for Error {}
}
#[cfg(all(feature = "base32", any(feature = "alloc", feature = "std")))]
pub use err::Error;
#[cfg(all(feature = "base32", any(feature = "alloc", feature = "std")))]
mod segs {
	#[cfg(feature = "alloc")]
	use alloc::string::String;
	use core::cmp::min;
	/// A helper iterator for splitting secrets into more human readable pieces of text.
	#[derive(Debug)]
	pub struct Segs {
		pub(crate) sec: String,
		pub(crate) chunk_size: usize,
		pub(crate) curr: usize,
	}
	impl Iterator for Segs {
		type Item = String;

		fn next(&mut self) -> Option<Self::Item> {
			if self.curr >= self.sec.len() {
				None
			} else {
				let (off, ()) = (self.curr, self.curr += self.chunk_size);
				let end = min(self.curr, self.sec.len());
				let next = self.sec.get(off..end)?;
				Some(next.into())
			}
		}
	}
	impl Segs {
		/// Joins segments into a string.
		///
		/// ```rust
		/// use miniotp::HOTP;
		/// let sec_human = HOTP::new(b"HUMAN").base32_segs(4);
		/// assert_eq!(sec_human.join(" "), "JBKU 2QKO");
		/// ```
		pub fn join<S: AsRef<str>>(
			self,
			sep: S,
		) -> String {
			let s = sep.as_ref();
			let cap =
				self.sec.len() + s.len() * (self.sec.len() / self.chunk_size) + 1;
			let mut first = true;
			self.fold(String::with_capacity(cap), |mut acc, seg| {
				if first {
					first = false;
				} else {
					acc.push_str(s);
				}
				acc.push_str(&seg);
				acc
			})
		}
	}
}
#[cfg(all(feature = "base32", any(feature = "alloc", feature = "std")))]
pub use segs::Segs;