use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use ring::rand::{SecureRandom, SystemRandom};
use super::{keytype::KeyType, pubkey::PublicKey, reader::Reader, writer::Writer};
use super::{signature, SSHCertificateSigner};
use crate::{error::Error, Result};
use std::convert::TryFrom;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CertType {
User = 1,
Host = 2,
}
impl TryFrom<&str> for CertType {
type Error = &'static str;
fn try_from(s: &str) -> std::result::Result<Self, Self::Error> {
match s {
"user" | "User" => Ok(CertType::User),
"host" | "Host" => Ok(CertType::Host),
_ => Err("Unknown certificate type"),
}
}
}
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"),
}
}
}
const STANDARD_EXTENSIONS: [(&str, &str); 5] = [
("permit-agent-forwarding", ""),
("permit-port-forwarding", ""),
("permit-pty", ""),
("permit-user-rc", ""),
("permit-X11-forwarding", ""),
];
#[derive(Debug, PartialEq, Eq)]
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 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>,
pub serialized: Vec<u8>,
}
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 outer_kt = KeyType::from_name(iter.next().ok_or(Error::InvalidFormat)?)?;
let data = iter.next().ok_or(Error::InvalidFormat)?;
let comment = iter.next().map(String::from);
let decoded = base64::decode(data)?;
let mut cert = Certificate::from_bytes(&decoded)?;
cert.comment = comment;
if cert.key_type.kind != outer_kt.kind {
return Err(Error::KeyTypeMismatch);
}
Ok(cert)
}
pub fn from_bytes(data: &[u8]) -> Result<Certificate> {
let mut reader = Reader::new(&data);
let kt_name = reader.read_string()?;
let key_type = KeyType::from_name(&kt_name)?;
if !key_type.is_cert {
return Err(Error::NotCertificate);
}
let nonce = reader.read_bytes()?;
let key = PublicKey::from_reader(&key_type.as_pubkey_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::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 signed_len = reader.get_offset();
let signature = reader.read_bytes()?;
reader.set_offset(0)?;
let signed_bytes = reader.read_raw_bytes(signed_len)?;
signature::verify_signature(&signature, &signed_bytes, &signature_key)?;
Ok(Certificate {
key_type,
nonce,
key,
serial,
cert_type,
key_id,
principals,
valid_after,
valid_before,
critical_options,
extensions,
reserved,
signature_key,
signature,
comment: None,
serialized: data.to_vec(),
})
}
pub fn standard_extensions() -> HashMap<String, String> {
let mut hm = HashMap::new();
for extension in &STANDARD_EXTENSIONS {
hm.insert(String::from(extension.0), String::from(extension.1));
}
hm
}
pub fn builder(
pubkey: &PublicKey,
cert_type: CertType,
signing_key: &PublicKey,
) -> Result<Certificate> {
let kt_name = pubkey.key_type.as_cert_name();
let key_type = KeyType::from_name(kt_name.as_str())?;
let rng = SystemRandom::new();
let mut nonce = [0x0u8; 32];
match SecureRandom::fill(&rng, &mut nonce) {
Ok(()) => (),
Err(_) => return Err(Error::UnexpectedEof),
};
let mut serial = [0x0u8; 8];
match SecureRandom::fill(&rng, &mut serial) {
Ok(()) => (),
Err(_) => return Err(Error::UnexpectedEof),
};
let serial = u64::from_be_bytes(serial);
Ok(Certificate {
nonce: nonce.to_vec(),
key: pubkey.clone(),
key_type,
serial,
cert_type,
key_id: String::new(),
principals: vec![],
valid_after: 0,
valid_before: 0,
critical_options: HashMap::new(),
extensions: HashMap::new(),
reserved: vec![0, 0, 0, 0, 0, 0, 0, 0],
signature_key: signing_key.clone(),
signature: vec![],
comment: None,
serialized: vec![],
})
}
pub fn serial(mut self, serial: u64) -> Self {
self.serial = serial;
self
}
pub fn key_id<S: AsRef<str>>(mut self, key_id: S) -> Self {
self.key_id = key_id.as_ref().to_owned();
self
}
pub fn principal<S: AsRef<str>>(mut self, principal: S) -> Self {
self.principals.push(principal.as_ref().to_owned());
self
}
pub fn set_principals(mut self, principals: &[String]) -> Self {
self.principals = principals.to_vec();
self
}
pub fn valid_after(mut self, valid_after: u64) -> Self {
self.valid_after = valid_after;
self
}
pub fn valid_before(mut self, valid_before: u64) -> Self {
self.valid_before = valid_before;
self
}
pub fn critical_option<S: AsRef<str>>(mut self, option: S, value: S) -> Self {
self.critical_options
.insert(option.as_ref().to_owned(), value.as_ref().to_owned());
self
}
pub fn set_critical_options(mut self, critical_options: HashMap<String, String>) -> Self {
self.critical_options = critical_options;
self
}
pub fn extension<S: AsRef<str>>(mut self, option: S, value: S) -> Self {
self.extensions
.insert(option.as_ref().to_owned(), value.as_ref().to_owned());
self
}
pub fn set_extensions(mut self, extensions: HashMap<String, String>) -> Self {
self.extensions = extensions;
self
}
pub fn comment<S: AsRef<str>>(mut self, comment: S) -> Self {
self.comment = Some(comment.as_ref().to_owned());
self
}
pub fn tbs_certificate(&self) -> Vec<u8> {
let mut writer = Writer::new();
let kt_name = self.key_type.as_cert_name();
writer.write_string(kt_name.as_str());
writer.write_bytes(&self.nonce);
writer.write_pub_key_data(&self.key);
writer.write_u64(self.serial);
writer.write_u32(self.cert_type as u32);
writer.write_string(&self.key_id);
writer.write_string_vec(&self.principals);
writer.write_u64(self.valid_after);
writer.write_u64(self.valid_before);
writer.write_string_map(&self.critical_options);
writer.write_string_map(&self.extensions);
writer.write_u32(0x0);
writer.write_bytes(&self.signature_key.encode());
writer.as_bytes().to_vec()
}
pub fn add_signature(mut self, signature: &[u8]) -> Result<Self> {
let mut tbs = self.tbs_certificate();
signature::verify_signature(signature, &tbs, &self.signature_key)?;
let mut wrapped_writer = Writer::new();
wrapped_writer.write_bytes(signature);
tbs.extend_from_slice(&wrapped_writer.into_bytes());
self.signature = signature.to_vec();
self.serialized = tbs;
Ok(self)
}
pub fn sign<T: SSHCertificateSigner>(self, signer: &T) -> Result<Self> {
let tbs_certificate = self.tbs_certificate();
let signature = signer.sign(&tbs_certificate).ok_or(Error::SigningError)?;
self.add_signature(&signature)
}
}
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 {
Error::UnexpectedEof => break,
_ => return Err(e),
},
};
let value_buf = reader.read_bytes()?;
let value = if !value_buf.is_empty() {
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 {
Error::UnexpectedEof => break,
_ => return Err(e),
},
};
items.push(principal);
}
Ok(items)
}
impl fmt::Display for Certificate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !f.alternate() {
write!(
f,
"{} {} {}",
&self.key_type.name,
base64::encode(&self.serialized),
&self.key_id
)
} else {
let mut pretty: String = format!("Type: {} {}\n", self.key_type, self.cert_type);
pretty.push_str(&format!(
"Public Key: {} {}:{}\n",
self.key_type.short_name,
self.key.fingerprint().kind,
self.key.fingerprint().hash
));
pretty.push_str(&format!(
"Signing CA: {} {}:{} (using {})\n",
self.signature_key.key_type.short_name,
self.signature_key.fingerprint().kind,
self.signature_key.fingerprint().hash,
self.signature_key.key_type
));
pretty.push_str(&format!("Key ID: \"{}\"\n", self.key_id));
pretty.push_str(&format!("Serial: {}\n", self.serial));
if self.valid_before == 0xFFFFFFFFFFFFFFFF && self.valid_after == 0x0 {
pretty.push_str("Valid: forever\n");
} else {
pretty.push_str(&format!(
"Valid between: {} and {}\n",
self.valid_after, self.valid_before
));
}
if self.principals.is_empty() {
pretty.push_str("Principals: (none)\n");
} else {
pretty.push_str("Principals\n");
for principal in &self.principals {
pretty.push_str(&format!("\t{}\n", principal));
}
}
if self.critical_options.is_empty() {
pretty.push_str("Critical Options: (none)\n");
} else {
pretty.push_str("Critical Options:\n");
for (name, value) in &self.critical_options {
pretty.push_str(&format!("\t{} {}\n", name, value));
}
}
if self.extensions.is_empty() {
pretty.push_str("Extensions: (none)\n");
} else {
pretty.push_str("Extensions:\n");
for name in self.extensions.keys() {
pretty.push_str(&format!("\t{}\n", &name));
}
}
write!(f, "{}", pretty)
}
}
}