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
use std::io; use serde::{self, Deserialize, Serialize}; use stoken::{self, chrono::Utc}; use self::config::{Config, TotpOptions}; pub mod config; pub mod totp; #[derive(Serialize, Deserialize, Debug, Clone)] pub enum TokenAlgorithm { #[serde(rename = "sha1")] TotpSha1, #[serde(rename = "stoken")] SToken, } impl Copy for TokenAlgorithm {} fn stoken(config: &Config, name: &str) -> Option<String> { let value = config.lookup(name)?; let token = stoken::export::import(value.secret.clone())?; return Some(stoken::generate(token, Utc::now())); } pub fn token(config: Config, name: &str) -> Option<String> { match config.lookup(name).and_then(|n| { n.algorithm }).unwrap_or(TokenAlgorithm::TotpSha1) { TokenAlgorithm::TotpSha1 => totp::standard_totp(config, name), TokenAlgorithm::SToken => stoken(&config, name) } } pub fn add_totp_secret(config: Config, config_dir: std::path::PathBuf, name: String, secret: String) -> io::Result<()> { base32::decode(base32::Alphabet::RFC4648 { padding: false }, &secret) .expect("Invalid base32 OTP secret"); return add_secret(config, config_dir, name, secret, TokenAlgorithm::TotpSha1); } pub fn add_secret( mut config: Config, config_dir: std::path::PathBuf, name: String, secret: String, algorithm: TokenAlgorithm) -> io::Result<()> { config.totp.insert(name, TotpOptions { secret, algorithm: Some(algorithm) }); let string = toml::to_string(&config).expect("unable to write config to TOML"); config::ensure_config_dir(&config_dir)?; std::fs::write(config_dir.join("config.toml"), string) } pub fn list_secrets(config: Config, prefix: Option<String>) -> io::Result<Vec<String>> { use std::iter::FromIterator; Ok(Vec::from_iter(config.totp.keys().map(|k| k.clone()) .filter(|_n| prefix.is_none()))) } pub fn delete_secret(mut config: Config, config_dir: std::path::PathBuf, name: String) -> io::Result<()> { config.totp.remove(&name); let string = toml::to_string(&config).expect("unable to write config to TOML"); config::ensure_config_dir(&config_dir)?; std::fs::write(config_dir.join("config.toml"), string) }