libknox 0.4.0

secret vault encrypted with GPG
Documentation
use std::error::Error;
use std::time::{SystemTime, UNIX_EPOCH};

use oath::{totp_raw_custom_time, totp_raw_now, HashType};

use crate::{Entry, TotpConfig_Hash, VaultError};

pub fn get_totp(entry: &Entry, time: Option<u64>) -> Result<(String, u64), Box<dyn Error>> {
  if !entry.has_totp() {
    return Err(VaultError::throw(
      "TOTP generation was not configured for this entry",
    ));
  }

  let hash = match entry.get_totp().get_hash() {
    TotpConfig_Hash::SHA1 => HashType::SHA1,
    TotpConfig_Hash::SHA256 => HashType::SHA256,
    TotpConfig_Hash::SHA512 => HashType::SHA512,
  };

  let secret = entry.get_totp().get_secret();
  let interval = entry.get_totp().get_interval();
  let totp = match time {
    Some(time) => totp_raw_custom_time(
      secret,
      entry.get_totp().get_length(),
      0,
      interval,
      time,
      &hash,
    ),

    None => totp_raw_now(
      secret,
      entry.get_totp().get_length(),
      0,
      entry.get_totp().get_interval(),
      &hash,
    ),
  };

  let now = SystemTime::now()
    .duration_since(UNIX_EPOCH)
    .unwrap()
    .as_secs();

  let left = (interval * (now / interval)) + interval;

  Ok((format!("{:0>6}", totp), left))
}

#[cfg(test)]
mod test {
  use chrono::prelude::*;
  use protobuf::SingularPtrField;

  use crate::{Entry, TotpConfig, TotpConfig_Hash};

  #[test]
  fn get_totp() {
    let entry = Entry {
      totp: SingularPtrField::some(TotpConfig {
        secret: String::from("acbdefghijklmnopqrst").as_bytes().to_vec(),
        interval: 30,
        length: 6,
        hash: TotpConfig_Hash::SHA1,
        ..TotpConfig::default()
      }),
      ..Entry::default()
    };

    let totp = super::get_totp(
      &entry,
      Some(Utc.ymd(2014, 11, 28).and_hms(0, 0, 0).timestamp() as u64),
    );

    assert_eq!(totp.is_ok(), true);

    if let Ok((totp, _)) = totp {
      assert_eq!(totp, String::from("329633"));
    }
  }
}