88 lines
2.4 KiB
Rust
88 lines
2.4 KiB
Rust
//!
|
|
|
|
use std::{env, path::PathBuf, process::ExitCode, str::FromStr};
|
|
|
|
use keyfork_shard::openpgp::{discover_certs, openpgp::Cert, split};
|
|
|
|
#[derive(Clone, Debug)]
|
|
enum Error {
|
|
Usage(String),
|
|
Input,
|
|
Threshold(u8, u8),
|
|
InvalidCertCount(usize, u8),
|
|
}
|
|
|
|
impl std::fmt::Display for Error {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Error::Usage(program_name) => {
|
|
write!(f, "Usage: {program_name} threshold max key_discovery")
|
|
}
|
|
Error::Input => f.write_str("Expected hex encoded input"),
|
|
Error::Threshold(threshold, max) => {
|
|
write!(
|
|
f,
|
|
"Invalid threshold: 0 < threshold {threshold} <= max {max} < 256"
|
|
)
|
|
}
|
|
Error::InvalidCertCount(count, max) => {
|
|
write!(f, "Invalid cert count: count {count} != max {max}")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {}
|
|
|
|
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
|
|
|
|
fn validate(threshold: &str, max: &str, key_discovery: &str) -> Result<(u8, Vec<Cert>)> {
|
|
let threshold = u8::from_str(threshold)?;
|
|
let max = u8::from_str(max)?;
|
|
let key_discovery = PathBuf::from(key_discovery);
|
|
if threshold > max {
|
|
return Err(Error::Threshold(threshold, max).into());
|
|
}
|
|
|
|
// Verify path exists
|
|
std::fs::metadata(&key_discovery)?;
|
|
|
|
// Load certs from path
|
|
let certs = discover_certs(key_discovery)?;
|
|
if certs.len() != max.into() {
|
|
return Err(Error::InvalidCertCount(certs.len(), max).into());
|
|
}
|
|
|
|
Ok((threshold, certs))
|
|
}
|
|
|
|
fn run() -> Result<()> {
|
|
let mut args = env::args();
|
|
let program_name = args.next().expect("program name");
|
|
let args = args.collect::<Vec<_>>();
|
|
let (threshold, cert_list) = match args.as_slice() {
|
|
[threshold, max, key_discovery] => validate(threshold, max, key_discovery)?,
|
|
_ => return Err(Error::Usage(program_name).into()),
|
|
};
|
|
let input = {
|
|
use std::io::stdin;
|
|
let Some(line) = stdin().lines().next() else {
|
|
return Err(Error::Input.into());
|
|
};
|
|
smex::decode(&line?)?
|
|
};
|
|
|
|
split(threshold, cert_list, &input, std::io::stdout())?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn main() -> ExitCode {
|
|
let result = run();
|
|
if let Err(e) = result {
|
|
eprintln!("Error: {e}");
|
|
return ExitCode::FAILURE;
|
|
}
|
|
ExitCode::SUCCESS
|
|
}
|