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
#![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.

#[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 {
	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;