80 lines
2 KiB
Rust
80 lines
2 KiB
Rust
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<Vec<u8>, ()> {
|
|
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::<V4>::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)
|
|
}
|