use super::keytype::{Curve, KeyType, KeyTypeKind};
use crate::{error::Error, Result};
#[cfg(feature = "rsa-signing")]
use num_bigint::{BigInt, BigUint, Sign};
use super::{PublicKey, PublicKeyKind, EcdsaPublicKey, Ed25519PublicKey, RsaPublicKey};
use super::reader::Reader;
#[cfg(feature = "rsa-signing")]
use simple_asn1::{ASN1Block, ASN1Class, ToASN1};
use std::fs::File;
use std::io::Read;
use std::path::Path;
#[derive(Debug, PartialEq, Clone)]
pub struct RsaPrivateKey {
pub n: Vec<u8>,
pub e: Vec<u8>,
pub d: Vec<u8>,
pub coefficient: Vec<u8>,
pub p: Vec<u8>,
pub q: Vec<u8>,
pub exp: Option<Vec<u8>>,
pub exq: Option<Vec<u8>>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct EcdsaPrivateKey {
pub curve: Curve,
pub key: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Ed25519PrivateKey {
pub key: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum PrivateKeyKind {
Rsa(RsaPrivateKey),
Ecdsa(EcdsaPrivateKey),
Ed25519(Ed25519PrivateKey),
}
#[derive(Debug, PartialEq, Clone)]
pub struct PrivateKey {
pub key_type: KeyType,
pub kind: PrivateKeyKind,
pub pubkey: PublicKey,
pub comment: Option<String>,
}
#[cfg(feature = "rsa-signing")]
impl ToASN1 for RsaPrivateKey {
type Error = Error;
fn to_asn1_class(&self, _class: ASN1Class) -> std::result::Result<Vec<ASN1Block>, Error> {
Ok(vec![ASN1Block::Sequence(
0,
[
vec![ASN1Block::Integer(0, BigInt::new(Sign::Plus, vec![0]))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.n))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.e))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.d))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.p))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.q))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.exp.as_ref().unwrap()))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.exq.as_ref().unwrap()))],
vec![ASN1Block::Integer(0, BigInt::from_bytes_be(Sign::Plus, &self.coefficient))],
Vec::new(),
]
.concat(),
)])
}
}
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 {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<PrivateKey> {
let mut contents = String::new();
File::open(path)?.read_to_string(&mut contents)?;
PrivateKey::from_string(&contents)
}
pub fn from_string(contents: &str) -> Result<PrivateKey> {
let mut iter = contents.lines();
let header = iter.next().unwrap_or("");
if header != "-----BEGIN OPENSSH PRIVATE KEY-----" {
return Err(Error::InvalidFormat);
}
let mut encoded_key = String::new();
loop {
let part = match iter.next() {
Some(p) => p,
None => return Err(Error::InvalidFormat),
};
if part == "-----END OPENSSH PRIVATE KEY-----" {
break;
}
encoded_key.push_str(part);
}
let decoded = base64::decode(encoded_key)?;
let mut reader = Reader::new(&decoded);
let k = PrivateKey::from_reader(&mut reader)?;
Ok(k)
}
pub fn from_bytes<T: ?Sized + AsRef<[u8]>>(buffer: &T) -> Result<PrivateKey> {
let mut reader = Reader::new(buffer);
read_private_key(&mut reader)
}
pub(crate) fn from_reader(reader: &mut Reader<'_>) -> Result<PrivateKey> {
let preamble = reader.read_cstring()?;
if preamble != "openssh-key-v1" {
return Err(Error::InvalidFormat);
}
let cipher_name = reader.read_string()?;
let kdf = reader.read_string()?;
if cipher_name != "none" || kdf != "none" {
return Err(Error::EncryptedPrivateKeyNotSupported);
}
reader.read_string()?;
let number_of_keys = reader.read_u32()?;
if number_of_keys != 1 {
return Err(Error::InvalidFormat);
}
let pubkey = reader
.read_bytes()
.and_then(|v| PublicKey::from_bytes(&v))?;
let _remaining_length = reader.read_u32()?;
let c1 = reader.read_u32()?;
let c2 = reader.read_u32()?;
if c1 != c2 {
return Err(Error::InvalidFormat);
}
let private_key = read_private_key(reader)?;
if private_key.pubkey != pubkey {
return Err(Error::InvalidFormat);
}
Ok(private_key)
}
}