use ring::digest;
use std::fmt;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
use super::keytype::{Curve, CurveKind};
use super::error::{Error, ErrorKind, Result};
use super::keytype::{KeyType, KeyTypeKind};
use super::reader::Reader;
use super::writer::Writer;
#[derive(Debug, PartialEq, Clone)]
pub enum PublicKeyKind {
Rsa(RsaPublicKey),
Ecdsa(EcdsaPublicKey),
Ed25519(Ed25519PublicKey),
}
#[derive(Debug, PartialEq, Clone)]
pub struct RsaPublicKey {
pub e: Vec<u8>,
pub n: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct EcdsaPublicKey {
pub curve: Curve,
pub key: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct Ed25519PublicKey {
pub key: Vec<u8>,
}
#[derive(Debug, PartialEq, Clone)]
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(c) => c,
None => "",
};
write!(
f,
"{} {} {}",
self.key_type,
base64::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 => digest::digest(&digest::SHA256, &data.as_ref()).as_ref().to_vec(),
FingerprintKind::Sha384 => digest::digest(&digest::SHA384, &data.as_ref()).as_ref().to_vec(),
FingerprintKind::Sha512 => digest::digest(&digest::SHA512, &data.as_ref()).as_ref().to_vec(),
};
let mut encoded = base64::encode(&digest);
let hash = match encoded.find('=') {
Some(offset) => encoded.drain(..offset).collect(),
None => encoded,
};
Fingerprint {
kind,
hash,
}
}
}
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 mut iter = contents.split_whitespace();
let kt_name = iter
.next()
.ok_or_else(|| Error::with_kind(ErrorKind::InvalidFormat))?;
let data = iter
.next()
.ok_or_else(|| Error::with_kind(ErrorKind::InvalidFormat))?;
let comment = iter.next().map(String::from);
let key_type = KeyType::from_name(&kt_name)?;
let decoded = base64::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,
kind: k.kind,
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::Ecdsa | KeyTypeKind::EcdsaCert => {
let identifier = reader.read_string()?;
let curve = Curve::from_identifier(&identifier)?;
let key = reader.read_bytes()?;
let k = EcdsaPublicKey {
curve,
key,
};
PublicKeyKind::Ecdsa(k)
}
KeyTypeKind::Ed25519 | KeyTypeKind::Ed25519Cert => {
let k = Ed25519PublicKey {
key: reader.read_bytes()?,
};
PublicKeyKind::Ed25519(k)
}
};
let key = PublicKey {
key_type: kt,
kind,
comment: None,
};
Ok(key)
}
pub fn bits(&self) -> usize {
match self.kind {
PublicKeyKind::Rsa(ref k) => k.n.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::Ecdsa(ref k) => {
w.write_string(&k.curve.identifier);
w.write_bytes(&k.key);
}
PublicKeyKind::Ed25519(ref k) => {
w.write_bytes(&k.key);
}
}
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::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)),
}
}
}