Can revoke issued certificates
This commit is contained in:
		@@ -13,10 +13,11 @@ use openssl::pkey::{PKey, Private};
 | 
				
			|||||||
use openssl::x509::extension::{
 | 
					use openssl::x509::extension::{
 | 
				
			||||||
    BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, SubjectKeyIdentifier,
 | 
					    BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, SubjectKeyIdentifier,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use openssl::x509::{X509Crl, X509Name, X509NameBuilder, X509Req, X509};
 | 
					use openssl::x509::{CrlStatus, X509Crl, X509Name, X509NameBuilder, X509Req, X509};
 | 
				
			||||||
use openssl_sys::{
 | 
					use openssl_sys::{
 | 
				
			||||||
    X509_CRL_add0_revoked, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate,
 | 
					    X509_CRL_add0_revoked, X509_CRL_new, X509_CRL_set1_lastUpdate, X509_CRL_set1_nextUpdate,
 | 
				
			||||||
    X509_CRL_set_issuer_name, X509_CRL_set_version, X509_CRL_sign, X509_REVOKED_dup,
 | 
					    X509_CRL_set_issuer_name, X509_CRL_set_version, X509_CRL_sign, X509_REVOKED_dup,
 | 
				
			||||||
 | 
					    X509_REVOKED_new, X509_REVOKED_set_revocationDate, X509_REVOKED_set_serialNumber,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::app_config::AppConfig;
 | 
					use crate::app_config::AppConfig;
 | 
				
			||||||
@@ -365,7 +366,7 @@ pub fn initialize_server_ca() -> anyhow::Result<()> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Initialize or refresh a CRL
 | 
					/// Initialize or refresh a CRL
 | 
				
			||||||
fn refresh_crl(d: &CertData) -> anyhow::Result<()> {
 | 
					fn refresh_crl(d: &CertData, new_cert: Option<&X509>) -> anyhow::Result<()> {
 | 
				
			||||||
    let crl_path = d.crl.as_ref().ok_or(PKIError::MissingCRL)?;
 | 
					    let crl_path = d.crl.as_ref().ok_or(PKIError::MissingCRL)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let old_crl = if crl_path.exists() {
 | 
					    let old_crl = if crl_path.exists() {
 | 
				
			||||||
@@ -373,7 +374,9 @@ fn refresh_crl(d: &CertData) -> anyhow::Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Check if revocation is un-needed
 | 
					        // Check if revocation is un-needed
 | 
				
			||||||
        let next_update = crl.next_update().ok_or(PKIError::MissingCRLNextUpdate)?;
 | 
					        let next_update = crl.next_update().ok_or(PKIError::MissingCRLNextUpdate)?;
 | 
				
			||||||
        if next_update.compare(Asn1Time::days_from_now(0)?.as_ref())? == Ordering::Greater {
 | 
					        if next_update.compare(Asn1Time::days_from_now(0)?.as_ref())? == Ordering::Greater
 | 
				
			||||||
 | 
					            && new_cert.is_none()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            return Ok(());
 | 
					            return Ok(());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -386,7 +389,7 @@ fn refresh_crl(d: &CertData) -> anyhow::Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // based on https://github.com/openssl/openssl/blob/master/crypto/x509/x509_vfy.c
 | 
					    // based on https://github.com/openssl/openssl/blob/master/crypto/x509/x509_vfy.c
 | 
				
			||||||
    unsafe {
 | 
					    unsafe {
 | 
				
			||||||
        let crl = openssl_sys::X509_CRL_new();
 | 
					        let crl = X509_CRL_new();
 | 
				
			||||||
        if crl.is_null() {
 | 
					        if crl.is_null() {
 | 
				
			||||||
            return Err(PKIError::GenCRLError("Could not construct CRL!").into());
 | 
					            return Err(PKIError::GenCRLError("Could not construct CRL!").into());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -420,6 +423,31 @@ fn refresh_crl(d: &CertData) -> anyhow::Result<()> {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If requested, add new entry
 | 
				
			||||||
 | 
					        if let Some(new_cert) = new_cert {
 | 
				
			||||||
 | 
					            let entry = X509_REVOKED_new();
 | 
				
			||||||
 | 
					            if entry.is_null() {
 | 
				
			||||||
 | 
					                return Err(PKIError::GenCRLError("X509_CRL_new for new entry").into());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if X509_REVOKED_set_serialNumber(entry, new_cert.serial_number().as_ptr()) == 0 {
 | 
				
			||||||
 | 
					                return Err(
 | 
				
			||||||
 | 
					                    PKIError::GenCRLError("X509_REVOKED_set_serialNumber for new entry").into(),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let revocation_date = Asn1Time::days_from_now(0)?;
 | 
				
			||||||
 | 
					            if X509_REVOKED_set_revocationDate(entry, revocation_date.as_ptr()) == 0 {
 | 
				
			||||||
 | 
					                return Err(
 | 
				
			||||||
 | 
					                    PKIError::GenCRLError("X509_REVOKED_set_revocationDate for new entry").into(),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if X509_CRL_add0_revoked(crl, X509_REVOKED_dup(entry)) == 0 {
 | 
				
			||||||
 | 
					                return Err(PKIError::GenCRLError("X509_CRL_add0_revoked for new entry").into());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let md = MessageDigest::sha256();
 | 
					        let md = MessageDigest::sha256();
 | 
				
			||||||
        if X509_CRL_sign(crl, d.key.as_ptr(), md.as_ptr()) == 0 {
 | 
					        if X509_CRL_sign(crl, d.key.as_ptr(), md.as_ptr()) == 0 {
 | 
				
			||||||
            return Err(PKIError::GenCRLError("X509_CRL_sign").into());
 | 
					            return Err(PKIError::GenCRLError("X509_CRL_sign").into());
 | 
				
			||||||
@@ -434,9 +462,9 @@ fn refresh_crl(d: &CertData) -> anyhow::Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// Refresh revocation lists
 | 
					/// Refresh revocation lists
 | 
				
			||||||
pub fn refresh_crls() -> anyhow::Result<()> {
 | 
					pub fn refresh_crls() -> anyhow::Result<()> {
 | 
				
			||||||
    refresh_crl(&CertData::load_root_ca()?)?;
 | 
					    refresh_crl(&CertData::load_root_ca()?, None)?;
 | 
				
			||||||
    refresh_crl(&CertData::load_web_ca()?)?;
 | 
					    refresh_crl(&CertData::load_web_ca()?, None)?;
 | 
				
			||||||
    refresh_crl(&CertData::load_devices_ca()?)?;
 | 
					    refresh_crl(&CertData::load_devices_ca()?, None)?;
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -451,3 +479,31 @@ pub fn gen_certificate_for_device(csr: &X509Req) -> anyhow::Result<String> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Ok(String::from_utf8(cert)?)
 | 
					    Ok(String::from_utf8(cert)?)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Check if a certificate is revoked
 | 
				
			||||||
 | 
					fn is_revoked(cert: &X509, ca: &CertData) -> anyhow::Result<bool> {
 | 
				
			||||||
 | 
					    let crl = X509Crl::from_pem(&std::fs::read(
 | 
				
			||||||
 | 
					        ca.crl.as_ref().ok_or(PKIError::MissingCRL)?,
 | 
				
			||||||
 | 
					    )?)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let res = crl.get_by_cert(cert);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(matches!(res, CrlStatus::Revoked(_)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Revoke a certificate
 | 
				
			||||||
 | 
					pub fn revoke(cert: &X509, ca: &CertData) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					    // Check if certificate is already revoked
 | 
				
			||||||
 | 
					    if is_revoked(cert, ca)? {
 | 
				
			||||||
 | 
					        // No op
 | 
				
			||||||
 | 
					        return Ok(());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    refresh_crl(ca, Some(cert))?;
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Revoke a device certificate
 | 
				
			||||||
 | 
					pub fn revoke_device_cert(cert: &X509) -> anyhow::Result<()> {
 | 
				
			||||||
 | 
					    revoke(cert, &CertData::load_devices_ca()?)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ use crate::app_config::AppConfig;
 | 
				
			|||||||
use crate::crypto::pki;
 | 
					use crate::crypto::pki;
 | 
				
			||||||
use crate::devices::device::{Device, DeviceId, DeviceInfo};
 | 
					use crate::devices::device::{Device, DeviceId, DeviceInfo};
 | 
				
			||||||
use crate::utils::time_utils::time_secs;
 | 
					use crate::utils::time_utils::time_secs;
 | 
				
			||||||
use openssl::x509::X509Req;
 | 
					use openssl::x509::{X509Req, X509};
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(thiserror::Error, Debug)]
 | 
					#[derive(thiserror::Error, Debug)]
 | 
				
			||||||
@@ -15,6 +15,10 @@ pub enum DevicesListError {
 | 
				
			|||||||
    ValidateDeviceFailedDeviceNotFound,
 | 
					    ValidateDeviceFailedDeviceNotFound,
 | 
				
			||||||
    #[error("Validated device failed: the device is already validated!")]
 | 
					    #[error("Validated device failed: the device is already validated!")]
 | 
				
			||||||
    ValidateDeviceFailedDeviceAlreadyValidated,
 | 
					    ValidateDeviceFailedDeviceAlreadyValidated,
 | 
				
			||||||
 | 
					    #[error("Requested device was not found")]
 | 
				
			||||||
 | 
					    DeviceNotFound,
 | 
				
			||||||
 | 
					    #[error("Requested device is not validated")]
 | 
				
			||||||
 | 
					    DeviceNotValidated,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct DevicesList(HashMap<DeviceId, Device>);
 | 
					pub struct DevicesList(HashMap<DeviceId, Device>);
 | 
				
			||||||
@@ -129,12 +133,26 @@ impl DevicesList {
 | 
				
			|||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Get single certificate information
 | 
				
			||||||
 | 
					    fn get_cert(&self, id: &DeviceId) -> anyhow::Result<X509> {
 | 
				
			||||||
 | 
					        let dev = self
 | 
				
			||||||
 | 
					            .get_single(id)
 | 
				
			||||||
 | 
					            .ok_or(DevicesListError::DeviceNotFound)?;
 | 
				
			||||||
 | 
					        if !dev.validated {
 | 
				
			||||||
 | 
					            return Err(DevicesListError::DeviceNotValidated.into());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(X509::from_pem(&std::fs::read(
 | 
				
			||||||
 | 
					            AppConfig::get().device_cert_path(id),
 | 
				
			||||||
 | 
					        )?)?)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Delete a device
 | 
					    /// Delete a device
 | 
				
			||||||
    pub fn delete(&mut self, id: &DeviceId) -> anyhow::Result<()> {
 | 
					    pub fn delete(&mut self, id: &DeviceId) -> anyhow::Result<()> {
 | 
				
			||||||
        let crt_path = AppConfig::get().device_cert_path(id);
 | 
					        let crt_path = AppConfig::get().device_cert_path(id);
 | 
				
			||||||
        if crt_path.is_file() {
 | 
					        if crt_path.is_file() {
 | 
				
			||||||
            // TODO : implement
 | 
					            let cert = self.get_cert(id)?;
 | 
				
			||||||
            unimplemented!("Certificate revocation not implemented yet!");
 | 
					            pki::revoke_device_cert(&cert)?;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let csr_path = AppConfig::get().device_csr_path(id);
 | 
					        let csr_path = AppConfig::get().device_csr_path(id);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,8 @@ async fn main() -> std::io::Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    pki::refresh_crls().expect("Failed to initialize Root CA!");
 | 
					    pki::refresh_crls().expect("Failed to initialize Root CA!");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO : schedule CRL auto renewal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Initialize energy actor
 | 
					    // Initialize energy actor
 | 
				
			||||||
    let actor = EnergyActor::new()
 | 
					    let actor = EnergyActor::new()
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user