use crate::utils::*;
use botan_sys::*;
use crate::pubkey::Pubkey;
#[derive(Debug)]
pub struct Certificate {
obj: botan_x509_cert_t,
}
botan_impl_drop!(Certificate, botan_x509_cert_destroy);
impl Clone for Certificate {
fn clone(&self) -> Certificate {
self.duplicate()
.expect("copying X509 cert object succeeded")
}
}
#[derive(Debug, Copy, Clone)]
pub enum CertUsage {
NoRestrictions,
DigitalSignature,
NonRepudiation,
KeyEncipherment,
DataEncipherment,
KeyAgreement,
CertificateSign,
CrlSign,
EncipherOnly,
DecipherOnly,
}
impl From<X509KeyConstraints> for CertUsage {
fn from(err: X509KeyConstraints) -> CertUsage {
match err {
X509KeyConstraints::NO_CONSTRAINTS => CertUsage::NoRestrictions,
X509KeyConstraints::DIGITAL_SIGNATURE => CertUsage::DigitalSignature,
X509KeyConstraints::NON_REPUDIATION => CertUsage::NonRepudiation,
X509KeyConstraints::KEY_ENCIPHERMENT => CertUsage::KeyEncipherment,
X509KeyConstraints::DATA_ENCIPHERMENT => CertUsage::DataEncipherment,
X509KeyConstraints::KEY_AGREEMENT => CertUsage::KeyAgreement,
X509KeyConstraints::KEY_CERT_SIGN => CertUsage::CertificateSign,
X509KeyConstraints::CRL_SIGN => CertUsage::CrlSign,
X509KeyConstraints::ENCIPHER_ONLY => CertUsage::EncipherOnly,
X509KeyConstraints::DECIPHER_ONLY => CertUsage::DecipherOnly,
}
}
}
impl From<CertUsage> for X509KeyConstraints {
fn from(err: CertUsage) -> X509KeyConstraints {
match err {
CertUsage::NoRestrictions => X509KeyConstraints::NO_CONSTRAINTS,
CertUsage::DigitalSignature => X509KeyConstraints::DIGITAL_SIGNATURE,
CertUsage::NonRepudiation => X509KeyConstraints::NON_REPUDIATION,
CertUsage::KeyEncipherment => X509KeyConstraints::KEY_ENCIPHERMENT,
CertUsage::DataEncipherment => X509KeyConstraints::DATA_ENCIPHERMENT,
CertUsage::KeyAgreement => X509KeyConstraints::KEY_AGREEMENT,
CertUsage::CertificateSign => X509KeyConstraints::KEY_CERT_SIGN,
CertUsage::CrlSign => X509KeyConstraints::CRL_SIGN,
CertUsage::EncipherOnly => X509KeyConstraints::ENCIPHER_ONLY,
CertUsage::DecipherOnly => X509KeyConstraints::DECIPHER_ONLY,
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum CertValidationStatus {
Success(i32),
Failed(i32),
}
impl CertValidationStatus {
#[must_use]
pub fn success(&self) -> bool {
match self {
CertValidationStatus::Success(_) => true,
CertValidationStatus::Failed(_) => false,
}
}
}
impl ToString for CertValidationStatus {
fn to_string(&self) -> String {
let code = match self {
CertValidationStatus::Success(x) => x,
CertValidationStatus::Failed(x) => x,
};
unsafe {
let result_str = botan_x509_cert_validation_status(*code);
let cstr = CStr::from_ptr(result_str);
cstr.to_str().unwrap().to_owned()
}
}
}
impl Certificate {
pub(crate) fn handle(&self) -> botan_x509_cert_t {
self.obj
}
pub fn load(data: &[u8]) -> Result<Certificate> {
let obj = botan_init!(botan_x509_cert_load, data.as_ptr(), data.len())?;
Ok(Certificate { obj })
}
pub fn from_file(fsname: &str) -> Result<Certificate> {
let fsname = make_cstr(fsname)?;
let obj = botan_init!(botan_x509_cert_load_file, fsname.as_ptr())?;
Ok(Certificate { obj })
}
pub fn serial_number(&self) -> Result<Vec<u8>> {
let sn_len = 32; call_botan_ffi_returning_vec_u8(sn_len, &|out_buf, out_len| unsafe {
botan_x509_cert_get_serial_number(self.obj, out_buf, out_len)
})
}
pub fn fingerprint(&self, hash: &str) -> Result<Vec<u8>> {
let fprint_len = 128;
let hash = make_cstr(hash)?;
call_botan_ffi_returning_vec_u8(fprint_len, &|out_buf, out_len| unsafe {
botan_x509_cert_get_fingerprint(self.obj, hash.as_ptr(), out_buf, out_len)
})
}
pub fn duplicate(&self) -> Result<Certificate> {
let obj = botan_init!(botan_x509_cert_dup, self.obj)?;
Ok(Certificate { obj })
}
pub fn authority_key_id(&self) -> Result<Vec<u8>> {
let akid_len = 32;
call_botan_ffi_returning_vec_u8(akid_len, &|out_buf, out_len| unsafe {
botan_x509_cert_get_authority_key_id(self.obj, out_buf, out_len)
})
}
pub fn subject_key_id(&self) -> Result<Vec<u8>> {
let skid_len = 32;
call_botan_ffi_returning_vec_u8(skid_len, &|out_buf, out_len| unsafe {
botan_x509_cert_get_subject_key_id(self.obj, out_buf, out_len)
})
}
pub fn public_key_bits(&self) -> Result<Vec<u8>> {
let pk_len = 4096; call_botan_ffi_returning_vec_u8(pk_len, &|out_buf, out_len| unsafe {
botan_x509_cert_get_public_key_bits(self.obj, out_buf, out_len)
})
}
pub fn public_key(&self) -> Result<Pubkey> {
let mut key = ptr::null_mut();
botan_call!(botan_x509_cert_get_public_key, self.obj, &mut key)?;
Ok(Pubkey::from_handle(key))
}
pub fn to_string(&self) -> Result<String> {
let as_str_len = 4096;
call_botan_ffi_returning_string(as_str_len, &|out_buf, out_len| unsafe {
botan_x509_cert_to_string(self.obj, out_buf as *mut c_char, out_len)
})
}
pub fn allows_usage(&self, usage: CertUsage) -> Result<bool> {
let usage_bit: X509KeyConstraints = X509KeyConstraints::from(usage);
let r = botan_bool_in_rc!(botan_x509_cert_allowed_usage, self.obj, usage_bit as u32)?;
Ok(!r)
}
pub fn verify(
&self,
intermediates: &[&Certificate],
trusted: &[&Certificate],
trusted_path: Option<&str>,
hostname: Option<&str>,
reference_time: Option<u64>,
) -> Result<CertValidationStatus> {
let required_key_strength = 110;
let trusted_path = make_cstr(trusted_path.unwrap_or(""))?;
let hostname = make_cstr(hostname.unwrap_or(""))?;
let mut trusted_h = Vec::new();
for t in trusted {
trusted_h.push(t.handle());
}
let mut intermediates_h = Vec::new();
for t in intermediates {
intermediates_h.push(t.handle());
}
let mut result = 0;
let rc = unsafe {
botan_x509_cert_verify(
&mut result,
self.obj,
intermediates_h.as_ptr(),
intermediates_h.len(),
trusted_h.as_ptr(),
trusted_h.len(),
trusted_path.as_ptr(),
required_key_strength,
hostname.as_ptr(),
reference_time.unwrap_or(0),
)
};
if rc == 0 {
Ok(CertValidationStatus::Success(result))
} else if rc == 1 {
Ok(CertValidationStatus::Failed(result))
} else {
Err(Error::from_rc(rc))
}
}
pub fn matches_hostname(&self, hostname: &str) -> Result<bool> {
let hostname = make_cstr(hostname)?;
let rc = unsafe { botan_x509_cert_hostname_match(self.obj, hostname.as_ptr()) };
if rc == 0 {
Ok(true)
} else if rc == -1 {
Ok(false)
} else {
Err(Error::from_rc(rc))
}
}
}