use std::fmt;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
use super::error::{Error, ErrorKind, Result};
use super::keytype::{KeyType, KeyTypeKind};
use super::reader::Reader;
use super::writer::Writer;
use base64;
use base64::Engine;
use sha2::{Digest, Sha256, Sha384, Sha512};
#[derive(Clone, Debug, PartialEq)]
pub enum PublicKeyKind {
Rsa(RsaPublicKey),
Dsa(DsaPublicKey),
Ecdsa(EcdsaPublicKey),
Ed25519(Ed25519PublicKey),
}
#[derive(Clone, Debug, PartialEq)]
pub struct RsaPublicKey {
pub e: Vec<u8>,
pub n: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct DsaPublicKey {
pub p: Vec<u8>,
pub q: Vec<u8>,
pub g: Vec<u8>,
pub y: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum CurveKind {
Nistp256,
Nistp384,
Nistp521,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Curve {
pub kind: CurveKind,
pub identifier: &'static str,
}
impl Curve {
pub fn from_identifier(id: &str) -> Result<Curve> {
let curve = match id {
"nistp256" => Curve {
kind: CurveKind::Nistp256,
identifier: "nistp256",
},
"nistp384" => Curve {
kind: CurveKind::Nistp384,
identifier: "nistp384",
},
"nistp521" => Curve {
kind: CurveKind::Nistp521,
identifier: "nistp521",
},
_ => return Err(Error::with_kind(ErrorKind::UnknownCurve(id.to_string()))),
};
Ok(curve)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct EcdsaPublicKey {
pub curve: Curve,
pub key: Vec<u8>,
pub sk_application: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Ed25519PublicKey {
pub key: Vec<u8>,
pub sk_application: Option<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PublicKey {
pub key_type: KeyType,
pub kind: PublicKeyKind,
pub comment: Option<String>,
}
impl fmt::Display for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let comment = match self.comment {
Some(ref c) => c,
None => "",
};
write!(
f,
"{} {} {}",
self.key_type,
base64::engine::general_purpose::STANDARD.encode(&self.encode()),
comment
)
}
}
#[derive(Debug, PartialEq)]
pub enum FingerprintKind {
Sha256,
Sha384,
Sha512,
}
impl fmt::Display for FingerprintKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let kind = match *self {
FingerprintKind::Sha256 => "SHA256",
FingerprintKind::Sha384 => "SHA384",
FingerprintKind::Sha512 => "SHA512",
};
write!(f, "{}", kind)
}
}
#[derive(Debug)]
pub struct Fingerprint {
pub kind: FingerprintKind,
pub hash: String,
}
impl fmt::Display for Fingerprint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.kind, self.hash)
}
}
impl Fingerprint {
pub fn compute<T: ?Sized + AsRef<[u8]>>(kind: FingerprintKind, data: &T) -> Fingerprint {
let digest = match kind {
FingerprintKind::Sha256 => Sha256::digest(&data.as_ref()).to_vec(),
FingerprintKind::Sha384 => Sha384::digest(&data.as_ref()).to_vec(),
FingerprintKind::Sha512 => Sha512::digest(&data.as_ref()).to_vec(),
};
let mut encoded = base64::engine::general_purpose::STANDARD.encode(&digest);
let hash = match encoded.find('=') {
Some(offset) => encoded.drain(..offset).collect(),
None => encoded,
};
let fp = Fingerprint {
kind: kind,
hash: hash,
};
fp
}
}
impl PublicKey {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<PublicKey> {
let mut contents = String::new();
File::open(path)?.read_to_string(&mut contents)?;
PublicKey::from_string(&contents)
}
pub fn from_string(contents: &str) -> Result<PublicKey> {
let contents = contents
.lines()
.next()
.ok_or(Error::with_kind(ErrorKind::InvalidFormat))?;
let (kt_name, remainder) =
split_whitespace(contents).ok_or(Error::with_kind(ErrorKind::InvalidFormat))?;
let (data, comment) = split_whitespace(remainder)
.map(|(data, comment)| {
let comment = String::from(comment);
(data, Some(comment))
})
.unwrap_or(
(remainder.trim_end(), None),
);
let kt = KeyType::from_name(&kt_name)?;
let decoded = base64::engine::general_purpose::STANDARD.decode(&data)?;
let mut reader = Reader::new(&decoded);
let kt_from_reader = reader.read_string()?;
if kt_name != kt_from_reader {
return Err(Error::with_kind(ErrorKind::KeyTypeMismatch));
}
let k = PublicKey::from_reader(&kt_name, &mut reader)?;
let key = PublicKey {
key_type: kt,
kind: k.kind,
comment: comment,
};
Ok(key)
}
pub fn from_bytes<T: ?Sized + AsRef<[u8]>>(data: &T) -> Result<PublicKey> {
let mut reader = Reader::new(&data);
let kt_name = reader.read_string()?;
PublicKey::from_reader(&kt_name, &mut reader)
}
pub(crate) fn from_reader(kt_name: &str, reader: &mut Reader) -> Result<PublicKey> {
let kt = KeyType::from_name(&kt_name)?;
let kind = match kt.kind {
KeyTypeKind::Rsa | KeyTypeKind::RsaCert => {
let k = RsaPublicKey {
e: reader.read_mpint()?,
n: reader.read_mpint()?,
};
PublicKeyKind::Rsa(k)
}
KeyTypeKind::Dsa | KeyTypeKind::DsaCert => {
let k = DsaPublicKey {
p: reader.read_mpint()?,
q: reader.read_mpint()?,
g: reader.read_mpint()?,
y: reader.read_mpint()?,
};
PublicKeyKind::Dsa(k)
}
KeyTypeKind::Ecdsa | KeyTypeKind::EcdsaCert | KeyTypeKind::EcdsaSk => {
let identifier = reader.read_string()?;
let curve = Curve::from_identifier(&identifier)?;
let key = reader.read_bytes()?;
let sk_application = match kt.kind {
KeyTypeKind::EcdsaSk => Some(reader.read_string()?),
_ => None,
};
let k = EcdsaPublicKey {
curve: curve,
key: key,
sk_application: sk_application,
};
PublicKeyKind::Ecdsa(k)
}
KeyTypeKind::Ed25519 | KeyTypeKind::Ed25519Cert | KeyTypeKind::Ed25519Sk => {
let key = reader.read_bytes()?;
let sk_application = match kt.kind {
KeyTypeKind::Ed25519Sk => Some(reader.read_string()?),
_ => None,
};
let k = Ed25519PublicKey {
key: key,
sk_application: sk_application,
};
PublicKeyKind::Ed25519(k)
}
};
let key = PublicKey {
key_type: kt,
kind: kind,
comment: None,
};
Ok(key)
}
pub fn bits(&self) -> usize {
match self.kind {
PublicKeyKind::Rsa(ref k) => k.n.len() * 8,
PublicKeyKind::Dsa(ref k) => k.p.len() * 8,
PublicKeyKind::Ecdsa(ref k) => match k.curve.kind {
CurveKind::Nistp256 => 256,
CurveKind::Nistp384 => 384,
CurveKind::Nistp521 => 521,
},
PublicKeyKind::Ed25519(_) => 256,
}
}
pub fn encode(&self) -> Vec<u8> {
let mut w = Writer::new();
w.write_string(self.key_type.plain);
match self.kind {
PublicKeyKind::Rsa(ref k) => {
w.write_mpint(&k.e);
w.write_mpint(&k.n);
}
PublicKeyKind::Dsa(ref k) => {
w.write_mpint(&k.p);
w.write_mpint(&k.q);
w.write_mpint(&k.g);
w.write_mpint(&k.y);
}
PublicKeyKind::Ecdsa(ref k) => {
w.write_string(&k.curve.identifier);
w.write_bytes(&k.key);
if self.key_type.kind == KeyTypeKind::EcdsaSk {
w.write_string(&k.sk_application.as_ref().unwrap());
}
}
PublicKeyKind::Ed25519(ref k) => {
w.write_bytes(&k.key);
if self.key_type.kind == KeyTypeKind::Ed25519Sk {
w.write_string(&k.sk_application.as_ref().unwrap());
}
}
}
w.into_bytes()
}
pub fn fingerprint(&self) -> Fingerprint {
self.fingerprint_with(FingerprintKind::Sha256)
}
pub fn fingerprint_with(&self, kind: FingerprintKind) -> Fingerprint {
Fingerprint::compute(kind, &self.encode())
}
pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
let encoded = self.encode();
let data = base64::engine::general_purpose::STANDARD.encode(&encoded);
match self.comment {
Some(ref c) => w.write_fmt(format_args!("{} {} {}\n", self.key_type.name, data, c)),
None => w.write_fmt(format_args!("{} {}\n", self.key_type.name, data)),
}
}
}
fn split_whitespace(s: &str) -> Option<(&str, &str)> {
let whitespace_start = s.find(char::is_whitespace)?;
let (left, right) = s.split_at_checked(whitespace_start)?;
let non_whitespace_start = right.find(|c: char| !c.is_whitespace())?;
let (_, right) = right.split_at_checked(non_whitespace_start)?;
Some((left, right))
}