#[cfg(feature = "oath-uri")]
use super::DEFAULT_KEY_URI_PARAM_POLICY;
use super::{
ErrorCode, HashFunction, DEFAULT_LOOK_AHEAD, DEFAULT_OTP_HASH, DEFAULT_OTP_OUT_BASE,
DEFAULT_OTP_OUT_LEN,
};
#[cfg(feature = "oath-uri")]
use crate::oath::key_uri::{KeyUriBuilder, UriType};
use hmac::{Hmac, Mac};
use sha1::Sha1;
use sha2::{Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256};
use sha3::{Keccak224, Keccak256, Keccak384, Keccak512, Sha3_224, Sha3_256, Sha3_384, Sha3_512};
use std::collections::HashMap;
macro_rules! compute_hmac {
($obj: ident, $hash: ty, $input: ident) => {{
let mut hmac = Hmac::<$hash>::new_from_slice(&$obj.key.as_slice()).unwrap();
hmac.update(&$input);
hmac.finalize().into_bytes().to_vec()
}};
}
pub struct HOTP {
key: Vec<u8>,
counter: u64,
output_len: usize,
output_base: String,
hash_function: HashFunction,
look_ahead: u64,
}
impl HOTP {
fn reduce_result(&self, hs: &[u8]) -> u32 {
let offset = (hs[hs.len() - 1] & 0xf) as usize;
let hash = hs[offset..offset + 4].to_vec();
let snum: u32 = ((u32::from(hash[0]) & 0x7f) << 24)
| ((u32::from(hash[1]) & 0xff) << 16)
| ((u32::from(hash[2]) & 0xff) << 8)
| (u32::from(hash[3]) & 0xff);
let base = self.output_base.len() as u32;
snum % base.pow(self.output_len as u32)
}
fn format_result(&self, nb: u32) -> String {
let mut code = Vec::with_capacity(self.output_len);
let mut nb = nb;
let base_len = self.output_base.len() as u32;
while nb > 0 {
code.push(
self.output_base
.chars()
.nth((nb % base_len) as usize)
.unwrap(),
);
nb /= base_len;
}
while code.len() != self.output_len {
code.push(self.output_base.chars().next().unwrap());
}
code.reverse();
code.iter().collect()
}
fn raw_generate(&self, counter: u64) -> String {
let msg = [
((counter >> 56) & 0xff) as u8,
((counter >> 48) & 0xff) as u8,
((counter >> 40) & 0xff) as u8,
((counter >> 32) & 0xff) as u8,
((counter >> 24) & 0xff) as u8,
((counter >> 16) & 0xff) as u8,
((counter >> 8) & 0xff) as u8,
(counter & 0xff) as u8,
];
let result: Vec<u8> = match self.hash_function {
HashFunction::Sha1 => compute_hmac!(self, Sha1, msg),
HashFunction::Sha224 => compute_hmac!(self, Sha224, msg),
HashFunction::Sha256 => compute_hmac!(self, Sha256, msg),
HashFunction::Sha384 => compute_hmac!(self, Sha384, msg),
HashFunction::Sha512 => compute_hmac!(self, Sha512, msg),
HashFunction::Sha512Trunc224 => compute_hmac!(self, Sha512_224, msg),
HashFunction::Sha512Trunc256 => compute_hmac!(self, Sha512_256, msg),
HashFunction::Sha3_224 => compute_hmac!(self, Sha3_224, msg),
HashFunction::Sha3_256 => compute_hmac!(self, Sha3_256, msg),
HashFunction::Sha3_384 => compute_hmac!(self, Sha3_384, msg),
HashFunction::Sha3_512 => compute_hmac!(self, Sha3_512, msg),
HashFunction::Keccak224 => compute_hmac!(self, Keccak224, msg),
HashFunction::Keccak256 => compute_hmac!(self, Keccak256, msg),
HashFunction::Keccak384 => compute_hmac!(self, Keccak384, msg),
HashFunction::Keccak512 => compute_hmac!(self, Keccak512, msg),
};
let hs = result.as_slice();
let nb = self.reduce_result(hs);
self.format_result(nb)
}
pub fn generate(&self) -> String {
self.raw_generate(self.counter)
}
pub fn get_counter(&self) -> u64 {
self.counter
}
pub fn increment_counter(&mut self) -> &mut HOTP {
self.counter += 1;
self
}
fn raw_is_valid(&self, code: &str) -> (bool, u64) {
if code.len() != self.output_len {
return (false, self.counter);
}
let mut results = HashMap::new();
let end = self.counter + 1 + self.look_ahead;
for counter in self.counter..end {
let r1 = self.raw_generate(counter);
let ref_code = r1.as_str().as_bytes();
let code = code.as_bytes();
let (code, ref_code) = match self.hash_function {
HashFunction::Sha1 => (
compute_hmac!(self, Sha1, code),
compute_hmac!(self, Sha1, ref_code),
),
HashFunction::Sha224 => (
compute_hmac!(self, Sha224, code),
compute_hmac!(self, Sha224, ref_code),
),
HashFunction::Sha256 => (
compute_hmac!(self, Sha256, code),
compute_hmac!(self, Sha256, ref_code),
),
HashFunction::Sha384 => (
compute_hmac!(self, Sha384, code),
compute_hmac!(self, Sha384, ref_code),
),
HashFunction::Sha512 => (
compute_hmac!(self, Sha512, code),
compute_hmac!(self, Sha512, ref_code),
),
HashFunction::Sha512Trunc224 => (
compute_hmac!(self, Sha512_224, code),
compute_hmac!(self, Sha512_224, ref_code),
),
HashFunction::Sha512Trunc256 => (
compute_hmac!(self, Sha512_256, code),
compute_hmac!(self, Sha512_256, ref_code),
),
HashFunction::Sha3_224 => (
compute_hmac!(self, Sha3_224, code),
compute_hmac!(self, Sha3_224, ref_code),
),
HashFunction::Sha3_256 => (
compute_hmac!(self, Sha3_256, code),
compute_hmac!(self, Sha3_256, ref_code),
),
HashFunction::Sha3_384 => (
compute_hmac!(self, Sha3_384, code),
compute_hmac!(self, Sha3_384, ref_code),
),
HashFunction::Sha3_512 => (
compute_hmac!(self, Sha3_512, code),
compute_hmac!(self, Sha3_512, ref_code),
),
HashFunction::Keccak224 => (
compute_hmac!(self, Keccak224, code),
compute_hmac!(self, Keccak224, ref_code),
),
HashFunction::Keccak256 => (
compute_hmac!(self, Keccak256, code),
compute_hmac!(self, Keccak256, ref_code),
),
HashFunction::Keccak384 => (
compute_hmac!(self, Keccak384, code),
compute_hmac!(self, Keccak384, ref_code),
),
HashFunction::Keccak512 => (
compute_hmac!(self, Keccak512, code),
compute_hmac!(self, Keccak512, ref_code),
),
};
results.insert(code == ref_code, counter);
}
match results.get(&true) {
Some(c) => (true, c + 1),
None => (false, self.counter),
}
}
pub fn is_valid(&self, code: &str) -> bool {
self.raw_is_valid(code).0
}
pub fn is_valid_sync(&mut self, code: &str) -> bool {
let (result, new_counter) = self.raw_is_valid(code);
self.counter = new_counter;
result
}
#[cfg(feature = "oath-uri")]
pub fn key_uri_format<'a>(
&'a self,
issuer: &'a str,
account_name: &'a str,
) -> KeyUriBuilder<'a> {
KeyUriBuilder {
parameters_visibility: DEFAULT_KEY_URI_PARAM_POLICY,
uri_type: UriType::HOTP,
key: &self.key,
issuer,
account_name,
custom_label: None,
custom_parameters: HashMap::new(),
algo: self.hash_function,
output_len: self.output_len,
output_base: &self.output_base,
counter: Some(self.counter),
period: None,
initial_time: None,
}
}
}
pub struct HOTPBuilder {
key: Option<Vec<u8>>,
counter: u64,
output_len: usize,
output_base: String,
hash_function: HashFunction,
runtime_error: Option<ErrorCode>,
look_ahead: u64,
}
impl Default for HOTPBuilder {
fn default() -> Self {
Self::new()
}
}
impl HOTPBuilder {
pub fn new() -> HOTPBuilder {
HOTPBuilder {
key: None,
counter: 0,
output_len: DEFAULT_OTP_OUT_LEN,
output_base: DEFAULT_OTP_OUT_BASE.to_string(),
hash_function: DEFAULT_OTP_HASH,
runtime_error: None,
look_ahead: DEFAULT_LOOK_AHEAD,
}
}
builder_common!(HOTPBuilder);
pub fn counter(&mut self, counter: u64) -> &mut HOTPBuilder {
self.counter = counter;
self
}
pub fn look_ahead(&mut self, nb: u64) -> &mut HOTPBuilder {
self.look_ahead = nb;
self
}
pub fn finalize(&self) -> Result<HOTP, ErrorCode> {
if let Some(e) = self.runtime_error {
return Err(e);
}
match self.code_length() {
n if n < 1_000_000 => return Err(ErrorCode::CodeTooSmall),
n if n > 2_147_483_648 => return Err(ErrorCode::CodeTooBig),
_ => (),
}
match self.key {
Some(ref k) => Ok(HOTP {
key: k.clone(),
counter: self.counter,
output_len: self.output_len,
output_base: self.output_base.clone(),
hash_function: self.hash_function,
look_ahead: self.look_ahead,
}),
None => Err(ErrorCode::InvalidKey),
}
}
}
#[cfg(test)]
mod tests {
use super::HOTPBuilder;
use crate::hash::HashFunction;
#[cfg(feature = "oath-uri")]
use crate::oath::ParametersVisibility;
#[test]
fn test_hotp_key_simple() {
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let hotp = HOTPBuilder::new().key(&key).finalize().unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 0);
assert_eq!(hotp.output_len, 6);
match hotp.hash_function {
HashFunction::Sha1 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 6);
assert_eq!(code, "755224");
}
#[test]
fn test_hotp_key_full() {
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let hotp = HOTPBuilder::new()
.key(&key)
.counter(5)
.output_len(8)
.hash_function(HashFunction::Sha256)
.finalize()
.unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 5);
assert_eq!(hotp.output_len, 8);
match hotp.hash_function {
HashFunction::Sha256 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 8);
assert_eq!(code, "89697997");
}
#[test]
fn test_hotp_asciikey_simple() {
let key_ascii = "12345678901234567890".to_owned();
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let hotp = HOTPBuilder::new().ascii_key(&key_ascii).finalize().unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 0);
assert_eq!(hotp.output_len, 6);
match hotp.hash_function {
HashFunction::Sha1 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 6);
assert_eq!(code, "755224");
}
#[test]
fn test_hotp_asciikey_full() {
let key_ascii = "12345678901234567890".to_owned();
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let hotp = HOTPBuilder::new()
.ascii_key(&key_ascii)
.counter(5)
.output_len(8)
.hash_function(HashFunction::Sha256)
.finalize()
.unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 5);
assert_eq!(hotp.output_len, 8);
match hotp.hash_function {
HashFunction::Sha256 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 8);
assert_eq!(code, "89697997");
}
#[test]
fn test_hotp_hexkey_simple() {
let key_hex = "3132333435363738393031323334353637383930".to_owned();
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let hotp = HOTPBuilder::new().hex_key(&key_hex).finalize().unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 0);
assert_eq!(hotp.output_len, 6);
match hotp.hash_function {
HashFunction::Sha1 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 6);
assert_eq!(code, "755224");
}
#[test]
fn test_hotp_hexkey_full() {
let key_hex = "3132333435363738393031323334353637383930".to_owned();
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let hotp = HOTPBuilder::new()
.hex_key(&key_hex)
.counter(5)
.output_len(8)
.hash_function(HashFunction::Sha512)
.finalize()
.unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 5);
assert_eq!(hotp.output_len, 8);
match hotp.hash_function {
HashFunction::Sha512 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 8);
assert_eq!(code, "16848329");
}
#[test]
fn test_hotp_base32key_simple() {
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let key_base32 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ".to_owned();
let hotp = HOTPBuilder::new()
.base32_key(&key_base32)
.finalize()
.unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 0);
assert_eq!(hotp.output_len, 6);
match hotp.hash_function {
HashFunction::Sha1 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 6);
assert_eq!(code, "755224");
}
#[test]
fn test_hotp_base32key_full() {
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let key_base32 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ".to_owned();
let hotp = HOTPBuilder::new()
.base32_key(&key_base32)
.counter(5)
.output_len(8)
.hash_function(HashFunction::Sha512)
.finalize()
.unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 5);
assert_eq!(hotp.output_len, 8);
match hotp.hash_function {
HashFunction::Sha512 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 8);
assert_eq!(code, "16848329");
}
#[test]
fn test_hotp_base64key_simple() {
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let key_base64 = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=".to_owned();
let hotp = HOTPBuilder::new()
.base64_key(&key_base64)
.finalize()
.unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 0);
assert_eq!(hotp.output_len, 6);
match hotp.hash_function {
HashFunction::Sha1 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 6);
assert_eq!(code, "755224");
}
#[test]
fn test_hotp_base64key_full() {
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let key_base64 = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=".to_owned();
let hotp = HOTPBuilder::new()
.base64_key(&key_base64)
.counter(5)
.output_len(8)
.hash_function(HashFunction::Sha512)
.finalize()
.unwrap();
assert_eq!(hotp.key, key);
assert_eq!(hotp.counter, 5);
assert_eq!(hotp.output_len, 8);
match hotp.hash_function {
HashFunction::Sha512 => assert!(true),
_ => assert!(false),
}
let code = hotp.generate();
assert_eq!(code.len(), 8);
assert_eq!(code, "16848329");
}
#[test]
fn test_nokey() {
match HOTPBuilder::new().finalize() {
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
#[test]
fn test_invalid_hexkey() {
let key = "!@#$%^&".to_owned();
match HOTPBuilder::new().hex_key(&key).finalize() {
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
#[test]
fn test_invalid_base32key() {
let key = "!@#$%^&".to_owned();
match HOTPBuilder::new().base32_key(&key).finalize() {
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
#[test]
fn test_empty_output_base() {
let key_ascii = "12345678901234567890".to_owned();
let output_base = "";
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_base(&output_base)
.finalize()
{
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
#[test]
fn test_invalid_output_base() {
let key_ascii = "12345678901234567890".to_owned();
let output_base = "1";
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_base(&output_base)
.finalize()
{
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
#[test]
fn test_small_result_base10() {
let key_ascii = "12345678901234567890".to_owned();
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_len(5)
.finalize()
{
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
#[test]
fn test_big_result_base10() {
let key_ascii = "12345678901234567890".to_owned();
for nb in vec![10, 42, 69, 1024, 0xffffff] {
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_len(nb)
.finalize()
{
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
}
#[test]
fn test_result_ok_base10() {
let key_ascii = "12345678901234567890".to_owned();
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_len(6)
.finalize()
{
Ok(_) => assert!(true),
Err(_) => assert!(false),
}
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_len(9)
.finalize()
{
Ok(_) => assert!(true),
Err(_) => assert!(false),
}
}
#[test]
fn test_small_result_base64() {
let key_ascii = "12345678901234567890".to_owned();
let base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_base(&base)
.output_len(3)
.finalize()
{
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
#[test]
fn test_big_result_base64() {
let key_ascii = "12345678901234567890".to_owned();
let base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_base(&base)
.output_len(6)
.finalize()
{
Ok(_) => assert!(false),
Err(_) => assert!(true),
}
}
#[test]
fn test_result_ok_base64() {
let key_ascii = "12345678901234567890".to_owned();
let base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_base(&base)
.output_len(4)
.finalize()
{
Ok(_) => assert!(true),
Err(_) => assert!(false),
}
match HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_base(&base)
.output_len(5)
.finalize()
{
Ok(_) => assert!(true),
Err(_) => assert!(false),
}
}
#[test]
fn test_rfc4226_examples() {
let key_ascii = "12345678901234567890".to_owned();
let key = vec![
49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48,
];
let hex_base = "0123456789ABCDEF";
let examples = [
["755224", "93CF18"],
["287082", "397EEA"],
["359152", "2FEF30"],
["969429", "EF7655"],
["338314", "C5938A"],
["254676", "C083D4"],
["287922", "56C032"],
["162583", "E5B397"],
["399871", "23443F"],
["520489", "79DC69"],
];
let mut hotp1 = HOTPBuilder::new().ascii_key(&key_ascii).finalize().unwrap();
let mut hotp2 = HOTPBuilder::new()
.key(&key)
.counter(0)
.hash_function(HashFunction::Sha1)
.finalize()
.unwrap();
let mut hotp3 = HOTPBuilder::new()
.ascii_key(&key_ascii)
.output_base(&hex_base)
.finalize()
.unwrap();
for count in 0..examples.len() {
let counter = count as u64;
assert_eq!(hotp1.counter, counter);
assert_eq!(hotp2.counter, counter);
assert_eq!(hotp3.counter, counter);
let code1 = hotp1.generate();
let code2 = hotp2.generate();
let code3 = hotp3.generate();
assert_eq!(code1, examples[count][0]);
assert_eq!(code2, examples[count][0]);
assert_eq!(code3, examples[count][1]);
hotp1.increment_counter();
hotp2.increment_counter();
hotp3.increment_counter();
assert_eq!(hotp1.counter, counter + 1);
assert_eq!(hotp2.counter, counter + 1);
assert_eq!(hotp3.counter, counter + 1);
}
}
#[test]
fn test_valid_sha1_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "755224".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha1)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha224_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "893239".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha224)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha256_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "875740".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha256)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha384_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "502125".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha384)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha512_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "125165".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha512)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha512t224_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "627914".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha512Trunc224)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha512t256_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "289990".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha512Trunc256)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha3_224_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "228979".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha3_224)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha3_256_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "170828".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha3_256)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha3_384_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "133113".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha3_384)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_sha3_512_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "342230".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha3_512)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_keccak_224_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "839246".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Keccak224)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_keccak_256_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "190777".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Keccak256)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_keccak_384_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "970541".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Keccak384)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_valid_keccak_512_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "108634".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Keccak512)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, true);
}
#[test]
fn test_invalid_sha1_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "123456".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha1)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha224_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "893238".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha224)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha256_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "123456".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha256)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha384_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "502225".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha384)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha512_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "123456".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha512)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha512t224_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "627904".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha512Trunc224)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha512t256_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "289900".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha512Trunc256)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha3_224_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "128979".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha3_224)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha3_256_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "170823".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha3_256)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha3_384_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "133013".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha3_384)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_sha3_512_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "342931".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Sha3_512)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_keccak_224_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "839046".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Keccak224)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_keccak_256_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "197777".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Keccak256)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_keccak_384_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "970241".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Keccak384)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_invalid_keccak_512_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "107634".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.hash_function(HashFunction::Keccak512)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_bad_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "!@#$%^".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
fn test_empty_code() {
let key_ascii = "12345678901234567890".to_owned();
let user_code = "".to_owned();
let valid = HOTPBuilder::new()
.ascii_key(&key_ascii)
.finalize()
.unwrap()
.is_valid(&user_code);
assert_eq!(valid, false);
}
#[test]
#[cfg(feature = "oath-uri")]
fn test_key_uri_format() {
let key_ascii = "12345678901234567890".to_owned();
let hotp = HOTPBuilder::new().ascii_key(&key_ascii).finalize().unwrap();
let uri = hotp
.key_uri_format("Provider 1", "alice@example.com")
.finalize();
assert_eq!(
uri,
"otpauth://hotp/Provider%201:alice@example.com?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&issuer=Provider+1&counter=0"
);
}
#[test]
#[cfg(feature = "oath-uri")]
fn test_key_uri_format_hide_all() {
let key_ascii = "12345678901234567890".to_owned();
let hotp = HOTPBuilder::new()
.output_len(7)
.hash_function(HashFunction::Sha256)
.ascii_key(&key_ascii)
.finalize()
.unwrap();
let uri = hotp
.key_uri_format("Provider 1", "alice@example.com")
.parameters_visibility_policy(ParametersVisibility::HideAll)
.finalize();
assert_eq!(
uri,
"otpauth://hotp/Provider%201:alice@example.com?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&counter=0"
);
}
#[test]
#[cfg(feature = "oath-uri")]
fn test_key_uri_format_overwrite_label() {
let key_ascii = "12345678901234567890".to_owned();
let hotp = HOTPBuilder::new().ascii_key(&key_ascii).finalize().unwrap();
let uri = hotp
.key_uri_format("Provider1", "alice@example.com")
.overwrite_label("Provider1Label")
.finalize();
assert_eq!(
uri,
"otpauth://hotp/Provider1Label?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&issuer=Provider1&counter=0"
);
}
#[test]
#[cfg(feature = "oath-uri")]
fn test_key_uri_format_add_parameter() {
let key_ascii = "12345678901234567890".to_owned();
let hotp = HOTPBuilder::new().ascii_key(&key_ascii).finalize().unwrap();
let uri = hotp
.key_uri_format("Provider1", "alice@example.com")
.add_parameter("foo", "bar baz")
.add_parameter("foo 2", "è_é")
.finalize();
assert_eq!(uri.len(), 141);
assert!(uri.starts_with(
"otpauth://hotp/Provider1:alice@example.com?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&issuer=Provider1&counter=0&"
));
assert!(uri.contains("&foo=bar+baz"));
assert!(uri.contains("&foo+2=%C3%A8_%C3%A9"));
}
}