blob: f5a267d2f2c12b8ac7d6a8d7b221607e2747b177 [file] [log] [blame]
extern crate clap;
extern crate ring;
use clap::{App, AppSettings, SubCommand, Arg, ArgMatches};
use ring::rand::SystemRandom;
use ring::signature::Ed25519KeyPair;
use std::io::{self, Read, Write};
use std::process::{Command, Stdio};
fn main() {
let matches = parser().get_matches();
match run_main(matches) {
Ok(()) => std::process::exit(0),
Err(e) => {
writeln!(&mut io::stderr(), "{:?}", e).unwrap();
::std::process::exit(1);
}
}
}
fn run_main(matches: ArgMatches) -> Result<(), String> {
if let Some(matches) = matches.subcommand_matches("keygen") {
let typ = matches.value_of("type").unwrap();
keygen(typ)
} else {
unreachable!() // because of AppSettings::SubcommandRequiredElseHelp
}
}
fn parser<'a, 'b>() -> App<'a, 'b> {
App::new("tuf")
.version(env!("CARGO_PKG_VERSION"))
.about("CLI tool for managing TUF repositories")
.settings(&[AppSettings::SubcommandRequiredElseHelp])
.subcommand(
SubCommand::with_name("keygen")
.about(
"Generate private keys and print them as PKCS#8v2 DER to STDOUT",
)
.arg(
Arg::with_name("type")
.takes_value(true)
.default_value("ed25519")
.possible_values(&["ed25519", "rsa-2048", "rsa-4096"])
.help(
"The type of key to generate. \
Note: rsa-XXX requires `openssl` to be on the PATH.",
),
),
)
}
fn keygen(typ: &str) -> Result<(), String> {
let der = match typ {
"ed25519" => {
Ed25519KeyPair::generate_pkcs8(&SystemRandom::new())
.map_err(|e| format!("Failed to generate key: {:?}", e))?
.to_vec()
}
"rsa-2048" => rsa_gen(2048)?,
"rsa-4096" => rsa_gen(4096)?,
_ => unreachable!(),
};
io::stdout().write(&der).map(|_| ()).map_err(|e| {
format!("Failed to write to STDOUT: {:?}", e)
})
}
fn rsa_gen(size: u32) -> Result<Vec<u8>, String> {
let gen = Command::new("openssl")
.args(
&[
"genpkey",
"-algorithm",
"RSA",
"-pkeyopt",
&format!("rsa_keygen_bits:{}", size),
"-pkeyopt",
"rsa_keygen_pubexp:65537",
"-outform",
"der",
],
)
.stdout(Stdio::piped())
.spawn()
.map_err(|e| format!("{:?}", e))?;
let mut pk8 = Command::new("openssl")
.args(
&[
"pkcs8",
"-inform",
"der",
"-topk8",
"-nocrypt",
"-outform",
"der",
],
)
.stdin(Stdio::piped())
.spawn()
.map_err(|e| format!("{:?}", e))?;
match pk8.stdin {
Some(ref mut stdin) => {
for byte in gen.stdout.ok_or("Failed to get stdout handle")?.bytes() {
stdin
.write(&[byte.map_err(|e| format!("Couldn't read byte: {:?}", e))?])
.map_err(|e| format!("Failed to write to stdin: {:?}", e))?;
}
}
None => return Err("Failed to get stdin".into()),
};
let out = pk8.wait_with_output().map_err(|e| {
format!("Failed to get stdout: {:?}", e)
})?;
Ok(out.stdout)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_clap() {
let _ = parser();
}
}