diff --git a/Cargo.lock b/Cargo.lock index 68bdfbf..553a84e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1198,7 +1198,7 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "sshcerts" -version = "0.4.4" +version = "0.5.0" dependencies = [ "base64", "clap 3.0.0-beta.2", diff --git a/Cargo.toml b/Cargo.toml index ebb7183..914cfb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sshcerts" -version = "0.4.4" +version = "0.5.0" authors = ["Mitchell Grenier "] edition = "2018" license-file = "LICENSE" diff --git a/examples/yk-provision.rs b/examples/yk-provision.rs index 39cf91f..f00ae67 100644 --- a/examples/yk-provision.rs +++ b/examples/yk-provision.rs @@ -20,7 +20,7 @@ fn provision_new_key(slot: SlotId, subject: &str, pin: &str, mgm_key: &[u8], alg println!("Provisioning new {:?} key called [{}] in slot: {:?}", alg, subject, slot); let policy = if secure { - println!("You're creating a secure key that will require touch to use. Touch key to continue..."); + println!("You're creating a secure key that will require touch to use. Touch Yubikey to continue..."); TouchPolicy::Cached } else { TouchPolicy::Never diff --git a/src/ssh/privkey.rs b/src/ssh/privkey.rs index 01febf1..002ffe1 100644 --- a/src/ssh/privkey.rs +++ b/src/ssh/privkey.rs @@ -2,7 +2,7 @@ use super::keytype::{Curve, KeyType, KeyTypeKind}; use crate::{error::Error, Result}; #[cfg(feature = "rsa-signing")] use num_bigint::{BigInt, BigUint, Sign}; -use super::PublicKey; +use super::{PublicKey, PublicKeyKind, EcdsaPublicKey, Ed25519PublicKey, RsaPublicKey}; use super::reader::Reader; #[cfg(feature = "rsa-signing")] use simple_asn1::{ASN1Block, ASN1Class, ToASN1}; @@ -110,6 +110,111 @@ impl ToASN1 for RsaPrivateKey { } } +fn read_private_key(reader: &mut Reader<'_>) -> Result { + let key_type = reader.read_string()?; + let kt = KeyType::from_name(&key_type)?; + + let (kind, pubkey) = match kt.kind { + KeyTypeKind::Rsa => { + let n = reader.read_mpint()?; + let e = reader.read_mpint()?; + let d = reader.read_mpint()?; + let coefficient = reader.read_mpint()?; + let p = reader.read_mpint()?; + let q = reader.read_mpint()?; + + #[cfg(feature = "rsa-signing")] + let exp = Some(BigUint::from_bytes_be(&d) + .modpow( + &BigUint::from_slice(&[0x1]), + &(BigUint::from_bytes_be(&p) - 1_u8) + ).to_bytes_be()); + #[cfg(not(feature = "rsa-signing"))] + let exp = None; + + #[cfg(feature = "rsa-signing")] + let exq = Some(BigUint::from_bytes_be(&d) + .modpow( + &BigUint::from_slice(&[0x1]), + &(BigUint::from_bytes_be(&q) - 1_u8) + ).to_bytes_be()); + #[cfg(not(feature = "rsa-signing"))] + let exq = None; + + ( + PrivateKeyKind::Rsa(RsaPrivateKey { + n: n.clone(), + e: e.clone(), + d, + coefficient, + p, + q, + exp: exp, + exq: exq, + } + ), + PublicKey { + key_type: kt.clone(), + kind: PublicKeyKind::Rsa(RsaPublicKey{ + e, + n + }), + comment: None, + } + ) + }, + KeyTypeKind::Ecdsa => { + let identifier = reader.read_string()?; + let curve = Curve::from_identifier(&identifier)?; + let pubkey = reader.read_bytes()?; + let key = reader.read_bytes()?; + let k = EcdsaPrivateKey { + curve: curve.clone(), + key, + }; + + ( + PrivateKeyKind::Ecdsa(k), + PublicKey { + key_type: kt.clone(), + kind: PublicKeyKind::Ecdsa(EcdsaPublicKey { + curve, + key: pubkey, + }), + comment: None, + } + ) + } + KeyTypeKind::Ed25519 => { + let pubkey = reader.read_bytes()?; + let k = Ed25519PrivateKey { + key: reader.read_bytes()?, + }; + + ( + PrivateKeyKind::Ed25519(k), + PublicKey { + key_type: kt.clone(), + kind: PublicKeyKind::Ed25519(Ed25519PublicKey { + key: pubkey, + }), + comment: None, + } + ) + } + _ => return Err(Error::UnknownKeyType(kt.name.to_string())), + }; + + let comment = reader.read_string()?; + + Ok(PrivateKey { + key_type: kt, + kind, + pubkey, + comment: if comment.len() == 0 {None} else {Some(comment)}, + }) +} + impl PrivateKey { /// Reads an OpenSSH private key from a given path. pub fn from_path>(path: P) -> Result { @@ -148,6 +253,12 @@ impl PrivateKey { Ok(k) } + /// Create a private key from just the private portion of a key file + pub fn from_bytes>(buffer: &T) -> Result { + let mut reader = Reader::new(buffer); + read_private_key(&mut reader) + } + /// This function is used for extracting a private key from an existing reader. pub(crate) fn from_reader(reader: &mut Reader<'_>) -> Result { let preamble = reader.read_cstring()?; @@ -190,94 +301,14 @@ impl PrivateKey { if c1 != c2 { return Err(Error::InvalidFormat); } - - // The key type is repeated here. - let key_type = reader.read_string()?; - let kt = KeyType::from_name(&key_type)?; - let kind = match kt.kind { - KeyTypeKind::Rsa => { - let n = reader.read_mpint()?; - let e = reader.read_mpint()?; - let d = reader.read_mpint()?; - let coefficient = reader.read_mpint()?; - let p = reader.read_mpint()?; - let q = reader.read_mpint()?; - - #[cfg(feature = "rsa-signing")] - let exp = Some(BigUint::from_bytes_be(&d) - .modpow( - &BigUint::from_slice(&[0x1]), - &(BigUint::from_bytes_be(&p) - 1_u8) - ).to_bytes_be()); - #[cfg(not(feature = "rsa-signing"))] - let exp = None; - - #[cfg(feature = "rsa-signing")] - let exq = Some(BigUint::from_bytes_be(&d) - .modpow( - &BigUint::from_slice(&[0x1]), - &(BigUint::from_bytes_be(&q) - 1_u8) - ).to_bytes_be()); - #[cfg(not(feature = "rsa-signing"))] - let exq = None; - - let k = RsaPrivateKey { - n, - e, - d, - coefficient, - p, - q, - exp: exp, - exq: exq, - }; + let private_key = read_private_key(reader)?; - let pubkey = match &pubkey.kind { - crate::ssh::pubkey::PublicKeyKind::Rsa(pubkey) => pubkey, - _ => return Err(Error::InvalidFormat), - }; - - if k.n != pubkey.n { - return Err(Error::InvalidFormat); - } - - if k.e != pubkey.e { - return Err(Error::InvalidFormat); - } - - PrivateKeyKind::Rsa(k) - }, - KeyTypeKind::Ecdsa => { - let identifier = reader.read_string()?; - let curve = Curve::from_identifier(&identifier)?; - // The pub key is also repeated here - let _pubkey = reader.read_bytes()?; - let key = reader.read_bytes()?; - let k = EcdsaPrivateKey { - curve, - key, - }; - - PrivateKeyKind::Ecdsa(k) - } - KeyTypeKind::Ed25519 => { - let _pubkey = reader.read_bytes()?; - let k = Ed25519PrivateKey { - key: reader.read_bytes()?, - }; + if private_key.pubkey != pubkey { + return Err(Error::InvalidFormat); + } - PrivateKeyKind::Ed25519(k) - } - _ => return Err(Error::UnknownKeyType(kt.name.to_string())), - }; - - Ok(PrivateKey { - key_type: kt, - kind, - pubkey, - comment: None, - }) + Ok(private_key) } } diff --git a/src/ssh/pubkey.rs b/src/ssh/pubkey.rs index 67beec9..a6eecd5 100644 --- a/src/ssh/pubkey.rs +++ b/src/ssh/pubkey.rs @@ -252,9 +252,9 @@ impl PublicKey { PublicKey::from_reader(&kt_name, &mut reader) } - // This function is used for extracting a public key from an existing reader, e.g. - // we already have a reader for reading an OpenSSH certificate key and - // we want to extract the public key information from it. + /// This function is used for extracting a public key from an existing reader, e.g. + /// we already have a reader for reading an OpenSSH certificate key and + /// we want to extract the public key information from it. pub(crate) fn from_reader(kt_name: &str, reader: &mut Reader<'_>) -> Result { let kt = KeyType::from_name(&kt_name)?; diff --git a/src/ssh/reader.rs b/src/ssh/reader.rs index 99b410d..6d52b17 100644 --- a/src/ssh/reader.rs +++ b/src/ssh/reader.rs @@ -2,7 +2,7 @@ use crate::{error::Error, Result}; use std::convert::TryInto; /// A `Reader` is used for reading from a byte sequence -/// representing an encoded OpenSSH public key or certificate. +/// representing an encoded OpenSSH public/private key or certificate. #[derive(Debug)] pub struct Reader<'a> { inner: &'a [u8],