libreauth 0.18.1

Collection of user authentication tools.
Documentation
//! Implementation of standards which are part of the [OATH Reference
//! Architecture](https://openauthentication.org/specifications-technical-resources/).
//!
//! ## Examples
//!
//! ```rust
//! let key_ascii = "12345678901234567890".to_owned();
//! let mut hotp = libreauth::oath::HOTPBuilder::new()
//!     .ascii_key(&key_ascii)
//!     .finalize()
//!     .unwrap();
//!
//! let code = hotp.generate();
//! assert_eq!(code, "755224");
//! assert!(hotp.is_valid(&"755224".to_owned()));
//!
//! let code = hotp.increment_counter().generate();
//! assert_eq!(code, "287082");
//! assert!(hotp.is_valid(&"287082".to_owned()));
//! ```
//!
//! ```rust
//! let key_base32 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ".to_owned();
//! let mut totp = libreauth::oath::TOTPBuilder::new()
//!     .base32_key(&key_base32)
//!     .finalize()
//!     .unwrap();
//!
//! let code = totp.generate();
//! println!("TOTP code: {}", code);
//!
//! assert!(totp.is_valid(&code));
//! ```

use crate::hash::HashFunction;

#[cfg(feature = "oath-uri")]
const DEFAULT_KEY_URI_PARAM_POLICY: ParametersVisibility = ParametersVisibility::ShowNonDefault;
const DEFAULT_OTP_HASH: HashFunction = HashFunction::Sha1;
const DEFAULT_OTP_OUT_BASE: &str = "0123456789";
const DEFAULT_OTP_OUT_LEN: usize = 6;
const DEFAULT_TOTP_PERIOD: u32 = 30;
const DEFAULT_TOTP_T0: u64 = 0;
const DEFAULT_LOOK_AHEAD: u64 = 0;

/// Error codes used in the C interface.
///
/// ## C interface
/// The C interface uses an enum of type `libreauth_oath_errno` and the
/// members has been renamed as follows:
/// <table>
///     <thead>
///         <tr>
///             <th>Rust</th>
///             <th>C</th>
///         </tr>
///     </thead>
///     <tbody>
///         <tr>
///             <td>Success</td>
///             <td>LIBREAUTH_OATH_SUCCESS</td>
///         </tr>
///         <tr>
///             <td>NullPtr</td>
///             <td>LIBREAUTH_OATH_NULL_PTR</td>
///         </tr>
///         <tr>
///             <td>NotEnoughSpace</td>
///             <td>LIBREAUTH_OATH_NOT_ENOUGH_SPACE</td>
///         </tr>
///         <tr>
///             <td>InvalidBaseLen</td>
///             <td>LIBREAUTH_OATH_INVALID_BASE_LEN</td>
///         </tr>
///         <tr>
///             <td>InvalidKeyLen</td>
///             <td>LIBREAUTH_OATH_INVALID_KEY_LEN</td>
///         </tr>
///         <tr>
///             <td>CodeTooSmall</td>
///             <td>LIBREAUTH_OATH_CODE_TOO_SMALL</td>
///         </tr>
///         <tr>
///             <td>CodeTooBig</td>
///             <td>LIBREAUTH_OATH_CODE_TOO_BIG</td>
///         </tr>
///         <tr>
///             <td>InvalidKey</td>
///             <td>LIBREAUTH_OATH_INVALID_KEY</td>
///         </tr>
///         <tr>
///             <td>InvalidPeriod</td>
///             <td>LIBREAUTH_OATH_INVALID_PERIOD</td>
///         </tr>
///         <tr>
///             <td>InvalidUTF8</td>
///             <td>LIBREAUTH_OATH_INVALID_UTF8</td>
///         </tr>
///     </tbody>
/// </table>
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum ErrorCode {
	Success = 0,

	NullPtr = 1,
	NotEnoughSpace = 2,

	InvalidBaseLen = 10,
	InvalidKeyLen = 11,
	CodeTooSmall = 12,
	CodeTooBig = 13,

	InvalidKey = 20,
	InvalidPeriod = 21,

	InvalidUTF8 = 30,
}

/// Errors used for the Rust interface.
///
/// *To implement `std::error::Error`, the `stderror` feature must be activated*
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "thiserror", derive(thiserror::Error))]
pub enum Error {
	#[cfg_attr(feature = "thiserror", error("Code too small"))]
	CodeTooSmall,
	#[cfg_attr(feature = "thiserror", error("Code too big"))]
	CodeTooBig,

	#[cfg_attr(feature = "thiserror", error("Invalid key"))]
	InvalidKey,

	#[cfg_attr(feature = "thiserror", error("Invalid period"))]
	InvalidPeriod,
}

impl From<Error> for ErrorCode {
	fn from(error: Error) -> Self {
		match error {
			Error::CodeTooSmall => ErrorCode::CodeTooSmall,
			Error::CodeTooBig => ErrorCode::CodeTooBig,
			Error::InvalidKey => ErrorCode::InvalidKey,
			Error::InvalidPeriod => ErrorCode::InvalidPeriod,
		}
	}
}

macro_rules! builder_common {
	() => {
		/// Sets the shared secret.
		pub fn key(&mut self, key: &[u8]) -> &mut Self {
			self.key = Some(key.to_owned());
			self
		}

		/// Sets the shared secret. This secret is passed as an ASCII string.
		pub fn ascii_key(&mut self, key: &str) -> &mut Self {
			self.key = Some(key.as_bytes().to_vec());
			self
		}

		/// Sets the shared secret. This secret is passed as an hexadecimal encoded string.
		pub fn hex_key(&mut self, key: &str) -> &mut Self {
			match hex::decode(key) {
				Ok(k) => {
					self.key = Some(k);
				}
				Err(_) => {
					self.runtime_error = Some(Error::InvalidKey);
				}
			}
			self
		}

		/// Sets the shared secret. This secret is passed as a base32 encoded string.
		pub fn base32_key(&mut self, key: &str) -> &mut Self {
			match base32::decode(base32::Alphabet::Rfc4648 { padding: false }, &key) {
				Some(k) => {
					self.key = Some(k);
				}
				None => {
					self.runtime_error = Some(Error::InvalidKey);
				}
			}
			self
		}

		/// Sets the shared secret. This secret is passed as a base64 encoded string.
		pub fn base64_key(&mut self, key: &str) -> &mut Self {
			use base64::Engine;
			match base64::engine::general_purpose::STANDARD.decode(key) {
				Ok(k) => {
					self.key = Some(k);
				}
				Err(_) => {
					self.runtime_error = Some(Error::InvalidKey);
				}
			}
			self
		}

		fn code_length(&self) -> usize {
			let base_len = self.output_base.len();
			let mut nb_bits = base_len;
			for _ in 1..self.output_len {
				nb_bits = match nb_bits.checked_mul(base_len) {
					Some(nb_bits) => nb_bits,
					None => return usize::MAX,
				};
			}
			nb_bits
		}

		/// Sets the number of characters for the code. The minimum and maximum values depends the base. Default is 6.
		pub fn output_len(&mut self, output_len: usize) -> &mut Self {
			self.output_len = output_len;
			self
		}

		/// Sets the base used to represents the output code. Default is "0123456789".
		pub fn output_base(&mut self, base: &str) -> &mut Self {
			self.output_base = base.to_string();
			self
		}

		/// Sets the hash function. Default is Sha1.
		pub fn hash_function(&mut self, hash_function: HashFunction) -> &mut Self {
			self.hash_function = hash_function;
			self
		}
	};
}

#[cfg(feature = "oath-uri")]
mod key_uri;
#[cfg(feature = "oath-uri")]
pub use self::key_uri::{KeyUriBuilder, ParametersVisibility};

mod hotp;
pub use self::hotp::HOTP;
pub use self::hotp::HOTPBuilder;

mod totp;
pub use self::totp::TOTP;
pub use self::totp::TOTPBuilder;

#[cfg(feature = "cbindings")]
mod cbindings;
#[cfg(feature = "cbindings")]
pub use self::cbindings::HOTPcfg;
#[cfg(feature = "cbindings")]
pub use self::cbindings::libreauth_hotp_generate;
#[cfg(all(feature = "cbindings", feature = "oath-uri"))]
pub use self::cbindings::libreauth_hotp_get_uri;
#[cfg(feature = "cbindings")]
pub use self::cbindings::libreauth_hotp_init;
#[cfg(feature = "cbindings")]
pub use self::cbindings::libreauth_hotp_is_valid;

#[cfg(feature = "cbindings")]
pub use self::cbindings::TOTPcfg;
#[cfg(feature = "cbindings")]
pub use self::cbindings::libreauth_totp_generate;
#[cfg(all(feature = "cbindings", feature = "oath-uri"))]
pub use self::cbindings::libreauth_totp_get_uri;
#[cfg(feature = "cbindings")]
pub use self::cbindings::libreauth_totp_init;
#[cfg(feature = "cbindings")]
pub use self::cbindings::libreauth_totp_is_valid;