use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use super::error::{Error, ErrorKind, Result};
use super::keytype::KeyType;
use super::pubkey::PublicKey;
use super::reader::Reader;
use base64;
use base64::Engine;
#[derive(Debug, PartialEq)]
pub enum CertType {
User,
Host,
}
impl fmt::Display for CertType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
CertType::User => write!(f, "user certificate"),
CertType::Host => write!(f, "host certificate"),
}
}
}
#[derive(Debug)]
pub struct Certificate {
pub key_type: KeyType,
pub nonce: Vec<u8>,
pub key: PublicKey,
pub serial: u64,
pub cert_type: CertType,
pub key_id: String,
pub valid_principals: Vec<String>,
pub valid_after: u64,
pub valid_before: u64,
pub critical_options: HashMap<String, String>,
pub extensions: HashMap<String, String>,
pub reserved: Vec<u8>,
pub signature_key: PublicKey,
pub signature: Vec<u8>,
pub comment: Option<String>,
}
impl Certificate {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Certificate> {
let mut contents = String::new();
File::open(path)?.read_to_string(&mut contents)?;
Certificate::from_string(&contents)
}
pub fn from_string(s: &str) -> Result<Certificate> {
let mut iter = s.split_whitespace();
let kt_name = iter
.next()
.ok_or(Error::with_kind(ErrorKind::InvalidFormat))?;
let kt = KeyType::from_name(&kt_name)?;
if !kt.is_cert {
return Err(Error::with_kind(ErrorKind::NotCertificate));
}
let data = iter
.next()
.ok_or(Error::with_kind(ErrorKind::InvalidFormat))?;
let comment = iter.next().map(|v| String::from(v));
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 nonce = reader.read_bytes()?;
let key = PublicKey::from_reader(&kt_name, &mut reader)?;
let serial = reader.read_u64()?;
let cert_type = match reader.read_u32()? {
1 => CertType::User,
2 => CertType::Host,
n => return Err(Error::with_kind(ErrorKind::InvalidCertType(n))),
};
let key_id = reader.read_string()?;
let principals = reader.read_bytes().and_then(|v| read_principals(&v))?;
let valid_after = reader.read_u64()?;
let valid_before = reader.read_u64()?;
let critical_options = reader.read_bytes().and_then(|v| read_options(&v))?;
let extensions = reader.read_bytes().and_then(|v| read_options(&v))?;
let reserved = reader.read_bytes()?;
let signature_key = reader
.read_bytes()
.and_then(|v| PublicKey::from_bytes(&v))?;
let signature = reader.read_bytes()?;
let cert = Certificate {
key_type: kt,
nonce: nonce,
key: key,
serial: serial,
cert_type: cert_type,
key_id: key_id,
valid_principals: principals,
valid_after: valid_after,
valid_before: valid_before,
critical_options: critical_options,
extensions: extensions,
reserved: reserved,
signature_key: signature_key,
signature: signature,
comment: comment,
};
Ok(cert)
}
}
fn read_options(buf: &[u8]) -> Result<HashMap<String, String>> {
let mut reader = Reader::new(&buf);
let mut options = HashMap::new();
loop {
let name = match reader.read_string() {
Ok(v) => v,
Err(e) => match e.kind {
ErrorKind::UnexpectedEof => break,
_ => return Err(e),
},
};
let value_buf = reader.read_bytes()?;
let value = if value_buf.len() > 0 {
Reader::new(&value_buf).read_string()?
} else {
"".to_string()
};
options.insert(name, value);
}
Ok(options)
}
fn read_principals(buf: &[u8]) -> Result<Vec<String>> {
let mut reader = Reader::new(&buf);
let mut items = Vec::new();
loop {
let principal = match reader.read_string() {
Ok(v) => v,
Err(e) => match e.kind {
ErrorKind::UnexpectedEof => break,
_ => return Err(e),
},
};
items.push(principal);
}
Ok(items)
}