use std::{ env, io::{self, BufRead, Write}, process, time::{Duration, SystemTime}, }; use base64::prelude::*; use humantime::format_rfc3339_seconds; use pasetors::{ keys::SymmetricKey, paserk::{FormatAsPaserk, Id}, version4::V4, }; fn main() { let encoded_secret = read_secret(); let secret = if let Ok(secret) = decode_secret(encoded_secret) { secret } else { eprintln!("Not a valid 32-byte, base64 encoded string."); process::exit(1); }; let (id, key) = generate_local_key(secret.as_slice()); let mut id_string = String::new(); id.fmt(&mut id_string).unwrap(); let one_year: Duration = Duration::from_secs_f64(60_f64 * 60_f64 * 24_f64 * 365.25_f64); let expiration = format_rfc3339_seconds(SystemTime::now() + one_year); println!("ID: {id_string}\nKey: {key}\nExpiration: {expiration}"); } fn read_secret() -> String { let secret_arg = env::args().skip(1).last(); let secret = if let Some(secret) = secret_arg { secret } else { print!("Enter a 32-byte, base64 encoded string: "); let _ = io::stdout().flush(); let mut buffer = String::with_capacity(32); let stdin = io::stdin(); stdin.lock().read_line(&mut buffer).unwrap(); buffer .strip_suffix("\r\n") .or(buffer.strip_suffix("\n")) .map(|buffer| buffer.to_owned()) .unwrap_or(buffer) }; secret } fn decode_secret(secret: String) -> Result, ()> { BASE64_STANDARD .decode(secret) .map_err(|_| ()) .and_then(|secret| { if secret.len() != 32 { Err(()) } else { Ok(secret) } }) } fn generate_local_key(secret: &[u8]) -> (Id, String) { let key = SymmetricKey::::from(secret).unwrap(); let mut serialized_key = String::with_capacity(54); let _ = key.fmt(&mut serialized_key); let id = Id::from(&key); (id, serialized_key) }