programatically generate keys
diff --git a/Cargo.toml b/Cargo.toml
index 5b6406e..2e40d9e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,18 +20,8 @@
name = "tuf"
path = "./src/lib.rs"
-[[bin]]
-name = "tuf"
-path = "./src/bin/tuf.rs"
-doc = false
-required-features = [ "cli" ]
-
-[features]
-cli = [ "clap" ]
-
[dependencies]
chrono = { version = "0.4", features = [ "serde" ] }
-clap = { version = "2", optional = true }
data-encoding = "2.0.0-rc.1"
derp = "0.0.4"
hyper = "0.10"
diff --git a/src/bin/tuf.rs b/src/bin/tuf.rs
deleted file mode 100644
index f5a267d..0000000
--- a/src/bin/tuf.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-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();
- }
-}
diff --git a/src/crypto.rs b/src/crypto.rs
index 2ea2b48..dfece0f 100644
--- a/src/crypto.rs
+++ b/src/crypto.rs
@@ -10,11 +10,12 @@
RSA_PSS_SHA512};
use serde::de::{Deserialize, Deserializer, Error as DeserializeError};
use serde::ser::{Serialize, Serializer, Error as SerializeError};
+use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::{self, Debug, Display};
use std::hash;
-use std::io::Read;
-use std::cmp::Ordering;
+use std::io::{Read, Write};
+use std::process::{Command, Stdio};
use std::str::FromStr;
use std::sync::Arc;
use untrusted::Input;
@@ -338,6 +339,20 @@
}
impl PrivateKey {
+ /// Generate a new `PrivateKey`.
+ ///
+ /// Note: For RSA keys, `openssl` needs to the on the `$PATH`.
+ pub fn new(key_type: KeyType) -> Result<Vec<u8>> {
+ match key_type {
+ KeyType::Ed25519 => {
+ Ed25519KeyPair::generate_pkcs8(&SystemRandom::new())
+ .map(|bytes| bytes.to_vec())
+ .map_err(|_| Error::Opaque("Failed to generate Ed25519 key".into()))
+ },
+ KeyType::Rsa => Self::rsa_gen(),
+ }
+ }
+
/// Create a private key from PKCS#8v2 DER bytes.
///
/// # Generating Keys
@@ -501,6 +516,48 @@
})
}
+ fn rsa_gen() -> Result<Vec<u8>> {
+ let gen = Command::new("openssl")
+ .args(
+ &[
+ "genpkey",
+ "-algorithm",
+ "RSA",
+ "-pkeyopt",
+ "rsa_keygen_bits:4096",
+ "-pkeyopt",
+ "rsa_keygen_pubexp:65537",
+ "-outform",
+ "der",
+ ],
+ )
+ .output()?;
+
+ let mut pk8 = Command::new("openssl")
+ .args(
+ &[
+ "pkcs8",
+ "-inform",
+ "der",
+ "-topk8",
+ "-nocrypt",
+ "-outform",
+ "der",
+ ],
+ )
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .spawn()?;
+
+ match pk8.stdin {
+ Some(ref mut stdin) => stdin.write_all(&gen.stdout)?,
+ None => return Err(Error::Opaque("openssl has no stdin".into())),
+ };
+
+ Ok(pk8.wait_with_output()?.stdout)
+ }
+
+
/// Return the public component of the key.
pub fn public(&self) -> &PublicKey {
&self.public
@@ -512,7 +569,6 @@
}
}
-
/// A structure containing information about a public key.
#[derive(Clone, Debug)]
pub struct PublicKey {
@@ -953,4 +1009,17 @@
let decoded: Signature = json::from_value(encoded).unwrap();
assert_eq!(decoded, sig);
}
+
+ #[test]
+ #[cfg(not(windows))]
+ fn new_rsa_key() {
+ let bytes = PrivateKey::new(KeyType::Rsa).unwrap();
+ let _ = PrivateKey::from_pkcs8(&bytes, SignatureScheme::RsaSsaPssSha256).unwrap();
+ }
+
+ #[test]
+ fn new_ed25519_key() {
+ let bytes = PrivateKey::new(KeyType::Ed25519).unwrap();
+ let _ = PrivateKey::from_pkcs8(&bytes, SignatureScheme::Ed25519).unwrap();
+ }
}