Skip to content

Commit

Permalink
Better key checking, parse private key from bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
obelisk committed Aug 7, 2021
1 parent 94d6b9e commit 448d6e7
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 93 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sshcerts"
version = "0.4.4"
version = "0.5.0"
authors = ["Mitchell Grenier <[email protected]>"]
edition = "2018"
license-file = "LICENSE"
Expand Down
2 changes: 1 addition & 1 deletion examples/yk-provision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
203 changes: 117 additions & 86 deletions src/ssh/privkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -110,6 +110,111 @@ impl ToASN1 for RsaPrivateKey {
}
}

fn read_private_key(reader: &mut Reader<'_>) -> Result<PrivateKey> {
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<P: AsRef<Path>>(path: P) -> Result<PrivateKey> {
Expand Down Expand Up @@ -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<T: ?Sized + AsRef<[u8]>>(buffer: &T) -> Result<PrivateKey> {
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<PrivateKey> {
let preamble = reader.read_cstring()?;
Expand Down Expand Up @@ -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)
}
}

6 changes: 3 additions & 3 deletions src/ssh/pubkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PublicKey> {
let kt = KeyType::from_name(&kt_name)?;

Expand Down
2 changes: 1 addition & 1 deletion src/ssh/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down

0 comments on commit 448d6e7

Please sign in to comment.